There are only two hard things in Computer Science: cache invalidation and naming things. This old truth is surprisingly significant about web development today since it doesn't mention one underestimated topic - security. Developers are often scared by and completely ignore security considerations in their estimations and planning. We like to think about security as a thing that is covered for us - e.g. Rails is considered as a framework with default settings adjusted to quite high security standards and we like to think that this is enough.
Unfortunately, without the knowledge and support of proper tools we can easily expose ourselves to security breaches – so today I would like to add two new tools to your toolbelt and show how you can easily use them together.
What is Vault?
Since Rails 4.1 we all know about the secrets.yml
file. Some people still prefer Figaro, dotenv or ENVied. Regardless of the solution that you prefer you have to somehow deliver confidential data – i.e. passwords, keys, etc. – to your app and you do that either with a plain file – e.g. secrets.yml
or by a system ENV
variable.
In the case of a file, you have to store it somewhere and there are zillions of reasons why this place shouldn't be your repository (on the other hand for personal use things like ansible vault could be enough).
You can always manipulate regular file directly on a server but this is very error prone, inconvenient and has many other drawbacks. And it doesn't scale. For ENV
you probably still have to base on a file if you cannot support yourself with magic tooling like heroku config:set
, so the problem doesn't differ too much.
This is where Vault enters the picture. In sum, it is a server that safely keeps your secrets and allows to access them via an API.
For more details please follow the documentation.
The thing is that thanks to this tool, you can finally store your credentials in a secure way, fetch them programmatically directly to your app and even define access rules who can manipulate data.
To install Vault you have to extract the binary and run it with the following instructions from the documentation. There is no single best config since it would base on your preferred storage (encrypted file, S3, consul, etc), authentication mechanism and structure of your organization. Everything is well described on the Vault site, so instead of doing copy paste, I will introduce another tool.
Free SSL/TLS
For many years, the secure layer of HTTP protocol was reserved for privileged use only because of the price - even more than thousands of dollars for a wildcard certificate each year. Most simply decided to cut that cost and transfer everything in plain text, so it didn't actually matter if a site had been built in a secure way or not - it was exposed anyway.
Fortunately two years ago we were able to see the light at the end of the tunnel when Let's Encrypt - a free SSL/TLS for everyone - was announced. Today it is not just an experiment but one of the foundations of the future web.
We’ve now issued more than 10 million certificates.
— Let's Encrypt (@letsencrypt) Sep 9, 2016
There are many articles describing how to use this free certificate on your website - official documentation included - so again I won't repeat that work here.
Instead, I will show you how to secure Vault transmission with Let's Encrypt certificate since Vault API communicates over http(s) protocol.
Tutorial
In this short tutorial, I base on Ubuntu 16.04 with Systemd and Vault 0.6.2 but it shouldn't differ too much in a different setup. Before you start you have to register a domain and point it to the server where Vault will be running.
Step 1: Create standalone cert
For our purpose, we would need a standalone certificate which means that it won't involve any additional web server. You can simply obtain it by:
$ letsencrypt certonly --standalone -d myvaultdomain.example.com
This will ask a few questions but you can provide answers in advance by passing additional arguments so you can fully automate that process if you have to. Finally, your new certificate will land under the /etc/letsencrypt/live/myvaultdomain.example.com/fullchain.pem
path.
Step 2: Configure Vault
I won't describe all the Vault config file options except a crucial one for this point - listener
which in fact is really self-documenting:
# config.hcl
listener "tcp" {
address = "myvaultdomain.example.com"
tls_cert_file = "/etc/letsencrypt/live/myvaultdomain.example.com/cert.pem"
tls_key_file = "/etc/letsencrypt/live/myvaultdomain.example.com/fullchain.pem"
}
Step 3: Run Vault
Below you will find an example of a Systemd service file but you can actually use anything that you use to run your services:
# /etc/systemd/system/vault.service
[Unit]
Description=Vault Server
Documentation=https://www.vaultproject.io/docs/
After=network-online.target
[Service]
EnvironmentFile=-/usr/local/etc/vault/env
ExecStart=/usr/local/bin/vault server $OPTIONS -config /usr/local/etc/vault/config.hcl
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
KillMode=process
Type=simple
[Install]
WantedBy=multi-user.target
And that's it! The only thing missing is enabling and starting the systemd service:
$ systemctl enable vault.service
$ systemctl start vault.service
Voilà! And don't forget to unseal your Vault!
Bonus: Automatic cert renewal
Let's Encrypt certificates are short lived (3 months) but are also designed for full automation so we shouldn't worry about that fact. Actually you can support that automation easily when using systemd. E.g.:
# /etc/systemd/system/letsencrypt.service
[Unit]
Description=Let's Encrypt renewal service
Documentation=https://certbot.eff.org/#ubuntuxenial-nginx
After=network.target
[Service]
ExecStart=/usr/bin/letsencrypt renew --agree-tos
ExecStartPost=/bin/systemctl reload vault.service
Type=oneshot
This simple service will try to renew all your certificates found on a server and reload a Vault service so it can be aware of renewed certs. Newer versions of letsencrypt allow us to pass this post trigger directly to the renew
command. Unfortunately, this option is not yet available on current Ubuntu LTS.
# /etc/systemd/system/letsencrypt.timer
[Unit]
Description=Daily renewal of Let's Encrypt's certificates
[Timer]
OnCalendar=daily
RandomizedDelaySec=10
Persistent=true
[Install]
WantedBy=timers.target
Finally, we have to add systemd timer which will run our service on a daily basis - by default, it will run a service with the same name as timer, but you can adjust this if you want. You can enable and start the timer in the same way as it was with vault.service
:
$ systemctl enable letsencrypt.timer
$ systemctl start letsencrypt.timer
Conclusion
That was simple, wasn't it? After a few simple steps, we finished with a server for secure credentials management and we protected its API communication with HTTPS protocol. Now you can dig into the Vault documentation and learn how to put your keys into a safe... well, vault.