Use Vault with client certificates
Still learning to use Vault, I want to experiment accessing the Vault using a client certificate instead of a regular token. This proved to be not-so-easy for reasons I hadn’t foreseen…
Step 1 - generate a certificate
This is well-documented on the Internet, with one caveat: many how-to’s don’t let you protect your key with a passphrase. I think you should. And this exactly turned out to be the unforeseen reason this whole thing was harder than expected…!
For documentation reasons, I will add my own method.
First create a config file to facilitate experimentation; please change the values in the [ dn ]
section and eg. call the file cert.conf
:
[ req ]
default_bits = 2048
prompt = no
encrypt_key = yes
default_md = sha256
distinguished_name = dn
[ dn ]
C = Country Name (2 letter code)
ST= State or Province Name (full name)
L = Locality Name (eg, city)
O = Personal
emailAddress= you@yourdomain.com
0.CN= Your Name
Now generate a key and certificate with passphrase using this config file (I will use th3p@ss
as the passphrase throughout this text):
openssl req -config cert.conf -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 14
The certificate will only be valid for 14 days - plenty of time to experiment :-) You can now verify the certificate:
$ openssl x509 -in cert.pem -noout -text
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 11244389068616569346 (0x9c0c1848572d1202)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=BE, ST=Vlaams-Brabant, L=Rotselaar, O=Personal/emailAddress=jo.vandeginste@gmail.com, CN=Jo Vandeginste
Validity
Not Before: Jul 20 13:07:24 2016 GMT
Not After : Aug 3 13:07:24 2016 GMT
Subject: C=BE, ST=Vlaams-Brabant, L=Rotselaar, O=Personal/emailAddress=jo.vandeginste@gmail.com, CN=Jo Vandeginste
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
[...]
Step 2 - connect to the Vault server
Now try to contact your Vault server with it:
$ curl https://vault.example.com/v1/sys/health --cert cert.pem --key key.pem
curl: (35) error reading X.509 key or certificate file: Decryption has failed.
What? So this I spent a long time figuring out, until the answer became obvious: the key is encrypted (passphrase), but curl doesn’t prompt for the passphrase. After some digging around, I found out that you need to put the passphrase together with the cert.pem
:
$ curl https://vault.example.com/v1/sys/health --cert cert.pem:th3p@ss --key key.pem
{"initialized":true,"sealed":false,"standby":false,"server_time_utc":1469020614}
Okay, better now! (Not really, since I see my passphrase clear text on the command line… There are ways around this but I still feel dirty now!)
Sadly, the vault
binary does not (yet) support encrypted keys:
$ VAULT_ADDR=https://vault.example.com vault auth -method=cert -client-cert=cert.pem -client-key=key.pem
Error initializing client to auth: crypto/tls: failed to parse private key
I found no solution, so for the binary to work we need an unencrypted key. We can decrypt our current key:
$ openssl rsa -in key.pem -out key.insecure.pem
Enter pass phrase for key.pem:
writing RSA key
With this insecure key, both curl and vault will work:
$ curl https://vault.example.com/v1/sys/health --cert cert.pem --key key.insecure.pem
{"initialized":true,"sealed":false,"standby":false,"server_time_utc":1469020745}
$ VAULT_ADDR=https://vault.example.com vault auth -method=cert -client-cert=cert.pem -client-key=key.insecure.pem
Error making API request.
URL: PUT https://vault.example.com/v1/auth/cert/login
Code: 400\. Errors:
* invalid certificate or no client certificate supplied
Okay, it doesn’t actually work, since the Vault server doesn’t know my certificate yet. This is the next step.
Step 3 - uploading your client certificate
This was surprisingly easy with sufficient documentation on Vault’s site. You do obviosly need a token with sufficient rights to perform these changes. I’m working on an experimental setup, so I have the root token my-token
at hand…
First make sure the cert
backend is enabled, then upload your new certificate (and grant it the root
policy for testing):
$ VAULT_TOKEN=my-token VAULT_ADDR=https://vault.example.com vault auth-enable cert # this may fail if it was already enabled
Successfully enabled 'cert' at 'cert'!
$ VAULT_TOKEN=my-token VAULT_ADDR=https://vault.example.com vault write auth/cert/certs/your.name display_name="Your Name" policies=root certificate=@cert.pem ttl=3600
Success! Data written to: auth/cert/certs/your.name
Verify the content of the auth/cert
:
$ VAULT_TOKEN=my-token VAULT_ADDR=https://vault.example.com vault read auth/cert/certs/your.name
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
display_name Your Name
policies root
ttl 3600
Step 4 - use your certificate
Now the real test :-) We can use our client certificate now to generate tokens to actually do something on the Vault. So step 1 is generating a token:
$ VAULT_ADDR=https://vault.example.com vault auth -method=cert -client-cert=cert.pem -client-key=key.insecure.pem
Successfully authenticated! You are now logged in.
The token below is already saved in the session. You do not
need to "vault auth" again with the token.
token: 52xxxx8e-xxxx-e22b-xxxx-e326xxxx0b7b
token_duration: 3599
token_policies: [root]
So now I have a Vault token 52xxxx8e-xxxx-e22b-xxxx-e326xxxx0b7b
, which I can use for 1 hour to do anything root
can (that was the policy I assigned to my certificate). Let’s do some basic stuff! First I exported VAULT_TOKEN
and VAULT_ADDR
for the sake of brevity.
Look at the information about the token itself:
$ vault token-lookup
Key Value
--- -----
accessor 98xxxx5d-xxxx-3906-xxxx-fcebxxxx8e58
creation_time 1469021773
creation_ttl 3600
display_name cert-Your Name
explicit_max_ttl 0
id 52xxxx8e-xxxx-e22b-xxxx-e326xxxx0b7b
meta map[authority_key_id: cert_name:your.name common_name:Your Name subject_key_id:]
num_uses 0
orphan true
path auth/cert/login
policies [root]
renewable true
role
ttl 3468
Let’s do some Vaulty actions with the token:
$ vault list /secret/ # List the currently existing keys at /secret/
Keys
teams/
test
$ vault read /secret/test # Read pre-existing data that I put there earlier
Key Value
--- -----
refresh_interval 2592000
a b
c 4
$ vault write /secret/test d=5 # Overwrite the pre-existing data
Success! Data written to: secret/test
$ vault read /secret/test # Yup, it's gone now!
Key Value
--- -----
refresh_interval 2592000
d 5
Now we wait for the rest of the hour to pass and verify our token has expired:
$ vault token-lookup
error looking up token: Error making API request.
URL: GET https://vault.example.com/v1/auth/token/lookup-self
Code: 400\. Errors:
* permission denied
You can get a new token any time with the auth
command, until your certificate expires (hopefully not after it expires, but I haven’t tried it)
comments powered by Disqus