Sample code demonstrating an implementation of crypto.Signer for HashiCorp Vault
where the TLS connection certificates are provided by its PKI Secrets engine.
That is, you can start a golang HTTPS server and client where the certificates are provided by Vault. The client and server will use a provided VAULT_TOKEN
to acquire the secret. Vaults’ PKI secrets engine allows generates a new PKI Keypair for each authorized call
Note: there are a number of considerations you need to account for before using Vault based certificates as one end of mTLS:
Max lease of a certificate:
Vault based certificates are desinged to be temprorary. You can extend the lease of a cert by setting -max-lease-ttl=
but in the end, ths is just a temp token..
Public/Private keys are reissued and not saved. Vault does NOT save the public private keys so everytime you initialize Vault, a NEW keypair is generated and lives on the TTL cited above. Needless to say, this has significant operational ramifications on usage at scale for just TLS….But if you use this just to run a webswerver instance, that should be fine.
From the Vault documentation:
This secrets engine aligns with Vault's philosophy of short-lived secrets. As such it is not expected that CRLs will grow large; the only place a private key is ever returned is to the requesting client (this secrets engine does not store generated private keys, except for CA certificates).
Anyway, this article is similar to one for Google Cloud KMS mtls
Note: if it wasn’t clear already..this repo is not supported by Google
You can find the source here
If you’re still interested in using Vault for a TLS server for any reason…
You know the drill…this repo uses vault server running in https
itself (i.,e not -dev
mode). You can bootstrap with any CA but keep the public key for that handy (you’ll need it later when authenticating the clients)
Here is a sample Vault server.conf
. You can find a sample CA, public, private server in this repo.
backend "file" {
path = "filebackend"
}
ui = true
listener "tcp" {
address = "127.0.0.1:8200"
tls_cert_file = "crt_vault.pem"
tls_key_file = "key_vault.pem"
}
$ vault server -config=server.conf
(add to /etc/hosts)
127.0.0.1 vault.domain.com server.domain.com
# if needed:
$ vault operator init
$ export export VAULT_ADDR='https://vault.domain.com:8200'
$ export VAULT_CACERT=`pwd`/CA_crt.pem
$ export VAULT_TOKEN=<tokenfrominit>
$ vault operator unseal
RootToken
to enable the pki backendvault secrets enable pki
vault write pki/config/urls \
issuing_certificates="https://vault.domain.com:8200/v1/pki/ca" \
crl_distribution_points="http://vault.domain.com:8200/v1/pki/crl"
The last command creates the CA and CRL urls at vault.domain.com
. Since this is just a test and because i’m sure you don’t own domain.com
, add the following to your /etc/hosts
127.0.0.1 vault.domain.com server.domain.com
Note
vault.domain.com
is the actual Vault server address…the SNI values forcrt_vault.pem
are bound to that…i’m just lazy and didn’t reissue the cert…
domain.com
vault write pki/root/generate/internal common_name=domain.com ttl=8760h
vault write pki/config/urls issuing_certificates="https://vault.domain.com:8200/v1/pki/ca" crl_distribution_points="https://vault.domain.com:8200/v1/pki/crl"
Once you initialize the PKI engine, download the CA Vault just generated for you (infact you should see the CA cert cain once you run the previous command)
curl -s --cacert CA_crt.pem https://vault.domain.com:8200/v1/pki/ca | openssl x509 -inform DER -outform PEM -out Vault_CA.pem -in -
vault write pki/roles/domain-dot-com \
allowed_domains=domain.com \
allow_subdomains=true \
max_ttl=72h
vault policy write pki-policy-server pki_server.hcl
vault policy write pki-policy-client pki_client.hcl
Where the pki_server creates a certificate with CN=server.domain.com
and the client with CN=client.domain.com
You can tune/refine the poicies as needed/necessary (i.,e the pki “read” path isn’t ever used)
VAULT_TOKEN
representing the server and client:$ vault token create -policy=pki-policy-server
Key Value
--- -----
token s.IumzeFZVsWqYcJ2IjlGaqZby
token_accessor ogu3k56jcqeQFOAS2dyQgT5g
token_duration 768h
token_renewable true
token_policies ["default" "pki-policy-server"]
identity_policies []
policies ["default" "pki-policy-server"]
$ vault token create -policy=pki-policy-client
Key Value
--- -----
token s.BtpHNHEpxaWkEF1ThQKEupwL
token_accessor dCyZhYvuIsiWjEILcAFlyiDU
token_duration 768h
token_renewable true
token_policies ["default" "pki-policy-client"]
identity_policies []
policies ["default" "pki-policy-client"]
Edit server/main.go
and add in the VAULT_TOKEN
The root path where you run the client and server should also include both the CA for Vault and the CA that vault generated for your domain (CA_crt.pem
, Vault_CA.pem
)
r, err := sal.NewVaultCrypto(&sal.Vault{
CertCN: "server.domain.com",
VaultToken: "s.IumzeFZVsWqYcJ2IjlGaqZby",
VaultPath: "pki/issue/domain-dot-com",
VaultCAcert: "CA_crt.pem",
VaultAddr: "https://vault.domain.com:8200",
ExtTLSConfig: &tls.Config{
ClientCAs: clientCaCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
},
})
Same thing as above on client/main.go
but use the client’s token
r, err := sal.NewVaultCrypto(&sal.Vault{
CertCN: "client.domain.com",
VaultToken: "s.BtpHNHEpxaWkEF1ThQKEupwL",
VaultPath: "pki/issue/domain-dot-com",
VaultCAcert: "CA_crt.pem",
VaultAddr: "https://vault.domain.com:8200",
})
if all goes well, you should see a lowly 200 ok
indicating a successful mTLS connection
Finally, you don’t ofcourse have to use mTLS here…you can just run a webserver with one certificate in memory and be done with it.
This site supports webmentions. Send me a mention via this form.