Installing Step CA in My Homelab

Step CA is an open-source private CA made by Smallstep. I will use it to generate certificates for some componenents in my lab.

First we install the dependencies:

sudo apt-get update && sudo apt-get install -y --no-install-recommends curl gpg ca-certificates

Then we get the Smallstep repository signing key:

sudo curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg   -o /etc/apt/keyrings/smallstep.asc

Then we add the Smallstep repository:

cat << 'EOF' | sudo tee /etc/apt/sources.list.d/smallstep.sources > /dev/null
Types: deb
URIs: https://packages.smallstep.com/stable/debian
Suites: debs
Components: main
Signed-By: /etc/apt/keyrings/smallstep.asc
EOF

Then we install step-cli and step-ca:

sudo apt-get update && sudo apt-get -y install step-cli step-ca

Then we check the install:

step-ca version
step version
Smallstep CA/0.30.2 (linux/amd64)
Release Date: 2026-03-23T00:18:00Z
Smallstep CLI/0.30.4 (linux/amd64)
Release Date: 2026-06-10T06:10:28Z

Next, we’ll run the initializer:

step ca init \
  --name "lostintransit.se" \
  --dns "stepca.lostintransit.se" \
  --address ":443" \
  --provisioner "[email protected]"
✔ Deployment Type: Standalone
Choose a password for your CA keys and first provisioner.
✔ [leave empty and we'll generate one]: 

Generating root certificate... done!
Generating intermediate certificate... done!

✔ Root certificate: /home/ddib/.step/certs/root_ca.crt
✔ Root private key: /home/ddib/.step/secrets/root_ca_key
✔ Root fingerprint: 8f08102ae41eb7fc6a57f62fbaccaf82cb7a67dbedca858a0352a75b4fa763cd
✔ Intermediate certificate: /home/ddib/.step/certs/intermediate_ca.crt
✔ Intermediate private key: /home/ddib/.step/secrets/intermediate_ca_key
✔ Database folder: /home/ddib/.step/db
✔ Default configuration: /home/ddib/.step/config/defaults.json
✔ Certificate Authority configuration: /home/ddib/.step/config/ca.json

Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.

The name we define will be used in the common name (CN) of the certificate. The DNS name is how the clients will reach the CA. It could also be an IP address.

Two certificates are generated. Let’s first look at the root certificate:

step certificate inspect root_ca.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 181056573757559942311126483785212531632 (0x88363a4abacade63f875537c81ac93b0)
        Signature Algorithm: ECDSA-SHA256
        Issuer: O=lostintransit.se,CN=lostintransit.se Root CA
        Validity
            Not Before: Jun 10 15:20:40 2026 UTC
            Not After : Jun 7 15:20:40 2036 UTC
        Subject: O=lostintransit.se,CN=lostintransit.se Root CA
        Subject Public Key Info:
            Public Key Algorithm: ECDSA
                Public-Key: (256 bit)
                X:
                    bc:99:12:4f:4a:9a:89:7b:b4:54:55:fc:b6:cf:27:
                    26:ea:62:ad:76:7f:27:e1:8f:16:55:fc:4e:f4:36:
                    48:71
                Y:
                    73:bd:b0:ae:bb:13:70:36:01:6d:f3:15:b2:fa:45:
                    9e:33:67:3b:fc:fe:54:a2:87:b8:c5:dd:9b:63:90:
                    7e:a0
                Curve: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1
            X509v3 Subject Key Identifier:
                50:70:2A:A4:91:33:B6:0B:60:37:B1:23:79:B2:8E:0A:BA:0F:B7:C1
    Signature Algorithm: ECDSA-SHA256
         30:45:02:21:00:8e:65:b6:dc:d1:2e:3e:a8:c2:12:7e:1f:06:
         07:2c:d7:7c:b3:92:95:f3:18:84:f6:a1:d6:dc:32:40:a3:6e:
         70:02:20:45:d8:f8:06:a3:02:fa:1c:b7:e8:c0:67:e1:36:6d:
         e3:4c:00:80:0c:84:0e:bf:91:94:7d:8d:9e:39:3d:3a:b5

Then the intermediate certificate:

step certificate inspect intermediate_ca.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 117200397332447190053195182546110244923 (0x582bf9b882cfe5b92a5c21d8f8b5e83b)
        Signature Algorithm: ECDSA-SHA256
        Issuer: O=lostintransit.se,CN=lostintransit.se Root CA
        Validity
            Not Before: Jun 10 15:20:41 2026 UTC
            Not After : Jun 7 15:20:41 2036 UTC
        Subject: O=lostintransit.se,CN=lostintransit.se Intermediate CA
        Subject Public Key Info:
            Public Key Algorithm: ECDSA
                Public-Key: (256 bit)
                X:
                    ac:b5:a4:8c:e0:84:46:50:96:cd:b7:b9:b9:75:a7:
                    a3:b5:78:08:34:e6:38:2b:5c:5f:65:d9:c1:56:91:
                    1d:d0
                Y:
                    d3:e4:fe:24:5d:fe:09:f4:ff:b1:60:84:68:22:54:
                    3d:78:9f:bc:e3:c2:a7:2e:cc:57:2d:03:01:8a:0b:
                    9f:6a
                Curve: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Subject Key Identifier:
                92:5D:76:F6:28:CE:19:62:13:3C:3A:F8:15:85:2B:10:63:1B:CC:E4
            X509v3 Authority Key Identifier:
                50:70:2A:A4:91:33:B6:0B:60:37:B1:23:79:B2:8E:0A:BA:0F:B7:C1
    Signature Algorithm: ECDSA-SHA256
         30:45:02:21:00:f0:0e:05:97:d5:97:27:7d:16:02:ac:32:9c:
         49:d8:45:77:b1:e4:22:f3:b8:6e:7b:14:95:d4:82:c3:bd:cb:
         0f:02:20:3b:df:14:76:f6:09:39:d5:97:1f:70:a2:96:a6:e3:
         f6:ca:22:a5:4a:fa:b0:0a:77:af:83:89:51:66:86:3d:cf

Note that when we sign certificates, they will be signed by the intermediate CA. When we import the root CA chain, we need the full path and will need to concatenate the two certs.

Next we’re going to do some hardening. First we create a service user named step without any shell or home login:

sudo useradd --system --home /etc/step-ca --shell /bin/false step

Then we need to grant permission to bind to port 443:

sudo setcap CAP_NET_BIND_SERVICE=+eip $(which step-ca)

We are using setcap, short for set capabilities. The CAP_NET_BIND_SERVICE is what allows us to do the binding. The +eip is effective (active immediately), inheritable (passed to child processes), and permitted. We use shell substitution with $(which step-ca) to return the full path to the binary, /usr/bin/step-ca.

Next, we’re going to move the CA configuration to a system-wide location (/etc/step-ca):

sudo mkdir -p /etc/step-ca
sudo cp -r ~/.step/* /etc/step-ca/
sudo chown -R step:step /etc/step-ca

We copy the existing files (recursively) from .step directory in the home directory and set the owner to be the step user and group. Only this user can read in the directory we created.

We need to update the configuration to point to the right directory:

sudo sed -i 's|/home/ddib/.step|/etc/step-ca|g' /etc/step-ca/config/defaults.json
sudo sed -i 's|/home/ddib/.step|/etc/step-ca|g' /etc/step-ca/config/ca.json

You can verify with:

sudo cat /etc/step-ca/config/defaults.json
sudo cat /etc/step-ca/config/ca.json

We need to configure the maximum certificate duration for the provisioner. The default is 24 hours which is too short for SD-WAN control plane certificates (my main use case). Edit the CA config:

sudo vi /etc/step-ca/config/ca.json

Find the provisioner entry and add a claims block after the encryptedKey line:

"encryptedKey": "your-encrypted-key-here",
"claims": {
    "maxTLSCertDuration": "87600h",
    "defaultTLSCertDuration": "8760h"
}

This sets the maximum certificate lifetime to 10 years and the default to 1 year.

Now we need to store the CA password for systemd:

echo "your-ca-password-here" | sudo tee /etc/step-ca/password.txt
sudo chmod 600 /etc/step-ca/password.txt
sudo chown step:step /etc/step-ca/password.txt

We modify so ownly the owner can read and write and set the owner to be the step user.

Next we create the systemd unit file:

sudo tee /etc/systemd/system/step-ca.service > /dev/null << 'EOF'
[Unit]
Description=step-ca Certificate Authority
After=network-online.target
Wants=network-online.target
ConditionFileNotEmpty=/etc/step-ca/config/ca.json
ConditionFileNotEmpty=/etc/step-ca/password.txt

[Service]
Type=simple
User=step
Group=step
Environment=STEPPATH=/etc/step-ca
WorkingDirectory=/etc/step-ca
ExecStart=/usr/bin/step-ca config/ca.json --password-file password.txt
Restart=on-failure
RestartSec=5
NoNewPrivileges=false
AmbientCapabilities=CAP_NET_BIND_SERVICE
ProtectSystem=strict
ReadWritePaths=/etc/step-ca/db
ProtectHome=true

[Install]
WantedBy=multi-user.target
EOF

This allows us to start the CA as a systemd service.

Then we enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now step-ca
sudo systemctl status step-ca
Created symlink /etc/systemd/system/multi-user.target.wants/step-ca.service → /etc/systemd/system/step-ca.service.
● step-ca.service - step-ca Certificate Authority
     Loaded: loaded (/etc/systemd/system/step-ca.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-06-11 07:52:59 UTC; 60ms ago
   Main PID: 9020 (step-ca)
      Tasks: 5 (limit: 2263)
     Memory: 6.1M (peak: 6.3M)
        CPU: 48ms
     CGroup: /system.slice/step-ca.service
             └─9020 /usr/bin/step-ca config/ca.json --password-file password.txt

Jun 11 07:52:59 stepca systemd[1]: Started step-ca.service - step-ca Certificate Authority.

That’s our basic installation of Step CA which will allow us to sign certificates.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top