From a8819376d3169b192f5498be7da6dc0c448b82a1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 2 Aug 2022 16:05:04 -0700 Subject: [PATCH 01/10] Remove empty lines on debug information At the start of step-ca some information about the CA is displayed, this change remove extra lines when displaying the ssh public keys. --- ca/ca.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ca/ca.go b/ca/ca.go index 7c00bb6b..741593d2 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -1,6 +1,7 @@ package ca import ( + "bytes" "context" "crypto/tls" "crypto/x509" @@ -342,10 +343,10 @@ func (ca *CA) Run() error { log.Printf("X.509 Root Fingerprint: %s", x509util.Fingerprint(crt)) } if authorityInfo.SSHCAHostPublicKey != nil { - log.Printf("SSH Host CA Key: %s\n", authorityInfo.SSHCAHostPublicKey) + log.Printf("SSH Host CA Key: %s\n", bytes.TrimSpace(authorityInfo.SSHCAHostPublicKey)) } if authorityInfo.SSHCAUserPublicKey != nil { - log.Printf("SSH User CA Key: %s\n", authorityInfo.SSHCAUserPublicKey) + log.Printf("SSH User CA Key: %s\n", bytes.TrimSpace(authorityInfo.SSHCAUserPublicKey)) } } From 9408d0f24b3f08b037b691cac16dc906b724632c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 2 Aug 2022 19:28:49 -0700 Subject: [PATCH 02/10] Send RA provisioner information to the CA --- authority/authority.go | 5 ++++- authority/provisioner/jwk.go | 14 ++++++++++++-- authority/provisioner/provisioner.go | 19 +++++++++++++++++++ authority/provisioner/x5c.go | 11 ++++++++++- authority/tls.go | 19 ++++++++++++++----- cas/apiv1/options.go | 4 ++++ cas/apiv1/requests.go | 18 +++++++++++++----- cas/stepcas/issuer.go | 8 +++++++- cas/stepcas/jwk_issuer.go | 15 +++++++++++---- cas/stepcas/stepcas.go | 19 +++++++++++++++---- cas/stepcas/x5c_issuer.go | 15 +++++++++++---- 11 files changed, 120 insertions(+), 27 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 933ceb14..3c74c037 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -312,6 +312,7 @@ func (a *Authority) init() error { if id := a.config.AuthorityConfig.AuthorityID; id != "" && !strings.EqualFold(id, linkedcaClient.authorityID) { return errors.New("error initializing linkedca: token authority and configured authority do not match") } + a.config.AuthorityConfig.AuthorityID = linkedcaClient.authorityID linkedcaClient.Run() } @@ -322,6 +323,9 @@ func (a *Authority) init() error { options = *a.config.AuthorityConfig.Options } + // AuthorityID might be empty. It's always available linked CAs/RAs. + options.AuthorityID = a.config.AuthorityConfig.AuthorityID + // Configure linked RA if linkedcaClient != nil && options.CertificateAuthority == "" { conf, err := linkedcaClient.GetConfiguration(ctx) @@ -357,7 +361,6 @@ func (a *Authority) init() error { return err } } - a.x509CAService, err = cas.New(ctx, options) if err != nil { return err diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index de592941..966fa155 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -23,7 +23,8 @@ type jwtPayload struct { } type stepPayload struct { - SSH *SignSSHOptions `json:"ssh,omitempty"` + SSH *SignSSHOptions `json:"ssh,omitempty"` + RAInfo *RAInfo `json:"ra,omitempty"` } // JWK is the default provisioner, an entity that can sign tokens necessary for @@ -172,8 +173,17 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign") } + // Wrap provisioner if the token is an RA token. + var self Interface = p + if claims.Step != nil && claims.Step.RAInfo != nil { + self = &raProvisioner{ + Interface: p, + raInfo: claims.Step.RAInfo, + } + } + return []SignOption{ - p, + self, templateOptions, // modifiers / withOptions newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID), diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 0d5cd41a..ba3153a3 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -340,6 +340,25 @@ type Permissions struct { CriticalOptions map[string]string `json:"criticalOptions"` } +// RAInfo is the information about a provisioner present in RA tokens generated +// by StepCAS. +type RAInfo struct { + AuthorityID string `json:"authorityId"` + ProvisionerID string `json:"provisionerId"` + ProvisionerType string `json:"provisionerType"` +} + +// raProvisioner wraps a provisioner with RA data. +type raProvisioner struct { + Interface + raInfo *RAInfo +} + +// RAInfo returns the RAInfo in the wrapped provisioner. +func (p *raProvisioner) RAInfo() *RAInfo { + return p.raInfo +} + // MockProvisioner for testing type MockProvisioner struct { Mret1, Mret2, Mret3 interface{} diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index b9ae24c5..183d40ea 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -221,8 +221,17 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign") } + // Wrap provisioner if the token is an RA token. + var self Interface = p + if claims.Step != nil && claims.Step.RAInfo != nil { + self = &raProvisioner{ + Interface: p, + raInfo: claims.Step.RAInfo, + } + } + return []SignOption{ - p, + self, templateOptions, // modifiers / withOptions newProvisionerExtensionOption(TypeX5C, p.Name, ""), diff --git a/authority/tls.go b/authority/tls.go index 4c29ca15..0eaace82 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -72,6 +72,10 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { } } +type raProvisioner interface { + RAInfo() *provisioner.RAInfo +} + // Sign creates a signed certificate from a certificate signing request. func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { var ( @@ -93,12 +97,16 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration var prov provisioner.Interface + var pInfo *casapi.ProvisionerInfo for _, op := range extraOpts { switch k := op.(type) { // Capture current provisioner case provisioner.Interface: prov = k - + pInfo = &casapi.ProvisionerInfo{ + ProvisionerID: prov.GetID(), + ProvisionerType: prov.GetType().String(), + } // Adds new options to NewCertificate case provisioner.CertificateOptions: certOptions = append(certOptions, k.Options(signOpts)...) @@ -221,10 +229,11 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign // Sign certificate lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate)) resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{ - Template: leaf, - CSR: csr, - Lifetime: lifetime, - Backdate: signOpts.Backdate, + Template: leaf, + CSR: csr, + Lifetime: lifetime, + Backdate: signOpts.Backdate, + Provisioner: pInfo, }) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index f69f933b..01c38efd 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -12,6 +12,10 @@ import ( // Options represents the configuration options used to select and configure the // CertificateAuthorityService (CAS) to use. type Options struct { + // AuthorityID is the the id oc the current authority. This is used on + // StepCAS to add information about the origin of a certificate. + AuthorityID string `json:"-"` + // The type of the CAS to use. Type string `json:"type"` diff --git a/cas/apiv1/requests.go b/cas/apiv1/requests.go index bf745c17..2d1b0784 100644 --- a/cas/apiv1/requests.go +++ b/cas/apiv1/requests.go @@ -52,11 +52,19 @@ const ( // CreateCertificateRequest is the request used to sign a new certificate. type CreateCertificateRequest struct { - Template *x509.Certificate - CSR *x509.CertificateRequest - Lifetime time.Duration - Backdate time.Duration - RequestID string + Template *x509.Certificate + CSR *x509.CertificateRequest + Lifetime time.Duration + Backdate time.Duration + RequestID string + Provisioner *ProvisionerInfo +} + +// ProvisionerInfo contains information of the provisioner used to authorize an +// certificate. +type ProvisionerInfo struct { + ProvisionerID string + ProvisionerType string } // CreateCertificateResponse is the response to a create certificate request. diff --git a/cas/stepcas/issuer.go b/cas/stepcas/issuer.go index be395e33..394489bc 100644 --- a/cas/stepcas/issuer.go +++ b/cas/stepcas/issuer.go @@ -10,8 +10,14 @@ import ( "github.com/smallstep/certificates/cas/apiv1" ) +type raInfo struct { + AuthorityID string `json:"authorityId,omitempty"` + ProvisionerID string `json:"provisionerId"` + ProvisionerType string `json:"provisionerType"` +} + type stepIssuer interface { - SignToken(subject string, sans []string) (string, error) + SignToken(subject string, sans []string, info *raInfo) (string, error) RevokeToken(subject string) (string, error) Lifetime(d time.Duration) time.Duration } diff --git a/cas/stepcas/jwk_issuer.go b/cas/stepcas/jwk_issuer.go index db45ef48..4ef4f541 100644 --- a/cas/stepcas/jwk_issuer.go +++ b/cas/stepcas/jwk_issuer.go @@ -53,25 +53,25 @@ func newJWKIssuer(caURL *url.URL, client *ca.Client, cfg *apiv1.CertificateIssue }, nil } -func (i *jwkIssuer) SignToken(subject string, sans []string) (string, error) { +func (i *jwkIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) { aud := i.caURL.ResolveReference(&url.URL{ Path: "/1.0/sign", }).String() - return i.createToken(aud, subject, sans) + return i.createToken(aud, subject, sans, info) } func (i *jwkIssuer) RevokeToken(subject string) (string, error) { aud := i.caURL.ResolveReference(&url.URL{ Path: "/1.0/revoke", }).String() - return i.createToken(aud, subject, nil) + return i.createToken(aud, subject, nil, nil) } func (i *jwkIssuer) Lifetime(d time.Duration) time.Duration { return d } -func (i *jwkIssuer) createToken(aud, sub string, sans []string) (string, error) { +func (i *jwkIssuer) createToken(aud, sub string, sans []string, info *raInfo) (string, error) { id, err := randutil.Hex(64) // 256 bits if err != nil { return "", err @@ -84,6 +84,13 @@ func (i *jwkIssuer) createToken(aud, sub string, sans []string) (string, error) "sans": sans, }) } + if info != nil { + builder = builder.Claims(map[string]interface{}{ + "step": map[string]interface{}{ + "ra": info, + }, + }) + } tok, err := builder.CompactSerialize() if err != nil { diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index 9fcbd36c..2ab48c7a 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -23,6 +23,7 @@ func init() { type StepCAS struct { iss stepIssuer client *ca.Client + authorityID string fingerprint string } @@ -59,6 +60,7 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) { return &StepCAS{ iss: iss, client: client, + authorityID: opts.AuthorityID, fingerprint: opts.CertificateAuthorityFingerprint, }, nil } @@ -73,7 +75,16 @@ func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 return nil, errors.New("createCertificateRequest `lifetime` cannot be 0") } - cert, chain, err := s.createCertificate(req.CSR, req.Lifetime) + var info *raInfo + if p := req.Provisioner; p != nil { + info = &raInfo{ + AuthorityID: s.authorityID, + ProvisionerID: p.ProvisionerID, + ProvisionerType: p.ProvisionerType, + } + } + + cert, chain, err := s.createCertificate(req.CSR, req.Lifetime, info) if err != nil { return nil, err } @@ -135,7 +146,7 @@ func (s *StepCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequ }, nil } -func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.Duration) (*x509.Certificate, []*x509.Certificate, error) { +func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.Duration, raInfo *raInfo) (*x509.Certificate, []*x509.Certificate, error) { sans := make([]string, 0, len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses)+len(cr.URIs)) sans = append(sans, cr.DNSNames...) sans = append(sans, cr.EmailAddresses...) @@ -151,11 +162,11 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D commonName = sans[0] } - token, err := s.iss.SignToken(commonName, sans) + token, err := s.iss.SignToken(commonName, sans, raInfo) if err != nil { return nil, nil, err } - + println(token) resp, err := s.client.Sign(&api.SignRequest{ CsrPEM: api.CertificateRequest{CertificateRequest: cr}, OTT: token, diff --git a/cas/stepcas/x5c_issuer.go b/cas/stepcas/x5c_issuer.go index 76ed9c3c..a005e501 100644 --- a/cas/stepcas/x5c_issuer.go +++ b/cas/stepcas/x5c_issuer.go @@ -46,13 +46,13 @@ func newX5CIssuer(caURL *url.URL, cfg *apiv1.CertificateIssuer) (*x5cIssuer, err }, nil } -func (i *x5cIssuer) SignToken(subject string, sans []string) (string, error) { +func (i *x5cIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) { aud := i.caURL.ResolveReference(&url.URL{ Path: "/1.0/sign", Fragment: "x5c/" + i.issuer, }).String() - return i.createToken(aud, subject, sans) + return i.createToken(aud, subject, sans, info) } func (i *x5cIssuer) RevokeToken(subject string) (string, error) { @@ -61,7 +61,7 @@ func (i *x5cIssuer) RevokeToken(subject string) (string, error) { Fragment: "x5c/" + i.issuer, }).String() - return i.createToken(aud, subject, nil) + return i.createToken(aud, subject, nil, nil) } func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration { @@ -76,7 +76,7 @@ func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration { return d } -func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error) { +func (i *x5cIssuer) createToken(aud, sub string, sans []string, info *raInfo) (string, error) { signer, err := newX5CSigner(i.certFile, i.keyFile, i.password) if err != nil { return "", err @@ -94,6 +94,13 @@ func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error) "sans": sans, }) } + if info != nil { + builder = builder.Claims(map[string]interface{}{ + "step": map[string]interface{}{ + "ra": info, + }, + }) + } tok, err := builder.CompactSerialize() if err != nil { From 7a1e6a0e1f1f351f57e1920a619536a0751bc1f4 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 11:57:42 -0700 Subject: [PATCH 03/10] Fix and extend stepcas unit tests --- cas/stepcas/issuer_test.go | 2 +- cas/stepcas/jwk_issuer_test.go | 23 +++++++++++++++++------ cas/stepcas/stepcas_test.go | 9 +++++++++ cas/stepcas/x5c_issuer_test.go | 27 +++++++++++++++++++-------- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/cas/stepcas/issuer_test.go b/cas/stepcas/issuer_test.go index 6fffd729..726dedbf 100644 --- a/cas/stepcas/issuer_test.go +++ b/cas/stepcas/issuer_test.go @@ -13,7 +13,7 @@ import ( type mockErrIssuer struct{} -func (m mockErrIssuer) SignToken(subject string, sans []string) (string, error) { +func (m mockErrIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) { return "", apiv1.ErrNotImplemented{} } diff --git a/cas/stepcas/jwk_issuer_test.go b/cas/stepcas/jwk_issuer_test.go index 7ebfcb3f..81a6d900 100644 --- a/cas/stepcas/jwk_issuer_test.go +++ b/cas/stepcas/jwk_issuer_test.go @@ -27,11 +27,16 @@ func Test_jwkIssuer_SignToken(t *testing.T) { type args struct { subject string sans []string + info *raInfo + } + type stepClaims struct { + RA *raInfo `json:"ra"` } type claims struct { - Aud []string `json:"aud"` - Sub string `json:"sub"` - Sans []string `json:"sans"` + Aud []string `json:"aud"` + Sub string `json:"sub"` + Sans []string `json:"sans"` + Step stepClaims `json:"step"` } tests := []struct { name string @@ -39,8 +44,11 @@ func Test_jwkIssuer_SignToken(t *testing.T) { args args wantErr bool }{ - {"ok", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}}, false}, - {"fail", fields{caURL, "ra@doe.org", &mockErrSigner{}}, args{"doe", []string{"doe.org"}}, true}, + {"ok", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}, nil}, false}, + {"ok ra", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}, &raInfo{ + AuthorityID: "authority-id", ProvisionerID: "provisioner-id", ProvisionerType: "provisioner-type", + }}, false}, + {"fail", fields{caURL, "ra@doe.org", &mockErrSigner{}}, args{"doe", []string{"doe.org"}, nil}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -49,7 +57,7 @@ func Test_jwkIssuer_SignToken(t *testing.T) { issuer: tt.fields.issuer, signer: tt.fields.signer, } - got, err := i.SignToken(tt.args.subject, tt.args.sans) + got, err := i.SignToken(tt.args.subject, tt.args.sans, tt.args.info) if (err != nil) != tt.wantErr { t.Errorf("jwkIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr) return @@ -65,6 +73,9 @@ func Test_jwkIssuer_SignToken(t *testing.T) { Sub: tt.args.subject, Sans: tt.args.sans, } + if tt.args.info != nil { + want.Step.RA = tt.args.info + } if err := jwt.Claims(testX5CKey.Public(), &c); err != nil { t.Errorf("jwt.Claims() error = %v", err) } diff --git a/cas/stepcas/stepcas_test.go b/cas/stepcas/stepcas_test.go index ad7851bf..4654292d 100644 --- a/cas/stepcas/stepcas_test.go +++ b/cas/stepcas/stepcas_test.go @@ -665,6 +665,14 @@ func TestStepCAS_CreateCertificate(t *testing.T) { Certificate: testCrt, CertificateChain: []*x509.Certificate{testIssCrt}, }, false}, + {"ok with provisioner", fields{jwk, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{ + CSR: testCR, + Lifetime: time.Hour, + Provisioner: &apiv1.ProvisionerInfo{ProvisionerID: "provisioner-id", ProvisionerType: "ACME"}, + }}, &apiv1.CreateCertificateResponse{ + Certificate: testCrt, + CertificateChain: []*x509.Certificate{testIssCrt}, + }, false}, {"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{ CSR: nil, Lifetime: time.Hour, @@ -691,6 +699,7 @@ func TestStepCAS_CreateCertificate(t *testing.T) { s := &StepCAS{ iss: tt.fields.iss, client: tt.fields.client, + authorityID: "authority-id", fingerprint: tt.fields.fingerprint, } got, err := s.CreateCertificate(tt.args.req) diff --git a/cas/stepcas/x5c_issuer_test.go b/cas/stepcas/x5c_issuer_test.go index b1bc653d..5b260dda 100644 --- a/cas/stepcas/x5c_issuer_test.go +++ b/cas/stepcas/x5c_issuer_test.go @@ -51,11 +51,16 @@ func Test_x5cIssuer_SignToken(t *testing.T) { type args struct { subject string sans []string + info *raInfo + } + type stepClaims struct { + RA *raInfo `json:"ra"` } type claims struct { - Aud []string `json:"aud"` - Sub string `json:"sub"` - Sans []string `json:"sans"` + Aud []string `json:"aud"` + Sub string `json:"sub"` + Sans []string `json:"sans"` + Step stepClaims `json:"step"` } tests := []struct { name string @@ -63,10 +68,13 @@ func Test_x5cIssuer_SignToken(t *testing.T) { args args wantErr bool }{ - {"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, false}, - {"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, true}, - {"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe", []string{"doe.org"}}, true}, - {"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe", []string{"doe.org"}}, true}, + {"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, false}, + {"ok ra", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, &raInfo{ + AuthorityID: "authority-id", ProvisionerID: "provisioner-id", ProvisionerType: "provisioner-type", + }}, false}, + {"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, true}, + {"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe", []string{"doe.org"}, nil}, true}, + {"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -76,7 +84,7 @@ func Test_x5cIssuer_SignToken(t *testing.T) { keyFile: tt.fields.keyFile, issuer: tt.fields.issuer, } - got, err := i.SignToken(tt.args.subject, tt.args.sans) + got, err := i.SignToken(tt.args.subject, tt.args.sans, tt.args.info) if (err != nil) != tt.wantErr { t.Errorf("x5cIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr) } @@ -91,6 +99,9 @@ func Test_x5cIssuer_SignToken(t *testing.T) { Sub: tt.args.subject, Sans: tt.args.sans, } + if tt.args.info != nil { + want.Step.RA = tt.args.info + } if err := jwt.Claims(testX5CKey.Public(), &c); err != nil { t.Errorf("jwt.Claims() error = %v", err) } From f9df8ac05fc920d9be08d04e19577e61f5c7de74 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 12:03:49 -0700 Subject: [PATCH 04/10] Remove unused interface --- authority/tls.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/authority/tls.go b/authority/tls.go index 0eaace82..d7be04fc 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -72,10 +72,6 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { } } -type raProvisioner interface { - RAInfo() *provisioner.RAInfo -} - // Sign creates a signed certificate from a certificate signing request. func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { var ( From a1f54921d2bd301b5aea2813d3ed60d354825169 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 12:07:45 -0700 Subject: [PATCH 05/10] Rename internal field --- authority/provisioner/jwk.go | 8 ++++---- authority/provisioner/x5c.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index 966fa155..5cfb0409 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -23,8 +23,8 @@ type jwtPayload struct { } type stepPayload struct { - SSH *SignSSHOptions `json:"ssh,omitempty"` - RAInfo *RAInfo `json:"ra,omitempty"` + SSH *SignSSHOptions `json:"ssh,omitempty"` + RA *RAInfo `json:"ra,omitempty"` } // JWK is the default provisioner, an entity that can sign tokens necessary for @@ -175,10 +175,10 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // Wrap provisioner if the token is an RA token. var self Interface = p - if claims.Step != nil && claims.Step.RAInfo != nil { + if claims.Step != nil && claims.Step.RA != nil { self = &raProvisioner{ Interface: p, - raInfo: claims.Step.RAInfo, + raInfo: claims.Step.RA, } } diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index 183d40ea..9f9a0e4e 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -223,10 +223,10 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // Wrap provisioner if the token is an RA token. var self Interface = p - if claims.Step != nil && claims.Step.RAInfo != nil { + if claims.Step != nil && claims.Step.RA != nil { self = &raProvisioner{ Interface: p, - raInfo: claims.Step.RAInfo, + raInfo: claims.Step.RA, } } From 9648fe6b4c3c3dec90670f916dc4815284237abc Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 15:32:39 -0700 Subject: [PATCH 06/10] Remove debug statement --- cas/stepcas/stepcas.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index 2ab48c7a..03166c5f 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -166,7 +166,7 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D if err != nil { return nil, nil, err } - println(token) + resp, err := s.client.Sign(&api.SignRequest{ CsrPEM: api.CertificateRequest{CertificateRequest: cr}, OTT: token, From 6b5d3dca951452286138b4c9ebede76c73dc7aa4 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 18:44:04 -0700 Subject: [PATCH 07/10] Add provisioner name to RA info --- authority/provisioner/provisioner.go | 1 + authority/tls.go | 1 + cas/stepcas/issuer.go | 1 + cas/stepcas/stepcas.go | 1 + 4 files changed, 4 insertions(+) diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index ba3153a3..bdfc7da7 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -346,6 +346,7 @@ type RAInfo struct { AuthorityID string `json:"authorityId"` ProvisionerID string `json:"provisionerId"` ProvisionerType string `json:"provisionerType"` + ProvisionerName string `json:"provisionerName"` } // raProvisioner wraps a provisioner with RA data. diff --git a/authority/tls.go b/authority/tls.go index d7be04fc..24cd86c5 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -102,6 +102,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign pInfo = &casapi.ProvisionerInfo{ ProvisionerID: prov.GetID(), ProvisionerType: prov.GetType().String(), + ProvisionerName: prov.GetName(), } // Adds new options to NewCertificate case provisioner.CertificateOptions: diff --git a/cas/stepcas/issuer.go b/cas/stepcas/issuer.go index 394489bc..57421ffe 100644 --- a/cas/stepcas/issuer.go +++ b/cas/stepcas/issuer.go @@ -14,6 +14,7 @@ type raInfo struct { AuthorityID string `json:"authorityId,omitempty"` ProvisionerID string `json:"provisionerId"` ProvisionerType string `json:"provisionerType"` + ProvisionerName string `json:"provisionerName"` } type stepIssuer interface { diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index 03166c5f..ffcbeab9 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -81,6 +81,7 @@ func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 AuthorityID: s.authorityID, ProvisionerID: p.ProvisionerID, ProvisionerType: p.ProvisionerType, + ProvisionerName: p.ProvisionerName, } } From 64744562c69c465779ddc1f038844ecc49285ab8 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 3 Aug 2022 18:44:25 -0700 Subject: [PATCH 08/10] Send RA provisioner to linkedca. --- authority/linkedca.go | 21 +++++++++++++++++++++ cas/apiv1/requests.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/authority/linkedca.go b/authority/linkedca.go index 0b98f877..0380cbaf 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -277,6 +277,7 @@ func (c *linkedCaClient) StoreCertificateChain(p provisioner.Interface, fullchai PemCertificate: serializeCertificateChain(fullchain[0]), PemCertificateChain: serializeCertificateChain(fullchain[1:]...), Provisioner: createProvisionerIdentity(p), + RaProvisioner: createRegistrationAuthorityProvisioner(p), }) return errors.Wrap(err, "error posting certificate") } @@ -392,6 +393,26 @@ func createProvisionerIdentity(p provisioner.Interface) *linkedca.ProvisionerIde } } +type raProvisioner interface { + RAInfo() *provisioner.RAInfo +} + +func createRegistrationAuthorityProvisioner(p provisioner.Interface) *linkedca.RegistrationAuthorityProvisioner { + if rap, ok := p.(raProvisioner); ok { + info := rap.RAInfo() + typ := linkedca.Provisioner_Type_value[strings.ToUpper(info.ProvisionerType)] + return &linkedca.RegistrationAuthorityProvisioner{ + AuthorityId: info.AuthorityID, + Provisioner: &linkedca.ProvisionerIdentity{ + Id: info.ProvisionerID, + Type: linkedca.Provisioner_Type(typ), + Name: info.ProvisionerName, + }, + } + } + return nil +} + func serializeCertificate(crt *x509.Certificate) string { if crt == nil { return "" diff --git a/cas/apiv1/requests.go b/cas/apiv1/requests.go index 2d1b0784..2fa3c4ef 100644 --- a/cas/apiv1/requests.go +++ b/cas/apiv1/requests.go @@ -65,6 +65,7 @@ type CreateCertificateRequest struct { type ProvisionerInfo struct { ProvisionerID string ProvisionerType string + ProvisionerName string } // CreateCertificateResponse is the response to a create certificate request. diff --git a/go.mod b/go.mod index 546ec53d..5ad8d4a4 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 go.step.sm/crypto v0.16.2 - go.step.sm/linkedca v0.16.1 + go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20220403103023-749bd193bc2b golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect diff --git a/go.sum b/go.sum index 32a27e27..6d065e28 100644 --- a/go.sum +++ b/go.sum @@ -816,8 +816,8 @@ go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/ go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.16.2 h1:Pr9aazTwWBBZNogUsOqhOrPSdwAa9pPs+lMB602lnDA= go.step.sm/crypto v0.16.2/go.mod h1:1WkTOTY+fOX/RY4TnZREp6trQAsBHRQ7nu6QJBiNQF8= -go.step.sm/linkedca v0.16.1 h1:CdbMV5SjnlRsgeYTXaaZmQCkYIgJq8BOzpewri57M2k= -go.step.sm/linkedca v0.16.1/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM= +go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b h1:Au+36ljo23YpEiIZk9lcum1GCF80XKrUz+7pYcHi07s= +go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= From c5c7c30cc2450efc3840b4ff880ea1f6a9ba1db1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 4 Aug 2022 10:07:20 -0700 Subject: [PATCH 09/10] Fix typo in ProvisionerInfo --- cas/apiv1/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cas/apiv1/requests.go b/cas/apiv1/requests.go index 2fa3c4ef..33a879ce 100644 --- a/cas/apiv1/requests.go +++ b/cas/apiv1/requests.go @@ -60,7 +60,7 @@ type CreateCertificateRequest struct { Provisioner *ProvisionerInfo } -// ProvisionerInfo contains information of the provisioner used to authorize an +// ProvisionerInfo contains information of the provisioner used to authorize a // certificate. type ProvisionerInfo struct { ProvisionerID string From a2f776694342c39fb4bc3dfc643ba1778f83c781 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 4 Aug 2022 10:31:57 -0700 Subject: [PATCH 10/10] Use released version of linkedca --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5ad8d4a4..a7c4fa55 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 go.step.sm/crypto v0.16.2 - go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b + go.step.sm/linkedca v0.17.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20220403103023-749bd193bc2b golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect diff --git a/go.sum b/go.sum index 6d065e28..011796cd 100644 --- a/go.sum +++ b/go.sum @@ -816,8 +816,8 @@ go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/ go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.16.2 h1:Pr9aazTwWBBZNogUsOqhOrPSdwAa9pPs+lMB602lnDA= go.step.sm/crypto v0.16.2/go.mod h1:1WkTOTY+fOX/RY4TnZREp6trQAsBHRQ7nu6QJBiNQF8= -go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b h1:Au+36ljo23YpEiIZk9lcum1GCF80XKrUz+7pYcHi07s= -go.step.sm/linkedca v0.16.2-0.20220803232448-166e79f0864b/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM= +go.step.sm/linkedca v0.17.0 h1:90XYS0cPCVilsS1udTOph7TVnsNVVPK/gb66VIAP4RU= +go.step.sm/linkedca v0.17.0/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=