mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
3744787348
* refactor github package and use builder pattern for client * switch to github package * some renamings * fix panic on uninitialized provider * fix according to review comments --------- Co-authored-by: Gulom Alimov <gulomjon.alimov@sap.com> Co-authored-by: Jordi van Liempt <35920075+jliempt@users.noreply.github.com>
167 lines
5.5 KiB
Go
167 lines
5.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/ado"
|
|
piperGithub "github.com/SAP/jenkins-library/pkg/github"
|
|
"github.com/SAP/jenkins-library/pkg/jenkins"
|
|
"github.com/SAP/jenkins-library/pkg/vault"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/log"
|
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
|
)
|
|
|
|
type vaultRotateSecretIDUtils interface {
|
|
GetAppRoleSecretIDTtl(secretID, roleName string) (time.Duration, error)
|
|
GetAppRoleName() (string, error)
|
|
GenerateNewAppRoleSecret(secretID string, roleName string) (string, error)
|
|
UpdateSecretInStore(config *vaultRotateSecretIdOptions, secretID string) error
|
|
GetConfig() *vaultRotateSecretIdOptions
|
|
}
|
|
|
|
type vaultRotateSecretIDUtilsBundle struct {
|
|
*vault.Client
|
|
config *vaultRotateSecretIdOptions
|
|
updateFunc func(config *vaultRotateSecretIdOptions, secretID string) error
|
|
}
|
|
|
|
func (v vaultRotateSecretIDUtilsBundle) GetConfig() *vaultRotateSecretIdOptions {
|
|
return v.config
|
|
}
|
|
|
|
func (v vaultRotateSecretIDUtilsBundle) UpdateSecretInStore(config *vaultRotateSecretIdOptions, secretID string) error {
|
|
return v.updateFunc(config, secretID)
|
|
}
|
|
|
|
func vaultRotateSecretId(config vaultRotateSecretIdOptions, telemetryData *telemetry.CustomData) {
|
|
|
|
vaultConfig := &vault.Config{
|
|
Config: &api.Config{
|
|
Address: config.VaultServerURL,
|
|
},
|
|
Namespace: config.VaultNamespace,
|
|
}
|
|
client, err := vault.NewClientWithAppRole(vaultConfig, GeneralConfig.VaultRoleID, GeneralConfig.VaultRoleSecretID)
|
|
if err != nil {
|
|
log.Entry().WithError(err).Fatal("could not create Vault client")
|
|
}
|
|
defer client.MustRevokeToken()
|
|
|
|
utils := vaultRotateSecretIDUtilsBundle{
|
|
Client: &client,
|
|
config: &config,
|
|
updateFunc: writeVaultSecretIDToStore,
|
|
}
|
|
|
|
err = runVaultRotateSecretID(utils)
|
|
if err != nil {
|
|
log.Entry().WithError(err).Fatal("step execution failed")
|
|
}
|
|
}
|
|
|
|
func runVaultRotateSecretID(utils vaultRotateSecretIDUtils) error {
|
|
config := utils.GetConfig()
|
|
|
|
roleName, err := utils.GetAppRoleName()
|
|
if err != nil {
|
|
log.Entry().WithError(err).Warn("Could not fetch Vault AppRole role name from Vault. Secret ID rotation failed!")
|
|
return nil
|
|
}
|
|
|
|
ttl, err := utils.GetAppRoleSecretIDTtl(GeneralConfig.VaultRoleSecretID, roleName)
|
|
|
|
if err != nil {
|
|
log.Entry().WithError(err).Warn("Could not fetch secret ID TTL. Secret ID rotation failed!")
|
|
return nil
|
|
}
|
|
|
|
log.Entry().Debugf("Your secret ID is about to expire in %.0f", ttl.Round(time.Hour*24).Hours()/24)
|
|
|
|
if ttl > time.Duration(config.DaysBeforeExpiry)*24*time.Hour {
|
|
return nil
|
|
}
|
|
|
|
newSecretID, err := utils.GenerateNewAppRoleSecret(GeneralConfig.VaultRoleSecretID, roleName)
|
|
|
|
if err != nil || newSecretID == "" {
|
|
log.Entry().WithError(err).Warn("Generating a new secret ID failed. Secret ID rotation faield!")
|
|
return nil
|
|
}
|
|
|
|
if err = utils.UpdateSecretInStore(config, newSecretID); err != nil {
|
|
log.Entry().WithError(err).Warnf("Could not write secret back to secret store %s", config.SecretStore)
|
|
return err
|
|
}
|
|
log.Entry().Infof("Secret has been successfully updated in secret store %s", config.SecretStore)
|
|
return nil
|
|
|
|
}
|
|
|
|
func writeVaultSecretIDToStore(config *vaultRotateSecretIdOptions, secretID string) error {
|
|
switch config.SecretStore {
|
|
case "jenkins":
|
|
ctx := context.Background()
|
|
instance, err := jenkins.Instance(ctx, &http.Client{}, config.JenkinsURL, config.JenkinsUsername, config.JenkinsToken)
|
|
if err != nil {
|
|
log.Entry().Warn("Could not write secret ID back to Jenkins")
|
|
return err
|
|
}
|
|
credManager := jenkins.NewCredentialsManager(instance)
|
|
credential := jenkins.StringCredentials{ID: config.VaultAppRoleSecretTokenCredentialsID, Secret: secretID}
|
|
return jenkins.UpdateCredential(ctx, credManager, config.JenkinsCredentialDomain, credential)
|
|
case "ado":
|
|
adoBuildClient, err := ado.NewBuildClient(config.AdoOrganization, config.AdoPersonalAccessToken, config.AdoProject, config.AdoPipelineID)
|
|
if err != nil {
|
|
log.Entry().Warn("Could not write secret ID back to Azure DevOps")
|
|
return err
|
|
}
|
|
variables := []ado.Variable{
|
|
{
|
|
Name: config.VaultAppRoleSecretTokenCredentialsID,
|
|
Value: secretID,
|
|
IsSecret: true,
|
|
},
|
|
}
|
|
if err := adoBuildClient.UpdateVariables(variables); err != nil {
|
|
log.Entry().Warn("Could not write secret ID back to Azure DevOps")
|
|
return err
|
|
}
|
|
case "github":
|
|
// Additional info:
|
|
// https://github.com/google/go-github/blob/master/example/newreposecretwithxcrypto/main.go
|
|
|
|
ctx, client, err := piperGithub.NewClientBuilder(config.GithubToken, config.GithubAPIURL).Build()
|
|
if err != nil {
|
|
log.Entry().Warnf("Could not write secret ID back to GitHub Actions: GitHub client not created: %v", err)
|
|
return err
|
|
}
|
|
|
|
publicKey, _, err := client.Actions.GetRepoPublicKey(ctx, config.Owner, config.Repository)
|
|
if err != nil {
|
|
log.Entry().Warnf("Could not write secret ID back to GitHub Actions: repository's public key not retrieved: %v", err)
|
|
return err
|
|
}
|
|
|
|
encryptedSecret, err := piperGithub.CreateEncryptedSecret(config.VaultAppRoleSecretTokenCredentialsID, secretID, publicKey)
|
|
if err != nil {
|
|
log.Entry().Warnf("Could not write secret ID back to GitHub Actions: secret encryption failed: %v", err)
|
|
return err
|
|
}
|
|
|
|
_, err = client.Actions.CreateOrUpdateRepoSecret(ctx, config.Owner, config.Repository, encryptedSecret)
|
|
if err != nil {
|
|
log.Entry().Warnf("Could not write secret ID back to GitHub Actions: submission to GitHub failed: %v", err)
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("error: invalid secret store: %s", config.SecretStore)
|
|
}
|
|
return nil
|
|
}
|