1
0
Fork 0

step-ca: Deploy Step CA service

[Step CA] is an open-source online X.509 and SSH certificate authority
service.  It supports issuing certificates via various protocols,
including ACME and its own HTTP API via the `step` command-line utility.
Clients can authenticate using a variety of methods, such as JWK, Open
ID Connect, or mTLS.  This makes it very flexible and easy to introduce
to an existing ecosystem.

Although the CA service is mostly stateless, it does have an on-disk
database where stores some information, notably the list of SSH hosts
for which it has signed certificates.  Most other operations, though, do
not require any persistent state; the service does not keep track of
every single certificate it signed, for example.  It can be configured
to store authentication information (referred to as "provisioners") in
the database instead of the configuration file, by enabling the "remote
provisioner management" feature.  This has the advantage of being able
to modify authentication configuration without updating a Kubernetes
ConfigMap and restarting the service.

The official Step CA documentation recommends using the `step ca init`
command initialize a new certificate authority.  This command performs a
few steps:

* Generates an ECDSA key pair and uses it to create a self-signed root
  certificate
* Generates a second ECDSA key pair and signs an intermediate CA
  certificate using the root CA key
* Generates an ECDSA key pair and SSH root certificate
* Creates a `ca.json` configuration file

These steps can be performed separately, and in fact, I created the
intermediate CA certificate and signed it with the (offline) *dch Root
CA* certificate.

When the service starts for the first time, because
`authority/enableAdmin` is `true` and `authority/provisioners` is empty,
a new "Admin JWK" provisioner will be created automatically.  This key
will be encrypted with the same password used to encrypt the
intermediate CA certificate private key, and can be used to create other
provisioners.

[Step CA]: https://smallstep.com/docs/step-ca/
dch-webhooks-secrets
Dustin 2023-10-10 21:41:00 -05:00
parent 6cd7eae0d3
commit 0a9596d8bd
10 changed files with 326 additions and 0 deletions

3
step-ca/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.key
password
ssh_*_key

100
step-ca/README.md Normal file
View File

@ -0,0 +1,100 @@
# Step CA
[Step CA] is an open-source online X.509 and SSH certificate authority servier.
It provides an HTTP API for remote control via the `step` command, which is
used by clients for certificate issuance and administrators for configuration
and control. It also supports other certificate issuance protocols, including
[ACME]. Clients can authenticate using a variety of protocols, such as JWK,
OpenID Connect, mTLS, and more.
## Offline Root CA
The *dch Root CA R2* private key is managed externally from Step CA. It is
stored offline (on a flash drive in a fireproof save). Only the CA certificate
is used by the online CA service, where it is provided to clients to include in
as a trust anchor in their respective certificate stores.
*dch Root CA R2* replaces *dch Root CA R1*, which has not been used for some
time.
## Online Intermediate CA
Step CA manages the *dch CA R2* intermediate certificate authority. The
private key for this CA is stored in the `intermediate_ca.key` file, encrypted
with the password in `password`. This key pair is needed by the online CA to
sign end-entity certificates.
## SSH CA
In addition to X.509 (TLS) certificates, Step CA can also manage SSH
certificates. These can be used in place of "plain" SSH keys that must be
managed out-of-band (i.e. `~/.ssh/known_hosts` and `~/.ssh/authorized_keys`).
Instead of maintaining a database of hosts' public keys, clients can trust the
CA certificate that signs the hosts' certificates. To do so, simply add a
single line to `~/.ssh/known_hosts`:
```
@cert-authority *.pyrocufflink.blue ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ8KfNjDh0R/jCmPcJrVafvmuw5JZw+cKoy9RCYNwHsRwPoRHfyzjV1VUZolJfEz+Qm3u+mgYJ/oSquCelY84xE=
```
Any host with a hostname that matches `*.pyrocufflink.blue` and presents a
certificate signed by the listed certificate will be automatically trusted; the
`ssh` client will not prompt for manual key verification on the first
connection.
Similarly, hosts can trust client keys that are signed by the CA by *either*
adding the certificate to per-user `~/.ssh/authorized_keys` files *or* by
setting the global `TrustedUserCAKeys` parameter in the SSH server
configuration.
`~/.ssh/authorized_keys`:
```
cert-authority ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBImIoTTmhynCVy/vJ/Q2bWydzqVsvwhGvDgBbklw0eDt8UEbbP9HHPhxiMDtiAhbvRTg5BhYVAlR1MgdooT5dwQ=
```
`/etc/ssh/sshd_config`:
```
TrustedUserCAKeys /etc/ssh/ca.pub
```
`/etc/ssh/ca.pub`:
```
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBImIoTTmhynCVy/vJ/Q2bWydzqVsvwhGvDgBbklw0eDt8UEbbP9HHPhxiMDtiAhbvRTg5BhYVAlR1MgdooT5dwQ=
```
SSH host certificates are typically valid for 30 days, so hosts need to have
some automation in place to automatically renew their certificates. Using the
"SSHPOP" provisioner, hosts can use the `step ssh renew` command to renew their
certificates; existing signed SSH certificates are usable as authentication
credentials.
SSH user certificates are typically valid for 24 hours. Clients will need to
request a new certificate every day using the `step ssh login` command:
```sh
step ssh login --provisioner=authelia dustin
```
Note the `--provisioner=authelia` argument seems to be required, even if a
default provisioner is specified in `~/.step/config/defaults.json`.
The final positional argument is the name of the *remote* SSH user, *not* the
user logging in to the OIDC IdP.
## NodePort Service (No Ingress)
Step CA supports authenticating clients using mTLS, such as to renew a user
certificate. For this to work, the client must communicate directly with the
server; proxies and load balancers must not intercept the communication or
provide TLS termination, as then the server would not have access to the client
certificate. As such, the service is exposed as a NodePort and not via an
Ingress.
[Step CA]: https://smallstep.com/docs/step-ca/
[ACME]: https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment

36
step-ca/ca.json Normal file
View File

@ -0,0 +1,36 @@
{
"root": "certs/root_ca.crt",
"federatedRoots": null,
"crt": "certs/intermediate_ca.crt",
"key": "secrets/intermediate_ca.key",
"address": ":32599",
"insecureAddress": "",
"dnsNames": [
"ca.pyrocufflink.blue"
],
"ssh": {
"hostKey": "secrets/ssh_host_ca_key",
"userKey": "secrets/ssh_user_ca_key"
},
"logger": {
"format": "text"
},
"db": {
"type": "badgerv2",
"dataSource": "db",
"badgerFileLoadingMode": ""
},
"authority": {
"enableAdmin": true,
"provisioners": []
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minVersion": 1.2,
"maxVersion": 1.3,
"renegotiation": false
}
}

View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICCTCCAa+gAwIBAgIUOrt38QEPFGRaBSZgyuDNQPCLUZowCgYIKoZIzj0EAwIw
QDELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD0R1c3RpbiBDLiBIYXRjaDEXMBUGA1UE
AwwORENIIFJvb3QgQ0EgUjIwHhcNMjMwOTI4MDI0NzMwWhcNMjMxMDI4MDI0NzMw
WjAUMRIwEAYDVQQDEwlkY2gtY2EgUjIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AAQ1rK98igj6Y5lbeP8HS1zqQCtkcmz8uk1jp4VgznWT3Q8BanjA55UHQi/xx4xz
BYu4QIkJhtqcR5a7YXSr7fQvo4GyMIGvMB0GA1UdDgQWBBQGy1GZZxrCjGDiIGdR
YhTMZZqhkTAfBgNVHSMEGDAWgBTM+d8kb1koGmKRtJs4gN9zYa+6oTASBgNVHRMB
Af8ECDAGAQH/AgEAMAsGA1UdDwQEAwIBhjBMBggrBgEFBQcBAQRAMD4wPAYIKwYB
BQUHMAKGMGh0dHBzOi8vZHVzdGluLmhhdGNoLm5hbWUvZGNoLWNhL2RjaC1yb290
LWNhLmNydDAKBggqhkjOPQQDAgNIADBFAiADzZFTGwWNkwZF1U7uZEon7D6sLmCS
WGftl3/IgOrpwwIhAM8bE5UlY3gXt8AObj8VQgGk5Bh38jmOXnDaK0iRz1qm
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: step-ca
resources:
- namespace.yaml
- step-ca.yaml
configMapGenerator:
- name: step-ca-config
files:
- ca.json
- name: step-ca-certs
files:
- root_ca.crt
- intermediate_ca.crt
- ssh_host_ca_key.pub
- ssh_user_ca_key.pub
secretGenerator:
- name: step-ca
files:
- intermediate_ca.key
- password
- ssh_host_ca_key
- ssh_user_ca_key

4
step-ca/namespace.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: step-ca

12
step-ca/root_ca.crt Normal file
View File

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBxDCCAWqgAwIBAgIUbHz2tssa09zsHk+EdGD3QKprMKQwCgYIKoZIzj0EAwQw
QDELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD0R1c3RpbiBDLiBIYXRjaDEXMBUGA1UE
AwwORENIIFJvb3QgQ0EgUjIwHhcNMjMwOTI0MjA1MzA5WhcNNDMwOTE5MjA1MzA5
WjBAMQswCQYDVQQGEwJVUzEYMBYGA1UECgwPRHVzdGluIEMuIEhhdGNoMRcwFQYD
VQQDDA5EQ0ggUm9vdCBDQSBSMjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE2D
NJHRcjuA19ZoprBKaxIfUxAbz6LigM7dgtO6+isaMlxRAVJmsITADIE/22RrUDgD
Ofkt2iZTUjMrz3AxXhWjQjBAMB0GA1UdDgQWBBTM+d8kb1koGmKRtJs4gN9zYa+6
oTASBgNVHRMBAf8ECDAGAQH/AgEBMAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDBANI
ADBFAiEA2Ka8mMiAFLmrFWt0dAml247re2+i4UPhyHcOBfNK+goCIHv+vEw7CHZQ
irIa697nfe4KiXIMwHlAMS1+1QZohFDC
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ8KfNjDh0R/jCmPcJrVafvmuw5JZw+cKoy9RCYNwHsRwPoRHfyzjV1VUZolJfEz+Qm3u+mgYJ/oSquCelY84xE=

View File

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBImIoTTmhynCVy/vJ/Q2bWydzqVsvwhGvDgBbklw0eDt8UEbbP9HHPhxiMDtiAhbvRTg5BhYVAlR1MgdooT5dwQ=

128
step-ca/step-ca.yaml Normal file
View File

@ -0,0 +1,128 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: step-ca
namespace: step-ca
labels:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
app.kubernetes.io/part-of: step-ca
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: step-ca
namespace: step-ca
labels:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
app.kubernetes.io/part-of: step-ca
spec:
ports:
- port: 32599
nodePort: 32599
name: step-ca
selector:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
type: NodePort
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: step-ca
namespace: step-ca
labels:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
app.kubernetes.io/part-of: step-ca
spec:
serviceName: step-ca
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
template:
metadata:
labels:
app.kubernetes.io/name: step-ca
app.kubernetes.io/component: step-ca
app.kubernetes.io/instance: step-ca
spec:
enableServiceLinks: false
containers:
- name: step-ca
image: docker.io/smallstep/step-ca:0.25.0
workingDir: /step
env:
- name: CONFIGPATH
value: /step/config/ca.json
- name: PWDPATH
value: /step/secrets/password
- name: STEPPATH
value: /step
ports:
- containerPort: 32599
name: step-ca
readinessProbe: &probe
httpGet:
port: 32599
path: /health
scheme: HTTPS
failureThreshold: 3
periodSeconds: 60
successThreshold: 1
timeoutSeconds: 1
startupProbe:
<<: *probe
failureThreshold: 30
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- mountPath: /step/certs
name: certs
readOnly: true
- mountPath: /step/config
name: config
readOnly: true
- mountPath: /step/db
name: data
subPath: db
- mountPath: /step/secrets
name: secrets
readOnly: true
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
volumes:
- name: config
configMap:
name: step-ca-config
- name: certs
configMap:
name: step-ca-certs
- name: secrets
secret:
secretName: step-ca
- name: data
persistentVolumeClaim:
claimName: step-ca