Define a backend for the secrets engine
In these tutorials, you will write a custom secrets engine against the authentication API of a fictional coffee-shop application using the HashiCorp Vault Plugin SDK.
You will learn how to create a new secrets backend, build a set of Vault roles, and create workflows to renew and revoke an API token using Vault.
You may build your own secrets engine to:
- Manage secrets for an existing internal or proprietary tool
- Extend the capabilities of an existing secrets engine (such as scheduled revocation of tokens) with a custom workflow
In this tutorial, you will set up your Vault secrets engine development environment and create a backend to store data, configuration, and secrets.
To do this, you will:
- Set up your development environment.
You will clone the HashiCups secrets engine repository. This contains many of the interfaces and objects you need to create a secrets engine. - Explore the plugin's entry point.
You will examine a file that allows Vault to start the secrets engine. - Explore the secrets engine's backend.
You will examine a scaffold that defines an empty schema and functions to store the secrets engine's information in a Vault backend. - Explore the client to access the demo application.
You will examine the client that encapsulates an API client for the secrets engine to use. - Explore the configuration for the secrets engine
You will examine the configuration attributes for the secrets engine, defined at theconfig
path. - Update the client to access your target API
You will replace the code in the scaffold with a client to access the target API. - Update the backend to use your target API's client
You will replace the code in the scaffold to create a secrets engine backend that can access the target API.
Prerequisites
- Golang 1.16+ installed and configured.
- Vault 1.8+ CLI installed locally.
Set up your development environment
Clone the learn-vault-plugin-secrets-hashicups repository.
Change into the repository directory.
Note
If you are stuck in this tutorial, refer to the
learn-vault-plugin-secrets-hashicups/solution
directory.
Explore the plugin's entry point
Open cmd/vault-plugin-secrets-hashicups/main.go
. The contents
of the main
function use the Vault SDK's plugin
library
to start the plugin and communicate with the Vault API.
As you examine the file, note the following:
VaultPluginTLSProvider
: runs inside a plugin and gets the SSL certificate for Vault, returning a Vault TLS configuration object.BackendFactoryFunc
: provide your secrets engine's backend factory toBackendFactoryFunc
. In this case, we pass the factory for the HashiCups backend framework.hclog
: use HashiCorp's logging package to surface errors from the plugin.
1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930
Note
Vault handles custom secrets engines through an external process and gRPC. Verified secrets engines are bundled as part of the Vault binary. Vault handles "builtin" secrets engines in memory.
Explore the secrets engine's backend
Open backend.go
. The contents create a backend in Vault for the secrets engine
to store data.
Note
The following details involve code that you must include in your own secrets engine. Edit the names of the structs and libraries so they reference your target API.
The first function, Factory
, creates a new backend in Vault as logical.Backend
.
It should include a call to set up the backend.
The file contains a backend object for your secrets engine.
In this case, you name it hashiCupsBackend
.
It should have three attributes:
framework.Backend
: implementslogical.Backend
lock
: locking mechanisms for writing or changing secrets engine dataclient
: stores the client for the target API, HashiCups
The file creates a method named backend
that returns a hashiCupsBackend
object.
It contains a few important attributes, including:
Help
: outputs help text for the backend.PathsSpecial
: sets any special paths for the secrets engine's API. Secrets engines usually reference two main fields, with optional fields typically used for auth methods.LocalStorage
: For enterprise. These paths store data local to the Vault instance and will not be replicated, more important if you use Vault's Write-Ahead-Log. You would add a path forframework.WALPrefix
. You do not need to set it for this secrets engine.SealWrapStorage
: For enterprise. When using a seal, these paths should be wrapped with extra encryption. These will usually be set to:Root
: Optional. Paths that require a root token for access.Unauthenticated
: Optional. Paths that can be accessed without authentication. Usually used for for auth methods on the/login
endpoint.
Paths
: add the secrets engine's API paths.Secrets
: add any secrets types, such as tokens or passwordsBackendType
: uselogical.TypeLogical
for secrets enginesInvalidate
: call the function to invalidate the secrets engine's configuration.
The file includes two methods extending the backend
type. Implement
a reset
method to lock the backend while you reset the
target API client object.
You also need to implement the invalidate
method referenced in backend
.
It calls reset
to reset the configuration and target API client object.
12345
Examine the getClient
method in backend.go
. The method should take in the
context and Vault storage API interface. You may notice that it does not
return the demo application client, as per the function. You will update
this method when you implement the target API client.
1 2 3 4 5 6 7 8 9 101112131415
At the bottom of backend.go
, define a string with help text for the
backend.
Explore the client to access the demo application
Open client.go
.
Note
The following details involve code that you must include in your own secrets engine. Edit the names of the structs and libraries so they reference your target API.
Import the Go SDK for HashiCups from a library named hashicups-client-go
.
Note
When you create a secrets engine, you need to import the Go SDK for your target API. If your secrets engine does not have a Go SDK, you can write your own and import it for use in the secrets engine.
The file initializes an empty hashiCupsClient
object
that stores a reference to the client to interface with the HashiCups API.
1234567
newClient
takes a Vault configuration object as an input, something you will
define depending on the configuration for your secrets engine.
1234567
Explore the configuration for the secrets engine
Open path_config.go
.
Note
The following details involve code that you must include in your own secrets engine. Edit the names of the structs and libraries so they reference your target API.
Define a constant for configStoragePath
as config
.
You use the config
path for secrets engines to define
attributes like username, password, and API endpoint.
1 2 3 4 5 6 7 8 9 10111213
The file defines an object for hashiCupsConfig
.
You need this object in order to
store configuration attributes related to the secrets
engine.
Note
For your own secrets engine, you can define the configuration with any attributes you need in order for your API's client to authenticate. The attributes can include identifiers for API resources.
Examine the getConfig
method. You need to implement
this method to pass the context
and storage path for Vault to store the hashiCupsConfig
into the config
path for the secrets engine.
1 2 3 4 5 6 7 8 9 1011121314151617
Update the client to access your target API
Return to client.go
. You must configure the client
to access the target API, HashiCups.
Note
Replace the methods and structs in the scaffold with the embedded code examples.
Replace the newClient
method in the scaffold.
The method now checks that hashiCupsConfig
has all of
its attributes and has a configuration defined.
1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930313233
Note
If your secrets engine has many attributes, you may only need to check for the required ones.
Using the hashiCupsConfig
object you defined in path_config.go
,
initialize a new HashiCups client using the URL, username, and password.
Return the HashiCups client in the hashiCupsClient
object.
1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930313233
Note
In your own secrets engine, you need to initialize the client based on your target API's Go SDK.
Update the backend to use your target API's client
Return to backend.go
. Recall that the getClient
method
returned an error by default. You need to add your client to
this method in order for the secrets engine to access your target API!
Note
Replace the methods and structs in the scaffold with the embedded code examples.
Replace the getClient
function in backend.go
. You must retrieve
your secrets engine's configuration using the getConfig
function.
1 2 3 4 5 6 7 8 9 1011121314151617181920212223242526272829
Vault will keep running the secrets engine and checking for a valid client. If you
delete the configuration for the secrets engine or reset it, getClient
returns
a new hashiCupsConfig
.
1 2 3 4 5 6 7 8 9 1011121314151617181920212223242526272829
Otherwise, Vault will create a new client based on the
configuration in the secrets engine. Set the client to
the backend
object and return it.
1 2 3 4 5 6 7 8 9 1011121314151617181920212223242526272829
Next steps
Congratulations! You configured the secrets engine to retrieve attributes
from its config
path and create a new client to access your target API.
If you are stuck in this tutorial, refer to the
plugins/vault-plugin-secrets-hashicups/solution
directory.
- To learn more about Vault plugins, refer to the Vault Plugin System Documentation.
- Define your secrets engine's configuration in the next tutorial.