Skip to content

Commit

Permalink
Adds commands to generate ca and end-entity certs (#187)
Browse files Browse the repository at this point in the history
  • Loading branch information
anekkanti authored Jun 9, 2023
1 parent 615eebb commit efd88b8
Show file tree
Hide file tree
Showing 8 changed files with 1,026 additions and 25 deletions.
502 changes: 502 additions & 0 deletions app/certificates.go

Large diffs are not rendered by default.

199 changes: 199 additions & 0 deletions app/certificates_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package app

import (
"testing"
"time"

"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
"github.com/urfave/cli/v2"
)

func TestCertificates(t *testing.T) {
suite.Run(t, new(CertificatesTestSuite))
}

type CertificatesTestSuite struct {
suite.Suite
cliApp *cli.App
mockCtrl *gomock.Controller
}

func (s *CertificatesTestSuite) SetupTest() {
s.mockCtrl = gomock.NewController(s.T())

out, err := NewCertificatesCommand()
s.Require().NoError(err)

AutoConfirmFlag.Value = true
s.cliApp = &cli.App{
Name: "test",
Commands: []*cli.Command{out.Command},
Flags: []cli.Flag{
AutoConfirmFlag,
},
}
}

func (s *CertificatesTestSuite) RunCmd(args ...string) error {
return s.cliApp.Run(append([]string{"tcld"}, args...))
}

func (s *CertificatesTestSuite) AfterTest(suiteName, testName string) {
s.mockCtrl.Finish()
}

func (s *CertificatesTestSuite) TestCertificateGenerateCore() {
type args struct {
rsa bool
caValidityPeriod time.Duration
endEntityValidityPeriod time.Duration
organization string
}
tests := []struct {
name string
args args
caGenerationErrMsg string
endEntityGenerationErrMsg string
}{
{
"success - defaults",
args{
organization: "test-certificate",
caValidityPeriod: 365 * 24 * time.Hour,
},
"",
"",
},
{
"success - options",
args{
rsa: true,
organization: "test-certificate",
caValidityPeriod: 365 * 24 * time.Hour,
endEntityValidityPeriod: 24 * time.Hour,
},
"",
"",
},
{
"failure - missing required fields",
args{},
"Error:Field validation for 'Organization' failed on the 'required' tag",
"",
},
{
"failure - end-entity validity period too big",
args{
rsa: true,
organization: "test-certificate",
caValidityPeriod: 365 * 24 * time.Hour,
endEntityValidityPeriod: 500 * 24 * time.Hour,
},
"",
"validity period of 12000h0m0s puts certificate's expiry after certificate authority's expiry",
},
}
for _, tt := range tests {
s.Run(tt.name, func() {
caPem, caPrivKeyPem, err := generateCACertificate(generateCACertificateInput{
Organization: tt.args.organization,
ValidityPeriod: tt.args.caValidityPeriod,
RSAAlgorithm: tt.args.rsa,
})

if tt.caGenerationErrMsg == "" {
s.NoError(err, "ca cert generation failed")
} else {
s.Error(err, "expected ca cert generation to fail")
s.ErrorContains(err, tt.caGenerationErrMsg)
return
}

certBytes, certKeyBytes, err := generateEndEntityCertificate(generateEndEntityCertificateInput{
Organization: tt.args.organization + "-leaf",
ValidityPeriod: tt.args.endEntityValidityPeriod,
CaPem: caPem,
CaPrivateKeyPEM: caPrivKeyPem,
})

if tt.endEntityGenerationErrMsg == "" {
s.NoError(err, "end-entity cert generation failed")
if err != nil {
return
}

// Even though these are not CA certs, we use this function to make sure
// the leaf certificates we have generated are actually valid
_, _, _, err = parseCACerts(certBytes, certKeyBytes)
s.NoError(err)

} else {
s.Error(err, "expected end-entity cert generation to fail")
s.ErrorContains(err, tt.endEntityGenerationErrMsg)
}
})
}
}

func (s *CertificatesTestSuite) TestGenerateCACertificateCMD() {
tests := []struct {
name string
args []string
expectErrMsg string
}{
{
name: "generate ca success",
args: []string{"gen", "ca", "--org", "testorg", "-d", "8d", "--ca-cert", "/tmp/" + uuid.NewString(), "--ca-key", "/tmp/" + uuid.NewString()},
expectErrMsg: "",
},
{
name: "generate ca failure - validity period too short",
args: []string{"gen", "ca", "--org", "testorg", "-d", "3d", "--ca-cert", "/tmp/" + uuid.NewString(), "--ca-key", "/tmp/" + uuid.NewString()},
expectErrMsg: "validity-period cannot be less than: 168h0m0s",
},
{
name: "generate ca failure - validity period too long",
args: []string{"gen", "ca", "--org", "testorg", "-d", "1000d", "--ca-cert", "/tmp/" + uuid.NewString(), "--ca-key", "/tmp/" + uuid.NewString()},
expectErrMsg: "validity-period cannot be more than: 8760h0m0s",
},
{
name: "generate ca failure - validity period malformed",
args: []string{"gen", "ca", "--org", "testorg", "-d", "malformed", "--ca-cert", "/tmp/" + uuid.NewString(), "--ca-key", "/tmp/" + uuid.NewString()},
expectErrMsg: "failed to parse validity-period: time: invalid duration",
},
}

for _, tc := range tests {
s.Run(tc.name, func() {
err := s.RunCmd(tc.args...)
if tc.expectErrMsg != "" {
s.Error(err)
s.ErrorContains(err, tc.expectErrMsg)
} else {
s.NoError(err)
}
})
}
}

func (s *CertificatesTestSuite) TestGenerateCertificateCMDEndToEnd() {
caCertFile := "/tmp/" + uuid.NewString()
caKeyFile := "/tmp/" + uuid.NewString()
leafCertFile := "/tmp/" + uuid.NewString()
leafKeyFile := "/tmp/" + uuid.NewString()

s.NoError(s.RunCmd([]string{"gen", "ca", "--org", "testorg", "-d", "8d", "--ca-cert", caCertFile, "--ca-key", caKeyFile}...))
s.NoError(s.RunCmd([]string{"gen", "leaf", "--org", "testorg", "-d", "1d", "--ca-cert", caCertFile, "--ca-key", caKeyFile, "--cert", leafCertFile, "--key", leafKeyFile}...))

s.ErrorContains(
s.RunCmd([]string{"gen", "leaf", "--org", "testorg", "-d", "malformed", "--ca-cert", caCertFile, "--ca-key", caKeyFile, "--cert", leafCertFile, "--key", leafKeyFile}...),
"failed to parse validity-period: time: invalid duration",
)

s.ErrorContains(
s.RunCmd([]string{"gen", "leaf", "--org", "testorg", "-d", "100d", "--ca-cert", caCertFile, "--ca-key", caKeyFile, "--cert", leafCertFile, "--key", leafKeyFile}...),
"failed to generate end-entity certificate: validity period of 2400h0m0s puts certificate's expiry after certificate authority's expiry",
)
}
44 changes: 22 additions & 22 deletions app/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/temporalio/tcld/protogen/api/auth/v1"
"go.uber.org/multierr"
"io/ioutil"
"net/mail"
"strings"

"github.com/temporalio/tcld/protogen/api/auth/v1"
"go.uber.org/multierr"

"github.com/kylelemons/godebug/diff"
"github.com/temporalio/tcld/protogen/api/authservice/v1"
"github.com/temporalio/tcld/protogen/api/namespace/v1"
Expand All @@ -24,8 +25,6 @@ const (
CaCertificateFlagName = "ca-certificate"
CaCertificateFileFlagName = "ca-certificate-file"
caCertificateFingerprintFlagName = "ca-certificate-fingerprint"
certificateFilterFileFlagName = "certificate-filter-file"
certificateFilterInputFlagName = "certificate-filter-input"
searchAttributeFlagName = "search-attribute"
userNamespacePermissionFlagName = "user-namespace-permission"
)
Expand Down Expand Up @@ -477,25 +476,26 @@ func NewNamespaceCommand(getNamespaceClientFn GetNamespaceClientFn) (CommandOut,
Name: "accepted-client-ca",
Usage: "Manage client ca certificate used to verify client connections",
Aliases: []string{"ca"},
Subcommands: []*cli.Command{{
Name: "list",
Aliases: []string{"l"},
Usage: "List the accepted client ca certificates currently configured for the namespace",
Flags: []cli.Flag{
NamespaceFlag,
},
Action: func(ctx *cli.Context) error {
n, err := c.getNamespace(ctx.String(NamespaceFlagName))
if err != nil {
return err
}
out, err := parseCertificates(n.Spec.AcceptedClientCa)
if err != nil {
return err
}
return PrintObj(out)
Subcommands: []*cli.Command{
{
Name: "list",
Aliases: []string{"l"},
Usage: "List the accepted client ca certificates currently configured for the namespace",
Flags: []cli.Flag{
NamespaceFlag,
},
Action: func(ctx *cli.Context) error {
n, err := c.getNamespace(ctx.String(NamespaceFlagName))
if err != nil {
return err
}
out, err := parseCertificates(n.Spec.AcceptedClientCa)
if err != nil {
return err
}
return PrintObj(out)
},
},
},
{
Name: "add",
Aliases: []string{"a"},
Expand Down
1 change: 1 addition & 0 deletions cmd/tcld/fx.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func fxOptions() fx.Option {
app.GetLoginClient,
app.NewLoginCommand,
app.NewLogoutCommand,
app.NewCertificatesCommand,
func() app.GetNamespaceClientFn {
return app.GetNamespaceClient
},
Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module github.com/temporalio/tcld
go 1.18

require (
github.com/go-playground/validator/v10 v10.13.0
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/kylelemons/godebug v1.1.0
github.com/stretchr/testify v1.8.2
github.com/urfave/cli/v2 v2.25.1
github.com/urfave/cli/v2 v2.25.3
go.uber.org/fx v1.19.2
go.uber.org/multierr v1.6.0
google.golang.org/grpc v1.54.0
Expand All @@ -16,13 +18,17 @@ require (
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/leodido/go-urn v1.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/dig v1.16.1 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
Expand Down
17 changes: 15 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
Expand All @@ -13,10 +20,14 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -30,8 +41,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand All @@ -51,6 +62,8 @@ go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand Down
Loading

0 comments on commit efd88b8

Please sign in to comment.