Categorygithub.com/hashicorp/vault-client-go
modulepackage
0.4.3
Repository: https://github.com/hashicorp/vault-client-go.git
Documentation: pkg.go.dev

# README

vault-client-go

Go Reference Build

A simple HashiCorp Vault Go client library.

Note: This library is now available in BETA. Please try it out and give us feedback! Please do not use it in production.

Note: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, please responsibly disclose by contacting us at [email protected].

Contents

  1. Installation
  2. Examples
  3. Building the Library
  4. Under Development
  5. Documentation for API Endpoints

Installation

go get -u github.com/hashicorp/vault-client-go

Examples

Getting Started

Here is a simple example of using the library to read and write your first secret. For the sake of simplicity, we are authenticating with a root token. This example works with a Vault server running in -dev mode:

vault server -dev -dev-root-token-id="my-token"
package main

import (
	"context"
	"log"
	"time"

	"github.com/hashicorp/vault-client-go"
	"github.com/hashicorp/vault-client-go/schema"
)

func main() {
	ctx := context.Background()

	// prepare a client with the given base address
	client, err := vault.New(
		vault.WithAddress("http://127.0.0.1:8200"),
		vault.WithRequestTimeout(30*time.Second),
	)
	if err != nil {
		log.Fatal(err)
	}

	// authenticate with a root token (insecure)
	if err := client.SetToken("my-token"); err != nil {
		log.Fatal(err)
	}

	// write a secret
	_, err = client.Secrets.KvV2Write(ctx, "foo", schema.KvV2WriteRequest{
		Data: map[string]any{
			"password1": "abc123",
			"password2": "correct horse battery staple",
		}},
		vault.WithMountPath("secret"),
	)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("secret written successfully")

	// read the secret
	s, err := client.Secrets.KvV2Read(ctx, "foo", vault.WithMountPath("secret"))
	if err != nil {
		log.Fatal(err)
	}
	log.Println("secret retrieved:", s.Data.Data)
}

Authentication

In the previous example we used an insecure (root token) authentication method. For production applications, it is recommended to use approle or one of the platform-specific authentication methods instead (e.g. Kubernetes, AWS, Azure, etc.). The functions to access these authentication methods are automatically generated under client.Auth. Below is an example of how to authenticate using approle authentication method. Please refer to the approle documentation for more details.

resp, err := client.Auth.AppRoleLogin(
	ctx,
	schema.AppRoleLoginRequest{
		RoleId:   os.Getenv("MY_APPROLE_ROLE_ID"),
		SecretId: os.Getenv("MY_APPROLE_SECRET_ID"),
	},
	vault.WithMountPath("my/approle/path"), // optional, defaults to "approle"
)
if err != nil {
	log.Fatal(err)
}

if err := client.SetToken(resp.Auth.ClientToken); err != nil {
	log.Fatal(err)
}

The secret identifier is often delivered as a wrapped token. In this case, you should unwrap it first as demonstrated here.

Using Generic Methods

The library provides the following generic methods which let you read, modify, list, and delete an arbitrary path within Vault:

client.Read(...)
client.ReadRaw(...)

client.Write(...)
client.WriteFromBytes(...)
client.WriteFromReader(...)

client.List(...)

client.Delete(...)

For example, client.Secrets.KvV2Write(...) from the Getting Started section could be rewritten using a generic client.Write(...) like so:

_, err = client.Write(ctx, "/secret/data/foo", map[string]any{
	"data": map[string]any{
		"password1": "abc123",
		"password2": "correct horse battery staple",
	},
})

Using Generated Methods

The library has a number of generated methods corresponding to the known Vault API endpoints. They are organized in four categories:

client.Auth     // methods related to authentication
client.Secrets  // methods related to various secrets engines
client.Identity // methods related to identities, entities, and aliases
client.System   // various system-wide methods

Below is an example of accessing the generated MountsListSecretsEngines method (equivalent to vault secrets list or GET /v1/sys/mounts):

resp, err := client.System.MountsListSecretsEngines(ctx)
if err != nil {
	log.Fatal(err)
}

for engine := range resp.Data {
	log.Println(engine)
}

Modifying Requests

You can modify the requests in one of two ways, either at the client level or by decorating individual requests. In case both client-level and request-specific modifiers are present, the following rules will apply:

  • For scalar values (such as vault.WithToken example below), the request-specific decorators will take precedence over the client-level settings.
  • For slices (e.g. vault.WithResponseCallbacks), the request-specific decorators will be appended to the client-level settings for the given request.
  • For maps (e.g. vault.WithCustomHeaders), the request-specific decorators will be merged into the client-level settings using maps.Copy semantics (appended, overwriting the existing keys) for the given request.
// all subsequent requests will use the given token & namespace
_ = client.SetToken("my-token")
_ = client.SetNamespace("my-namespace")

// for scalar settings, request-specific decorators take precedence
resp, err := client.Secrets.KvV2Read(
	ctx,
	"my-secret",
	vault.WithToken("request-specific-token"),
	vault.WithNamespace("request-specific-namespace"),
)

Overriding Default Mount Path

Vault plugins can be mounted at arbitrary mount paths using -path command-line argument:

vault secrets enable -path=my/mount/path kv-v2

To accommodate this behavior, the requests defined under client.Auth and client.Secrets can be offset with mount path overrides using the following syntax:

// Equivalent to client.Read(ctx, "my/mount/path/data/my-secret")
secret, err := client.Secrets.KvV2Read(
	ctx,
	"my-secret",
	vault.WithMountPath("my/mount/path"),
)

Adding Custom Headers and Appending Query Parameters

The library allows adding custom headers and appending query parameters to all requests. vault.WithQueryParameters is primarily intended for the generic client.Read, client.ReadRaw, client.List, and client.Delete:

resp, err := client.Read(
    ctx,
    "/path/to/my/secret",
    vault.WithCustomHeaders(http.Header{
        "x-test-header1": {"a", "b"},
        "x-test-header2": {"c", "d"},
    }),
    vault.WithQueryParameters(url.Values{
        "param1": {"a"},
        "param2": {"b"},
    }),
)

Response Wrapping & Unwrapping

Please refer to the response-wrapping documentation for more background information.

// wrap the response with a 5 minute TTL
resp, err := client.Secrets.KvV2Read(
	ctx,
	"my-secret",
	vault.WithResponseWrapping(5*time.Minute),
)
wrapped := resp.WrapInfo.Token

// unwrap the response (usually done elsewhere)
unwrapped, err := vault.Unwrap[schema.KvV2ReadResponse](ctx, client, wrapped)

Error Handling

There are a couple specialized error types that the client can return:

  • ResponseError is the error returned when Vault responds with a status code outside of the 200 - 399 range.
  • RedirectError is the error returned when the client fails to process a redirect response.

The client also provides a convenience function vault.IsErrorStatus(...) to simplify error handling:

s, err := client.Secrets.KvV2Read(ctx, "my-secret")
if err != nil {
	if vault.IsErrorStatus(err, http.StatusForbidden) {
		// special handling for 403 errors
	}
	if vault.IsErrorStatus(err, http.StatusNotFound) {
		// special handling for 404 errors
	}
	return err
}

Using TLS

To enable TLS, simply specify the location of the Vault server's CA certificate file in the configuration:

tls := vault.TLSConfiguration{}
tls.ServerCertificate.FromFile = "/tmp/vault-ca.pem"

client, err := vault.New(
	vault.WithAddress("https://localhost:8200"),
	vault.WithTLS(tls),
)
if err != nil {
	log.Fatal(err)
}
...

You can test this with a -dev-tls Vault server:

vault server -dev-tls -dev-root-token-id="my-token"

Using TLS with Client-side Certificate Authentication

tls := vault.TLSConfiguration{}
tls.ServerCertificate.FromFile = "/tmp/vault-ca.pem"
tls.ClientCertificate.FromFile = "/tmp/client-cert.pem"
tls.ClientCertificateKey.FromFile = "/tmp/client-cert-key.pem"

client, err := vault.New(
	vault.WithAddress("https://localhost:8200"),
	vault.WithTLS(tls),
)
if err != nil {
	log.Fatal(err)
}

resp, err := client.Auth.CertLogin(ctx, schema.CertLoginRequest{
	Name: "my-cert",
})
if err != nil {
	log.Fatal(err)
}

if err := client.SetToken(resp.Auth.ClientToken); err != nil {
	log.Fatal(err)
}

Note: this is a temporary solution using a generated method. The user experience will be improved with the introduction of auth wrappers.

Loading Configuration from Environment Variables

client, err := vault.New(
	vault.WithEnvironment(),
)
if err != nil {
	log.Fatal(err)
}
export VAULT_ADDR=http://localhost:8200
export VAULT_TOKEN=my-token
go run main.go

Logging Requests & Responses with Request/Response Callbacks

client.SetRequestCallbacks(func(req *http.Request) {
	log.Println("request:", *req)
})
client.SetResponseCallbacks(func(req *http.Request, resp *http.Response) {
	log.Println("response:", *resp)
})

Additionally, vault.WithRequestCallbacks(..) / vault.WithResponseCallbacks(..) can be used to inject callbacks for individual requests. These request-level callbacks will be appended to the list of the respective client-level callbacks for the given request.

resp, err := client.Secrets.KvV2Read(
	ctx,
	"my-secret",
	vault.WithRequestCallbacks(func(req *http.Request) {
		log.Println("request:", *req)
	}),
	vault.WithResponseCallbacks(func(req *http.Request, resp *http.Response) {
		log.Println("response:", *resp)
	}),
)

Enforcing Read-your-writes Replication Semantics

Detailed background information of the read-after-write consistency problem can be found in the consistency and replication documentation pages.

You can enforce read-your-writes semantics for individual requests through callbacks:

var state string

// write
_, err := client.Secrets.KvV2Write(
	ctx,
	"my-secret",
	schema.KvV2WriteRequest{
		Data: map[string]any{
			"password1": "abc123",
			"password2": "correct horse battery staple",
		},
	}
	vault.WithResponseCallbacks(
		vault.RecordReplicationState(
			&state,
		),
	),
)

// read
secret, err := client.Secrets.KvV2Read(
	ctx,
	"my-secret",
	vault.WithRequestCallbacks(
		vault.RequireReplicationStates(
			&state,
		),
	),
)

Alternatively, enforce read-your-writes semantics for all requests using the following setting:

client, err := vault.New(
	vault.WithAddress("https://localhost:8200"),
	vault.WithEnforceReadYourWritesConsistency(),
)

Note: careful consideration should be made prior to enabling this setting since there will be a performance penalty paid upon each request.

Building the Library

The vast majority of the code (including the client's endpoint-related methods, request structures, and response structures) is generated from the openapi.json using openapi-generator. If you make any changes to the underlying templates (generate/templates/*), please make sure to regenerate the files by running the following:

make regen && go build ./... && go test ./...

Warning: Vault does not yet provide an official OpenAPI specification. The openapi.json file included in this repository may change in non-backwards compatible ways.

Under Development

This library is currently under active development. Below is a list of high-level features that have been implemented:

  • TLS
  • Read/Write/Delete/List base accessors
  • Automatic retries on errors (using go-retryablehttp)
  • Custom redirect logic
  • Client-side rate limiting
  • Vault-specific headers (X-Vault-Token, X-Vault-Namespace, etc.) and custom headers
  • Request/Response callbacks
  • Environment variables for configuration
  • Read-your-writes semantics
  • Thread-safe cloning and client modifications
  • Response wrapping & unwrapping
  • CI/CD pipelines
  • Structured responses for core requests

The following features are coming soon:

  • Testing framework
  • Authentication wrappers
  • Automatic renewal of tokens and leases
  • More structured responses

Documentation for API Endpoints

# Packages

No description provided by the author

# Functions

DefaultConfiguration returns the default configuration for the client.
DefaultRetryPolicy provides a default callback for RetryConfiguration.CheckRetry.
IsErrorStatus returns true if the given error is either a ResponseError or a RedirectError with the given status code.
MergeReplicationStates returns a merged array of replication states by iterating through all states in the `old` slice.
New returns a new client decorated with the given configuration options.
ParseReplicationState will parse the raw base64-encoded replication state into its individual components.
RecordReplicationState returns a response callback that will record the replication state returned by Vault in a response header.
RequireReplicationStates returns a request callback that will add request headers to specify the replication states we require of Vault.
Unwrap sends a request with the given wrapping token and returns the original wrapped response.
WithAddress specifies the Vault server base address in the form of scheme://host:port Default: https://127.0.0.1:8200.
WithConfiguration overwrites the default configuration object with the given one.
WithCustomHeaders sets custom headers for the next request; these headers will be appended to any custom headers set at the client level.
WithDisableRedirects prevents the client from automatically following redirects.
WithEnforceReadYourWritesConsistency ensures isolated read-after-write semantics by providing discovered cluster replication states in each request.
WithEnvironment populates the client's configuration object with values from environment values.
WithHTTPClient sets the HTTP client to use for all API requests.
WithMFACredentials sets the multi-factor authentication credentials for the next request, it takes precedence over the client-level MFA credentials.
WithMountPath overwrites the default mount path in client.Auth and client.Secrets requests with the given mount path string.
WithNamespace sets the namespace for the next request; it takes precedence over the client-level namespace.
WithQueryParameters appends the given list of query parameters to the request.
WithRateLimiter configures how frequently requests are allowed to happen.
WithReplicationForwardingMode sets a replication forwarding header to the given value for the next request; it takes precedence over the client-level replication forwarding header.
WithRequestCallbacks sets callbacks which will be invoked before the next request; these callbacks will be appended to the list of the callbacks set at the client-level.
WithRequestTimeout, given a non-negative value, will apply the timeout to each request function unless an earlier deadline is passed to the request function through context.Context.
WithResponseCallbacks sets callbacks which will be invoked after a successful response within the next request; these callbacks will be appended to the list of the callbacks set at the client-level.
WithResponseWrapping sets the response-wrapping TTL to the given duration for the next request; it takes precedence over the client-level response-wrapping TTL.
WithRetryConfiguration configures the internal go-retryablehttp client.
WithTLS configures the TLS settings in the base http.Client.
WithToken sets the token for the next request; it takes precedence over the client-level token.

# Constants

No description provided by the author
Setting this mode will add 'X-Vault-Forward' header to all subsequent requests, telling any performance standbys handling the requests to forward them to the active node.
Setting this mode will add 'X-Vault-Inconsistent' header to all subsequent requests; any performance standbys handling the requests will conditionally forward them to the active node if the state required isn't present on the node receiving this request.
Setting this mode will clear all forwarding headers.

# Structs

Auth is a simple wrapper around the client for Auth requests.
Client manages communication with Vault, initialize it with vault.New(...).
No description provided by the author
No description provided by the author
ClientConfiguration is used to configure the creation of the client.
Identity is a simple wrapper around the client for Identity requests.
No description provided by the author
No description provided by the author
No description provided by the author
RedirectError is the error returned when the client receives a redirect response and either 1.
ReplicationState is analogous to the WALState in github.com/vault/sdk.
Response is the structure returned by the majority of the requests to Vault.
ResponseAuth contains authentication information if we have it.
ResponseError is the error returned when Vault responds with an HTTP status code outside of the 200 - 399 range.
ResponseWrapInfo contains wrapping information if we have it.
RetryConfiguration is a collection of settings used to configure the internal go-retryablehttp client.
Secrets is a simple wrapper around the client for Secrets requests.
No description provided by the author
System is a simple wrapper around the client for System requests.
TLSConfiguration is a collection of TLS settings used to configure the internal http.Client.

# Type aliases

ClientOption is a configuration option to initialize a client.
No description provided by the author
No description provided by the author
RequestOption is a functional parameter used to modify a request.
No description provided by the author