Run step-ca in Docker Container Tutorial | Smallstep

Run a private online TLS certificate authority in a Docker container

This guide will illustrate how to run open source step-ca inside a Docker container. As an example, you will send secure communications between a standalone webserver and curl.

About this tutorial

If you run into any issues please let us know in Discord or in GitHub Discussions.

Overview

Requirements

Quickstart

On your Docker host, run:

docker run -d -v step:/home/step \
    -p 9000:9000 \
    -e "DOCKER_STEPCA_INIT_NAME=Smallstep" \
    -e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \
    smallstep/step-ca

The following environment variables are available for CA configuration:

Once step-ca is running, the CA's URL and SHA256 fingerprint are all clients need to bootstrap with the CA.

Let's bootstrap the step client. Run:

CA_FINGERPRINT=$(docker run -v step:/home/step smallstep/step-ca step certificate fingerprint certs/root_ca.crt)
step ca bootstrap --ca-url https://localhost:9000 --fingerprint $CA_FINGERPRINT

Output:

The root certificate has been saved in /Users/alice/.step/certs/root_ca.crt.
Your configuration has been saved in /Users/alice/.step/config/defaults.json.

Your CA is ready for use. You can view your CA password via:

docker run -v step:/home/step smallstep/step-ca cat secrets/password

Manual installation

1. Pull down the Docker image

Get the latest version of step-ca

docker pull smallstep/step-ca

2. Bring up PKI bootstrapping container

The Docker volume step will hold your CA configuration, keys, and database.

docker run -it -v step:/home/step smallstep/step-ca step ca init

The init command will step you through the bootstrapping process. Example output:

✔ What would you like to name your new PKI? (e.g. Smallstep): Smallstep
✔ What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
✔ What address will your new CA listen at? (e.g. :443): :9000
✔ What would you like to name the first provisioner for your new CA? (e.g. you@smallstep.com): admin@smallstep.com
✔ What do you want your password to be? [leave empty and we'll generate one]:

Generating root certificate...
all done!

Generating intermediate certificate...
all done!

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

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

Save the root fingerprint value! You'll need it for client bootstrapping.

3. Place the PKI root password in a known safe location

The image is expecting the password to be placed in /home/step/secrets/password. Bring up the shell prompt in the container again and write that file:

docker run -it -v step:/home/step smallstep/step-ca sh

Inside your container, write the file into the expected location:

 echo "<your password here>" > /home/step/secrets/password

Your CA is configured and ready to run.

4. Start step-ca

The CA runs an HTTPS API on port 9000 inside the container. Expose the server address locally and run the step-ca with:

docker run -d -p 9000:9000 -v step:/home/step smallstep/step-ca

Now, on your Docker host, bootstrap your step client configuration:

CA_FINGERPRINT=$(docker run  -v step:/home/step smallstep/step-ca step certificate fingerprint /home/step/certs/root_ca.crt)
step ca bootstrap --ca-url https://localhost:9000 --fingerprint $CA_FINGERPRINT

Output:

Your local step CLI is now configured to use the container instance of step-ca and our new root certificate is trusted by our local environment (inserted into local trust store).

Run a health check:

curl https://localhost:9000/health

Output:

{"status":"ok"}

Next steps:

Setting up a development environment

You will need:

Run this section on your host machine where Docker is installed.

Once you've bootstrapped your local environment, you can now run web services configured with TLS and mTLS. First, get a certificate for localhost:

step ca certificate localhost localhost.crt localhost.key

Output:

✔ Key ID: aTPGWP0qbuQdflR5VxtNouDIOXyNMH1H9KAZKP-UcHo (admin)
✔ Please enter the password to decrypt the provisioner key:
✔ CA: <https://localhost:9000/1.0/sign>
✔ Certificate: localhost.crt
✔ Private Key: localhost.key

Now save a copy of your root CA certificate:

step ca root root_ca.crt

Output:

The root certificate has been saved in root_ca.crt.

Next, let's launch a web server secured by HTTPS:

{
cat <<EOF > server.py
import BaseHTTPServer, ssl

class HelloHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200);
        self.send_header('content-type', 'text/html; charset=utf-8');
        self.end_headers()
        self.wfile.write(b'\n\xf0\x9f\x91\x8b Hello! Welcome to TLS \xf0\x9f\x94\x92\xe2\x9c\x85\n\n')

httpd = BaseHTTPServer.HTTPServer(('', 8443), HelloHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
                   server_side=True,
                   keyfile="localhost.key",
                   certfile="localhost.crt",
                   ca_certs="root_ca.crt")
httpd.serve_forever()
EOF

python server.py
}

Open up another terminal to see your server running:

$ curl https://localhost:8443
👋 Hello! Welcome to TLS 🔒✅

Or visit https://localhost:8443 from your browser.