Skip to content

Commit 4818533

Browse files
Changes for cli command to renew certificate (#912)
* Changes for cli command to renew certificate Signed-off-by: Pravin Pushkar <[email protected]> * adding renew-cert as subcommand to mtls Signed-off-by: Pravin Pushkar <[email protected]> * Impl for generating fresh certificates with provided ttl Signed-off-by: Pravin Pushkar <[email protected]> * fix lint and ttl Signed-off-by: Pravin Pushkar <[email protected]> * Adding a flag to restart services Signed-off-by: Pravin Pushkar <[email protected]> * Adding tests and some refactoring Signed-off-by: Pravin Pushkar <[email protected]> * Adding timeout and changing flags detail Signed-off-by: Pravin Pushkar <[email protected]> * fixing review comments Signed-off-by: Pravin Pushkar <[email protected]> * Fixing review comments and test failures Signed-off-by: Pravin Pushkar <[email protected]> * Fixing tests and readme Signed-off-by: Pravin Pushkar <[email protected]> * fixing uninstall and other certificate test Signed-off-by: Pravin Pushkar <[email protected]> * review comments fixes Signed-off-by: Pravin Pushkar <[email protected]>
1 parent ce0af7c commit 4818533

File tree

10 files changed

+679
-36
lines changed

10 files changed

+679
-36
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,23 @@ dapr mtls expiry
437437

438438
This can be used when upgrading to a newer version of Dapr, as it's recommended to carry over the existing certs for a zero downtime upgrade.
439439

440+
### Renew Dapr certificates of a kubernetes cluster with one of the 3 ways mentioned below:
441+
Renew certificate by generating new root and issuer certificates
442+
443+
```bash
444+
dapr mtls renew-certificate -k --valid-until <no of days> --restart
445+
```
446+
Use existing private root.key to generate new root and issuer certificates
447+
448+
```bash
449+
dapr mtls renew-certificate -k --private-key myprivatekey.key --valid-until <no of days>
450+
```
451+
Use user provided ca.crt, issuer.crt and issuer.key
452+
453+
```bash
454+
dapr mtls renew-certificate -k --ca-root-certificate <ca.crt> --issuer-private-key <issuer.key> --issuer-public-certificate <issuer.crt> --restart
455+
```
456+
440457
### List Components
441458

442459
To list all Dapr components on Kubernetes:

cmd/mtls.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,6 @@ func init() {
101101
MTLSCmd.MarkFlagRequired("kubernetes")
102102
MTLSCmd.AddCommand(ExportCMD)
103103
MTLSCmd.AddCommand(ExpiryCMD)
104+
MTLSCmd.AddCommand(RenewCertificateCmd())
104105
RootCmd.AddCommand(MTLSCmd)
105106
}

cmd/renew_certificate.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
Copyright 2021 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package cmd
15+
16+
import (
17+
"fmt"
18+
"os"
19+
"time"
20+
21+
"github.com/spf13/cobra"
22+
23+
"github.com/dapr/cli/pkg/kubernetes"
24+
"github.com/dapr/cli/pkg/print"
25+
"github.com/dapr/cli/utils"
26+
)
27+
28+
var (
29+
privateKey string
30+
caRootCertificateFile string
31+
issuerPrivateKeyFile string
32+
issuerPublicCertificateFile string
33+
validUntil uint
34+
restartDaprServices bool
35+
)
36+
37+
func RenewCertificateCmd() *cobra.Command {
38+
command := &cobra.Command{
39+
Use: "renew-certificate",
40+
Short: "Rotates Dapr root certificate of your kubernetes cluster",
41+
42+
Example: `
43+
# Generates new root and issuer certificates for kubernetes cluster
44+
dapr mtls renew-certificate -k --valid-until <no of days> --restart
45+
46+
# Uses existing private root.key to generate new root and issuer certificates for kubernetes cluster
47+
dapr mtls renew-certificate -k --private-key myprivatekey.key --valid-until <no of days>
48+
49+
# Rotates certificate of your kubernetes cluster with provided ca.cert, issuer.crt and issuer.key file path
50+
dapr mtls renew-certificate -k --ca-root-certificate <ca.crt> --issuer-private-key <issuer.key> --issuer-public-certificate <issuer.crt> --restart
51+
52+
# See more at: https://docs.dapr.io/getting-started/
53+
`,
54+
55+
Run: func(cmd *cobra.Command, args []string) {
56+
if kubernetesMode {
57+
print.PendingStatusEvent(os.Stdout, "Starting certificate rotation")
58+
if caRootCertificateFile != "" && issuerPrivateKeyFile != "" && issuerPublicCertificateFile != "" {
59+
print.InfoStatusEvent(os.Stdout, "Using provided certificates")
60+
err := kubernetes.RenewCertificate(kubernetes.RenewCertificateParams{
61+
RootCertificateFilePath: caRootCertificateFile,
62+
IssuerCertificateFilePath: issuerPublicCertificateFile,
63+
IssuerPrivateKeyFilePath: issuerPrivateKeyFile,
64+
Timeout: timeout,
65+
})
66+
if err != nil {
67+
logErrorAndExit(err)
68+
}
69+
} else if privateKey != "" {
70+
print.InfoStatusEvent(os.Stdout, "Using password file to generate root certificate")
71+
err := kubernetes.RenewCertificate(kubernetes.RenewCertificateParams{
72+
RootPrivateKeyFilePath: privateKey,
73+
ValidUntil: time.Hour * time.Duration(validUntil*24),
74+
Timeout: timeout,
75+
})
76+
if err != nil {
77+
logErrorAndExit(err)
78+
}
79+
} else {
80+
print.InfoStatusEvent(os.Stdout, "generating fresh certificates")
81+
err := kubernetes.RenewCertificate(kubernetes.RenewCertificateParams{
82+
ValidUntil: time.Hour * time.Duration(validUntil*24),
83+
Timeout: timeout,
84+
})
85+
if err != nil {
86+
logErrorAndExit(err)
87+
}
88+
}
89+
}
90+
},
91+
PostRun: func(cmd *cobra.Command, args []string) {
92+
expiry, err := kubernetes.Expiry()
93+
if err != nil {
94+
logErrorAndExit(err)
95+
}
96+
print.SuccessStatusEvent(os.Stdout,
97+
fmt.Sprintf("Certificate rotation is successful! Your new certicate is valid through %s", expiry.Format(time.RFC1123)))
98+
99+
if restartDaprServices {
100+
restartControlPlaneService()
101+
if err != nil {
102+
print.FailureStatusEvent(os.Stdout, err.Error())
103+
os.Exit(1)
104+
}
105+
}
106+
},
107+
}
108+
109+
command.Flags().BoolVarP(&kubernetesMode, "kubernetes", "k", false, "Renews root and issuer certificates of Dapr in a Kubernetes cluster")
110+
command.Flags().StringVarP(&privateKey, "private-key", "", "", "The root.key file which is used to generate root certificate")
111+
command.Flags().StringVarP(&caRootCertificateFile, "ca-root-certificate", "", "", "The root certificate file")
112+
command.Flags().StringVarP(&issuerPrivateKeyFile, "issuer-private-key", "", "", "The issuer certificate private key")
113+
command.Flags().StringVarP(&issuerPublicCertificateFile, "issuer-public-certificate", "", "", "The issuer certificate")
114+
command.Flags().UintVarP(&validUntil, "valid-until", "", 365, "Max days before certificate expires")
115+
command.Flags().BoolVarP(&restartDaprServices, "restart", "", false, "Restart Dapr control plane services")
116+
command.Flags().UintVarP(&timeout, "timeout", "", 300, "The timeout for the certificate renewal")
117+
command.MarkFlagRequired("kubernetes")
118+
return command
119+
}
120+
121+
func logErrorAndExit(err error) {
122+
err = fmt.Errorf("certificate rotation failed %w", err)
123+
print.FailureStatusEvent(os.Stderr, err.Error())
124+
os.Exit(1)
125+
}
126+
127+
func restartControlPlaneService() error {
128+
controlPlaneServices := []string{"deploy/dapr-sentry", "deploy/dapr-operator", "statefulsets/dapr-placement-server"}
129+
namespace, err := getDaprNamespace()
130+
if err != nil {
131+
print.FailureStatusEvent(os.Stdout, "Failed to fetch Dapr namespace")
132+
}
133+
for _, name := range controlPlaneServices {
134+
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Restarting %s..", name))
135+
_, err := utils.RunCmdAndWait("kubectl", "rollout", "restart", name, "-n", namespace)
136+
if err != nil {
137+
return fmt.Errorf("error in restarting deployment %s. Error is %w", name, err)
138+
}
139+
_, err = utils.RunCmdAndWait("kubectl", "rollout", "status", name, "-n", namespace)
140+
if err != nil {
141+
return fmt.Errorf("error in checking status for deployment %s. Error is %w", name, err)
142+
}
143+
}
144+
print.SuccessStatusEvent(os.Stdout, "All control plane services have restarted successfully!")
145+
return nil
146+
}
147+
148+
func getDaprNamespace() (string, error) {
149+
status, err := kubernetes.GetDaprResourcesStatus()
150+
if err != nil {
151+
return "", err
152+
}
153+
return status[0].Namespace, nil
154+
}

pkg/kubernetes/common.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright 2021 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package kubernetes
15+
16+
import (
17+
"errors"
18+
"strings"
19+
20+
helm "helm.sh/helm/v3/pkg/action"
21+
)
22+
23+
func GetDaprResourcesStatus() ([]StatusOutput, error) {
24+
sc, err := NewStatusClient()
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
status, err := sc.Status()
30+
if err != nil {
31+
return nil, err
32+
}
33+
34+
if len(status) == 0 {
35+
return nil, errors.New("dapr is not installed in your cluster")
36+
}
37+
return status, nil
38+
}
39+
40+
func GetDaprHelmChartName(helmConf *helm.Configuration) (string, error) {
41+
listClient := helm.NewList(helmConf)
42+
releases, err := listClient.Run()
43+
if err != nil {
44+
return "", err
45+
}
46+
var chart string
47+
for _, r := range releases {
48+
if r.Chart != nil && strings.Contains(r.Chart.Name(), "dapr") {
49+
chart = r.Name
50+
break
51+
}
52+
}
53+
return chart, nil
54+
}
55+
56+
func GetDaprVersion(status []StatusOutput) string {
57+
var daprVersion string
58+
for _, s := range status {
59+
if s.Name == operatorName {
60+
daprVersion = s.Version
61+
}
62+
}
63+
return daprVersion
64+
}

0 commit comments

Comments
 (0)