mariner renew-certificate fix (#1139)

* mariner renew-certificate fix

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* added UT and E2E test

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* lint fix

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* Update cmd/renew_certificate.go

Co-authored-by: Shubham Sharma <shubhash@microsoft.com>
Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* address review commnets

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* lint fix

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* trigger pr checks

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* Update utils/utils_test.go

Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* Update utils/utils_test.go

Co-authored-by: Shubham Sharma <shubhash@microsoft.com>
Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* Update pkg/kubernetes/renew_certificate.go

Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* added inline comments

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* trigger pr checks

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

* adding usage doc

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>

Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com>
Co-authored-by: Shubham Sharma <shubhash@microsoft.com>
Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
This commit is contained in:
Pravin Pushkar 2022-12-07 18:14:10 +05:30 committed by GitHub
parent 8a0936af9f
commit 63f14c36f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 126 additions and 12 deletions

View File

@ -537,6 +537,10 @@ Use user provided ca.crt, issuer.crt and issuer.key
```bash
dapr mtls renew-certificate -k --ca-root-certificate <ca.crt> --issuer-private-key <issuer.key> --issuer-public-certificate <issuer.crt> --restart
```
#### To view the complete list of flags and their combination, run below command:
```bash
dapr mtls renew-certificate -h
```
### List Components

View File

@ -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 <no
# Rotates certificate of your kubernetes cluster with provided ca.cert, issuer.crt and issuer.key file path
dapr mtls renew-certificate -k --ca-root-certificate <root.pem> --issuer-private-key <issuer.key> --issuer-public-certificate <issuer.pem> --restart
# Generates new root and issuer certificates for kubernetes cluster with provided image variant
dapr mtls renew-certificate -k --valid-until <no of days> --image-variant mariner --restart
# Use alias to renew certificate command
dapr mtls rnc -k --valid-until <no of days> --restart
dapr mtls renew-cert -k --valid-until <no of days> --restart
# See more at: https://docs.dapr.io/getting-started/
`,
@ -62,6 +70,10 @@ dapr mtls renew-certificate -k --ca-root-certificate <root.pem> --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 <root.pem> --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 <root.pem> --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 <root.pem> --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 <root.pem> --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
}

View File

@ -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

View File

@ -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")

View File

@ -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{

View File

@ -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: <version>-<variant>, 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, ""
}

View File

@ -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)
})
}
}