From 00a7fddcb747e76a116565c6834cf12588410a5b Mon Sep 17 00:00:00 2001 From: Dominik Schulz Date: Tue, 27 Mar 2018 11:25:08 +0200 Subject: [PATCH] Use secrets config for Vault (#730) --- pkg/store/root/mount.go | 2 +- pkg/store/vault/seccfg.go | 54 ++++++++++++++++++++++++++++++++++++++ pkg/store/vault/store.go | 55 ++++++++++++++++++++++++++++++--------- 3 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 pkg/store/vault/seccfg.go diff --git a/pkg/store/root/mount.go b/pkg/store/root/mount.go index ed5dbd5f8e..0434dc565b 100644 --- a/pkg/store/root/mount.go +++ b/pkg/store/root/mount.go @@ -88,7 +88,7 @@ func (r *Store) addMount(ctx context.Context, alias, path string, sc *config.Sto } func (r *Store) initSubVault(ctx context.Context, alias string, path *backend.URL) (store.Store, error) { - return vault.New(alias, path) + return vault.New(ctx, alias, path, r.cfg.Directory(), r.agent) } func (r *Store) initSub(ctx context.Context, alias string, path *backend.URL, keys []string) (store.Store, error) { diff --git a/pkg/store/vault/seccfg.go b/pkg/store/vault/seccfg.go new file mode 100644 index 0000000000..d38dcc9204 --- /dev/null +++ b/pkg/store/vault/seccfg.go @@ -0,0 +1,54 @@ +package vault + +import ( + "context" + + "github.com/justwatchcom/gopass/pkg/config/secrets" +) + +func (s *Store) storeSecret(ctx context.Context, key, value string) error { + pw, err := s.agent.Passphrase(ctx, "config.sec", "Please enter passphrase to (un)lock config secrets") + if err != nil { + return err + } + seccfg, err := secrets.New(s.cfgdir, pw) + if err != nil { + return err + } + return seccfg.Set(key, value) +} + +func (s *Store) eraseSecret(ctx context.Context, key string) error { + pw, err := s.agent.Passphrase(ctx, "config.sec", "Please enter passphrase to (un)lock config secrets") + if err != nil { + return err + } + seccfg, err := secrets.New(s.cfgdir, pw) + if err != nil { + return err + } + _ = s.agent.Remove(ctx, key) + return seccfg.Unset(key) +} + +func (s *Store) loadSecret(ctx context.Context, key string) (string, error) { + pw, err := s.agent.Passphrase(ctx, "config.sec", "Please enter passphrase to (un)lock config secrets") + if err != nil { + return "", err + } + seccfg, err := secrets.New(s.cfgdir, pw) + if err != nil { + return "", err + } + t, err := seccfg.Get(key) + if err == nil && t != "" { + return t, nil + } + t, err = s.agent.Passphrase(ctx, key, "Please enter the secret "+key) + if err != nil { + return "", err + } + _ = s.agent.Remove(ctx, key) + err = seccfg.Set(key, t) + return t, err +} diff --git a/pkg/store/vault/store.go b/pkg/store/vault/store.go index 8930a0cfb2..0f1c1e618d 100644 --- a/pkg/store/vault/store.go +++ b/pkg/store/vault/store.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/hashicorp/vault/api" + "github.com/justwatchcom/gopass/pkg/agent/client" "github.com/justwatchcom/gopass/pkg/backend" "github.com/justwatchcom/gopass/pkg/out" "github.com/justwatchcom/gopass/pkg/store" @@ -21,14 +22,16 @@ const ( // Store is a vault backed store type Store struct { - api *api.Client - alias string - url *backend.URL - path string + api *api.Client + alias string + url *backend.URL + path string + agent *client.Client + cfgdir string } // New creates a new store -func New(alias string, url *backend.URL) (*Store, error) { +func New(ctx context.Context, alias string, url *backend.URL, cfgdir string, agent *client.Client) (*Store, error) { cfg := &api.Config{ Address: fmt.Sprintf("%s://%s:%s", url.Scheme, url.Host, url.Port), } @@ -41,14 +44,42 @@ func New(alias string, url *backend.URL) (*Store, error) { if err != nil { return nil, err } - client.SetToken(url.Query.Get("token")) - return &Store{ - api: client, - alias: alias, - url: url, - path: url.Path, - }, nil + s := &Store{ + api: client, + alias: alias, + url: url, + path: url.Path, + cfgdir: cfgdir, + } + + token := url.Query.Get("token") + key := fmt.Sprintf("vault-%s-%s", alias, url.String()) + if token == "" { + out.Debug(ctx, "Requesting token from secrets config: %s", key) + t, err := s.loadSecret(ctx, key) + if err != nil { + return nil, errors.Wrapf(err, "failed to load token from secrets config: %s", err) + } + out.Debug(ctx, "Got token from secrets config: '%s'", t) + token = t + } + out.Debug(ctx, "Vault-Token: '%s'", token) + + s.api.SetToken(token) + + // test connection and save token if it works + if _, err := s.List(ctx, ""); err != nil { + out.Debug(ctx, "Vault access not working. removing saved token") + _ = s.eraseSecret + return nil, err + } + out.Debug(ctx, "Vault access OK. saving token") + if err := s.storeSecret(ctx, key, token); err != nil { + return nil, err + } + + return s, nil } func configureTLS(q url.Values, cfg *api.Config) error {