From a68208a3ba8cdb55bb6018cba7946f018a6c9556 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 22 Dec 2021 11:54:01 +0100 Subject: [PATCH 01/22] Set Step CLI User-Agent when performing ACME requests --- ca/acmeClient.go | 26 ++++++++++++++++++++------ ca/acmeClient_test.go | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ca/acmeClient.go b/ca/acmeClient.go index 28451a45..6c73b2fd 100644 --- a/ca/acmeClient.go +++ b/ca/acmeClient.go @@ -37,15 +37,18 @@ func NewACMEClient(endpoint string, contact []string, opts ...ClientOption) (*AC if err != nil { return nil, err } - ac := &ACMEClient{ client: &http.Client{ Transport: tr, }, dirLoc: endpoint, } - - resp, err := ac.client.Get(endpoint) + req, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return nil, errors.Wrapf(err, "creating GET request %s failed", endpoint) + } + req.Header.Set("User-Agent", UserAgent) + resp, err := ac.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", endpoint) } @@ -99,7 +102,12 @@ func (c *ACMEClient) GetDirectory() (*acmeAPI.Directory, error) { // GetNonce makes a nonce request to the ACME api and returns an // ACME directory object. func (c *ACMEClient) GetNonce() (string, error) { - resp, err := c.client.Get(c.dir.NewNonce) + req, err := http.NewRequest("GET", c.dir.NewNonce, nil) + if err != nil { + return "", errors.Wrapf(err, "creating GET request %s failed", c.dir.NewNonce) + } + req.Header.Set("User-Agent", UserAgent) + resp, err := c.client.Do(req) if err != nil { return "", errors.Wrapf(err, "client GET %s failed", c.dir.NewNonce) } @@ -171,9 +179,15 @@ func (c *ACMEClient) post(payload []byte, url string, headerOps ...withHeaderOpt if err != nil { return nil, err } - resp, err := c.client.Post(url, "application/jose+json", strings.NewReader(raw)) + req, err := http.NewRequest("POST", url, strings.NewReader(raw)) if err != nil { - return nil, errors.Wrapf(err, "client GET %s failed", c.dir.NewOrder) + return nil, errors.Wrapf(err, "creating POST request %s failed", url) + } + req.Header.Set("Content-Type", "application/jose+json") + req.Header.Set("User-Agent", UserAgent) + resp, err := c.client.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "client POST %s failed", c.dir.NewOrder) } return resp, nil } diff --git a/ca/acmeClient_test.go b/ca/acmeClient_test.go index d22c4972..ad5f2116 100644 --- a/ca/acmeClient_test.go +++ b/ca/acmeClient_test.go @@ -109,6 +109,7 @@ func TestNewACMEClient(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header switch { case i == 0: api.JSONStatus(w, tc.r1, tc.rc1) @@ -203,6 +204,7 @@ func TestACMEClient_GetNonce(t *testing.T) { tc := run(t) srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header w.Header().Set("Replay-Nonce", expectedNonce) api.JSONStatus(w, tc.r1, tc.rc1) }) @@ -309,6 +311,8 @@ func TestACMEClient_post(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -447,6 +451,8 @@ func TestACMEClient_NewOrder(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -567,6 +573,8 @@ func TestACMEClient_GetOrder(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -687,6 +695,8 @@ func TestACMEClient_GetAuthz(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -807,6 +817,8 @@ func TestACMEClient_GetChallenge(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -928,6 +940,8 @@ func TestACMEClient_ValidateChallenge(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -1053,6 +1067,8 @@ func TestACMEClient_FinalizeOrder(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -1180,6 +1196,8 @@ func TestACMEClient_GetAccountOrders(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) @@ -1309,6 +1327,8 @@ func TestACMEClient_GetCertificate(t *testing.T) { i := 0 srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header + w.Header().Set("Replay-Nonce", expectedNonce) if i == 0 { api.JSONStatus(w, tc.r1, tc.rc1) From 07addd0cac3d9178aa72540f94df8964497f47a4 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 22 Dec 2021 11:58:00 +0100 Subject: [PATCH 02/22] Fix linting issue --- ca/acmeClient.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ca/acmeClient.go b/ca/acmeClient.go index 6c73b2fd..cca35b93 100644 --- a/ca/acmeClient.go +++ b/ca/acmeClient.go @@ -43,7 +43,7 @@ func NewACMEClient(endpoint string, contact []string, opts ...ClientOption) (*AC }, dirLoc: endpoint, } - req, err := http.NewRequest("GET", endpoint, nil) + req, err := http.NewRequest("GET", endpoint, http.NoBody) if err != nil { return nil, errors.Wrapf(err, "creating GET request %s failed", endpoint) } @@ -102,7 +102,7 @@ func (c *ACMEClient) GetDirectory() (*acmeAPI.Directory, error) { // GetNonce makes a nonce request to the ACME api and returns an // ACME directory object. func (c *ACMEClient) GetNonce() (string, error) { - req, err := http.NewRequest("GET", c.dir.NewNonce, nil) + req, err := http.NewRequest("GET", c.dir.NewNonce, http.NoBody) if err != nil { return "", errors.Wrapf(err, "creating GET request %s failed", c.dir.NewNonce) } From 32390a29641c00616417929fa650e125465c8760 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 29 Dec 2021 14:12:03 -0800 Subject: [PATCH 03/22] Add initial implementation of a nebula provisioner. A nebula provisioner will generate a X509 or SSH certificate with the identities in the nebula certificate embedded in the token. The token is signed with the private key of the nebula certificate. --- authority/provisioner/nebula.go | 427 +++++++++++++++++++++++++++ authority/provisioner/provisioner.go | 6 + go.mod | 18 +- go.sum | 101 ++++++- 4 files changed, 528 insertions(+), 24 deletions(-) create mode 100644 authority/provisioner/nebula.go diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go new file mode 100644 index 00000000..d909bc19 --- /dev/null +++ b/authority/provisioner/nebula.go @@ -0,0 +1,427 @@ +package provisioner + +import ( + "context" + "crypto/ed25519" + "crypto/x509" + "encoding/base64" + "net" + "time" + + "github.com/pkg/errors" + "github.com/slackhq/nebula/cert" + "github.com/smallstep/certificates/errs" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/sshutil" + "go.step.sm/crypto/x25519" + "go.step.sm/crypto/x509util" + "golang.org/x/crypto/ssh" +) + +const ( + // NebulaCertHeader is the token header that contains a nebula certificate. + NebulaCertHeader jose.HeaderKey = "nbc" +) + +type Nebula struct { + ID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Roots []byte `json:"roots"` + Claims *Claims `json:"claims,omitempty"` + Options *Options `json:"options,omitempty"` + claimer *Claimer + caPool *cert.NebulaCAPool + audiences Audiences +} + +func (p *Nebula) Init(config Config) error { + switch { + case p.Type == "": + return errors.New("provisioner type cannot be empty") + case p.Name == "": + return errors.New("provisioner name cannot be empty") + case len(p.Roots) == 0: + return errors.New("provisioner root(s) cannot be empty") + } + + var err error + if p.claimer, err = NewClaimer(p.Claims, config.Claims); err != nil { + return err + } + + p.caPool, err = cert.NewCAPoolFromBytes(p.Roots) + if err != nil { + return errs.InternalServer("failed to start ca pool: %v", err) + } + + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) + + return nil +} + +// GetID returns the provisioner id. +func (p *Nebula) GetID() string { + if p.ID != "" { + return p.ID + } + return p.GetIDForToken() +} + +// GetIDForToken returns an identifier that will be used to load the provisioner +// from a token. +func (p *Nebula) GetIDForToken() string { + return "nebula/" + p.Name +} + +// GetTokenID returns the identifier of the token. +func (p *Nebula) GetTokenID(token string) (string, error) { + // Validate payload + t, err := jose.ParseSigned(token) + if err != nil { + return "", errors.Wrap(err, "error parsing token") + } + + // Get claims w/out verification. We need to look up the provisioner + // key in order to verify the claims and we need the issuer from the claims + // before we can look up the provisioner. + var claims jose.Claims + if err = t.UnsafeClaimsWithoutVerification(&claims); err != nil { + return "", errors.Wrap(err, "error verifying claims") + } + return claims.ID, nil +} + +// GetName returns the name of the provisioner. +func (p *Nebula) GetName() string { + return p.Name +} + +// GetType returns the type of provisioner. +func (p *Nebula) GetType() Type { + return TypeNebula +} + +// GetEncryptedKey returns the base provisioner encrypted key if it's defined. +func (p *Nebula) GetEncryptedKey() (kid string, key string, ok bool) { + return "", "", false +} + +// AuthorizeSign returns the list of SignOption for a Sign request. +func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) { + cert, claims, err := p.authorizeToken(token, p.audiences.Sign) + if err != nil { + return nil, err + } + + data := x509util.CreateTemplateData(claims.Subject, claims.SANs) + if v, err := unsafeParseSigned(token); err == nil { + data.SetToken(v) + } + data.Set("Cert", cert) + + templateOptions, err := TemplateOptions(p.Options, data) + if err != nil { + return nil, err + } + + return []SignOption{ + templateOptions, + // modifiers / withOptions + newProvisionerExtensionOption(TypeNebula, p.Name, ""), + profileLimitDuration{ + def: p.claimer.DefaultTLSCertDuration(), + notBefore: cert.Details.NotBefore, + notAfter: cert.Details.NotAfter, + }, + // validators + commonNameValidator(claims.Subject), + nebulaSANsValidator{ + Name: cert.Details.Name, + IPs: cert.Details.Ips, + }, + defaultPublicKeyValidator{}, + newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), + }, nil +} + +// AuthorizeSSHSign returns the list of SignOption for a SignSSH request. +// Currently the nebula provisioner only grant host ssh certificates +func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { + if !p.claimer.IsSSHCAEnabled() { + return nil, errs.Unauthorized("ssh is disabled for nebula provisioner '%s'", p.Name) + } + + cert, claims, err := p.authorizeToken(token, p.audiences.SSHSign) + if err != nil { + return nil, err + } + + // Default template attributes. + keyID := claims.Subject + principals := make([]string, len(cert.Details.Ips)+1) + principals[0] = cert.Details.Name + for i, ipnet := range cert.Details.Ips { + principals[i+1] = ipnet.IP.String() + } + + var signOptions []SignOption + // If step ssh options are given, validate them and set key id, principals + // and validity. + if claims.Step != nil || claims.Step.SSH != nil { + opts := claims.Step.SSH + + // Check that the token only contains valid principals. + v := nebulaPrincipalsValidator{ + Name: cert.Details.Name, + IPs: cert.Details.Ips, + } + if err := v.Valid(*opts); err != nil { + return nil, err + } + // Check that the cert type is a valid one. + if opts.CertType != "" && opts.CertType != SSHHostCert { + return nil, errs.Forbidden("ssh certificate type does not match - got %v, want %v", opts.CertType, SSHHostCert) + } + + signOptions = []SignOption{ + // validate is a host certificate and users's KeyID is the subject. + sshCertOptionsValidator(SignSSHOptions{ + CertType: SSHHostCert, + KeyID: claims.Subject, + }), + // validates user's SSHOptions with the ones in the token + sshCertOptionsValidator(*opts), + } + + // Use options in the token. + if opts.KeyID != "" { + keyID = opts.KeyID + } + if len(opts.Principals) > 0 { + principals = opts.Principals + } + + // Add modifiers from custom claims + t := now() + if !opts.ValidAfter.IsZero() { + signOptions = append(signOptions, sshCertValidAfterModifier(opts.ValidAfter.RelativeTime(t).Unix())) + } + if !opts.ValidBefore.IsZero() { + signOptions = append(signOptions, sshCertValidBeforeModifier(opts.ValidBefore.RelativeTime(t).Unix())) + } + } + + // Certificate templates. + data := sshutil.CreateTemplateData(sshutil.HostCert, keyID, principals) + if v, err := unsafeParseSigned(token); err == nil { + data.SetToken(v) + } + data.Set("Cert", cert) + + templateOptions, err := TemplateSSHOptions(p.Options, data) + if err != nil { + return nil, err + } + + return append(signOptions, + templateOptions, + // Checks the validity bounds, and set the validity if has not been set. + &sshLimitDuration{p.claimer, cert.Details.NotAfter}, + // Validate public key. + &sshDefaultPublicKeyValidator{}, + // Validate the validity period. + &sshCertValidityValidator{p.claimer}, + // Require all the fields in the SSH certificate + &sshCertDefaultValidator{}, + ), nil +} + +// AuthorizeRenew returns an error if the renewal is disabled. +func (p *Nebula) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { + if p.claimer.IsDisableRenewal() { + return errs.Unauthorized("renew is disabled for nebula provisioner '%s'", p.GetName()) + } + return nil +} + +// AuthorizeRevoke returns an error if the token is not valid. +func (p *Nebula) AuthorizeRevoke(ctx context.Context, token string) error { + return p.validateToken(token, p.audiences.Revoke) +} + +// AuthorizeSSHRevoke returns an error if SSH is disabled or the token is invalid. +func (p *Nebula) AuthorizeSSHRevoke(ctx context.Context, token string) error { + if !p.claimer.IsSSHCAEnabled() { + return errs.Unauthorized("ssh is disabled for nebula provisioner '%s'", p.Name) + } + if _, _, err := p.authorizeToken(token, p.audiences.Revoke); err != nil { + return err + } + return nil +} + +// AuthorizeSSHRenew returns an unauthorized error. +func (p *Nebula) AuthorizeSSHRenew(ctx context.Context, token string) (*ssh.Certificate, error) { + return nil, errs.Unauthorized("nebula provisioner does not support SSH renew") +} + +// AuthorizeSSHRekey returns an unauthorized error. +func (p *Nebula) AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certificate, []SignOption, error) { + return nil, nil, errs.Unauthorized("nebula provisioner does not support SSH rekey") +} + +func (p *Nebula) validateToken(token string, audiences []string) error { + _, _, err := p.authorizeToken(token, audiences) + return err +} + +func (p *Nebula) authorizeToken(token string, audiences []string) (*cert.NebulaCertificate, *jwtPayload, error) { + jwt, err := jose.ParseSigned(token) + if err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token")) + } + + // Extract nebula certificate + nbc, ok := jwt.Headers[0].ExtraHeaders[NebulaCertHeader] + if !ok { + return nil, nil, errs.Unauthorized("failed to parse token: nbc header is missing") + } + s, ok := nbc.(string) + if !ok { + return nil, nil, errs.Unauthorized("failed to parse token: nbc header is not valid") + } + b, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token: nbc header is not valid")) + } + c, _, err := cert.UnmarshalNebulaCertificateFromPEM(b) + if err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse nebula certificate: nbc header is not valid")) + } + + // Validate nebula certificate against CA + if valid, err := c.Verify(now(), p.caPool); !valid { + if err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("token is not valid: failed to unmarshal certificate")) + } + return nil, nil, errs.Unauthorized("token is not valid: failed to unmarshal certificate") + } + + var pub interface{} + if c.Details.IsCA { + pub = ed25519.PublicKey(c.Details.PublicKey) + } else { + pub = x25519.PublicKey(c.Details.PublicKey) + } + + // Validate token with public key + var claims jwtPayload + if err := jose.Verify(jwt, pub, &claims); err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("token is not valid: signature does not match")) + } + + // According to "rfc7519 JSON Web Token" acceptable skew should be no + // more than a few minutes. + if err = claims.ValidateWithLeeway(jose.Expected{ + Issuer: p.Name, + Time: now(), + }, time.Minute); err != nil { + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("token is not valid: invalid claims")) + } + // Validate token and subject too. + if !matchesAudience(claims.Audience, audiences) { + return nil, nil, errs.Unauthorized("token is not valid: invalid claims") + } + if claims.Subject == "" { + return nil, nil, errs.Unauthorized("token is not valid: subject cannot be empty") + } + + return c, &claims, nil +} + +type nebulaSANsValidator struct { + Name string + IPs []*net.IPNet +} + +// Valid verifies that the SANs stored in the validator are contained with those +// requested in the x509 certificate request. +func (v nebulaSANsValidator) Valid(req *x509.CertificateRequest) error { + dnsNames, ips, emails, uris := x509util.SplitSANs([]string{v.Name}) + if len(req.DNSNames) > 0 { + if err := dnsNamesValidator(dnsNames).Valid(req); err != nil { + return err + } + } + if len(req.EmailAddresses) > 0 { + if err := emailAddressesValidator(emails).Valid(req); err != nil { + return err + } + } + if len(req.URIs) > 0 { + if err := urisValidator(uris).Valid(req); err != nil { + return err + } + } + if len(req.IPAddresses) > 0 { + for _, ip := range req.IPAddresses { + var valid bool + // Check ip in name + for _, ipInName := range ips { + if ip.Equal(ipInName) { + valid = true + break + } + } + // Check ip network + if !valid { + for _, ipnet := range v.IPs { + if ip.Equal(ipnet.IP) { + valid = true + break + } + } + } + if !valid { + return errs.Forbidden("certificate request does not contain the valid IP addresses - got %v, want %v", req.IPAddresses, v.IPs) + } + } + } + + return nil +} + +type nebulaPrincipalsValidator struct { + Name string + IPs []*net.IPNet +} + +// Valid checks that the SignSSHOptions principals contains only names in the +// nebula certificate. +func (v nebulaPrincipalsValidator) Valid(got SignSSHOptions) error { + for _, p := range got.Principals { + var valid bool + if p == v.Name { + valid = true + } + if !valid { + if ip := net.ParseIP(p); ip != nil { + for _, ipnet := range v.IPs { + if ip.Equal(ipnet.IP) { + valid = true + break + } + } + } + } + + if !valid { + return errs.Forbidden( + "ssh certificate principals does contain a valid name or IP address - got %v, want %s or %v", + got.Principals, v.Name, v.IPs, + ) + } + } + return nil +} diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 5d6b2f80..55ebe092 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -156,6 +156,8 @@ const ( TypeSSHPOP Type = 9 // TypeSCEP is used to indicate the SCEP provisioners TypeSCEP Type = 10 + // TypeNebula is used to indicate the Nebula provisioners + TypeNebula Type = 11 ) // String returns the string representation of the type. @@ -181,6 +183,8 @@ func (t Type) String() string { return "SSHPOP" case TypeSCEP: return "SCEP" + case TypeNebula: + return "Nebula" default: return "" } @@ -251,6 +255,8 @@ func (l *List) UnmarshalJSON(data []byte) error { p = &SSHPOP{} case "scep": p = &SCEP{} + case "nebula": + p = &Nebula{} default: // Skip unsupported provisioners. A client using this method may be // compiled with a version of smallstep/certificates that does not diff --git a/go.mod b/go.mod index 830cab82..8ce67bbb 100644 --- a/go.mod +++ b/go.mod @@ -21,23 +21,23 @@ require ( github.com/google/go-cmp v0.5.6 github.com/google/uuid v1.3.0 github.com/googleapis/gax-go/v2 v2.0.5 - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect github.com/micromdm/scep/v2 v2.1.0 github.com/newrelic/go-agent v2.15.0+incompatible github.com/pkg/errors v0.9.1 github.com/rs/xid v1.2.1 - github.com/sirupsen/logrus v1.4.2 + github.com/sirupsen/logrus v1.8.1 + github.com/slackhq/nebula v1.5.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/nosql v0.3.9 github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 - go.step.sm/crypto v0.13.0 + go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706 go.step.sm/linkedca v0.7.0 - golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 - golang.org/x/net v0.0.0-20210913180222-943fd674d43e + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f google.golang.org/api v0.47.0 google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 google.golang.org/grpc v1.39.0 @@ -45,8 +45,6 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 ) -//replace github.com/smallstep/nosql => ../nosql - -//replace go.step.sm/crypto => ../crypto - -//replace go.step.sm/cli-utils => ../cli-utils +// replace github.com/smallstep/nosql => ../nosql +// replace go.step.sm/crypto => ../crypto +// replace go.step.sm/cli-utils => ../cli-utils diff --git a/go.sum b/go.sum index ede1fa22..43789e2e 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM= @@ -99,6 +101,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -112,6 +116,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= @@ -131,6 +136,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -156,6 +162,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -191,6 +198,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -207,6 +215,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= @@ -223,6 +232,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -276,6 +286,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -351,27 +362,35 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -394,6 +413,7 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyex github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU= github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -416,6 +436,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -423,6 +444,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f/go.mod h1:nwPd6pDNId/Xi16qtKrFHrauSwMNuvk+zcjk89wrnlA= github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU= github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -459,19 +481,29 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -489,8 +521,13 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/slackhq/nebula v1.5.2 h1:wuIOHsOnrNw3rQx8yPxXiGu8wAtAxxtUI/K8W7Vj7EI= +github.com/slackhq/nebula v1.5.2/go.mod h1:xaCM6wqbFk/NRmmUe1bv88fWBm3a1UioXJVIpR52WlE= github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= @@ -499,6 +536,7 @@ github.com/smallstep/nosql v0.3.9/go.mod h1:X2qkYpNcW3yjLUvhEHfgGfClpKbFPapewvx7 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -523,8 +561,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -534,6 +573,9 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -541,6 +583,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -562,8 +605,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.step.sm/cli-utils v0.7.0 h1:2GvY5Muid1yzp7YQbfCCS+gK3q7zlHjjLL5Z0DXz8ds= go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/E= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.13.0 h1:mQuP9Uu2FNmqCJNO0OTbvolnYXzONy4wdUBtUVcP1s8= -go.step.sm/crypto v0.13.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= +go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706 h1:051Yd6Am2AmHQT6m9TeP0Xt9TbQ09pUfQY6LIm6ygPo= +go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= go.step.sm/linkedca v0.7.0 h1:ydYigs0CgLFkPGjOO4KJcAcAWbuPP8ECF1IsyHdftYc= go.step.sm/linkedca v0.7.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -585,9 +628,12 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -667,8 +713,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -711,6 +761,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -720,11 +771,13 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -733,15 +786,20 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -749,12 +807,17 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -763,8 +826,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -812,6 +876,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -820,15 +885,19 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -955,6 +1024,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -969,9 +1039,12 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From cb72796a2d73cbc343df66b6c1a070debb48a347 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 29 Dec 2021 16:07:05 -0800 Subject: [PATCH 04/22] Fix decoding of certificate. --- authority/provisioner/nebula.go | 36 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index d909bc19..8cafadfb 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -109,7 +109,7 @@ func (p *Nebula) GetEncryptedKey() (kid string, key string, ok bool) { // AuthorizeSign returns the list of SignOption for a Sign request. func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) { - cert, claims, err := p.authorizeToken(token, p.audiences.Sign) + crt, claims, err := p.authorizeToken(token, p.audiences.Sign) if err != nil { return nil, err } @@ -118,7 +118,10 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, if v, err := unsafeParseSigned(token); err == nil { data.SetToken(v) } - data.Set("Cert", cert) + + // The nebula certificate will be available using the template variable Crt. + // For example {{ .Crt.Details.Groups }} can be used to get all the groups. + // data.SetCertificate(crt) templateOptions, err := TemplateOptions(p.Options, data) if err != nil { @@ -131,14 +134,14 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, newProvisionerExtensionOption(TypeNebula, p.Name, ""), profileLimitDuration{ def: p.claimer.DefaultTLSCertDuration(), - notBefore: cert.Details.NotBefore, - notAfter: cert.Details.NotAfter, + notBefore: crt.Details.NotBefore, + notAfter: crt.Details.NotAfter, }, // validators commonNameValidator(claims.Subject), nebulaSANsValidator{ - Name: cert.Details.Name, - IPs: cert.Details.Ips, + Name: crt.Details.Name, + IPs: crt.Details.Ips, }, defaultPublicKeyValidator{}, newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), @@ -152,16 +155,16 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti return nil, errs.Unauthorized("ssh is disabled for nebula provisioner '%s'", p.Name) } - cert, claims, err := p.authorizeToken(token, p.audiences.SSHSign) + crt, claims, err := p.authorizeToken(token, p.audiences.SSHSign) if err != nil { return nil, err } // Default template attributes. keyID := claims.Subject - principals := make([]string, len(cert.Details.Ips)+1) - principals[0] = cert.Details.Name - for i, ipnet := range cert.Details.Ips { + principals := make([]string, len(crt.Details.Ips)+1) + principals[0] = crt.Details.Name + for i, ipnet := range crt.Details.Ips { principals[i+1] = ipnet.IP.String() } @@ -173,8 +176,8 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti // Check that the token only contains valid principals. v := nebulaPrincipalsValidator{ - Name: cert.Details.Name, - IPs: cert.Details.Ips, + Name: crt.Details.Name, + IPs: crt.Details.Ips, } if err := v.Valid(*opts); err != nil { return nil, err @@ -217,7 +220,10 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti if v, err := unsafeParseSigned(token); err == nil { data.SetToken(v) } - data.Set("Cert", cert) + + // The nebula certificate will be available using the template variable Crt. + // For example {{ .Crt.Details.Groups }} can be used to get all the groups. + // data.SetCertificate(crt) templateOptions, err := TemplateSSHOptions(p.Options, data) if err != nil { @@ -227,7 +233,7 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti return append(signOptions, templateOptions, // Checks the validity bounds, and set the validity if has not been set. - &sshLimitDuration{p.claimer, cert.Details.NotAfter}, + &sshLimitDuration{p.claimer, crt.Details.NotAfter}, // Validate public key. &sshDefaultPublicKeyValidator{}, // Validate the validity period. @@ -291,7 +297,7 @@ func (p *Nebula) authorizeToken(token string, audiences []string) (*cert.NebulaC if !ok { return nil, nil, errs.Unauthorized("failed to parse token: nbc header is not valid") } - b, err := base64.RawURLEncoding.DecodeString(s) + b, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token: nbc header is not valid")) } From 9ec027688738449a1c5a4da5fc89b85897307515 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 3 Jan 2022 18:54:01 -0800 Subject: [PATCH 05/22] Update certificate set with new api. --- authority/provisioner/nebula.go | 10 +- authority/provisioner/nebula_test.go | 438 +++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 4 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 authority/provisioner/nebula_test.go diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index 8cafadfb..6079070b 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -103,7 +103,7 @@ func (p *Nebula) GetType() Type { } // GetEncryptedKey returns the base provisioner encrypted key if it's defined. -func (p *Nebula) GetEncryptedKey() (kid string, key string, ok bool) { +func (p *Nebula) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } @@ -121,7 +121,7 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, // The nebula certificate will be available using the template variable Crt. // For example {{ .Crt.Details.Groups }} can be used to get all the groups. - // data.SetCertificate(crt) + data.SetAuthorizationCertificate(crt) templateOptions, err := TemplateOptions(p.Options, data) if err != nil { @@ -222,8 +222,8 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti } // The nebula certificate will be available using the template variable Crt. - // For example {{ .Crt.Details.Groups }} can be used to get all the groups. - // data.SetCertificate(crt) + // For example {{ .AuthorizationCrt.Details.Groups }} can be used to get all the groups. + data.SetAuthorizationCertificate(crt) templateOptions, err := TemplateSSHOptions(p.Options, data) if err != nil { @@ -244,7 +244,7 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti } // AuthorizeRenew returns an error if the renewal is disabled. -func (p *Nebula) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { +func (p *Nebula) AuthorizeRenew(ctx context.Context, crt *x509.Certificate) error { if p.claimer.IsDisableRenewal() { return errs.Unauthorized("renew is disabled for nebula provisioner '%s'", p.GetName()) } diff --git a/authority/provisioner/nebula_test.go b/authority/provisioner/nebula_test.go new file mode 100644 index 00000000..4ed72171 --- /dev/null +++ b/authority/provisioner/nebula_test.go @@ -0,0 +1,438 @@ +package provisioner + +import ( + "context" + "crypto" + "crypto/ed25519" + "crypto/rand" + "net" + "reflect" + "strings" + "testing" + "time" + + "github.com/slackhq/nebula/cert" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/randutil" + "go.step.sm/crypto/x25519" + "go.step.sm/crypto/x509util" +) + +func mustNebulaIPNet(t *testing.T, s string) *net.IPNet { + t.Helper() + ip, ipNet, err := net.ParseCIDR(s) + if err != nil { + t.Fatal(err) + } + if ip.To4() == nil { + t.Fatalf("nebula only supports ipv4, have %s", s) + } + ipNet.IP = ip + return ipNet +} + +func mustNebulaCA(t *testing.T) (*cert.NebulaCertificate, ed25519.PrivateKey) { + t.Helper() + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + nc := &cert.NebulaCertificate{ + Details: cert.NebulaCertificateDetails{ + Name: "TestCA", + Groups: []string{"test"}, + Ips: []*net.IPNet{ + mustNebulaIPNet(t, "10.1.0.0/16"), + }, + Subnets: nil, + NotBefore: time.Now(), + NotAfter: time.Now().Add(10 * time.Minute), + PublicKey: pub, + IsCA: true, + }, + } + if err := nc.Sign(priv); err != nil { + t.Fatal(err) + } + return nc, priv +} + +func mustNebulaCert(t *testing.T, name string, ipNet *net.IPNet, groups []string, ca *cert.NebulaCertificate, signer ed25519.PrivateKey) (*cert.NebulaCertificate, crypto.Signer) { + t.Helper() + + pub, priv, err := x25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + + issuer, err := ca.Sha256Sum() + if err != nil { + t.Fatal(err) + } + + nc := &cert.NebulaCertificate{ + Details: cert.NebulaCertificateDetails{ + Name: name, + Ips: []*net.IPNet{ipNet}, + Groups: groups, + NotBefore: time.Now(), + NotAfter: time.Now().Add(5 * time.Minute), + PublicKey: pub, + IsCA: false, + Issuer: issuer, + }, + } + + if err := nc.Sign(signer); err != nil { + t.Fatal(err) + } + + return nc, priv +} + +func mustNebulaProvisioner(t *testing.T) (*Nebula, *cert.NebulaCertificate, ed25519.PrivateKey) { + t.Helper() + + nc, signer := mustNebulaCA(t) + ncPem, err := nc.MarshalToPEM() + if err != nil { + t.Fatal(err) + } + + p := &Nebula{ + Type: TypeNebula.String(), + Name: "nebulous", + Roots: ncPem, + } + if err := p.Init(Config{ + Claims: globalProvisionerClaims, + Audiences: testAudiences, + }); err != nil { + t.Fatal(err) + } + + return p, nc, signer +} + +func mustNebulaToken(t *testing.T, sub, iss, aud string, iat time.Time, sans []string, nc *cert.NebulaCertificate, key crypto.Signer) string { + t.Helper() + ncPEM, err := nc.MarshalToPEM() + if err != nil { + t.Fatal(err) + } + + so := new(jose.SignerOptions) + so.WithType("JWT") + so.WithHeader(NebulaCertHeader, ncPEM) + + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.XEdDSA, Key: key}, so) + if err != nil { + t.Fatal(err) + } + + id, err := randutil.ASCII(64) + if err != nil { + t.Fatal(err) + } + + claims := struct { + jose.Claims + SANS []string `json:"sans"` + }{ + Claims: jose.Claims{ + ID: id, + Subject: sub, + Issuer: iss, + IssuedAt: jose.NewNumericDate(iat), + NotBefore: jose.NewNumericDate(iat), + Expiry: jose.NewNumericDate(iat.Add(5 * time.Minute)), + Audience: []string{aud}, + }, + SANS: sans, + } + tok, err := jose.Signed(sig).Claims(claims).CompactSerialize() + if err != nil { + t.Fatal(err) + } + return tok +} + +func TestNebula_Init(t *testing.T) { + nc, _ := mustNebulaCA(t) + ncPem, err := nc.MarshalToPEM() + if err != nil { + t.Fatal(err) + } + + cfg := Config{ + Claims: globalProvisionerClaims, + Audiences: testAudiences, + } + + type fields struct { + Type string + Name string + Roots []byte + Claims *Claims + Options *Options + } + type args struct { + config Config + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {"ok", fields{"Nebula", "Nebulous", ncPem, nil, nil}, args{cfg}, false}, + {"ok with claims", fields{"Nebula", "Nebulous", ncPem, &Claims{DefaultTLSDur: &Duration{Duration: time.Hour}}, nil}, args{cfg}, false}, + {"ok with options", fields{"Nebula", "Nebulous", ncPem, nil, &Options{X509: &X509Options{Template: x509util.DefaultLeafTemplate}}}, args{cfg}, false}, + {"fail type", fields{"", "Nebulous", ncPem, nil, nil}, args{cfg}, true}, + {"fail name", fields{"Nebula", "", ncPem, nil, nil}, args{cfg}, true}, + {"fail root", fields{"Nebula", "Nebulous", nil, nil, nil}, args{cfg}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Nebula{ + Type: tt.fields.Type, + Name: tt.fields.Name, + Roots: tt.fields.Roots, + Claims: tt.fields.Claims, + Options: tt.fields.Options, + } + if err := p.Init(tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("Nebula.Init() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNebula_GetID(t *testing.T) { + type fields struct { + ID string + Name string + } + tests := []struct { + name string + fields fields + want string + }{ + {"ok with id", fields{"1234", "nebulous"}, "1234"}, + {"ok with name", fields{"", "nebulous"}, "nebula/nebulous"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Nebula{ + ID: tt.fields.ID, + Name: tt.fields.Name, + } + if got := p.GetID(); got != tt.want { + t.Errorf("Nebula.GetID() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_GetIDForToken(t *testing.T) { + type fields struct { + Name string + } + tests := []struct { + name string + fields fields + want string + }{ + {"ok", fields{"nebulous"}, "nebula/nebulous"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Nebula{ + Name: tt.fields.Name, + } + if got := p.GetIDForToken(); got != tt.want { + t.Errorf("Nebula.GetIDForToken() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_GetTokenID(t *testing.T) { + p, ca, signer := mustNebulaProvisioner(t) + c1, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"group"}, ca, signer) + t1 := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], now(), []string{"test.lan", "10.1.0.1"}, c1, priv) + _, claims, err := parseToken(t1) + if err != nil { + t.Fatal(err) + } + + type args struct { + token string + } + tests := []struct { + name string + p *Nebula + args args + want string + wantErr bool + }{ + {"ok", p, args{t1}, claims.ID, false}, + {"fail parse", p, args{"token"}, "", true}, + {"fail claims", p, args{func() string { + parts := strings.Split(t1, ".") + + return parts[0] + ".eyIifQ." + parts[1] + }()}, "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.p.GetTokenID(tt.args.token) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.GetTokenID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Nebula.GetTokenID() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_GetName(t *testing.T) { + type fields struct { + Name string + } + tests := []struct { + name string + fields fields + want string + }{ + {"ok", fields{"nebulous"}, "nebulous"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Nebula{ + Name: tt.fields.Name, + } + if got := p.GetName(); got != tt.want { + t.Errorf("Nebula.GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_GetType(t *testing.T) { + type fields struct { + Type string + } + tests := []struct { + name string + fields fields + want Type + }{ + {"ok", fields{"Nebula"}, TypeNebula}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Nebula{ + Type: tt.fields.Type, + } + if got := p.GetType(); got != tt.want { + t.Errorf("Nebula.GetType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_GetEncryptedKey(t *testing.T) { + tests := []struct { + name string + p *Nebula + wantKid string + wantKey string + wantOk bool + }{ + {"ok", &Nebula{}, "", "", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotKid, gotKey, gotOk := tt.p.GetEncryptedKey() + if gotKid != tt.wantKid { + t.Errorf("Nebula.GetEncryptedKey() gotKid = %v, want %v", gotKid, tt.wantKid) + } + if gotKey != tt.wantKey { + t.Errorf("Nebula.GetEncryptedKey() gotKey = %v, want %v", gotKey, tt.wantKey) + } + if gotOk != tt.wantOk { + t.Errorf("Nebula.GetEncryptedKey() gotOk = %v, want %v", gotOk, tt.wantOk) + } + }) + } +} + +func TestNebula_AuthorizeSign(t *testing.T) { + ctx := context.TODO() + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + ok := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], now(), []string{"test.lan", "10.1.0.1"}, crt, priv) + + pBadOptions, _, _ := mustNebulaProvisioner(t) + pBadOptions.caPool = p.caPool + pBadOptions.Options = &Options{ + X509: &X509Options{ + TemplateData: []byte(`{""}`), + }, + } + + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + wantErr bool + }{ + {"ok", p, args{ctx, ok}, false}, + {"fail token", p, args{ctx, "token"}, true}, + {"fail template", pBadOptions, args{ctx, ok}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := tt.p.AuthorizeSign(tt.args.ctx, tt.args.token) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeSign() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func TestNebula_AuthorizeSSHSign(t *testing.T) { + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + want []SignOption + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.p.AuthorizeSSHSign(tt.args.ctx, tt.args.token) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeSSHSign() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Nebula.AuthorizeSSHSign() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod index 8ce67bbb..8e3d456f 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 - go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706 + go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 go.step.sm/linkedca v0.7.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f diff --git a/go.sum b/go.sum index 43789e2e..0f4d2a26 100644 --- a/go.sum +++ b/go.sum @@ -605,8 +605,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.step.sm/cli-utils v0.7.0 h1:2GvY5Muid1yzp7YQbfCCS+gK3q7zlHjjLL5Z0DXz8ds= go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/E= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706 h1:051Yd6Am2AmHQT6m9TeP0Xt9TbQ09pUfQY6LIm6ygPo= -go.step.sm/crypto v0.13.1-0.20211229193631-2dc253d64706/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= +go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 h1:58QFQDVfw/dQuR9iW/dqvstdhrXbPEzImQF5sBVkI0k= +go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= go.step.sm/linkedca v0.7.0 h1:ydYigs0CgLFkPGjOO4KJcAcAWbuPP8ECF1IsyHdftYc= go.step.sm/linkedca v0.7.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= From 76794ce6135c90121d39ba42e0a9ffd253e59e98 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 4 Jan 2022 12:05:58 -0800 Subject: [PATCH 06/22] Use default SANs without sans in the token. Fix step claim condition in SSH --- authority/provisioner/nebula.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index 6079070b..9cf379f2 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -114,7 +114,16 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, return nil, err } - data := x509util.CreateTemplateData(claims.Subject, claims.SANs) + sans := claims.SANs + if len(sans) == 0 { + sans = make([]string, len(crt.Details.Ips)+1) + sans[0] = crt.Details.Name + for i, ipnet := range crt.Details.Ips { + sans[i+1] = ipnet.IP.String() + } + } + + data := x509util.CreateTemplateData(claims.Subject, sans) if v, err := unsafeParseSigned(token); err == nil { data.SetToken(v) } @@ -171,7 +180,7 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti var signOptions []SignOption // If step ssh options are given, validate them and set key id, principals // and validity. - if claims.Step != nil || claims.Step.SSH != nil { + if claims.Step != nil && claims.Step.SSH != nil { opts := claims.Step.SSH // Check that the token only contains valid principals. @@ -261,7 +270,7 @@ func (p *Nebula) AuthorizeSSHRevoke(ctx context.Context, token string) error { if !p.claimer.IsSSHCAEnabled() { return errs.Unauthorized("ssh is disabled for nebula provisioner '%s'", p.Name) } - if _, _, err := p.authorizeToken(token, p.audiences.Revoke); err != nil { + if _, _, err := p.authorizeToken(token, p.audiences.SSHRevoke); err != nil { return err } return nil From 99845d38bbda1310e5ef8c7e74cf372db9503938 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 4 Jan 2022 12:06:44 -0800 Subject: [PATCH 07/22] Add some extra unit tests for nebula. --- authority/provisioner/nebula_test.go | 283 ++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 6 deletions(-) diff --git a/authority/provisioner/nebula_test.go b/authority/provisioner/nebula_test.go index 4ed72171..84bf2926 100644 --- a/authority/provisioner/nebula_test.go +++ b/authority/provisioner/nebula_test.go @@ -5,6 +5,7 @@ import ( "crypto" "crypto/ed25519" "crypto/rand" + "crypto/x509" "net" "reflect" "strings" @@ -16,6 +17,7 @@ import ( "go.step.sm/crypto/randutil" "go.step.sm/crypto/x25519" "go.step.sm/crypto/x509util" + "golang.org/x/crypto/ssh" ) func mustNebulaIPNet(t *testing.T, s string) *net.IPNet { @@ -98,11 +100,14 @@ func mustNebulaProvisioner(t *testing.T) (*Nebula, *cert.NebulaCertificate, ed25 if err != nil { t.Fatal(err) } - + bTrue := true p := &Nebula{ Type: TypeNebula.String(), Name: "nebulous", Roots: ncPem, + Claims: &Claims{ + EnableSSHCA: &bTrue, + }, } if err := p.Init(Config{ Claims: globalProvisionerClaims, @@ -157,6 +162,54 @@ func mustNebulaToken(t *testing.T, sub, iss, aud string, iat time.Time, sans []s return tok } +func mustNebulaSSHToken(t *testing.T, sub, iss, aud string, iat time.Time, opts *SignSSHOptions, nc *cert.NebulaCertificate, key crypto.Signer) string { + t.Helper() + ncPEM, err := nc.MarshalToPEM() + if err != nil { + t.Fatal(err) + } + + so := new(jose.SignerOptions) + so.WithType("JWT") + so.WithHeader(NebulaCertHeader, ncPEM) + + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.XEdDSA, Key: key}, so) + if err != nil { + t.Fatal(err) + } + + id, err := randutil.ASCII(64) + if err != nil { + t.Fatal(err) + } + + claims := struct { + jose.Claims + Step *stepPayload `json:"step,omitempty"` + }{ + Claims: jose.Claims{ + ID: id, + Subject: sub, + Issuer: iss, + IssuedAt: jose.NewNumericDate(iat), + NotBefore: jose.NewNumericDate(iat), + Expiry: jose.NewNumericDate(iat.Add(5 * time.Minute)), + Audience: []string{aud}, + }, + } + if opts != nil { + claims.Step = &stepPayload{ + SSH: opts, + } + } + + tok, err := jose.Signed(sig).Claims(claims).CompactSerialize() + if err != nil { + t.Fatal(err) + } + return tok +} + func TestNebula_Init(t *testing.T) { nc, _ := mustNebulaCA(t) ncPem, err := nc.MarshalToPEM() @@ -375,6 +428,7 @@ func TestNebula_AuthorizeSign(t *testing.T) { p, ca, signer := mustNebulaProvisioner(t) crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) ok := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], now(), []string{"test.lan", "10.1.0.1"}, crt, priv) + okNoSANs := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], now(), nil, crt, priv) pBadOptions, _, _ := mustNebulaProvisioner(t) pBadOptions.caPool = p.caPool @@ -395,6 +449,7 @@ func TestNebula_AuthorizeSign(t *testing.T) { wantErr bool }{ {"ok", p, args{ctx, ok}, false}, + {"ok no sans", p, args{ctx, okNoSANs}, false}, {"fail token", p, args{ctx, "token"}, true}, {"fail template", pBadOptions, args{ctx, ok}, true}, } @@ -410,6 +465,44 @@ func TestNebula_AuthorizeSign(t *testing.T) { } func TestNebula_AuthorizeSSHSign(t *testing.T) { + ctx := context.TODO() + // Ok provisioner + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + ok := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], now(), &SignSSHOptions{ + CertType: "host", + KeyID: "test.lan", + Principals: []string{"test.lan", "10.1.0.1"}, + }, crt, priv) + okNoOptions := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], now(), nil, crt, priv) + okWithValidity := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], now(), &SignSSHOptions{ + ValidAfter: NewTimeDuration(now().Add(1 * time.Hour)), + ValidBefore: NewTimeDuration(now().Add(10 * time.Hour)), + }, crt, priv) + failUserCert := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], now(), &SignSSHOptions{ + CertType: "user", + }, crt, priv) + failPrincipals := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], now(), &SignSSHOptions{ + CertType: "host", + KeyID: "test.lan", + Principals: []string{"test.lan", "10.1.0.1", "foo.bar"}, + }, crt, priv) + + // Provisioner with SSH disabled + var bFalse bool + pDisabled, _, _ := mustNebulaProvisioner(t) + pDisabled.caPool = p.caPool + pDisabled.Claims.EnableSSHCA = &bFalse + + // Provisioner with bad templates + pBadOptions, _, _ := mustNebulaProvisioner(t) + pBadOptions.caPool = p.caPool + pBadOptions.Options = &Options{ + SSH: &SSHOptions{ + TemplateData: []byte(`{""}`), + }, + } + type args struct { ctx context.Context token string @@ -418,20 +511,198 @@ func TestNebula_AuthorizeSSHSign(t *testing.T) { name string p *Nebula args args - want []SignOption wantErr bool }{ - // TODO: Add test cases. + {"ok", p, args{ctx, ok}, false}, + {"ok no options", p, args{ctx, okNoOptions}, false}, + {"ok with validity", p, args{ctx, okWithValidity}, false}, + {"fail token", p, args{ctx, "token"}, true}, + {"fail user", p, args{ctx, failUserCert}, true}, + {"fail principals", p, args{ctx, failPrincipals}, true}, + {"fail disabled", pDisabled, args{ctx, ok}, true}, + {"fail template", pBadOptions, args{ctx, ok}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.p.AuthorizeSSHSign(tt.args.ctx, tt.args.token) + _, err := tt.p.AuthorizeSSHSign(tt.args.ctx, tt.args.token) if (err != nil) != tt.wantErr { t.Errorf("Nebula.AuthorizeSSHSign() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Nebula.AuthorizeSSHSign() = %v, want %v", got, tt.want) + }) + } +} + +func TestNebula_AuthorizeRenew(t *testing.T) { + ctx := context.TODO() + // Ok provisioner + p, _, _ := mustNebulaProvisioner(t) + + // Provisioner with renewal disabled + bTrue := true + pDisabled, _, _ := mustNebulaProvisioner(t) + pDisabled.Claims.DisableRenewal = &bTrue + + type args struct { + ctx context.Context + crt *x509.Certificate + } + tests := []struct { + name string + p *Nebula + args args + wantErr bool + }{ + {"ok", p, args{ctx, &x509.Certificate{}}, false}, + {"fail disabled", pDisabled, args{ctx, &x509.Certificate{}}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.p.AuthorizeRenew(tt.args.ctx, tt.args.crt); (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeRenew() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNebula_AuthorizeRevoke(t *testing.T) { + ctx := context.TODO() + // Ok provisioner + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + ok := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Revoke[0], now(), nil, crt, priv) + + // Fail different CA + nc, signer := mustNebulaCA(t) + crt, priv = mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, nc, signer) + failToken := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Revoke[0], now(), nil, crt, priv) + + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + wantErr bool + }{ + {"ok", p, args{ctx, ok}, false}, + {"fail token", p, args{ctx, failToken}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.p.AuthorizeRevoke(tt.args.ctx, tt.args.token); (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeRevoke() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNebula_AuthorizeSSHRevoke(t *testing.T) { + ctx := context.TODO() + // Ok provisioner + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + ok := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHRevoke[0], now(), nil, crt, priv) + + // Fail different CA + nc, signer := mustNebulaCA(t) + crt, priv = mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, nc, signer) + failToken := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHRevoke[0], now(), nil, crt, priv) + + // Provisioner with SSH disabled + var bFalse bool + pDisabled, _, _ := mustNebulaProvisioner(t) + pDisabled.caPool = p.caPool + pDisabled.Claims.EnableSSHCA = &bFalse + + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + wantErr bool + }{ + {"ok", p, args{ctx, ok}, false}, + {"fail token", p, args{ctx, failToken}, true}, + {"fail disabled", pDisabled, args{ctx, ok}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.p.AuthorizeSSHRevoke(tt.args.ctx, tt.args.token); (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeSSHRevoke() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNebula_AuthorizeSSHRenew(t *testing.T) { + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + t1 := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHRenew[0], now(), nil, crt, priv) + + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + want *ssh.Certificate + wantErr bool + }{ + {"fail", p, args{context.TODO(), t1}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.p.AuthorizeSSHRenew(tt.args.ctx, tt.args.token) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeSSHRenew() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Nebula.AuthorizeSSHRenew() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNebula_AuthorizeSSHRekey(t *testing.T) { + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + t1 := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHRekey[0], now(), nil, crt, priv) + + type args struct { + ctx context.Context + token string + } + tests := []struct { + name string + p *Nebula + args args + want *ssh.Certificate + want1 []SignOption + wantErr bool + }{ + {"fail", p, args{context.TODO(), t1}, nil, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, err := tt.p.AuthorizeSSHRekey(tt.args.ctx, tt.args.token) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.AuthorizeSSHRekey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Nebula.AuthorizeSSHRekey() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("Nebula.AuthorizeSSHRekey() got1 = %v, want %v", got1, tt.want1) } }) } From de51c2edfbb05487ccdbaeea8b35ba1949da3eea Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 4 Jan 2022 18:16:41 -0800 Subject: [PATCH 08/22] More unit tests for nebula. --- authority/provisioner/nebula_test.go | 266 ++++++++++++++++++++++++++- 1 file changed, 256 insertions(+), 10 deletions(-) diff --git a/authority/provisioner/nebula_test.go b/authority/provisioner/nebula_test.go index 84bf2926..a5925b7d 100644 --- a/authority/provisioner/nebula_test.go +++ b/authority/provisioner/nebula_test.go @@ -7,11 +7,13 @@ import ( "crypto/rand" "crypto/x509" "net" + "net/url" "reflect" "strings" "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/slackhq/nebula/cert" "go.step.sm/crypto/jose" "go.step.sm/crypto/randutil" @@ -26,7 +28,7 @@ func mustNebulaIPNet(t *testing.T, s string) *net.IPNet { if err != nil { t.Fatal(err) } - if ip.To4() == nil { + if ip = ip.To4(); ip == nil { t.Fatalf("nebula only supports ipv4, have %s", s) } ipNet.IP = ip @@ -46,7 +48,7 @@ func mustNebulaCA(t *testing.T) (*cert.NebulaCertificate, ed25519.PrivateKey) { Ips: []*net.IPNet{ mustNebulaIPNet(t, "10.1.0.0/16"), }, - Subnets: nil, + Subnets: []*net.IPNet{}, NotBefore: time.Now(), NotAfter: time.Now().Add(10 * time.Minute), PublicKey: pub, @@ -72,16 +74,24 @@ func mustNebulaCert(t *testing.T, name string, ipNet *net.IPNet, groups []string t.Fatal(err) } + invertedGroups := make(map[string]struct{}, len(groups)) + for _, name := range groups { + invertedGroups[name] = struct{}{} + } + + t1 := time.Now().Truncate(time.Second) nc := &cert.NebulaCertificate{ Details: cert.NebulaCertificateDetails{ - Name: name, - Ips: []*net.IPNet{ipNet}, - Groups: groups, - NotBefore: time.Now(), - NotAfter: time.Now().Add(5 * time.Minute), - PublicKey: pub, - IsCA: false, - Issuer: issuer, + Name: name, + Ips: []*net.IPNet{ipNet}, + Subnets: []*net.IPNet{}, + Groups: groups, + NotBefore: t1, + NotAfter: t1.Add(5 * time.Minute), + PublicKey: pub, + IsCA: false, + Issuer: issuer, + InvertedGroups: invertedGroups, }, } @@ -244,6 +254,10 @@ func TestNebula_Init(t *testing.T) { {"fail type", fields{"", "Nebulous", ncPem, nil, nil}, args{cfg}, true}, {"fail name", fields{"Nebula", "", ncPem, nil, nil}, args{cfg}, true}, {"fail root", fields{"Nebula", "Nebulous", nil, nil, nil}, args{cfg}, true}, + {"fail bad root", fields{"Nebula", "Nebulous", ncPem[:16], nil, nil}, args{cfg}, true}, + {"fail bad claims", fields{"Nebula", "Nebulous", ncPem, &Claims{ + MinTLSDur: &Duration{Duration: 0}, + }, nil}, args{cfg}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -707,3 +721,235 @@ func TestNebula_AuthorizeSSHRekey(t *testing.T) { }) } } + +func TestNebula_authorizeToken(t *testing.T) { + t1 := now() + p, ca, signer := mustNebulaProvisioner(t) + crt, priv := mustNebulaCert(t, "test.lan", mustNebulaIPNet(t, "10.1.0.1/16"), []string{"test"}, ca, signer) + ok := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], t1, []string{"10.1.0.1"}, crt, priv) + okNoSANs := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], t1, nil, crt, priv) + okSSH := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], t1, &SignSSHOptions{ + CertType: "host", + KeyID: "test.lan", + Principals: []string{"test.lan"}, + }, crt, priv) + okSSHNoOptions := mustNebulaSSHToken(t, "test.lan", p.Name, p.audiences.SSHSign[0], t1, nil, crt, priv) + + // Token with errors + failNotBefore := mustNebulaToken(t, "test.lan", p.Name, p.audiences.Sign[0], t1.Add(1*time.Hour), []string{"10.1.0.1"}, crt, priv) + failIssuer := mustNebulaToken(t, "test.lan", "foo", p.audiences.Sign[0], t1, []string{"10.1.0.1"}, crt, priv) + failAudience := mustNebulaToken(t, "test.lan", p.Name, "foo", t1, []string{"10.1.0.1"}, crt, priv) + failSubject := mustNebulaToken(t, "", p.Name, p.audiences.Sign[0], t1, []string{"10.1.0.1"}, crt, priv) + + // Not a nebula token + jwk, err := generateJSONWebKey() + if err != nil { + t.Fatal(err) + } + simpleToken, err := generateSimpleToken("iss", "aud", jwk) + if err != nil { + t.Fatal(err) + } + + // Provisioner with a different CA + p2, _, _ := mustNebulaProvisioner(t) + + x509Claims := jose.Claims{ + ID: "[REPLACEME]", + Subject: "test.lan", + Issuer: p.Name, + IssuedAt: jose.NewNumericDate(t1), + NotBefore: jose.NewNumericDate(t1), + Expiry: jose.NewNumericDate(t1.Add(5 * time.Minute)), + Audience: []string{p.audiences.Sign[0]}, + } + sshClaims := jose.Claims{ + ID: "[REPLACEME]", + Subject: "test.lan", + Issuer: p.Name, + IssuedAt: jose.NewNumericDate(t1), + NotBefore: jose.NewNumericDate(t1), + Expiry: jose.NewNumericDate(t1.Add(5 * time.Minute)), + Audience: []string{p.audiences.SSHSign[0]}, + } + + type args struct { + token string + audiences []string + } + tests := []struct { + name string + p *Nebula + args args + want *cert.NebulaCertificate + want1 *jwtPayload + wantErr bool + }{ + {"ok x509", p, args{ok, p.audiences.Sign}, crt, &jwtPayload{ + Claims: x509Claims, + SANs: []string{"10.1.0.1"}, + }, false}, + {"ok x509 no sans", p, args{okNoSANs, p.audiences.Sign}, crt, &jwtPayload{ + Claims: x509Claims, + }, false}, + {"ok ssh", p, args{okSSH, p.audiences.SSHSign}, crt, &jwtPayload{ + Claims: sshClaims, + Step: &stepPayload{ + SSH: &SignSSHOptions{ + CertType: "host", + KeyID: "test.lan", + Principals: []string{"test.lan"}, + }, + }, + }, false}, + {"ok ssh no principals", p, args{okSSHNoOptions, p.audiences.SSHSign}, crt, &jwtPayload{ + Claims: sshClaims, + }, false}, + {"fail parse", p, args{"bad.token", p.audiences.Sign}, nil, nil, true}, + {"fail header", p, args{simpleToken, p.audiences.Sign}, nil, nil, true}, + {"fail verify", p2, args{ok, p.audiences.Sign}, nil, nil, true}, + {"fail claims nbf", p, args{failNotBefore, p.audiences.Sign}, nil, nil, true}, + {"fail claims iss", p, args{failIssuer, p.audiences.Sign}, nil, nil, true}, + {"fail claims aud", p, args{failAudience, p.audiences.Sign}, nil, nil, true}, + {"fail claims sub", p, args{failSubject, p.audiences.Sign}, nil, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, err := tt.p.authorizeToken(tt.args.token, tt.args.audiences) + if (err != nil) != tt.wantErr { + t.Errorf("Nebula.authorizeToken() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Nebula.authorizeToken() got = %#v, want %#v", got, tt.want) + t.Error(cmp.Equal(got, tt.want)) + } + + if got1 != nil && tt.want1 != nil { + tt.want1.ID = got1.ID + } + + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("Nebula.authorizeToken() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func Test_nebulaSANsValidator_Valid(t *testing.T) { + ipNet := mustNebulaIPNet(t, "10.1.2.3/16") + type fields struct { + Name string + IPs []*net.IPNet + } + type args struct { + req *x509.CertificateRequest + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {"ok", fields{"dns.name", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + DNSNames: []string{"dns.name"}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, false}, + {"ok name only", fields{"dns.name", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + DNSNames: []string{"dns.name"}, + }}, false}, + {"ok ip only", fields{"dns.name", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, false}, + {"ok email name", fields{"jane@doe.org", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + EmailAddresses: []string{"jane@doe.org"}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, false}, + {"ok uri name", fields{"urn:foobar", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + URIs: []*url.URL{{Scheme: "urn", Opaque: "foobar"}}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, false}, + {"ok ip name", fields{"127.0.0.1", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv4(10, 1, 2, 3)}, + }}, false}, + {"ok multiple ips", fields{"dns.name", []*net.IPNet{ipNet, mustNebulaIPNet(t, "10.2.2.3/8")}}, args{&x509.CertificateRequest{ + DNSNames: []string{"dns.name"}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3), net.IPv4(10, 2, 2, 3)}, + }}, false}, + {"fail dns", fields{"fail.name", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + DNSNames: []string{"dns.name"}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, true}, + {"fail email", fields{"fail@doe.org", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + EmailAddresses: []string{"jane@doe.org"}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, true}, + {"fail uri", fields{"urn:barfoo", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + URIs: []*url.URL{{Scheme: "urn", Opaque: "foobar"}}, + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 3)}, + }}, true}, + {"fail ip", fields{"127.0.0.1", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + IPAddresses: []net.IP{net.IPv4(10, 1, 2, 1), net.IPv4(10, 1, 2, 3)}, + }}, true}, + {"fail nebula ip", fields{"dns.name", []*net.IPNet{ipNet}}, args{&x509.CertificateRequest{ + DNSNames: []string{"dns.name"}, + IPAddresses: []net.IP{net.IPv4(10, 2, 2, 3)}, + }}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := nebulaSANsValidator{ + Name: tt.fields.Name, + IPs: tt.fields.IPs, + } + if err := v.Valid(tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("nebulaSANsValidator.Valid() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_nebulaPrincipalsValidator_Valid(t *testing.T) { + ipNet := mustNebulaIPNet(t, "10.1.2.3/16") + + type fields struct { + Name string + IPs []*net.IPNet + } + type args struct { + got SignSSHOptions + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {"ok", fields{"dns.name", []*net.IPNet{ipNet}}, args{SignSSHOptions{ + Principals: []string{"dns.name", "10.1.2.3"}, + }}, false}, + {"ok name", fields{"dns.name", []*net.IPNet{ipNet}}, args{SignSSHOptions{ + Principals: []string{"dns.name"}, + }}, false}, + {"ok ip", fields{"dns.name", []*net.IPNet{ipNet}}, args{SignSSHOptions{ + Principals: []string{"10.1.2.3"}, + }}, false}, + {"fail name", fields{"dns.name", []*net.IPNet{ipNet}}, args{SignSSHOptions{ + Principals: []string{"foo.name", "10.1.2.3"}, + }}, true}, + {"fail ip", fields{"dns.name", []*net.IPNet{ipNet}}, args{SignSSHOptions{ + Principals: []string{"dns.name", "10.2.2.3"}, + }}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := nebulaPrincipalsValidator{ + Name: tt.fields.Name, + IPs: tt.fields.IPs, + } + if err := v.Valid(tt.args.got); (err != nil) != tt.wantErr { + t.Errorf("nebulaPrincipalsValidator.Valid() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From 6a1d0cb9f82e73281f4f66d4ad8c6bfe7b506c97 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 4 Jan 2022 18:42:57 -0800 Subject: [PATCH 09/22] Add linkedca conversions. --- authority/admin/db.go | 2 ++ authority/provisioners.go | 36 ++++++++++++++++++++++++++++++++++++ go.mod | 3 ++- go.sum | 4 ++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/authority/admin/db.go b/authority/admin/db.go index 8a6339d9..bf34a3c2 100644 --- a/authority/admin/db.go +++ b/authority/admin/db.go @@ -44,6 +44,8 @@ func UnmarshalProvisionerDetails(typ linkedca.Provisioner_Type, data []byte) (*l v.Data = new(linkedca.ProvisionerDetails_SSHPOP) case linkedca.Provisioner_SCEP: v.Data = new(linkedca.ProvisionerDetails_SCEP) + case linkedca.Provisioner_NEBULA: + v.Data = new(linkedca.ProvisionerDetails_Nebula) default: return nil, fmt.Errorf("unsupported provisioner type %s", typ) } diff --git a/authority/provisioners.go b/authority/provisioners.go index 5a0c354f..c7be830e 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -710,6 +710,22 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Claims: claims, Options: options, }, nil + case *linkedca.ProvisionerDetails_Nebula: + var roots []byte + for i, root := range d.Nebula.GetRoots() { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, root...) + } + return &provisioner.Nebula{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil default: return nil, fmt.Errorf("provisioner %s not implemented", p.Type) } @@ -937,6 +953,26 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro X509Template: x509Template, SshTemplate: sshTemplate, }, nil + case *provisioner.Nebula: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Id: p.ID, + Type: linkedca.Provisioner_NEBULA, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_Nebula{ + Nebula: &linkedca.NebulaProvisioner{ + Roots: provisionerPEMToLinkedca(p.Roots), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil default: return nil, fmt.Errorf("provisioner %s not implemented", p.GetType()) } diff --git a/go.mod b/go.mod index 8e3d456f..23f5142e 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 - go.step.sm/linkedca v0.7.0 + go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f google.golang.org/api v0.47.0 @@ -48,3 +48,4 @@ require ( // replace github.com/smallstep/nosql => ../nosql // replace go.step.sm/crypto => ../crypto // replace go.step.sm/cli-utils => ../cli-utils +// replace go.step.sm/linkedca => ../linkedca diff --git a/go.sum b/go.sum index 0f4d2a26..b0851c73 100644 --- a/go.sum +++ b/go.sum @@ -607,8 +607,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.13.1-0.20220104020740-cfaa65f61443 h1:58QFQDVfw/dQuR9iW/dqvstdhrXbPEzImQF5sBVkI0k= go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= -go.step.sm/linkedca v0.7.0 h1:ydYigs0CgLFkPGjOO4KJcAcAWbuPP8ECF1IsyHdftYc= -go.step.sm/linkedca v0.7.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6 h1:rh+wCqQhMeXqUfb7Kycd8DtO7K0JckYsPZS3nOuEtGA= +go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= From 6600f1253e0f878cb2a8e14fe8e9ccf292b3701a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 5 Jan 2022 10:12:49 -0800 Subject: [PATCH 10/22] Fix error messages after review. --- authority/provisioner/nebula.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index 9cf379f2..d9001ec4 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -52,7 +52,7 @@ func (p *Nebula) Init(config Config) error { p.caPool, err = cert.NewCAPoolFromBytes(p.Roots) if err != nil { - return errs.InternalServer("failed to start ca pool: %v", err) + return errs.InternalServer("failed to create ca pool: %v", err) } p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) @@ -399,7 +399,7 @@ func (v nebulaSANsValidator) Valid(req *x509.CertificateRequest) error { } } if !valid { - return errs.Forbidden("certificate request does not contain the valid IP addresses - got %v, want %v", req.IPAddresses, v.IPs) + return errs.Forbidden("certificate request does not contain a valid IP addresses - got %v, want %v", req.IPAddresses, v.IPs) } } } @@ -433,7 +433,7 @@ func (v nebulaPrincipalsValidator) Valid(got SignSSHOptions) error { if !valid { return errs.Forbidden( - "ssh certificate principals does contain a valid name or IP address - got %v, want %s or %v", + "ssh certificate principals does not contain a valid name or IP address - got %v, want %s or %v", got.Principals, v.Name, v.IPs, ) } From f49a4b326f24aecb2f06efa0e3a218ef20310114 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 5 Jan 2022 10:54:09 -0800 Subject: [PATCH 11/22] Add missing comments. --- authority/provisioner/nebula.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index d9001ec4..55142e55 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -23,6 +23,15 @@ const ( NebulaCertHeader jose.HeaderKey = "nbc" ) +// Nebula is a provisioner that verifies tokens signed using nebula private +// keys. The tokens embed a header parameter with the certificate that can be +// used to verify the signature. Those certificates are verified using the +// Nebula CAs encoded in Roots. The process is similar to X5C or SSHPOP tokens. +// +// Because of Nebula "leaf" certificates use X25519 keys, the tokens are signed +// using XEd25519 defined at +// https://signal.org/docs/specifications/xeddsa/#xeddsa and implemented by +// go.step.sm/crypto/x25519. type Nebula struct { ID string `json:"-"` Type string `json:"type"` @@ -35,6 +44,7 @@ type Nebula struct { audiences Audiences } +// Init verifies and initializes the nebula provisioner. func (p *Nebula) Init(config Config) error { switch { case p.Type == "": From b424aa3dc13f1571e76353520c8281e9606da52f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 6 Jan 2022 11:19:46 -0800 Subject: [PATCH 12/22] Add nebula header and use der version of certificate. --- authority/provisioner/nebula.go | 16 ++++++++-------- authority/provisioner/nebula_test.go | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index 55142e55..ec0aaeee 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -20,7 +20,7 @@ import ( const ( // NebulaCertHeader is the token header that contains a nebula certificate. - NebulaCertHeader jose.HeaderKey = "nbc" + NebulaCertHeader jose.HeaderKey = "nebula" ) // Nebula is a provisioner that verifies tokens signed using nebula private @@ -308,21 +308,21 @@ func (p *Nebula) authorizeToken(token string, audiences []string) (*cert.NebulaC } // Extract nebula certificate - nbc, ok := jwt.Headers[0].ExtraHeaders[NebulaCertHeader] + h, ok := jwt.Headers[0].ExtraHeaders[NebulaCertHeader] if !ok { - return nil, nil, errs.Unauthorized("failed to parse token: nbc header is missing") + return nil, nil, errs.Unauthorized("failed to parse token: nebula header is missing") } - s, ok := nbc.(string) + s, ok := h.(string) if !ok { - return nil, nil, errs.Unauthorized("failed to parse token: nbc header is not valid") + return nil, nil, errs.Unauthorized("failed to parse token: nebula header is not valid") } b, err := base64.StdEncoding.DecodeString(s) if err != nil { - return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token: nbc header is not valid")) + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token: nebula header is not valid")) } - c, _, err := cert.UnmarshalNebulaCertificateFromPEM(b) + c, err := cert.UnmarshalNebulaCertificate(b) if err != nil { - return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse nebula certificate: nbc header is not valid")) + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse nebula certificate: nebula header is not valid")) } // Validate nebula certificate against CA diff --git a/authority/provisioner/nebula_test.go b/authority/provisioner/nebula_test.go index a5925b7d..bc539af1 100644 --- a/authority/provisioner/nebula_test.go +++ b/authority/provisioner/nebula_test.go @@ -131,14 +131,14 @@ func mustNebulaProvisioner(t *testing.T) (*Nebula, *cert.NebulaCertificate, ed25 func mustNebulaToken(t *testing.T, sub, iss, aud string, iat time.Time, sans []string, nc *cert.NebulaCertificate, key crypto.Signer) string { t.Helper() - ncPEM, err := nc.MarshalToPEM() + ncDer, err := nc.Marshal() if err != nil { t.Fatal(err) } so := new(jose.SignerOptions) so.WithType("JWT") - so.WithHeader(NebulaCertHeader, ncPEM) + so.WithHeader(NebulaCertHeader, ncDer) sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.XEdDSA, Key: key}, so) if err != nil { @@ -174,14 +174,14 @@ func mustNebulaToken(t *testing.T, sub, iss, aud string, iat time.Time, sans []s func mustNebulaSSHToken(t *testing.T, sub, iss, aud string, iat time.Time, opts *SignSSHOptions, nc *cert.NebulaCertificate, key crypto.Signer) string { t.Helper() - ncPEM, err := nc.MarshalToPEM() + ncDer, err := nc.Marshal() if err != nil { t.Fatal(err) } so := new(jose.SignerOptions) so.WithType("JWT") - so.WithHeader(NebulaCertHeader, ncPEM) + so.WithHeader(NebulaCertHeader, ncDer) sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.XEdDSA, Key: key}, so) if err != nil { From 449a9fdfd6d16f55afcc9a84e706d91089fb21fa Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 6 Jan 2022 12:00:58 -0800 Subject: [PATCH 13/22] Address review comments. --- authority/provisioner/nebula.go | 52 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index ec0aaeee..f6d89aa8 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -9,7 +9,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/slackhq/nebula/cert" + nebula "github.com/slackhq/nebula/cert" "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" "go.step.sm/crypto/sshutil" @@ -19,16 +19,17 @@ import ( ) const ( - // NebulaCertHeader is the token header that contains a nebula certificate. + // NebulaCertHeader is the token header that contains a Nebula certificate. NebulaCertHeader jose.HeaderKey = "nebula" ) -// Nebula is a provisioner that verifies tokens signed using nebula private -// keys. The tokens embed a header parameter with the certificate that can be -// used to verify the signature. Those certificates are verified using the -// Nebula CAs encoded in Roots. The process is similar to X5C or SSHPOP tokens. +// Nebula is a provisioner that verifies tokens signed using Nebula private +// keys. The tokens contain a Nebula certificate in the header, which can be +// used to verify the token signature. The certificates are themselves verified +// using the Nebula CA certificates encoded in Roots. The verification process +// is similar to the process for X5C tokens. // -// Because of Nebula "leaf" certificates use X25519 keys, the tokens are signed +// Because Nebula "leaf" certificates use X25519 keys, the tokens are signed // using XEd25519 defined at // https://signal.org/docs/specifications/xeddsa/#xeddsa and implemented by // go.step.sm/crypto/x25519. @@ -40,11 +41,11 @@ type Nebula struct { Claims *Claims `json:"claims,omitempty"` Options *Options `json:"options,omitempty"` claimer *Claimer - caPool *cert.NebulaCAPool + caPool *nebula.NebulaCAPool audiences Audiences } -// Init verifies and initializes the nebula provisioner. +// Init verifies and initializes the Nebula provisioner. func (p *Nebula) Init(config Config) error { switch { case p.Type == "": @@ -60,7 +61,7 @@ func (p *Nebula) Init(config Config) error { return err } - p.caPool, err = cert.NewCAPoolFromBytes(p.Roots) + p.caPool, err = nebula.NewCAPoolFromBytes(p.Roots) if err != nil { return errs.InternalServer("failed to create ca pool: %v", err) } @@ -138,7 +139,7 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, data.SetToken(v) } - // The nebula certificate will be available using the template variable Crt. + // The Nebula certificate will be available using the template variable Crt. // For example {{ .Crt.Details.Groups }} can be used to get all the groups. data.SetAuthorizationCertificate(crt) @@ -168,7 +169,7 @@ func (p *Nebula) AuthorizeSign(ctx context.Context, token string) ([]SignOption, } // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. -// Currently the nebula provisioner only grant host ssh certificates +// Currently the Nebula provisioner only grants host SSH certificates. func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { return nil, errs.Unauthorized("ssh is disabled for nebula provisioner '%s'", p.Name) @@ -240,7 +241,7 @@ func (p *Nebula) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOpti data.SetToken(v) } - // The nebula certificate will be available using the template variable Crt. + // The Nebula certificate will be available using the template variable Crt. // For example {{ .AuthorizationCrt.Details.Groups }} can be used to get all the groups. data.SetAuthorizationCertificate(crt) @@ -301,13 +302,13 @@ func (p *Nebula) validateToken(token string, audiences []string) error { return err } -func (p *Nebula) authorizeToken(token string, audiences []string) (*cert.NebulaCertificate, *jwtPayload, error) { +func (p *Nebula) authorizeToken(token string, audiences []string) (*nebula.NebulaCertificate, *jwtPayload, error) { jwt, err := jose.ParseSigned(token) if err != nil { return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token")) } - // Extract nebula certificate + // Extract Nebula certificate h, ok := jwt.Headers[0].ExtraHeaders[NebulaCertHeader] if !ok { return nil, nil, errs.Unauthorized("failed to parse token: nebula header is missing") @@ -320,7 +321,7 @@ func (p *Nebula) authorizeToken(token string, audiences []string) (*cert.NebulaC if err != nil { return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse token: nebula header is not valid")) } - c, err := cert.UnmarshalNebulaCertificate(b) + c, err := nebula.UnmarshalNebulaCertificate(b) if err != nil { return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse nebula certificate: nebula header is not valid")) } @@ -401,15 +402,18 @@ func (v nebulaSANsValidator) Valid(req *x509.CertificateRequest) error { } // Check ip network if !valid { - for _, ipnet := range v.IPs { - if ip.Equal(ipnet.IP) { + for _, ipNet := range v.IPs { + if ip.Equal(ipNet.IP) { valid = true break } } } if !valid { - return errs.Forbidden("certificate request does not contain a valid IP addresses - got %v, want %v", req.IPAddresses, v.IPs) + for _, ipNet := range v.IPs { + ips = append(ips, ipNet.IP) + } + return errs.Forbidden("certificate request contains invalid IP addresses - got %v, want %v", req.IPAddresses, ips) } } } @@ -423,7 +427,7 @@ type nebulaPrincipalsValidator struct { } // Valid checks that the SignSSHOptions principals contains only names in the -// nebula certificate. +// Nebula certificate. func (v nebulaPrincipalsValidator) Valid(got SignSSHOptions) error { for _, p := range got.Principals { var valid bool @@ -442,9 +446,13 @@ func (v nebulaPrincipalsValidator) Valid(got SignSSHOptions) error { } if !valid { + ips := make([]net.IP, len(v.IPs)) + for i, ipNet := range v.IPs { + ips[i] = ipNet.IP + } return errs.Forbidden( - "ssh certificate principals does not contain a valid name or IP address - got %v, want %s or %v", - got.Principals, v.Name, v.IPs, + "ssh certificate principals contains invalid name or IP addresses - got %v, want %s or %v", + got.Principals, v.Name, ips, ) } } From 98044cf08d11fa1563cb9d0a05ec8f9f625cd663 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 6 Jan 2022 12:04:57 -0800 Subject: [PATCH 14/22] Use a tagged 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 23f5142e..4aea248e 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 - go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6 + go.step.sm/linkedca v0.9.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f google.golang.org/api v0.47.0 diff --git a/go.sum b/go.sum index b0851c73..32e26f1a 100644 --- a/go.sum +++ b/go.sum @@ -607,8 +607,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.13.1-0.20220104020740-cfaa65f61443 h1:58QFQDVfw/dQuR9iW/dqvstdhrXbPEzImQF5sBVkI0k= go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= -go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6 h1:rh+wCqQhMeXqUfb7Kycd8DtO7K0JckYsPZS3nOuEtGA= -go.step.sm/linkedca v0.8.1-0.20220105022833-86b7a26c91f6/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.9.0 h1:xKXZoRXy4B7LeGBZozq62IQ0p3v8dT33O9UOMpVtRtI= +go.step.sm/linkedca v0.9.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= From 01a76445eac83cf3b15d569cb245b1c94f064d15 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 6 Jan 2022 12:50:26 -0800 Subject: [PATCH 15/22] Upgrade go.step.sm/crypto --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4aea248e..16633d1c 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.0 - go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 + go.step.sm/crypto v0.14.0 go.step.sm/linkedca v0.9.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f diff --git a/go.sum b/go.sum index 32e26f1a..f66f7934 100644 --- a/go.sum +++ b/go.sum @@ -605,8 +605,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.step.sm/cli-utils v0.7.0 h1:2GvY5Muid1yzp7YQbfCCS+gK3q7zlHjjLL5Z0DXz8ds= go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/E= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443 h1:58QFQDVfw/dQuR9iW/dqvstdhrXbPEzImQF5sBVkI0k= -go.step.sm/crypto v0.13.1-0.20220104020740-cfaa65f61443/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= +go.step.sm/crypto v0.14.0 h1:HzSkUDwqKhODKpsTxevJz956U2xVDZ3sDdGQVwR6Ttw= +go.step.sm/crypto v0.14.0/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g= go.step.sm/linkedca v0.9.0 h1:xKXZoRXy4B7LeGBZozq62IQ0p3v8dT33O9UOMpVtRtI= go.step.sm/linkedca v0.9.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= From 09202248164188289555a6dea21be1ce267cba6f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 7 Jan 2022 11:09:32 -0800 Subject: [PATCH 16/22] Fix error message. --- authority/provisioner/nebula.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/nebula.go b/authority/provisioner/nebula.go index f6d89aa8..a77f4281 100644 --- a/authority/provisioner/nebula.go +++ b/authority/provisioner/nebula.go @@ -326,12 +326,12 @@ func (p *Nebula) authorizeToken(token string, audiences []string) (*nebula.Nebul return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("failed to parse nebula certificate: nebula header is not valid")) } - // Validate nebula certificate against CA + // Validate nebula certificate against CAs if valid, err := c.Verify(now(), p.caPool); !valid { if err != nil { - return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("token is not valid: failed to unmarshal certificate")) + return nil, nil, errs.UnauthorizedErr(err, errs.WithMessage("token is not valid: failed to verify certificate against configured CA")) } - return nil, nil, errs.Unauthorized("token is not valid: failed to unmarshal certificate") + return nil, nil, errs.Unauthorized("token is not valid: failed to verify certificate against configured CA") } var pub interface{} From de549adf2d259baf5215006d9b3524f2b885ba4f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 7 Jan 2022 11:24:59 -0800 Subject: [PATCH 17/22] Do not add extra new lines when creating nebula provisioners --- authority/provisioners.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/authority/provisioners.go b/authority/provisioners.go index c7be830e..a98b78a6 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -1,6 +1,7 @@ package authority import ( + "bytes" "context" "crypto/x509" "encoding/json" @@ -713,7 +714,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, case *linkedca.ProvisionerDetails_Nebula: var roots []byte for i, root := range d.Nebula.GetRoots() { - if i > 0 { + if i > 0 && !bytes.HasSuffix(root, []byte{'\n'}) { roots = append(roots, '\n') } roots = append(roots, root...) From a5455d35728d8308a11c808994103bcc9163c412 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 10 Jan 2022 15:49:37 +0100 Subject: [PATCH 18/22] Improve errors related to template execution failures (slightly) --- authority/ssh.go | 8 +++++++ authority/ssh_test.go | 5 ++++ authority/testdata/templates/badjson.tpl | 3 +++ authority/tls.go | 10 ++++++++ authority/tls_test.go | 29 ++++++++++++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 authority/testdata/templates/badjson.tpl diff --git a/authority/ssh.go b/authority/ssh.go index 7b8f26e2..e582eddd 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -198,6 +198,14 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi errs.WithKeyVal("signOptions", signOpts), ) } + // explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors + if strings.HasPrefix(err.Error(), "error unmarshaling certificate") { + msg := strings.TrimSpace(strings.TrimPrefix(err.Error(), "error unmarshaling certificate:")) + return nil, errs.ApplyOptions( + errs.InternalServer("authority.Sign: failed to apply certificate template: %s", msg), + errs.WithKeyVal("signOptions", signOpts), + ) + } return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH") } diff --git a/authority/ssh_test.go b/authority/ssh_test.go index b0907a79..523cfc8d 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -172,6 +172,10 @@ func TestAuthority_SignSSH(t *testing.T) { SSH: &provisioner.SSHOptions{Template: `{{ fail "an error"}}`}, }, sshutil.CreateTemplateData(sshutil.UserCert, "key-id", []string{"user"})) assert.FatalError(t, err) + userFailTemplateFile, err := provisioner.TemplateSSHOptions(&provisioner.Options{ + SSH: &provisioner.SSHOptions{TemplateFile: "./testdata/templates/badjson.tpl"}, + }, sshutil.CreateTemplateData(sshutil.UserCert, "key-id", []string{"user"})) + assert.FatalError(t, err) now := time.Now() @@ -222,6 +226,7 @@ func TestAuthority_SignSSH(t *testing.T) { {"fail-no-host-key", fields{signer, nil}, args{pub, provisioner.SignSSHOptions{CertType: "host"}, []provisioner.SignOption{hostTemplate}}, want{}, true}, {"fail-bad-type", fields{signer, nil}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userTemplate, sshTestModifier{CertType: 100}}}, want{}, true}, {"fail-custom-template", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userFailTemplate, userOptions}}, want{}, true}, + {"fail-custom-template-file", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userFailTemplateFile, userOptions}}, want{}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/authority/testdata/templates/badjson.tpl b/authority/testdata/templates/badjson.tpl new file mode 100644 index 00000000..1088f142 --- /dev/null +++ b/authority/testdata/templates/badjson.tpl @@ -0,0 +1,3 @@ +{ + "subject": "badjson.localhost, +} \ No newline at end of file diff --git a/authority/tls.go b/authority/tls.go index dfa88ac3..6affd15d 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "encoding/pem" "net/http" + "strings" "time" "github.com/pkg/errors" @@ -126,6 +127,15 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign errs.WithKeyVal("signOptions", signOpts), ) } + // explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors + if strings.HasPrefix(err.Error(), "error unmarshaling certificate") { + msg := strings.TrimSpace(strings.TrimPrefix(err.Error(), "error unmarshaling certificate:")) + return nil, errs.ApplyOptions( + errs.InternalServer("authority.Sign: failed to apply certificate template: %s", msg), + errs.WithKeyVal("csr", csr), + errs.WithKeyVal("signOptions", signOpts), + ) + } return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign", opts...) } diff --git a/authority/tls_test.go b/authority/tls_test.go index 3acf05f5..1fd3d6cb 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -396,6 +396,35 @@ ZYtQ9Ot36qc= code: http.StatusBadRequest, } }, + "fail bad JSON template file": func(t *testing.T) *signTest { + csr := getCSR(t, priv) + testAuthority := testAuthority(t) + p, ok := testAuthority.provisioners.Load("step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc") + if !ok { + t.Fatal("provisioner not found") + } + p.(*provisioner.JWK).Options = &provisioner.Options{ + X509: &provisioner.X509Options{ + TemplateFile: "./testdata/templates/badjson.tpl", + }, + } + testExtraOpts, err := testAuthority.Authorize(ctx, token) + assert.FatalError(t, err) + testAuthority.db = &db.MockAuthDB{ + MStoreCertificate: func(crt *x509.Certificate) error { + assert.Equals(t, crt.Subject.CommonName, "smallstep test") + return nil + }, + } + return &signTest{ + auth: testAuthority, + csr: csr, + extraOpts: testExtraOpts, + signOpts: signOpts, + err: errors.New("authority.Sign: failed to apply certificate template"), + code: http.StatusInternalServerError, + } + }, "ok": func(t *testing.T) *signTest { csr := getCSR(t, priv) _a := testAuthority(t) From 0475a4d26ffdb243bb66eeaf81f7ed317658294f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 12 Jan 2022 10:41:36 +0100 Subject: [PATCH 19/22] Refactor extraction of JSON template syntax errors --- authority/ssh.go | 5 ++--- authority/tls.go | 22 ++++++++++++++++++---- authority/tls_test.go | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/authority/ssh.go b/authority/ssh.go index e582eddd..4a67b28c 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -200,10 +200,9 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi } // explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors if strings.HasPrefix(err.Error(), "error unmarshaling certificate") { - msg := strings.TrimSpace(strings.TrimPrefix(err.Error(), "error unmarshaling certificate:")) - return nil, errs.ApplyOptions( - errs.InternalServer("authority.Sign: failed to apply certificate template: %s", msg), + return nil, errs.InternalServerErr(templatingError(err), errs.WithKeyVal("signOptions", signOpts), + errs.WithMessage("error applying certificate template"), ) } return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH") diff --git a/authority/tls.go b/authority/tls.go index 6affd15d..d4838342 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -7,7 +7,9 @@ import ( "crypto/x509" "encoding/asn1" "encoding/base64" + "encoding/json" "encoding/pem" + "fmt" "net/http" "strings" "time" @@ -127,13 +129,12 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign errs.WithKeyVal("signOptions", signOpts), ) } - // explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors + // explicitly check for unmarshaling errors, which are most probably caused by JSON template (syntax) errors if strings.HasPrefix(err.Error(), "error unmarshaling certificate") { - msg := strings.TrimSpace(strings.TrimPrefix(err.Error(), "error unmarshaling certificate:")) - return nil, errs.ApplyOptions( - errs.InternalServer("authority.Sign: failed to apply certificate template: %s", msg), + return nil, errs.InternalServerErr(templatingError(err), errs.WithKeyVal("csr", csr), errs.WithKeyVal("signOptions", signOpts), + errs.WithMessage("error applying certificate template"), ) } return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign", opts...) @@ -559,3 +560,16 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { tlsCrt.Leaf = resp.Certificate return &tlsCrt, nil } + +// templatingError tries to extract more information about the cause of +// an error related to (most probably) malformed template data and adds +// this to the error message. +func templatingError(err error) error { + cause := errors.Cause(err) + var syntaxError *json.SyntaxError + if errors.As(err, &syntaxError) { + // offset is arguably not super clear to the user, but it's the best we can do here + cause = fmt.Errorf("%s at offset %d", cause.Error(), syntaxError.Offset) + } + return errors.Wrap(cause, "error applying certificate template") +} diff --git a/authority/tls_test.go b/authority/tls_test.go index 1fd3d6cb..bc0f0526 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -421,7 +421,7 @@ ZYtQ9Ot36qc= csr: csr, extraOpts: testExtraOpts, signOpts: signOpts, - err: errors.New("authority.Sign: failed to apply certificate template"), + err: errors.New("error applying certificate template"), code: http.StatusInternalServerError, } }, From a3cf6bac36c07cb45b6aaafa9f84a53bf26e384b Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 12 Jan 2022 11:15:39 +0100 Subject: [PATCH 20/22] Add special handling for *json.UnmarshalTypeError --- authority/ssh_test.go | 11 ++++-- .../{badjson.tpl => badjsonsyntax.tpl} | 0 authority/testdata/templates/badjsonvalue.tpl | 10 ++++++ authority/tls.go | 9 ++++- authority/tls_test.go | 35 +++++++++++++++++-- 5 files changed, 58 insertions(+), 7 deletions(-) rename authority/testdata/templates/{badjson.tpl => badjsonsyntax.tpl} (100%) create mode 100644 authority/testdata/templates/badjsonvalue.tpl diff --git a/authority/ssh_test.go b/authority/ssh_test.go index 523cfc8d..2ea65352 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -172,8 +172,12 @@ func TestAuthority_SignSSH(t *testing.T) { SSH: &provisioner.SSHOptions{Template: `{{ fail "an error"}}`}, }, sshutil.CreateTemplateData(sshutil.UserCert, "key-id", []string{"user"})) assert.FatalError(t, err) - userFailTemplateFile, err := provisioner.TemplateSSHOptions(&provisioner.Options{ - SSH: &provisioner.SSHOptions{TemplateFile: "./testdata/templates/badjson.tpl"}, + userJSONSyntaxErrorTemplateFile, err := provisioner.TemplateSSHOptions(&provisioner.Options{ + SSH: &provisioner.SSHOptions{TemplateFile: "./testdata/templates/badjsonsyntax.tpl"}, + }, sshutil.CreateTemplateData(sshutil.UserCert, "key-id", []string{"user"})) + assert.FatalError(t, err) + userJSONValueErrorTemplateFile, err := provisioner.TemplateSSHOptions(&provisioner.Options{ + SSH: &provisioner.SSHOptions{TemplateFile: "./testdata/templates/badjsonvalue.tpl"}, }, sshutil.CreateTemplateData(sshutil.UserCert, "key-id", []string{"user"})) assert.FatalError(t, err) @@ -226,7 +230,8 @@ func TestAuthority_SignSSH(t *testing.T) { {"fail-no-host-key", fields{signer, nil}, args{pub, provisioner.SignSSHOptions{CertType: "host"}, []provisioner.SignOption{hostTemplate}}, want{}, true}, {"fail-bad-type", fields{signer, nil}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userTemplate, sshTestModifier{CertType: 100}}}, want{}, true}, {"fail-custom-template", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userFailTemplate, userOptions}}, want{}, true}, - {"fail-custom-template-file", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userFailTemplateFile, userOptions}}, want{}, true}, + {"fail-custom-template-syntax-error-file", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userJSONSyntaxErrorTemplateFile, userOptions}}, want{}, true}, + {"fail-custom-template-syntax-value-file", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userJSONValueErrorTemplateFile, userOptions}}, want{}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/authority/testdata/templates/badjson.tpl b/authority/testdata/templates/badjsonsyntax.tpl similarity index 100% rename from authority/testdata/templates/badjson.tpl rename to authority/testdata/templates/badjsonsyntax.tpl diff --git a/authority/testdata/templates/badjsonvalue.tpl b/authority/testdata/templates/badjsonvalue.tpl new file mode 100644 index 00000000..107cde02 --- /dev/null +++ b/authority/testdata/templates/badjsonvalue.tpl @@ -0,0 +1,10 @@ +{ + "subject": 1, + "sans": {{ toJson .SANs }}, +{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} + "keyUsage": ["keyEncipherment", "digitalSignature"], +{{- else }} + "keyUsage": ["digitalSignature"], +{{- end }} + "extKeyUsage": ["serverAuth", "clientAuth"] +} \ No newline at end of file diff --git a/authority/tls.go b/authority/tls.go index d4838342..78f629e7 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -566,10 +566,17 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { // this to the error message. func templatingError(err error) error { cause := errors.Cause(err) - var syntaxError *json.SyntaxError + var ( + syntaxError *json.SyntaxError + typeError *json.UnmarshalTypeError + ) if errors.As(err, &syntaxError) { // offset is arguably not super clear to the user, but it's the best we can do here cause = fmt.Errorf("%s at offset %d", cause.Error(), syntaxError.Offset) } + if errors.As(err, &typeError) { + // slightly rewriting the default error message to include the offset + cause = fmt.Errorf("cannot unmarshal %s at offset %d into Go value of type %s", typeError.Value, typeError.Offset, typeError.Type) + } return errors.Wrap(cause, "error applying certificate template") } diff --git a/authority/tls_test.go b/authority/tls_test.go index bc0f0526..3a0c999e 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -396,7 +396,7 @@ ZYtQ9Ot36qc= code: http.StatusBadRequest, } }, - "fail bad JSON template file": func(t *testing.T) *signTest { + "fail bad JSON syntax template file": func(t *testing.T) *signTest { csr := getCSR(t, priv) testAuthority := testAuthority(t) p, ok := testAuthority.provisioners.Load("step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc") @@ -405,7 +405,7 @@ ZYtQ9Ot36qc= } p.(*provisioner.JWK).Options = &provisioner.Options{ X509: &provisioner.X509Options{ - TemplateFile: "./testdata/templates/badjson.tpl", + TemplateFile: "./testdata/templates/badjsonsyntax.tpl", }, } testExtraOpts, err := testAuthority.Authorize(ctx, token) @@ -421,7 +421,36 @@ ZYtQ9Ot36qc= csr: csr, extraOpts: testExtraOpts, signOpts: signOpts, - err: errors.New("error applying certificate template"), + err: errors.New("error applying certificate template: invalid character"), + code: http.StatusInternalServerError, + } + }, + "fail bad JSON value template file": func(t *testing.T) *signTest { + csr := getCSR(t, priv) + testAuthority := testAuthority(t) + p, ok := testAuthority.provisioners.Load("step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc") + if !ok { + t.Fatal("provisioner not found") + } + p.(*provisioner.JWK).Options = &provisioner.Options{ + X509: &provisioner.X509Options{ + TemplateFile: "./testdata/templates/badjsonvalue.tpl", + }, + } + testExtraOpts, err := testAuthority.Authorize(ctx, token) + assert.FatalError(t, err) + testAuthority.db = &db.MockAuthDB{ + MStoreCertificate: func(crt *x509.Certificate) error { + assert.Equals(t, crt.Subject.CommonName, "smallstep test") + return nil + }, + } + return &signTest{ + auth: testAuthority, + csr: csr, + extraOpts: testExtraOpts, + signOpts: signOpts, + err: errors.New("error applying certificate template: cannot unmarshal"), code: http.StatusInternalServerError, } }, From 4afcdd55ff3bdca0e6589be4b561f283a32fbc69 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 12 Jan 2022 12:24:38 -0800 Subject: [PATCH 21/22] Update doc line on WithSSHGetHosts --- authority/options.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authority/options.go b/authority/options.go index 0f80cbbf..a18d40e2 100644 --- a/authority/options.go +++ b/authority/options.go @@ -101,8 +101,8 @@ func WithSSHBastionFunc(fn func(ctx context.Context, user, host string) (*config } } -// WithSSHGetHosts sets a custom function to get the bastion for a -// given user-host pair. +// WithSSHGetHosts sets a custom function to return a list of step ssh enabled +// hosts. func WithSSHGetHosts(fn func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)) Option { return func(a *Authority) error { a.sshGetHostsFunc = fn From 50c3bce98d230a7a682b4f1f9bb7f7307ae2adbb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 12 Jan 2022 21:34:38 +0100 Subject: [PATCH 22/22] Change if/if to if/else-if when checking the type of JSON error --- authority/tls.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/authority/tls.go b/authority/tls.go index 78f629e7..cc049655 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -573,8 +573,7 @@ func templatingError(err error) error { if errors.As(err, &syntaxError) { // offset is arguably not super clear to the user, but it's the best we can do here cause = fmt.Errorf("%s at offset %d", cause.Error(), syntaxError.Offset) - } - if errors.As(err, &typeError) { + } else if errors.As(err, &typeError) { // slightly rewriting the default error message to include the offset cause = fmt.Errorf("cannot unmarshal %s at offset %d into Go value of type %s", typeError.Value, typeError.Offset, typeError.Type) }