From 0abdc46f717db8c621e3e8675c07ac250c263b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Schneider?= <56670304+soerenschneider@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:22:37 +0200 Subject: [PATCH] feat: add kubernetes vault auth --- cmd/main.go | 4 ++ go.mod | 5 ++- go.sum | 5 ++- internal/config/common.go | 13 +++--- pkg/certstorage/vault/auth_kubernetes.go | 55 ++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 pkg/certstorage/vault/auth_kubernetes.go diff --git a/cmd/main.go b/cmd/main.go index 32cdd12..dcfaa7f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -81,6 +81,10 @@ func buildVaultAuth(conf config.AcmeVaultServerConfig) (vault.Auth, error) { FromString: conf.SecretId, } return vault.NewApproleAuth(conf.RoleId, secretId) + case "k8s": + return vault.NewVaultKubernetesAuth(conf.K8sRoleId, conf.K8sMountPath) + case "implicit": + return vault.NewImplicitAuth() default: return nil, fmt.Errorf("no valid auth method: %s", conf.AuthMethod) } diff --git a/go.mod b/go.mod index 2f121e8..eb1783f 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,14 @@ require ( github.com/blushft/go-diagrams v0.0.0-20201006005127-c78c821223d9 github.com/go-acme/lego/v4 v4.13.3 github.com/go-playground/validator/v10 v10.15.0 - github.com/hashicorp/vault/api v1.9.2 + github.com/hashicorp/vault/api v1.10.0 github.com/hashicorp/vault/api/auth/approle v0.4.1 + github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 github.com/prometheus/client_golang v1.16.0 github.com/rs/zerolog v1.30.0 github.com/stretchr/testify v1.8.4 go.uber.org/multierr v1.11.0 + golang.org/x/net v0.11.0 ) require ( @@ -57,7 +59,6 @@ require ( github.com/stretchr/objx v0.5.0 // indirect golang.org/x/crypto v0.10.0 // indirect golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.11.0 // indirect golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index ca95ade..147d76a 100644 --- a/go.sum +++ b/go.sum @@ -91,10 +91,13 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= +github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ= +github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/hashicorp/vault/api/auth/approle v0.4.1 h1:NElpX7DZ2uaLGwY+leWXHUqw9tepsYkcHvIowgIZteI= github.com/hashicorp/vault/api/auth/approle v0.4.1/go.mod h1:rlI2VbmuHkptRun7DngpxOSvRC+JuITqAs/Z09pUucU= +github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhPcH4paDFKcu7AjEXi94rI= +github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608= github.com/iancoleman/strcase v0.1.1/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= diff --git a/internal/config/common.go b/internal/config/common.go index ce6ae92..7129f33 100644 --- a/internal/config/common.go +++ b/internal/config/common.go @@ -12,12 +12,15 @@ var validate = validator.New() type VaultConfig struct { Addr string `json:"vaultAddr" validate:"required,http_url"` - AuthMethod string `json:"vaultAuthMethod" validate:"required,oneof=token approle"` - Token string `json:"vaultToken" validate:"required_if=RoleId ''"` + AuthMethod string `json:"vaultAuthMethod" validate:"required,oneof=token approle k8s"` + Token string `json:"vaultToken" validate:"required_if=AuthMethod 'token'"` - RoleId string `json:"vaultRoleId" validate:"required_if=Token ''"` - SecretId string `json:"vaultSecretId" validate:"excluded_unless=SecretIdFile '',required_if=SecretIdFile '' Token ''"` - SecretIdFile string `json:"vaultSecretIdFile" validate:"excluded_unless=SecretId '',required_if=SecretId '' Token ''"` + RoleId string `json:"vaultRoleId" validate:"required_if=AuthMethod 'approle'"` + SecretId string `json:"vaultSecretId" validate:"excluded_unless=SecretIdFile '',required_if=SecretIdFile '' AuthMethod 'approle'"` + SecretIdFile string `json:"vaultSecretIdFile" validate:"excluded_unless=SecretId '',required_if=SecretId '' AuthMethod 'approle'"` + + K8sRoleId string `json:"vaultK8sRoleId" validate:"required_if=AuthMethod 'k8s'"` + K8sMountPath string `json:"vaultK8sMountPath"` PathPrefix string `json:"vaultPathPrefix" validate:"required,startsnotwith=/,startsnotwith=/secret,endsnotwith=/,ne=acmevault"` DomainPathFormat string `json:"domainPathFormat" validate:"omitempty,containsrune=%"` diff --git a/pkg/certstorage/vault/auth_kubernetes.go b/pkg/certstorage/vault/auth_kubernetes.go new file mode 100644 index 0000000..d48c362 --- /dev/null +++ b/pkg/certstorage/vault/auth_kubernetes.go @@ -0,0 +1,55 @@ +package vault + +import ( + "fmt" + + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/api/auth/kubernetes" + "golang.org/x/net/context" +) + +const ( + defaultServiceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" // #nosec G101 + defaultMount = "kubernetes" +) + +type KubernetesAuth struct { + role string + serviceAccountTokenFile string + mount string +} + +func NewVaultKubernetesAuth(role string, mountPath string) (*KubernetesAuth, error) { + return &KubernetesAuth{ + role: role, + mount: mountPath, + serviceAccountTokenFile: defaultServiceAccountTokenFile, + }, nil +} + +func (t *KubernetesAuth) Logout(ctx context.Context, client *api.Client) error { + path := "auth/token/revoke-self" + _, err := client.Logical().Write(path, map[string]interface{}{}) + return err +} + +func (t *KubernetesAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + k8sAuth, err := kubernetes.NewKubernetesAuth( + t.role, + kubernetes.WithServiceAccountTokenPath(t.serviceAccountTokenFile), + kubernetes.WithMountPath(t.mount)) + + if err != nil { + return nil, fmt.Errorf("unable to initialize Kubernetes kubernetes method: %w", err) + } + + authInfo, err := client.Auth().Login(context.TODO(), k8sAuth) + if err != nil { + return nil, fmt.Errorf("unable to log in with Kubernetes kubernetes: %w", err) + } + if authInfo == nil { + return nil, fmt.Errorf("no kubernetes info was returned after login") + } + + return authInfo, nil +}