github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cloud/gke/vault/vault_backend.go (about)

     1  package vault
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/olli-ai/jx/v2/pkg/kube"
     7  
     8  	"github.com/jenkins-x/jx-logging/pkg/log"
     9  	"github.com/olli-ai/jx/v2/pkg/cloud/gke"
    10  	"github.com/olli-ai/jx/v2/pkg/util"
    11  	"github.com/pkg/errors"
    12  	"k8s.io/client-go/kubernetes"
    13  )
    14  
    15  const (
    16  	gkeServiceAccountSecretKey = "service-account.json"
    17  	//DefaultVaultAbbreviation is vault service accounts suffix
    18  	DefaultVaultAbbreviation = "vt"
    19  )
    20  
    21  var (
    22  	ServiceAccountRoles = []string{"roles/storage.objectAdmin",
    23  		"roles/cloudkms.admin",
    24  		"roles/cloudkms.cryptoKeyEncrypterDecrypter",
    25  	}
    26  )
    27  
    28  // KmsConfig keeps the configuration for Google KMS service
    29  type KmsConfig struct {
    30  	Keyring  string
    31  	Key      string
    32  	Location string
    33  	project  string
    34  }
    35  
    36  // This is a loose collection of methods needed to set up a vault in GKE.
    37  // If they are generic enough and needed elsewhere, we can move them up one level to more generic GCP methods.
    38  
    39  // CreateKmsConfig creates a KMS config for the GKE Vault
    40  func CreateKmsConfig(gcloud gke.GClouder, vaultName, keyringName string, keyName string, projectID string) (*KmsConfig, error) {
    41  	if keyringName == "" {
    42  		keyringName = gke.KeyringName(vaultName)
    43  	}
    44  	config := &KmsConfig{
    45  		Keyring:  keyringName,
    46  		Key:      keyName,
    47  		Location: gke.KmsLocation,
    48  		project:  projectID,
    49  	}
    50  
    51  	err := gcloud.CreateKmsKeyring(config.Keyring, config.project)
    52  	if err != nil {
    53  		return nil, errors.Wrapf(err, "creating kms keyring '%s'", config.Keyring)
    54  	}
    55  
    56  	if config.Key == "" {
    57  		config.Key = gke.KeyName(vaultName)
    58  	}
    59  
    60  	err = gcloud.CreateKmsKey(config.Key, config.Keyring, config.project)
    61  	if err != nil {
    62  		return nil, errors.Wrapf(err, "creating the kms key '%s'", config.Key)
    63  	}
    64  	return config, nil
    65  }
    66  
    67  // CreateGCPServiceAccount creates a service account in GCP for the vault service
    68  func CreateVaultGCPServiceAccount(gcloud gke.GClouder, kubeClient kubernetes.Interface, vaultName, namespace, clusterName, projectID string) (string, error) {
    69  
    70  	gcpServiceAccountSecretName, error := gcloud.CreateGCPServiceAccount(kubeClient, vaultName, DefaultVaultAbbreviation, namespace, clusterName, projectID, ServiceAccountRoles, gkeServiceAccountSecretKey)
    71  
    72  	if error != nil {
    73  		return "", errors.Wrap(error, "creating the Vault GCP Service Account")
    74  	}
    75  	return gcpServiceAccountSecretName, nil
    76  }
    77  
    78  // CreateBucket Creates a bucket in GKE to store the backend (encrypted) data for vault
    79  func CreateBucket(gcloud gke.GClouder, vaultName, bucketName string, projectID, zone string, recreate bool, batchMode bool, handles util.IOFileHandles) (string, error) {
    80  	if bucketName == "" {
    81  		bucketName = gke.BucketName(vaultName)
    82  	}
    83  	exists, err := gcloud.BucketExists(projectID, bucketName)
    84  	if err != nil {
    85  		return "", errors.Wrap(err, "checking if Vault GCS bucket exists")
    86  	}
    87  	if exists {
    88  		if !recreate {
    89  			return bucketName, nil
    90  		}
    91  		if batchMode {
    92  			log.Logger().Warnf("We are deleting the Vault bucket %s so that Vault will install cleanly", bucketName)
    93  		} else {
    94  			if answer, err := util.Confirm(fmt.Sprintf("We are about to delete bucket %q, so we can install a clean Vault. Are you sure: ", bucketName),
    95  				true, "We recommend you delete the Vault bucket on install to ensure Vault starts up reliably", handles); !answer {
    96  				return bucketName, err
    97  			}
    98  		}
    99  		err = gcloud.DeleteAllObjectsInBucket(bucketName)
   100  		if err != nil {
   101  			return "", errors.Wrapf(err, "failed to remove objects from GCS bucket %s", bucketName)
   102  		}
   103  	}
   104  
   105  	if zone == "" {
   106  		return "", errors.New("GKE zone must be provided in 'gke-zone' option")
   107  	}
   108  	region := gke.GetRegionFromZone(zone)
   109  	err = gcloud.CreateBucket(projectID, bucketName, region)
   110  	if err != nil {
   111  		return "", errors.Wrap(err, "creating Vault GCS bucket")
   112  	}
   113  	return bucketName, nil
   114  }
   115  
   116  // GetGoogleZone returns the Google zone as registered in the install values during the Jenkins X install process.
   117  // If the zone cannot be read the empty string is returned.
   118  func GetGoogleZone(kubeClient kubernetes.Interface, ns string) string {
   119  	data, err := kube.ReadInstallValues(kubeClient, ns)
   120  	if err != nil {
   121  		return ""
   122  	}
   123  	return data[kube.Zone]
   124  }
   125  
   126  // GetGoogleProjectID returns the Google project ID as registered in the install values during the Jenkins X install process.
   127  // If the project ID cannot be read the empty string is returned.
   128  func GetGoogleProjectID(kubeClient kubernetes.Interface, ns string) string {
   129  	data, err := kube.ReadInstallValues(kubeClient, ns)
   130  	if err != nil {
   131  		return ""
   132  	}
   133  	return data[kube.ProjectID]
   134  }