mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 10:37:56 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			137 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: MPL-2.0
 | |
| 
 | |
| package kubernetes
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/api"
 | |
| )
 | |
| 
 | |
| type KubernetesAuth struct {
 | |
| 	roleName            string
 | |
| 	mountPath           string
 | |
| 	serviceAccountToken string
 | |
| }
 | |
| 
 | |
| var _ api.AuthMethod = (*KubernetesAuth)(nil)
 | |
| 
 | |
| type LoginOption func(a *KubernetesAuth) error
 | |
| 
 | |
| const (
 | |
| 	defaultMountPath               = "kubernetes"
 | |
| 	defaultServiceAccountTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
 | |
| )
 | |
| 
 | |
| // NewKubernetesAuth creates a KubernetesAuth struct which can be passed to
 | |
| // the client.Auth().Login method to authenticate to Vault. The roleName
 | |
| // parameter should be the name of the role in Vault that was created with
 | |
| // this app's Kubernetes service account bound to it.
 | |
| //
 | |
| // The Kubernetes service account token JWT is retrieved from
 | |
| // /var/run/secrets/kubernetes.io/serviceaccount/token by default. To change this
 | |
| // path, pass the WithServiceAccountTokenPath option. To instead pass the
 | |
| // JWT directly as a string, or to read the value from an environment
 | |
| // variable, use WithServiceAccountToken and WithServiceAccountTokenEnv respectively.
 | |
| //
 | |
| // Supported options: WithMountPath, WithServiceAccountTokenPath, WithServiceAccountTokenEnv, WithServiceAccountToken
 | |
| func NewKubernetesAuth(roleName string, opts ...LoginOption) (*KubernetesAuth, error) {
 | |
| 	if roleName == "" {
 | |
| 		return nil, fmt.Errorf("no role name was provided")
 | |
| 	}
 | |
| 
 | |
| 	a := &KubernetesAuth{
 | |
| 		roleName:  roleName,
 | |
| 		mountPath: defaultMountPath,
 | |
| 	}
 | |
| 
 | |
| 	// Loop through each option
 | |
| 	for _, opt := range opts {
 | |
| 		// Call the option giving the instantiated
 | |
| 		// *KubernetesAuth as the argument
 | |
| 		err := opt(a)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error with login option: %w", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if a.serviceAccountToken == "" {
 | |
| 		token, err := readTokenFromFile(defaultServiceAccountTokenPath)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error reading service account token from default location: %w", err)
 | |
| 		}
 | |
| 		a.serviceAccountToken = token
 | |
| 	}
 | |
| 
 | |
| 	// return the modified auth struct instance
 | |
| 	return a, nil
 | |
| }
 | |
| 
 | |
| func (a *KubernetesAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
 | |
| 	if ctx == nil {
 | |
| 		ctx = context.Background()
 | |
| 	}
 | |
| 
 | |
| 	loginData := map[string]interface{}{
 | |
| 		"jwt":  a.serviceAccountToken,
 | |
| 		"role": a.roleName,
 | |
| 	}
 | |
| 
 | |
| 	path := fmt.Sprintf("auth/%s/login", a.mountPath)
 | |
| 	resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("unable to log in with Kubernetes auth: %w", err)
 | |
| 	}
 | |
| 	return resp, nil
 | |
| }
 | |
| 
 | |
| func WithMountPath(mountPath string) LoginOption {
 | |
| 	return func(a *KubernetesAuth) error {
 | |
| 		a.mountPath = mountPath
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithServiceAccountTokenPath allows you to specify a different path to
 | |
| // where your application's Kubernetes service account token is mounted,
 | |
| // instead of the default of /var/run/secrets/kubernetes.io/serviceaccount/token
 | |
| func WithServiceAccountTokenPath(pathToToken string) LoginOption {
 | |
| 	return func(a *KubernetesAuth) error {
 | |
| 		token, err := readTokenFromFile(pathToToken)
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("unable to read service account token from file: %w", err)
 | |
| 		}
 | |
| 		a.serviceAccountToken = token
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func WithServiceAccountToken(jwt string) LoginOption {
 | |
| 	return func(a *KubernetesAuth) error {
 | |
| 		a.serviceAccountToken = jwt
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func WithServiceAccountTokenEnv(envVar string) LoginOption {
 | |
| 	return func(a *KubernetesAuth) error {
 | |
| 		token := os.Getenv(envVar)
 | |
| 		if token == "" {
 | |
| 			return fmt.Errorf("service account token was specified with an environment variable with an empty value")
 | |
| 		}
 | |
| 		a.serviceAccountToken = token
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func readTokenFromFile(filepath string) (string, error) {
 | |
| 	jwt, err := os.ReadFile(filepath)
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("unable to read file containing service account token: %w", err)
 | |
| 	}
 | |
| 	return string(jwt), nil
 | |
| }
 | 
