rebase: bump github.com/hashicorp/vault/api from 1.7.2 to 1.8.1

Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.7.2 to 1.8.1.
- [Release notes](https://github.com/hashicorp/vault/releases)
- [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/vault/compare/v1.7.2...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/vault/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2022-10-18 06:47:04 +00:00 committed by mergify[bot]
parent 3a490a4df0
commit 02ed5ec189
39 changed files with 998 additions and 326 deletions

4
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/vault/api v1.7.2
github.com/hashicorp/vault/api v1.8.1
github.com/kubernetes-csi/csi-lib-utils v0.11.0
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.1
github.com/libopenstorage/secrets v0.0.0-20210908194121-a1d19aa9713a
@ -100,7 +100,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault v1.4.2 // indirect
github.com/hashicorp/vault/sdk v0.5.1 // indirect
github.com/hashicorp/vault/sdk v0.6.0 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect

10
go.sum
View File

@ -607,6 +607,8 @@ github.com/hashicorp/go-kms-wrapping v0.5.1 h1:Ed6Z5gV3LY3J9Ora4cwxVmV8Hyt6CPOTr
github.com/hashicorp/go-kms-wrapping v0.5.1/go.mod h1:cGIibZmMx9qlxS1pZTUrEgGqA+7u3zJyvVYMhjU2bDs=
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0 h1:xuTi5ZwjimfpvpL09jDE71smCBRpnF5xfo871BSX4gs=
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 h1:pSjQfW3vPtrOTcasTUKgCTQT7OGPPTTMVRrOfU6FJD8=
github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk=
github.com/hashicorp/go-memdb v1.0.2 h1:AIjzJlwIxz2inhZqRJZfe6D15lPeF0/cZyS1BVlnlHg=
github.com/hashicorp/go-memdb v1.0.2/go.mod h1:I6dKdmYhZqU0RJSheVEWgTNWdVQH5QvTgIUQ0t/t32M=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@ -708,8 +710,8 @@ github.com/hashicorp/vault/api v1.0.5-0.20191122173911-80fcc7907c78/go.mod h1:Uf
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
github.com/hashicorp/vault/api v1.0.5-0.20200902155336-f9d5ce5a171a/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk=
github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ=
github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M=
github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI=
github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU=
github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU=
@ -719,8 +721,8 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02/go.mod h1:W
github.com/hashicorp/vault/sdk v0.1.14-0.20200427170607-03332aaf8d18/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
github.com/hashicorp/vault/sdk v0.1.14-0.20200429182704-29fce8f27ce4/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
github.com/hashicorp/vault/sdk v0.5.1 h1:zly/TmNgOXCGgWIRA8GojyXzG817POtVh3uzIwzZx+8=
github.com/hashicorp/vault/sdk v0.5.1/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU=
github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs=
github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=

View File

@ -4,5 +4,6 @@ Vault API
This provides the `github.com/hashicorp/vault/api` package which contains code useful for interacting with a Vault server.
For examples of how to use this module, see the [vault-examples](https://github.com/hashicorp/vault-examples) repo.
For a step-by-step walkthrough on using these client libraries, see the [developer quickstart](https://www.vaultproject.io/docs/get-started/developer-qs).
[![GoDoc](https://godoc.org/github.com/hashicorp/vault/api?status.png)](https://godoc.org/github.com/hashicorp/vault/api)

View File

@ -33,29 +33,30 @@ import (
)
const (
EnvVaultAddress = "VAULT_ADDR"
EnvVaultAgentAddr = "VAULT_AGENT_ADDR"
EnvVaultCACert = "VAULT_CACERT"
EnvVaultCACertBytes = "VAULT_CACERT_BYTES"
EnvVaultCAPath = "VAULT_CAPATH"
EnvVaultClientCert = "VAULT_CLIENT_CERT"
EnvVaultClientKey = "VAULT_CLIENT_KEY"
EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
EnvVaultSRVLookup = "VAULT_SRV_LOOKUP"
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
EnvVaultNamespace = "VAULT_NAMESPACE"
EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
EnvVaultWrapTTL = "VAULT_WRAP_TTL"
EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
EnvVaultToken = "VAULT_TOKEN"
EnvVaultMFA = "VAULT_MFA"
EnvRateLimit = "VAULT_RATE_LIMIT"
EnvHTTPProxy = "VAULT_HTTP_PROXY"
EnvVaultProxyAddr = "VAULT_PROXY_ADDR"
HeaderIndex = "X-Vault-Index"
HeaderForward = "X-Vault-Forward"
HeaderInconsistent = "X-Vault-Inconsistent"
TLSErrorString = "This error usually means that the server is running with TLS disabled\n" +
EnvVaultAddress = "VAULT_ADDR"
EnvVaultAgentAddr = "VAULT_AGENT_ADDR"
EnvVaultCACert = "VAULT_CACERT"
EnvVaultCACertBytes = "VAULT_CACERT_BYTES"
EnvVaultCAPath = "VAULT_CAPATH"
EnvVaultClientCert = "VAULT_CLIENT_CERT"
EnvVaultClientKey = "VAULT_CLIENT_KEY"
EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
EnvVaultSRVLookup = "VAULT_SRV_LOOKUP"
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
EnvVaultNamespace = "VAULT_NAMESPACE"
EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
EnvVaultWrapTTL = "VAULT_WRAP_TTL"
EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
EnvVaultToken = "VAULT_TOKEN"
EnvVaultMFA = "VAULT_MFA"
EnvRateLimit = "VAULT_RATE_LIMIT"
EnvHTTPProxy = "VAULT_HTTP_PROXY"
EnvVaultProxyAddr = "VAULT_PROXY_ADDR"
EnvVaultDisableRedirects = "VAULT_DISABLE_REDIRECTS"
HeaderIndex = "X-Vault-Index"
HeaderForward = "X-Vault-Forward"
HeaderInconsistent = "X-Vault-Inconsistent"
TLSErrorString = "This error usually means that the server is running with TLS disabled\n" +
"but the client is configured to use TLS. Please either enable TLS\n" +
"on the server or run the client with -address set to an address\n" +
"that uses the http protocol:\n\n" +
@ -176,6 +177,16 @@ type Config struct {
// since there will be a performance penalty paid upon each request.
// This feature requires Enterprise server-side.
ReadYourWrites bool
// DisableRedirects when set to true, will prevent the client from
// automatically following a (single) redirect response to its initial
// request. This behavior may be desirable if using Vault CLI on the server
// side.
//
// Note: Disabling redirect following behavior could cause issues with
// commands such as 'vault operator raft snapshot' as this redirects to the
// primary node.
DisableRedirects bool
}
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
@ -340,6 +351,7 @@ func (c *Config) ReadEnvironment() error {
var envSRVLookup bool
var limit *rate.Limiter
var envVaultProxy string
var envVaultDisableRedirects bool
// Parse the environment variables
if v := os.Getenv(EnvVaultAddress); v != "" {
@ -347,8 +359,6 @@ func (c *Config) ReadEnvironment() error {
}
if v := os.Getenv(EnvVaultAgentAddr); v != "" {
envAgentAddress = v
} else if v := os.Getenv(EnvVaultAgentAddress); v != "" {
envAgentAddress = v
}
if v := os.Getenv(EnvVaultMaxRetries); v != "" {
maxRetries, err := strconv.ParseUint(v, 10, 32)
@ -390,13 +400,7 @@ func (c *Config) ReadEnvironment() error {
var err error
envInsecure, err = strconv.ParseBool(v)
if err != nil {
return fmt.Errorf("could not parse VAULT_SKIP_VERIFY")
}
} else if v := os.Getenv(EnvVaultInsecure); v != "" {
var err error
envInsecure, err = strconv.ParseBool(v)
if err != nil {
return fmt.Errorf("could not parse VAULT_INSECURE")
return fmt.Errorf("could not parse %s", EnvVaultSkipVerify)
}
}
if v := os.Getenv(EnvVaultSRVLookup); v != "" {
@ -420,6 +424,16 @@ func (c *Config) ReadEnvironment() error {
envVaultProxy = v
}
if v := os.Getenv(EnvVaultDisableRedirects); v != "" {
var err error
envVaultDisableRedirects, err = strconv.ParseBool(v)
if err != nil {
return fmt.Errorf("could not parse %s", EnvVaultDisableRedirects)
}
c.DisableRedirects = envVaultDisableRedirects
}
// Configure the HTTP clients TLS configuration.
t := &TLSConfig{
CACert: envCACert,
@ -470,6 +484,51 @@ func (c *Config) ReadEnvironment() error {
return nil
}
// ParseAddress transforms the provided address into a url.URL and handles
// the case of Unix domain sockets by setting the DialContext in the
// configuration's HttpClient.Transport. This function must be called with
// c.modifyLock held for write access.
func (c *Config) ParseAddress(address string) (*url.URL, error) {
u, err := url.Parse(address)
if err != nil {
return nil, err
}
c.Address = address
if strings.HasPrefix(address, "unix://") {
// When the address begins with unix://, always change the transport's
// DialContext (to match previous behaviour)
socket := strings.TrimPrefix(address, "unix://")
if transport, ok := c.HttpClient.Transport.(*http.Transport); ok {
transport.DialContext = func(context.Context, string, string) (net.Conn, error) {
return net.Dial("unix", socket)
}
// Since the address points to a unix domain socket, the scheme in the
// *URL would be set to `unix`. The *URL in the client is expected to
// be pointing to the protocol used in the application layer and not to
// the transport layer. Hence, setting the fields accordingly.
u.Scheme = "http"
u.Host = socket
u.Path = ""
} else {
return nil, fmt.Errorf("attempting to specify unix:// address with non-transport transport")
}
} else if strings.HasPrefix(c.Address, "unix://") {
// When the address being set does not begin with unix:// but the previous
// address in the Config did, change the transport's DialContext back to
// use the default configuration that cleanhttp uses.
if transport, ok := c.HttpClient.Transport.(*http.Transport); ok {
transport.DialContext = cleanhttp.DefaultPooledTransport().DialContext
}
}
return u, nil
}
func parseRateLimit(val string) (rate float64, burst int, err error) {
_, err = fmt.Sscanf(val, "%f:%d", &rate, &burst)
if err != nil {
@ -542,27 +601,11 @@ func NewClient(c *Config) (*Client, error) {
address = c.AgentAddress
}
u, err := url.Parse(address)
u, err := c.ParseAddress(address)
if err != nil {
return nil, err
}
if strings.HasPrefix(address, "unix://") {
socket := strings.TrimPrefix(address, "unix://")
transport := c.HttpClient.Transport.(*http.Transport)
transport.DialContext = func(context.Context, string, string) (net.Conn, error) {
return net.Dial("unix", socket)
}
// Since the address points to a unix domain socket, the scheme in the
// *URL would be set to `unix`. The *URL in the client is expected to
// be pointing to the protocol used in the application layer and not to
// the transport layer. Hence, setting the fields accordingly.
u.Scheme = "http"
u.Host = socket
u.Path = ""
}
client := &Client{
addr: u,
config: c,
@ -621,14 +664,11 @@ func (c *Client) SetAddress(addr string) error {
c.modifyLock.Lock()
defer c.modifyLock.Unlock()
parsedAddr, err := url.Parse(addr)
parsedAddr, err := c.config.ParseAddress(addr)
if err != nil {
return errwrap.Wrapf("failed to set address: {{err}}", err)
}
c.config.modifyLock.Lock()
c.config.Address = addr
c.config.modifyLock.Unlock()
c.addr = parsedAddr
return nil
}
@ -720,6 +760,42 @@ func (c *Client) SetMaxRetries(retries int) {
c.config.MaxRetries = retries
}
func (c *Client) SetMaxIdleConnections(idle int) {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
c.config.modifyLock.Lock()
defer c.config.modifyLock.Unlock()
c.config.HttpClient.Transport.(*http.Transport).MaxIdleConns = idle
}
func (c *Client) MaxIdleConnections() int {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
c.config.modifyLock.Lock()
defer c.config.modifyLock.Unlock()
return c.config.HttpClient.Transport.(*http.Transport).MaxIdleConns
}
func (c *Client) SetDisableKeepAlives(disable bool) {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
c.config.modifyLock.Lock()
defer c.config.modifyLock.Unlock()
c.config.HttpClient.Transport.(*http.Transport).DisableKeepAlives = disable
}
func (c *Client) DisableKeepAlives() bool {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
c.config.modifyLock.RLock()
defer c.config.modifyLock.RUnlock()
return c.config.HttpClient.Transport.(*http.Transport).DisableKeepAlives
}
func (c *Client) MaxRetries() int {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
@ -1216,6 +1292,7 @@ func (c *Client) rawRequestWithContext(ctx context.Context, r *Request) (*Respon
outputCurlString := c.config.OutputCurlString
outputPolicy := c.config.OutputPolicy
logger := c.config.Logger
disableRedirects := c.config.DisableRedirects
c.config.modifyLock.RUnlock()
c.modifyLock.RUnlock()
@ -1309,8 +1386,8 @@ START:
return result, err
}
// Check for a redirect, only allowing for a single redirect
if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
// Check for a redirect, only allowing for a single redirect (if redirects aren't disabled)
if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 && !disableRedirects {
// Parse the updated location
respLoc, err := resp.Location()
if err != nil {
@ -1369,6 +1446,7 @@ func (c *Client) httpRequestWithContext(ctx context.Context, r *Request) (*Respo
httpClient := c.config.HttpClient
outputCurlString := c.config.OutputCurlString
outputPolicy := c.config.OutputPolicy
disableRedirects := c.config.DisableRedirects
// add headers
if c.headers != nil {
@ -1441,8 +1519,8 @@ func (c *Client) httpRequestWithContext(ctx context.Context, r *Request) (*Respo
return result, err
}
// Check for a redirect, only allowing for a single redirect
if resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307 {
// Check for a redirect, only allowing for a single redirect, if redirects aren't disabled
if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && !disableRedirects {
// Parse the updated location
respLoc, err := resp.Location()
if err != nil {

View File

@ -1,5 +1,11 @@
package api
import "errors"
// ErrSecretNotFound is returned by KVv1 and KVv2 wrappers to indicate that the
// secret is missing at the given location.
var ErrSecretNotFound = errors.New("secret not found")
// A KVSecret is a key-value secret returned by Vault's KV secrets engine,
// and is the most basic type of secret stored in Vault.
//

View File

@ -19,7 +19,7 @@ func (kv *KVv1) Get(ctx context.Context, secretPath string) (*KVSecret, error) {
return nil, fmt.Errorf("error encountered while reading secret at %s: %w", pathToRead, err)
}
if secret == nil {
return nil, fmt.Errorf("no secret found at %s", pathToRead)
return nil, fmt.Errorf("%w: at %s", ErrSecretNotFound, pathToRead)
}
return &KVSecret{

View File

@ -2,7 +2,9 @@ package api
import (
"context"
"errors"
"fmt"
"net/http"
"sort"
"strconv"
"time"
@ -115,7 +117,7 @@ func (kv *KVv2) Get(ctx context.Context, secretPath string) (*KVSecret, error) {
return nil, fmt.Errorf("error encountered while reading secret at %s: %w", pathToRead, err)
}
if secret == nil {
return nil, fmt.Errorf("no secret found at %s", pathToRead)
return nil, fmt.Errorf("%w: at %s", ErrSecretNotFound, pathToRead)
}
kvSecret, err := extractDataAndVersionMetadata(secret)
@ -123,11 +125,7 @@ func (kv *KVv2) Get(ctx context.Context, secretPath string) (*KVSecret, error) {
return nil, fmt.Errorf("error parsing secret at %s: %w", pathToRead, err)
}
cm, err := extractCustomMetadata(secret)
if err != nil {
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToRead, err)
}
kvSecret.CustomMetadata = cm
kvSecret.CustomMetadata = extractCustomMetadata(secret)
return kvSecret, nil
}
@ -149,7 +147,7 @@ func (kv *KVv2) GetVersion(ctx context.Context, secretPath string, version int)
return nil, err
}
if secret == nil {
return nil, fmt.Errorf("no secret with version %d found at %s", version, pathToRead)
return nil, fmt.Errorf("%w: for version %d at %s", ErrSecretNotFound, version, pathToRead)
}
kvSecret, err := extractDataAndVersionMetadata(secret)
@ -157,11 +155,7 @@ func (kv *KVv2) GetVersion(ctx context.Context, secretPath string, version int)
return nil, fmt.Errorf("error parsing secret at %s: %w", pathToRead, err)
}
cm, err := extractCustomMetadata(secret)
if err != nil {
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToRead, err)
}
kvSecret.CustomMetadata = cm
kvSecret.CustomMetadata = extractCustomMetadata(secret)
return kvSecret, nil
}
@ -175,7 +169,7 @@ func (kv *KVv2) GetVersionsAsList(ctx context.Context, secretPath string) ([]KVV
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, fmt.Errorf("no secret metadata found at %s", pathToRead)
return nil, fmt.Errorf("%w: no metadata at %s", ErrSecretNotFound, pathToRead)
}
md, err := extractFullMetadata(secret)
@ -202,7 +196,7 @@ func (kv *KVv2) GetMetadata(ctx context.Context, secretPath string) (*KVMetadata
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, fmt.Errorf("no secret metadata found at %s", pathToRead)
return nil, fmt.Errorf("%w: no metadata at %s", ErrSecretNotFound, pathToRead)
}
md, err := extractFullMetadata(secret)
@ -244,7 +238,7 @@ func (kv *KVv2) Put(ctx context.Context, secretPath string, data map[string]inte
return nil, fmt.Errorf("error writing secret to %s: %w", pathToWriteTo, err)
}
if secret == nil {
return nil, fmt.Errorf("no secret was written to %s", pathToWriteTo)
return nil, fmt.Errorf("%w: after writing to %s", ErrSecretNotFound, pathToWriteTo)
}
metadata, err := extractVersionMetadata(secret)
@ -258,11 +252,7 @@ func (kv *KVv2) Put(ctx context.Context, secretPath string, data map[string]inte
Raw: secret,
}
cm, err := extractCustomMetadata(secret)
if err != nil {
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToWriteTo, err)
}
kvSecret.CustomMetadata = cm
kvSecret.CustomMetadata = extractCustomMetadata(secret)
return kvSecret, nil
}
@ -325,19 +315,19 @@ func (kv *KVv2) Patch(ctx context.Context, secretPath string, newData map[string
// Determine which kind of patch to use,
// the newer HTTP Patch style or the older read-then-write style
var kvs *KVSecret
var perr error
var err error
switch patchMethod {
case "rw":
kvs, perr = readThenWrite(ctx, kv.c, kv.mountPath, secretPath, newData)
kvs, err = readThenWrite(ctx, kv.c, kv.mountPath, secretPath, newData)
case "patch":
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
kvs, err = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
case "":
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
kvs, err = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
default:
return nil, fmt.Errorf("unsupported patch method provided; value for patch method should be string \"rw\" or \"patch\"")
}
if perr != nil {
return nil, fmt.Errorf("unable to perform patch: %w", perr)
if err != nil {
return nil, fmt.Errorf("unable to perform patch: %w", err)
}
if kvs == nil {
return nil, fmt.Errorf("no secret was written to %s", secretPath)
@ -478,7 +468,7 @@ func (kv *KVv2) Rollback(ctx context.Context, secretPath string, toVersion int)
// Now run it again and read the version we want to roll back to
rollbackVersion, err := kv.GetVersion(ctx, secretPath, toVersion)
if err != nil {
return nil, fmt.Errorf("unable to get previous version %d of secret: %s", toVersion, err)
return nil, fmt.Errorf("unable to get previous version %d of secret: %w", toVersion, err)
}
err = validateRollbackVersion(rollbackVersion)
@ -495,30 +485,24 @@ func (kv *KVv2) Rollback(ctx context.Context, secretPath string, toVersion int)
return kvs, nil
}
func extractCustomMetadata(secret *Secret) (map[string]interface{}, error) {
func extractCustomMetadata(secret *Secret) map[string]interface{} {
// Logical Writes return the metadata directly, Reads return it nested inside the "metadata" key
customMetadataInterface, ok := secret.Data["custom_metadata"]
if !ok {
metadataInterface, ok := secret.Data["metadata"]
if !ok { // if that's not found, bail since it should have had one or the other
return nil, fmt.Errorf("secret is missing expected fields")
}
metadataInterface := secret.Data["metadata"]
metadataMap, ok := metadataInterface.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected type for 'metadata' element: %T (%#v)", metadataInterface, metadataInterface)
}
customMetadataInterface, ok = metadataMap["custom_metadata"]
if !ok {
return nil, fmt.Errorf("metadata missing expected field \"custom_metadata\": %v", metadataMap)
return nil
}
customMetadataInterface = metadataMap["custom_metadata"]
}
cm, ok := customMetadataInterface.(map[string]interface{})
if !ok && customMetadataInterface != nil {
return nil, fmt.Errorf("unexpected type for 'metadata' element: %T (%#v)", customMetadataInterface, customMetadataInterface)
if !ok {
return nil
}
return cm, nil
return cm
}
func extractDataAndVersionMetadata(secret *Secret) (*KVSecret, error) {
@ -687,18 +671,28 @@ func mergePatch(ctx context.Context, client *Client, mountPath string, secretPat
secret, err := client.Logical().JSONMergePatch(ctx, pathToMergePatch, wrappedData)
if err != nil {
// If it's a 405, that probably means the server is running a pre-1.9
// Vault version that doesn't support the HTTP PATCH method.
// Fall back to the old way of doing it.
if re, ok := err.(*ResponseError); ok && re.StatusCode == 405 {
return readThenWrite(ctx, client, mountPath, secretPath, newData)
var re *ResponseError
if errors.As(err, &re) {
switch re.StatusCode {
// 403
case http.StatusForbidden:
return nil, fmt.Errorf("received 403 from Vault server; please ensure that token's policy has \"patch\" capability: %w", err)
// 404
case http.StatusNotFound:
return nil, fmt.Errorf("%w: performing merge patch to %s", ErrSecretNotFound, pathToMergePatch)
// 405
case http.StatusMethodNotAllowed:
// If it's a 405, that probably means the server is running a pre-1.9
// Vault version that doesn't support the HTTP PATCH method.
// Fall back to the old way of doing it.
return readThenWrite(ctx, client, mountPath, secretPath, newData)
}
}
if re, ok := err.(*ResponseError); ok && re.StatusCode == 403 {
return nil, fmt.Errorf("received 403 from Vault server; please ensure that token's policy has \"patch\" capability: %w", err)
}
return nil, fmt.Errorf("error performing merge patch to %s: %s", pathToMergePatch, err)
return nil, fmt.Errorf("error performing merge patch to %s: %w", pathToMergePatch, err)
}
metadata, err := extractVersionMetadata(secret)
@ -712,11 +706,7 @@ func mergePatch(ctx context.Context, client *Client, mountPath string, secretPat
Raw: secret,
}
cm, err := extractCustomMetadata(secret)
if err != nil {
return nil, fmt.Errorf("error reading custom metadata for secret %s: %w", secretPath, err)
}
kvSecret.CustomMetadata = cm
kvSecret.CustomMetadata = extractCustomMetadata(secret)
return kvSecret, nil
}
@ -730,7 +720,7 @@ func readThenWrite(ctx context.Context, client *Client, mountPath string, secret
// Make sure the secret already exists
if existingVersion == nil || existingVersion.Data == nil {
return nil, fmt.Errorf("no existing secret was found at %s when doing read-then-write patch operation: %w", secretPath, err)
return nil, fmt.Errorf("%w: at %s as part of read-then-write patch operation", ErrSecretNotFound, secretPath)
}
// Verify existing secret has metadata

View File

@ -50,25 +50,24 @@ const (
// LifetimeWatcher is a process for watching lifetime of a secret.
//
// watcher, err := client.NewLifetimeWatcher(&LifetimeWatcherInput{
// Secret: mySecret,
// })
// go watcher.Start()
// defer watcher.Stop()
// watcher, err := client.NewLifetimeWatcher(&LifetimeWatcherInput{
// Secret: mySecret,
// })
// go watcher.Start()
// defer watcher.Stop()
//
// for {
// select {
// case err := <-watcher.DoneCh():
// if err != nil {
// log.Fatal(err)
// }
//
// // Renewal is now over
// case renewal := <-watcher.RenewCh():
// log.Printf("Successfully renewed: %#v", renewal)
// }
// }
// for {
// select {
// case err := <-watcher.DoneCh():
// if err != nil {
// log.Fatal(err)
// }
//
// // Renewal is now over
// case renewal := <-watcher.RenewCh():
// log.Printf("Successfully renewed: %#v", renewal)
// }
// }
//
// `DoneCh` will return if renewal fails, or if the remaining lease duration is
// under a built-in threshold and either renewing is not extending it or
@ -251,7 +250,8 @@ func (r *LifetimeWatcher) doRenew() error {
}
func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, initLeaseDuration int, credString string,
renew renewFunc, initialRetryInterval time.Duration) error {
renew renewFunc, initialRetryInterval time.Duration,
) error {
if credString == "" ||
(nonRenewable && r.renewBehavior == RenewBehaviorErrorOnErrors) {
return r.errLifetimeWatcherNotRenewable

View File

@ -60,19 +60,19 @@ func (d *OutputStringError) buildCurlString() (string, error) {
finalCurlString = fmt.Sprintf("%s-X %s ", finalCurlString, d.Request.Method)
}
if d.ClientCACert != "" {
clientCACert := strings.Replace(d.ClientCACert, "'", "'\"'\"'", -1)
clientCACert := strings.ReplaceAll(d.ClientCACert, "'", "'\"'\"'")
finalCurlString = fmt.Sprintf("%s--cacert '%s' ", finalCurlString, clientCACert)
}
if d.ClientCAPath != "" {
clientCAPath := strings.Replace(d.ClientCAPath, "'", "'\"'\"'", -1)
clientCAPath := strings.ReplaceAll(d.ClientCAPath, "'", "'\"'\"'")
finalCurlString = fmt.Sprintf("%s--capath '%s' ", finalCurlString, clientCAPath)
}
if d.ClientCert != "" {
clientCert := strings.Replace(d.ClientCert, "'", "'\"'\"'", -1)
clientCert := strings.ReplaceAll(d.ClientCert, "'", "'\"'\"'")
finalCurlString = fmt.Sprintf("%s--cert '%s' ", finalCurlString, clientCert)
}
if d.ClientKey != "" {
clientKey := strings.Replace(d.ClientKey, "'", "'\"'\"'", -1)
clientKey := strings.ReplaceAll(d.ClientKey, "'", "'\"'\"'")
finalCurlString = fmt.Sprintf("%s--key '%s' ", finalCurlString, clientKey)
}
for k, v := range d.Request.Header {
@ -87,7 +87,7 @@ func (d *OutputStringError) buildCurlString() (string, error) {
if len(body) > 0 {
// We need to escape single quotes since that's what we're using to
// quote the body
escapedBody := strings.Replace(string(body), "'", "'\"'\"'", -1)
escapedBody := strings.ReplaceAll(string(body), "'", "'\"'\"'")
finalCurlString = fmt.Sprintf("%s-d '%s' ", finalCurlString, escapedBody)
}

View File

@ -16,7 +16,11 @@ import (
"github.com/hashicorp/errwrap"
)
var (
const (
// PluginAutoMTLSEnv is used to ensure AutoMTLS is used. This will override
// setting a TLSProviderFunc for a plugin.
PluginAutoMTLSEnv = "VAULT_PLUGIN_AUTOMTLS_ENABLED"
// PluginMetadataModeEnv is an ENV name used to disable TLS communication
// to bootstrap mounting plugins.
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
@ -24,51 +28,51 @@ var (
// PluginUnwrapTokenEnv is the ENV name used to pass unwrap tokens to the
// plugin.
PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN"
// sudoPaths is a map containing the paths that require a token's policy
// to have the "sudo" capability. The keys are the paths as strings, in
// the same format as they are returned by the OpenAPI spec. The values
// are the regular expressions that can be used to test whether a given
// path matches that path or not (useful specifically for the paths that
// contain templated fields.)
sudoPaths = map[string]*regexp.Regexp{
"/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`),
"/pki/root": regexp.MustCompile(`^/pki/root$`),
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
"/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`),
"/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`),
"/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`),
"/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`),
"/sys/config/ui/headers/": regexp.MustCompile(`^/sys/config/ui/headers/$`),
"/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`),
"/sys/leases": regexp.MustCompile(`^/sys/leases$`),
"/sys/leases/lookup/": regexp.MustCompile(`^/sys/leases/lookup/$`),
"/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup/.+$`),
"/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`),
"/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`),
"/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`),
"/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`),
"/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`),
"/sys/raw": regexp.MustCompile(`^/sys/raw$`),
"/sys/raw/{path}": regexp.MustCompile(`^/sys/raw/.+$`),
"/sys/remount": regexp.MustCompile(`^/sys/remount$`),
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),
// enterprise-only paths
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),
"/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`),
"/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`),
"/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`),
"/sys/storage/raft/snapshot-auto/config/": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/$`),
"/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`),
}
)
// sudoPaths is a map containing the paths that require a token's policy
// to have the "sudo" capability. The keys are the paths as strings, in
// the same format as they are returned by the OpenAPI spec. The values
// are the regular expressions that can be used to test whether a given
// path matches that path or not (useful specifically for the paths that
// contain templated fields.)
var sudoPaths = map[string]*regexp.Regexp{
"/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`),
"/pki/root": regexp.MustCompile(`^/pki/root$`),
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
"/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`),
"/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`),
"/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`),
"/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`),
"/sys/config/ui/headers/": regexp.MustCompile(`^/sys/config/ui/headers/$`),
"/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`),
"/sys/leases": regexp.MustCompile(`^/sys/leases$`),
"/sys/leases/lookup/": regexp.MustCompile(`^/sys/leases/lookup/$`),
"/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup/.+$`),
"/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`),
"/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`),
"/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`),
"/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`),
"/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`),
"/sys/raw": regexp.MustCompile(`^/sys/raw$`),
"/sys/raw/{path}": regexp.MustCompile(`^/sys/raw/.+$`),
"/sys/remount": regexp.MustCompile(`^/sys/remount$`),
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),
// enterprise-only paths
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),
"/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`),
"/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`),
"/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`),
"/sys/storage/raft/snapshot-auto/config/": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/$`),
"/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`),
}
// PluginAPIClientMeta is a helper that plugins can use to configure TLS connections
// back to Vault.
type PluginAPIClientMeta struct {
@ -120,7 +124,7 @@ func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error)
// VaultPluginTLSProviderContext is run inside a plugin and retrieves the response
// wrapped TLS certificate from vault. It returns a configured TLS Config.
func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
if os.Getenv(PluginMetadataModeEnv) == "true" {
if os.Getenv(PluginAutoMTLSEnv) == "true" || os.Getenv(PluginMetadataModeEnv) == "true" {
return nil
}

View File

@ -85,11 +85,10 @@ func (c *SSHHelperConfig) SetTLSParameters(clientConfig *Config, certPool *x509.
}
// Returns true if any of the following conditions are true:
// * CA cert is configured
// * CA path is configured
// * configured to skip certificate verification
// * TLS server name is configured
//
// - CA cert is configured
// - CA path is configured
// - configured to skip certificate verification
// - TLS server name is configured
func (c *SSHHelperConfig) shouldSetTLSParameters() bool {
return c.CACert != "" || c.CAPath != "" || c.TLSServerName != "" || c.TLSSkipVerify
}

View File

@ -87,7 +87,8 @@ func (c *Sys) ListAuditWithContext(ctx context.Context) (map[string]*Audit, erro
// DEPRECATED: Use EnableAuditWithOptions instead
func (c *Sys) EnableAudit(
path string, auditType string, desc string, opts map[string]string) error {
path string, auditType string, desc string, opts map[string]string,
) error {
return c.EnableAuditWithOptions(path, &EnableAuditOptions{
Type: auditType,
Description: desc,

View File

@ -266,6 +266,7 @@ type MountConfigInput struct {
AllowedResponseHeaders []string `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"`
PluginVersion string `json:"plugin_version,omitempty"`
// Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
@ -281,6 +282,10 @@ type MountOutput struct {
Local bool `json:"local"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
ExternalEntropyAccess bool `json:"external_entropy_access" mapstructure:"external_entropy_access"`
PluginVersion string `json:"plugin_version" mapstructure:"plugin_version"`
RunningVersion string `json:"running_plugin_version" mapstructure:"running_plugin_version"`
RunningSha256 string `json:"running_sha256" mapstructure:"running_sha256"`
DeprecationStatus string `json:"deprecation_status" mapstructure:"deprecation_status"`
}
type MountConfigOutput struct {

View File

@ -22,6 +22,8 @@ type ListPluginsResponse struct {
// PluginsByType is the list of plugins by type.
PluginsByType map[consts.PluginType][]string `json:"types"`
Details []PluginDetails `json:"details,omitempty"`
// Names is the list of names of the plugins.
//
// Deprecated: Newer server responses should be returning PluginsByType (json:
@ -29,6 +31,14 @@ type ListPluginsResponse struct {
Names []string `json:"names"`
}
type PluginDetails struct {
Type string `json:"type"`
Name string `json:"name"`
Version string `json:"version,omitempty"`
Builtin bool `json:"builtin"`
DeprecationStatus string `json:"deprecation_status,omitempty" mapstructure:"deprecation_status"`
}
// ListPlugins wraps ListPluginsWithContext using context.Background.
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
return c.ListPluginsWithContext(context.Background(), i)
@ -40,25 +50,7 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
defer cancelFunc()
path := ""
method := ""
if i.Type == consts.PluginTypeUnknown {
path = "/v1/sys/plugins/catalog"
method = http.MethodGet
} else {
path = fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Type)
method = "LIST"
}
req := c.c.NewRequest(method, path)
if method == "LIST" {
// Set this for broader compatibility, but we use LIST above to be able
// to handle the wrapping lookup function
req.Method = http.MethodGet
req.Params.Set("list", "true")
}
resp, err := c.c.rawRequestWithContext(ctx, req)
resp, err := c.c.rawRequestWithContext(ctx, c.c.NewRequest(http.MethodGet, "/v1/sys/plugins/catalog"))
if err != nil && resp == nil {
return nil, err
}
@ -67,27 +59,6 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
}
defer resp.Body.Close()
// We received an Unsupported Operation response from Vault, indicating
// Vault of an older version that doesn't support the GET method yet;
// switch it to a LIST.
if resp.StatusCode == 405 {
req.Params.Set("list", "true")
resp, err := c.c.rawRequestWithContext(ctx, req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct {
Data struct {
Keys []string `json:"keys"`
} `json:"data"`
}
if err := resp.DecodeJSON(&result); err != nil {
return nil, err
}
return &ListPluginsResponse{Names: result.Data.Keys}, nil
}
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
@ -99,7 +70,8 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
result := &ListPluginsResponse{
PluginsByType: make(map[consts.PluginType][]string),
}
if i.Type == consts.PluginTypeUnknown {
switch i.Type {
case consts.PluginTypeUnknown:
for _, pluginType := range consts.PluginTypes {
pluginsRaw, ok := secret.Data[pluginType.String()]
if !ok {
@ -121,14 +93,38 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
}
result.PluginsByType[pluginType] = plugins
}
} else {
default:
pluginsRaw, ok := secret.Data[i.Type.String()]
if !ok {
return nil, fmt.Errorf("no %s entry in returned data", i.Type.String())
}
var respKeys []string
if err := mapstructure.Decode(secret.Data["keys"], &respKeys); err != nil {
if err := mapstructure.Decode(pluginsRaw, &respKeys); err != nil {
return nil, err
}
result.PluginsByType[i.Type] = respKeys
}
if detailed, ok := secret.Data["detailed"]; ok {
var details []PluginDetails
if err := mapstructure.Decode(detailed, &details); err != nil {
return nil, err
}
switch i.Type {
case consts.PluginTypeUnknown:
result.Details = details
default:
// Filter for just the queried type.
for _, entry := range details {
if entry.Type == i.Type.String() {
result.Details = append(result.Details, entry)
}
}
}
}
return result, nil
}
@ -137,16 +133,19 @@ type GetPluginInput struct {
Name string `json:"-"`
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
Type consts.PluginType `json:"type"`
Version string `json:"version"`
}
// GetPluginResponse is the response from the GetPlugin call.
type GetPluginResponse struct {
Args []string `json:"args"`
Builtin bool `json:"builtin"`
Command string `json:"command"`
Name string `json:"name"`
SHA256 string `json:"sha256"`
Args []string `json:"args"`
Builtin bool `json:"builtin"`
Command string `json:"command"`
Name string `json:"name"`
SHA256 string `json:"sha256"`
DeprecationStatus string `json:"deprecation_status,omitempty"`
Version string `json:"version,omitempty"`
}
// GetPlugin wraps GetPluginWithContext using context.Background.
@ -161,6 +160,9 @@ func (c *Sys) GetPluginWithContext(ctx context.Context, i *GetPluginInput) (*Get
path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodGet, path)
if i.Version != "" {
req.Params.Set("version", i.Version)
}
resp, err := c.c.rawRequestWithContext(ctx, req)
if err != nil {
@ -194,6 +196,9 @@ type RegisterPluginInput struct {
// SHA256 is the shasum of the plugin.
SHA256 string `json:"sha256,omitempty"`
// Version is the optional version of the plugin being registered
Version string `json:"version,omitempty"`
}
// RegisterPlugin wraps RegisterPluginWithContext using context.Background.
@ -227,6 +232,9 @@ type DeregisterPluginInput struct {
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
// Version of the plugin. Optional.
Version string `json:"version,omitempty"`
}
// DeregisterPlugin wraps DeregisterPluginWithContext using context.Background.
@ -242,7 +250,7 @@ func (c *Sys) DeregisterPluginWithContext(ctx context.Context, i *DeregisterPlug
path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodDelete, path)
req.Params.Set("version", i.Version)
resp, err := c.c.rawRequestWithContext(ctx, req)
if err == nil {
defer resp.Body.Close()

View File

@ -93,20 +93,22 @@ func sealStatusRequestWithContext(ctx context.Context, c *Sys, r *Request) (*Sea
}
type SealStatusResponse struct {
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
StorageType string `json:"storage_type,omitempty"`
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
StorageType string `json:"storage_type,omitempty"`
HCPLinkStatus string `json:"hcp_link_status,omitempty"`
HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"`
}
type UnsealOpts struct {

View File

@ -49,6 +49,26 @@ var expectedNISTPCurveHashBits = map[int]int{
521: 512,
}
// Mapping of constant names<->constant values for SignatureAlgorithm
var SignatureAlgorithmNames = map[string]x509.SignatureAlgorithm{
"sha256withrsa": x509.SHA256WithRSA,
"sha384withrsa": x509.SHA384WithRSA,
"sha512withrsa": x509.SHA512WithRSA,
"ecdsawithsha256": x509.ECDSAWithSHA256,
"ecdsawithsha384": x509.ECDSAWithSHA384,
"ecdsawithsha512": x509.ECDSAWithSHA512,
"sha256withrsapss": x509.SHA256WithRSAPSS,
"sha384withrsapss": x509.SHA384WithRSAPSS,
"sha512withrsapss": x509.SHA512WithRSAPSS,
"pureed25519": x509.PureEd25519,
"ed25519": x509.PureEd25519, // Duplicated for clarity; most won't expect the "Pure" prefix.
}
// OID for RFC 5280 Delta CRL Indicator CRL extension.
//
// > id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
var DeltaCRLIndicatorOID = asn1.ObjectIdentifier([]int{2, 5, 29, 27})
// GetHexFormatted returns the byte buffer formatted in hex with
// the specified separator between bytes.
func GetHexFormatted(buf []byte, sep string) string {
@ -87,6 +107,16 @@ func GetSubjKeyID(privateKey crypto.Signer) ([]byte, error) {
return getSubjectKeyID(privateKey.Public())
}
// Returns the explicit SKID when used for cross-signing, else computes a new
// SKID from the key itself.
func getSubjectKeyIDFromBundle(data *CreationBundle) ([]byte, error) {
if len(data.Params.SKID) > 0 {
return data.Params.SKID, nil
}
return getSubjectKeyID(data.CSR.PublicKey)
}
func getSubjectKeyID(pub interface{}) ([]byte, error) {
var publicKeyBytes []byte
switch pub := pub.(type) {
@ -151,18 +181,21 @@ func ParsePKIJSON(input []byte) (*ParsedCertBundle, error) {
}
func ParseDERKey(privateKeyBytes []byte) (signer crypto.Signer, format BlockType, err error) {
if signer, err = x509.ParseECPrivateKey(privateKeyBytes); err == nil {
var firstError error
if signer, firstError = x509.ParseECPrivateKey(privateKeyBytes); firstError == nil {
format = ECBlock
return
}
if signer, err = x509.ParsePKCS1PrivateKey(privateKeyBytes); err == nil {
var secondError error
if signer, secondError = x509.ParsePKCS1PrivateKey(privateKeyBytes); secondError == nil {
format = PKCS1Block
return
}
var thirdError error
var rawKey interface{}
if rawKey, err = x509.ParsePKCS8PrivateKey(privateKeyBytes); err == nil {
if rawKey, thirdError = x509.ParsePKCS8PrivateKey(privateKeyBytes); thirdError == nil {
switch rawSigner := rawKey.(type) {
case *rsa.PrivateKey:
signer = rawSigner
@ -178,7 +211,7 @@ func ParseDERKey(privateKeyBytes []byte) (signer crypto.Signer, format BlockType
return
}
return nil, UnknownBlock, err
return nil, UnknownBlock, fmt.Errorf("got errors attempting to parse DER private key:\n1. %v\n2. %v\n3. %v", firstError, secondError, thirdError)
}
func ParsePEMKey(keyPem string) (crypto.Signer, BlockType, error) {
@ -756,6 +789,29 @@ func CreateCertificateWithKeyGenerator(data *CreationBundle, randReader io.Reade
return createCertificate(data, randReader, keyGenerator)
}
// Set correct correct RSA sig algo
func certTemplateSetSigAlgo(certTemplate *x509.Certificate, data *CreationBundle) {
if data.Params.UsePSS {
switch data.Params.SignatureBits {
case 256:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSAPSS
case 384:
certTemplate.SignatureAlgorithm = x509.SHA384WithRSAPSS
case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSAPSS
}
} else {
switch data.Params.SignatureBits {
case 256:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case 384:
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
}
}
}
func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGenerator KeyGenerator) (*ParsedCertBundle, error) {
var err error
result := &ParsedCertBundle{}
@ -824,14 +880,7 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen
if data.SigningBundle != nil {
switch data.SigningBundle.PrivateKeyType {
case RSAPrivateKey:
switch data.Params.SignatureBits {
case 256:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case 384:
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
}
certTemplateSetSigAlgo(certTemplate, data)
case Ed25519PrivateKey:
certTemplate.SignatureAlgorithm = x509.PureEd25519
case ECPrivateKey:
@ -853,14 +902,7 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen
switch data.Params.KeyType {
case "rsa":
switch data.Params.SignatureBits {
case 256:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case 384:
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
}
certTemplateSetSigAlgo(certTemplate, data)
case "ed25519":
certTemplate.SignatureAlgorithm = x509.PureEd25519
case "ec":
@ -1066,7 +1108,7 @@ func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBun
return nil, err
}
subjKeyID, err := getSubjectKeyID(data.CSR.PublicKey)
subjKeyID, err := getSubjectKeyIDFromBundle(data)
if err != nil {
return nil, err
}
@ -1087,14 +1129,7 @@ func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBun
switch data.SigningBundle.PrivateKeyType {
case RSAPrivateKey:
switch data.Params.SignatureBits {
case 256:
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
case 384:
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
case 512:
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
}
certTemplateSetSigAlgo(certTemplate, data)
case ECPrivateKey:
switch data.Params.SignatureBits {
case 256:
@ -1266,3 +1301,26 @@ func CreateKeyBundleWithKeyGenerator(keyType string, keyBits int, randReader io.
}
return result, nil
}
// CreateDeltaCRLIndicatorExt allows creating correctly formed delta CRLs
// that point back to the last complete CRL that they're based on.
func CreateDeltaCRLIndicatorExt(completeCRLNumber int64) (pkix.Extension, error) {
bigNum := big.NewInt(completeCRLNumber)
bigNumValue, err := asn1.Marshal(bigNum)
if err != nil {
return pkix.Extension{}, fmt.Errorf("unable to marshal complete CRL number (%v): %v", completeCRLNumber, err)
}
return pkix.Extension{
Id: DeltaCRLIndicatorOID,
// > When a conforming CRL issuer generates a delta CRL, the delta
// > CRL MUST include a critical delta CRL indicator extension.
Critical: true,
// This extension only includes the complete CRL number:
//
// > BaseCRLNumber ::= CRLNumber
//
// But, this needs to be encoded as a big number for encoding/asn1
// to work properly.
Value: bigNumValue,
}, nil
}

View File

@ -710,6 +710,7 @@ type CAInfoBundle struct {
ParsedCertBundle
URLs *URLEntries
LeafNotAfterBehavior NotAfterBehavior
RevocationSigAlg x509.SignatureAlgorithm
}
func (b *CAInfoBundle) GetCAChain() []*CertBlock {
@ -782,6 +783,7 @@ type CreationParameters struct {
PolicyIdentifiers []string
BasicConstraintsValidForNonCA bool
SignatureBits int
UsePSS bool
ForceAppendCaChain bool
// Only used when signing a CA cert
@ -796,6 +798,9 @@ type CreationParameters struct {
// The duration the certificate will use NotBefore
NotBeforeDuration time.Duration
// The explicit SKID to use; especially useful for cross-signing.
SKID []byte
}
type CreationBundle struct {

View File

@ -0,0 +1,31 @@
package consts
const VaultAllowPendingRemovalMountsEnv = "VAULT_ALLOW_PENDING_REMOVAL_MOUNTS"
// DeprecationStatus represents the current deprecation state for builtins
type DeprecationStatus uint32
// These are the states of deprecation for builtin plugins
const (
Supported = iota
Deprecated
PendingRemoval
Removed
Unknown
)
// String returns the string representation of a builtin deprecation status
func (s DeprecationStatus) String() string {
switch s {
case Supported:
return "supported"
case Deprecated:
return "deprecated"
case PendingRemoval:
return "pending removal"
case Removed:
return "removed"
default:
return ""
}
}

View File

@ -25,7 +25,6 @@ type LockEntry struct {
// Lock B, Lock A
//
// Where process 1 is now deadlocked trying to lock B, and process 2 deadlocked trying to lock A
//
func CreateLocks() []*LockEntry {
ret := make([]*LockEntry, LockCount)
for i := range ret {

View File

@ -42,9 +42,10 @@ func NewVaultLogger(level log.Level) log.Logger {
// writer and a Vault formatter
func NewVaultLoggerWithWriter(w io.Writer, level log.Level) log.Logger {
opts := &log.LoggerOptions{
Level: level,
Output: w,
JSONFormat: ParseEnvLogFormat() == JSONFormat,
Level: level,
IndependentLevels: true,
Output: w,
JSONFormat: ParseEnvLogFormat() == JSONFormat,
}
return log.New(opts)
}

View File

@ -7,7 +7,11 @@ import (
version "github.com/hashicorp/go-version"
)
var (
const (
// PluginAutoMTLSEnv is used to ensure AutoMTLS is used. This will override
// setting a TLSProviderFunc for a plugin.
PluginAutoMTLSEnv = "VAULT_PLUGIN_AUTOMTLS_ENABLED"
// PluginMlockEnabled is the ENV name used to pass the configuration for
// enabling mlock
PluginMlockEnabled = "VAULT_PLUGIN_MLOCK_ENABLED"
@ -27,6 +31,10 @@ var (
// PluginCACertPEMEnv is an ENV name used for holding a CA PEM-encoded
// string. Used for testing.
PluginCACertPEMEnv = "VAULT_TESTING_PLUGIN_CA_PEM"
// PluginMultiplexingOptOut is an ENV name used to define a comma separated list of plugin names
// opted-out of the multiplexing feature; for emergencies if multiplexing ever causes issues
PluginMultiplexingOptOut = "VAULT_PLUGIN_MULTIPLEXING_OPT_OUT"
)
// OptionallyEnableMlock determines if mlock should be called, and if so enables

View File

@ -1,12 +1,16 @@
package pluginutil
import (
context "context"
"context"
"fmt"
"os"
"strings"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
"github.com/hashicorp/go-secure-stdlib/strutil"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
type PluginMultiplexingServerImpl struct {
@ -15,17 +19,22 @@ type PluginMultiplexingServerImpl struct {
Supported bool
}
func (pm PluginMultiplexingServerImpl) MultiplexingSupport(ctx context.Context, req *MultiplexingSupportRequest) (*MultiplexingSupportResponse, error) {
func (pm PluginMultiplexingServerImpl) MultiplexingSupport(_ context.Context, _ *MultiplexingSupportRequest) (*MultiplexingSupportResponse, error) {
return &MultiplexingSupportResponse{
Supported: pm.Supported,
}, nil
}
func MultiplexingSupported(ctx context.Context, cc grpc.ClientConnInterface) (bool, error) {
func MultiplexingSupported(ctx context.Context, cc grpc.ClientConnInterface, name string) (bool, error) {
if cc == nil {
return false, fmt.Errorf("client connection is nil")
}
out := strings.Split(os.Getenv(PluginMultiplexingOptOut), ",")
if strutil.StrListContains(out, name) {
return false, nil
}
req := new(MultiplexingSupportRequest)
resp, err := NewPluginMultiplexingClient(cc).MultiplexingSupport(ctx, req)
if err != nil {
@ -45,3 +54,22 @@ func MultiplexingSupported(ctx context.Context, cc grpc.ClientConnInterface) (bo
return resp.Supported, nil
}
func GetMultiplexIDFromContext(ctx context.Context) (string, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return "", fmt.Errorf("missing plugin multiplexing metadata")
}
multiplexIDs := md[MultiplexingCtxKey]
if len(multiplexIDs) != 1 {
return "", fmt.Errorf("unexpected number of IDs in metadata: (%d)", len(multiplexIDs))
}
multiplexID := multiplexIDs[0]
if multiplexID == "" {
return "", fmt.Errorf("empty multiplex ID in metadata")
}
return multiplexID, nil
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.19.4
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: sdk/helper/pluginutil/multiplexing.proto
package pluginutil

View File

@ -16,12 +16,14 @@ import (
type PluginClientConfig struct {
Name string
PluginType consts.PluginType
Version string
PluginSets map[int]plugin.PluginSet
HandshakeConfig plugin.HandshakeConfig
Logger log.Logger
IsMetadataMode bool
AutoMTLS bool
MLock bool
Wrapper RunnerUtil
}
type runConfig struct {
@ -33,8 +35,6 @@ type runConfig struct {
// Initialized with what's in PluginRunner.Env, but can be added to
env []string
wrapper RunnerUtil
PluginClientConfig
}
@ -43,7 +43,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
cmd.Env = append(cmd.Env, rc.env...)
// Add the mlock setting to the ENV of the plugin
if rc.MLock || (rc.wrapper != nil && rc.wrapper.MlockEnabled()) {
if rc.MLock || (rc.Wrapper != nil && rc.Wrapper.MlockEnabled()) {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMlockEnabled, "true"))
}
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
@ -54,6 +54,9 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
metadataEnv := fmt.Sprintf("%s=%t", PluginMetadataModeEnv, rc.IsMetadataMode)
cmd.Env = append(cmd.Env, metadataEnv)
automtlsEnv := fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, rc.AutoMTLS)
cmd.Env = append(cmd.Env, automtlsEnv)
var clientTLSConfig *tls.Config
if !rc.AutoMTLS && !rc.IsMetadataMode {
// Get a CA TLS Certificate
@ -70,7 +73,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
// Use CA to sign a server cert and wrap the values in a response wrapped
// token.
wrapToken, err := wrapServerConfig(ctx, rc.wrapper, certBytes, key)
wrapToken, err := wrapServerConfig(ctx, rc.Wrapper, certBytes, key)
if err != nil {
return nil, err
}
@ -120,7 +123,7 @@ func Env(env ...string) RunOpt {
func Runner(wrapper RunnerUtil) RunOpt {
return func(rc *runConfig) {
rc.wrapper = wrapper
rc.Wrapper = wrapper
}
}

View File

@ -5,7 +5,8 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/go-version"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"google.golang.org/grpc"
@ -14,7 +15,8 @@ import (
// Looker defines the plugin Lookup function that looks into the plugin catalog
// for available plugins and returns a PluginRunner
type Looker interface {
LookupPlugin(context.Context, string, consts.PluginType) (*PluginRunner, error)
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*PluginRunner, error)
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*PluginRunner, error)
}
// RunnerUtil interface defines the functions needed by the runner to wrap the
@ -35,6 +37,7 @@ type LookRunnerUtil interface {
type PluginClient interface {
Conn() grpc.ClientConnInterface
Reload() error
plugin.ClientProtocol
}
@ -45,6 +48,7 @@ const MultiplexingCtxKey string = "multiplex_id"
type PluginRunner struct {
Name string `json:"name" structs:"name"`
Type consts.PluginType `json:"type" structs:"type"`
Version string `json:"version" structs:"version"`
Command string `json:"command" structs:"command"`
Args []string `json:"args" structs:"args"`
Env []string `json:"env" structs:"env"`
@ -81,6 +85,20 @@ func (r *PluginRunner) RunMetadataMode(ctx context.Context, wrapper RunnerUtil,
)
}
// VersionedPlugin holds any versioning information stored about a plugin in the
// plugin catalog.
type VersionedPlugin struct {
Type string `json:"type"` // string instead of consts.PluginType so that we get the string form in API responses.
Name string `json:"name"`
Version string `json:"version"`
SHA256 string `json:"sha256,omitempty"`
Builtin bool `json:"builtin"`
DeprecationStatus string `json:"deprecation_status,omitempty"`
// Pre-parsed semver struct of the Version field
SemanticVersion *version.Version `json:"-"`
}
// CtxCancelIfCanceled takes a context cancel func and a context. If the context is
// shutdown the cancelfunc is called. This is useful for merging two cancel
// functions.

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.19.4
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: sdk/logical/identity.proto
package logical

View File

@ -137,3 +137,20 @@ type Auditor interface {
AuditRequest(ctx context.Context, input *LogInput) error
AuditResponse(ctx context.Context, input *LogInput) error
}
// Externaler allows us to check if a backend is running externally (i.e., over GRPC)
type Externaler interface {
IsExternal() bool
}
type PluginVersion struct {
Version string
}
// PluginVersioner is an optional interface to return version info.
type PluginVersioner interface {
// PluginVersion returns the version for the backend
PluginVersion() PluginVersion
}
var EmptyPluginVersion = PluginVersion{""}

View File

@ -3,6 +3,7 @@ package logical
import (
"context"
"crypto"
"crypto/cipher"
"io"
)
@ -33,8 +34,9 @@ type ManagedKey interface {
}
type (
ManagedKeyConsumer func(context.Context, ManagedKey) error
ManagedSigningKeyConsumer func(context.Context, ManagedSigningKey) error
ManagedKeyConsumer func(context.Context, ManagedKey) error
ManagedSigningKeyConsumer func(context.Context, ManagedSigningKey) error
ManagedEncryptingKeyConsumer func(context.Context, ManagedEncryptingKey) error
)
type ManagedKeySystemView interface {
@ -51,6 +53,12 @@ type ManagedKeySystemView interface {
// WithManagedSigningKeyByUUID retrieves an instantiated managed signing key for consumption by the given function,
// with the same semantics as WithManagedKeyByUUID
WithManagedSigningKeyByUUID(ctx context.Context, keyUuid, backendUUID string, f ManagedSigningKeyConsumer) error
// WithManagedSigningKeyByName retrieves an instantiated managed signing key for consumption by the given function,
// with the same semantics as WithManagedKeyByName
WithManagedEncryptingKeyByName(ctx context.Context, keyName, backendUUID string, f ManagedEncryptingKeyConsumer) error
// WithManagedSigningKeyByUUID retrieves an instantiated managed signing key for consumption by the given function,
// with the same semantics as WithManagedKeyByUUID
WithManagedEncryptingKeyByUUID(ctx context.Context, keyUuid, backendUUID string, f ManagedEncryptingKeyConsumer) error
}
type ManagedAsymmetricKey interface {
@ -82,3 +90,8 @@ type ManagedSigningKey interface {
// as needed so as to use per request contexts.
GetSigner(context.Context) (crypto.Signer, error)
}
type ManagedEncryptingKey interface {
ManagedKey
GetAEAD(iv []byte) (cipher.AEAD, error)
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.19.4
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: sdk/logical/plugin.proto
package logical

View File

@ -365,6 +365,7 @@ const (
ListOperation = "list"
HelpOperation = "help"
AliasLookaheadOperation = "alias-lookahead"
ResolveRoleOperation = "resolve-role"
// The operations below are called globally, the path is less relevant.
RevokeOperation Operation = "revoke"
@ -377,7 +378,6 @@ type MFACreds map[string][]string
// InitializationRequest stores the parameters and context of an Initialize()
// call being made to a logical.Backend.
type InitializationRequest struct {
// Storage can be used to durably store and retrieve state.
Storage Storage
}

View File

@ -310,3 +310,12 @@ func (w *StatusHeaderResponseWriter) setCustomResponseHeaders(status int) {
}
var _ WrappingResponseWriter = &StatusHeaderResponseWriter{}
// ResolveRoleResponse returns a standard response to be returned by functions handling a ResolveRoleOperation
func ResolveRoleResponse(roleName string) (*Response, error) {
return &Response{
Data: map[string]interface{}{
"role": roleName,
},
}, nil
}

View File

@ -54,7 +54,15 @@ type SystemView interface {
// LookupPlugin looks into the plugin catalog for a plugin with the given
// name. Returns a PluginRunner or an error if a plugin can not be found.
LookupPlugin(context.Context, string, consts.PluginType) (*pluginutil.PluginRunner, error)
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error)
// LookupPluginVersion looks into the plugin catalog for a plugin with the given
// name and version. Returns a PluginRunner or an error if a plugin can not be found.
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*pluginutil.PluginRunner, error)
// ListVersionedPlugins returns information about all plugins of a certain
// type in the catalog, including any versioning information stored for them.
ListVersionedPlugins(ctx context.Context, pluginType consts.PluginType) ([]pluginutil.VersionedPlugin, error)
// NewPluginClient returns a client for managing the lifecycle of plugin
// processes
@ -168,6 +176,14 @@ func (d StaticSystemView) LookupPlugin(_ context.Context, _ string, _ consts.Plu
return nil, errors.New("LookupPlugin is not implemented in StaticSystemView")
}
func (d StaticSystemView) LookupPluginVersion(_ context.Context, _ string, _ consts.PluginType, _ string) (*pluginutil.PluginRunner, error) {
return nil, errors.New("LookupPluginVersion is not implemented in StaticSystemView")
}
func (d StaticSystemView) ListVersionedPlugins(_ context.Context, _ consts.PluginType) ([]pluginutil.VersionedPlugin, error) {
return nil, errors.New("ListVersionedPlugins is not implemented in StaticSystemView")
}
func (d StaticSystemView) MlockEnabled() bool {
return d.EnableMlock
}

View File

@ -0,0 +1,204 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: sdk/logical/version.proto
package logical
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_sdk_logical_version_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_sdk_logical_version_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_sdk_logical_version_proto_rawDescGZIP(), []int{0}
}
// VersionReply is the reply for the Version method.
type VersionReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PluginVersion string `protobuf:"bytes,1,opt,name=plugin_version,json=pluginVersion,proto3" json:"plugin_version,omitempty"`
}
func (x *VersionReply) Reset() {
*x = VersionReply{}
if protoimpl.UnsafeEnabled {
mi := &file_sdk_logical_version_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VersionReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VersionReply) ProtoMessage() {}
func (x *VersionReply) ProtoReflect() protoreflect.Message {
mi := &file_sdk_logical_version_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VersionReply.ProtoReflect.Descriptor instead.
func (*VersionReply) Descriptor() ([]byte, []int) {
return file_sdk_logical_version_proto_rawDescGZIP(), []int{1}
}
func (x *VersionReply) GetPluginVersion() string {
if x != nil {
return x.PluginVersion
}
return ""
}
var File_sdk_logical_version_proto protoreflect.FileDescriptor
var file_sdk_logical_version_proto_rawDesc = []byte{
0x0a, 0x19, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67,
0x69, 0x63, 0x61, 0x6c, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x35, 0x0a,
0x0c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x25, 0x0a,
0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x32, 0x41, 0x0a, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61,
0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_sdk_logical_version_proto_rawDescOnce sync.Once
file_sdk_logical_version_proto_rawDescData = file_sdk_logical_version_proto_rawDesc
)
func file_sdk_logical_version_proto_rawDescGZIP() []byte {
file_sdk_logical_version_proto_rawDescOnce.Do(func() {
file_sdk_logical_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_sdk_logical_version_proto_rawDescData)
})
return file_sdk_logical_version_proto_rawDescData
}
var file_sdk_logical_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_sdk_logical_version_proto_goTypes = []interface{}{
(*Empty)(nil), // 0: logical.Empty
(*VersionReply)(nil), // 1: logical.VersionReply
}
var file_sdk_logical_version_proto_depIdxs = []int32{
0, // 0: logical.PluginVersion.Version:input_type -> logical.Empty
1, // 1: logical.PluginVersion.Version:output_type -> logical.VersionReply
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_sdk_logical_version_proto_init() }
func file_sdk_logical_version_proto_init() {
if File_sdk_logical_version_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_sdk_logical_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_sdk_logical_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_sdk_logical_version_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_sdk_logical_version_proto_goTypes,
DependencyIndexes: file_sdk_logical_version_proto_depIdxs,
MessageInfos: file_sdk_logical_version_proto_msgTypes,
}.Build()
File_sdk_logical_version_proto = out.File
file_sdk_logical_version_proto_rawDesc = nil
file_sdk_logical_version_proto_goTypes = nil
file_sdk_logical_version_proto_depIdxs = nil
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package logical;
option go_package = "github.com/hashicorp/vault/sdk/logical";
message Empty {}
// VersionReply is the reply for the Version method.
message VersionReply {
string plugin_version = 1;
}
// PluginVersion is an optional RPC service implemented by plugins.
service PluginVersion {
// Version returns version information for the plugin.
rpc Version(Empty) returns (VersionReply);
}

View File

@ -0,0 +1,103 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package logical
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// PluginVersionClient is the client API for PluginVersion service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PluginVersionClient interface {
// Version returns version information for the plugin.
Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionReply, error)
}
type pluginVersionClient struct {
cc grpc.ClientConnInterface
}
func NewPluginVersionClient(cc grpc.ClientConnInterface) PluginVersionClient {
return &pluginVersionClient{cc}
}
func (c *pluginVersionClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionReply, error) {
out := new(VersionReply)
err := c.cc.Invoke(ctx, "/logical.PluginVersion/Version", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// PluginVersionServer is the server API for PluginVersion service.
// All implementations must embed UnimplementedPluginVersionServer
// for forward compatibility
type PluginVersionServer interface {
// Version returns version information for the plugin.
Version(context.Context, *Empty) (*VersionReply, error)
mustEmbedUnimplementedPluginVersionServer()
}
// UnimplementedPluginVersionServer must be embedded to have forward compatible implementations.
type UnimplementedPluginVersionServer struct {
}
func (UnimplementedPluginVersionServer) Version(context.Context, *Empty) (*VersionReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
}
func (UnimplementedPluginVersionServer) mustEmbedUnimplementedPluginVersionServer() {}
// UnsafePluginVersionServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PluginVersionServer will
// result in compilation errors.
type UnsafePluginVersionServer interface {
mustEmbedUnimplementedPluginVersionServer()
}
func RegisterPluginVersionServer(s grpc.ServiceRegistrar, srv PluginVersionServer) {
s.RegisterService(&PluginVersion_ServiceDesc, srv)
}
func _PluginVersion_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PluginVersionServer).Version(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/logical.PluginVersion/Version",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PluginVersionServer).Version(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
// PluginVersion_ServiceDesc is the grpc.ServiceDesc for PluginVersion service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PluginVersion_ServiceDesc = grpc.ServiceDesc{
ServiceName: "logical.PluginVersion",
HandlerType: (*PluginVersionServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Version",
Handler: _PluginVersion_Version_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "sdk/logical/version.proto",
}

View File

@ -1,5 +1,10 @@
package physical
import (
"encoding/hex"
"fmt"
)
// Entry is used to represent data stored by the physical backend
type Entry struct {
Key string
@ -9,3 +14,7 @@ type Entry struct {
// Only used in replication
ValueHash []byte
}
func (e *Entry) String() string {
return fmt.Sprintf("Key: %s. SealWrap: %t. Value: %s. ValueHash: %s", e.Key, e.SealWrap, hex.EncodeToString(e.Value), hex.EncodeToString(e.ValueHash))
}

View File

@ -10,10 +10,9 @@ import (
"sync"
"sync/atomic"
"github.com/armon/go-radix"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/sdk/physical"
radix "github.com/armon/go-radix"
)
// Verify interfaces are satisfied
@ -27,10 +26,11 @@ var (
)
var (
PutDisabledError = errors.New("put operations disabled in inmem backend")
GetDisabledError = errors.New("get operations disabled in inmem backend")
DeleteDisabledError = errors.New("delete operations disabled in inmem backend")
ListDisabledError = errors.New("list operations disabled in inmem backend")
PutDisabledError = errors.New("put operations disabled in inmem backend")
GetDisabledError = errors.New("get operations disabled in inmem backend")
DeleteDisabledError = errors.New("delete operations disabled in inmem backend")
ListDisabledError = errors.New("list operations disabled in inmem backend")
GetInTxnDisabledError = errors.New("get operations inside transactions are disabled in inmem backend")
)
// InmemBackend is an in-memory only physical backend. It is useful
@ -45,6 +45,7 @@ type InmemBackend struct {
failPut *uint32
failDelete *uint32
failList *uint32
failGetInTxn *uint32
logOps bool
maxValueSize int
}
@ -73,6 +74,7 @@ func NewInmem(conf map[string]string, logger log.Logger) (physical.Backend, erro
failPut: new(uint32),
failDelete: new(uint32),
failList: new(uint32),
failGetInTxn: new(uint32),
logOps: os.Getenv("VAULT_INMEM_LOG_ALL_OPS") != "",
maxValueSize: maxValueSize,
}, nil
@ -100,6 +102,7 @@ func NewTransactionalInmem(conf map[string]string, logger log.Logger) (physical.
failPut: new(uint32),
failDelete: new(uint32),
failList: new(uint32),
failGetInTxn: new(uint32),
logOps: os.Getenv("VAULT_INMEM_LOG_ALL_OPS") != "",
maxValueSize: maxValueSize,
},
@ -189,6 +192,14 @@ func (i *InmemBackend) FailGet(fail bool) {
atomic.StoreUint32(i.failGet, val)
}
func (i *InmemBackend) FailGetInTxn(fail bool) {
var val uint32
if fail {
val = 1
}
atomic.StoreUint32(i.failGetInTxn, val)
}
// Delete is used to permanently delete an entry
func (i *InmemBackend) Delete(ctx context.Context, key string) error {
i.permitPool.Acquire()
@ -280,7 +291,7 @@ func (i *InmemBackend) FailList(fail bool) {
atomic.StoreUint32(i.failList, val)
}
// Implements the transaction interface
// Transaction implements the transaction interface
func (t *TransactionalInmemBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
t.permitPool.Acquire()
defer t.permitPool.Release()
@ -288,5 +299,12 @@ func (t *TransactionalInmemBackend) Transaction(ctx context.Context, txns []*phy
t.Lock()
defer t.Unlock()
failGetInTxn := atomic.LoadUint32(t.failGetInTxn)
for _, t := range txns {
if t.Operation == physical.GetOperation && failGetInTxn != 0 {
return GetInTxnDisabledError
}
}
return physical.GenericTransactionHandler(ctx, t, txns)
}

View File

@ -2,8 +2,9 @@ package physical
import (
"context"
"fmt"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-multierror"
)
// TxnEntry is an operation that takes atomically as part of
@ -13,6 +14,10 @@ type TxnEntry struct {
Entry *Entry
}
func (t *TxnEntry) String() string {
return fmt.Sprintf("Operation: %s. Entry: %s", t.Operation, t.Entry)
}
// Transactional is an optional interface for backends that
// support doing transactional updates of multiple keys. This is
// required for some features such as replication.
@ -40,6 +45,19 @@ func GenericTransactionHandler(ctx context.Context, t PseudoTransactional, txns
rollbackStack := make([]*TxnEntry, 0, len(txns))
var dirty bool
// Update all of our GET transaction entries, so we can populate existing values back at the wal layer.
for _, txn := range txns {
if txn.Operation == GetOperation {
entry, err := t.GetInternal(ctx, txn.Entry.Key)
if err != nil {
return err
}
if entry != nil {
txn.Entry.Value = entry.Value
}
}
}
// We walk the transactions in order; each successful operation goes into a
// LIFO for rollback if we hit an error along the way
TxnWalk:
@ -78,6 +96,7 @@ TxnWalk:
dirty = true
break TxnWalk
}
// Nothing existed so in fact rolling back requires a delete
var rollbackEntry *TxnEntry
if entry == nil {

6
vendor/modules.txt vendored
View File

@ -332,11 +332,11 @@ github.com/hashicorp/hcl/json/token
## explicit; go 1.13
github.com/hashicorp/vault/command/agent/auth
github.com/hashicorp/vault/command/agent/auth/kubernetes
# github.com/hashicorp/vault/api v1.7.2
# github.com/hashicorp/vault/api v1.8.1
## explicit; go 1.17
github.com/hashicorp/vault/api
# github.com/hashicorp/vault/sdk v0.5.1
## explicit; go 1.17
# github.com/hashicorp/vault/sdk v0.6.0
## explicit; go 1.19
github.com/hashicorp/vault/sdk/helper/certutil
github.com/hashicorp/vault/sdk/helper/compressutil
github.com/hashicorp/vault/sdk/helper/consts