Hashicorp Vault - API access with Go Program
Hashicorp Vault, solution to store secrets, certs, access tokens and sensitive data of your application. It exposes an API by which we can access secrets from our application.To know more about Vault, check out docs
In this post, am gonna share basic code template in Golang to access Vault. Assuming you have some knowledge of Golang
. I am diving code into two parts.
- Formater to prepare request and response to and from server.
- HTTP Handler to work on Vault API with methods supporting POST, GET, DELETE for writing, reading and deleting contents. In here, were are working on KV secrets engine of VAULT
Formatter
Following code consists of method to unmarshal json to Response
Data struct
package utils
// Result response struct from vault API
type Result struct {
Request_id string
Lease_id string
Renewable bool
Lease_duration int
Data data
Metadata resultMetadata
Wrap_info string
Warnings string
Auth string
Errors []string
}
type data struct {
Data map[string]interface{}
Metadata map[string]interface{}
Keys []string
}
type resultMetadata struct {
Created_time time.Time
Deletion_time time.Time
version int
Destroyed bool
}
// RequestData request payload for storing data
type RequestData struct {
Options options `json:"options"`
Data map[string]interface{} `json:"data"`
}
type options struct {
Cas int `json:"cas"` // to label in lowercase while marshalling
}
// Formatter formats
func Formatter(data []byte) {
var v Result
if err := json.Unmarshal(data, &v); err != nil {
panic(err)
}
return v
}
HTTP Handler
This file encaps code for preparing HTTP calls and implementing API calls. QueryStore
, reads data from vault kv secrets engine. LoadStore
, loads data from vault kv secrets engine. Delete
obviously deletes data secrets engine.
package utils
func prepareRequest(vaultEndpoint, method string, data []byte) (*http.Client, *http.Request) {
vaultKey, err := os.GetEnv("VAULT_KEY")
httpClient := &http.Client{
Timeout: 30 * time.Second, // 30s timeout
}
req, err := http.NewRequest(method, vaultEndpoint, bytes.NewBuffer(data))
if err != nil {
panic(err)
}
// note we are adding token to header
req.Header.Add("X-Vault-Token", vaultKey)
return httpClient, req
}
// QueryStore reads data with GET method
func QueryStore(vaultEndpoint string) Result {
var data []byte
client, req := prepareRequest(vaultEndpoint, "GET", nil)
if res, err := client.Do(req); err != nil {
panic(err)
} else {
data, _ = ioutil.ReadAll(res.Body)
defer res.Body.Close()
}
return Formatter(data)
}
// LoadStore loads store with data regarding
func LoadStore(vaultEndpoint string, data []byte) (Result, error) {
client, req := prepareRequest(vaultEndpoint, "POST", data)
res, err := client.Do(req)
if err != nil {
return Result{}, err
}
data, _ = ioutil.ReadAll(res.Body)
defer res.Body.Close()
return FormatResult(data), nil
}
// DeleteStore deletes item with key
func DeleteStore(vaultEndpoint string) (Result, error) {
client, req := prepareRequest(vaultEndpoint, "DELETE", nil)
res, err := client.Do(req)
if err != nil {
return Result{}, err
}
defer res.Body.Close()
return Result{}, nil
}
Here is the example of how to call above API calls. Before using, checkout vault API to know more API calls of Vault. For this example, lets assume the path for KV engine is /secrets
func loadCred() {
loadUrl = vaultUrl + "v1/secrets/data"
res, err := utils.QueryStore(vaultStorage.API+storeURL+key)
if err != nil || len(res.Errors) > 0 {
// handle error
}
// handle response here
}