Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rudimentary support for KV version 2 #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 5 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@ module github.com/cruise-automation/daytona

require (
cloud.google.com/go v0.40.0
github.com/aws/aws-sdk-go v1.19.39
github.com/aws/aws-sdk-go v1.25.37
github.com/briankassouf/jose v0.9.2-0.20180619214549-d2569464773f
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/frankban/quicktest v1.4.1 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-gcp-common v0.5.0
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/hashicorp/vault/api v1.0.5-0.20190909201928-35325e2c3262
github.com/hashicorp/vault v1.3.3
github.com/hashicorp/vault-plugin-secrets-kv v0.5.4
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820
github.com/hashicorp/vault/sdk v0.1.14-0.20200305172021-03a3749f220d
github.com/mitchellh/go-homedir v1.1.0
github.com/pierrec/lz4 v2.2.6+incompatible // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
google.golang.org/api v0.6.0
google.golang.org/appengine v1.6.0 // indirect
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect
)

go 1.13
502 changes: 498 additions & 4 deletions go.sum

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions pkg/secrets/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package secrets

import (
"io/ioutil"
"os"
"testing"

kv "github.com/hashicorp/vault-plugin-secrets-kv"
"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
)

func startVaultTestCluster(t *testing.T) (*vault.TestCluster, *api.Client) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": kv.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
NumCores: 1,
})

cluster.Start()

core := cluster.Cores[0]
vault.TestWaitActive(t, core.Core)
client := core.Client

err := client.Sys().Mount("kv", &api.MountInput{
Type: "kv-v2",
})
if err != nil {
t.Fatal(err)
}

return cluster, client
}

func generateTestFile(prefix string) (*os.File, error) {
file, err := ioutil.TempFile(os.TempDir(), prefix)
if err != nil {
return nil, err
}
return file, nil
}
4 changes: 3 additions & 1 deletion pkg/secrets/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package secrets

import (
"context"
"log"

"github.com/hashicorp/vault/api"
)
Expand Down Expand Up @@ -90,6 +91,7 @@ func (pr *ParallelReader) worker() {
case <-pr.ctx.Done():
return
case keyPath := <-pr.keyPathInChan:
log.Printf("Reading path %s\n", keyPath)
secret, err := pr.logicalClient.Read(keyPath)
pr.secretOutChan <- &SecretResult{
KeyPath: keyPath,
Expand All @@ -98,4 +100,4 @@ func (pr *ParallelReader) worker() {
}
}
}
}
}
31 changes: 29 additions & 2 deletions pkg/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func SecretFetcher(client *api.Client, config cfg.Config) {
for _, env := range envs {
// VAULT_SECRET_WHATEVER=secret/application/thing
// VAULT_SECRETS_WHATEVER=secret/application/things
// envKey=secretPath
// envKey=apex
pair := strings.Split(env, "=")
envKey := pair[0]
apex := os.Getenv(envKey)
Expand All @@ -90,6 +90,8 @@ func SecretFetcher(client *api.Client, config cfg.Config) {
continue
}

log.Printf("Found %s, attempting to read secrets from %s", envKey, apex)

// look for a corresponding secretDestinationPrefix key.
// sometimes these can be cased inconsistently so we have to attempt normalization.
// e.g. VAULT_SECRET_APPLICATIONA --> DAYTONA_SECRET_DESTINATION_applicationa
Expand Down Expand Up @@ -208,6 +210,13 @@ func (sd *SecretDefinition) addSecrets(client *api.Client, secretResult *SecretR
}
secretData := secret.Data

// kv version 2 nests secret data beneath two data keys
// this makes a best effort to extract it
nestedData, ok := secretData["data"]
if ok {
secretData = nestedData.(map[string]interface{})
}

// Return last error encountered during processing, if any
var lastErr error

Expand All @@ -228,6 +237,20 @@ func (sd *SecretDefinition) addSecrets(client *api.Client, secretResult *SecretR
func (sd *SecretDefinition) Walk(client *api.Client) error {
paths := make([]string, 0)

// kv version 2 secret backends list keys using a
// <mount_name>/metadata/<your supplied path> convention
// this extracts the mount and path values so they can be
// replaced by 'data' and re-assbled below when being
// added to the path slice
var isKV2 bool
var mount, sPath string
kv2Split := strings.Split(sd.secretApex, "metadata")
if len(kv2Split) == 2 {
isKV2 = true
mount = kv2Split[0]
sPath = kv2Split[1]
}

list, err := client.Logical().List(sd.secretApex)
if err != nil {
return fmt.Errorf("there was a problem listing %s: %s", sd.secretApex, err)
Expand All @@ -246,7 +269,11 @@ func (sd *SecretDefinition) Walk(client *api.Client) error {
if !ok {
return fmt.Errorf("non-string secret name: %#v", key)
}
paths = append(paths, path.Join(sd.secretApex, key))
apex := sd.secretApex
if isKV2 {
apex = mount + "data" + sPath
}
paths = append(paths, path.Join(apex, key))
}
sd.paths = paths
return nil
Expand Down
68 changes: 68 additions & 0 deletions pkg/secrets/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,71 @@ func TestSecretAWalk(t *testing.T) {
assert.Equal(t, "bbbb", destSecrets["credentials_api_b"])
assert.Equal(t, "password", destSecrets["other"])
}

func TestKV2(t *testing.T) {
cluster, client := startVaultTestCluster(t)
defer cluster.Cleanup()

var config cfg.Config

_, err := client.Logical().Write(fmt.Sprintf("kv/data/single"), map[string]interface{}{
"data": map[string]interface{}{
"value": "just a regular ole value",
},
})
if err != nil {
t.Fatal(err)
}

for i := 1; i < 4; i++ {
_, err := client.Logical().Write(fmt.Sprintf("kv/data/multiple/thing%v", i), map[string]interface{}{
"data": map[string]interface{}{
"value": fmt.Sprintf("%v", i),
},
})
if err != nil {
t.Fatal(err)
}
}

testFiles := make([]*os.File, 0, 2)
testFileNames := []string{"secret-kv2-multiple-dest-", "secret-kv2-single-"}

for _, fileName := range testFileNames {
f, err := generateTestFile(fileName)
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
testFiles = append(testFiles, f)
}

os.Setenv("VAULT_SECRETS_THING", "kv/metadata/multiple")
os.Setenv("DAYTONA_SECRET_DESTINATION_THING", testFiles[0].Name())
os.Setenv("VAULT_SECRET_SINGLE", "kv/data/single")
os.Setenv("DAYTONA_SECRET_DESTINATION_SINGLE", testFiles[1].Name())
defer os.Unsetenv("VAULT_SECRETS_THING")
defer os.Unsetenv("DAYTONA_SECRET_DESTINATION_THING")
defer os.Unsetenv("VAULT_SECRET_SINGLE")
defer os.Unsetenv("DAYTONA_SECRET_DESTINATION_SINGLE")

SecretFetcher(client, config)

type expected struct {
Name string
Payload string
}

expectedData := []expected{
expected{Name: testFiles[0].Name(), Payload: `{"thing1":"1","thing2":"2","thing3":"3"}`},
expected{Name: testFiles[1].Name(), Payload: "just a regular ole value"},
}

for _, expected := range expectedData {
data, err := ioutil.ReadFile(expected.Name)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, expected.Payload, string(data))
}
}
19 changes: 19 additions & 0 deletions vendor/github.com/Jeffail/gabs/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading