From 63f14c36f97683f494477511c624a3642a1ceb03 Mon Sep 17 00:00:00 2001 From: Pravin Pushkar Date: Wed, 7 Dec 2022 18:14:10 +0530 Subject: [PATCH] mariner renew-certificate fix (#1139) * mariner renew-certificate fix Signed-off-by: Pravin Pushkar * added UT and E2E test Signed-off-by: Pravin Pushkar * lint fix Signed-off-by: Pravin Pushkar * Update cmd/renew_certificate.go Co-authored-by: Shubham Sharma Signed-off-by: Pravin Pushkar * address review commnets Signed-off-by: Pravin Pushkar * lint fix Signed-off-by: Pravin Pushkar * trigger pr checks Signed-off-by: Pravin Pushkar * Update utils/utils_test.go Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com> Signed-off-by: Pravin Pushkar * Update utils/utils_test.go Co-authored-by: Shubham Sharma Signed-off-by: Pravin Pushkar * Update pkg/kubernetes/renew_certificate.go Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com> Signed-off-by: Pravin Pushkar * added inline comments Signed-off-by: Pravin Pushkar * trigger pr checks Signed-off-by: Pravin Pushkar * adding usage doc Signed-off-by: Pravin Pushkar Signed-off-by: Pravin Pushkar Co-authored-by: Shubham Sharma Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com> --- README.md | 4 +++ cmd/renew_certificate.go | 24 ++++++++++--- pkg/kubernetes/renew_certificate.go | 22 ++++++++++-- tests/e2e/common/common.go | 12 ++++++- tests/e2e/kubernetes/kubernetes_test.go | 13 ++++++- utils/utils.go | 15 ++++++-- utils/utils_test.go | 48 ++++++++++++++++++++++++- 7 files changed, 126 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a0929c51..0e48ce77 100644 --- a/README.md +++ b/README.md @@ -537,6 +537,10 @@ Use user provided ca.crt, issuer.crt and issuer.key ```bash dapr mtls renew-certificate -k --ca-root-certificate --issuer-private-key --issuer-public-certificate --restart ``` +#### To view the complete list of flags and their combination, run below command: +```bash +dapr mtls renew-certificate -h +``` ### List Components diff --git a/cmd/renew_certificate.go b/cmd/renew_certificate.go index 4fa43a17..62495b32 100644 --- a/cmd/renew_certificate.go +++ b/cmd/renew_certificate.go @@ -37,8 +37,9 @@ var ( func RenewCertificateCmd() *cobra.Command { command := &cobra.Command{ - Use: "renew-certificate", - Short: "Rotates Dapr root certificate of your kubernetes cluster", + Use: "renew-certificate", + Aliases: []string{"renew-cert", "rnc"}, + Short: "Rotates the Dapr root certificate on your Kubernetes cluster", Example: ` # Generates new root and issuer certificates for kubernetes cluster @@ -50,6 +51,13 @@ dapr mtls renew-certificate -k --private-key myprivatekey.key --valid-until --issuer-private-key --issuer-public-certificate --restart +# Generates new root and issuer certificates for kubernetes cluster with provided image variant +dapr mtls renew-certificate -k --valid-until --image-variant mariner --restart + +# Use alias to renew certificate command +dapr mtls rnc -k --valid-until --restart +dapr mtls renew-cert -k --valid-until --restart + # See more at: https://docs.dapr.io/getting-started/ `, @@ -62,6 +70,10 @@ dapr mtls renew-certificate -k --ca-root-certificate --issuer-private if kubernetesMode { print.PendingStatusEvent(os.Stdout, "Starting certificate rotation") + err = utils.ValidateImageVariant(imageVariant) + if err != nil { + logErrorAndExit(err) + } if rootcertFlag || issuerKeyFlag || issuerCertFlag { flagArgsEmpty := checkReqFlagArgsEmpty(caRootCertificateFile, issuerPrivateKeyFile, issuerPublicCertificateFile) if flagArgsEmpty { @@ -75,6 +87,7 @@ dapr mtls renew-certificate -k --ca-root-certificate --issuer-private IssuerCertificateFilePath: issuerPublicCertificateFile, IssuerPrivateKeyFilePath: issuerPrivateKeyFile, Timeout: timeout, + ImageVariant: imageVariant, }) if err != nil { logErrorAndExit(err) @@ -90,6 +103,7 @@ dapr mtls renew-certificate -k --ca-root-certificate --issuer-private RootPrivateKeyFilePath: privateKey, ValidUntil: time.Hour * time.Duration(validUntil*24), Timeout: timeout, + ImageVariant: imageVariant, }) if err != nil { logErrorAndExit(err) @@ -97,8 +111,9 @@ dapr mtls renew-certificate -k --ca-root-certificate --issuer-private } else { print.InfoStatusEvent(os.Stdout, "generating fresh certificates") err = kubernetes.RenewCertificate(kubernetes.RenewCertificateParams{ - ValidUntil: time.Hour * time.Duration(validUntil*24), - Timeout: timeout, + ValidUntil: time.Hour * time.Duration(validUntil*24), + Timeout: timeout, + ImageVariant: imageVariant, }) if err != nil { logErrorAndExit(err) @@ -132,6 +147,7 @@ dapr mtls renew-certificate -k --ca-root-certificate --issuer-private command.Flags().UintVarP(&validUntil, "valid-until", "", 365, "Max days before certificate expires") command.Flags().BoolVarP(&restartDaprServices, "restart", "", false, "Restart Dapr control plane services") command.Flags().UintVarP(&timeout, "timeout", "", 300, "The timeout for the certificate renewal") + command.Flags().StringVarP(&imageVariant, "image-variant", "", "", "The image variant to use for the Dapr runtime, for example: mariner") command.MarkFlagRequired("kubernetes") return command } diff --git a/pkg/kubernetes/renew_certificate.go b/pkg/kubernetes/renew_certificate.go index 7da0d3b1..6efed9a7 100644 --- a/pkg/kubernetes/renew_certificate.go +++ b/pkg/kubernetes/renew_certificate.go @@ -26,6 +26,7 @@ import ( "k8s.io/helm/pkg/strvals" "github.com/dapr/cli/pkg/print" + "github.com/dapr/cli/utils" "github.com/dapr/dapr/pkg/sentry/ca" "github.com/dapr/dapr/pkg/sentry/certs" ) @@ -37,6 +38,7 @@ type RenewCertificateParams struct { RootPrivateKeyFilePath string ValidUntil time.Duration Timeout uint + ImageVariant string } func RenewCertificate(conf RenewCertificateParams) error { @@ -63,7 +65,7 @@ func RenewCertificate(conf RenewCertificateParams) error { } } print.InfoStatusEvent(os.Stdout, "Updating certifcates in your Kubernetes cluster") - err = renewCertificate(rootCertBytes, issuerCertBytes, issuerKeyBytes, conf.Timeout) + err = renewCertificate(rootCertBytes, issuerCertBytes, issuerKeyBytes, conf.Timeout, conf.ImageVariant) if err != nil { return err } @@ -86,14 +88,25 @@ func parseCertificateFiles(rootCert, issuerCert, issuerKey string) ([]byte, []by return rootCertBytes, issuerCertBytes, issuerKeyBytes, nil } -func renewCertificate(rootCert, issuerCert, issuerKey []byte, timeout uint) error { +func renewCertificate(rootCert, issuerCert, issuerKey []byte, timeout uint, imageVariant string) error { + var daprVersion, daprImageVariant string status, err := GetDaprResourcesStatus() if err != nil { return err } - daprVersion := GetDaprVersion(status) + daprVersion = GetDaprVersion(status) print.InfoStatusEvent(os.Stdout, "Dapr control plane version %s detected in namespace %s", daprVersion, status[0].Namespace) + // Get the control plane version from daprversion(1.x.x-mariner), if image variant is provided. + // Here, imageVariant is used only to extract the actual control plane version, + // and do some validation on top of that. + if imageVariant != "" { + daprVersion, daprImageVariant = utils.GetVersionAndImageVariant(daprVersion) + if daprImageVariant != imageVariant { + return fmt.Errorf("error in parsing dapr version. found image variant %q is not same as provided value %q", daprImageVariant, imageVariant) + } + } + helmConf, err := helmConfig(status[0].Namespace) if err != nil { return err @@ -104,11 +117,14 @@ func renewCertificate(rootCert, issuerCert, issuerKey []byte, timeout uint) erro return err } upgradeClient := helm.NewUpgrade(helmConf) + + // Reuse the existing helm configuration values i.e. tags, registry, etc. upgradeClient.ReuseValues = true upgradeClient.Wait = true upgradeClient.Timeout = time.Duration(timeout) * time.Second upgradeClient.Namespace = status[0].Namespace + // Override the helm configuration values with the new certificates. vals, err := createHelmParamsForNewCertificates(string(rootCert), string(issuerCert), string(issuerKey)) if err != nil { return err diff --git a/tests/e2e/common/common.go b/tests/e2e/common/common.go index 7b7728cc..749b3fb3 100644 --- a/tests/e2e/common/common.go +++ b/tests/e2e/common/common.go @@ -437,7 +437,17 @@ func GenerateNewCertAndRenew(details VersionDetails, opts TestOptions) func(t *t err := exportCurrentCertificate(daprPath) require.NoError(t, err, "expected no error on certificate exporting") - output, err := spawn.Command(daprPath, "mtls", "renew-certificate", "-k", "--valid-until", "20", "--restart") + args := []string{ + "mtls", + "renew-certificate", + "-k", + "--valid-until", "20", + "--restart", + } + if details.ImageVariant != "" { + args = append(args, "--image-variant", details.ImageVariant) + } + output, err := spawn.Command(daprPath, args...) t.Log(output) require.NoError(t, err, "expected no error on certificate renewal") diff --git a/tests/e2e/kubernetes/kubernetes_test.go b/tests/e2e/kubernetes/kubernetes_test.go index c262712f..e7e09695 100644 --- a/tests/e2e/kubernetes/kubernetes_test.go +++ b/tests/e2e/kubernetes/kubernetes_test.go @@ -381,7 +381,9 @@ func TestRenewCertWithIncorrectFlags(t *testing.T) { } } -func TestKubernetesInstallwithMarinerImages(t *testing.T) { +// install dapr control plane with mariner docker images. +// Renew the certificate of this control plane. +func TestK8sInstallwithMarinerImagesAndRenewCertificate(t *testing.T) { // ensure clean env for test ensureCleanEnv(t) @@ -402,6 +404,15 @@ func TestKubernetesInstallwithMarinerImages(t *testing.T) { tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...) + // tests for certifcate renewal with newly generated certificates. + tests = append(tests, []common.TestCase{ + {"Renew certificate which expires in less than 30 days", common.GenerateNewCertAndRenew(currentVersionDetails, installOpts)}, + }...) + tests = append(tests, common.GetTestsPostCertificateRenewal(currentVersionDetails, installOpts)...) + tests = append(tests, []common.TestCase{ + {"Cert Expiry warning message check " + currentVersionDetails.RuntimeVersion, common.CheckMTLSStatus(currentVersionDetails, installOpts, true)}, + }...) + // teardown everything tests = append(tests, common.GetTestsOnUninstall(currentVersionDetails, common.TestOptions{ CheckResourceExists: map[common.Resource]bool{ diff --git a/utils/utils.go b/utils/utils.go index bc361d05..dc094dfb 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -41,6 +41,8 @@ const ( DOCKER ContainerRuntime = "docker" PODMAN ContainerRuntime = "podman" + marinerImageVariantName = "mariner" + socketFormat = "%s/dapr-%s-%s.socket" ) @@ -72,8 +74,6 @@ func Contains[T comparable](vs []T, x T) bool { return false } -const marinerImageVariantName = "mariner" - // PrintTable to print in the table format. func PrintTable(csvContent string) { WriteTable(os.Stdout, csvContent) @@ -310,3 +310,14 @@ func GetVariantVersion(version, imageVariant string) string { } return fmt.Sprintf("%s-%s", version, imageVariant) } + +// Returns image version and variant. +// Expected imageTag format: -, i.e. 1.0.0-mariner or 1.0.0-rc.1-mariner. +func GetVersionAndImageVariant(imageTag string) (string, string) { + imageVersionOffset := strings.LastIndex(imageTag, "-") + imageVariant := imageTag[imageVersionOffset+1:] + if imageVariant == marinerImageVariantName { + return imageTag[:imageVersionOffset], imageVariant + } + return imageTag, "" +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 8daeb0fe..ed0c9735 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -13,7 +13,11 @@ limitations under the License. package utils -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestContainerRuntimeUtils(t *testing.T) { testcases := []struct { @@ -117,3 +121,45 @@ func TestContains(t *testing.T) { }) } } + +func TestGetVersionAndImageVariant(t *testing.T) { + testcases := []struct { + name string + input string + expectedVersion string + expectedImageVariant string + }{ + { + name: "image tag contains version and variant", + input: "1.9.0-mariner", + expectedVersion: "1.9.0", + expectedImageVariant: "mariner", + }, + { + name: "image tag contains only version", + input: "1.9.0", + expectedVersion: "1.9.0", + expectedImageVariant: "", + }, + { + name: "image tag contains only rc version and variant", + input: "1.9.0-rc.1-mariner", + expectedVersion: "1.9.0-rc.1", + expectedImageVariant: "mariner", + }, + { + name: "image tag contains only rc version", + input: "1.9.0-rc.1", + expectedVersion: "1.9.0-rc.1", + expectedImageVariant: "", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + version, imageVariant := GetVersionAndImageVariant(tc.input) + assert.Equal(t, tc.expectedVersion, version) + assert.Equal(t, tc.expectedImageVariant, imageVariant) + }) + } +}