(Major rewrite) |
(→Common Guidelines: use absolute paths in example) |
||
Line 57: | Line 57: | ||
RemainAfterExit=no | RemainAfterExit=no | ||
ExecStart=/usr/bin/sscg --package tog-pegasus --ca-file client.pem --cert-file server.pem --cert-key-file file.pem | ExecStart=/usr/bin/sscg --package tog-pegasus --ca-file /etc/Pegasus/client.pem --cert-file /etc/Pegasus/server.pem --cert-key-file /etc/Pegasus/file.pem | ||
</pre> | </pre> | ||
Revision as of 16:47, 16 July 2015
First-time Service Setup
Many system services require some amount of initial setup before they can run properly for the first time. Common examples are the generation of private keys and certificates or a unique, system-specific identifier.
Traditionally, this was done by RPM scriptlets as part of the installation or upgrade of a package. This was sensible for a time when the majority of installations were performed by attended or unattended installers (such as anaconda and kickstart).
Today we see an increased reliance on generating virtual machine images for use in both traditional and cloud-computing environments. In those cases, having system-specific data created at package installation time is problematic. It means that the production of such images need to have significant care applied to remove any system-specific information about them and then additional tools written to apply the corrected information post-deployment. The goal of this guideline is to ensure that if a system clean-up service such as virt-sysprep is run on the system and then the machine is rebooted, any service that requires first-time configuration will re-run it. The mechanism by which we will accomplish this is to remove such first-time configuration from RPM scriptlets (e.g. %post
) and instead execute this configuration as part of service startup with systemd.
This guideline describes a mechanism that can be used for both traditional and cloud-based deployment styles.
Note: this requirement can be waived if the equivalent functionality is incorporated as part of the service's own standard startup. These guidelines are meant to address services that require setup before the service can be started.
Defining System-Specific Setup
A particular setup task is defined thusly: "Any action that must be performed on the system where the service will be run whose output is not identical for all systems running that service."
Some non-exhaustive examples of system-specific configuration:
- The SSH daemon generates a public/private host key
- The mod_ssl httpd module creates a self-signed certificate for the machine's hostname
- A remote logging service creates a UUID to represent this machine
A few examples that should not be considered system-specific configuration:
- Creating a service user and/or group. This is safe to copy to clones of the system.
- Any content that is automatically re-generated by the service upon deletion.
Common Guidelines
For all system-specific cases, we will take advantage of systemd's service functionality. Packagers will create a new service unit file for each service unit in their package that requires per-system configuration. This service unit will be named <servicename>-init.service
and installed to /usr/lib/systemd/system
. For example, the tog-pegasus.service
configuration unit would be /usr/lib/systemd/tog-pegasus-init.service
.
The contents of this service unit will be as follows:
[Unit] Description=One-time configuration for <servicename> ConditionPathExists=!/path/to/generated/config ConditionPathExists=|!/path/to/other/generated/config (one or more lines optional) [Service] Type=oneshot RemainAfterExit=no ExecStart=/path/to/config/script
The syntax for ConditionPathExists=
uses the ! to indicate negation (the file is not present). The | is used to create an OR logical pairing (resulting in the lack of ANY of these files causing the configuration to be run).
To use tog-pegasus.service
as an example:
[Unit] Description=One-time configuration for <servicename> ConditionPathExists=!/etc/Pegasus/server.pem ConditionPathExists=|!/etc/Pegasus/file.pem ConditionPathExists=|!/etc/Pegasus/client.pem [Service] Type=oneshot RemainAfterExit=no ExecStart=/usr/bin/sscg --package tog-pegasus --ca-file /etc/Pegasus/client.pem --cert-file /etc/Pegasus/server.pem --cert-key-file /etc/Pegasus/file.pem
The ExecStart
command may do anything, so long as it returns 0 on success. In this case, we are generating a self-signed certificate for the service to use.
Packagers will also need to update their primary service unit to require this one and run after it:
[Unit] ... Requires=<service>-init.service After=<service>-init.service
To continue the tog-pegasus.service
example:
[Unit] Description=OpenPegasus CIM Server After=syslog.target slpd.service Requires=tog-pegasus-init.service After=tog-pegasus-init.service [Service] Type=forking ExecStart=/usr/sbin/cimserver PIDFile=/var/run/tog-pegasus/cimserver.pid [Install] WantedBy=multi-user.target
Special Case: Self-signed Certificate Generation
If your service makes use of the SSL/TLS protocol for transport security, your service will require a service certificate. Ideally the administrator deploying a new service should obtain an X.509 certificate from an appropriate Certificate Authority (CA), which should be from a globally operating CA (such as a commercial SSL certificate vendor) if your service will be available on the public Internet, or from a private CA (such as a domain controller CA) if your service will run inside an Intranet.
However, it is often desirable to start using a self-signed certificate, which can be immediately created, and allows the administrator to immediately proceed doing the installation tasks. This document will explain how to obtain a self-signed certificate, but it is recommended that it gets replaced prior to public deployment.
The disadvantage of self-signed certificates is that most client software (like web browsers) will reject them as untrusted, and if at all, will require the user to override and trust it explicitly. The way this can be done varies depending on the client software. It is easier to add a CA certificate to the system store and mark it as trusted.
Therefore, instead of creating a self-signed certificate, we will create a temporary CA certificate and use it to sign the certificate used by the service. Afterwards we will delete the private key of the CA certificate, which will remove the ability to use it to create additional certificates. Afterwards we can import the CA certificate as trusted into the local system certificate store, and consequently every local client software that respects the system CA store will accept the service certificate as trusted.
Easy implementation with sscg
There is a simple tool in Fedora called sscg (Self-Signed Certificate Generator) that will create a certificate using the above mechanism for you. See the tog-pegasus example above for a simple invocation.
usage: sscg [-h] [--debug] [--cert-format {PEM,ASN1}] [--lifetime LIFETIME] [--key-strength {512,1024,2048,4096}] [--hash-alg {md4,md5,ripemd160,sha,sha1,sha224,sha256,sha384,sha512,whirlpool}] --package PACKAGE [--ca-file CA_FILE] --cert-file CERT_FILE --cert-key-file CERT_KEY_FILE [--hostname HOSTNAME] [--subject-alt-names SUBJECT_ALT_NAMES [SUBJECT_ALT_NAMES ...]] [--country COUNTRY] [--state STATE] [--locality LOCALITY] [--organization ORGANIZATION] [--organizational-unit ORGANIZATIONAL_UNIT] Generate a self-signed service certificate optional arguments: -h, --help show this help message and exit --debug Enable logging of debug messages. Output: --cert-format {PEM,ASN1} Certificate file format. Default=PEM --lifetime LIFETIME Certificate lifetime (days). Default=3650 (10 years) --key-strength {512,1024,2048,4096} Strength of the certificate private keys in bits. Default=2048 --hash-alg {md4,md5,ripemd160,sha,sha1,sha224,sha256,sha384,sha512,whirlpool} Hashing algorithm to use for signing. Default=sha256 --package PACKAGE The name of the package needing a certificate --ca-file CA_FILE Path where the public CA certificate will be stored. Default: ca.crt in service certificate directory --cert-file CERT_FILE Path where the public service certificate will be stored. --cert-key-file CERT_KEY_FILE Path where the private key of the service certificate will be stored Certificate Details: --hostname HOSTNAME The valid hostname of the certificate. Must be an FQDN. Default: system hostname --subject-alt-names SUBJECT_ALT_NAMES [SUBJECT_ALT_NAMES ...] One or more additional valid hostnames for the certificate --country COUNTRY Certificate DN: Country (C) --state STATE Certificate DN: State (ST) --locality LOCALITY Certificate DN: Locality (L) --organization ORGANIZATION Certificate DN: Organization (O) --organizational-unit ORGANIZATIONAL_UNIT Certificate DN: Organizational Unit (OU)