This README and repository contains the instructions and materials necessary to replicate the demo from my RocDev Talk: "Build a PKI for your Apps in 5 Minutes with Hashicorp Vault"
This is designed to be a demo for development purposes. Literally nothing here is designed for production use. In fact, several actions in this demo (such as using only 1 unseal key for Vault) would be very, very bad in production.
If you intend to use Vault as a production PKI, ensure that you know what you're doing.
This demo will deploy the following:
- Vault, running in server mode on the local machine
- NGINX, running in a container
NGINX will be configured to:
- Enable TLS using a certificate created with the deployed Vault CA
- Require clients to present a certificate signed by the Vault CA
For the sake of demonstration, cURL will be used as the client in this topology. You can use your imagination and pretend this is a real app.
To follow along, you will need:
- A Linux computer (this is untested with Mac or Windows)
- Editor of your preference
First, clone this repo:
git clone firstname.lastname@example.org:acritelli/five-minute-pki.git
Download latest Vault binary, unzip it, and move it to a location in your
wget https://releases.hashicorp.com/vault/1.7.1/vault_1.7.1_linux_amd64.zip unzip vault_1.7.1_linux_amd64.zip mv vault /usr/local/bin/
Start up a Vault server using the provided config. This will launch Vault using persistent, file-based storage in
data/ so that you can stop and restart Vault, if needed.
vault server -config=config/
With Vault running in server mode, launch another terminal to perform additional commands from. Set your
VAULT_ADDR environment variable:
Initialize Vault. Note that this only needs to be done once. You don't have to repeat this if you stop and restart Vault. The
tee command saves the unseal key and root token into a file for later retrieval.
$ vault operator init -key-shares=1 -key-threshold=1 | tee init.txt Unseal Key 1: HkWQna4pR8M7eW649Dijii/RazPOj4n+PItjOWq9yaY= Initial Root Token: s.YwDWkPH8qaRNuzkjIFPuAWww Vault initialized with 1 key shares and a key threshold of 1. Please securely distribute the key shares printed above. When the Vault is re-sealed, restarted, or stopped, you must supply at least 1 of these keys to unseal it before it can start servicing requests. Vault does not store the generated master key. Without at least 1 key to reconstruct the master key, Vault will remain permanently sealed! It is possible to generate new unseal keys, provided you have a quorum of existing unseal keys shares. See "vault operator rekey" for more information.
Unseal Vault. This must be done every time Vault is restarted. Use the unseal key from the
init action above.
$ vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 1 Threshold 1 Version 1.7.1 Storage Type file Cluster Name vault-cluster-3bf17c1a Cluster ID ef51a85e-1291-aa09-d580-4bf5f49f3f16 HA Enabled false
Log into Vault using the root token from the
init action above:
$ vault login Token (will be hidden): Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.YwDWkPH8qaRNuzkjIFPuAWww token_accessor 8SbuIyJeq1lfW9RbmIG8ScWc token_duration ∞ token_renewable false token_policies ["root"] identity_policies  policies ["root"]
Enable the PKI secrets engine and adjust the maximum lease TTL to 1 year:
$ vault secrets enable pki Success! Enabled the pki secrets engine at: pki/ $ vault secrets tune -max-lease-ttl=8760h pki Success! Tuned the secrets engine at: pki/
Tell Vault to generate a root CA certificate:
$ vault write pki/root/generate/internal \ common_name=example.com \ ttl=8760h Key Value --- ----- certificate <-- output snipped --> expiration 1652540586 issuing_ca <-- output snipped --> serial_number 42:6c:e5:1e:0b:5d:2a:bb:92:96:07:84:2b:22:28:45:9b:e8:d6:99
Create a new role that will allow you to sign certificates for example.com and its subdomains:
$ vault write pki/roles/example.com \ allowed_domains=example.com \ allow_subdomains=true Success! Data written to: pki/roles/example.com
Generate a certificate for the web server at www.example.com:
❯ vault write pki/issue/example.com common_name=www.example.com Key Value --- ----- certificate <-- output snipped --> expiration 1623769552 issuing_ca <-- output snipped --> private_key <-- output snipped --> private_key_type rsa serial_number 7b:5a:4a:d0:f7:4f:af:48:62:17:8f:a7:0b:01:d0:d7:4c:91:2c:73
Copy the following values from the above output to the specified files:
|certificate||nginx/tls/www.example.com.crt||The certificate for www.example.com|
|issuing_ca||nginx/tls/example.com.ca.crt||The CA certificate for the Vault CA. Used for client certificate verification|
|private_key||nginx/tls/www.example.com.key||The private key for the www.example.com certificate|
Open yet another terminal and launch an NGINX container. Be sure to update the bind mounts below to point at the appropriate directories on your system:
docker run --rm \ -v /home/tony/Desktop/five_minute_pki/nginx/html:/usr/share/nginx/html \ -v /home/tony/Desktop/five_minute_pki/nginx/conf.d/:/etc/nginx/conf.d \ -v /home/tony/Desktop/five_minute_pki/nginx/tls:/etc/nginx/tls \ -p 8443:443 \ nginx
/etc/hosts file and add a line like the one below:
Attempt to access the website running in the container. It should fail at this point because you have not supplied a client certificate:
$ curl -k https://www.example.com:8443 <html> <head><title>400 No required SSL certificate was sent</title></head> <body> <center><h1>400 Bad Request</h1></center> <center>No required SSL certificate was sent</center> <hr><center>nginx/1.19.10</center> </body> </html>
Generate a client certificate:
$ vault write pki/issue/example.com common_name=client.example.com Key Value --- ----- certificate <-- output snipped --> expiration 1623769850 issuing_ca <-- output snipped --> private_key <-- output snipped --> private_key_type rsa serial_number 7d:53:76:52:91:85:05:9c:cd:9a:15:c9:a9:fc:ea:af:de:38:ae:fd
private_key values, concatenated together, into a file called
Attempt to access the website again, but this time specify the client certificate:
$ curl -k -E client.example.com.pem https://www.example.com:8443 <h1>Hello!</h1>
If you ever intend to try using Vault as a production PKI, be sure to check out some of the resources below.