1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/pkg/config/vault.go
Kevin Stiehl 24aafb0b69
add vaultSecretFileReferences (#2203)
* add vaultSecretFileReferences

* fix test

* fix test

* go generate

* remove code duplication

Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
2020-10-26 14:20:04 +01:00

165 lines
4.7 KiB
Go

package config
import (
"io/ioutil"
"os"
"github.com/SAP/jenkins-library/pkg/config/interpolation"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/vault"
"github.com/hashicorp/vault/api"
)
var (
vaultFilter = []string{
"vaultAppRoleID",
"vaultAppRoleSecreId",
"vaultServerUrl",
"vaultNamespace",
"vaultBasePath",
"vaultPipelineName",
"vaultPath",
}
// VaultSecretFileDirectory holds the directory for the current step run to temporarily store secret files fetched from vault
VaultSecretFileDirectory = ""
)
// VaultCredentials hold all the auth information needed to fetch configuration from vault
type VaultCredentials struct {
AppRoleID string
AppRoleSecretID string
}
// vaultClient interface for mocking
type vaultClient interface {
GetKvSecret(string) (map[string]string, error)
}
func getVaultClientFromConfig(config StepConfig, creds VaultCredentials) (vaultClient, error) {
address, addressOk := config.Config["vaultServerUrl"].(string)
// if vault isn't used it's not an error
if !addressOk || creds.AppRoleID == "" || creds.AppRoleSecretID == "" {
log.Entry().Info("Skipping fetching secrets from vault since it is not configured")
return nil, nil
}
namespace := ""
// namespaces are only available in vault enterprise so using them should be optional
if config.Config["vaultNamespace"] != nil {
namespace = config.Config["vaultNamespace"].(string)
log.Entry().Debugf("Using vault namespace %s", namespace)
}
client, err := vault.NewClientWithAppRole(&api.Config{Address: address}, creds.AppRoleID, creds.AppRoleSecretID, namespace)
if err != nil {
return nil, err
}
log.Entry().Infof("Fetching secrets from vault at %s", address)
return &client, nil
}
func resolveAllVaultReferences(config *StepConfig, client vaultClient, params []StepParameters) {
for _, param := range params {
// we don't overwrite secrets that have already been set in any way
if _, ok := config.Config[param.Name].(string); ok {
continue
}
if ref := param.GetReference("vaultSecret"); ref != nil {
resolveVaultReference(ref, config, client, param)
}
if ref := param.GetReference("vaultSecretFile"); ref != nil {
resolveVaultReference(ref, config, client, param)
}
}
}
func resolveVaultReference(ref *ResourceReference, config *StepConfig, client vaultClient, param StepParameters) {
var secretValue *string
for _, vaultPath := range ref.Paths {
// it should be possible to configure the root path were the secret is stored
vaultPath, ok := interpolation.ResolveString(vaultPath, config.Config)
if !ok {
continue
}
secretValue = lookupPath(client, vaultPath, &param)
if secretValue != nil {
log.Entry().Infof("Resolved param '%s' with vault path '%s'", param.Name, vaultPath)
if ref.Type == "vaultSecret" {
config.Config[param.Name] = *secretValue
} else if ref.Type == "vaultSecretFile" {
filePath, err := createTemporarySecretFile(param.Name, *secretValue)
if err != nil {
log.Entry().WithError(err).Warnf("Couldn't create temporary secret file for '%s'", param.Name)
return
}
config.Config[param.Name] = filePath
}
break
}
}
if secretValue == nil {
log.Entry().Warnf("Could not resolve param '%s' from vault", param.Name)
}
}
// RemoveVaultSecretFiles removes all secret files that have been created during execution
func RemoveVaultSecretFiles() {
if VaultSecretFileDirectory != "" {
os.RemoveAll(VaultSecretFileDirectory)
}
}
func createTemporarySecretFile(namePattern string, content string) (string, error) {
if VaultSecretFileDirectory == "" {
var err error
VaultSecretFileDirectory, err = ioutil.TempDir("", "vault")
if err != nil {
return "", err
}
}
file, err := ioutil.TempFile(VaultSecretFileDirectory, namePattern)
if err != nil {
return "", err
}
defer file.Close()
_, err = file.WriteString(content)
if err != nil {
return "", err
}
return file.Name(), nil
}
func lookupPath(client vaultClient, path string, param *StepParameters) *string {
log.Entry().Infof("Trying to resolve vault parameter '%s' at '%s'", param.Name, path)
secret, err := client.GetKvSecret(path)
if err != nil {
log.Entry().WithError(err).Warnf("Couldn't fetch secret at '%s'", path)
return nil
}
if secret == nil {
return nil
}
field := secret[param.Name]
if field != "" {
log.RegisterSecret(field)
return &field
}
// try parameter aliases
for _, alias := range param.Aliases {
field := secret[param.Name]
if field != "" {
log.RegisterSecret(field)
if alias.Deprecated {
log.Entry().WithField("package", "SAP/jenkins-library/pkg/config").Warningf("DEPRECATION NOTICE: old step config key '%s' used in vault. Please switch to '%s'!", alias.Name, param.Name)
}
return &field
}
}
return nil
}