From 00b412a45a86a22c3b60c61e60db4a79e61f1942 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 12 Aug 2019 17:18:55 -0400 Subject: [PATCH 01/15] remove autocert bug + enhancement issue templates * add regular enhancement tracker --- .github/ISSUE_TEMPLATE/autocert_bug.md | 28 ------------------- .../ISSUE_TEMPLATE/autocert_enhancement.md | 11 -------- .github/ISSUE_TEMPLATE/enhancement.md | 11 ++++++++ 3 files changed, 11 insertions(+), 39 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/autocert_bug.md delete mode 100644 .github/ISSUE_TEMPLATE/autocert_enhancement.md create mode 100644 .github/ISSUE_TEMPLATE/enhancement.md diff --git a/.github/ISSUE_TEMPLATE/autocert_bug.md b/.github/ISSUE_TEMPLATE/autocert_bug.md deleted file mode 100644 index c4a75b6e..00000000 --- a/.github/ISSUE_TEMPLATE/autocert_bug.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Autocert Bug -about: Report a bug you found in autocert -labels: area/autocert bug ---- - -### Subject of the issue -Describe your issue here - -### Environment -* Kubernetes version: -* Cloud provider or hardware configuration: -* OS (e.g., from /etc/os-release): -* Kernel (e.g., `uname -a`): -* Install tools: -* Other: - -### Steps to reproduce -Tell us how to reproduce this issue - -### Expected behaviour -Tell us what should happen - -### Actual behaviour -Tell us what happens instead - -### Additional context -Add any other context about the problem here diff --git a/.github/ISSUE_TEMPLATE/autocert_enhancement.md b/.github/ISSUE_TEMPLATE/autocert_enhancement.md deleted file mode 100644 index 5260f5ec..00000000 --- a/.github/ISSUE_TEMPLATE/autocert_enhancement.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Autocert Enhancement -about: Suggest an enhancement to autocert -labels: area/autocert enhancement ---- - -### What would you like to be added - - -### Why this is needed - diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md new file mode 100644 index 00000000..0915d08f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -0,0 +1,11 @@ +--- +name: Certificates Enhancement +about: Suggest an enhancement to step certificates +labels: area/cert-management enhancement +--- + +### What would you like to be added + + +### Why this is needed + From f3faeeee4d6aae39dcc11d90e46a0a876b6375a0 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 21 Aug 2019 18:20:52 -0700 Subject: [PATCH 02/15] dep change golint revision --- Gopkg.lock | 18 +++++++++++------- Gopkg.toml | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index b629b1f1..9c49f222 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -83,15 +83,11 @@ version = "v1.4.1" [[projects]] - branch = "travis-1.9" - digest = "1:e8f5d9c09a7209c740e769713376abda388c41b777ba8e9ed52767e21acf379f" + digest = "1:4ee452f8994700dcab9e816aef1cb9eb2317218734c6ccf5135746e6c19f3dce" name = "github.com/golang/lint" - packages = [ - ".", - "golint", - ] + packages = ["golint"] pruneopts = "UT" - revision = "883fe33ffc4344bad1ecd881f61afd5ec5d80e0a" + revision = "06c8688daad7faa9da5a0c2f163a3d14aac986ca" [[projects]] digest = "1:97df918963298c287643883209a2c3f642e6593379f97ab400c2a2e219ab647d" @@ -358,6 +354,14 @@ pruneopts = "UT" revision = "4d3f4d9ffa16a13f451c3b2999e9c49e9750bf06" +[[projects]] + branch = "master" + digest = "1:0824a2eb250dd552f41daaf471266d085e8e8cabdbff9e4294bc799076e00da7" + name = "golang.org/x/lint" + packages = ["."] + pruneopts = "UT" + revision = "959b441ac422379a43da2230f62be024250818b0" + [[projects]] branch = "master" digest = "1:2f7468b0b3fd7d926072f0dcbb6ec81e337278b4e5de639d017e54f785f0b475" diff --git a/Gopkg.toml b/Gopkg.toml index bdb5b7d5..944ed32a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -32,6 +32,10 @@ required = [ "github.com/tsenart/deadcode", ] +[[constraint]] + name = "github.com/golang/lint" + revision = "06c8688daad7faa9da5a0c2f163a3d14aac986ca" + [[constraint]] name = "github.com/alecthomas/gometalinter" revision = "bae2f1293d092fd8167939d5108d1b025eaef9de" From 635c59ed24d3e16575ce15e536aecb1131e4ee65 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 23 Aug 2019 12:09:16 -0700 Subject: [PATCH 03/15] Accept emails SANs --- Gopkg.lock | 6 ++--- authority/authorize_test.go | 8 ++----- authority/provisioner/jwk.go | 3 ++- authority/provisioner/jwk_test.go | 2 +- authority/provisioner/sign_options.go | 20 ++++++++++++++++ authority/provisioner/sign_options_test.go | 27 ++++++++++++++++++++++ ca/client.go | 3 +-- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 9c49f222..0601e9fe 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -276,8 +276,8 @@ revision = "de77670473b5492f5d0bce155b5c01534c2d13f7" [[projects]] - branch = "master" - digest = "1:8eb842c27bca9dae16d77baeba7cf612135033da381faf833bb8c11c29a751c7" + branch = "email" + digest = "1:cef75c86c34990ab5ed7826c35b8241474302c8264c64a0207c155229784479c" name = "github.com/smallstep/cli" packages = [ "command", @@ -298,7 +298,7 @@ "utils", ] pruneopts = "UT" - revision = "98635d188cade54451e3997b530716297ce7fc00" + revision = "18e04f77ce327f282a5265dec5f35ec612868838" [[projects]] branch = "master" diff --git a/authority/authorize_test.go b/authority/authorize_test.go index 368e9807..03a1139f 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -65,7 +65,6 @@ func TestAuthority_authorizeToken(t *testing.T) { auth *Authority ott string err *apiError - res []interface{} } tests := map[string]func(t *testing.T) *authorizeTest{ "fail/invalid-ott": func(t *testing.T) *authorizeTest { @@ -273,7 +272,6 @@ func TestAuthority_authorizeRevoke(t *testing.T) { auth *Authority opts *RevokeOptions err error - res []interface{} } tests := map[string]func(t *testing.T) *authorizeTest{ "fail/token/invalid-ott": func(t *testing.T) *authorizeTest { @@ -383,7 +381,6 @@ func TestAuthority_AuthorizeSign(t *testing.T) { auth *Authority ott string err *apiError - res []interface{} } tests := map[string]func(t *testing.T) *authorizeTest{ "fail/invalid-ott": func(t *testing.T) *authorizeTest { @@ -449,7 +446,7 @@ func TestAuthority_AuthorizeSign(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - assert.Len(t, 6, got) + assert.Len(t, 7, got) } } }) @@ -476,7 +473,6 @@ func TestAuthority_Authorize(t *testing.T) { auth *Authority ott string err *apiError - res []interface{} } tests := map[string]func(t *testing.T) *authorizeTest{ "fail/invalid-ott": func(t *testing.T) *authorizeTest { @@ -542,7 +538,7 @@ func TestAuthority_Authorize(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - assert.Len(t, 6, got) + assert.Len(t, 7, got) } } }) diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index dca5dce9..d0b08fa1 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -141,11 +141,12 @@ func (p *JWK) AuthorizeSign(token string) ([]SignOption, error) { claims.SANs = []string{claims.Subject} } - dnsNames, ips := x509util.SplitSANs(claims.SANs) + dnsNames, ips, emails := x509util.SplitSANs(claims.SANs) return []SignOption{ commonNameValidator(claims.Subject), dnsNamesValidator(dnsNames), ipAddressesValidator(ips), + emailAddressesValidator(emails), profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID), newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), diff --git a/authority/provisioner/jwk_test.go b/authority/provisioner/jwk_test.go index 21789ca8..0f45744a 100644 --- a/authority/provisioner/jwk_test.go +++ b/authority/provisioner/jwk_test.go @@ -265,7 +265,7 @@ func TestJWK_AuthorizeSign(t *testing.T) { } } else { if assert.NotNil(t, got) { - assert.Len(t, 6, got) + assert.Len(t, 7, got) _cnv := got[0] cnv, ok := _cnv.(commonNameValidator) diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index fa7e1d6f..a106e9de 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -157,6 +157,26 @@ func (v ipAddressesValidator) Valid(req *x509.CertificateRequest) error { return nil } +// emailAddressesValidator validates the email address SANs of a certificate request. +type emailAddressesValidator []string + +// Valid checks that certificate request IP Addresses match those configured in +// the bootstrap (token) flow. +func (v emailAddressesValidator) Valid(req *x509.CertificateRequest) error { + want := make(map[string]bool) + for _, s := range v { + want[s] = true + } + got := make(map[string]bool) + for _, s := range req.EmailAddresses { + got[s] = true + } + if !reflect.DeepEqual(want, got) { + return errors.Errorf("certificate request does not contain the valid Email Addresses - got %v, want %v", req.EmailAddresses, v) + } + return nil +} + // validityValidator validates the certificate temporal validity settings. type validityValidator struct { min time.Duration diff --git a/authority/provisioner/sign_options_test.go b/authority/provisioner/sign_options_test.go index 18e5b8d2..bc6d7414 100644 --- a/authority/provisioner/sign_options_test.go +++ b/authority/provisioner/sign_options_test.go @@ -88,6 +88,33 @@ func Test_commonNameSliceValidator_Valid(t *testing.T) { } } +func Test_emailAddressesValidator_Valid(t *testing.T) { + type args struct { + req *x509.CertificateRequest + } + tests := []struct { + name string + v emailAddressesValidator + args args + wantErr bool + }{ + {"ok0", []string{}, args{&x509.CertificateRequest{EmailAddresses: []string{}}}, false}, + {"ok1", []string{"max@smallstep.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@smallstep.com"}}}, false}, + {"ok2", []string{"max@step.com", "mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@step.com", "mike@step.com"}}}, false}, + {"ok3", []string{"max@step.com", "mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"mike@step.com", "max@step.com"}}}, false}, + {"fail1", []string{"max@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"mike@step.com"}}}, true}, + {"fail2", []string{"mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@step.com", "mike@step.com"}}}, true}, + {"fail3", []string{"mike@step.com", "max@step.com"}, args{&x509.CertificateRequest{DNSNames: []string{"mike@step.com", "mex@step.com"}}}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("dnsNamesValidator.Valid() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + func Test_dnsNamesValidator_Valid(t *testing.T) { type args struct { req *x509.CertificateRequest diff --git a/ca/client.go b/ca/client.go index c9766293..6ac16e8a 100644 --- a/ca/client.go +++ b/ca/client.go @@ -541,8 +541,7 @@ func CreateSignRequest(ott string) (*api.SignRequest, crypto.PrivateKey, error) return nil, nil, errors.Wrap(err, "error generating key") } - var emails []string - dnsNames, ips := x509util.SplitSANs(claims.SANs) + dnsNames, ips, emails := x509util.SplitSANs(claims.SANs) if claims.Email != "" { emails = append(emails, claims.Email) } From 0939f0d0535729bb73a7b5c00326f9b986e35ac3 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 23 Aug 2019 16:35:49 -0700 Subject: [PATCH 04/15] dep update cli email sans --- Gopkg.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 0601e9fe..ead1da09 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -276,8 +276,8 @@ revision = "de77670473b5492f5d0bce155b5c01534c2d13f7" [[projects]] - branch = "email" - digest = "1:cef75c86c34990ab5ed7826c35b8241474302c8264c64a0207c155229784479c" + branch = "master" + digest = "1:130790954c8862bf8c2f635e220caf94cb8c72cc3957d0e2f1892e5b93f5e0e1" name = "github.com/smallstep/cli" packages = [ "command", @@ -298,7 +298,7 @@ "utils", ] pruneopts = "UT" - revision = "18e04f77ce327f282a5265dec5f35ec612868838" + revision = "60c9e9cea2e247ad90b11ce5b5c4639dfc783eff" [[projects]] branch = "master" From 2b41faa9cf2af9612e051ec7e09c3d8c082a0a69 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 26 Aug 2019 17:52:49 -0700 Subject: [PATCH 05/15] Enforce >= 2048 bit rsa keys at the provisioner layer * Fixes #94 * In the future this should be configurable by provisioner --- api/api_test.go | 24 ++++--- authority/authorize_test.go | 4 +- authority/provisioner/aws.go | 1 + authority/provisioner/aws_test.go | 10 +-- authority/provisioner/azure.go | 1 + authority/provisioner/azure_test.go | 6 +- authority/provisioner/gcp.go | 1 + authority/provisioner/gcp_test.go | 6 +- authority/provisioner/jwk.go | 1 + authority/provisioner/jwk_test.go | 6 +- authority/provisioner/oidc.go | 20 +++--- authority/provisioner/oidc_test.go | 4 +- authority/provisioner/sign_options.go | 22 +++++- authority/provisioner/sign_options_test.go | 72 ++++++++++++++++++++ authority/provisioner/testdata/ecdsa.csr | 7 ++ authority/provisioner/testdata/ecdsa.key | 8 +++ authority/provisioner/testdata/ed25519.csr | 6 ++ authority/provisioner/testdata/ed25519.key | 6 ++ authority/provisioner/testdata/rsa.csr | 16 +++++ authority/provisioner/testdata/rsa.key | 30 ++++++++ authority/provisioner/testdata/short-rsa.csr | 10 +++ authority/tls_test.go | 28 ++++++++ 22 files changed, 250 insertions(+), 39 deletions(-) create mode 100644 authority/provisioner/testdata/ecdsa.csr create mode 100644 authority/provisioner/testdata/ecdsa.key create mode 100644 authority/provisioner/testdata/ed25519.csr create mode 100644 authority/provisioner/testdata/ed25519.key create mode 100644 authority/provisioner/testdata/rsa.csr create mode 100644 authority/provisioner/testdata/rsa.key create mode 100644 authority/provisioner/testdata/short-rsa.csr diff --git a/api/api_test.go b/api/api_test.go index 3f99dd68..88110314 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -24,6 +24,8 @@ import ( "time" "github.com/go-chi/chi" + "github.com/pkg/errors" + "github.com/smallstep/assert" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/logging" @@ -150,7 +152,7 @@ func parseCertificate(data string) *x509.Certificate { } func parseCertificateRequest(data string) *x509.CertificateRequest { - block, _ := pem.Decode([]byte(csrPEM)) + block, _ := pem.Decode([]byte(data)) if block == nil { panic("failed to parse certificate request PEM") } @@ -385,13 +387,13 @@ func TestSignRequest_Validate(t *testing.T) { NotAfter time.Time } tests := []struct { - name string - fields fields - wantErr bool + name string + fields fields + err error }{ - {"missing csr", fields{CertificateRequest{}, "foobarzar", time.Time{}, time.Time{}}, true}, - {"invalid csr", fields{CertificateRequest{bad}, "foobarzar", time.Time{}, time.Time{}}, true}, - {"missing ott", fields{CertificateRequest{csr}, "", time.Time{}, time.Time{}}, true}, + {"missing csr", fields{CertificateRequest{}, "foobarzar", time.Time{}, time.Time{}}, errors.New("missing csr")}, + {"invalid csr", fields{CertificateRequest{bad}, "foobarzar", time.Time{}, time.Time{}}, errors.New("invalid csr")}, + {"missing ott", fields{CertificateRequest{csr}, "", time.Time{}, time.Time{}}, errors.New("missing ott")}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -401,8 +403,12 @@ func TestSignRequest_Validate(t *testing.T) { NotAfter: NewTimeDuration(tt.fields.NotAfter), NotBefore: NewTimeDuration(tt.fields.NotBefore), } - if err := s.Validate(); (err != nil) != tt.wantErr { - t.Errorf("SignRequest.Validate() error = %v, wantErr %v", err, tt.wantErr) + if err := s.Validate(); err != nil { + if assert.NotNil(t, tt.err) { + assert.HasPrefix(t, err.Error(), tt.err.Error()) + } + } else { + assert.Nil(t, tt.err) } }) } diff --git a/authority/authorize_test.go b/authority/authorize_test.go index 03a1139f..27a15513 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -446,7 +446,7 @@ func TestAuthority_AuthorizeSign(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - assert.Len(t, 7, got) + assert.Len(t, 8, got) } } }) @@ -538,7 +538,7 @@ func TestAuthority_Authorize(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - assert.Len(t, 7, got) + assert.Len(t, 8, got) } } }) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 8b288e3d..421b4af8 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -287,6 +287,7 @@ func (p *AWS) AuthorizeSign(token string) ([]SignOption, error) { } return append(so, + defaultPublicKeyValidator{}, commonNameValidator(payload.Claims.Subject), profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), newProvisionerExtensionOption(TypeAWS, p.Name, doc.AccountID, "InstanceID", doc.InstanceID), diff --git a/authority/provisioner/aws_test.go b/authority/provisioner/aws_test.go index 41793848..98c885fa 100644 --- a/authority/provisioner/aws_test.go +++ b/authority/provisioner/aws_test.go @@ -326,11 +326,11 @@ func TestAWS_AuthorizeSign(t *testing.T) { wantLen int wantErr bool }{ - {"ok", p1, args{t1}, 4, false}, - {"ok", p2, args{t2}, 6, false}, - {"ok", p2, args{t2Hostname}, 6, false}, - {"ok", p2, args{t2PrivateIP}, 6, false}, - {"ok", p1, args{t4}, 4, false}, + {"ok", p1, args{t1}, 5, false}, + {"ok", p2, args{t2}, 7, false}, + {"ok", p2, args{t2Hostname}, 7, false}, + {"ok", p2, args{t2PrivateIP}, 7, false}, + {"ok", p1, args{t4}, 5, false}, {"fail account", p3, args{t3}, 0, true}, {"fail token", p1, args{"token"}, 0, true}, {"fail subject", p1, args{failSubject}, 0, true}, diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index ee7fb744..40d14c9c 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -275,6 +275,7 @@ func (p *Azure) AuthorizeSign(token string) ([]SignOption, error) { } return append(so, + defaultPublicKeyValidator{}, profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), newProvisionerExtensionOption(TypeAzure, p.Name, p.TenantID), newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), diff --git a/authority/provisioner/azure_test.go b/authority/provisioner/azure_test.go index c370682a..8fbc9b20 100644 --- a/authority/provisioner/azure_test.go +++ b/authority/provisioner/azure_test.go @@ -281,9 +281,9 @@ func TestAzure_AuthorizeSign(t *testing.T) { wantLen int wantErr bool }{ - {"ok", p1, args{t1}, 3, false}, - {"ok", p2, args{t2}, 5, false}, - {"ok", p1, args{t11}, 3, false}, + {"ok", p1, args{t1}, 4, false}, + {"ok", p2, args{t2}, 6, false}, + {"ok", p1, args{t11}, 4, false}, {"fail tenant", p3, args{t3}, 0, true}, {"fail resource group", p4, args{t4}, 0, true}, {"fail token", p1, args{"token"}, 0, true}, diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 234b00fe..5ee92237 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -228,6 +228,7 @@ func (p *GCP) AuthorizeSign(token string) ([]SignOption, error) { } return append(so, + defaultPublicKeyValidator{}, profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), newProvisionerExtensionOption(TypeGCP, p.Name, claims.Subject, "InstanceID", ce.InstanceID, "InstanceName", ce.InstanceName), newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), diff --git a/authority/provisioner/gcp_test.go b/authority/provisioner/gcp_test.go index ce935526..04dffd95 100644 --- a/authority/provisioner/gcp_test.go +++ b/authority/provisioner/gcp_test.go @@ -311,9 +311,9 @@ func TestGCP_AuthorizeSign(t *testing.T) { wantLen int wantErr bool }{ - {"ok", p1, args{t1}, 3, false}, - {"ok", p2, args{t2}, 5, false}, - {"ok", p3, args{t3}, 3, false}, + {"ok", p1, args{t1}, 4, false}, + {"ok", p2, args{t2}, 6, false}, + {"ok", p3, args{t3}, 4, false}, {"fail token", p1, args{"token"}, 0, true}, {"fail key", p1, args{failKey}, 0, true}, {"fail iss", p1, args{failIss}, 0, true}, diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index d0b08fa1..a4bc1137 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -143,6 +143,7 @@ func (p *JWK) AuthorizeSign(token string) ([]SignOption, error) { dnsNames, ips, emails := x509util.SplitSANs(claims.SANs) return []SignOption{ + defaultPublicKeyValidator{}, commonNameValidator(claims.Subject), dnsNamesValidator(dnsNames), ipAddressesValidator(ips), diff --git a/authority/provisioner/jwk_test.go b/authority/provisioner/jwk_test.go index 0f45744a..5e3ad5f7 100644 --- a/authority/provisioner/jwk_test.go +++ b/authority/provisioner/jwk_test.go @@ -265,14 +265,14 @@ func TestJWK_AuthorizeSign(t *testing.T) { } } else { if assert.NotNil(t, got) { - assert.Len(t, 7, got) + assert.Len(t, 8, got) - _cnv := got[0] + _cnv := got[1] cnv, ok := _cnv.(commonNameValidator) assert.True(t, ok) assert.Equals(t, string(cnv), "subject") - _dnv := got[1] + _dnv := got[2] dnv, ok := _dnv.(dnsNamesValidator) assert.True(t, ok) if tt.name == "ok-sans" { diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index 0b2e2700..66e8b9b7 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -264,21 +264,19 @@ func (o *OIDC) AuthorizeSign(token string) ([]SignOption, error) { if err != nil { return nil, err } - // Admins should be able to authorize any SAN - if o.IsAdmin(claims.Email) { - return []SignOption{ - profileDefaultDuration(o.claimer.DefaultTLSCertDuration()), - newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID), - newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()), - }, nil - } - return []SignOption{ - emailOnlyIdentity(claims.Email), + so := []SignOption{ + defaultPublicKeyValidator{}, profileDefaultDuration(o.claimer.DefaultTLSCertDuration()), newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID), newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()), - }, nil + } + // Admins should be able to authorize any SAN + if o.IsAdmin(claims.Email) { + return so, nil + } + + return append(so, emailOnlyIdentity(claims.Email)), nil } // AuthorizeRenewal returns an error if the renewal is disabled. diff --git a/authority/provisioner/oidc_test.go b/authority/provisioner/oidc_test.go index 80920f41..431bb7f8 100644 --- a/authority/provisioner/oidc_test.go +++ b/authority/provisioner/oidc_test.go @@ -286,9 +286,9 @@ func TestOIDC_AuthorizeSign(t *testing.T) { } else { assert.NotNil(t, got) if tt.name == "admin" { - assert.Len(t, 3, got) - } else { assert.Len(t, 4, got) + } else { + assert.Len(t, 5, got) } } }) diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index a106e9de..d535dfb6 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -1,6 +1,8 @@ package provisioner import ( + "crypto/ecdsa" + "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" @@ -10,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/cli/crypto/x509util" + "golang.org/x/crypto/ed25519" ) // Options contains the options that can be passed to the Sign method. @@ -78,7 +81,7 @@ func (e emailOnlyIdentity) Valid(req *x509.CertificateRequest) error { case len(req.EmailAddresses) == 0: return errors.New("certificate request does not contain any email address") case len(req.EmailAddresses) > 1: - return errors.New("certificate request does not contain too many email addresses") + return errors.New("certificate request contains too many email addresses") case req.EmailAddresses[0] == "": return errors.New("certificate request cannot contain an empty email address") case req.EmailAddresses[0] != string(e): @@ -88,6 +91,23 @@ func (e emailOnlyIdentity) Valid(req *x509.CertificateRequest) error { } } +// defaultPublicKeyValidator validates the public key of a certificate request. +type defaultPublicKeyValidator struct{} + +// Valid checks that certificate request common name matches the one configured. +func (v defaultPublicKeyValidator) Valid(req *x509.CertificateRequest) error { + switch k := req.PublicKey.(type) { + case *rsa.PublicKey: + if k.Size() < 256 { + return errors.New("rsa key in CSR must be at least 2048 bits (256 bytes)") + } + case *ecdsa.PublicKey, ed25519.PublicKey: + default: + return errors.Errorf("unrecognized public key of type '%T' in CSR", k) + } + return nil +} + // commonNameValidator validates the common name of a certificate request. type commonNameValidator string diff --git a/authority/provisioner/sign_options_test.go b/authority/provisioner/sign_options_test.go index bc6d7414..38f1d5e6 100644 --- a/authority/provisioner/sign_options_test.go +++ b/authority/provisioner/sign_options_test.go @@ -7,6 +7,12 @@ import ( "net/url" "testing" "time" + + "github.com/pkg/errors" + "github.com/smallstep/assert" + "github.com/smallstep/cli/crypto/pemutil" + "github.com/smallstep/cli/crypto/x509util" + stepx509 "github.com/smallstep/cli/pkg/x509" ) func Test_emailOnlyIdentity_Valid(t *testing.T) { @@ -41,6 +47,72 @@ func Test_emailOnlyIdentity_Valid(t *testing.T) { } } +func Test_defaultPublicKeyValidator_Valid(t *testing.T) { + _shortRSA, err := pemutil.Read("./testdata/short-rsa.csr") + assert.FatalError(t, err) + shortRSA, ok := _shortRSA.(*x509.CertificateRequest) + assert.Fatal(t, ok) + + _rsa, err := pemutil.Read("./testdata/rsa.csr") + assert.FatalError(t, err) + rsaCSR, ok := _rsa.(*x509.CertificateRequest) + assert.Fatal(t, ok) + + _ecdsa, err := pemutil.Read("./testdata/ecdsa.csr") + assert.FatalError(t, err) + ecdsaCSR, ok := _ecdsa.(*x509.CertificateRequest) + assert.Fatal(t, ok) + + _ed25519, err := pemutil.Read("./testdata/ed25519.csr", pemutil.WithStepCrypto()) + assert.FatalError(t, err) + ed25519CSR, ok := _ed25519.(*stepx509.CertificateRequest) + assert.Fatal(t, ok) + + v := defaultPublicKeyValidator{} + tests := []struct { + name string + csr *x509.CertificateRequest + err error + }{ + { + "fail/unrecognized-key-type", + &x509.CertificateRequest{PublicKey: "foo"}, + errors.New("unrecognized public key of type 'string' in CSR"), + }, + { + "fail/rsa/too-short", + shortRSA, + errors.New("rsa key in CSR must be at least 2048 bits (256 bytes)"), + }, + { + "ok/rsa", + rsaCSR, + nil, + }, + { + "ok/ecdsa", + ecdsaCSR, + nil, + }, + { + "ok/ed25519", + x509util.ToX509CertificateRequest(ed25519CSR), + nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := v.Valid(tt.csr); err != nil { + if assert.NotNil(t, tt.err) { + assert.HasPrefix(t, err.Error(), tt.err.Error()) + } + } else { + assert.Nil(t, tt.err) + } + }) + } +} + func Test_commonNameValidator_Valid(t *testing.T) { type args struct { req *x509.CertificateRequest diff --git a/authority/provisioner/testdata/ecdsa.csr b/authority/provisioner/testdata/ecdsa.csr new file mode 100644 index 00000000..5fe5be03 --- /dev/null +++ b/authority/provisioner/testdata/ecdsa.csr @@ -0,0 +1,7 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIHqMIGRAgEAMA4xDDAKBgNVBAMTA2ZvbzBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABKdDjTb7XIYCWC4QUq1xn5hgf3J4WpfWbd3C5frKrA4/VdQ+XfpHQIxDoHqh +jcWke0SEETc9i6HDDtWv8bXSETegITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQH +MAWCA2ZvbzAKBggqhkjOPQQDAgNIADBFAiEA1pFLT8p/YogG0o6NEEmdxzwbOzJA +A+C+DvoT91c1OcQCIGUjP3s+k6Xwdf/VukUZXTfG1lobmkZhO3vYxAjPkwA7 +-----END CERTIFICATE REQUEST----- diff --git a/authority/provisioner/testdata/ecdsa.key b/authority/provisioner/testdata/ecdsa.key new file mode 100644 index 00000000..37c04f0f --- /dev/null +++ b/authority/provisioner/testdata/ecdsa.key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,54abd40e525b255542ee6161ec438721 + +fJvmEc5n0IG4t4FKF+ekKhpog4ods2nZjBR5KLkGH5oSGAOEADSXIRBK76Jnm/nz +Kv8ZwGqxNnoJUQyeTMlyg5OnOUAQPyNBPvoItOlD2DP32WJXgQ+NSHB2h9pcBGYG +yLWrCtzl9/P9REWskanPO4RujP27Ht62omcMO7SxxNI= +-----END EC PRIVATE KEY----- diff --git a/authority/provisioner/testdata/ed25519.csr b/authority/provisioner/testdata/ed25519.csr new file mode 100644 index 00000000..c5ca07a5 --- /dev/null +++ b/authority/provisioner/testdata/ed25519.csr @@ -0,0 +1,6 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIGuMGICAQAwDjEMMAoGA1UEAxMDZm9vMCowBQYDK2VwAyEA3yF/Igqb5UTp6XOq +yj+cZL9nIfjDKrUT0fMzDAHtIqqgITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQH +MAWCA2ZvbzAFBgMrZXADQQAIAx7N6ezi4NL8n0oJU8v3AmVSi0XvTuIHXUtcLGoU +OZtlO3zjWI+DgcT/ADeEKn+T8OazDxcCbTBbHiM2hIsA +-----END CERTIFICATE REQUEST----- diff --git a/authority/provisioner/testdata/ed25519.key b/authority/provisioner/testdata/ed25519.key new file mode 100644 index 00000000..38de1972 --- /dev/null +++ b/authority/provisioner/testdata/ed25519.key @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGkMGAGCSqGSIb3DQEFDTBTMDIGCSqGSIb3DQEFDDAlBBDJ0vCXdpPyUiLlbge5 +1g0jAgMBhqAwDAYIKoZIhvcNAgkAADAdBglghkgBZQMEASoEENtOknzU2eS2mlxl +73Yo/IoEQEyJS2EEx3+oYaKlFIB90e1Zkmi8da7d3r2iUlfc7faRAiKChcEvtEas +vYF2l9LEZ9DXv1Rm1uyNuSpXuddHScE= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/authority/provisioner/testdata/rsa.csr b/authority/provisioner/testdata/rsa.csr new file mode 100644 index 00000000..3352b48c --- /dev/null +++ b/authority/provisioner/testdata/rsa.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICdDCCAVwCAQAwDjEMMAoGA1UEAxMDZm9vMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA86h3t/KJylE0/aPxvF9JqPaOwSsGexuDWqDVJSOWBJi/ZqUA +Ea2Gy05ZIJkQ5GOy0bUs2JCNCVXVkfPrUkX6IvIlXpTjutjMDYyYGdgQjzpKPnOA +v3mO2a7mLMzJunws7pvrUPP7z5KDCKSAPf6VAcu/na8rGDWn1TUYR8hINK1rLQQf +OcyNWrr7yLkR84jSsrw/Qgc8NS//F4ccca1NfZecPEtxgcHjKdDQZ3SYRAfb6Dc0 +jRuvoByAd3q9okOOr70gpMXgpoFVArDynaHMPK9xJ1w2p3s2/NhOYgY9f9rtcWTo +afoAcHK1jy5iQCogFUKt1bUCz5IsaYkRt+D+HQIDAQABoCEwHwYJKoZIhvcNAQkO +MRIwEDAOBgNVHREEBzAFggNmb28wDQYJKoZIhvcNAQELBQADggEBAOsv1UKwEbcY +8Fj2Pl55BjkqQG4PqSQdWJZfK0ol/GRty5XFaTgOUZyTeXOag84OGw0qM0E7kkUa +O5QwDOpnmIgg01Ywr4QM166l1iED+eOUscXJMonBAsS3JNYF1JxcDyKzIl/dt9+w +JXQ64uquuD57amOs8++ROfKW988HzXm0OnoHj8LZ1Mq2yUmxvnnfVnmMpZWo43sA +8NQs4v9dT5wLByFvBjcaWiGVZwZiwT4Q/Msskv9L0o1On0fgCJ6PjLYdblTwMHDZ +syH+X8SsUqeEmyvtiRc1XUeFbxS2hnPXJCXeyfljqwsBNGaVhBXcsV2Lg7IaloBF +/RyWqQZ44eE= +-----END CERTIFICATE REQUEST----- diff --git a/authority/provisioner/testdata/rsa.key b/authority/provisioner/testdata/rsa.key new file mode 100644 index 00000000..b5cf51dc --- /dev/null +++ b/authority/provisioner/testdata/rsa.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,e77ed7e2d2572b5a246a1c4b994190bb + +29o7UA/L7OF7inTPKkwBrtd8CJdXVQs9R3oJPitmFk8SZbrLHEiEhF1C0uB3xK3s +GWQ9O7bjERM3uAvQkd7MSkUUBpyPXS9GFacd85e85d1Ubl6miTAwkFrQnT9yn6n/ +Fak5JtkmdB6ObfVTioOwT1jdtGTifKg1bIhYISgwqCWhgV2fUFk6HQAAIMXTTRc+ +ZK1WbunT7LimYrnN3gQ4ylm/4C8nQl3JCGpvWZaRoH91q1LLD6IwWmX0D09F37dU +X3KoKv/GvuDlV3H1dUBwxhU+GI5/lItPp9OcdLZnnr67Gs+X+do3MFT1h675TM4N +c9QEIJB6RYatLBKHCS7j8W7EbuJAFZ+MCCapP92ERmVVVsWPY+V7CDVQxM2v4X/w +7C7JYx8b4xuQbdvu9KVU5irsXg8hBx7kDb/mtWjT4+8+sseLKA4oOmI6XwVMdbow +MciGilAIaNtWwQHe0EK9E9tiQfc9OyzxdrfplRckAAehHuPGU7+iMCsigCLT3aiV +CDHmnLdTXKIvGe8faTQoJphrb9F8bobGo5D4ZqX5f6gKuPIJsfd/r0GD8VNSF/Q7 +SJQMhkVyaixFB0gQbmea7sTyScdW+Qne7nLpam3ISgo+G4CAH8W88wLnuHMLmvoC +ZE3HvArSeQZ0WPHgB86AfoNRIxd6Emgb+dFyA6wPJC29nZkB8PFSrAHp0zp7KilF +fe9K2dVAUBZFhQthQIYAjJmYLCukLhxUALiqdSmQZrt6DSE33K8s5ed2KJu/60G6 +lZwIzQHPXesRhwmwbkfPB8CyWM+L6osdWv8QyMdM8Wb+66zkhKWBNbm+ccMfP6Zf +1ynF/a/DRX8bf81w+nvLsCGTdxVuEVEpuzS1NclKTmYQu58Ol0RgQe2JSxL89n+A +JAHUu9g9LcTg2jNPjxeA/vusSXMZRrPqrUCYhHhcgR4mE13uyyFI/9frk0gPpKXp +/FislMydWov2JRp1ixzypMBqlFR/zF6j6m3P1g7gchwScWzrZQHD58xdRin4Udiv +OR4huswh5v2i/0KozBoUAwbvPGERnMlTaGoBMPJ5Xe/jkBJw3uC3Dhi74uyUCjqU +hMQW4RJKmuiZVfAIX0RdgeUWXPs+8pf2pXrpIiVHCAHDrxXNMC7X6/9EcBN15B88 +W5/KIRngDeB2oVYrn1GfO7iLu1Rd8VFXyaVItOXq7WrL2pwm8ANhWcFDdnXf6jHW +BcKss1j8rZxOchksf+ZPXhn3QkdooD9iVONky1zLIsV5GPwMe8+yXwXznzJSbHH7 +dOfhK93fZqUwx4gFULwCuWIwLTfNmQ3VzdKioGt39RFDVQb+pbR7p9jv899VjsVO +TBBpRa00fvbK1H2CMVHnwwIf82M4XypSNGR/tSD3AImZPb5RfZnznoXXCMEfYVsd +8/Ry4GHusA+zxCjCxHFtXVkb9sklewJtnUmN5mUzo/81szuigLB5IADR21IOyVBq +A4kz96Ta885Z5owhonfZp1HD53pDEbxCuuIy+fgYfjfDSAj3L/QT3ZKrdcIdYQap +PhrNRW3j38koAatTLd3+E9KqBO5BiY+T5h+Q3XesWnaXInfu5WKiiEm5hHiejA0C +-----END RSA PRIVATE KEY----- diff --git a/authority/provisioner/testdata/short-rsa.csr b/authority/provisioner/testdata/short-rsa.csr new file mode 100644 index 00000000..abbd2ba9 --- /dev/null +++ b/authority/provisioner/testdata/short-rsa.csr @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBdDCB2wIBADAOMQwwCgYDVQQDEwNmb28wgaIwDQYJKoZIhvcNAQEBBQADgZAA +MIGMAoGEAK8dks7oV6kcIFEaWna7CDGYPAE8IL7rNi+ruQ1dIYz+JtxT7OPjbCn/ +t5iqni96+35iS/8CvMtEuquOMTMSWOWwlurrbTbLqCazuz/g233o8udxSxhny3cY +wHogp4cXCX6cFll6DeUnoCEuTTSIu8IBHbK48VfNw4V4gGz6cp/H93HrAgMBAAGg +ITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQHMAWCA2ZvbzANBgkqhkiG9w0BAQsF +AAOBhABCZsYM+Kgje68Z9Fjl2+cBwtQHvZDarh+cz6W1SchinZ1T0aNQvSj/otOe +ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG +2FbarLt9jN2aJLAYQPwtSeGTAZ74tLOPRPnTP6aMfFNg4XCR0uveHA== +-----END CERTIFICATE REQUEST----- diff --git a/authority/tls_test.go b/authority/tls_test.go index 142eedde..5e3a3746 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -7,6 +7,7 @@ import ( "crypto/x509/pkix" "encoding/asn1" "encoding/base64" + "encoding/pem" "fmt" "net/http" "reflect" @@ -203,6 +204,33 @@ func TestSign(t *testing.T) { }, } }, + "fail rsa key too short": func(t *testing.T) *signTest { + shortRSAKeyPEM := `-----BEGIN CERTIFICATE REQUEST----- +MIIBdDCB2wIBADAOMQwwCgYDVQQDEwNmb28wgaIwDQYJKoZIhvcNAQEBBQADgZAA +MIGMAoGEAK8dks7oV6kcIFEaWna7CDGYPAE8IL7rNi+ruQ1dIYz+JtxT7OPjbCn/ +t5iqni96+35iS/8CvMtEuquOMTMSWOWwlurrbTbLqCazuz/g233o8udxSxhny3cY +wHogp4cXCX6cFll6DeUnoCEuTTSIu8IBHbK48VfNw4V4gGz6cp/H93HrAgMBAAGg +ITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQHMAWCA2ZvbzANBgkqhkiG9w0BAQsF +AAOBhABCZsYM+Kgje68Z9Fjl2+cBwtQHvZDarh+cz6W1SchinZ1T0aNQvSj/otOe +ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG +2FbarLt9jN2aJLAYQPwtSeGTAZ74tLOPRPnTP6aMfFNg4XCR0uveHA== +-----END CERTIFICATE REQUEST-----` + block, _ := pem.Decode([]byte(shortRSAKeyPEM)) + assert.FatalError(t, err) + csr, err := x509.ParseCertificateRequest(block.Bytes) + assert.FatalError(t, err) + + return &signTest{ + auth: a, + csr: csr, + extraOpts: extraOpts, + signOpts: signOpts, + err: &apiError{errors.New("sign: rsa key in CSR must be at least 2048 bits (256 bytes)"), + http.StatusUnauthorized, + context{"csr": csr, "signOptions": signOpts}, + }, + } + }, "fail store cert in db": func(t *testing.T) *signTest { csr := getCSR(t, priv) _a := testAuthority(t) From d3e74a0d2e9e03c79c127a5e0c0873da2155356a Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 27 Aug 2019 16:39:48 -0700 Subject: [PATCH 06/15] switch from metalinter to golangci-lint --- .gitignore | 5 +++ .golangci.yml | 68 +++++++++++++++++++++++++++++++++++++ Gopkg.lock | 46 ------------------------- Gopkg.toml | 17 ---------- Makefile | 28 +++------------ authority/authority_test.go | 9 +++-- 6 files changed, 83 insertions(+), 90 deletions(-) create mode 100644 .golangci.yml diff --git a/.gitignore b/.gitignore index ed2ab99d..4ae6b5bd 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ coverage.txt vendor output + +# Ignore modules until switch from gopkg +go.mod +go.sum + diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..706a6b0b --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,68 @@ +linters-settings: + govet: + check-shadowing: true + settings: + printf: + funcs: + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + golint: + min-confidence: 0 + gocyclo: + min-complexity: 10 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + depguard: + list-type: blacklist + packages: + # logging is allowed only by logutils.Log, logrus + # is allowed to use only in logutils package + - github.com/sirupsen/logrus + misspell: + locale: US + lll: + line-length: 140 + goimports: + local-prefixes: github.com/golangci/golangci-lint + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + - dupImport # https://github.com/go-critic/go-critic/issues/845 + +linters: + disable-all: true + enable: + - gofmt + - golint + - vet + - misspell + - ineffassign + - deadcode + +run: + skip-dirs: + - pkg + +issues: + exclude: + - can't lint + - declaration of "err" shadows declaration at line + - should have a package comment, unless it's in another file for this package + - error strings should not be capitalized or end with punctuation or a newline +# golangci.com configuration +# https://github.com/golangci/golangci/wiki/Configuration +service: + golangci-lint-version: 1.17.x # use the fixed version to not introduce new linters unexpectedly + prepare: + - echo "here I can run custom commands, but no preparation needed for this repo" diff --git a/Gopkg.lock b/Gopkg.lock index ead1da09..dc947d29 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -9,13 +9,6 @@ pruneopts = "UT" revision = "e2d15f34fcf99d5dbb871c820ec73f710fca9815" -[[projects]] - digest = "1:304cb78c285eaf02ab529ad02a257cad9b4845022915e6c82f87860ac53222d8" - name = "github.com/alecthomas/gometalinter" - packages = ["."] - pruneopts = "UT" - revision = "bae2f1293d092fd8167939d5108d1b025eaef9de" - [[projects]] branch = "master" digest = "1:c198fdc381e898e8fb62b8eb62758195091c313ad18e52a3067366e1dda2fb3c" @@ -32,17 +25,6 @@ pruneopts = "UT" revision = "2972be24d48e78746da79ba8e24e8b488c9880de" -[[projects]] - digest = "1:848ef40f818e59905140552cc49ff3dc1a15f955e4b56d1c5c2cc4b54dbadf0c" - name = "github.com/client9/misspell" - packages = [ - ".", - "cmd/misspell", - ] - pruneopts = "UT" - revision = "b90dc15cfd220ecf8bbc9043ecb928cef381f011" - version = "v0.3.4" - [[projects]] digest = "1:21ac9938fb1098b3a7b0dd909fb30878d33231177fac11a2821114eb9c1088ff" name = "github.com/dgraph-io/badger" @@ -82,13 +64,6 @@ revision = "72cd26f257d44c1114970e19afddcd812016007e" version = "v1.4.1" -[[projects]] - digest = "1:4ee452f8994700dcab9e816aef1cb9eb2317218734c6ccf5135746e6c19f3dce" - name = "github.com/golang/lint" - packages = ["golint"] - pruneopts = "UT" - revision = "06c8688daad7faa9da5a0c2f163a3d14aac986ca" - [[projects]] digest = "1:97df918963298c287643883209a2c3f642e6593379f97ab400c2a2e219ab647d" name = "github.com/golang/protobuf" @@ -105,14 +80,6 @@ pruneopts = "UT" revision = "6f45313302b9c56850fc17f99e40caebce98c716" -[[projects]] - branch = "master" - digest = "1:824d147914b40e56e9e1eebd602bc6bb9761989d52fd8e4a498428467980eb17" - name = "github.com/gordonklaus/ineffassign" - packages = ["."] - pruneopts = "UT" - revision = "1003c8bd00dc2869cb5ca5282e6ce33834fed514" - [[projects]] branch = "master" digest = "1:e51f40f0c19b39c1825eadd07d5c0a98a2ad5942b166d9fc4f54750ce9a04810" @@ -314,14 +281,6 @@ pruneopts = "UT" revision = "a0934e12468769d8cbede3ed316c47a4b88de4ca" -[[projects]] - branch = "master" - digest = "1:ba52e5a5fb800ce55108b7a5f181bb809aab71c16736051312b0aa969f82ad39" - name = "github.com/tsenart/deadcode" - packages = ["."] - pruneopts = "UT" - revision = "210d2dc333e90c7e3eedf4f2242507a8e83ed4ab" - [[projects]] branch = "master" digest = "1:6743b69de0d73e91004e4e201cf4965b59a0fa5caf6f0ffbe0cb9ee8807738a7" @@ -472,11 +431,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ - "github.com/alecthomas/gometalinter", - "github.com/client9/misspell/cmd/misspell", "github.com/go-chi/chi", - "github.com/golang/lint/golint", - "github.com/gordonklaus/ineffassign", "github.com/newrelic/go-agent", "github.com/pkg/errors", "github.com/rs/xid", @@ -495,7 +450,6 @@ "github.com/smallstep/cli/usage", "github.com/smallstep/nosql", "github.com/smallstep/nosql/database", - "github.com/tsenart/deadcode", "github.com/urfave/cli", "golang.org/x/crypto/ocsp", "golang.org/x/net/http2", diff --git a/Gopkg.toml b/Gopkg.toml index 944ed32a..8937374b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -23,23 +23,6 @@ # non-go = false # go-tests = true # unused-packages = true - -required = [ - "github.com/alecthomas/gometalinter", - "github.com/golang/lint/golint", - "github.com/client9/misspell/cmd/misspell", - "github.com/gordonklaus/ineffassign", - "github.com/tsenart/deadcode", -] - -[[constraint]] - name = "github.com/golang/lint" - revision = "06c8688daad7faa9da5a0c2f163a3d14aac986ca" - -[[constraint]] - name = "github.com/alecthomas/gometalinter" - revision = "bae2f1293d092fd8167939d5108d1b025eaef9de" - [[override]] name = "gopkg.in/alecthomas/kingpin.v3-unstable" revision = "63abe20a23e29e80bbef8089bd3dee3ac25e5306" diff --git a/Makefile b/Makefile index 8b116ddd..164cab35 100644 --- a/Makefile +++ b/Makefile @@ -22,25 +22,18 @@ all: build test lint bootstra%: $Q which dep || go get github.com/golang/dep/cmd/dep $Q dep ensure + $Q GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.17.1 + vendor: Gopkg.lock $Q dep ensure -BOOTSTRAP=\ - github.com/golang/lint/golint \ - github.com/client9/misspell/cmd/misspell \ - github.com/gordonklaus/ineffassign \ - github.com/tsenart/deadcode \ - github.com/alecthomas/gometalinter - define VENDOR_BIN_TMPL vendor/bin/$(notdir $(1)): vendor $Q go build -o $$@ ./vendor/$(1) VENDOR_BINS += vendor/bin/$(notdir $(1)) endef -$(foreach pkg,$(BOOTSTRAP),$(eval $(call VENDOR_BIN_TMPL,$(pkg)))) - .PHONY: bootstra% vendor ################################################# @@ -126,24 +119,11 @@ integration: bin/$(BINNAME) # Linting ######################################### -LINTERS=\ - gofmt \ - golint \ - vet \ - misspell \ - ineffassign \ - deadcode - -$(patsubst %,%-bin,$(filter-out gofmt vet,$(LINTERS))): %-bin: vendor/bin/% -gofmt-bin vet-bin: - -$(LINTERS): %: vendor/bin/gometalinter %-bin vendor - $Q PATH=`pwd`/vendor/bin:$$PATH gometalinter --tests --disable-all --vendor \ - --deadline=5m -s data -s pkg --enable $@ ./... fmt: $Q gofmt -l -w $(SRC) -lint: $(LINTERS) +lint: + $Q LOG_LEVEL=error golangci-lint run .PHONY: $(LINTERS) lint fmt diff --git a/authority/authority_test.go b/authority/authority_test.go index 30ee3121..ee517517 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -124,11 +124,14 @@ func TestAuthorityNew(t *testing.T) { assert.True(t, auth.initOnce) assert.NotNil(t, auth.intermediateIdentity) for _, p := range tc.config.AuthorityConfig.Provisioners { - _p, ok := auth.provisioners.Load(p.GetID()) + var _p provisioner.Interface + _p, ok = auth.provisioners.Load(p.GetID()) assert.True(t, ok) assert.Equals(t, p, _p) - if kid, encryptedKey, ok := p.GetEncryptedKey(); ok { - key, ok := auth.provisioners.LoadEncryptedKey(kid) + var kid, encryptedKey string + if kid, encryptedKey, ok = p.GetEncryptedKey(); ok { + var key string + key, ok = auth.provisioners.LoadEncryptedKey(kid) assert.True(t, ok) assert.Equals(t, encryptedKey, key) } From 7c9ab9814fb676cb3c125c3dac4893271f1b7ae5 Mon Sep 17 00:00:00 2001 From: Michael Malone Date: Wed, 28 Aug 2019 11:58:53 -0700 Subject: [PATCH 07/15] Updated README with features & a bunch of cleanup --- README.md | 314 +++++++++++++++++++++++++++++------------------------- 1 file changed, 167 insertions(+), 147 deletions(-) diff --git a/README.md b/README.md index 3a6e9170..a1806889 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ An online certificate authority and related tools for secure automated certificate management, so you can use TLS everywhere. +This repository is for `step-ca`, a certificate authority that exposes an API for automated certificate management. It also contains a [golang SDK](https://github.com/smallstep/certificates/tree/master/examples#basic-client-usage) for interacting with `step-ca` programatically. However, you'll probably want to use the [`step` command-line tool](https://github.com/smallstep/cli) to operate `step-ca` and get certificates, instead of using this low-level SDK directly. + +**Questions? Find us [on gitter](https://gitter.im/smallstep/community).** + [Website](https://smallstep.com) | [Documentation](#documentation) | [Installation Guide](#installation-guide) | @@ -21,6 +25,38 @@ An online certificate authority and related tools for secure automated certifica ![Animated terminal showing step certificates in practice](https://github.com/smallstep/certificates/raw/master/docs/images/step-ca-2-legged.gif) +## Features + +It's super easy to get started and to operate `step-ca` thanks to [streamlined initialization](https://github.com/smallstep/certificates#lets-get-started) and [safe, sane defaults](https://github.com/smallstep/certificates/blob/master/docs/defaults.md). **Get started in 15 minutes.** + +### A private certificate authority you run yourself + +- Issue client and server certificates to VMs, containers, devices, and people using internal hostnames and emails +- [RFC5280](https://tools.ietf.org/html/rfc5280) and [CA/Browser Forum](https://cabforum.org/baseline-requirements-documents/) compliant certificates that work **for TLS and HTTPS** (SSH coming soon!) +- Choose key types (RSA, ECDSA, EdDSA) & lifetimes to suit your needs +- [Short-lived certificates](https://smallstep.com/blog/passive-revocation.html) with **fully automated** enrollment, renewal, and revocation +- Fast, stable, and capable of high availability deployment using [root federation](https://smallstep.com/blog/step-v0.8.3-federation-root-rotation.html) and/or multiple intermediaries +- Operate as an online intermediate for an existing root CA +- [Pluggable database backends](https://github.com/smallstep/certificates/blob/master/docs/database.md) for persistence +- [Helm charts](https://hub.helm.sh/charts/smallstep/step-certificates), [autocert](https://github.com/smallstep/autocert), and [cert-manager integration](https://github.com/smallstep/step-issuer) for kubernetes + +### Lots of (automatable) ways to get certificates + +- [Single sign-on](https://smallstep.com/blog/easily-curl-services-secured-by-https-tls.html) using Okta, GSuite, Active Directory, or any other OAuth OIDC identity provider +- Instance identity documents for VMs on AWS, GCP, and Azure +- [Single-use short-lived tokens](https://smallstep.com/docs/design-doc.html#jwk-provisioner) issued by your CD tool β€” Puppet, Chef, Ansible, Terraform, etc. +- Use an existing certificate from another CA (e.g., using a device certificate like [Twilio's Trust OnBoard](https://www.twilio.com/wireless/trust-onboard)) *coming soon* +- ACMEv2 (RFC8555) support so you can **run your own private ACME server** *[coming soon](https://github.com/smallstep/certificates/tree/acme)* + +### Easy certificate management and automation via [`step` CLI](https://github.com/smallstep/cli) [integration](https://smallstep.com/docs/cli/ca/) + +- Generate key pairs where they're needed so private keys are never transmitted across the network +- [Authenticate and obtain a certificate](https://smallstep.com/docs/cli/ca/certificate/) using any enrollment mechanism supported by `step-ca` +- Securely [distribute root certificates](https://smallstep.com/docs/cli/ca/root/) and [bootstrap](https://smallstep.com/docs/cli/ca/bootstrap/) PKI relying parties +- [Renew](https://smallstep.com/docs/cli/ca/renew/) and [revoke](https://smallstep.com/docs/cli/ca/revoke/) certificates issued by `step-ca` +- [Install root certificates](https://smallstep.com/docs/cli/certificate/install/) so your CA is trusted by default (issue development certificates **that [work in browsers](https://smallstep.com/blog/step-v0-8-6-valid-HTTPS-certificates-for-dev-pre-prod.html)**) +- [Inspect](https://smallstep.com/docs/cli/certificate/inspect/) and [lint](https://smallstep.com/docs/cli/certificate/lint/) certificates + ## Motivation Managing your own *public key infrastructure* (PKI) can be tedious and error @@ -49,32 +85,20 @@ need. makes it much easier to implement good security practices early, and incrementally improve them as your system matures. -For more information and docs see [the Step +For more information and [docs](https://smallstep.com/docs) see [the smallstep website](https://smallstep.com/certificates) and the [blog -post](https://smallstep.com/blog/step-certificates.html) announcing Step -Certificate Authority. - -> ## πŸ†• Autocert -> -> If you're using Kubernetes, make sure you [check out -> autocert](https://github.com/smallstep/autocert): a kubernetes add-on that builds on `step -> certificates` to automatically inject TLS/HTTPS certificates into your containers. +post](https://smallstep.com/blog/step-certificates.html) announcing this project. ## Installation Guide These instructions will install an OS specific version of the `step-ca` binary on your local machine. -> NOTE: While `step` is not required to run the Step Certificate Authority (CA) -> we strongly recommend installing both `step cli` and `step certificates` -> because the Step CA is much easier to initialize, manage, and debug using -> the `step cli` toolkit. +While `step` is not required to run `step-ca`, it will make your life easier so you'll probably want to [install it](https://github.com/smallstep/cli#installation-guide) too. ### Mac OS -Install `step` via [Homebrew](https://brew.sh/). The -[Homebrew Formula](https://github.com/Homebrew/homebrew-core/blob/master/Formula/step.rb) -installs both `step cli` and `step certificates`. +Install `step` and `step-ca` together via [Homebrew](https://brew.sh/):

 $ brew install step
@@ -101,10 +125,10 @@ $ brew uninstall step
 
 #### Debian
 
-1. [Optional] Install `step cli`.
+1. [Optional] Install `step`.
 
     Download the latest Debian package from
-    [`step cli` releases](https://github.com/smallstep/cli/releases):
+    [`step` releases](https://github.com/smallstep/cli/releases):
 
     ```
     $ wget https://github.com/smallstep/cli/releases/download/X.Y.Z/step_X.Y.Z_amd64.deb
@@ -116,10 +140,9 @@ $ brew uninstall step
     $ sudo dpkg -i step_X.Y.Z_amd64.deb
     ```
 
-2. Install `step certificates`.
+2. Install `step-ca`.
 
-    Download the latest Debian package from
-    [`step certificates` releases](https://github.com/smallstep/certificates/releases):
+    Download the latest Debian package from [releases](https://github.com/smallstep/certificates/releases):
 
     ```
     $ wget https://github.com/smallstep/certificates/releases/download/X.Y.Z/step-certificates_X.Y.Z_amd64.deb
@@ -136,29 +159,43 @@ $ brew uninstall step
 We are using the [Arch User Repository](https://aur.archlinux.org) to distribute
 `step` binaries for Arch Linux.
 
-* [Optional] The `step-cli` binary tarball can be found [here](https://aur.archlinux.org/packages/step-cli-bin/).
+* [Optional] The `step` binary tarball can be found [here](https://aur.archlinux.org/packages/step-cli-bin/).
 * The `step-ca` binary tarball can be found [here](https://aur.archlinux.org/packages/step-ca-bin/).
 
 You can use [pacman](https://www.archlinux.org/pacman/) to install the packages.
 
+### Kubernetes
+
+We publish [helm charts](https://hub.helm.sh/charts/smallstep/step-certificates) for easy installation on kubernetes:
+
+```
+helm install step-certificates
+```
+
+> 
+>
+> If you're using Kubernetes, make sure you [check out
+> autocert](https://github.com/smallstep/autocert): a kubernetes add-on that builds on `step
+> certificates` to automatically inject TLS/HTTPS certificates into your containers.
+
 ### Test
 
 

 $ step version
-Smallstep CLI/0.8.5 (darwin/amd64)
-Release Date: 2019-02-13 22:17 UTC
+Smallstep CLI/0.10.0 (darwin/amd64)
+Release Date: 2019-04-30 19:01 UTC
 
 $ step-ca version
-Smallstep CA/0.8.4 (darwin/amd64)
-Release Date: 2019-02-18 18:56 UTC
+Smallstep CA/0.10.0 (darwin/amd64)
+Release Date: 2019-04-30 19:02 UTC
 
## Quickstart In the following guide we'll run a simple `hello` server that requires clients -to connect over an authorized and encrypted channel (HTTP over TLS). The Step -Certificate Authority (CA) will issue an identity dial tone to our server -enabling it to authenticate and encrypt communication. Let's get started! +to connect over an authorized and encrypted channel using HTTPS. `step-ca` +will issue certificates to our server, allowing it to authenticate and encrypt +communication. Let's get started! ### Prerequisites @@ -167,152 +204,135 @@ enabling it to authenticate and encrypt communication. Let's get started! ### Let's get started! -1. Initialize and run the Step CA. +#### 1. Run `step ca init` to create your CA's keys & certificates and configure `step-ca`: - `step ca init` initializes the CA and accomplishes two tasks. +

+$ step ca init
+βœ” What would you like to name your new PKI? (e.g. Smallstep): Example Inc.
+βœ” What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
+βœ” What address will your new CA listen at? (e.g. :443): 127.0.0.1:8080
+βœ” What would you like to name the first provisioner for your new CA? (e.g. you@smallstep.com): bob@example.com
+βœ” What do you want your password to be? [leave empty and we'll generate one]: abc123
 
-    1. Generate a Public Key Infrastructure (PKI) with Root and Intermediate
-X.509 Certificates and private keys.
+Generating root certificate...
+all done!
 
-       The root X.509 Certificate is a fancy public key that will be
-       distributed to clients enabling them to authenticate all certificates
-       generated by your PKI. The root private key should be kept in a very
-       private place - but as this is just a demo we won't worry about that
-       right now ([more info on storing sensitive
-       data](./docs/GETTING_STARTED.md#passwords)). The intermediate
-       private key will be used to sign new certificates ([Why is it more
-       secure to use intermediate CA
-       certificates?](https://security.stackexchange.com/questions/128779/why-is-it-more-secure-to-use-intermediate-ca-certificates))
-       and the intermediate certificate will be distributed along with newly
-       minted leaf certificates. In our demo, the server will present the
-       intermediate certificate along with it's *server* (leaf) certificate
-       allowing our client to validate the full chain using the root.
+Generating intermediate certificate...
+all done!
 
-    2. Generate the configuration file required by the Step CA.
+βœ” Root certificate: /Users/bob/src/github.com/smallstep/step/.step/certs/root_ca.crt
+βœ” Root private key: /Users/bob/src/github.com/smallstep/step/.step/secrets/root_ca_key
+βœ” Root fingerprint: 702a094e239c9eec6f0dcd0a5f65e595bf7ed6614012825c5fe3d1ae1b2fd6ee
+βœ” Intermediate certificate: /Users/bob/src/github.com/smallstep/step/.step/certs/intermediate_ca.crt
+βœ” Intermediate private key: /Users/bob/src/github.com/smallstep/step/.step/secrets/intermediate_ca_key
+βœ” Default configuration: /Users/bob/src/github.com/smallstep/step/.step/config/defaults.json
+βœ” Certificate Authority configuration: /Users/bob/src/github.com/smallstep/step/.step/config/ca.json
 
-       See the [Getting Started](./docs/GETTING_STARTED.md) guide for an in depth
-       explanation of the Step CA configuration file.
+Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
+
-

-    $ step ca init
-    βœ” What would you like to name your new PKI? (e.g. Smallstep): Example Inc.
-    βœ” What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
-    βœ” What address will your new CA listen at? (e.g. :443): 127.0.0.1:8080
-    βœ” What would you like to name the first provisioner for your new CA? (e.g. you@smallstep.com): bob@example.com
-    βœ” What do you want your password to be? [leave empty and we'll generate one]: abc123
+This command will:
 
-    Generating root certificate...
-    all done!
+- Generate [password protected](https://github.com/smallstep/certificates/blob/master/docs/GETTING_STARTED.md#passwords)  private keys for your CA to sign certificates
+- Generate a root and [intermediate signing certificate](https://security.stackexchange.com/questions/128779/why-is-it-more-secure-to-use-intermediate-ca-certificates) for your CA
+- Create a JSON configuration file for `step-ca` (see [getting started](./docs/GETTING_STARTED.md) for details)
 
-    Generating intermediate certificate...
-    all done!
+You can find these artifacts in `$STEPPATH` (or `~/.step` by default).
 
-    βœ” Root certificate: /Users/bob/src/github.com/smallstep/step/.step/certs/root_ca.crt
-    βœ” Root private key: /Users/bob/src/github.com/smallstep/step/.step/secrets/root_ca_key
-    βœ” Root fingerprint: 702a094e239c9eec6f0dcd0a5f65e595bf7ed6614012825c5fe3d1ae1b2fd6ee
-    βœ” Intermediate certificate: /Users/bob/src/github.com/smallstep/step/.step/certs/intermediate_ca.crt
-    βœ” Intermediate private key: /Users/bob/src/github.com/smallstep/step/.step/secrets/intermediate_ca_key
-    βœ” Default configuration: /Users/bob/src/github.com/smallstep/step/.step/config/defaults.json
-    βœ” Certificate Authority configuration: /Users/bob/src/github.com/smallstep/step/.step/config/ca.json
+#### 2. Start `step-ca`:
 
-    Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
+You'll be prompted for your password from the previous step, to decrypt the CA's private signing key:
 
-    $ step-ca $(step path)/config/ca.json
-    Please enter the password to decrypt /Users/bob/src/github.com/smallstep/step/.step/secrets/intermediate_ca_key: abc123
-    2019/02/18 13:28:58 Serving HTTPS on 127.0.0.1:8080 ...
-    
+

+$ step-ca $(step path)/config/ca.json
+Please enter the password to decrypt /Users/bob/src/github.com/smallstep/step/.step/secrets/intermediate_ca_key: abc123
+2019/02/18 13:28:58 Serving HTTPS on 127.0.0.1:8080 ...
+
- Now we've got an 'up and running' online CA! +#### 3. Copy our `hello world` golang server. -2. Copy our `hello world` golang server. +``` +$ cat > srv.go < srv.go < +$ step ca certificate localhost srv.crt srv.key +βœ” Key ID: rQxROEr7Kx9TNjSQBTETtsu3GKmuW9zm02dMXZ8GUEk (bob@example.com) +βœ” Please enter the password to decrypt the provisioner key: abc123 +βœ” CA: https://localhost:8080/1.0/sign +βœ” Certificate: srv.crt +βœ” Private Key: srv.key -

-    $ step ca certificate localhost srv.crt srv.key
-    βœ” Key ID: rQxROEr7Kx9TNjSQBTETtsu3GKmuW9zm02dMXZ8GUEk (bob@example.com)
-    βœ” Please enter the password to decrypt the provisioner key: abc123
-    βœ” CA: https://localhost:8080/1.0/sign
-    βœ” Certificate: srv.crt
-    βœ” Private Key: srv.key
+$ step certificate inspect --bundle srv.crt
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 140439335711218707689123407681832384336 (0x69a7a1d7f6f22f68059d2d9088307750)
+    Signature Algorithm: ECDSA-SHA256
+        Issuer: CN=Example Inc. Intermediate CA
+        Validity
+            Not Before: Feb 18 21:32:35 2019 UTC
+            Not After : Feb 19 21:32:35 2019 UTC
+        Subject: CN=localhost
+...
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 207035091234452090159026162349261226844 (0x9bc18217bd560cf07db23178ed90835c)
+    Signature Algorithm: ECDSA-SHA256
+        Issuer: CN=Example Inc. Root CA
+        Validity
+            Not Before: Feb 18 21:27:21 2019 UTC
+            Not After : Feb 15 21:27:21 2029 UTC
+        Subject: CN=Example Inc. Intermediate CA
+...
+
- $ step certificate inspect --bundle srv.crt - Certificate: - Data: - Version: 3 (0x2) - Serial Number: 140439335711218707689123407681832384336 (0x69a7a1d7f6f22f68059d2d9088307750) - Signature Algorithm: ECDSA-SHA256 - Issuer: CN=Example Inc. Intermediate CA - Validity - Not Before: Feb 18 21:32:35 2019 UTC - Not After : Feb 19 21:32:35 2019 UTC - Subject: CN=localhost - ... - Certificate: - Data: - Version: 3 (0x2) - Serial Number: 207035091234452090159026162349261226844 (0x9bc18217bd560cf07db23178ed90835c) - Signature Algorithm: ECDSA-SHA256 - Issuer: CN=Example Inc. Root CA - Validity - Not Before: Feb 18 21:27:21 2019 UTC - Not After : Feb 15 21:27:21 2029 UTC - Subject: CN=Example Inc. Intermediate CA - ... -
+Note that `step` and `step-ca` handle details like [certificate bundling](https://smallstep.com/blog/everything-pki.html#intermediates-chains-and-bundling) for you. - Notice that when you inspect `srv.crt` there are actually two certificates - present. The first is your **server** (leaf) certificate and the second is - the intermediate certificate. When an intermediate CA is used to sign - **leaf** certificates it is not enough for the server to only show it's - **leaf** certificate because the client (which only has access to the root - certificate) will not be able to validate the full chain. +#### 5. Run the simple server. -4. Run the simple server. +

+$ go run srv.go &
+
-

-    $ go run srv.go &
-    
+#### 6. Get the root certificate from the Step CA. -5. Get the root certificate from the Step CA. +In a new Terminal window: - In a new Terminal window: +

+$ step ca root root.crt
+The root certificate has been saved in root.crt.
+
-

-    $ step ca root root.crt
-    The root certificate has been saved in root.crt.
-    
+#### 7. Make an authenticated, encrypted curl request to your server using HTTP over TLS. -6. Make an authenticated, encrypted curl request to your server using HTTP over TLS. - -

-    $ curl --cacert root.crt https://localhost:8443/hi
-    Hello, world!
-    
+

+$ curl --cacert root.crt https://localhost:8443/hi
+Hello, world!
+
*All Done!* From 9200f11ed8cfc0c5d4e512591c215633943d978c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 28 Aug 2019 13:15:38 +0200 Subject: [PATCH 08/15] Skip unsupported provisioners. --- authority/provisioner/provisioner.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 711b0439..05d04031 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -150,7 +150,11 @@ func (l *List) UnmarshalJSON(data []byte) error { case "azure": p = &Azure{} default: - return errors.Errorf("provisioner type %s not supported", typ.Type) + // Skip unsupported files. When this method is specially used on + // clients, these might be compiled with a version that does not + // support a specific provisioner type, if we don't skip it we will + // force to re-compile the client to make it compatible. + continue } if err := json.Unmarshal(data, p); err != nil { return errors.Errorf("error unmarshaling provisioner") From ca8daf5f12b50c26293c37fdc71efeaf9ed475c7 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 28 Aug 2019 17:28:03 -0700 Subject: [PATCH 09/15] Update comment and warn --- Gopkg.lock | 68 ++-------------------------- authority/provisioner/provisioner.go | 14 ++++-- 2 files changed, 15 insertions(+), 67 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index dc947d29..a8a79842 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -9,14 +9,6 @@ pruneopts = "UT" revision = "e2d15f34fcf99d5dbb871c820ec73f710fca9815" -[[projects]] - branch = "master" - digest = "1:c198fdc381e898e8fb62b8eb62758195091c313ad18e52a3067366e1dda2fb3c" - name = "github.com/alecthomas/units" - packages = ["."] - pruneopts = "UT" - revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" - [[projects]] branch = "master" digest = "1:454adc7f974228ff789428b6dc098638c57a64aa0718f0bd61e53d3cd39d7a75" @@ -72,14 +64,6 @@ revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" version = "v1.2.0" -[[projects]] - branch = "master" - digest = "1:750e747d0aad97b79f4a4e00034bae415c2ea793fd9e61438d966ee9c79579bf" - name = "github.com/google/shlex" - packages = ["."] - pruneopts = "UT" - revision = "6f45313302b9c56850fc17f99e40caebce98c716" - [[projects]] branch = "master" digest = "1:e51f40f0c19b39c1825eadd07d5c0a98a2ad5942b166d9fc4f54750ce9a04810" @@ -108,7 +92,7 @@ revision = "2d01aacdc34a083dca635ba869909f5fc0cd4f41" [[projects]] - digest = "1:2a2a76072bd413b3484a0b5bb2fbb078b0b7dd8950e9276c900e14dce2354679" + digest = "1:2d2bc0f23cca6b59cec3fbece9abc102bdb19f548dd58d7667e57699074a2c76" name = "github.com/manifoldco/promptui" packages = [ ".", @@ -116,8 +100,8 @@ "screenbuf", ] pruneopts = "UT" - revision = "20f2a94120aa14a334121a6de66616a7fa89a5cd" - version = "v0.3.2" + revision = "157c96fb638a14d268b305cf2012582431fcc410" + version = "v0.3.1" [[projects]] digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" @@ -173,27 +157,6 @@ revision = "f5bce3387232559bcbe6a5f8227c4bf508dac1ba" version = "v1.11.0" -[[projects]] - digest = "1:07140002dbf37da92090f731b46fa47be4820b82fe5c14a035203b0e813d0ec2" - name = "github.com/nicksnyder/go-i18n" - packages = [ - "i18n", - "i18n/bundle", - "i18n/language", - "i18n/translation", - ] - pruneopts = "UT" - revision = "0dc1626d56435e9d605a29875701721c54bc9bbd" - version = "v1.10.0" - -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - [[projects]] digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" name = "github.com/pkg/errors" @@ -313,14 +276,6 @@ pruneopts = "UT" revision = "4d3f4d9ffa16a13f451c3b2999e9c49e9750bf06" -[[projects]] - branch = "master" - digest = "1:0824a2eb250dd552f41daaf471266d085e8e8cabdbff9e4294bc799076e00da7" - name = "golang.org/x/lint" - packages = ["."] - pruneopts = "UT" - revision = "959b441ac422379a43da2230f62be024250818b0" - [[projects]] branch = "master" digest = "1:2f7468b0b3fd7d926072f0dcbb6ec81e337278b4e5de639d017e54f785f0b475" @@ -399,13 +354,6 @@ revision = "54a98f90d1c46b7731eb8fb305d2a321c30ef610" version = "v1.5.0" -[[projects]] - digest = "1:39efb07a0d773dc09785b237ada4e10b5f28646eb6505d97bc18f8d2ff439362" - name = "gopkg.in/alecthomas/kingpin.v3-unstable" - packages = ["."] - pruneopts = "UT" - revision = "63abe20a23e29e80bbef8089bd3dee3ac25e5306" - [[projects]] digest = "1:9593bab40e981b1f90b7e07faeab0d09b75fe338880d08880f986a9d3283c53f" name = "gopkg.in/square/go-jose.v2" @@ -419,14 +367,6 @@ revision = "730df5f748271903322feb182be83b43ebbbe27d" version = "v2.3.1" -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - [solve-meta] analyzer-name = "dep" analyzer-version = 1 @@ -445,12 +385,14 @@ "github.com/smallstep/cli/crypto/x509util", "github.com/smallstep/cli/errs", "github.com/smallstep/cli/jose", + "github.com/smallstep/cli/pkg/x509", "github.com/smallstep/cli/token", "github.com/smallstep/cli/token/provision", "github.com/smallstep/cli/usage", "github.com/smallstep/nosql", "github.com/smallstep/nosql/database", "github.com/urfave/cli", + "golang.org/x/crypto/ed25519", "golang.org/x/crypto/ocsp", "golang.org/x/net/http2", "gopkg.in/square/go-jose.v2", diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 05d04031..c6c53455 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -3,6 +3,7 @@ package provisioner import ( "crypto/x509" "encoding/json" + "log" "net/url" "strings" @@ -150,10 +151,15 @@ func (l *List) UnmarshalJSON(data []byte) error { case "azure": p = &Azure{} default: - // Skip unsupported files. When this method is specially used on - // clients, these might be compiled with a version that does not - // support a specific provisioner type, if we don't skip it we will - // force to re-compile the client to make it compatible. + // Skip unsupported provisioners. A client using this method may be + // compiled with a version of smallstep/certificates that does not + // support a specific provisioner type. If we don't skip unknown + // provisioners, a client encountering an unknown provisioner will + // break. Rather than break the client, we skip the provisioner but + // warn the user that an unknown provisioner was found and suggest + // that the user update their client's dependency on + // step/certificates and recompile. + log.Printf("[WARN] Unknown provisioner of type '%s' found. Please update your client.", typ.Type) continue } if err := json.Unmarshal(data, p); err != nil { From ac234771c79db3e54dcc306adca249df72f386ae Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 29 Aug 2019 10:49:52 -0700 Subject: [PATCH 10/15] Remove unknown provisioner WARNning and leave TODO --- authority/provisioner/provisioner.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index c6c53455..3118e6b0 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -3,7 +3,6 @@ package provisioner import ( "crypto/x509" "encoding/json" - "log" "net/url" "strings" @@ -155,11 +154,11 @@ func (l *List) UnmarshalJSON(data []byte) error { // compiled with a version of smallstep/certificates that does not // support a specific provisioner type. If we don't skip unknown // provisioners, a client encountering an unknown provisioner will - // break. Rather than break the client, we skip the provisioner but + // break. Rather than break the client, we skip the provisioner. + // TODO: accept a pluggable logger (depending on client) that can // warn the user that an unknown provisioner was found and suggest // that the user update their client's dependency on // step/certificates and recompile. - log.Printf("[WARN] Unknown provisioner of type '%s' found. Please update your client.", typ.Type) continue } if err := json.Unmarshal(data, p); err != nil { From 8516ea2fc6b33ae59bc0a731e2c8a52c7d914931 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 29 Aug 2019 10:57:16 -0700 Subject: [PATCH 11/15] dep update cli --- Gopkg.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index a8a79842..0b976ddd 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -207,7 +207,7 @@ [[projects]] branch = "master" - digest = "1:130790954c8862bf8c2f635e220caf94cb8c72cc3957d0e2f1892e5b93f5e0e1" + digest = "1:c986e637991c8d7a02675fd89a3e3452af6ec390d4d9dd3c4393949effc9a626" name = "github.com/smallstep/cli" packages = [ "command", @@ -228,7 +228,7 @@ "utils", ] pruneopts = "UT" - revision = "60c9e9cea2e247ad90b11ce5b5c4639dfc783eff" + revision = "8012d7ee6e6759ca411fbf66feb87cee96272b62" [[projects]] branch = "master" From e3bd2d0e2b07edf70ff12f5dfcb9f68ad848127b Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 29 Aug 2019 15:58:07 -0700 Subject: [PATCH 12/15] Custom AppHelpTemplate for step-ca --- cmd/step-ca/main.go | 62 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index 2513b552..fbb0f2b4 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -4,6 +4,7 @@ import ( "bytes" "flag" "fmt" + "html" "io/ioutil" "log" "net/http" @@ -11,6 +12,7 @@ import ( "reflect" "regexp" "runtime" + "strconv" "time" "unicode" @@ -54,12 +56,64 @@ func printFullVersion() { fmt.Printf("Release Date: %s\n", releaseDate()) } +// appHelpTemplate contains the modified template for the main app +var appHelpTemplate = `## NAME +**{{.HelpName}}** -- {{.Usage}} + +## USAGE +{{if .UsageText}}{{.UsageText}}{{else}}**{{.HelpName}}**{{if .Commands}} {{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}_[arguments]_{{end}}{{end}}{{if .Description}} + +## DESCRIPTION +{{.Description}}{{end}}{{if .VisibleCommands}} + +## COMMANDS + +{{range .VisibleCategories}}{{if .Name}}{{.Name}}:{{end}} +||| +|---|---|{{range .VisibleCommands}} +| **{{join .Names ", "}}** | {{.Usage}} |{{end}} +{{end}}{{if .VisibleFlags}}{{end}} + +## OPTIONS + +{{range $index, $option := .VisibleFlags}}{{if $index}} +{{end}}{{$option}} +{{end}}{{end}}{{if .Copyright}}{{if len .Authors}} + +## AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + +{{range $index, $author := .Authors}}{{if $index}} +{{end}}{{$author}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + +## ONLINE + +This documentation is available online at https://smallstep.com/docs/certificates + +## VERSION + +{{.Version}}{{end}}{{end}} + +## COPYRIGHT + +{{.Copyright}} + +## FEEDBACK ` + + html.UnescapeString("&#"+strconv.Itoa(128525)+";") + " " + + html.UnescapeString("&#"+strconv.Itoa(127867)+";") + + ` + +The **step-ca** utility is not instrumented for usage statistics. It does not phone home. +But your feedback is extremely valuable. Any information you can provide regarding how you’re using **step-ca** helps. +Please send us a sentence or two, good or bad: **feedback@smallstep.com** or join https://gitter.im/smallstep/community. +{{end}} +` + func main() { // Override global framework components cli.VersionPrinter = func(c *cli.Context) { printFullVersion() } - cli.AppHelpTemplate = usage.AppHelpTemplate + cli.AppHelpTemplate = appHelpTemplate cli.SubcommandHelpTemplate = usage.SubcommandHelpTemplate cli.CommandHelpTemplate = usage.CommandHelpTemplate cli.HelpPrinter = usage.HelpPrinter @@ -118,7 +172,7 @@ intermediate private key.`, app.Commands = []cli.Command{ { Name: "version", - Usage: "Displays the current version of the cli", + Usage: "Displays the current version of step-ca", // Command prints out the current version of the tool Action: func(c *cli.Context) error { printFullVersion() @@ -143,12 +197,12 @@ intermediate private key.`, }() } - app.Action = func(ctx *cli.Context) error { + app.Action = func(_ *cli.Context) error { // Hack to be able to run a the top action as a subcommand cmd := cli.Command{Name: "start", Action: startAction, Flags: app.Flags} set := flag.NewFlagSet(app.Name, flag.ContinueOnError) set.Parse(os.Args) - ctx = cli.NewContext(app, set, nil) + ctx := cli.NewContext(app, set, nil) return cmd.Run(ctx) } From 8e46cf0522dd434cc85e9d9c758099d0adc9ca76 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 29 Aug 2019 16:02:26 -0700 Subject: [PATCH 13/15] dep update cli --- Gopkg.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 0b976ddd..03026d94 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -207,7 +207,7 @@ [[projects]] branch = "master" - digest = "1:c986e637991c8d7a02675fd89a3e3452af6ec390d4d9dd3c4393949effc9a626" + digest = "1:4998154cc4ffb0ab954b7ecaad6899dcdea8f8ec79c94a071994d1a3b332d493" name = "github.com/smallstep/cli" packages = [ "command", @@ -228,7 +228,7 @@ "utils", ] pruneopts = "UT" - revision = "8012d7ee6e6759ca411fbf66feb87cee96272b62" + revision = "3254c70079f798ee5f269c98fe27abd9a11c4aad" [[projects]] branch = "master" From f25e953bf0934402a21afd1995ad10085964b57b Mon Sep 17 00:00:00 2001 From: Michael Malone Date: Fri, 30 Aug 2019 17:58:52 -0700 Subject: [PATCH 14/15] Fixed some README.md formatting --- README.md | 55 ++++++++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a1806889..4cd362b3 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,7 @@ While `step` is not required to run `step-ca`, it will make your life easier so Install `step` and `step-ca` together via [Homebrew](https://brew.sh/): -

-$ brew install step
+
$ brew install step
 
 # Test installation ...
 $ step certificate inspect https://smallstep.com
@@ -111,15 +110,15 @@ Certificate:
         Serial Number: 326381749415081530968054238478851085504954 (0x3bf265673332db2d0c70e48a163fb7d11ba)
     Signature Algorithm: SHA256-RSA
         Issuer: C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3
-...
-
+...
> Note: If you have installed `step` previously through the `smallstep/smallstep` > tap you will need to run the following commands before installing: -``` -$ brew untap smallstep/smallstep -$ brew uninstall step -``` +> +> ``` +> $ brew untap smallstep/smallstep +> $ brew uninstall step +> ``` ### Linux @@ -180,15 +179,13 @@ helm install step-certificates ### Test -

-$ step version
+
$ step version
 Smallstep CLI/0.10.0 (darwin/amd64)
 Release Date: 2019-04-30 19:01 UTC
 
 $ step-ca version
 Smallstep CA/0.10.0 (darwin/amd64)
-Release Date: 2019-04-30 19:02 UTC
-
+Release Date: 2019-04-30 19:02 UTC
## Quickstart @@ -206,8 +203,7 @@ communication. Let's get started! #### 1. Run `step ca init` to create your CA's keys & certificates and configure `step-ca`: -

-$ step ca init
+
$ step ca init
 βœ” What would you like to name your new PKI? (e.g. Smallstep): Example Inc.
 βœ” What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
 βœ” What address will your new CA listen at? (e.g. :443): 127.0.0.1:8080
@@ -228,8 +224,7 @@ all done!
 βœ” Default configuration: /Users/bob/src/github.com/smallstep/step/.step/config/defaults.json
 βœ” Certificate Authority configuration: /Users/bob/src/github.com/smallstep/step/.step/config/ca.json
 
-Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
-
+Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
This command will: @@ -243,11 +238,9 @@ You can find these artifacts in `$STEPPATH` (or `~/.step` by default). You'll be prompted for your password from the previous step, to decrypt the CA's private signing key: -

-$ step-ca $(step path)/config/ca.json
+
$ step-ca $(step path)/config/ca.json
 Please enter the password to decrypt /Users/bob/src/github.com/smallstep/step/.step/secrets/intermediate_ca_key: abc123
-2019/02/18 13:28:58 Serving HTTPS on 127.0.0.1:8080 ...
-
+2019/02/18 13:28:58 Serving HTTPS on 127.0.0.1:8080 ...
#### 3. Copy our `hello world` golang server. @@ -277,8 +270,7 @@ EOF #### 4. Get an identity for your server from the Step CA. -

-$ step ca certificate localhost srv.crt srv.key
+
$ step ca certificate localhost srv.crt srv.key
 βœ” Key ID: rQxROEr7Kx9TNjSQBTETtsu3GKmuW9zm02dMXZ8GUEk (bob@example.com)
 βœ” Please enter the password to decrypt the provisioner key: abc123
 βœ” CA: https://localhost:8080/1.0/sign
@@ -307,32 +299,25 @@ Certificate:
             Not Before: Feb 18 21:27:21 2019 UTC
             Not After : Feb 15 21:27:21 2029 UTC
         Subject: CN=Example Inc. Intermediate CA
-...
-
+...
Note that `step` and `step-ca` handle details like [certificate bundling](https://smallstep.com/blog/everything-pki.html#intermediates-chains-and-bundling) for you. #### 5. Run the simple server. -

-$ go run srv.go &
-
+
$ go run srv.go &
#### 6. Get the root certificate from the Step CA. In a new Terminal window: -

-$ step ca root root.crt
-The root certificate has been saved in root.crt.
-
+
$ step ca root root.crt
+The root certificate has been saved in root.crt.
#### 7. Make an authenticated, encrypted curl request to your server using HTTP over TLS. -

-$ curl --cacert root.crt https://localhost:8443/hi
-Hello, world!
-
+
$ curl --cacert root.crt https://localhost:8443/hi
+Hello, world!
*All Done!* From 0d8854bc53d3075356c568baa0174d6cdfc583e8 Mon Sep 17 00:00:00 2001 From: Mike Maxey Date: Tue, 3 Sep 2019 15:59:21 -0700 Subject: [PATCH 15/15] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cd362b3..94393207 100644 --- a/README.md +++ b/README.md @@ -351,5 +351,5 @@ help solve problems in this space. ## Further Reading -Check out the [Getting Started](./docs/GETTING_STARTED.md) guide for more examples +Check out the [Getting Started](https://smallstep.com/docs/getting-started/) guide for more examples and best practices on running Step CA in production.