salt.states.ssh_pki

Manage OpenSSH certificates

New in version 3008.0.

depends:

cryptography

Configuration instructions and general remarks are documented in the execution module docs.

About

This module can enable managing a complete SSH PKI infrastructure, including creating private keys, CAs and certificates. It includes the ability to generate a private key on a server, and have the corresponding public key sent to a remote CA to create a CA-signed certificate. This can be done in a secure manner, where private keys are always generated locally and never moved across the network.

Note

In addition to the native Salt backend (the ssh_pki execution module), you can have the state module call a different (compatible) execution module using the backend parameter.

Example

Here is a simple example scenario. In this example, ca is the CA server and www is a web server that needs a certificate signed by ca.

Note

Remote signing using the native Salt backend requires the setup of Peer Communication and signing policies. Please see the execution module docs.

/srv/salt/top.sls

base:
  '*':
    - cert
  'ca':
    - ca
  'www':
    - www

This state creates the CA key and signing policy. It also publishes the public key to the mine, where it can be easily retrieved by other minions.

# /srv/salt/ca.sls

Ensure SSH PKI directories exist:
  file.directory:
    - name: /etc/pki/ssh/issued_certs
    - makedirs: true

Create CA private key:
  ssh_pki.private_key_managed:
    - name: /etc/pki/ssh/ca.key
    - algo: ed25519
    - backup: true
    - require:
      - file: /etc/pki/ssh/issued_certs

Write CA public key:
  ssh_pki.public_key_managed:
    - name: /etc/pki/ssh/ca.pub
    - public_key_source: /etc/pki/ssh/ca.key
    - require:
      - ssh_pki: /etc/pki/ssh/ca.key
# /srv/salt/ssh_pki.conf

# publish the CA certificate to the mine
mine_functions:
  ssh_ca:
    - mine_function: ssh_pki.get_public_key
    - /etc/pki/ssh/ca.key

# define at least one signing policy for remote signing
ssh_signing_policies:
  www_host:
    - minions: 'www'
    - signing_private_key: /etc/pki/ssh/ca.key
    - cert_type: host
    - ttl: 7d
    - copypath: /etc/pki/ssh/issued_certs/

This example state will instruct minion SSH servers to trust certificates signed by our new CA. Mind that the specifics depend on the OS.

# /srv/salt/cert.sls

Write the trusted CA file:
  file.managed:
    - name: /etc/ssh/trusted-user-ca-keys.pem
    - contents: {{ salt["mine.get"]("ca", "ssh_ca")["ca"] | json }}
    - user: root
    - group: root

Ensure SSH is configured to trust the CA:
  file.managed:
    - name: /etc/ssh/sshd_config.d/salt_ca_trust.conf
    - contents: |
        TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
    - require:
      - file: /etc/ssh/trusted-user-ca-keys.pem

This state creates a private key to use as the host key, then requests a certificate signed by our CA according to the www_host policy and configures the SSH server to use it.

# /srv/salt/www.sls

Create host private key:
  ssh_pki.private_key_managed:
    - name: /etc/ssh/ssh_host_rsa_key
    - algo: ed25519
    - backup: true

Request certificate:
  ssh_pki.certificate_managed:
    - name: /etc/ssh/ssh_host_rsa_key.pub
    - ca_server: ca
    - signing_policy: www_host
    - private_key: /etc/ssh/ssh_host_rsa_key
    - backup: true
    - require:
      - ssh_pki: /etc/ssh/ssh_host_rsa_key

Ensure SSH is configured to use the certificate:
  file.managed:
    - name: /etc/ssh/sshd_config.d/host_cert.conf
    - contents: |
        HostKey /etc/ssh/ssh_host_rsa_key
        HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
    - require:
      - ssh_pki: /etc/ssh/ssh_host_rsa_key
      - ssh_pki: /etc/ssh/ssh_host_rsa_key.pub
salt.states.ssh_pki.certificate_managed(name, ttl_remaining=None, ca_server=None, backend=None, backend_args=None, signing_policy=None, copypath=None, cert_type=None, signing_private_key=None, signing_private_key_passphrase=None, public_key=None, private_key=None, private_key_passphrase=None, serial_number=None, not_before=None, not_after=None, ttl=None, critical_options=None, extensions=None, valid_principals=None, all_principals=False, key_id=None, **kwargs)

Ensure an OpenSSH certificate is present as specified.

This function accepts the same arguments as ssh_pki.create_certificate, as well as most ones for :py:func:`file.managed <salt.states.file.managed>.

Note

Since file.managed also has an encoding param, it can be passed as file_encoding instead.

name

The path the certificate should be present at.

ttl_remaining

The certificate will be recreated once the remaining certificate validity period is less than this number of seconds. Can also be specified as a time string like 12d or 1.5h. Defaults to 30d for host keys and 1h for user keys.

ca_server

Request a remotely signed certificate from another minion acting as a CA server via the ssh_pki execution module. For this to work, a signing_policy must be specified, and that same policy must be configured on the ca_server. Also, the Salt master must permit peers to call the ssh_pki.sign_remote_certificate function. See the execution module docs for details.

backend

Instead of using the ssh_pki execution module for certificate creation, use this backend. It must provide a compatible API for create_certificate and get_signing_policy.

backend_args

If backend is specified, pass these additional keyword arguments to it. Must be a mapping (dict).

signing_policy

The name of a configured signing policy. Parameters specified in there are hardcoded and cannot be overridden. This is required for remote signing, otherwise optional.

copypath

Create a copy of the issued certificate in this directory. The file will be named <serial_number>.crt.

cert_type

The certificate type to generate. Either user or host. Required if not specified in the signing policy.

private_key

The private key corresponding to the public key the certificate should be issued for. Either this or public_key is required.

private_key_passphrase

If private_key is specified and encrypted, the passphrase to decrypt it.

public_key

The public key the certificate should be issued for. Either this or private_key is required.

signing_private_key

The private key of the CA that should be used to sign the certificate. Required.

signing_private_key_passphrase

If signing_private_key is encrypted, the passphrase to decrypt it.

serial_number

A serial number to be embedded in the certificate. If unspecified, will autogenerate one. This should be an integer, either in decimal or hexadecimal notation.

not_before

Set a specific date the certificate should not be valid before. The format should follow %Y-%m-%d %H:%M:%S and will be interpreted as GMT/UTC. Defaults to the time of issuance.

not_after

Set a specific date the certificate should not be valid after. The format should follow %Y-%m-%d %H:%M:%S and will be interpreted as GMT/UTC. If unspecified, defaults to the current time plus ttl.

ttl

If not_after is unspecified, a time string (like 30d or 12h) or the number of seconds from the time of issuance the certificate should be valid for. Defaults to 30d for host certificates and 24h for client certificates.

critical_options

A mapping of critical option name to option value to set on the certificate. If an option does not take a value, specify it as true.

extensions

A mapping of extension name to extension value to set on the certificate. If an extension does not take a value, specify it as true.

valid_principals

A list of valid principals.

all_principals

Allow any principals. Defaults to false.

key_id

Specify a string-valued key ID for the signed public key. When the certificate is used for authentication, this value will be logged in plaintext.

salt.states.ssh_pki.certificate_managed_ssh(name, result, comment, changes, encoding=None, contents=None, **kwargs)

Helper for the SSH wrapper module. This receives a certificate and dumps the data to the target. A file.managed sub-state run will be performed.

salt.states.ssh_pki.private_key_managed(name, algo='rsa', keysize=None, passphrase=None, new=False, overwrite=False, **kwargs)

Ensure a private key is present as specified.

This function accepts the same arguments as ssh_pki.create_private_key, as well as most ones for file.managed.

Note

If mode is unspecified, it will default to 0400.

name

The path the private key should be present at.

algo

The digital signature scheme the private key should be based on. Available: rsa, ec, ed25519. Defaults to rsa.

keysize

For rsa, specifies the bitlength of the private key (2048, 3072, 4096). For ec, specifies the NIST curve to use (256, 384, 521). Irrelevant for ed25519. Defaults to 2048 for RSA and 256 for EC.

passphrase

If this is specified, the private key will be encrypted using this passphrase. The encryption algorithm cannot be selected, it will be determined automatically as the best available one.

new

Always create a new key. Defaults to false. Combining new with prereq can allow key rotation whenever a new certificate is generated.

overwrite

Overwrite an existing private key if the provided passphrase cannot decrypt it. Defaults to false.

Example:

The Jinja templating in this example ensures a new private key is generated if the file does not exist and whenever the associated certificate is to be renewed.

Manage www private key:
  ssh_pki.private_key_managed:
    - name: /root/.ssh/www
    - keysize: 4096
    - new: true
{%- if salt["file.file_exists"]("/root/.ssh/www") %}
    - prereq:
      - ssh_pki: /root/.ssh/www.crt
{%- endif %}
salt.states.ssh_pki.private_key_managed_ssh(name, result, comment, changes, tempfile=None, **kwargs)

Helper for the SSH wrapper module to report the correct return and perform a file.managed sub-state run.

salt.states.ssh_pki.public_key_managed(name, public_key_source, passphrase=None, **kwargs)

Ensure a public key is present as specified.

This function accepts most arguments for file.managed.

Note

If mode is unspecified, it will default to 0400.

name

The path the public key should be present at.

public_key_source

The certificate (or any reference that can be passed to get_public_key) to retrieve the public key from.

passphrase

If public_key_source is an encrypted private key, specify its passphrase here.