github.com/jaylevin/jenkins-library@v1.230.4/cmd/vaultRotateSecretId.go (about) 1 package cmd 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/hashicorp/vault/api" 10 11 "github.com/SAP/jenkins-library/pkg/ado" 12 "github.com/SAP/jenkins-library/pkg/jenkins" 13 "github.com/SAP/jenkins-library/pkg/vault" 14 15 "github.com/SAP/jenkins-library/pkg/log" 16 "github.com/SAP/jenkins-library/pkg/telemetry" 17 ) 18 19 type vaultRotateSecretIDUtils interface { 20 GetAppRoleSecretIDTtl(secretID, roleName string) (time.Duration, error) 21 GetAppRoleName() (string, error) 22 GenerateNewAppRoleSecret(secretID string, roleName string) (string, error) 23 UpdateSecretInStore(config *vaultRotateSecretIdOptions, secretID string) error 24 GetConfig() *vaultRotateSecretIdOptions 25 } 26 27 type vaultRotateSecretIDUtilsBundle struct { 28 *vault.Client 29 config *vaultRotateSecretIdOptions 30 updateFunc func(config *vaultRotateSecretIdOptions, secretID string) error 31 } 32 33 func (v vaultRotateSecretIDUtilsBundle) GetConfig() *vaultRotateSecretIdOptions { 34 return v.config 35 } 36 37 func (v vaultRotateSecretIDUtilsBundle) UpdateSecretInStore(config *vaultRotateSecretIdOptions, secretID string) error { 38 return v.updateFunc(config, secretID) 39 } 40 41 func vaultRotateSecretId(config vaultRotateSecretIdOptions, telemetryData *telemetry.CustomData) { 42 43 vaultConfig := &vault.Config{ 44 Config: &api.Config{ 45 Address: config.VaultServerURL, 46 }, 47 Namespace: config.VaultNamespace, 48 } 49 client, err := vault.NewClientWithAppRole(vaultConfig, GeneralConfig.VaultRoleID, GeneralConfig.VaultRoleSecretID) 50 if err != nil { 51 log.Entry().WithError(err).Fatal("could not create Vault client") 52 } 53 defer client.MustRevokeToken() 54 55 utils := vaultRotateSecretIDUtilsBundle{ 56 Client: &client, 57 config: &config, 58 updateFunc: writeVaultSecretIDToStore, 59 } 60 61 err = runVaultRotateSecretID(utils) 62 if err != nil { 63 log.Entry().WithError(err).Fatal("step execution failed") 64 } 65 } 66 67 func runVaultRotateSecretID(utils vaultRotateSecretIDUtils) error { 68 config := utils.GetConfig() 69 70 roleName, err := utils.GetAppRoleName() 71 if err != nil { 72 log.Entry().WithError(err).Warn("Could not fetch Vault AppRole role name from Vault. Secret ID rotation failed!") 73 return nil 74 } 75 76 ttl, err := utils.GetAppRoleSecretIDTtl(GeneralConfig.VaultRoleSecretID, roleName) 77 78 if err != nil { 79 log.Entry().WithError(err).Warn("Could not fetch secret ID TTL. Secret ID rotation failed!") 80 return nil 81 } 82 83 log.Entry().Debugf("Your secret ID is about to expire in %.0f", ttl.Round(time.Hour*24).Hours()/24) 84 85 if ttl > time.Duration(config.DaysBeforeExpiry)*24*time.Hour { 86 return nil 87 } 88 89 newSecretID, err := utils.GenerateNewAppRoleSecret(GeneralConfig.VaultRoleSecretID, roleName) 90 91 if err != nil || newSecretID == "" { 92 log.Entry().WithError(err).Warn("Generating a new secret ID failed. Secret ID rotation faield!") 93 return nil 94 } 95 96 if err = utils.UpdateSecretInStore(config, newSecretID); err != nil { 97 log.Entry().WithError(err).Warnf("Could not write secret back to secret store %s", config.SecretStore) 98 return err 99 } 100 log.Entry().Infof("Secret has been successfully updated in secret store %s", config.SecretStore) 101 return nil 102 103 } 104 105 func writeVaultSecretIDToStore(config *vaultRotateSecretIdOptions, secretID string) error { 106 switch config.SecretStore { 107 case "jenkins": 108 ctx := context.Background() 109 instance, err := jenkins.Instance(ctx, &http.Client{}, config.JenkinsURL, config.JenkinsUsername, config.JenkinsToken) 110 if err != nil { 111 log.Entry().Warn("Could not write secret ID back to Jenkins") 112 return err 113 } 114 credManager := jenkins.NewCredentialsManager(instance) 115 credential := jenkins.StringCredentials{ID: config.VaultAppRoleSecretTokenCredentialsID, Secret: secretID} 116 return jenkins.UpdateCredential(ctx, credManager, config.JenkinsCredentialDomain, credential) 117 case "ado": 118 adoBuildClient, err := ado.NewBuildClient(config.AdoOrganization, config.AdoPersonalAccessToken, config.AdoProject, config.AdoPipelineID) 119 if err != nil { 120 log.Entry().Warn("Could not write secret ID back to Azure DevOps") 121 return err 122 } 123 variables := []ado.Variable{ 124 { 125 Name: config.VaultAppRoleSecretTokenCredentialsID, 126 Value: secretID, 127 IsSecret: true, 128 }, 129 } 130 if err := adoBuildClient.UpdateVariables(variables); err != nil { 131 log.Entry().Warn("Could not write secret ID back to Azure DevOps") 132 return err 133 } 134 default: 135 return fmt.Errorf("error: invalid secret store: %s", config.SecretStore) 136 } 137 return nil 138 }