Skip to content

Commit

Permalink
Add generate-certificate command
Browse files Browse the repository at this point in the history
Allows to generate Dapr certificates either as output to console or save to local files

Signed-off-by: Anton Troshin <[email protected]>
  • Loading branch information
antontroshin committed Feb 15, 2025
1 parent ac6822e commit 629aee8
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 6 deletions.
97 changes: 97 additions & 0 deletions cmd/generate_certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package cmd

import (
"fmt"
"os"
"path/filepath"
"time"

"github.com/spf13/cobra"

"github.com/dapr/cli/pkg/kubernetes"
"github.com/dapr/cli/pkg/print"
)

var (
certificateValidUntil uint
certificateSavePath string
)

func GenerateCertificateCmd() *cobra.Command {
command := &cobra.Command{
Use: "generate-certificate",
Short: "Generate new Dapr root certificate and output to console or save to local folder",
Example: `
# Generate a new certificate and print to console
dapr mtls generate-certificate
# Generate a new certificate and save to local folder
dapr mtls generate-certificate --out ./certs
# Generate a new certificate with specific expiration days from today
dapr mtls generate-certificate --valid-until <no of days>
# Generate a new certificate with specific expiration days from today and save to local folder
dapr mtls generate-certificate --valid-until <no of days> --out ./certs
`,
Run: func(cmd *cobra.Command, args []string) {
rootCertBytes, issuerCertBytes, issuerKeyBytes, err := kubernetes.GenerateNewCertificates(
time.Duration(certificateValidUntil)*time.Hour*24,
"")
if err != nil {
print.FailureStatusEvent(os.Stderr, fmt.Sprintf("error generating cert: %s", err))
os.Exit(1)
}

savePathSet := cmd.Flags().Lookup("out").Changed

if savePathSet {
_, err := os.Stat(certificateSavePath)

if os.IsNotExist(err) {
errDir := os.MkdirAll(certificateSavePath, 0o755)
if errDir != nil {
print.FailureStatusEvent(os.Stderr, fmt.Sprintf("error creating directory: %s", err))
os.Exit(1)
}
}
err = os.WriteFile(filepath.Join(certificateSavePath, "ca.crt"), rootCertBytes, 0o600)
if err != nil {
print.FailureStatusEvent(os.Stderr, fmt.Sprintf("error writing ca.crt: %s", err))
os.Exit(1)
}

err = os.WriteFile(filepath.Join(certificateSavePath, "issuer.crt"), issuerCertBytes, 0o600)
if err != nil {
print.FailureStatusEvent(os.Stderr, fmt.Sprintf("error writing issuer.crt: %s", err))
os.Exit(1)
}

err = os.WriteFile(filepath.Join(certificateSavePath, "issuer.key"), issuerKeyBytes, 0o600)
if err != nil {
print.FailureStatusEvent(os.Stderr, fmt.Sprintf("error writing issuer.key: %s", err))
os.Exit(1)
}
print.InfoStatusEvent(os.Stdout, "Generated new certificates and saved to %s", certificateSavePath)
print.SuccessStatusEvent(os.Stdout, "CA Root Certificate: %s", filepath.Join(certificateSavePath, "ca.crt"))
print.SuccessStatusEvent(os.Stdout, "Issuer Certificate: %s", filepath.Join(certificateSavePath, "issuer.crt"))
print.SuccessStatusEvent(os.Stdout, "Issuer Key: %s", filepath.Join(certificateSavePath, "issuer.key"))
} else {
print.InfoStatusEvent(os.Stdout, "Generated new certificates")

print.SuccessStatusEvent(os.Stdout, "CA Root Certificate: ca.crt")
fmt.Println(string(rootCertBytes))

print.SuccessStatusEvent(os.Stdout, "Issuer Certificate: issuer.crt")
fmt.Println(string(issuerCertBytes))

print.SuccessStatusEvent(os.Stdout, "Issuer Key: issuer.key")
fmt.Println(string(issuerKeyBytes))
}
},
}

command.Flags().UintVarP(&certificateValidUntil, "valid-until", "", 365, "Max days before certificate expires")
command.Flags().StringVarP(&certificateSavePath, "out", "o", ".", "The output directory path to save the certs")
return command
}
1 change: 1 addition & 0 deletions cmd/mtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,6 @@ func init() {
MTLSCmd.AddCommand(ExportCMD)
MTLSCmd.AddCommand(ExpiryCMD)
MTLSCmd.AddCommand(RenewCertificateCmd())
MTLSCmd.AddCommand(GenerateCertificateCmd())
RootCmd.AddCommand(MTLSCmd)
}
2 changes: 1 addition & 1 deletion cmd/renew_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ dapr mtls renew-cert -k --valid-until <no of days> --restart
logErrorAndExit(err)
}
print.SuccessStatusEvent(os.Stdout,
"Certificate rotation is successful! Your new certicate is valid through "+expiry.Format(time.RFC1123))
"Certificate rotation is successful! Your new certificate is valid through "+expiry.Format(time.RFC1123))

if restartDaprServices {
restartControlPlaneService()
Expand Down
56 changes: 56 additions & 0 deletions tests/e2e/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ func GetTestForCertRenewal(currentVersionDetails VersionDetails, installOpts Tes
return tests
}

func GetTestForCertGeneration() []TestCase {
return []TestCase{
{"Generate certificate", GenerateCertificateTests()},
{"Generate certificate with out dir", GenerateCertificateWithOutDirTests()},
}
}

func GetTestsPostCertificateRenewal(details VersionDetails, opts TestOptions) []TestCase {
return []TestCase{
{"crds exist " + details.RuntimeVersion, CRDTest(details, opts)},
Expand Down Expand Up @@ -681,6 +688,55 @@ func NegativeScenarioForCertRenew() func(t *testing.T) {
}
}

func GenerateCertificateTests() func(t *testing.T) {
return func(t *testing.T) {
daprPath := GetDaprPath()
args := []string{
"mtls", "generate-certificate",
}
output, err := spawn.Command(daprPath, args...)
t.Log(output)
require.NoError(t, err, "expected no error on certificate generation")
assert.Contains(t, output, "Generated new certificates")

assert.Contains(t, output, "CA Root Certificate: ca.crt")
assert.Contains(t, output, "Issuer Certificate: issuer.crt")
assert.Contains(t, output, "Issuer Key: issuer.key")

beginCertCount := strings.Count(output, "-----BEGIN CERTIFICATE-----")
assert.Equal(t, beginCertCount, 2, "expected 2 BEGIN CERTIFICATE in output")

Check failure on line 707 in tests/e2e/common/common.go

View workflow job for this annotation

GitHub Actions / Build linux_amd64 binaries

expected-actual: need to reverse actual and expected values (testifylint)

endCertCount := strings.Count(output, "-----END CERTIFICATE-----")
assert.Equal(t, endCertCount, 2, "expected 2 END CERTIFICATE in output")

Check failure on line 710 in tests/e2e/common/common.go

View workflow job for this annotation

GitHub Actions / Build linux_amd64 binaries

expected-actual: need to reverse actual and expected values (testifylint)

beginPrivateKeyCount := strings.Count(output, "-----BEGIN PRIVATE KEY-----")
assert.Equal(t, beginPrivateKeyCount, 1, "expected 1 BEGIN PRIVATE KEY in output")

Check failure on line 713 in tests/e2e/common/common.go

View workflow job for this annotation

GitHub Actions / Build linux_amd64 binaries

expected-actual: need to reverse actual and expected values (testifylint)

endPrivateKeyCount := strings.Count(output, "-----END PRIVATE KEY-----")
assert.Equal(t, endPrivateKeyCount, 1, "expected 1 END PRIVATE KEY in output")
}
}

func GenerateCertificateWithOutDirTests() func(t *testing.T) {
return func(t *testing.T) {
daprPath := GetDaprPath()
args := []string{
"mtls", "generate-certificate", "--out", "./certs",
}
output, err := spawn.Command(daprPath, args...)
t.Log(output)

require.NoError(t, err, "expected no error on certificate generation")
assert.Contains(t, output, "Generated new certificates and saved to ./certs")
assert.Contains(t, output, "CA Root Certificate: certs/ca.crt")
assert.Contains(t, output, "Issuer Certificate: certs/issuer.crt")
assert.Contains(t, output, "Issuer Key: certs/issuer.key")
assert.FileExists(t, "./certs/ca.crt")
assert.FileExists(t, "./certs/issuer.crt")
assert.FileExists(t, "./certs/issuer.key")
}
}

func CheckMTLSStatus(details VersionDetails, opts TestOptions, shouldWarningExist bool) func(t *testing.T) {
return func(t *testing.T) {
daprPath := GetDaprPath()
Expand Down
13 changes: 8 additions & 5 deletions tests/e2e/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,12 @@ func TestRenewCertificateMTLSEnabled(t *testing.T) {

tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...)

// tests for certifcate renewal.
// tests for certificate renewal.
tests = append(tests, common.GetTestForCertRenewal(currentVersionDetails, installOpts)...)

// tests for certificate generation.
tests = append(tests, common.GetTestForCertGeneration()...)

// teardown everything
tests = append(tests, common.GetTestsOnUninstall(currentVersionDetails, common.TestOptions{
CheckResourceExists: map[common.Resource]bool{
Expand Down Expand Up @@ -268,7 +271,7 @@ func TestRenewCertificateMTLSDisabled(t *testing.T) {

tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...)

// tests for certifcate renewal.
// tests for certificate renewal.
tests = append(tests, common.GetTestForCertRenewal(currentVersionDetails, installOpts)...)

// teardown everything
Expand Down Expand Up @@ -303,7 +306,7 @@ func TestRenewCertWithPrivateKey(t *testing.T) {

tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...)

// tests for certifcate renewal with newly generated certificates when pem encoded private root.key file is provided
// tests for certificate renewal with newly generated certificates when pem encoded private root.key file is provided
tests = append(tests, []common.TestCase{
{"Renew certificate which expires in less than 30 days", common.UseProvidedPrivateKeyAndRenewCerts(currentVersionDetails, installOpts)},
}...)
Expand Down Expand Up @@ -375,7 +378,7 @@ func TestRenewCertWithIncorrectFlags(t *testing.T) {

tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...)

// tests for certifcate renewal with incorrect set of flags provided.
// tests for certificate renewal with incorrect set of flags provided.
tests = append(tests, []common.TestCase{
{"Renew certificate with incorrect flags", common.NegativeScenarioForCertRenew()},
}...)
Expand Down Expand Up @@ -417,7 +420,7 @@ func TestK8sInstallwithMarinerImagesAndRenewCertificate(t *testing.T) {

tests = append(tests, common.GetTestsOnInstall(currentVersionDetails, installOpts)...)

// tests for certifcate renewal.
// tests for certificate renewal.
tests = append(tests, common.GetTestForCertRenewal(currentVersionDetails, installOpts)...)

// teardown everything
Expand Down

0 comments on commit 629aee8

Please sign in to comment.