From 1f50659e87c1b4a8d66696b9aa026e40aafbf068 Mon Sep 17 00:00:00 2001 From: Marc Sauter Date: Mon, 18 Mar 2019 15:45:28 +0100 Subject: [PATCH] pkg/auth now in github.com/postfinance/vault/k8s --- README.md | 4 +- cmd/authenticator/main.go | 15 +-- cmd/synchronizer/main.go | 102 +++++++--------- cmd/token-renewer/main.go | 20 +-- demo/k8s/authenticator/deployment.yaml | 4 +- demo/k8s/synchronizer/deployment.yaml | 4 +- demo/k8s/token-renewer/deployment.yaml | 8 +- demo/profile | 2 +- go.mod | 106 ++++++++++++++-- pkg/auth/auth.go | 161 ------------------------- 10 files changed, 166 insertions(+), 260 deletions(-) delete mode 100644 pkg/auth/auth.go diff --git a/README.md b/README.md index eb16d6d..ce6f5ea 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,9 @@ The usual environment variables for Vault will be used: - VAULT_TOKEN_PATH - the destination path on disk to store the token. Usually this is a shared volume. -- VAULT_K8S_MOUNT_PATH - the name of the mount where the Kubernetes auth method is enabled. This defaults to auth/kubernetes, but if you changed the mount path you will need to set this value to that path (vault auth enable -path=k8s kubernetes -> VAULT_K8S_MOUNT_PATH=auth/k8s) +- VAULT_AUTH_MOUNT_PATH - the name of the mount where the Kubernetes auth method is enabled. This defaults to kubernetes, but if you changed the mount path you will need to set this value to that path (vault auth enable -path=k8s kubernetes -> VAULT_AUTH_MOUNT_PATH=k8s) -- SERVICE_ACCOUNT_PATH - the path on disk where the Kubernetes service account jtw token lives. This defaults to /var/run/secrets/kubernetes.io/serviceaccount/token. +- SERVICE_ACCOUNT_TOKEN_PATH - the path on disk where the Kubernetes service account jtw token lives. This defaults to /var/run/secrets/kubernetes.io/serviceaccount/token. - ALLOW_FAIL - the container will successfully terminate even if the authentication to Vault failed, no token will be written to VAULT_TOKEN_PATH. **This condition needs to be handeled in the succeeding container.** (default: "false") diff --git a/cmd/authenticator/main.go b/cmd/authenticator/main.go index 8fc2650..8b0d283 100644 --- a/cmd/authenticator/main.go +++ b/cmd/authenticator/main.go @@ -2,7 +2,7 @@ // // the received Vault token will be stored in VAULT_TOKEN_PATH // -// authenticator is meant to be used in an init container on Kubernetes. +// authenticator is meant to be used in an init container on Kubernetes package main import ( @@ -10,13 +10,13 @@ import ( "os" "github.com/pkg/errors" - "github.com/postfinance/vault-kubernetes/pkg/auth" + "github.com/postfinance/vault/k8s" ) func main() { - c, err := auth.NewConfigFromEnvironment() + c, err := k8s.NewFromEnvironment() if err != nil { - log.Fatal(errors.Wrap(err, "failed to get config")) + log.Fatal(err) } token, err := c.Authenticate() @@ -24,16 +24,13 @@ func main() { if c.AllowFail { log.Println(errors.Wrap(err, "authentication failed - ALLOW_FAIL is set therefore pod will continue")) os.Exit(0) - } else { - log.Fatal(errors.Wrap(err, "authentication failed")) } + log.Fatal(errors.Wrap(err, "authentication failed")) } log.Printf("successfully authenticated to vault") if err := c.StoreToken(token); err != nil { log.Fatal(err) } - log.Printf("successfully stored vault token at %s", c.VaultTokenPath) - - os.Exit(0) + log.Printf("successfully stored vault token at %s", c.TokenPath) } diff --git a/cmd/synchronizer/main.go b/cmd/synchronizer/main.go index 7c20bc4..8d02214 100644 --- a/cmd/synchronizer/main.go +++ b/cmd/synchronizer/main.go @@ -14,8 +14,8 @@ import ( "path" "strings" - "github.com/hashicorp/vault/api" "github.com/pkg/errors" + "github.com/postfinance/vault/k8s" "github.com/postfinance/vault/kv" corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -31,17 +31,22 @@ const ( func main() { c, err := newFromEnvironment() if err != nil { - log.Fatal(errors.Wrap(err, "failed to get config")) + log.Fatal(err) } - if err := c.loadToken(); err != nil { + token, err := c.vault.LoadToken() + if err != nil { if err := c.checkSecrets(); err != nil { log.Fatal(err) } - // you get only here if ALLOW_FAIL=true was set for vault-kubernetes-auth Init Container and vault-kubernetes-auth failed to authenticate + // you get only here ... + // IF ALLOW_FAIL=true was set for vault-kubernetes-authenticator + // AND vault-kubernetes-authenticator failed to authenticate + // AND all testable secrets are present log.Println(errors.Wrap(err, "cannot synchronize secrets - all secrets seems to be available therefore pod creation will continue")) os.Exit(0) } + c.vault.UseToken(token) if err := c.prepare(); err != nil { log.Fatal(errors.Wrap(err, "failed to prepare synchronization of secrets")) @@ -51,25 +56,23 @@ func main() { log.Fatal(errors.Wrap(err, "failed to synchronize secrets")) } log.Printf("secrets successfully synchronized") - - os.Exit(0) } -type config struct { - VaultTokenPath string - Secrets map[string]string // key = kubernetes secret name, value = vault secret name - SecretPrefix string // prefix for kubernetes secret name - Namespace string - k8sClientset *kubernetes.Clientset - vaultClient *api.Client - secretClients map[string]*kv.Client +type syncConfig struct { + Secrets map[string]string // key = kubernetes secret name, value = vault secret name + SecretPrefix string // prefix for kubernetes secret name + Namespace string + k8sClientset *kubernetes.Clientset + secretClients map[string]*kv.Client + vault *k8s.Vault } -func newFromEnvironment() (*config, error) { - c := &config{} - c.VaultTokenPath = os.Getenv("VAULT_TOKEN_PATH") - if c.VaultTokenPath == "" { - return nil, fmt.Errorf("missing VAULT_TOKEN_PATH") +func newFromEnvironment() (*syncConfig, error) { + var err error + c := &syncConfig{} + c.vault, err = k8s.NewFromEnvironment() + if err != nil { + return nil, err } c.Secrets = make(map[string]string) for _, item := range strings.Split(os.Getenv("VAULT_SECRETS"), ",") { @@ -105,38 +108,19 @@ func newFromEnvironment() (*config, error) { if err != nil { return nil, errors.Wrap(err, "failed to get k8s k8sClientset") } - // connect to vault - vaultConfig := api.DefaultConfig() - if err := vaultConfig.ReadEnvironment(); err != nil { - return nil, errors.Wrap(err, "failed to read environment for vault") - } - c.vaultClient, err = api.NewClient(vaultConfig) - if err != nil { - return nil, errors.Wrap(err, "failed to create vault client") - } return c, nil } -// loadToken from VaultTokenPath -func (c *config) loadToken() error { - content, err := ioutil.ReadFile(c.VaultTokenPath) - if err != nil { - return errors.Wrap(err, "could not get vault token") - } - c.vaultClient.SetToken(string(content)) - return nil -} - // checkSecrets check the existence of a secret and not the content -func (c *config) checkSecrets() error { +func (sc *syncConfig) checkSecrets() error { // check secrets - for k, v := range c.Secrets { + for k, v := range sc.Secrets { if strings.HasSuffix(v, "/") { log.Printf("WARNING: cannot check existence of secrets from vault path %s without connection to vault\n", v) continue } log.Println("check k8s secret", k, "from vault secret", v) - _, err := c.k8sClientset.CoreV1().Secrets(c.Namespace).Get(k, metav1.GetOptions{}) + _, err := sc.k8sClientset.CoreV1().Secrets(sc.Namespace).Get(k, metav1.GetOptions{}) if err != nil { return fmt.Errorf("secret %s does not exist", k) } @@ -145,13 +129,13 @@ func (c *config) checkSecrets() error { } // synchronize secret from vault to the current kubernetes namespace -func (c *config) synchronize() error { +func (sc *syncConfig) synchronize() error { // create/update the secrets annotations := make(map[string]string) - for k, v := range c.Secrets { + for k, v := range sc.Secrets { // get secret from vault log.Println("read", v, "from vault") - s, err := c.secretClients[strings.SplitN(v, "/", 2)[0]].Read(v) + s, err := sc.secretClients[strings.SplitN(v, "/", 2)[0]].Read(v) if err != nil { return err } @@ -163,25 +147,25 @@ func (c *config) synchronize() error { // create/update k8s secret annotations[vaultAnnotation] = v secret := &corev1.Secret{} - secret.Name = fmt.Sprintf("%s%s", c.SecretPrefix, k) + secret.Name = fmt.Sprintf("%s%s", sc.SecretPrefix, k) secret.Data = data secret.Annotations = annotations // create (insert) or update the secret - _, err = c.k8sClientset.CoreV1().Secrets(c.Namespace).Get(secret.Name, metav1.GetOptions{}) + _, err = sc.k8sClientset.CoreV1().Secrets(sc.Namespace).Get(secret.Name, metav1.GetOptions{}) if apierr.IsNotFound(err) { log.Println("create secret", secret.Name, "from vault secret", v) - if _, err := c.k8sClientset.CoreV1().Secrets(c.Namespace).Create(secret); err != nil { + if _, err := sc.k8sClientset.CoreV1().Secrets(sc.Namespace).Create(secret); err != nil { return err } continue } log.Println("update secret", secret.Name, "from vault secret", v) - if _, err = c.k8sClientset.CoreV1().Secrets(c.Namespace).Update(secret); err != nil { + if _, err = sc.k8sClientset.CoreV1().Secrets(sc.Namespace).Update(secret); err != nil { return err } } // delete obsolete secrets - secretList, err := c.k8sClientset.CoreV1().Secrets(c.Namespace).List(metav1.ListOptions{}) + secretList, err := sc.k8sClientset.CoreV1().Secrets(sc.Namespace).List(metav1.ListOptions{}) if err != nil { log.Println(errors.Wrap(err, "cleanup of unused vault secrets failed")) os.Exit(0) @@ -192,11 +176,11 @@ func (c *config) synchronize() error { continue } // only if vault secret is not in secrets - if _, ok := c.Secrets[strings.TrimPrefix(s.Name, c.SecretPrefix)]; ok { + if _, ok := sc.Secrets[strings.TrimPrefix(s.Name, sc.SecretPrefix)]; ok { continue } log.Println("delete secret", s.Name) - if err := c.k8sClientset.CoreV1().Secrets(c.Namespace).Delete(s.Name, &metav1.DeleteOptions{}); err != nil { + if err := sc.k8sClientset.CoreV1().Secrets(sc.Namespace).Delete(s.Name, &metav1.DeleteOptions{}); err != nil { log.Println(errors.Wrapf(err, "delete obsolete vault secret %s failed", s.Name)) } } @@ -204,18 +188,18 @@ func (c *config) synchronize() error { } // prepare -func (c *config) prepare() error { - c.secretClients = make(map[string]*kv.Client) +func (sc *syncConfig) prepare() error { + sc.secretClients = make(map[string]*kv.Client) secrets := make(map[string]string) - for k, v := range c.Secrets { + for k, v := range sc.Secrets { mount := strings.SplitN(v, "/", 2)[0] // ensure kv.Client for mount - if _, ok := c.secretClients[mount]; !ok { - secretClient, err := kv.New(c.vaultClient, mount+"/") + if _, ok := sc.secretClients[mount]; !ok { + secretClient, err := kv.New(sc.vault.Client(), mount+"/") if err != nil { return err } - c.secretClients[mount] = secretClient + sc.secretClients[mount] = secretClient } // v is a secret if !strings.HasSuffix(v, "/") { @@ -223,7 +207,7 @@ func (c *config) prepare() error { continue } // v is a path -> get all secrets from v - keys, err := c.secretClients[mount].List(v) + keys, err := sc.secretClients[mount].List(v) if err != nil { return err } @@ -231,6 +215,6 @@ func (c *config) prepare() error { secrets[k] = path.Join(v, k) } } - c.Secrets = secrets + sc.Secrets = secrets return nil } diff --git a/cmd/token-renewer/main.go b/cmd/token-renewer/main.go index eeb5e1e..582643b 100644 --- a/cmd/token-renewer/main.go +++ b/cmd/token-renewer/main.go @@ -13,28 +13,33 @@ import ( "syscall" "github.com/pkg/errors" - "github.com/postfinance/vault-kubernetes/pkg/auth" + "github.com/postfinance/vault/k8s" ) func main() { - c, err := auth.NewConfigFromEnvironment() + c, err := k8s.NewFromEnvironment() if err != nil { - log.Fatal(errors.Wrap(err, "failed to get config")) + log.Fatal(err) } // get token and re-authenticate if enabled token, err := c.GetToken() if err != nil { - log.Fatal(errors.Wrap(err, "failed to get token")) + log.Fatal(err) + } + // store token in case re-authentication was done + if err := c.StoreToken(token); err != nil { + log.Fatal(err) } renewer, err := c.NewRenewer(token) if err != nil { - log.Fatal(errors.Wrap(err, "failed to get token renewer")) + log.Fatal(err) } log.Println("start renewer loop") go renewer.Renew() + exit := make(chan os.Signal, 1) signal.Notify(exit, os.Interrupt, syscall.SIGTERM) for { @@ -47,9 +52,10 @@ func main() { case <-renewer.RenewCh(): log.Println("token renewed") case <-exit: - renewer.Stop() log.Println("signal received - stop execution") - os.Exit(0) + renewer.Stop() + goto Exit } } +Exit: } diff --git a/demo/k8s/authenticator/deployment.yaml b/demo/k8s/authenticator/deployment.yaml index 27d94cd..cbb56c6 100644 --- a/demo/k8s/authenticator/deployment.yaml +++ b/demo/k8s/authenticator/deployment.yaml @@ -32,8 +32,8 @@ spec: value: ${VAULT_ADDR} - name: VAULT_SKIP_VERIFY value: "true" - - name: VAULT_K8S_MOUNT_PATH - value: ${VAULT_K8S_MOUNT_PATH} + - name: VAULT_AUTH_MOUNT_PATH + value: ${VAULT_AUTH_MOUNT_PATH} - name: VAULT_ROLE value: ${VAULT_ROLE} - name: VAULT_TOKEN_PATH diff --git a/demo/k8s/synchronizer/deployment.yaml b/demo/k8s/synchronizer/deployment.yaml index 8a08130..6bcb576 100644 --- a/demo/k8s/synchronizer/deployment.yaml +++ b/demo/k8s/synchronizer/deployment.yaml @@ -32,8 +32,8 @@ spec: value: ${VAULT_ADDR} - name: VAULT_SKIP_VERIFY value: "true" - - name: VAULT_K8S_MOUNT_PATH - value: ${VAULT_K8S_MOUNT_PATH} + - name: VAULT_AUTH_MOUNT_PATH + value: ${VAULT_AUTH_MOUNT_PATH} - name: VAULT_ROLE value: ${VAULT_ROLE} - name: VAULT_TOKEN_PATH diff --git a/demo/k8s/token-renewer/deployment.yaml b/demo/k8s/token-renewer/deployment.yaml index ee61017..ad73223 100644 --- a/demo/k8s/token-renewer/deployment.yaml +++ b/demo/k8s/token-renewer/deployment.yaml @@ -33,8 +33,8 @@ spec: value: ${VAULT_ADDR} - name: VAULT_SKIP_VERIFY value: "true" - - name: VAULT_K8S_MOUNT_PATH - value: ${VAULT_K8S_MOUNT_PATH} + - name: VAULT_AUTH_MOUNT_PATH + value: ${VAULT_AUTH_MOUNT_PATH} - name: VAULT_ROLE value: ${VAULT_ROLE} - name: VAULT_TOKEN_PATH @@ -51,8 +51,8 @@ spec: value: ${VAULT_ADDR} - name: VAULT_SKIP_VERIFY value: "true" - - name: VAULT_K8S_MOUNT_PATH - value: ${VAULT_K8S_MOUNT_PATH} + - name: VAULT_AUTH_MOUNT_PATH + value: ${VAULT_AUTH_MOUNT_PATH} - name: VAULT_TOKEN_PATH value: ${VAULT_TOKEN_PATH} - name: kuard diff --git a/demo/profile b/demo/profile index 2fa1aac..134e36c 100644 --- a/demo/profile +++ b/demo/profile @@ -10,7 +10,7 @@ export TOKEN_RENEWER_IMAGE="vault-kubernetes-token-renewer:x.y.z" export IMAGE="gcr.io/kuar-demo/kuard-amd64:1" export VAULT_ADDR="http://vault-dev-server.vault-dev.svc.cluster.local:8200" -export VAULT_K8S_MOUNT_PATH="auth/${CLUSTER}" +export VAULT_AUTH_MOUNT_PATH="${CLUSTER}" export VAULT_ROLE="${CLUSTER}-${NAMESPACE}-auth" export VAULT_TOKEN_DIR="/home/vault" export VAULT_TOKEN_PATH="${VAULT_TOKEN_DIR}/.vault-token" diff --git a/go.mod b/go.mod index 804694b..b5d2eea 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,108 @@ module github.com/postfinance/vault-kubernetes require ( - github.com/gogo/protobuf v1.1.1 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect - github.com/golang/protobuf v1.2.0 // indirect - github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect + contrib.go.opencensus.io/exporter/ocagent v0.4.7 // indirect + github.com/Azure/azure-sdk-for-go v26.3.1+incompatible // indirect + github.com/Azure/go-autorest v11.5.2+incompatible // indirect + github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/SAP/go-hdb v0.14.0 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190313125804-df99bc4cb58f // indirect + github.com/araddon/gou v0.0.0-20190110011759-c797efecbb61 // indirect + github.com/aws/aws-sdk-go v1.18.1 // indirect + github.com/boombuler/barcode v1.0.0 // indirect + github.com/briankassouf/jose v0.9.1 // indirect + github.com/centrify/cloud-golang-sdk v0.0.0-20190214225812-119110094d0f // indirect + github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 // indirect + github.com/coreos/etcd v3.3.12+incompatible // indirect + github.com/coreos/go-oidc v2.0.0+incompatible // indirect + github.com/coreos/go-semver v0.2.0 // indirect + github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 // indirect + github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect + github.com/dancannon/gorethink v4.0.0+incompatible // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20190313032549-041949b8d268 // indirect + github.com/dimchansky/utfbom v1.1.0 // indirect + github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 // indirect + github.com/fullsailor/pkcs7 v0.0.0-20180613152042-8306686428a5 // indirect + github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7 // indirect + github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92 // indirect + github.com/garyburd/redigo v1.6.0 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-ldap/ldap v3.0.2+incompatible // indirect + github.com/go-sql-driver/mysql v1.4.1 // indirect + github.com/go-stomp/stomp v2.0.2+incompatible // indirect + github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 // indirect + github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect + github.com/googleapis/gax-go v2.0.2+incompatible // indirect github.com/googleapis/gnostic v0.2.0 // indirect + github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 // indirect + github.com/gorilla/websocket v1.4.0 // indirect github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/hashicorp/consul v1.4.3 // indirect + github.com/hashicorp/go-gcp-common v0.0.0-20180425173946-763e39302965 // indirect + github.com/hashicorp/go-hclog v0.8.0 // indirect + github.com/hashicorp/go-memdb v0.0.0-20190306140544-eea0b16292ad // indirect + github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104 // indirect + github.com/hashicorp/go-version v1.1.0 // indirect + github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/hashicorp/nomad v0.8.7 // indirect + github.com/hashicorp/serf v0.8.2 // indirect github.com/hashicorp/vault v1.0.3 + github.com/hashicorp/vault-plugin-auth-alicloud v0.0.0-20190311155555-98628998247d // indirect + github.com/hashicorp/vault-plugin-auth-azure v0.0.0-20190201222632-0af1d040b5b3 // indirect + github.com/hashicorp/vault-plugin-auth-centrify v0.0.0-20180816201131-66b0a34a58bf // indirect + github.com/hashicorp/vault-plugin-auth-gcp v0.0.0-20190201215414-7d4c2101e7d0 // indirect + github.com/hashicorp/vault-plugin-auth-jwt v0.0.0-20190305162423-b9b5cad5b980 // indirect + github.com/hashicorp/vault-plugin-auth-kubernetes v0.0.0-20190201222209-db96aa4ab438 // indirect + github.com/hashicorp/vault-plugin-secrets-ad v0.0.0-20190131222416-4796d9980125 // indirect + github.com/hashicorp/vault-plugin-secrets-alicloud v0.0.0-20190131211812-b0abe36195cb // indirect + github.com/hashicorp/vault-plugin-secrets-azure v0.0.0-20181207232500-0087bdef705a // indirect + github.com/hashicorp/vault-plugin-secrets-gcp v0.0.0-20190311200649-621231cb86fe // indirect + github.com/hashicorp/vault-plugin-secrets-gcpkms v0.0.0-20190116164938-d6b25b0b4a39 // indirect + github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190227052836-76a82948fe5b // indirect + github.com/influxdata/influxdb v1.7.4 // indirect + github.com/influxdata/platform v0.0.0-20190117200541-d500d3cf5589 // indirect + github.com/jeffchao/backoff v0.0.0-20140404060208-9d7fd7aa17f2 // indirect + github.com/jefferai/jsonx v1.0.0 // indirect + github.com/jonboulle/clockwork v0.1.0 // indirect github.com/json-iterator/go v1.1.5 // indirect - github.com/magefile/mage v1.7.1 + github.com/keybase/go-crypto v0.0.0-20190312101036-b475f2ecc1fe // indirect + github.com/mattbaird/elastigo v0.0.0-20170123220020-2fe47fd29e4b // indirect + github.com/michaelklishin/rabbit-hole v1.5.0 // indirect + github.com/mitchellh/hashstructure v1.0.0 // indirect + github.com/mitchellh/pointerstructure v0.0.0-20170205204203-f2329fcfa9e2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/onsi/ginkgo v1.8.0 // indirect + github.com/onsi/gomega v1.5.0 // indirect + github.com/ory-am/common v0.4.0 // indirect + github.com/pborman/uuid v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.8.1 - github.com/postfinance/mage/git v0.0.0-20181010062859-d7e921ceca60 - github.com/postfinance/vault/kv v0.0.1 // indirect - golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.2.1 // indirect - k8s.io/api v0.0.0-20181121071145-b7bd5f2d334c // indirect - k8s.io/apimachinery v0.0.0-20181121071008-d4f83ca2e260 // indirect - k8s.io/client-go v9.0.0+incompatible // indirect + github.com/postfinance/vault/k8s v0.0.2 // indirect + github.com/postfinance/vault/kv v0.0.1 + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/pquerna/otp v1.1.0 // indirect + github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec // indirect + github.com/soheilhy/cmux v0.1.4 // indirect + github.com/streadway/amqp v0.0.0-20190312223743-14f78b41ce6d // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect + github.com/ugorji/go/codec v0.0.0-20190309163734-c4a1c341dc93 // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect + google.golang.org/api v0.1.0 // indirect + google.golang.org/appengine v1.4.0 // indirect + google.golang.org/grpc v1.19.0 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/gorethink/gorethink.v4 v4.1.0 // indirect + gopkg.in/ory-am/dockertest.v2 v2.2.3 // indirect + gopkg.in/square/go-jose.v2 v2.3.0 // indirect + k8s.io/api v0.0.0-20181121071145-b7bd5f2d334c + k8s.io/apimachinery v0.0.0-20181121071008-d4f83ca2e260 + k8s.io/client-go v9.0.0+incompatible k8s.io/klog v0.1.0 // indirect + layeh.com/radius v0.0.0-20190118135028-0f678f039617 // indirect sigs.k8s.io/yaml v1.1.0 // indirect ) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go deleted file mode 100644 index 42a928a..0000000 --- a/pkg/auth/auth.go +++ /dev/null @@ -1,161 +0,0 @@ -// Package auth provides authentication with Vault on Kubernetes -// -// Authentication is done with the Kubernetes Auth Method by Vault. -// -// See also ``Kubernetes Auth Method`` from the Vault documentation -// https://www.vaultproject.io/docs/auth/kubernetes.html -package auth - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "strconv" - "strings" - - "github.com/hashicorp/vault/api" - "github.com/pkg/errors" -) - -// Config represents the configuration to get a valid Vault token -type Config struct { - VaultRole string - VaultTokenPath string - VaultReAuth bool - VaultTTL int - VaultK8SMountPath string - ServiceAccountPath string - AllowFail bool - vault *api.Client -} - -// NewConfigFromEnvironment returns a initialized Config for authentication -func NewConfigFromEnvironment() (*Config, error) { - c := &Config{} - c.VaultRole = os.Getenv("VAULT_ROLE") - c.VaultTokenPath = os.Getenv("VAULT_TOKEN_PATH") - if c.VaultTokenPath == "" { - return nil, fmt.Errorf("missing VAULT_TOKEN_PATH") - } - if s := os.Getenv("VAULT_REAUTH"); s != "" { - b, err := strconv.ParseBool(s) - if err != nil { - return nil, errors.Wrap(err, "1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False are valid values for ALLOW_FAIL") - } - c.VaultReAuth = b - } - if s := os.Getenv("VAULT_TTL"); s != "" { - i, err := strconv.Atoi(s) - if err != nil { - return nil, errors.Wrap(err, "%s is not a valid integer for VAULT_TTL") - } - c.VaultTTL = i - } - c.VaultK8SMountPath = os.Getenv("VAULT_K8S_MOUNT_PATH") - if c.VaultK8SMountPath == "" { - c.VaultK8SMountPath = "auth/kubernetes" - } - c.VaultK8SMountPath = strings.Trim(c.VaultK8SMountPath, "/") - c.ServiceAccountPath = os.Getenv("SERVICE_ACCOUNT_PATH") - if c.ServiceAccountPath == "" { - c.ServiceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" - } - if s := os.Getenv("ALLOW_FAIL"); s != "" { - b, err := strconv.ParseBool(s) - if err != nil { - return nil, errors.Wrap(err, "1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False are valid values for ALLOW_FAIL") - } - c.AllowFail = b - } - // create vault client - vaultConfig := api.DefaultConfig() - if err := vaultConfig.ReadEnvironment(); err != nil { - return nil, errors.Wrap(err, "failed to read environment for vault") - } - var err error - c.vault, err = api.NewClient(vaultConfig) - if err != nil { - return nil, errors.Wrap(err, "failed to create vault client") - } - return c, nil -} - -// Authenticate to vault -func (c *Config) Authenticate() (string, error) { - var empty string - // read jwt of serviceaccount - content, err := ioutil.ReadFile(c.ServiceAccountPath) - if err != nil { - return empty, errors.Wrap(err, "failed to read jwt token") - } - jwt := string(bytes.TrimSpace(content)) - - // authenticate - data := make(map[string]interface{}) - data["role"] = c.VaultRole - data["jwt"] = jwt - s, err := c.vault.Logical().Write(path.Join(c.VaultK8SMountPath, "login"), data) - if err != nil { - return empty, errors.Wrapf(err, "login failed with role from environment variable VAULT_ROLE: %q", c.VaultRole) - } - if len(s.Warnings) > 0 { - return empty, fmt.Errorf("login failed with: %s", strings.Join(s.Warnings, " - ")) - } - return s.Auth.ClientToken, nil -} - -// LoadToken from VaultTokenPath -func (c *Config) LoadToken() (string, error) { - content, err := ioutil.ReadFile(c.VaultTokenPath) - if err != nil { - return "", errors.Wrap(err, "failed to load token") - } - return string(content), nil -} - -// StoreToken in VaultTokenPath -func (c *Config) StoreToken(token string) error { - if err := ioutil.WriteFile(c.VaultTokenPath, []byte(token), 0644); err != nil { - return errors.Wrap(err, "failed to save token") - } - return nil -} - -// GetToken tries to load the vault token from VaultTokenPath -// if token is not available, invalid or not renewable -// and VaultReAuth is true, try to re-authenticate -func (c *Config) GetToken() (string, error) { - var empty string - token, err := c.LoadToken() - if err != nil { - if c.VaultReAuth { - return c.Authenticate() - } - return empty, errors.Wrapf(err, "failed to load token form: %s", c.VaultTokenPath) - } - c.vault.SetToken(token) - if _, err = c.vault.Auth().Token().RenewSelf(c.VaultTTL); err != nil { - if c.VaultReAuth { - return c.Authenticate() - } - return empty, errors.Wrap(err, "failed to renew token") - } - return token, nil -} - -// NewRenewer returns a *api.Renewer to renew the vault token regularly -func (c *Config) NewRenewer(token string) (*api.Renewer, error) { - c.vault.SetToken(token) - // renew the token to get a secret usable for renewer - secret, err := c.vault.Auth().Token().RenewSelf(c.VaultTTL) - if err != nil { - return nil, errors.Wrap(err, "failed to renew-self token") - } - renewer, err := c.vault.NewRenewer(&api.RenewerInput{Secret: secret}) - if err != nil { - return nil, errors.Wrap(err, "failed to get token renewer") - } - return renewer, nil -}