mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-14 11:03:09 +02:00
116a91ce28
* added secret fetching from vault * added rootPath and vault namespaces * write tests for vault config loading * fix smaller bugs * rename rootPath to basePath * fix integration test * make basePath optional * write more tests * remove unnecessary code * make basePath configurable * remove mixin for vault * rewrite tests * fix tests for searching over multiple paths Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
154 lines
3.4 KiB
Go
154 lines
3.4 KiB
Go
package vault
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
// Client handles communication with Vault
|
|
type Client struct {
|
|
lClient logicalClient
|
|
}
|
|
|
|
// logicalClient interface for mocking
|
|
type logicalClient interface {
|
|
Read(string) (*api.Secret, error)
|
|
}
|
|
|
|
// NewClient instantiates a Client and sets the specified token
|
|
func NewClient(config *api.Config, token, namespace string) (Client, error) {
|
|
if config == nil {
|
|
config = api.DefaultConfig()
|
|
}
|
|
client, err := api.NewClient(config)
|
|
if err != nil {
|
|
return Client{}, err
|
|
}
|
|
|
|
if namespace != "" {
|
|
client.SetNamespace(namespace)
|
|
}
|
|
|
|
client.SetToken(token)
|
|
return Client{client.Logical()}, nil
|
|
}
|
|
|
|
// GetSecret uses the given path to fetch a secret from vault
|
|
func (v Client) GetSecret(path string) (*api.Secret, error) {
|
|
path = sanitizePath(path)
|
|
c := v.lClient
|
|
|
|
secret, err := c.Read(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return secret, nil
|
|
}
|
|
|
|
// GetKvSecret reads secret from the KV engine.
|
|
// It Automatically transforms the logical path to the HTTP API Path for the corresponding KV Engine version
|
|
func (v Client) GetKvSecret(path string) (map[string]string, error) {
|
|
path = sanitizePath(path)
|
|
mountpath, version, err := v.getKvInfo(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if version == 2 {
|
|
path = addPrefixToKvPath(path, mountpath, "data")
|
|
} else if version != 1 {
|
|
return nil, fmt.Errorf("KV Engine in version %d is currently not supported", version)
|
|
}
|
|
|
|
secret, err := v.GetSecret(path)
|
|
if secret == nil || err != nil {
|
|
return nil, err
|
|
|
|
}
|
|
var rawData interface{}
|
|
switch version {
|
|
case 1:
|
|
rawData = secret.Data
|
|
case 2:
|
|
var ok bool
|
|
rawData, ok = secret.Data["data"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("Missing 'data' field in response: %v", rawData)
|
|
}
|
|
}
|
|
|
|
data, ok := rawData.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("Excpected 'data' field to be a map[string]interface{} but got %T instead", rawData)
|
|
}
|
|
|
|
secretData := make(map[string]string, len(data))
|
|
for k, v := range data {
|
|
valueStr, ok := v.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Expected secret value to be a string but got %T instead", v)
|
|
}
|
|
secretData[k] = valueStr
|
|
}
|
|
return secretData, nil
|
|
}
|
|
|
|
func addPrefixToKvPath(p, mountPath, apiPrefix string) string {
|
|
switch {
|
|
case p == mountPath, p == strings.TrimSuffix(mountPath, "/"):
|
|
return path.Join(mountPath, apiPrefix)
|
|
default:
|
|
p = strings.TrimPrefix(p, mountPath)
|
|
return path.Join(mountPath, apiPrefix, p)
|
|
}
|
|
}
|
|
|
|
func (v *Client) getKvInfo(path string) (string, int, error) {
|
|
secret, err := v.GetSecret("sys/internal/ui/mounts/" + path)
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
|
|
if secret == nil {
|
|
return "", 0, fmt.Errorf("Failed to get version and engine mountpoint for path: %s", path)
|
|
}
|
|
|
|
var mountPath string
|
|
if mountPathRaw, ok := secret.Data["path"]; ok {
|
|
mountPath = mountPathRaw.(string)
|
|
}
|
|
|
|
options := secret.Data["options"]
|
|
if options == nil {
|
|
return mountPath, 1, nil
|
|
}
|
|
|
|
versionRaw := options.(map[string]interface{})["version"]
|
|
if versionRaw == nil {
|
|
return mountPath, 1, nil
|
|
}
|
|
|
|
version := versionRaw.(string)
|
|
if version == "" {
|
|
return mountPath, 1, nil
|
|
}
|
|
|
|
vNumber, err := strconv.Atoi(version)
|
|
if err != nil {
|
|
return mountPath, 0, err
|
|
}
|
|
|
|
return mountPath, vNumber, nil
|
|
}
|
|
|
|
func sanitizePath(path string) string {
|
|
path = strings.TrimSpace(path)
|
|
path = strings.TrimPrefix(path, "/")
|
|
path = strings.TrimSuffix(path, "/")
|
|
return path
|
|
}
|