github.com/jenkins-x/jx/v2@v2.1.155/pkg/vault/vault.go (about)

     1  package vault
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/jenkins-x/jx/v2/pkg/errorutil"
     7  	"github.com/jenkins-x/jx/v2/pkg/util"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  const (
    12  	// SystemVaultName stores the name of the Vault instance created and managed by Jenkins X unless an external Vault
    13  	// instance is used in which case this name will be empty.
    14  	SystemVaultName = "systemVaultName"
    15  
    16  	// URL stores the URL of the external Vault instance if no system internal Vault instance is used.
    17  	URL = "vaultURL"
    18  
    19  	// ServiceAccount stores the name of the service account used to connect to Vault.
    20  	ServiceAccount = "vaultServiceAccount"
    21  
    22  	// Namespace stores the service account namespace which is allowed to connect to Vault.
    23  	Namespace = "vaultNamespace"
    24  
    25  	// SecretEngineMountPoint defines the Vault mount point for the KV secret engine.
    26  	SecretEngineMountPoint = "vaultSecretEngineMountPoint"
    27  
    28  	// KubernetesAuthPath defines the path under which the Kubernetes auth method is configured.
    29  	KubernetesAuthPath = "vaultKubernetesAuthPath"
    30  
    31  	// DefaultKVEngineMountPoint default mount point for the KV V2 engine
    32  	DefaultKVEngineMountPoint = "secret"
    33  
    34  	// DefaultKubernetesAuthPath is the default Kubernetes auth path
    35  	DefaultKubernetesAuthPath = "kubernetes"
    36  
    37  	// defaultServiceAccountSuffix is the default suffix appended to the Vault name if no service account name is specified.
    38  	defaultServiceAccountSuffix = "-vt"
    39  )
    40  
    41  // Vault stores the required information to connect and authenticate against a Vault instance.
    42  type Vault struct {
    43  	// Name defines the name of the Vault instance, provided we are dealing with an Jenkins X managed Vault instance
    44  	Name string
    45  
    46  	// ServiceAccountName is the name of the service account allowed to authenticate against Vault.
    47  	ServiceAccountName string
    48  
    49  	// Namespace of the service account authorized to authenticate against Vault.
    50  	Namespace string
    51  
    52  	// URL specifies the Vault URL to connect to.
    53  	URL string
    54  
    55  	// SecretEngineMountPoint is the mount point to be used for writing data into the KV engine.
    56  	SecretEngineMountPoint string
    57  
    58  	// KubernetesAuthPath is the path under which the Vault Kubernetes auth method is configured.
    59  	KubernetesAuthPath string
    60  }
    61  
    62  // NewExternalVault creates an external Vault instance configuration from the provided parameters.
    63  func NewExternalVault(url string, serviceAccountName string, namespace string, secretEngineMountPoint string, kubernetesAuthPath string) (Vault, error) {
    64  	if url == "" {
    65  		return Vault{}, errors.New("URL cannot be empty for an external Vault configuration")
    66  	}
    67  
    68  	data := map[string]string{}
    69  
    70  	data[SystemVaultName] = ""
    71  	data[URL] = url
    72  	data[ServiceAccount] = serviceAccountName
    73  	data[Namespace] = namespace
    74  	data[SecretEngineMountPoint] = secretEngineMountPoint
    75  	data[KubernetesAuthPath] = kubernetesAuthPath
    76  
    77  	return FromMap(data, namespace)
    78  }
    79  
    80  // NewInternalVault creates an internal Vault instance configuration from the provided parameters.
    81  func NewInternalVault(name string, serviceAccountName string, namespace string) (Vault, error) {
    82  	if name == "" {
    83  		return Vault{}, errors.New("name cannot be empty for an internal Vault configuration")
    84  	}
    85  
    86  	if serviceAccountName == "" {
    87  		serviceAccountName = fmt.Sprintf("%s-%s", name, defaultServiceAccountSuffix)
    88  	}
    89  
    90  	data := map[string]string{}
    91  	data[SystemVaultName] = name
    92  	data[ServiceAccount] = serviceAccountName
    93  	data[Namespace] = namespace
    94  
    95  	return FromMap(data, namespace)
    96  }
    97  
    98  // FromMap reads the configuration of a Vault instance from a map.
    99  // defaultNamespace is used when there is no namespace value provided in the map (for backwards compatibility reasons).
   100  func FromMap(data map[string]string, defaultNamespace string) (Vault, error) {
   101  	if data[SystemVaultName] != "" && data[URL] != "" {
   102  		return Vault{}, errors.New("systemVaultName and URL cannot be specified together")
   103  	}
   104  
   105  	secretEngineMountPoint := data[SecretEngineMountPoint]
   106  	if secretEngineMountPoint == "" {
   107  		secretEngineMountPoint = DefaultKVEngineMountPoint
   108  	}
   109  
   110  	kubernetesAuthPath := data[KubernetesAuthPath]
   111  	if kubernetesAuthPath == "" {
   112  		kubernetesAuthPath = DefaultKubernetesAuthPath
   113  	}
   114  
   115  	namespace := data[Namespace]
   116  	if namespace == "" {
   117  		namespace = defaultNamespace
   118  	}
   119  
   120  	vault := Vault{
   121  		Name:                   data[SystemVaultName],
   122  		URL:                    data[URL],
   123  		ServiceAccountName:     data[ServiceAccount],
   124  		Namespace:              namespace,
   125  		SecretEngineMountPoint: secretEngineMountPoint,
   126  		KubernetesAuthPath:     kubernetesAuthPath,
   127  	}
   128  
   129  	var err error
   130  	external := vault.IsExternal()
   131  	if external {
   132  		err = vault.validateExternalConfiguration()
   133  	} else {
   134  		err = vault.validateInternalConfiguration()
   135  	}
   136  
   137  	return vault, err
   138  }
   139  
   140  // IsExternal returns true if the Vault instance represents an externally managed Vault instance or one managed by Jenkins X.
   141  func (v *Vault) IsExternal() bool {
   142  	if v.URL == "" {
   143  		return false
   144  	}
   145  	return true
   146  }
   147  
   148  // ToMap writes the configuration of this Vault instance to a map
   149  func (v *Vault) ToMap() map[string]string {
   150  	data := map[string]string{}
   151  
   152  	data[SystemVaultName] = v.Name
   153  	data[URL] = v.URL
   154  	data[ServiceAccount] = v.ServiceAccountName
   155  	data[Namespace] = v.Namespace
   156  	data[SecretEngineMountPoint] = v.SecretEngineMountPoint
   157  	data[KubernetesAuthPath] = v.KubernetesAuthPath
   158  
   159  	return data
   160  }
   161  
   162  // validateExternalConfiguration validates the values of the Vault configuration for an external Vault setup.
   163  func (v *Vault) validateExternalConfiguration() error {
   164  	var validationErrors []error
   165  	var err error
   166  	if v.URL == "" {
   167  		return errors.New("URL cannot be empty")
   168  	}
   169  
   170  	if v.URL != "" && !util.IsValidUrl(v.URL) {
   171  		err = errors.Errorf("'%s' not a valid URL", v.URL)
   172  		validationErrors = append(validationErrors, err)
   173  	}
   174  
   175  	if v.ServiceAccountName == "" {
   176  		err = errors.New("external vault service account name cannot be empty")
   177  		validationErrors = append(validationErrors, err)
   178  	}
   179  
   180  	if v.Namespace == "" {
   181  		err = errors.New("external vault namespace cannot be empty")
   182  		validationErrors = append(validationErrors, err)
   183  	}
   184  
   185  	if v.SecretEngineMountPoint == "" {
   186  		err = errors.New("external vault secret engine mount point cannot be empty")
   187  		validationErrors = append(validationErrors, err)
   188  	}
   189  
   190  	if v.KubernetesAuthPath == "" {
   191  		err = errors.New("external vault Kubernetes auth path cannot be empty")
   192  		validationErrors = append(validationErrors, err)
   193  	}
   194  	return errorutil.CombineErrors(validationErrors...)
   195  }
   196  
   197  // validateInternalConfiguration validates the values of the Vault configuration  for an internal, Jenkins X managed, Vault setup.
   198  func (v *Vault) validateInternalConfiguration() error {
   199  	var validationErrors []error
   200  	var err error
   201  	if v.Name == "" {
   202  		err = errors.New("internal vault name cannot be empty")
   203  		validationErrors = append(validationErrors, err)
   204  	}
   205  
   206  	if v.ServiceAccountName == "" {
   207  		err = errors.New("internal vault service account name cannot be empty")
   208  		validationErrors = append(validationErrors, err)
   209  	}
   210  
   211  	if v.Namespace == "" {
   212  		err = errors.New("internal vault namespace cannot be empty")
   213  		validationErrors = append(validationErrors, err)
   214  	}
   215  
   216  	return errorutil.CombineErrors(validationErrors...)
   217  }