1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00

Writing secrets to vault (#2770)

Co-authored-by: Kevin Stiehl <kevin.stiehl@numericas.de>
Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
Siarhei Pazdniakou 2021-05-06 11:27:23 +03:00 committed by GitHub
parent 4ffd61d34c
commit 0673d3fed6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 982 additions and 38 deletions

24
go.mod
View File

@ -9,33 +9,26 @@ require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/Microsoft/hcsshim v0.8.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/bmatcuk/doublestar v1.3.2
github.com/bndr/gojenkins v1.0.1
github.com/containerd/containerd v1.4.1 // indirect
github.com/docker/docker v1.4.2-0.20200114201811-16a3519d870b // indirect
github.com/elliotchance/orderedmap v1.3.0
github.com/evanphx/json-patch v4.9.0+incompatible
github.com/frankban/quicktest v1.10.0 // indirect
github.com/getsentry/sentry-go v0.7.0
github.com/ghodss/yaml v1.0.0
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0
github.com/go-openapi/runtime v0.19.22
github.com/go-openapi/spec v0.19.9 // indirect
github.com/go-openapi/strfmt v0.19.5
github.com/go-openapi/validate v0.19.11 // indirect
github.com/golang/snappy v0.0.2 // indirect
github.com/google/go-cmp v0.5.2
github.com/google/go-cmp v0.5.4
github.com/google/go-containerregistry v0.1.3
github.com/google/go-github/v32 v32.1.0
github.com/google/uuid v1.1.2
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/go-retryablehttp v0.6.7
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/vault/api v1.0.4
github.com/hashicorp/vault v1.7.1
github.com/hashicorp/vault/api v1.1.0
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/jarcoal/httpmock v1.0.8
@ -43,11 +36,7 @@ require (
github.com/magicsong/color-glog v0.0.1 // indirect
github.com/magicsong/sonargo v0.0.1
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/motemen/go-nuts v0.0.0-20200601065735-3df31f16cb2f
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/piper-validation/fortify-client-go v0.0.0-20210114140201-1261216783c6
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
@ -56,16 +45,11 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1
github.com/testcontainers/testcontainers-go v0.5.1
go.mongodb.org/mongo-driver v1.4.1 // indirect
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/mod v0.3.0
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/sync v0.0.0-20200930132711-30421366ff76 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect
google.golang.org/grpc v1.32.0 // indirect
gopkg.in/ini.v1 v1.61.0
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.3.0
)

836
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,70 @@ func TestGetVaultSecret(t *testing.T) {
}
func TestWriteVaultSecret(t *testing.T) {
t.Parallel()
ctx := context.Background()
const testToken = "vault-token"
req := testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
AlwaysPullImage: true,
Image: "vault:1.4.3",
ExposedPorts: []string{"8200/tcp"},
Env: map[string]string{"VAULT_DEV_ROOT_TOKEN_ID": testToken},
WaitingFor: wait.ForLog("Vault server started!").WithStartupTimeout(20 * time.Second)},
Started: true,
}
vaultContainer, err := testcontainers.GenericContainer(ctx, req)
assert.NoError(t, err)
defer vaultContainer.Terminate(ctx)
ip, err := vaultContainer.Host(ctx)
assert.NoError(t, err)
port, err := vaultContainer.MappedPort(ctx, "8200")
host := fmt.Sprintf("http://%s:%s", ip, port.Port())
config := &vault.Config{Config: &api.Config{Address: host}}
// setup vault for testing
secretData := map[string]string{
"key1": "value1",
"key2": "value2",
}
client, err := vault.NewClient(config, testToken)
assert.NoError(t, err)
err = client.WriteKvSecret("secret/test", secretData)
assert.NoError(t, err)
secret, err := client.GetKvSecret("secret/test")
assert.NoError(t, err)
assert.Equal(t, "value1", secret["key1"])
assert.Equal(t, "value2", secret["key2"])
// enabling KV engine 1
vaultClient, err := api.NewClient(config.Config)
assert.NoError(t, err)
vaultClient.SetToken(testToken)
_, err = vaultClient.Logical().Write("sys/mounts/kv", SecretData{
"path": "kv",
"type": "kv",
"options": SecretData{
"version": "1",
},
})
assert.NoError(t, err)
err = client.WriteKvSecret("secret/test1", secretData)
assert.NoError(t, err)
secret, err = client.GetKvSecret("secret/test1")
assert.NoError(t, err)
assert.Equal(t, "value1", secret["key1"])
assert.Equal(t, "value2", secret["key2"])
}
func TestVaultAppRole(t *testing.T) {
t.Parallel()
ctx := context.Background()

View File

@ -326,7 +326,7 @@ steps:
var c Config
myConfig := ioutil.NopCloser(strings.NewReader("invalid config"))
_, err := c.GetStepConfig(nil, "", myConfig, nil, false, StepFilters{}, []StepParameters{}, nil, nil, "stage1", "step1", []Alias{})
assert.EqualError(t, err, "failed to parse custom pipeline configuration: format of configuration is invalid \"invalid config\": error unmarshaling JSON: json: cannot unmarshal string into Go value of type config.Config", "default error expected")
assert.EqualError(t, err, "failed to parse custom pipeline configuration: format of configuration is invalid \"invalid config\": error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type config.Config", "default error expected")
})
t.Run("Failure case defaults", func(t *testing.T) {
@ -334,7 +334,7 @@ steps:
myConfig := ioutil.NopCloser(strings.NewReader(""))
myDefaults := []io.ReadCloser{ioutil.NopCloser(strings.NewReader("invalid defaults"))}
_, err := c.GetStepConfig(nil, "", myConfig, myDefaults, false, StepFilters{}, []StepParameters{}, nil, nil, "stage1", "step1", []Alias{})
assert.EqualError(t, err, "failed to read default configuration: error unmarshalling \"invalid defaults\": error unmarshaling JSON: json: cannot unmarshal string into Go value of type config.Config", "default error expected")
assert.EqualError(t, err, "failed to read default configuration: error unmarshalling \"invalid defaults\": error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type config.Config", "default error expected")
})
//ToDo: test merging of env and parameters/flags

View File

@ -146,6 +146,35 @@ func (v Client) GetKvSecret(path string) (map[string]string, error) {
return secretData, nil
}
// WriteKvSecret writes secret to kv engine
func (v Client) WriteKvSecret(path string, newSecret map[string]string) error {
oldSecret, err := v.GetKvSecret(path)
if err != nil {
return err
}
secret := make(map[string]interface{}, len(oldSecret))
for k, v := range oldSecret {
secret[k] = v
}
for k, v := range newSecret {
secret[k] = v
}
path = sanitizePath(path)
mountpath, version, err := v.getKvInfo(path)
if err != nil {
return err
}
if version == 2 {
path = addPrefixToKvPath(path, mountpath, "data")
secret = map[string]interface{}{"data": secret}
} else if version != 1 {
return fmt.Errorf("KV Engine in version %d is currently not supported", version)
}
_, err = v.lClient.Write(path, secret)
return err
}
// GenerateNewAppRoleSecret creates a new secret-id
func (v *Client) GenerateNewAppRoleSecret(secretID, appRoleName string) (string, error) {
appRolePath := v.getAppRolePath(appRoleName)

View File

@ -3,18 +3,22 @@ package vault
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"path"
"strings"
"testing"
"time"
"github.com/pkg/errors"
"github.com/stretchr/testify/mock"
mocks "github.com/SAP/jenkins-library/pkg/vault/mocks"
"github.com/hashicorp/vault/api"
"github.com/stretchr/testify/assert"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
)
type SecretData = map[string]interface{}
@ -111,6 +115,63 @@ func TestGetKV1Secret(t *testing.T) {
})
}
func TestWriteKvSecret(t *testing.T) {
const secretName = "secret/test"
tests := []struct {
name string
initialSecret map[string]string
writingSecret map[string]string
expectedSecret map[string]string
}{
{
name: "Test write new KV2 secret",
initialSecret: nil,
writingSecret: map[string]string{"key": "value"},
expectedSecret: map[string]string{"key": "value"},
},
{
name: "Test rewrite KV2 secret with new keys",
initialSecret: map[string]string{"key1": "value1"},
writingSecret: map[string]string{"key2": "value2"},
expectedSecret: map[string]string{"key1": "value1", "key2": "value2"},
},
{
name: "Test rewrite KV2 secret with existed keys",
initialSecret: map[string]string{"key1": "value1"},
writingSecret: map[string]string{"key1": "value2"},
expectedSecret: map[string]string{"key1": "value2"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cluster := vault.NewTestCluster(t, &vault.CoreConfig{
DevToken: "token",
}, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
vaultClient := cluster.Cores[0].Client
client := Client{vaultClient.Logical(), &Config{}}
cluster.Start()
defer cluster.Cleanup()
if test.initialSecret != nil {
err := client.WriteKvSecret(secretName, test.initialSecret)
assert.NoError(t, err)
}
err := client.WriteKvSecret(secretName, test.writingSecret)
assert.NoError(t, err)
secret, err := client.GetKvSecret(secretName)
assert.NoError(t, err)
assert.Equal(t, test.expectedSecret, secret)
})
}
}
func TestSecretIDGeneration(t *testing.T) {
t.Parallel()
const secretID = "secret-id"