Configuring step-ca
Provisioners
Provisioners are methods of using the CA to get certificates for humans or machines. They offer different modes of authorization for the CA. In this section we'll discuss the different provisioners, their target use cases, and how to add, remove, and configure them.
Overview
- Choosing a Provisioner
- Authorization Scope by Provisioner
- Provisioner Management
- Remote Provisioner Management
- Provisioner Types
Choosing a Provisioner
When choosing a provisioner for your use case, you can narrow your options by starting from the types of identifiers you need on a certificate.
Identifier Type | JWK | OAuth | X5C | ACME dns-01 | ACME http-01 | ACME tls-alpn-01 | ACME device-attest-01 | Nebula | SCEP | K8sSA | Cloud Provisioners |
---|---|---|---|---|---|---|---|---|---|---|---|
Hostname | ✔️ | 𝗫 | ✔️ | ✔️ | ✔️ | ✔️ | 𝗫 | ✔️ | ✔️ | 𝗫 | ✔️ |
IP address | ✔️ | 𝗫 | ✔️ | 𝗫 | ✔️ | ✔️ | 𝗫 | ✔️ | ✔️ | 𝗫 | ✔️ |
Hardware ID | ✔️ | 𝗫 | ✔️ | 𝗫 | 𝗫 | 𝗫 | ✔️ | 𝗫 | ✔️ | 𝗫 | 𝗫 |
Service Account | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | ✔️ | ✔️ | 𝗫 |
Human | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
Authorization Scope by Provisioner
Every provisioner has a slightly different scope of authorization. Below is a table detailing the authorization capabilities of each provisioner.
Provisioner Capabilities | x509-sign | x509-renew | x509-revoke | ssh-user-cert-sign | ssh-host-cert-sign | ssh-user-cert-renew1 | ssh-host-cert-renew | ssh-revoke | ssh-rekey |
---|---|---|---|---|---|---|---|---|---|
JWK | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | ✔️ | 𝗫 |
OIDC | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ 2 | 𝗫 | 𝗫 | ✔️ | 𝗫 |
X5C | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
Nebula | ✔️ | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | ✔️ | 𝗫 |
K8sSA | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
ACME | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
SCEP | ✔️ | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
SSHPOP | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | 𝗫 | ✔️ | ✔️ | ✔️ |
AWS | ✔️ | ✔️ | 𝗫 | 𝗫 | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
Azure | ✔️ | ✔️ | 𝗫 | 𝗫 | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
GCP | ✔️ | ✔️ | 𝗫 | 𝗫 | ✔️ | 𝗫 | 𝗫 | 𝗫 | 𝗫 |
For an example of how to interpret this table, let's take the JWK
provisioner.
The JWK
provisioner is capable of signing, renewing, and revoking X.509
certificates, as well signing user and host SSH certificates. A JWK
provisioner
cannot renew or rekey SSH certificates.
An SSHPOP
provisioner can revoke and rekey SSH certificates and renew SSH host
certificates. An SSHPOP
provisioner cannot sign, renew, or revoke X.509
certificates, and it cannot sign SSH user and host certificates or renew SSH
user certificates.
It's important to understand the capabilities and limitations when selecting a provisioner for a given workload.
Provisioner Management
Use the step ca provisioner
command group to add, remove, or modify provisioner configurations. Run these commands directly on your CA machine. They need to modify the $(step path)/config/ca.json
configuration file.
Some provisioner options override global defaults for your CA.
For a list of global options, see the configuration guide section for the authority
configuration block.
A remote provisioner management API can be enabled in step-ca
.
It is disabled by default.
With remote provisioner management, the CA's provisioner configuration is stored in the database instead of ca.json
. (The global CA configuration remains in ca.json
.)
This feature can be useful if you
have multiple CA administrators,
run several load-balanced step-ca
instances,
or if you want to manage your provisioners remotely (eg. with Infrastructure as Code (IaC) tools; see below).
See Remote Provisioner Management for more.
Common Provisioner Operations
Common provisioner operations include:
- Adding a provisioner
- Removing a provisioner
- Listing all provisioner
- Modifying the configuration of an existing provisioner
Unless you are using remote provisioner management, you must send a SIGHUP
signal, or restart the step-ca
process, for changes to your provisioner configuration to take effect.
Add a provisioner
Use step ca provisioner add
to add a provisioner:
step ca provisioner add acme --type ACME
See the command reference for complete documentation and examples.
Remove a provisioner
Use step ca provisioner remove
to remove provisioners:
step ca provisioner remove acme
You can also edit the ca.json
configuration file and remove the entire block
containing the provisioner you'd like to remove.
List all provisioners
To get a list of all of your current provisioners, use step ca provisioner list
or the /provisioners
endpoint on your CA.
Modify a provisioner
Use step ca provisioner update
to update provisioner configurations:
step ca provisioner update acme \
--x509-min-dur=20m \
--x509-max-dur=72h \
--x509-default-dur=36h
In this example we've modified the minumum, maximum, and default durations for TLS certificates generated by this provisioner.
Provisioner configuration can be used to affect X.509 and SSH certificate lifetimes, extensions, and templates. There are some provisioner-specific options, which are covered by the documentation for each provisioner type, below.
Remote Provisioner Management
This feature is disabled by default.
When remote provisioner management is enabled, your provisioner configuration is
stored in the database, rather than in ca.json
.
Provisioner configuration is managed by running step ca provisioner
commands locally or remotely.
These commands require you to sign in as an Admin user.
Enable Remote Provisioner Management
On a New CA
To enable remote provisioner management on a new CA, pass --remote-management
to step ca init
. This will:
- Add an initial JWK provisioner called Admin JWK to the database.
- Create an initial Super Admin user, with default username
step
, and link it to the Admin JWK provisioner.
On an Existing CA
To migrate your ca.json
provisioners to the database, and to enable remote provisioner management on an existing CA:
- Stop
step-ca
if it is running. - Update your
"authority"
block inca.json
to include the following:
"authority": {
"enableAdmin": true,
"provisioners": [
...
]
},
-
Start
step-ca
from the command line.When
step-ca
starts up, it will:- Migrate the provisioners from your
ca.json
to the database - Repurpose the first JWK provisioner in your
ca.json
as an administrative provisioner. If no JWK provisioner exists, it will add a JWK provisioner called Admin JWK to the database. You will be prompted for a password that will encrypt the new provisioner key. - Create an initial Super Admin user, with username
step
, and link it to the administrative provisioner.
- Migrate the provisioners from your
-
Remove old provisioner configurations from
ca.json
. Once your provisioners are migrated to the database, the provisioners inca.json
are ignored and you can remove them from that file:"authority": { "enableAdmin": true, "provisioners": [] },
Managing Admin Users
With remote provisioner management,
"Admins" can remotely manage provisioners.
"Super Admins" are Admins that can also manage the list of Admins for the CA using step ca admin
.
Create An Admin User
To perform CA administrative operations, you sign into that provisioner as an Admin.
As a Super Admin, lets create an Admin user linked to the Admin JWK provisioner:
step ca admin add carl "Admin JWK"
Output:
SUBJECT PROVISIONER TYPE
carl Admin JWK (JWK) ADMIN
Authenticating as an admin
An Admin (or a Super Admin) is a combination of a username (a subject name) and a provisioner. There are no passwords associated with administrators. An administrator is just a subject name and a provisioner name.
The default Super Admin username is step
, and the default admin provisioner is called Admin JWK
.
You can authenticate as an administrator via the authentication scheme of the provisioner.
For example, for the default JWK provisioner, running administrative commands in step
will prompt for the provisioner password.
Tip: Single Sign-On can be used for Admin users. To enable SSO CA administration, first add an OIDC provisioner linked to your identity provider. Then, add an Admin user associated with that provisioner, using your email address as the Admin's subject name.
The Admin and Super Admin privileges are not scoped to a provisioner: Admins can modify any provisioner configurations. Super Admins are Admins that can also modify Admins, regardless of provisioner.
Use Remote Provisioner Management
You're all set. 🎉
You can use the step ca provisioner
commands, from any client, to modify your CA's provisioner configuration.
Unattended Remote Provisioner Management
If you want to manage provisioners remotely using a script or an Infrastructure as Code (IaC) tool, you will need an administrative certificate and key.
An administrative certificate must meet the following criteria:
- The Subject (or any SAN) must match the name of an Admin or Super Admin.
- The certificate must be issued by the provisioner of the Admin or Super Admin.
- The certificate must be valid and unexpired.
- A passively revoked certificate is valid until it expires; for immediate revocation, remove the admin instead of revoking the certificate.
To create an admin certificate, run:
step ca certificate step admin.crt admin.key --provisioner "Admin JWK"
Output:
✔ Provisioner: Admin JWK (JWK) [kid: 5UasaZcgkI_PxQeZqssfq5mzFnGHOWL5g5kDt07Vc1k]
Please enter the password to decrypt the provisioner key:
✔ CA: https://localhost
✔ Certificate: admin.crt
✔ Private Key: admin.key
With that certificate, you can use it as follows:
step ca provisioner add ... --admin-cert=admin.crt --admin-key=admin.key
If the admin.crt
represents a Super Admin, the same flags can also be used to manage Admins:
step ca admin add ... --admin-cert=admin.crt --admin-key=admin_key
Provisioner Types
JWK
JWK is the default provisioner type. It uses public-key cryptography to sign and validate a JSON Web Token (JWT).
The step
CLI tool will create a JWK provisioner when step ca init
is used.
Example
Add a JWK provisioner:
step ca provisioner add you@smallstep.com --create
In the ca.json configuration file, a complete JWK provisioner example looks like:
{
"type": "JWK",
"name": "you@smallstep.com",
"key": {
"use": "sig",
"kty": "EC",
"kid": "NPM_9Gz_omTqchS6Xx9Yfvs-EuxkYo6VAk4sL7gyyM4",
"crv": "P-256",
"alg": "ES256",
"x": "bBI5AkO9lwvDuWGfOr0F6ttXC-ZRzJo8kKn5wTzRJXI",
"y": "rcfaqE-EEZgs34Q9SSH3f9Ua5a8dKopXNfEzDD8KRlU"
},
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiTlV6MjlEb3hKMVdOaFI3dUNjaGdYZyJ9.YN7xhz6RAbz_9bcuXoymBOj8bOg23ETAdmSCRyHpxGekkV0q3STYYg.vo1oBnZsZjgRu5Ln.Xop8AvZ74h_im2jxeaq-hYYWnaK_eF7MGr4xcZGodMUxp-hGPqS85oWkyprkQLYt1-jXTURfpejtmPeB4-sxgj7OFxMYYus84BdkG9BZgSBmMN9SqZItOv4pqg_NwQA0bv9g9A_e-N6QUFanxuYQsEPX_-IwWBDbNKyN9bXbpEQa0FKNVsTvFahGzOxQngXipi265VADkh8MJLjYerplKIbNeOJJbLd9CbS9fceLvQUNr3ACGgAejSaWmeNUVqbho1lY4882iS8QVx1VzjluTXlAMdSUUDHArHEihz008kCyF0YfvNdGebyEDLvTmF6KkhqMpsWn3zASYBidc9k._ch9BtvRRhcLD838itIQlw",
"claims": {
"minTLSCertDuration": "5m",
"maxTLSCertDuration": "24h",
"defaultTLSCertDuration": "24h",
"disableRenewal": false,
"minHostSSHCertDuration": "5m",
"maxHostSSHCertDuration": "1680h",
"minUserSSHCertDuration": "5m",
"maxUserSSHCertDuration": "24h",
"enableSSHCA": true
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
},
"ssh": {
"templateFile": "templates/certs/ssh/default.tpl"
}
}
}
-
type: for a JWK provisioner it must be
JWK
, this field is case insensitive. -
name: identifies the provisioner, a good practice is to use an email address or a descriptive string that allows the identification of the owner, but it can be any non-empty string.
-
key: is the JWK (JSON Web Key) representation of a public key used to validate a signed token.
-
encryptedKey*: is the encrypted private key used to sign a token. It's a JWE compact string containing the JWK representation of the private key. This value is not necessary for CA operation, but is provided for the convenience of clients. Without the
encryptedKey
attribute, the private key must be provided by the client, using the--key
flag. -
options*: see template configuration for more details.
Decrypting the private key
We can use step crypto jwe decrypt
to see the private key encrypted with the password asdf
:
$ step ca provisioner list \
| jq -r '.[] | select(.name == "you@smallstep.com") | .encryptedKey' \
| step crypto jwe decrypt \
| jq
Please enter the password to decrypt the content encryption key:
{
"use": "sig",
"kty": "EC",
"kid": "NPM_9Gz_omTqchS6Xx9Yfvs-EuxkYo6VAk4sL7gyyM4",
"crv": "P-256",
"alg": "ES256",
"x": "bBI5AkO9lwvDuWGfOr0F6ttXC-ZRzJo8kKn5wTzRJXI",
"y": "rcfaqE-EEZgs34Q9SSH3f9Ua5a8dKopXNfEzDD8KRlU",
"d": "rsjCCM_2FQ-uk7nywBEQHl84oaPo4mTpYDgXAu63igE"
}
Changing a JWK provisioner password
- Retrieve the current encrypted key.
Run the following, changing the provisioner name in the jq
command to match your configuration:
OLD_ENCRYPTED_KEY=$(step ca provisioner list \
| jq -r '.[] | select(.name == "you@smallstep.com").encryptedKey')
- Update the encrypted key password.
Run:
ENCRYPTED_KEY=$(echo $OLD_ENCRYPTED_KEY | \
step crypto jwe decrypt | \
step crypto jwe encrypt --alg PBES2-HS256+A128KW | \
step crypto jose format)
You'll be asked for the old and new passwords.
- Update the provisioner.
Run the following, changing the provisioner name in the command to match your configuration:
step ca provisioner update you@smallstep.com \
--private-key=<(echo -n "$ENCRYPTED_KEY")
- Send a
SIGHUP
tostep-ca
to reload the configuration file.
(Skip this step if you have enabled remote provisioner administration.)
killall -i -s SIGHUP step-ca
Rekeying a JWK provisioner
- Update the provisioner.
Run the following, changing the provisioner name in the command to match your configuration:
step ca provisioner update you@smallstep.com --create
- Send a
SIGHUP
tostep-ca
to reload the configuration file.
(Skip this step if you have enabled remote provisioner administration.)
killall -i -s SIGHUP step-ca
Removing the encrypted private key from a JWK provisioner
The encrypted private key stored in the JWK provisioner configuration and published to the public /provisioners
endpoint is provided for client convenience.
It is not required for step-ca
to operate.
To remove this key:
- Update the provisioner.
Run the following, changing the provisioner name in the command to match your configuration:
step ca provisioner update you@smallstep.com --private-key ""
- Send a
SIGHUP
tostep-ca
to reload the configuration file.
(Skip this step if you have enabled remote provisioner administration.)
killall -i -s SIGHUP step-ca
OAuth/OIDC Single Sign-on
Sometimes it's useful to issue certificates to people.
So step-ca
supports single sign-on with identity providers (IdPs) like Google, Okta, Microsoft Entra ID, Keycloak,
or any other provider that supports OAuth's OpenID Connect extension.
OpenID Connect is an extension to OAuth 2.0 that adds an identity layer. Providers that support OIDC can issue identity tokens ("ID tokens") to OAuth clients. These are JSON Web Tokens (JWTs) containing user identity information (eg. full name, username, email address). Like certificates, OIDC tokens have a validity period and are cryptographically signed by a trust authority (the OAuth provider).
Here's an example OIDC identity token issued by Google:
{
"alg": "RS256",
"kid": "cd49b2ab16e1e9a496c8239dac0dadd09d443012",
"typ": "JWT"
}.{
"iss": "https://accounts.google.com",
"azp": "1087160488420-8qt7bavg3qesdhs6it824mhnfgcfe8il.apps.googleusercontent.com",
"aud": "1087160488420-8qt7bavg3qesdhs6it824mhnfgcfe8il.apps.googleusercontent.com",
"sub": "115449349109627210866",
"hd": "smallstep.com",
"email": "mike@smallstep.com",
"email_verified": true,
"at_hash": "lE6o-GdMpurFQ0WrJ9-H7g",
"nonce": "5f5820880a43c3f50d55ce79af15430b14b4059bdf4efbe717da6af8bfc53122",
"iat": 1621877714,
"exp": 1621881314
}.[Signature]
The OIDC provisioner in step-ca
can be configured to trust and accept an OAuth provider's ID tokens for authentication.
By default, the issued certificate will use the subject (sub) claim from the identity token as its subject.
The value of the token's email claim is also included as an email SAN in the certificate.
Fig. 3: Diagram of how step works with individuals using a single sign-on provisioner
From the user's perspective, when requesting a certificate, step
detects the OIDC provisioner and initiates the OAuth login flow automatically:
$ step ca certificate mike@smallstep.com mike.crt mike.key
✔ Key ID: 650445034027-jsjdrkiskeq9ke99ud2rqkst82ft8uch.apps.googleusercontent.com (Google)
✔ CA: https://ca.internal
✔ Certificate: mike.crt
✔ Private Key: mike.key
$ step certificate inspect --short mike.crt
X.509v3 TLS Certificate (ECDSA P-256) [Serial: 2581...6739]
Subject: 115449349109627210866
mike@smallstep.com
Issuer: Smallstep Intermediate CA
Provisioner: Google [ID: 6504....com]
Valid from: 2019-06-20T18:21:52Z
to: 2019-06-21T18:21:52Z
Configuring your identity provider (IdP)
When creating an OAuth app, there isn't much to configure on the IdP.
Most providers will ask you to specify a Redirect URI, where the ID token will be delivered at the end of the OAuth flow.
Since step
starts its own local web server to receive the token, use http://127.0.0.1
as the Redirect URI.
Example: Google Identity
One of the most common providers, and the one used in the following example, is Google Identity.
Add a Google provisioner:
$ step ca provisioner add Google --type oidc \
--client-id 650445034027-jsjdrkiskeq9ke99ud2rqkst82ft8uch.apps.googleusercontent.com \
--client-secret 6Q7lGMua_Oox4nA92QBXYypT \
--configuration-endpoint https://accounts.google.com/.well-known/openid-configuration \
--domain smallstep.com --domain gmail.com
Example ca.json
provisioner configuration for a Google provisioner:
{
"type": "OIDC",
"name": "Google",
"clientID": "1087160488420-8qt7bavg3qesdhs6it824mhnfgcfe8il.apps.googleusercontent.com",
"clientSecret": "udTrOT3gzrO7W9fDPgZQLfYJ",
"configurationEndpoint": "https://accounts.google.com/.well-known/openid-configuration",
"admins": ["you@smallstep.com"],
"domains": ["smallstep.com"],
"listenAddress": ":10000",
"claims": {
"maxTLSCertDuration": "8h",
"defaultTLSCertDuration": "2h",
"disableRenewal": true
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
},
"ssh": {
"templateFile": "templates/certs/ssh/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be OIDC.
-
name: a string used to identify the provider when the CLI is used.
-
clientID: the client id provided by the identity provider used to initialize the authentication flow.
-
clientSecret: the shared secret provided by the identity provider; used to get the id token during the OAuth flow. Some identity providers may use an empty string as a secret. In the context of
step-ca
, the client "secret" is not actually a secret and is available via the CA's/provisioners
configuration endpoint, because everystep
client needs to use it locally. -
configurationEndpoint: is the HTTP address used by the CA to get the OpenID Connect configuration and public keys used to validate the tokens.
-
admins*: These privileged users may request certificates with arbitrary subject names (custom SANs). Non-privileged users may only request certificates for their email address.
-
domains*: is the list of domains valid. If provided only the emails with the provided domains will be able to authenticate.
-
listenAddress*: is the address (
:port
orhost:port
) where the authorization server will redirect the client's web browser at the end of the authorization flow. By default, thestep
client will bind to 127.0.0.1 on a random port. This parameter is only required if the authorization server demands a specific port for loopback IP redirect URIs. -
options*: see template configuration for more details.
Browserless Console Mode
For OpenID Connect, by default, step
opens a web browser to run the Authorization Code flow with PKCE.
Sometimes it's helpful to use OAuth in an input-constrained environment where no web browser is available.
The Device Authorization Grant flow (aka "Device Flow") is an OAuth 2.0 extension designed for this scenario.
The step
client supports the Device Authorization Grant flow.
To use the Device Authorization Grant flow for input-constrained devices, run:
$ step ca certificate foo foo.crt foo.key --console
or
$ STEP_CONSOLE=true step ssh certificate carl carl.crt
Notes
Further reading
- Tutorial: Use Keycloak to issue user certificates with step-ca
- Tutorial: Connect your identity provider and issue X.509 certificates for user authentication to services
X5C - X.509 Certificate
With the X5C provisioner, a client can authenticate a certificate request using an existing X.509 certificate from a different CA. It allows clients to use a different PKI to bootstrap trust. Configure this provisioner with a root CA certificate, and any certificate that chains up to that root can be used in a certificate request.
- Clients may request an X.509 or SSH certificate
- Clients must provide an X.509 certificate bundle whose root is trusted by the provisioner
- Clients sign their request with the certificate private key
- The validity period of the new certificate must fall within the validity period of the certificate used to authenticate the request
The X5C provisioner uses X5C tokens for authentication. An X5C token is a JWT, signed by the certificate private key, with an x5c
header that contains the certificate bundle.
Example
If you would like any certificate signed by step-ca
to become a provisioner
(have the ability to request new certificates with any name),
you can create an X5C provisioner using the root certificate used by
step-ca
, like so:
step ca provisioner add x5c-smallstep --type X5C --x5c-root $(step path)/certs/root_ca.crt
Or, you can configure the X5C provisioner with an outside root, granting provisioner capabilities to a completely separate PKI.
Below is an example of an X5C provisioner in the ca.json
:
...
{
"type": "X5C",
"name": "x5c",
"roots": "LS0tLS1 ... Q0FURS0tLS0tCg==",
"claims": {
"maxTLSCertDuration": "8h",
"defaultTLSCertDuration": "2h",
"disableRenewal": true
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
},
"ssh": {
"templateFile": "templates/certs/ssh/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
X5C
. -
name: a string used to identify the provider when the CLI is used.
-
roots: a base64 encoded list of root certificate PEM blocks used for validating X5C tokens.
-
options*: see template configuration for more details.
Nebula
This is an experimental feature.
If you have a Nebula overlay network, you can create a Nebula provisioner and configure it with your Nebula root CA certificate.
Clients can then use their Nebula client certificate and private key to request an X.509 or SSH host certificate from step-ca
.
The Nebula certificate they use for authentication must be issued by the Nebula root CA configured in the provisioner.
The Nebula client certificate is used for authorization, too:
The client is allowed to request an X.509 or SSH host certificate with the name
or any of the ips
appearing on the Nebula client certificate.
To be clear, Nebula certificates can contain a single name
and a list of ips
.
The name
field is often a DNS hostname, but it could be an email, IP, or URI.
And ips
contains a list of CIDR blocks.
In step-ca
, the Nebula provisioner will authorize certificate subjects or SANs that include the name
, plus IPs in any of the CIDR blocks in ips
on the Nebula certificate.
To get started, create a Nebula provisioner:
step ca provisioner add --type Nebula --nebula-root /etc/nebula/ca.crt
Now you can get an X509 certificate with the Nebula provisioner, using a Nebula client certificate. Here's an example using the DNS name host3.example.com
, and two IPs:
step ca certificate host3.example.com host3.crt host3.key \
--nebula-cert /etc/nebula/client.crt \
--nebula-key /etc/nebula/client.key \
--san 192.168.100.2 \
--san 192.168.100.3
SSHPOP - SSH Certificate
An SSHPOP provisioner allows a client to renew, revoke, or rekey an SSH certificate using that certificate for authentication with the CA. The renew and rekey methods can only be used on SSH host certificates.
An SSHPOP provisioner is configured with the user and host root ssh certificates
from the ca.json
. The SSHPOP provisioner can only authenticate SSHPOP tokens
generated using SSH certificates created by step-ca
.
An SSHPOP token is a JWT, signed by the certificate private key, with an sshpop
header that contains the SSH certificate.
When configured with the --ssh
option (step ca init --ssh
), the CA
will contain a default SSHPOP provisioner named sshpop
.
Example
Add an SSHPOP provisioner:
step ca provisioner add sshpop --type SSHPOP
An example SSHPOP provisioner in the ca.json
:
...
{
"type": "SSHPOP",
"name": "sshpop",
"claims": {
"enableSSHCA": true
},
"options": {
"ssh": {
"templateFile": "templates/certs/ssh/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
SSHPOP
. -
name: a string used to identify the provider when the CLI is used.
-
options*: see template configuration for more details.
ACME
The ACME Protocol can authenticate Certificate Signing Requests (CSRs) in a way that enables automation.
ACME clients must answer challenges presented by the ACME server to prove to the CA that they control the identifiers listed in the CSR.
ACME supports four different types of challenges: http-01
, dns-01
, tls-alpn-01
, and device-attest-01
. These are designed for operability in different environments.
See ACME Basics for a description of each challenge type and their tradeoffs.
In a typical setup, an ACME server might issue server certificates via the http-01
, dns-01
, tls-alpn-01
challenge types, and client certificates via the device-attest-01
challenge type.
The ACME provisioner in step-ca
supports issuing X.509 certificates using IP, hostname, and device identifiers.
Example
Add an ACME provisioner:
step ca provisioner add acme-example --type ACME
This will add an ACME server to step-ca.
ACME clients are commonly configured with an ACME directory URL.
If your CA is hosted at ca.internal
, and you run the above command, your ACME server directory URL will be https://ca.internal/acme/acme-example/directory
.
To configure popular ACME clients to use step-ca
, see our tutorial on the subject.
Here's an example of an ACME provisioner configuration in ca.json
:
...
{
"type": "ACME",
"name": "acme-example",
"forceCN": true,
"claims": {
"maxTLSCertDuration": "8h",
"defaultTLSCertDuration": "2h"
},
"termsOfService": "",
"website": "",
"caaIdentities": [],
"challenges": [
"http-01",
"dns-01",
"tls-alpn-01"
],
"attestationFormats": [
"apple",
"step",
"tpm"
],
"attestationRoots": [],
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
ACME
. -
name: a string used to identify the provider when the CLI is used.
-
forceCN*: force one of the SANs to become the Common Name, if a common name is not provided.
-
options*: see template configuration for more details.
-
termsOfService*: contains a URL pointing to the ACME server's terms of service. Defaults to empty.
-
website*: contains a URL pointing to more information about the ACME server. Defaults to empty.
-
caaIdentities*: an array of hostnames that the ACME server identifies itself with. These hostnames can be used by ACME clients to determine the correct issuer domain name to use when configuring CAA records. Defaults to empty array.
-
challenges*: which ACME challenge types are allowed. Note that
device-attest-01
is disabled by default. -
attestationFormats*: which attestation formats are allowed for
device-attest-01
challenges. Valid values areapple
,step
, andtpm
. By default, all formats are enabled. Theapple
format is for Apple devices, and adds trust for Apple's CAs. Thestep
format is for non-TPM devices that can issue attestation certificates, such as YubiKey PIV. It adds trust for Yubico's root CA. Thetpm
format is for TPMs. It does not trust any TPM vendor CAs by default. -
attestationRoots*: contains a trust bundle of root certificates in PEM format that will be used to verify attestation certificates. The default value depends on the value of attestationFormats. If provided, this PEM bundle will override the CA trust established by setting attestationFormats.
See ACME Basics
for more guidance on configuring and using the ACME protocol with step-ca
.
ACME for Device Attestation
The device-attest-01
challenge supports device attestations from iOS, iPadOS, tvOS, and YubiKeys. An attestation certificate is used to satisfy the ACME challenge. The CSR may include a device ID in a permanentIdentifier
SAN (RFC 4043), depending on the application.
Managed Device Attestation for Apple Devices
To add a device-attest-01
provisioner for use with Managed Device Attestation on Apple devices, run:
step ca provisioner add acme-da \
--type ACME \
--challenge device-attest-01 \
--attestation-format apple
In your Apple MDM profile, you will need:
- A
CertificateRoot
payload, containing your root CA certificate PEM block, so that it's trusted by the device. - An
ACMECertificate
payload. For this one, set theClientIdentifier
to the UDID or serial number of the device. - Set
HardwareBound
totrue
Device Attestation for YubiKeys
To add a device-attest-01
provisioner for YubiKey devices, run:
step ca provisioner add acme-da \
--type ACME \
--challenge device-attest-01 \
--attestation-format step
To get a client certificate for a hardware-bound private key on your YubiKey:
-
Create a
device-attest-01
provisioner as described above. -
Install
step-kms-plugin
-
Insert the YubiKey.
-
Generate a private key in one of the YubiKey's slots, if you haven't already. In this example, we'll use slot
9a
. Run:step kms create 'yubikey:slot-id=9a' \ --kms 'yubikey:?pin-value=123456' -
Get a certificate for the YubiKey from
step-ca
. Substitute the serial number of your YubiKey here:{ YUBIKEY_SERIAL_NO=123EXAMPLE90 step ca certificate --attestation-uri 'yubikey:slot-id=9a' \ --kms 'yubikey:?pin-value=123456' \ --provisioner acme-da $YUBIKEY_SERIAL_NO yubikey.crt }
SCEP
The SCEP provisioner can sign and renew certificates using the SCEP protocol (RFC8894). SCEP is very popular for use in network equipment and mobile device management (MDM). It runs over HTTP using POSTed binary data or base64-encoded GET parameters, using CMS (PKCS#7) and CSR (PKCS#10) data formats. A (shared) secret authenticates clients to the CA.
Requirements
Your CA must use an RSA intermediate CA, even if your client supports ECDSA.
The RSA intermediate is used to decrypt the contents of the SCEP pkcsPKIEnvelope
containing the certificate request.
This operation cannot be performed using an ECDSA key.
Because step ca init
creates an ECDSA chain by default, you will need to convert your CA to use an RSA CA chain before using the SCEP provisioner.
Configure the Provisioner
In this example, we will add a SCEP provisioner using challenge secret secret1234
and AES-256-CBC
as the encryption algorithm:
step ca provisioner add my_scep_provisioner \
--type SCEP --challenge "secret1234" \
--encryption-algorithm-identifier 2
The shared challenge
should be a value that you will distribute to your SCEP clients.
Here's an example of a SCEP provisioner in $(step path)/config/ca.json
:
{
"type": "SCEP",
"name": "scepca",
"forceCN": true,
"challenge": "secret1234",
"minimumPublicKeyLength": 2048,
"includeRoot": true,
"encryptionAlgorithmIdentifier": 2,
}
- The
forceCN
parameter is optional. It behaves the same asforceCN
in the ACME provisioner, and it defaults to false. challenge
is the secret shared between the provisioner and SCEP clients. By default no secret is used.- The
minimumPublicKeyLength
parameter can be used to set the minimum length of public keys submitted by a client. Defaults to 2048. - When
includeRoot
is set to true, the root CA certificate will be returned in responses toGetCACert
requests in addition to the intermediate CA certificate. This option was added to support a specific use case for the macOS SCEP client (see certificates#746 for more details). Defaults to false. - The
encryptionAlgorithmIdentifier
parameter can be used to change the encryption algorithm used for encrypting the request content. Defaults to 0:DES-CBC
for legacy compatibility.
If you don't want to rely on a single, static secret, you can configure a SCEPCHALLENGE
webhook instead.
When a SCEP client requests a certificate, the webhook server will receive a request with the scepChallenge
and scepTransactionID
properties from the SCEP request.
The webhook server can then decide if the request is allowed or not.
Enable the HTTP Server
By default SCEP will only be served via HTTPS.
Most SCEP clients use HTTP, so you will most likely need your CA to listen using HTTP too, which it does not do by default.
Enable this by filling in the "insecureAddress"
property to your top-level CA configuration:
...
"insecureAddress": ":8080",
...
Finally, restart step-ca
.
Your SCEP provisioner is now available at the endpoint http://ca.example.com:8080/scep/scepca
.
K8sSA - Kubernetes Service Account
A K8sSA provisioner allows a client to request a certificate from the server using a Kubernetes Service Account Token.
As of the time when this provisioner was coded, the Kubernetes Service Account API for retrieving the token from a running instance was still in beta. Therefore, our K8sSA provisioner must be configured with the public key that will be used to validate K8sSA tokens.
K8sSA tokens are very minimal. There is no place for SANs, or other details that a user may want validated in a CSR. It is essentially a bearer token. Therefore, at this time a K8sSA token can be used to sign a CSR with any SANs. Said differently, the K8sSA provisioner does little to no validation on the CSR before signing it. You should only configure and use this provisioner if you know what you are doing. If a malicious user obtains the private key they will be able to create certificates with any SANs and Subject.
Example
Add a K8sSA provsioner:
step ca provisioner add my-kube-provisioner --type K8sSA --pem-keys key.pub
An example of a K8sSA provisioner in the ca.json
:
...
{
"type": "K8sSA",
"name": "my-kube-provisioner",
"publicKeys": "LS0tLS1...LS0tCg==",
"claims": {
"maxTLSCertDuration": "8h",
"defaultTLSCertDuration": "2h"
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
K8sSA
. -
name: a string used to identify the provider when the CLI is used.
-
publicKeys: a base64 encoded list of public keys used to validate K8sSA tokens.
-
options*: see template configuration for more details.
Cloud Provisioners
step-ca
can be configured to use instance identity documents (IIDs) to authorize certificate signing requests from cloud VMs running on AWS, GCP, or Azure.
IIDs are signed JSON tokens, created when the instance is launched, and made available via the instance metadata API.
Here's the contents of an example IID from AWS:
{
"devpayProductCodes" : null,
"marketplaceProductCodes" : [ "1abc2defghijklm3nopqrs4tu" ],
"availabilityZone" : "us-west-2b",
"privateIp" : "10.158.112.84",
"version" : "2017-09-30",
"instanceId" : "i-1234567890abcdef0",
"billingProducts" : null,
"instanceType" : "t2.micro",
"accountId" : "123456789012",
"imageId" : "ami-5fb8c835",
"pendingTime" : "2016-11-19T16:32:11Z",
"architecture" : "x86_64",
"kernelId" : null,
"ramdiskId" : null,
"region" : "us-west-2"
}
Fig. 2: Diagram of how step works with a cloud service as the provisioner
Once you’ve configured step-ca
to accept IIDs for authentication,
step
will detect the provisioner type,
obtain an IID token from your cloud provider's metadata API,
and use it to obtain a certificate from step-ca
.
From the CA's perspective, an IID is a single-use token.
A host can only get one certificate, ever, per IID.
A host can then renew its certificate using step ca renew
.
If the certificate ever expires, the host will need to use a different provisioner to issue a new one.
Security Risks and Limitations
While IIDs simplify the integration of step-ca
, the approach comes with risks.
Unfortunately, an IID usually doesn't contain enough information to authenticate a host certificate. For example, while an IID may contain the private or public IP for the host, it will not contain all of the DNS names and IP addresses that you may want on the certificate.
Because of this, step-ca
's cloud provisioners use the Trust on First Use (TOFU) security model,
allowing any instance to get a certificate with any names (SANs) on it.
This allows for a "cryptographic perimeter": If a host presents a certificate that was signed by your CA using a cloud provisioner, and the CA is configured to verify the instance's account, project, or tenant ID in the IID, you can have some confidence that the request came from your infrastructure (not someone else's), but you cannot assume that the names on the certificate are authentic.
Because of this, every host in your infrastructure must be trusted.
Mitigating the risk of IIDs
Here are some things you can do to mitigate risk when using IIDs:
- Configure the provisioner with
instanceAge
. The IID will effectively "expire" if it's not used withininstanceAge
after the instance is launched. - Use a trusted launch configuration / User Data script to obtain a certificate.
Coupled with
instanceAge
, this will give you more assuance that the names on your certificates can be trusted. - Restrict provisioning by your cloud provider account or project IDs.
Configure the provisioner's
accounts
(AWS) orprojectIDs
(GCP) setting. - Disable Custom SANs, if possible.
When using the
disableCustomSANs
setting, the CA will only issue certificates with authentic name(s) extracted from the signed instance identity document. Unfortunately, the names on the IID may not be the names that you use to refer to your servers and services. - Instances that don't obtain a certificate are a risk.
Anyone with access to the instance will be able to obtain a certificate binding any names,
so long as the instance is younger than
instanceAge
. Consider requesting certificates even for instances that will never use them, so that the IID cannot later be used by an attacker.
There are a lot of details to get right to make this model secure, many of which are environment-dependent and beyond the scope of this document.
AWS
The AWS provisioner allows granting a certificate to an Amazon EC2 instance using the Instance Identity Documents
The step
CLI will generate a custom JWT
token containing the instance identity document and its signature and the CA
will grant a certificate after validating it.
Example
Find your AWS account ID to restrict access to our VMs:
On the host running step-ca
add an AWS provisioner to your configuration by running:
step ca provisioner add "AWS IID Provisioner" --type AWS --aws-account 123456789042
In the ca.json
, an AWS provisioner looks like:
{
"type": "AWS",
"name": "Amazon Web Services",
"accounts": ["123456789042"],
"disableCustomSANs": false,
"disableTrustOnFirstUse": false,
"instanceAge": "1h",
"claims": {
"maxTLSCertDuration": "2160h",
"defaultTLSCertDuration": "2160h"
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
AWS
. -
name: a string used to identify the provider when the CLI is used.
-
accounts*: the list of AWS account numbers that are allowed to use this provisioner. If none is specified, all AWS accounts will be valid.
-
disableCustomSANs*: by default custom SANs are valid, but if this option is set to true only the SANs available in the instance identity document will be valid, these are the private IP and the DNS
ip-<private-ip>.<region>.compute.internal
. -
disableTrustOnFirstUse*: by default only one certificate will be granted per instance, but if the option is set to true this limit is not set and different tokens can be used to get different certificates.
-
instanceAge*: The maximum age of an instance that should be allowed to obtain a certificate. Limits certificate issuance to new instances to mitigate the risk of credential-misuse from instances that don't need a certificate.
-
options*: see template configuration for more details.
GCP
The GCP provisioner grants certificates to Google Compute Engine instance using its identity token. The CA will validate the JWT and grant a certificate.
Example
On the host running step-ca
, add an GCP provisioner to your configuration by running:
step ca provisioner add Google --type GCP \
--gcp-service-account 1234567890-compute@developer.gserviceaccount.com \
--gcp-service-account 9876543210-compute@developer.gserviceaccount.com \
--gcp-project identity --gcp-project accounting
In the ca.json
, a GCP provisioner looks like:
{
"type": "GCP",
"name": "Google Cloud",
"serviceAccounts": ["1234567890"],
"projectIDs": ["project-id"],
"disableCustomSANs": false,
"disableTrustOnFirstUse": false,
"instanceAge": "1h",
"claims": {
"maxTLSCertDuration": "2160h",
"defaultTLSCertDuration": "2160h"
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
GCP
. -
name: a string used to identify the provider when the CLI is used.
-
serviceAccounts
*: the list of service account numbers that are allowed to use this provisioner. If none is specified, all service accounts will be valid. -
projectIDs*: the list of project identifiers that are allowed to use this provisioner. If non is specified all project will be valid.
-
disableCustomSANs*: by default custom SANs are valid, but if this option is set to true only the SANs available in the instance identity document will be valid, these are the DNS
<instance-name>.c.<project-id>.internal
and<instance-name>.<zone>.c.<project-id>.internal
-
disableTrustOnFirstUse*: by default only one certificate will be granted per instance, but if the option is set to true this limit is not set and different tokens can be used to get different certificates.
-
instanceAge*: The maximum age of an instance that should be allowed to obtain a certificate. Limits certificate issuance to new instances to mitigate the risk of credential-misuse from instances that don't need a certificate.
-
options*: see template configuration for more details.
Azure
The Azure provisioner grants certificates to Microsoft Azure instances using the managed identities tokens. The CA will validate the JWT and grant a certificate.
Example
On the host running step-ca
, add an Azure provisioner to your configuration by running:
step ca provisioner add Azure --type Azure \
--azure-tenant bc9043e2-b645-4c1c-a87a-78f8644bfe57 \
--azure-resource-group identity --azure-resource-group accounting
In the ca.json
, an Azure provisioner looks like:
{
"type": "Azure",
"name": "Microsoft Azure",
"tenantId": "b17c217c-84db-43f0-babd-e06a71083cda",
"resourceGroups": ["backend", "accounting"],
"audience": "https://management.azure.com/",
"disableCustomSANs": false,
"disableTrustOnFirstUse": false,
"claims": {
"maxTLSCertDuration": "2160h",
"defaultTLSCertDuration": "2160h"
},
"options": {
"x509": {
"templateFile": "templates/certs/x509/default.tpl"
}
}
}
-
type: indicates the provisioner type and must be
Azure
. -
name: a string used to identify the provider when the CLI is used.
-
tenantId: the Azure account tenant id for this provisioner. This id is the Directory ID available in the Microsoft Entra ID properties.
-
audience*: defaults to
https://management.azure.com/
but it can be changed if necessary. -
resourceGroups*: the list of resource group names that are allowed to use this provisioner. If none is specified, all resource groups will be valid.
-
disableCustomSANs*: by default custom SANs are valid, but if this option is set to true only the SANs available in the token will be valid, in Azure only the virtual machine name is available.
-
disableTrustOnFirstUse*: by default only one certificate will be granted per instance, but if the option is set to true this limit is not set and different tokens can be used to get different certificates.
-
options*: see template configuration for more details.