Being a data company, Lusha takes security issues very seriously. This is why we use Hashicorp’s Vault.
What is Vault?
Vault by HashiCorp is an identity-based secret and encryption management system. It helps manage credentials effectively.
A modern system requires access to a multitude of secrets: database credentials, API keys for external services, etc.
Vault helps store your secret securely, define dynamic secrets, lease, renew and revoke secrets.
Authentication Methods
Vault implements various authentication methods in order to assign identity and a set of policies to a user.
Some of Its auth methods includes: AppRole, Kubernetes, AWS, Github, Google Cloud, JWT/OIDC, Kerberos, and Okta. For the sake of this article, I’ll focus on K8S auth and show you how to implement Vault’s K8S auth with Golang,
Kubernetes Authentication
In this method, we will leverage the Kubernetes Service Account Token.
This authentication method makes it easy to introduce a Vault token into a Kubernetes Pod.
In the section below we will implement a function that will authenticate to vault using K8S auth and will help us implement secrets fetching later on.
Let’s begin by defining some constants based on our vault environment and configuration.
Once that is done, we can initialize an HTTP client that will use us to authenticate to vault.
We’ll use our new HTTP client to create a vault client, also configuring the address to our vault server.
We are using vault’s command package and importing vaultKube
to create a new kubernetesAuthMethod
while using our predefined mountPath
and KubeTokenRole
.
Using the authed.Authenticate
method we’re able to read our token from the default service account path (that can also be configured while creating kubernetesAuthMethod
), using the returned token we’ll send an auth request to vault with the vc.Logical().Write
method, this method returns a client token which we will set on our vaultClient
.
Congrats! We’re now ready to observe our functioning vault client that will let us fetch secrets from vault.
But the above process is wrapped in sync.Once
. To ensure this operation only occurs once, we need to only do it the first time we request the client. After this initial time, each call to the GetVaultClient
function will only return the already authenticated client without authenticating again.
Getting a Secret
Now, after authenticating using K8S method we can start fetching our secrets.
Let’s see how it’s done and implement a helper function for it.
So what do we have here?
We’ve got two continuous functions: GetVaultSecret
, which gets a path to a secret and returns it, and extractSecret
, which casts vault’s response and assumes we are using JSON secret values. If you use a string of secrets, you can just replace the cast.
Whenever we need a secret, we’ll call GetVaultSecret
with the desired secret path.
As we can see, this function calls the GetVaultClient
function we implemented before, then reads the requested path from vault and pairs it using the extractSecret
function that casts the response as explained above.
If everything goes according to plan, we’ve got our secret! 🎉
Using Hashicorp’s Vault to manage secrets is highly recommended and will make your secret management easier. I hope I helped you with implementing your first Vault integration in golang.
Have fun managing your secrets!