From 697fa5f9445114cd6687714896af0f7df2d4fb1a Mon Sep 17 00:00:00 2001 From: Isaac Boaz Date: Wed, 3 Jul 2024 14:51:21 -0700 Subject: [PATCH] Feature/azure cert deploy (#1132) * Adding support for cert rotations. * Adding APIMConfig template --------- Co-authored-by: Karnveer Gill --- .../trcdb/trcapimgmtbase/trcapimgmtbase.go | 2 +- .../trcdb/trccertmgmtbase/trccertmgmtbase.go | 84 +++++++++++++++++++ .../trcdb/trcplgtoolbase/trcplgtoolbase.go | 12 +++ .../APIMConfig/APIMConfig/config.yml.tmpl | 12 +++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 atrium/vestibulum/trcdb/trccertmgmtbase/trccertmgmtbase.go create mode 100644 installation/trcsh/trc_templates/APIMConfig/APIMConfig/config.yml.tmpl diff --git a/atrium/vestibulum/trcdb/trcapimgmtbase/trcapimgmtbase.go b/atrium/vestibulum/trcdb/trcapimgmtbase/trcapimgmtbase.go index 2fdc646a8..4bb840f2f 100644 --- a/atrium/vestibulum/trcdb/trcapimgmtbase/trcapimgmtbase.go +++ b/atrium/vestibulum/trcdb/trcapimgmtbase/trcapimgmtbase.go @@ -129,7 +129,7 @@ func CommonMain(envPtr *string, return err } - //Adding a 3 minute timeout on APIM Update. + //Adding a 2 minute timeout on APIM Update. go func(ctxC context.CancelFunc) { time.Sleep(time.Second * 120) ctxC() diff --git a/atrium/vestibulum/trcdb/trccertmgmtbase/trccertmgmtbase.go b/atrium/vestibulum/trcdb/trccertmgmtbase/trccertmgmtbase.go new file mode 100644 index 000000000..5de0b63e3 --- /dev/null +++ b/atrium/vestibulum/trcdb/trccertmgmtbase/trccertmgmtbase.go @@ -0,0 +1,84 @@ +package trccertmgmtbase + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" +) + +func CommonMain(certPathPtr *string, driverConfig *eUtils.DriverConfig, mod *kv.Modifier) error { + if len(*certPathPtr) == 0 { + return errors.New("certPath flag is empty, expected path to cert") + } + + certBytes, err := os.ReadFile(*certPathPtr) + if err != nil { + return err + } + + apimConfigMap := make(map[string]string) + tempMap, readErr := mod.ReadData("super-secrets/Restricted/APIMConfig/config") + if readErr != nil { + return readErr + } else if len(tempMap) == 0 { + return errors.New("Couldn't get apim configs for update.") + } + + for key, value := range tempMap { + apimConfigMap[fmt.Sprintf("%v", key)] = fmt.Sprintf("%v", value) + } + + svc, err := azidentity.NewClientSecretCredential( + apimConfigMap["azureTenantId"], + apimConfigMap["azureClientId"], + apimConfigMap["azureClientSecret"], + nil) + if err != nil { + driverConfig.CoreConfig.Log.Printf("failed to obtain a credential: %v", err) + return err + } + + ctx, _ := context.WithCancel(context.Background()) + clientFactory, err := armapimanagement.NewClientFactory(apimConfigMap["SUBSCRIPTION_ID"], svc, nil) + if err != nil { + driverConfig.CoreConfig.Log.Printf("failed to create client: %v", err) + return err + } + + if resourceGroupName, exists := apimConfigMap["RESOURCE_GROUP_NAME"]; exists { + if serviceName, exists := apimConfigMap["SERVICE_NAME"]; exists { + certificateId := time.Now().UTC().Format(strings.ReplaceAll(time.RFC3339, ":", "-")) + + etag := "*" + + _, err = clientFactory.NewCertificateClient().CreateOrUpdate(ctx, resourceGroupName, serviceName, certificateId, armapimanagement.CertificateCreateOrUpdateParameters{ + Properties: &armapimanagement.CertificateCreateOrUpdateProperties{ + Data: to.Ptr(base64.StdEncoding.EncodeToString(certBytes)), + Password: to.Ptr(apimConfigMap["CERTIFICATE_PASSWORD"]), + }, + }, &armapimanagement.CertificateClientCreateOrUpdateOptions{IfMatch: &etag}) + + if err != nil { + driverConfig.CoreConfig.Log.Printf("failed to finish certificate request") + return err + } + + fmt.Printf("Certificate %v successfully deployed\n", certificateId) + } else { + return errors.New("SERVICE_NAME is not populated in apimConfigMap") + } + } else { + return errors.New("RESOURCE_GROUP_NAME is not populated in apimConfigMap") + } + return nil +} diff --git a/atrium/vestibulum/trcdb/trcplgtoolbase/trcplgtoolbase.go b/atrium/vestibulum/trcdb/trcplgtoolbase/trcplgtoolbase.go index 9eb5f47e2..2a485655b 100644 --- a/atrium/vestibulum/trcdb/trcplgtoolbase/trcplgtoolbase.go +++ b/atrium/vestibulum/trcdb/trcplgtoolbase/trcplgtoolbase.go @@ -24,6 +24,7 @@ import ( eUtils "github.com/trimble-oss/tierceron/pkg/utils" trcapimgmtbase "github.com/trimble-oss/tierceron/atrium/vestibulum/trcdb/trcapimgmtbase" + "github.com/trimble-oss/tierceron/atrium/vestibulum/trcdb/trccertmgmtbase" ) func CommonMain(envDefaultPtr *string, @@ -95,6 +96,9 @@ func CommonMain(envDefaultPtr *string, //APIM flags updateAPIMPtr := flagset.Bool("updateAPIM", false, "Used to update Azure APIM") + // Cert flags + certPathPtr := flagset.String("certPath", "", "Path to certificate to push to Azure") + if trcshDriverConfig == nil || !trcshDriverConfig.DriverConfig.IsShellSubProcess { args := argLines[1:] for i := 0; i < len(args); i++ { @@ -340,6 +344,14 @@ func CommonMain(envDefaultPtr *string, return nil } + if len(*certPathPtr) > 0 { + updateCertError := trccertmgmtbase.CommonMain(certPathPtr, config, mod) + if updateCertError != nil { + fmt.Println("Couldn't update Cert...proceeding with build") + } + return nil + } + // Get existing configs if they exist... pluginToolConfig, plcErr := trcvutils.GetPluginToolConfig(&trcshDriverConfigBase.DriverConfig, mod, coreopts.BuildOptions.ProcessDeployPluginEnvConfig(map[string]interface{}{}), *defineServicePtr) if plcErr != nil { diff --git a/installation/trcsh/trc_templates/APIMConfig/APIMConfig/config.yml.tmpl b/installation/trcsh/trc_templates/APIMConfig/APIMConfig/config.yml.tmpl new file mode 100644 index 000000000..6a8ec9086 --- /dev/null +++ b/installation/trcsh/trc_templates/APIMConfig/APIMConfig/config.yml.tmpl @@ -0,0 +1,12 @@ +API_MANAGEMENT_SERVICE_NAME: {{ .API_MANAGEMENT_SERVICE_NAME }} +API_URL: {{ .API_URL }} +SUBSCRIPTION_ID: {{ .SUBSCRIPTION_ID }} +RESOURCE_GROUP_NAME: {{ .RESOURCE_GROUP_NAME }} +SERVICE_NAME: {{ .SERVICE_NAME }} +API_NAME: {{ .API_NAME }} +API_PATH: {{ .API_PATH }} +azureClientId: {{ .azureClientId }} +azureClientSecret: {{ .azureClientSecret }} +azureTenantId: {{.azureTenantId }} +API_TITLE: {{.API_TITLE }} +CERTIFICATE_PASSWORD: {{.CERTIFICATE_PASSWORD }}