....

This is just a wip command by command play by play for now, i will add context and infos later(TM).

random comments:

  • root$ denotes stuff done as root
  • $ denotes stuff done as normal user
  • you totally can use a different easy-rsa instance to generate your sub-ca, it does not need to be done in the root-ca "pki" - i will try to mark the points where you need to divert at a later time, essentially you need two easy-rsa instances(go figure, one with a ca build and one without - and then move the CSR/Finished cert between them)

Relevant Domains:

  • kubecluster.example.org - subdomain your cluster should have
  • step-certificates.step-ca.svc.cluster.local - service and namespace under which your intermediate is reachable in the cluster
  • kubemaster.example.org - node fqdn
  • kubenode01.example.org - node fqdn
  • kubenode02.example.org - node fqdn
  • (add/remove more node fqdns)

Derived domains: - ca.kubecluster.example.org - fqdn for your ca - .kubecluster.example.org - subdomain for your cluster including "children"

You need a root ca managed by easy-rsa. Our first step is to create a template for our intermediate. This will need be run on the CA side.

$ cd $CA_PATH
$ cat > x509-types/ca.kubecluster.example.org <<EOF


# CA_PATH_LEN for CA path length limits. You could also do this here
# manually as in the following example in place of the existing line:
#
# basicConstraints = CA:TRUE, pathlen:0

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = cRLSign, keyCertSign

# this magic bit creates a subca which is not allowed to sign other CAs - nice fun :)
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
nameConstraints = critical,@nameconsts
subjectAltName = critical,@sans

[sans]
# configure all the namespaces & aliases here - atleast what you used for the commonname of the subca(to be created) and the CA alias in the cluster
DNS.0=ca.kubecluster.example.org
# helm-release-name.namespace.svc.cluster-local
DNS.1=step-certificates.step-ca.svc.cluster.local

[nameconsts]
# apparently ordered numbers are not needed :P
permitted;DNS.0=.kubecluster.example.org
permitted;DNS.2=kubecluster.example.org
# repeat the DNS.1 SAN here in the nameconstraint
permitted;DNS.3=step-certificates.step-ca.svc.cluster.local
# allow it to build certs for all nodenames in the cluster
permitted;DNS.4=kubemaster.example.org
permitted;DNS.5=kubenode01.example.org
permitted;DNS.6=kubenode02.example.org
EOF

The next commands need to run on the node preparing the step-ca config:

$ make-ca cluster-step-ca
$ cd cluster-step-ca
$ ./easyrsa init-pki
$ pwgen -s 50 1 > ./intermediate-pw
$ EASYRSA_CN=ca.kubecluster.example.org  EASYRSA_ALGO=ec EASYRSA_PASSOUT=file:./intermediate-pw ./easyrsa gen-req ca.kubecluster.example.org batch
* Notice:
Using Easy-RSA configuration from: /home/k3s-rootless-experiments/dev/tmp/talos1-home-easy-rsa/vars

* WARNING:

  Move your vars file to your PKI folder, where it is safe!

* Notice:
Using SSL: openssl OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)

-----
* Notice:

Keypair and certificate request completed. Your files are:
req: /home/k3s-rootless-experiments/dev/tmp/talos1-home-easy-rsa/pki/reqs/ca.kubecluster.example.org.req
key: /home/k3s-rootless-experiments/dev/tmp/talos1-home-easy-rsa/pki/private/ca.kubecluster.example.org.key

We now have a request we copy over to the CA, with which we can then generate the signed intermediate cert.

$ cd $CA_DIR
$ # copy the generated req to ca.kubecluster.example.org.req to this dir
$ ./easyrsa import-req ./ca.kubecluster.example.org.csr.pem ca.kubecluster.example.org
$ ./easyrsa sign-req ca.kubecluster.example.org ca.kubecluster.example.org
* No Easy-RSA 'vars' configuration file exists!

* Using SSL: openssl OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)

You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a ca.kubecluster.home.stejau.vpn certificate for 825 days:

subject=
    commonName                = ca.kubecluster.example.org


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes

Using configuration from /root/vpnint-easy-rsa/easyrsa3/pki/98b8aa22/temp.5.x
Enter pass phrase for /root/vpnint-easy-rsa/easyrsa3/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'ca.kubecluster.example.org'
Certificate is to be certified until Dec  4 22:53:17 2026 GMT (825 days)

Write out database with 1 new entries
Database updated

Notice
------
Certificate created at:
* /root/vpnint-easy-rsa/easyrsa3/pki/issued/ca.kubecluster.example.org.crt


$ openssl x509 -in /home/kind01/talos1-step-ca-autocert/pki/issued/ca.kubecluster.example.org.crt -noout -text
...

The generated cert should contain all the SANs/name constraints from the template.

We continue on the step-ca host:


$ # Copy the signed cert into pki/issued/ca.kubecluster.example.org.crt.
$ # Copy the root ca crt from pk/ca.crt into pki/ca.crt
$ pwgen -s 50 1 > pwd
$ step ca init --password-file=pwd --provisioner-password-file=pwd --name=test1 --key-password-file=pwd --deployment-type=standalone --dns ca.kubecluster.example.org --dns step-certificates.step-ca.svc.cluster.local --address=:9000 --provisioner=step-ca-issuer --helm > test1.yaml
$ cat ./test1.yaml  | yq -o json | jq --arg fingerprint "$(openssl x509 -in pki/ca.crt -noout -fingerprint -sha256 | sed 's/.*=//; s/://g')" --rawfile root_ca_crt pki/ca.crt --rawfile intermediate_ca_pw ./intermediate-pw --rawfile intermediate_ca_key pki/private/ca.kubecluster.home.stejau.vpn.key --rawfile intermediate_ca_crt pki/issued/ca.kubecluster.home.stejau.vpn.crt '.inject.certificates.root_ca = $root_ca_crt | .inject.secrets.x509.root_ca_key = null | .inject.certificates.intermediate_ca = $intermediate_ca_crt | .inject.secrets.x509.intermediate_ca_key = $intermediate_ca_key | .inject.secrets.ca_password = ($intermediate_ca_pw | @base64) | .inject.secrets.provisioner_password = null | .inject.config.files["defaults.json"].fingerprint = $fingerprint' |  yq -o yaml -P | tee test2.yaml

$ # you now have the step-ca config inside test2.yaml.

TODOs:

  • How to get from test2.yaml to a working step-ca
  • Add provisioner password to above invokation
  • step-ca-cluster-issuer - generate config like above
  • multiple provisioners - how to do?
  • ???

Caveat:

  • it might be that x509-types in a modern easy-rsa install is under pki/ instead of in the same dir as the easy-rsa script