Storing Secrets¶
There are currently two ways to provide “secrets” for containers:
- Encrypted values inserted into environment variable
- Mount a directory from the host system
Encrypted Variables¶
Guide¶
Note: this guide covers both server setup and configuring specific containers. Usually setup (steps 1-3) is done once. And adding keys to a container (steps 4-5) is more regular job.
Create a key private key on the server:
ssh-keygen -f /etc/lithos/keys/main.key -t ed25519 -P ""
You can create a shared key or a per-project key. Depending on your convenience. Synchronize the key accross all the servers in the same cluster. This key should never leave that set of servers.
Add the reference to the key into your Sandbox Config (e.g.
/etc/lithos/sandboxes/myapp.yaml
):secrets-private-key: /etc/lithos/keys/main.key secrets-namespaces: [myapp]
You can omit
secrets-namespaces
if you’re sole owner of this server/cluster (it allows only empty string as a namespace). You can also make per-process namespaces (extra-secrets-namespaces
).Publish your public key
/etc/lithos/keys/main.key.pub
for your users. (Cryptography guarantees that even if this key is shared publically, i.e. commited into a git repo, or accessible over non-authorized web URL system is safe)Your users may now fetch the public key and encrypt their secrets with
lithos_crypt
(get static binary on releases page):$ lithos_crypt encrypt -k main.key.pub -n myapp -d the_secret v2:ROit92I5:KqWSX0BY:8MtOoWUX:nHcVCIWZG2hivi0rKa8MRnAIbt7TDTHB8YC8bBnac3IGMzk57R/HsBhxeqCdC7Ljyf8pszBBjIGD33f6lwBM7Q==
The important thing here is to encrypt with the right key and the right namespace.
Then put a secret into your Container Configuration:
executable: /usr/bin/python3 environ: DATABASE_URL: postgresql://myappuser@db.example.com/myappdb secret-environ: DATABASE_PASSWORD: v2:ROit92I5:KqWSX0BY:8MtOoWUX:nHcVCIWZG2hivi0rKa8MRnAIbt7TDTHB8YC8bBnac3IGMzk57R/HsBhxeqCdC7Ljyf8pszBBjIGD33f6lwBM7Q==
That’s it. To add a new password to the same or another container repeat steps 4-5.
This scheme is specifically designed to be safe to store in a (public) git repository by using secure encryption.
Ananomy of the Encrypted Key¶
As you might see there is a pattern in an encrypted key. Here is how it looks like:
v2:ROit92I5:KqWSX0BY:8MtOoWUX:nHcVCIWZG2hivi0rKa8MRnAIbt7TDTHB8YC8bBnac3IGM‥wBM7Q==
^-- encrypted "namespace:actual_secret"
^^^^^^^^-- short hash of the password itself
^^^^^^^^-- short hash of the secrets namespace
^^^^^^^^-- short hash of the public key used for encryption
^^-- encryption version
Note the following things:
Only version
v2
is supported (v1
was broken and dropped in 0.16.0)The short hash is base64-encoded 6-bytes length blake2b hash of the value. You can check in using
b2sum
utility from recent version ofcoreutils
:$ echo -n "the_secret" | b2sum -l48 | xxd -r -p | base64 8MtOoWUX
(Note: we need
xxd
becauseb2sum
outputs hexadecimal bytes, also note-n
inecho
command, as it’s a common mistake, without the optionecho
outputs newline at the end).The encrypted payload contains
<namespace>:
prefix. While we could check just the hash. Prefix allows providing better error messages.The underlying encyrption is curve25519xsalsa20poly1305 which is compatible with libnacl and libsodium.
Let’s see how it might be helpful, here is the list of keys:
1 2 3 | v2:h+M9Ue9x:82HdsExJ:Gd3ocJsr:/+f4ezLfKIP/mp0xdF7H6gfdM7onHWwbGFQX+M1aB+PoCNQidKyz/1yEGrwxD+i+qBGwLVBIXRqIc5FJ6/hw26CE
v2:ROit92I5:cX9ciQzf:Gd3ocJsr:LMHBRtPFpMRRrljNnkaU6Y9JyVvEukRiDs4mitnTksNGSX5xU/zADWDwEOCOtYoelbJeyDdPhM7Q1mEOSwjeyO317Q==
v2:ROit92I5:82HdsExJ:Gd3ocJsr:Hp3pngQZUos5b8ioKVUx40kegM1uDsYWwsWqC1cJ1/1KmQPQQWJZe86xgl1EOIxbuLj6PUlBH8yz5qCnWp//Ofbc
|
You can see that:
- All of them have same secret (3rd column)
- Second and third ones have same encryption key (1st column)
- First and third ones have the same namespace (2nd column)
This is useful for versioning and debugging problems. You can’t deduce the actual password from this data anyway unless your password is very simple (dictioanry attack) or you already know it.
Note: even if all three {encryption key, namespace, secret} match, the last part of data (encrypted payload) will be different each time you encode that same value. All of the outputs are equally right.
Security Notes¶
- Namespaces allow to divide security zones between many projects without nightmare of generating, syncing and managing secret keys per project.
- Namespaces match exactly they aren’t prefixes or any other kind of pattern
- If you rely on
lithos_switch
to switch containers securely (with untrusted Process Config), you need to use different private key per project (as otherwiseextra-secrets-namespaces
can be used to steal keys)