diff --git a/examples/README.md b/examples/README.md index 64b099cc..857d6591 100644 --- a/examples/README.md +++ b/examples/README.md @@ -316,3 +316,129 @@ sure the certificate changes after 74-80 seconds. ```sh certificates $ step certificate inspect --insecure https://localhost:8443 ``` + +## NGINX with Step CA certificates + +The example under the `docker` directory shows how to combine the Smallstep CA +with NGINX to server pages or proxy services using certificates created by the +step-ca. + +This example creates 3 different docker images: + +* nginx-test: docker image with NGINX and a script using inotify-tools to watch + for changes in the certificate to reload NGINX. +* step-ca-test: docker image with the Smallstep CA +* step-renewer-test: docker images with the step cli tool, it creates the + certificate and has a cron that renews the certificate. Right now the cron + runs every minute for testing purposes. + +To run this test you need to have docker daemon running. With docker running +swith to the `examples/docker directory` and just run `make` + +``` +certificates $ cd examples/docker/ +docker $ make +GOOS=linux go build -o ca/step-ca github.com/smallstep/certificates/cmd/step-ca +GOOS=linux go build -o renewer/step github.com/smallstep/cli/cmd/step +docker build -t nginx-test:latest nginx +... +docker-compose up +WARNING: The Docker Engine you're using is running in swarm mode. + +Compose does not use swarm mode to deploy services to multiple nodes in a swarm. All containers will be scheduled on the current node. + +To deploy your application across the swarm, use `docker stack deploy`. + +Creating network "docker_default" with the default driver +Creating docker_ca_1 ... done +Creating docker_renewer_1 ... done +Creating docker_nginx_1 ... done +Attaching to docker_ca_1, docker_renewer_1, docker_nginx_1 +ca_1 | 2018/11/12 19:39:16 Serving HTTPS on :443 ... +nginx_1 | Setting up watches. +nginx_1 | Watches established. +... +``` + +Make will build the binaries for step and step-ca, create the images, create the +containers and start them using docker composer. + +NGINX will be listening on your local machine on https://localhost:4443, but to +make sure the cert is right we need to add the following entry to `/etc/hosts`: + +``` +127.0.0.1 nginx +``` + +Now we can use curl to check: + +```sh +docker $ curl --cacert ca/pki/secrets/root_ca.crt https://nginx:4443/ + + +
+If you see this page, the nginx web server is successfully installed and +working. Further configuration is required.
+ +For online documentation and support please refer to
+nginx.org.
+Commercial support is available at
+nginx.com.
Thank you for using nginx.
+ + +``` + +Now you can use `make inspect` to inspect the certificate to see how the +certificate gets updated every minute: + +```sh +docker $ make inspect | head +step certificate inspect https://localhost:4443 --insecure +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 220353801925419530569669982276277771655 (0xa5c6993a7e110e6f009c83c79edc1d87) + Signature Algorithm: ECDSA-SHA256 + Issuer: CN=Smallstep Intermediate CA + Validity + Not Before: Nov 10 02:13:00 2018 UTC + Not After : Nov 11 02:13:00 2018 UTC +docker $ make inspect | head +step certificate inspect https://localhost:4443 --insecure +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 207756171799719353821615361892302471392 (0x9c4c621c04d3e8be401ff0d14c5440e0) + Signature Algorithm: ECDSA-SHA256 + Issuer: CN=Smallstep Intermediate CA + Validity + Not Before: Nov 10 02:14:00 2018 UTC + Not After : Nov 11 02:14:00 2018 UTC +``` + +Finally, to remove the containers and volumes you can use `make down`: + +```sh +docker $ make down +docker-compose down +Stopping docker_nginx_1 ... done +Stopping docker_renewer_1 ... done +Stopping docker_ca_1 ... done +Removing docker_nginx_1 ... done +Removing docker_renewer_1 ... done +Removing docker_ca_1 ... done +Removing network docker_default +``` diff --git a/examples/docker/Makefile b/examples/docker/Makefile new file mode 100644 index 00000000..61008bd1 --- /dev/null +++ b/examples/docker/Makefile @@ -0,0 +1,23 @@ +all: binaries build up + +binaries: + GOOS=linux go build -o ca/step-ca github.com/smallstep/certificates/cmd/step-ca + GOOS=linux go build -o renewer/step github.com/smallstep/cli/cmd/step + +build: build-nginx build-ca build-renewer + +build-nginx: + docker build -t nginx-test:latest nginx +build-ca: + docker build -t step-ca-test:latest ca +build-renewer: + docker build -t step-renewer-test:latest renewer + +up: + docker-compose up + +down: + docker-compose down + +inspect: + step certificate inspect https://localhost:4443 --insecure diff --git a/examples/docker/ca/Dockerfile b/examples/docker/ca/Dockerfile new file mode 100644 index 00000000..6cc9be44 --- /dev/null +++ b/examples/docker/ca/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine + +ADD step-ca /usr/local/bin/step-ca +COPY pki /run + +# Smallstep CA +CMD ["step-ca", "/run/config/ca.json"] diff --git a/examples/docker/ca/pki/config/ca.json b/examples/docker/ca/pki/config/ca.json new file mode 100644 index 00000000..a34758d0 --- /dev/null +++ b/examples/docker/ca/pki/config/ca.json @@ -0,0 +1,58 @@ +{ + "root": "/run/secrets/root_ca.crt", + "crt": "/run/secrets/intermediate_ca.crt", + "key": "/run/secrets/intermediate_ca_key", + "password": "password", + "address": ":443", + "dnsNames": [ + "ca" + ], + "logger": { + "format": "text" + }, + "authority": { + "provisioners": [ + { + "name": "mariano@smallstep.com", + "type": "jwk", + "key": { + "use": "sig", + "kty": "EC", + "kid": "DmAtZt2EhmZr_iTJJ387fr4Md2NbzMXGdXQNW1UWPXk", + "crv": "P-256", + "alg": "ES256", + "x": "jXoO1j4CXxoTC32pNzkVC8l6k2LfP0k5ndhJZmcdVbk", + "y": "c3JDL4GTFxJWHa8EaHdMh4QgwMh64P2_AGWrD0ADXcI" + }, + "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiOTFVWjdzRGw3RlNXcldfX1I1NUh3USJ9.FcWtrBDNgrkA33G9Ll9sXh1cPF-3jVXeYe1FLmSDc_Q2PmfLOPvJOA.0ZoN32ayaRWnufJb.WrkffMmDLWiq1-2kn-w7-kVBGW12gjNCBHNHB1hyEdED0rWH1YWpKd8FjoOACdJyLhSn4kAS3Lw5AH7fvO27A48zzvoxZU5EgSm5HG9IjkIH-LBJ-v79ShkpmPylchgjkFhxa5epD11OIK4rFmI7s-0BCjmJokLR_DZBhDMw2khGnsr_MEOfAz9UnqXaQ4MIy8eT52xUpx68gpWFlz2YP3EqiYyNEv0PpjMtyP5lO2i8-p8BqvuJdus9H3fO5Dg-1KVto1wuqh4BQ2JKTauv60QAnM_4sdxRHku3F_nV64SCrZfDvnN2ve21raFROtyXaqHZhN6lyoPxDncy8v4.biaOblEe0N-gMpJyFZ-3-A" + }, + { + "name": "mike@smallstep.com", + "type": "jwk", + "key": { + "use": "sig", + "kty": "EC", + "kid": "YYNxZ0rq0WsT2MlqLCWvgme3jszkmt99KjoGEJJwAKs", + "crv": "P-256", + "alg": "ES256", + "x": "LsI8nHBflc-mrCbRqhl8d3hSl5sYuSM1AbXBmRfznyg", + "y": "F99LoOvi7z-ZkumsgoHIhodP8q9brXe4bhF3szK-c_w" + }, + "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiVERQS2dzcEItTUR4ZDJxTGo0VlpwdyJ9.2_j0cZgTm2eFkZ-hrtr1hBIvLxN0w3TZhbX0Jrrq7vBMaywhgFcGTA.mCasZCbZJ-JT7vjA.bW052WDKSf_ueEXq1dyxLq0n3qXWRO-LXr7OzBLdUKWKSBGQrzqS5KJWqdUCPoMIHTqpwYvm-iD6uFlcxKBYxnsAG_hoq_V3icvvwNQQSd_q7Thxr2_KtPIDJWNuX1t5qXp11hkgb-8d5HO93CmN7xNDG89pzSUepT6RYXOZ483mP5fre9qzkfnrjx3oPROCnf3SnIVUvqk7fwfXuniNsg3NrNqncHYUQNReiq3e9I1R60w0ZQTvIReY7-zfiq7iPgVqmu5I7XGgFK4iBv0L7UOEora65b4hRWeLxg5t7OCfUqrS9yxAk8FdjFb9sEfjopWViPRepB0dYPH8dVI.fb6-7XWqp0j6CR9Li0NI-Q", + "claims": { + "minTLSCertDuration": "60s", + "defaultTLSCertDuration": "120s" + } + } + ] + }, + "tls": { + "cipherSuites": [ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + ], + "minVersion": 1.2, + "maxVersion": 1.2, + "renegotiation": false + } +} \ No newline at end of file diff --git a/examples/docker/ca/pki/secrets/intermediate_ca.crt b/examples/docker/ca/pki/secrets/intermediate_ca.crt new file mode 100644 index 00000000..099e9241 --- /dev/null +++ b/examples/docker/ca/pki/secrets/intermediate_ca.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxjCCAWugAwIBAgIQAYoOWhdChUmmKzlc0DWcWDAKBggqhkjOPQQDAjAcMRow +GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xODExMDIyMzU0MTNaFw0yODEw +MzAyMzU0MTNaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASxvIWme8/yDAxkR63KgSYkpN7mHKBH +k5c8S+uzba4xWbaxZtEZ9NNhEIAgYFZ9/3ThrzLOsuGwRCvPTaD5iycQo4GGMIGD +MA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU8dKIy5ZLH2h3ihWgqjcpoo5e +q3YwHwYDVR0jBBgwFoAU0IpOvAyBnn9UhDqOQzXnfEU3aYMwCgYIKoZIzj0EAwID +SQAwRgIhANXlcktuaEvORhgRvzQ6vVNgvpqCEXW3CcCHjUl1xSdaAiEAmakkpfFq +VsT5PqPnTRgOWlFESRhQ9btl6nQ+2Lt/S5A= +-----END CERTIFICATE----- diff --git a/examples/docker/ca/pki/secrets/intermediate_ca_key b/examples/docker/ca/pki/secrets/intermediate_ca_key new file mode 100644 index 00000000..e99a9e99 --- /dev/null +++ b/examples/docker/ca/pki/secrets/intermediate_ca_key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,4c7758e66df1884f6560839de64d4dd3 + +S8Ha8uA+bA3IGPurYODwd9VaJZ6FHI2tlznHXCOxT1MlGqyEAc4aWS11QBUz0Ucp +excwlqM8kfh5BcN5a+vvInHnv74ZiNPdpt/apzz2LIx52pApzASiKVXRsAUmR4Pv +3MsO1/cVHkilpee1uC+axL32d5YmyP0URpSNJK9BhZo= +-----END EC PRIVATE KEY----- diff --git a/examples/docker/ca/pki/secrets/root_ca.crt b/examples/docker/ca/pki/secrets/root_ca.crt new file mode 100644 index 00000000..db24c535 --- /dev/null +++ b/examples/docker/ca/pki/secrets/root_ca.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBfDCCASGgAwIBAgIQY0CXerxuM+EhTbpVxxLRKjAKBggqhkjOPQQDAjAcMRow +GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xODExMDIyMzU0MTNaFw0yODEw +MzAyMzU0MTNaMBwxGjAYBgNVBAMTEVNtYWxsc3RlcCBSb290IENBMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAEEGa7ZeL4WVIfPFDS7glJkIVsITVQgjfyz+AhcYaS +rkJZlWOGZ60br9uE/wEfUcX1zavrX1Wz+bSJzTvT0AVBNqNFMEMwDgYDVR0PAQH/ +BAQDAgGmMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFNCKTrwMgZ5/VIQ6 +jkM153xFN2mDMAoGCCqGSM49BAMCA0kAMEYCIQCRA4EdlTTMhs2Zd1cT75ZgxeGa +mjVPl1vqBxLkHqEO+QIhAPKVm7E452ZBe2o5rQRxGwa94MI+CyuEIH9md3nTgWWX +-----END CERTIFICATE----- diff --git a/examples/docker/ca/pki/secrets/root_ca_key b/examples/docker/ca/pki/secrets/root_ca_key new file mode 100644 index 00000000..fe4016df --- /dev/null +++ b/examples/docker/ca/pki/secrets/root_ca_key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,98fdc560ba714aebb9fd4b714395d8ce + +2bFn8yRb8lMvDR6oh22PocfhXdaoVNt4QwHCJNy0K0fG8CMokwDfEec//LseP6rA +7/EV11+ZgoN9xyTNe1kB6zFv7/kzCpRm23sqtyio+8xXWnLZNYKBRYYEeJWBUqqd +GAfazg4ZFzoIH5TEPWCEAp7M9CVvtiw1SeA/zjewp2k= +-----END EC PRIVATE KEY----- diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml new file mode 100644 index 00000000..d38708bd --- /dev/null +++ b/examples/docker/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3.3' + +services: + ca: + image: step-ca-test:latest + ports: + - "8443:443" + restart: always + + renewer: + depends_on: + - ca + image: step-renewer-test:latest + volumes: + - certificates:/var/local/step + secrets: + - password + environment: + STEPPATH: /home/step + STEP_CA_URL: https://ca + STEP_FINGERPRINT: 84a033e84196f73bd593fad7a63e509e57fd982f02084359c4e8c5c864efc27d + STEP_ROOT: /var/local/step/root_ca.crt + STEP_KID: DmAtZt2EhmZr_iTJJ387fr4Md2NbzMXGdXQNW1UWPXk + STEP_PASSWORD_FILE: /run/secrets/password + COMMON_NAME: nginx + + nginx: + depends_on: + - renewer + image: nginx-test:latest + ports: + - "4443:443" + volumes: + - certificates:/var/local/step:ro + restart: always + +volumes: + certificates: + +secrets: + password: + file: ./password.txt diff --git a/examples/docker/nginx/Dockerfile b/examples/docker/nginx/Dockerfile new file mode 100644 index 00000000..b46aca98 --- /dev/null +++ b/examples/docker/nginx/Dockerfile @@ -0,0 +1,11 @@ +FROM nginx:alpine + +RUN apk add inotify-tools +RUN mkdir -p /var/local/step +COPY site.conf /etc/nginx/conf.d/ +COPY certwatch.sh / +COPY entrypoint.sh / + +# Certificate watcher and nginx +ENTRYPOINT ["/entrypoint.sh"] +CMD ["nginx", "-g", "daemon off;"] diff --git a/examples/docker/nginx/certwatch.sh b/examples/docker/nginx/certwatch.sh new file mode 100755 index 00000000..ccee8c34 --- /dev/null +++ b/examples/docker/nginx/certwatch.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +while true; do + inotifywait -e modify /var/local/step/site.crt + nginx -s reload +done diff --git a/examples/docker/nginx/entrypoint.sh b/examples/docker/nginx/entrypoint.sh new file mode 100755 index 00000000..7d9cc035 --- /dev/null +++ b/examples/docker/nginx/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Wait for renewer +sleep 10 + +# watch for the update of the cert and reload nginx +/certwatch.sh & + +# Run docker CMD +exec "$@" \ No newline at end of file diff --git a/examples/docker/nginx/site.conf b/examples/docker/nginx/site.conf new file mode 100644 index 00000000..2dbb3d93 --- /dev/null +++ b/examples/docker/nginx/site.conf @@ -0,0 +1,11 @@ +server { + listen 443 ssl; + server_name localhost; + ssl_certificate /var/local/step/site.crt; + ssl_certificate_key /var/local/step/site.key; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } +} \ No newline at end of file diff --git a/examples/docker/password.txt b/examples/docker/password.txt new file mode 100644 index 00000000..f3097ab1 --- /dev/null +++ b/examples/docker/password.txt @@ -0,0 +1 @@ +password diff --git a/examples/docker/renewer/Dockerfile b/examples/docker/renewer/Dockerfile new file mode 100644 index 00000000..ca116a68 --- /dev/null +++ b/examples/docker/renewer/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:latest + +RUN mkdir -p /var/local/step +ADD step /usr/local/bin/step +ADD crontab /var/spool/cron/crontabs/root +RUN chmod 0644 /var/spool/cron/crontabs/root + +COPY entrypoint.sh / +ENTRYPOINT ["/entrypoint.sh"] +CMD ["/usr/sbin/crond", "-l", "2", "-f"] diff --git a/examples/docker/renewer/crontab b/examples/docker/renewer/crontab new file mode 100644 index 00000000..db8e681b --- /dev/null +++ b/examples/docker/renewer/crontab @@ -0,0 +1,2 @@ +# min hour day month weekday command +* * * * * rm -f /var/local/step/site-new.crt && step ca renew --out /var/local/step/site-new.crt /var/local/step/site.crt /var/local/step/site.key && mv /var/local/step/site-new.crt /var/local/step/site.crt diff --git a/examples/docker/renewer/entrypoint.sh b/examples/docker/renewer/entrypoint.sh new file mode 100755 index 00000000..6add1ed2 --- /dev/null +++ b/examples/docker/renewer/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# Wait for CA +sleep 5 + +if [ ! -f /var/local/step/root_ca.crt ]; then + # Donwload the root certificate + step ca root /var/local/step/root_ca.crt +fi + +if [ ! -f /var/local/step/site.crt ]; then + # Get token + STEP_TOKEN=$(step ca token $COMMON_NAME) + # Donwload the root certificate + step ca certificate --token $STEP_TOKEN $COMMON_NAME /var/local/step/site.crt /var/local/step/site.key +fi + +exec "$@" \ No newline at end of file