Skip to content

Commit

Permalink
feat: add step_certificate role.
Browse files Browse the repository at this point in the history
This role can accommodate all different types of provisioners.
For the currently implementation, only:
  - ACME
  - JWK
If the requested provisioner is not supported,
role will fail with appropriate message.

If this role is adopted, suggest we deprecate `step_acme_cert`.

FIXES: maxhoesel-ansible#127
  • Loading branch information
eengstrom committed Oct 6, 2021
1 parent c88d501 commit 706d2d8
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 140 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
---
**NOTE**

This collection is stil under active development. While we try to preserve compatibility with previous versions,
breaking changes may ocurr between minor releases (such as 0.3, 0.4, etc.) if required.
This collection is still under active development. While we try to preserve compatibility with previous versions,
breaking changes may occur between minor releases (such as 0.3, 0.4, etc.) if required.

---

Expand All @@ -18,7 +18,7 @@ and the [CLI tool](https://github.com/smallstep/cli). Possible uses for this col
- Managing your `step-ca` server install (installation, configuration, provisioners)
- Automated bootstrapping of hosts to trust your CA
- Token or certificate creation from within your Ansible playbooks
- [Complete configuration of client certificates via ACME, including automatic renewal](roles/step_acme_cert/README.md)
- [Complete configuration of client certificates, including automatic renewal](roles/step_certificate/README.md)


## Components
Expand All @@ -29,9 +29,9 @@ and the [CLI tool](https://github.com/smallstep/cli). Possible uses for this col
|------|-------------|
| [`step_ca`](roles/step_ca/README.md) | Install step-ca as an internal CA.
| [`step_bootstrap_host`](roles/step_bootstrap_host/README.md) | Configure a client host to trust your CA using step-cli.
| [`step_acme_cert`](roles/step_acme_cert/README.md) | Set up a Let's Encrypt-style certificate on a host using your ca, including automatic renewal.
| [`step_cli`](roles/step_cli/README.md) | Install step-cli and nothing else. Used by bootstrap_host and step_ca under the hood.

| [`step_certificate`](roles/step_certificate/README.md) | Create a new host certificate from a CA, including automatic renewal. Supports multiple provisioners.
| [`step_acme_cert`](roles/step_acme_cert/README.md) | (**DEPRECATED**) Create a host certificate from a CA, including automatic renewal, via ACME provisioner.

### Standalone Modules

Expand All @@ -48,10 +48,10 @@ To learn more about the differences between Online/Offline/Local-Only Modules, s

| Module | Description | Online | Offline/Local |
|---------|-------------|--------|---------------|
| `step_ca_bootstrap` | Initialize `step-cli` to trust a step-ca server | X | |
| `step_ca_bootstrap` | Initialize `step-cli` to trust a `step-ca` server | X | |
| `step_ca_certificate` | Generate a new private key and certificate signed by the CA root certificate | X | `offline` parameter |
| `step_ca_provisioner` | Manage provisioners on a `step-ca` server | | X |
| `step_ca_provisioner_claims` | Manage default or provisioner claims on a `step-ca server | | X |
| `step_ca_provisioner_claims` | Manage default or provisioner claims on a `step-ca` server | | X |
| `step_ca_renew` | Renew a valid certificate | X | `offline` parameter |
| `step_ca_revoke` | Revoke a Certificate | X | `offline` parameter |
| `step_ca_token` | Generate an OTT granting access to the CA | X | `offline` parameter |
Expand Down Expand Up @@ -169,7 +169,7 @@ You can take a look at the available modules to further configure your CA and ho

## Module Usage

This collection contains several modules for mamaging your smallstep environment.
This collection contains several modules for managing your smallstep environment.
Most of them wrap around `step-cli` commands, so they usually support all the features of the respective command.

If you'd like to know more about an individual module, you can view its documentation using `ansible-doc maxhoesel.smallstep.<step_module_name>`.
Expand All @@ -187,7 +187,7 @@ See [this table](#ca-modules) for details.

In order to talk to your CA in online mode, `step-cli` needs to already trust it. You can achieve this by:
- Running the module on a host that was configured with `step_bootstrap_host` as root (recommended).
- Pasing the `ca_url` and `root` parameters to the module.
- Passing the `ca_url` and `root` parameters to the module.

For offline mode, you need to:
- Provide `step-cli` with the path to your CA config (`$STEPPATH/config/ca.json` by default).
Expand Down
132 changes: 99 additions & 33 deletions roles/step_certificate/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# maxhoesel.smallstep.step_acme_cert
# maxhoesel.smallstep.step_certificate

Get a certificate from a CA with ACME and setup automatic renewal using `step-cli renew`.

Expand Down Expand Up @@ -30,36 +30,51 @@ before setting up a renewal service using `step-cli ca renew`s `--daemon` mode.

### CA

##### `step_acme_cert_ca_provisioner`
- Name of the provisioner on the CA that will issue the ACME cert
##### `step_cert_ca_provisioner_type`
- Type of provisioner on the CA that will issue the certificate
- Required: Yes

##### `step_acme_cert_webroot_path`
- If set, this role will use `step-cli`s webroot mode to get a new certificate.
- If empty, this role will use the standalone mode instead, causing `step-cli` to bind itself to port 80. Make sure that no other services are listening on this port.
##### `step_cert_ca_provisioner_name`
- Name of the provisioner on the CA that will issue the certificate
- Required: Yes


### Provisioner

#### ACME

##### `step_cert_acme_webroot_path`
- If set, the ACME provisioner will use `step-cli`s webroot mode to get a new certificate.
- If empty, the ACME provisioner will use the standalone mode instead, causing `step-cli` to bind itself to port 80. Make sure that no other services are listening on this port.
Note that `step-cli` only needs to bind to this port when getting a *new* certificate. It does not need to bind if it is only *renewing* a certificate.
- Default: ""

#### JWK

##### `step_cert_ca_jwk_password_file`
- Path to the file on the client system containing the password used to decrypt the one-time token generating key from a JWK provisioner on the CA.
- Required: Yes, if using a JWK provisioner.

### Certificate

##### `step_acme_cert_name`
##### `step_cert_name`
- The subject name that the certificate will be issued for
- Default: `{{ ansible_fqdn }}`

##### `step_acme_cert_san`
##### `step_cert_san`
- Subject Alternate Names to add to the cert
- Must be a list of valid SANs
- Default: `[]`

##### `step_acme_cert_duration`
##### `step_cert_duration`
- Valid duration of the certificate
- Default: undefined (uses the default for the given provisioner, typically 24h)

##### `step_acme_cert_contact`
##### `step_cert_contact`
- Contact email for the CA for important notifications
- Default: `root@localhost`

##### `step_acme_cert_certfile`/`step_acme_cert_keyfile`
##### `step_cert_certfile`/`step_cert_keyfile`
- Details about the cert/key files on disk
- Is a dict with the following elements:
- `path`: Absolute path to the cert/key file. Defaults to `/etc/ssl/step.crt|step.key`. The directory must already exist.
Expand All @@ -68,30 +83,26 @@ before setting up a renewal service using `step-cli ca renew`s `--daemon` mode.

### Renewal

##### `step_acme_cert_renewal_service`
- Name of the systemd service that will handle cert renewals
- If you have multiple cert/key pairs on one system, you will have to set a unique service name for each pair. If you only have one, then you can leave this as is.
##### `step_cert_renewal_service`
- Name of the `systemd` service that will handle cert renewals
- If you have multiple cert/key pairs on one system, you will have to set a unique service name for each pair.
- Default: `step-renew`

##### `step_acme_cert_renewal_when`
##### `step_cert_renewal_when`
- Renew the cert when its remaining valid time crosses this threshold
- Default: undefined (uses the smallstep default: 1/3 of the certificates valid duration, i.e. 8 hours for a 24h cert)
- Default: undefined (uses the smallstep default: 1/3 of the certificates valid duration, e.g. 8 hours for a 24h cert)

##### `step_acme_cert_renewal_reload_services`
- Reload or restart these systemd services after a cert renewal
- Must be a list of systemd units
##### `step_cert_renewal_reload_services`
- Reload or restart these `systemd` services after a cert renewal
- Must be a list of `systemd` units
- Example: `["nginx", "mysqld"]`
- Default: `[]`

## Example Playbooks

---
**NOTE**
### ACME

Make sure that you are familiar with the way ACME works. You will need a functioning DNS environment at the very least
to make use of ACME certs.

---
Make sure that you are familiar with the way ACME certificate generation works. Minimally, to make use of the ACME provisioners, you will need open network ports (e.g. `:80`) between the client and the server, and a functioning DNS environment.

```yaml
# Configure your CA to include an ACME provisioner
Expand All @@ -110,7 +121,8 @@ to make use of ACME certs.
name: step-ca
state: reloaded

- hosts: clients
# Create a certificate using an ACME provisioner
- hosts: step_clients
tasks:
# Bootstrap the host to trust the CA
- role: maxhoesel.smallstep.step_bootstrap_host
Expand All @@ -119,13 +131,67 @@ to make use of ACME certs.
step_bootstrap_fingerprint: your CAs fingerprint
become: yes

# This will download a certificate to /etc/step/ that you can then use in other applications.
# See the step_acme_cert README for more options
- name: Configure an ACME cert + renewal
include_role:
name: maxhoesel.smallstep.step_acme_cert
vars:
step_acme_cert_ca_provisioner: ACME
# Configure an ACME provisioned cert + renewal in /etc/step
- role: maxhoesel.smallstep.step_certificate
vars:
step_cert_ca_provisioner_type: ACME
step_cert_ca_provisioner_name: ACME
become: yes
```
### JWK
When using a JWK provisioner, you will need a shared secret between the CA server and the CA clients. This file must be placed on the system before you attempt to generate a certificate.
```yaml
# Configure your CA to include a JWK provisioner
- hosts: step_ca
become: yes
tasks:
- name: step-ca | deploy CA JWK provisioner password
copy:
dest: "{{ step_jwk_provisioner_password_file }}"
content: "SUPER SECRET {{ step_provisioner_token_password_secret_thing }}"
owner: step-ca
group: step-ca
mode: 0600

- name: step-ca | configure JWK provisioner on the CA
maxhoesel.smallstep.step_ca_provisioner:
type: JWK
name: "JWK@{{ ansible_domain }}"
jwk_password_file: "{{ step_jwk_provisioner_password_file }}"
become_user: step-ca
notify: reload step-ca

handlers:
- name: reload step-ca
systemd:
name: step-ca
state: reloaded

# Create a certificate using a JWK provisioner
- hosts: step_clients
tasks:
# Bootstrap the host to trust the CA
- role: maxhoesel.smallstep.step_bootstrap_host
vars:
step_bootstrap_ca_url: https://myca.localdomain
step_bootstrap_fingerprint: your CAs fingerprint
become: yes

- name: step-client | deploy CA JWK provisioner password
copy:
dest: "{{ step_jwk_provisioner_password_file }}"
content: "SUPER SECRET {{ step_provisioner_token_password_secret_thing }}"
mode: 0600
owner: "{{ system_root_user }}"
group: "{{ system_root_group }}"

# Configure a JWK provisioned cert + renewal in /etc/step
- role: maxhoesel.smallstep.step_certificate
vars:
step_cert_ca_provisioner_type: JWK
step_cert_ca_provisioner_name: "JWK@{{ ansible_domain }}"
step_cert_ca_jwk_password_file: "{{ step_jwk_provisioner_password_file }}"
```
31 changes: 17 additions & 14 deletions roles/step_certificate/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,31 @@
step_cli_executable: step-cli
step_cli_steppath: /root/.step

#step_acme_cert_ca_url:
#step_acme_cert_ca_provisioner:
step_acme_cert_webroot_path: ""
# Required parameters - role will error if either is not provided
#step_cert_ca_provisioner_type:
#step_cert_ca_provisioner_name:

step_acme_cert_name: "{{ ansible_fqdn }}"
step_acme_cert_san: []
#step_acme_cert_duration: 24h
step_acme_cert_contact: root@localhost
# path to ACME provisioner web root for HTTP-01 challenge.
step_cert_acme_webroot_path: ""

step_acme_cert_certfile: "{{ step_acme_cert_certfile_defaults }}"
step_acme_cert_certfile_defaults:
step_cert_name: "{{ ansible_fqdn }}"
step_cert_san: []
step_cert_contact: "root@{{ ansible_fqdn }}"
#step_cert_duration: 24h

step_cert_certfile: "{{ step_cert_certfile_defaults }}"
step_cert_certfile_defaults:
path: /etc/ssl/step.crt
mode: "644"
owner: root
group: root
step_acme_cert_keyfile: "{{ step_acme_cert_keyfile_defaults }}"
step_acme_cert_keyfile_defaults:
step_cert_keyfile: "{{ step_cert_keyfile_defaults }}"
step_cert_keyfile_defaults:
path: /etc/ssl/step.key
mode: "600"
owner: root
group: root

step_acme_cert_renewal_service: step-renew
#step_acme_cert_renewal_when: 8h
step_acme_cert_renewal_reload_services: []
step_cert_renewal_service: step-renew
#step_cert_renewal_when: 8h
step_cert_renewal_reload_services: []
2 changes: 1 addition & 1 deletion roles/step_certificate/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: restart renewal service
service:
name: '{{ step_acme_cert_renewal_service }}'
name: '{{ step_cert_renewal_service }}'
state: restarted
Loading

0 comments on commit 706d2d8

Please sign in to comment.