From 0369151bfad6d95e5efdc27040c7ea1408eacc52 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Tue, 27 Apr 2021 08:18:35 -0700 Subject: [PATCH 001/291] use InsecureSkipVerify for validation The server will not yet have a valid certificate so we need to disable certificate validation in the HTTPGetter. --- acme/api/handler.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/acme/api/handler.go b/acme/api/handler.go index 2a6d3a02..b05bd0c4 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -64,8 +64,14 @@ type HandlerOptions struct { // NewHandler returns a new ACME API handler. func NewHandler(ops HandlerOptions) api.RouterHandler { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } client := http.Client{ - Timeout: 30 * time.Second, + Timeout: 30 * time.Second, + Transport: transport, } dialer := &net.Dialer{ Timeout: 30 * time.Second, From 7b5d6968a5e4b7ded7d8f1f30e0c6dba62ab6e2e Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 3 May 2021 12:48:20 -0700 Subject: [PATCH 002/291] first commit --- acme/db/nosql/nosql.go | 7 +- api/api.go | 3 +- api/sign.go | 12 +- api/ssh.go | 15 +- authority/admin.go | 12 + authority/authority.go | 41 ++- authority/{ => config}/config.go | 26 +- authority/{ => config}/config_test.go | 2 +- authority/config/ssh.go | 94 ++++++ authority/{ => config}/tls_options.go | 2 +- authority/{ => config}/tls_options_test.go | 2 +- authority/{ => config}/types.go | 2 +- authority/{ => config}/types_test.go | 2 +- authority/mgmt/api/admin.go | 112 +++++++ authority/mgmt/api/authConfig.go | 121 +++++++ authority/mgmt/api/handler.go | 50 +++ authority/mgmt/api/provisioner.go | 122 +++++++ authority/mgmt/config.go | 357 +++++++++++++++++++++ authority/mgmt/db.go | 149 +++++++++ authority/mgmt/db/nosql/admin.go | 163 ++++++++++ authority/mgmt/db/nosql/authConfig.go | 113 +++++++ authority/mgmt/db/nosql/nosql.go | 91 ++++++ authority/mgmt/db/nosql/provisioner.go | 174 ++++++++++ authority/mgmt/errors.go | 191 +++++++++++ authority/options.go | 9 +- authority/provisioner/claims.go | 21 ++ authority/ssh.go | 104 +----- authority/tls.go | 5 +- ca/bootstrap.go | 88 ++--- ca/ca.go | 12 +- ca/mgmtClient.go | 107 ++++++ ca/tls.go | 1 - commands/app.go | 4 +- commands/onboard.go | 4 +- pki/pki.go | 26 +- 35 files changed, 2032 insertions(+), 212 deletions(-) create mode 100644 authority/admin.go rename authority/{ => config}/config.go (93%) rename authority/{ => config}/config_test.go (99%) create mode 100644 authority/config/ssh.go rename authority/{ => config}/tls_options.go (99%) rename authority/{ => config}/tls_options_test.go (99%) rename authority/{ => config}/types.go (98%) rename authority/{ => config}/types_test.go (99%) create mode 100644 authority/mgmt/api/admin.go create mode 100644 authority/mgmt/api/authConfig.go create mode 100644 authority/mgmt/api/handler.go create mode 100644 authority/mgmt/api/provisioner.go create mode 100644 authority/mgmt/config.go create mode 100644 authority/mgmt/db.go create mode 100644 authority/mgmt/db/nosql/admin.go create mode 100644 authority/mgmt/db/nosql/authConfig.go create mode 100644 authority/mgmt/db/nosql/nosql.go create mode 100644 authority/mgmt/db/nosql/provisioner.go create mode 100644 authority/mgmt/errors.go create mode 100644 ca/mgmtClient.go diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go index 052f5729..e2edd050 100644 --- a/acme/db/nosql/nosql.go +++ b/acme/db/nosql/nosql.go @@ -23,11 +23,12 @@ var ( // DB is a struct that implements the AcmeDB interface. type DB struct { - db nosqlDB.DB + db nosqlDB.DB + authorityID string } // New configures and returns a new ACME DB backend implemented using a nosql DB. -func New(db nosqlDB.DB) (*DB, error) { +func New(db nosqlDB.DB, authorityID string) (*DB, error) { tables := [][]byte{accountTable, accountByKeyIDTable, authzTable, challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable} for _, b := range tables { @@ -36,7 +37,7 @@ func New(db nosqlDB.DB) (*DB, error) { string(b)) } } - return &DB{db}, nil + return &DB{db, authorityID}, nil } // save writes the new data to the database, overwriting the old data if it diff --git a/api/api.go b/api/api.go index 6a0a7e8f..d6cf9ab7 100644 --- a/api/api.go +++ b/api/api.go @@ -21,6 +21,7 @@ import ( "github.com/go-chi/chi" "github.com/pkg/errors" "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" @@ -32,7 +33,7 @@ type Authority interface { // context specifies the Authorize[Sign|Revoke|etc.] method. Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) AuthorizeSign(ott string) ([]provisioner.SignOption, error) - GetTLSOptions() *authority.TLSOptions + GetTLSOptions() *config.TLSOptions Root(shasum string) (*x509.Certificate, error) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) Renew(peer *x509.Certificate) ([]*x509.Certificate, error) diff --git a/api/sign.go b/api/sign.go index 69e9a1a5..d6fd2bc6 100644 --- a/api/sign.go +++ b/api/sign.go @@ -5,7 +5,7 @@ import ( "encoding/json" "net/http" - "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" ) @@ -37,11 +37,11 @@ func (s *SignRequest) Validate() error { // SignResponse is the response object of the certificate signature request. type SignResponse struct { - ServerPEM Certificate `json:"crt"` - CaPEM Certificate `json:"ca"` - CertChainPEM []Certificate `json:"certChain"` - TLSOptions *authority.TLSOptions `json:"tlsOptions,omitempty"` - TLS *tls.ConnectionState `json:"-"` + ServerPEM Certificate `json:"crt"` + CaPEM Certificate `json:"ca"` + CertChainPEM []Certificate `json:"certChain"` + TLSOptions *config.TLSOptions `json:"tlsOptions,omitempty"` + TLS *tls.ConnectionState `json:"-"` } // Sign is an HTTP handler that reads a certificate request and an diff --git a/api/ssh.go b/api/ssh.go index 9962ad4f..8c0c1aa3 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/templates" @@ -22,12 +23,12 @@ type SSHAuthority interface { RenewSSH(ctx context.Context, cert *ssh.Certificate) (*ssh.Certificate, error) RekeySSH(ctx context.Context, cert *ssh.Certificate, key ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error) - GetSSHRoots(ctx context.Context) (*authority.SSHKeys, error) - GetSSHFederation(ctx context.Context) (*authority.SSHKeys, error) + GetSSHRoots(ctx context.Context) (*config.SSHKeys, error) + GetSSHFederation(ctx context.Context) (*config.SSHKeys, error) GetSSHConfig(ctx context.Context, typ string, data map[string]string) ([]templates.Output, error) CheckSSHHost(ctx context.Context, principal string, token string) (bool, error) - GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]authority.Host, error) - GetSSHBastion(ctx context.Context, user string, hostname string) (*authority.Bastion, error) + GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) + GetSSHBastion(ctx context.Context, user string, hostname string) (*config.Bastion, error) } // SSHSignRequest is the request body of an SSH certificate request. @@ -86,7 +87,7 @@ type SSHCertificate struct { // SSHGetHostsResponse is the response object that returns the list of valid // hosts for SSH. type SSHGetHostsResponse struct { - Hosts []authority.Host `json:"hosts"` + Hosts []config.Host `json:"hosts"` } // MarshalJSON implements the json.Marshaler interface. Returns a quoted, @@ -239,8 +240,8 @@ func (r *SSHBastionRequest) Validate() error { // SSHBastionResponse is the response body used to return the bastion for a // given host. type SSHBastionResponse struct { - Hostname string `json:"hostname"` - Bastion *authority.Bastion `json:"bastion,omitempty"` + Hostname string `json:"hostname"` + Bastion *config.Bastion `json:"bastion,omitempty"` } // SSHSign is an HTTP handler that reads an SignSSHRequest with a one-time-token diff --git a/authority/admin.go b/authority/admin.go new file mode 100644 index 00000000..6c95de4f --- /dev/null +++ b/authority/admin.go @@ -0,0 +1,12 @@ +package authority + +// Admin is the type definining Authority admins. Admins can update Authority +// configuration, provisioners, and even other admins. +type Admin struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + Name string `json:"name"` + Provisioner string `json:"provisioner"` + IsSuperAdmin bool `json:"isSuperAdmin"` + IsDeleted bool `json:"isDeleted"` +} diff --git a/authority/authority.go b/authority/authority.go index 72fa081f..fcb01a03 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -13,6 +13,9 @@ import ( "github.com/smallstep/certificates/cas" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" + authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql" "github.com/smallstep/certificates/authority/provisioner" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" @@ -20,17 +23,15 @@ import ( kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/sshagentkms" "github.com/smallstep/certificates/templates" + "github.com/smallstep/nosql" "go.step.sm/crypto/pemutil" "golang.org/x/crypto/ssh" ) -const ( - legacyAuthority = "step-certificate-authority" -) - // Authority implements the Certificate Authority internal interface. type Authority struct { - config *Config + config *config.Config + mgmtDB *mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection db db.AuthDB @@ -55,14 +56,14 @@ type Authority struct { startTime time.Time // Custom functions - sshBastionFunc func(ctx context.Context, user, hostname string) (*Bastion, error) + sshBastionFunc func(ctx context.Context, user, hostname string) (*config.Bastion, error) sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error) - sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]Host, error) + sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) getIdentityFunc provisioner.GetIdentityFunc } // New creates and initiates a new Authority type. -func New(config *Config, opts ...Option) (*Authority, error) { +func New(config *config.Config, opts ...Option) (*Authority, error) { err := config.Validate() if err != nil { return nil, err @@ -92,7 +93,7 @@ func New(config *Config, opts ...Option) (*Authority, error) { // project without the limitations of the config. func NewEmbedded(opts ...Option) (*Authority, error) { a := &Authority{ - config: &Config{}, + config: &config.Config{}, certificates: new(sync.Map), } @@ -116,7 +117,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) { } // Initialize config required fields. - a.config.init() + a.config.Init() // Initialize authority from options or configuration. if err := a.init(); err != nil { @@ -143,6 +144,22 @@ func (a *Authority) init() error { } } + // Pull AuthConfig from DB. + if true { + mgmtDB, err := authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) + if err != nil { + return err + } + _ac, err := mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) + if err != nil { + return err + } + a.config.AuthorityConfig, err = _ac.ToCertificates() + if err != nil { + return err + } + } + // Initialize key manager if it has not been set in the options. if a.keyManager == nil { var options kmsapi.Options @@ -314,7 +331,7 @@ func (a *Authority) init() error { } // Merge global and configuration claims - claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, globalProvisionerClaims) + claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) if err != nil { return err } @@ -326,7 +343,7 @@ func (a *Authority) init() error { return err } // Initialize provisioners - audiences := a.config.getAudiences() + audiences := a.config.GetAudiences() a.provisioners = provisioner.NewCollection(audiences) config := provisioner.Config{ Claims: claimer.Claims(), diff --git a/authority/config.go b/authority/config/config.go similarity index 93% rename from authority/config.go rename to authority/config/config.go index b02bc2be..1f6b7619 100644 --- a/authority/config.go +++ b/authority/config/config.go @@ -1,4 +1,4 @@ -package authority +package config import ( "encoding/json" @@ -15,6 +15,10 @@ import ( "github.com/smallstep/certificates/templates" ) +const ( + legacyAuthority = "step-certificate-authority" +) + var ( // DefaultTLSOptions represents the default TLS version as well as the cipher // suites used in the TLS certificates. @@ -28,10 +32,12 @@ var ( MaxVersion: 1.2, Renegotiation: false, } - defaultBackdate = time.Minute - defaultDisableRenewal = false - defaultEnableSSHCA = false - globalProvisionerClaims = provisioner.Claims{ + defaultBackdate = time.Minute + defaultDisableRenewal = false + defaultEnableSSHCA = false + // GlobalProvisionerClaims default claims for the Authority. Can be overriden + // by provisioner specific claims. + GlobalProvisionerClaims = provisioner.Claims{ MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs MaxTLSDur: &provisioner.Duration{Duration: 24 * time.Hour}, DefaultTLSDur: &provisioner.Duration{Duration: 24 * time.Hour}, @@ -151,9 +157,9 @@ func LoadConfiguration(filename string) (*Config, error) { return &c, nil } -// initializes the minimal configuration required to create an authority. This +// Init initializes the minimal configuration required to create an authority. This // is mainly used on embedded authorities. -func (c *Config) init() { +func (c *Config) Init() { if c.DNSNames == nil { c.DNSNames = []string{"localhost", "127.0.0.1", "::1"} } @@ -246,13 +252,13 @@ func (c *Config) Validate() error { return err } - return c.AuthorityConfig.Validate(c.getAudiences()) + return c.AuthorityConfig.Validate(c.GetAudiences()) } -// getAudiences returns the legacy and possible urls without the ports that will +// GetAudiences returns the legacy and possible urls without the ports that will // be used as the default provisioner audiences. The CA might have proxies in // front so we cannot rely on the port. -func (c *Config) getAudiences() provisioner.Audiences { +func (c *Config) GetAudiences() provisioner.Audiences { audiences := provisioner.Audiences{ Sign: []string{legacyAuthority}, Revoke: []string{legacyAuthority}, diff --git a/authority/config_test.go b/authority/config/config_test.go similarity index 99% rename from authority/config_test.go rename to authority/config/config_test.go index 87cd3fba..735ac33e 100644 --- a/authority/config_test.go +++ b/authority/config/config_test.go @@ -1,4 +1,4 @@ -package authority +package config import ( "fmt" diff --git a/authority/config/ssh.go b/authority/config/ssh.go new file mode 100644 index 00000000..4ba1bb38 --- /dev/null +++ b/authority/config/ssh.go @@ -0,0 +1,94 @@ +package config + +import ( + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/crypto/jose" + "golang.org/x/crypto/ssh" +) + +// SSHConfig contains the user and host keys. +type SSHConfig struct { + HostKey string `json:"hostKey"` + UserKey string `json:"userKey"` + Keys []*SSHPublicKey `json:"keys,omitempty"` + AddUserPrincipal string `json:"addUserPrincipal,omitempty"` + AddUserCommand string `json:"addUserCommand,omitempty"` + Bastion *Bastion `json:"bastion,omitempty"` +} + +// Bastion contains the custom properties used on bastion. +type Bastion struct { + Hostname string `json:"hostname"` + User string `json:"user,omitempty"` + Port string `json:"port,omitempty"` + Command string `json:"cmd,omitempty"` + Flags string `json:"flags,omitempty"` +} + +// HostTag are tagged with k,v pairs. These tags are how a user is ultimately +// associated with a host. +type HostTag struct { + ID string + Name string + Value string +} + +// Host defines expected attributes for an ssh host. +type Host struct { + HostID string `json:"hid"` + HostTags []HostTag `json:"host_tags"` + Hostname string `json:"hostname"` +} + +// Validate checks the fields in SSHConfig. +func (c *SSHConfig) Validate() error { + if c == nil { + return nil + } + for _, k := range c.Keys { + if err := k.Validate(); err != nil { + return err + } + } + return nil +} + +// SSHPublicKey contains a public key used by federated CAs to keep old signing +// keys for this ca. +type SSHPublicKey struct { + Type string `json:"type"` + Federated bool `json:"federated"` + Key jose.JSONWebKey `json:"key"` + publicKey ssh.PublicKey +} + +// Validate checks the fields in SSHPublicKey. +func (k *SSHPublicKey) Validate() error { + switch { + case k.Type == "": + return errors.New("type cannot be empty") + case k.Type != provisioner.SSHHostCert && k.Type != provisioner.SSHUserCert: + return errors.Errorf("invalid type %s, it must be user or host", k.Type) + case !k.Key.IsPublic(): + return errors.New("invalid key type, it must be a public key") + } + + key, err := ssh.NewPublicKey(k.Key.Key) + if err != nil { + return errors.Wrap(err, "error creating ssh key") + } + k.publicKey = key + return nil +} + +// PublicKey returns the ssh public key. +func (k *SSHPublicKey) PublicKey() ssh.PublicKey { + return k.publicKey +} + +// SSHKeys represents the SSH User and Host public keys. +type SSHKeys struct { + UserKeys []ssh.PublicKey + HostKeys []ssh.PublicKey +} diff --git a/authority/tls_options.go b/authority/config/tls_options.go similarity index 99% rename from authority/tls_options.go rename to authority/config/tls_options.go index 3edde605..5b0575d6 100644 --- a/authority/tls_options.go +++ b/authority/config/tls_options.go @@ -1,4 +1,4 @@ -package authority +package config import ( "crypto/tls" diff --git a/authority/tls_options_test.go b/authority/config/tls_options_test.go similarity index 99% rename from authority/tls_options_test.go rename to authority/config/tls_options_test.go index 96c58c5d..d7ccb20b 100644 --- a/authority/tls_options_test.go +++ b/authority/config/tls_options_test.go @@ -1,4 +1,4 @@ -package authority +package config import ( "crypto/tls" diff --git a/authority/types.go b/authority/config/types.go similarity index 98% rename from authority/types.go rename to authority/config/types.go index 0d0f2a90..6d7b9389 100644 --- a/authority/types.go +++ b/authority/config/types.go @@ -1,4 +1,4 @@ -package authority +package config import ( "encoding/json" diff --git a/authority/types_test.go b/authority/config/types_test.go similarity index 99% rename from authority/types_test.go rename to authority/config/types_test.go index 352c253f..b1a874d6 100644 --- a/authority/types_test.go +++ b/authority/config/types_test.go @@ -1,4 +1,4 @@ -package authority +package config import ( "reflect" diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go new file mode 100644 index 00000000..6f6aaa93 --- /dev/null +++ b/authority/mgmt/api/admin.go @@ -0,0 +1,112 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/smallstep/certificates/api" +) + +// CreateAdminRequest represents the body for a CreateAdmin request. +type CreateAdminRequest struct { + Name string `json:"name"` + Provisioner string `json:"provisioner"` + IsSuperAdmin bool `json:"isSuperAdmin"` +} + +// Validate validates a new-admin request body. +func (car *CreateAdminRequest) Validate() error { + return nil +} + +// UpdateAdminRequest represents the body for a UpdateAdmin request. +type UpdateAdminRequest struct { + Name string `json:"name"` + Provisioner string `json:"provisioner"` + IsSuperAdmin bool `json:"isSuperAdmin"` +} + +// Validate validates a new-admin request body. +func (uar *UpdateAdminRequest) Validate() error { + return nil +} + +// GetAdmin returns the requested admin, or an error. +func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + prov, err := h.db.GetAdmin(ctx, id) + if err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, prov) +} + +// GetAdmins returns all admins associated with the authority. +func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + admins, err := h.db.GetAdmins(ctx) + if err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, admins) +} + +// CreateAdmin creates a new admin. +func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var body CreateAdminRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + } + + adm := &config.Admin{ + Name: body.Name, + Provisioner: body.Provisioner, + IsSuperAdmin: body.IsSuperAdmin, + } + if err := h.db.CreateAdmin(ctx, adm); err != nil { + api.WriteError(w, err) + return + } + api.JSONStatus(w, adm, http.StatusCreated) +} + +// UpdateAdmin updates an existing admin. +func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + var body UpdateAdminRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + if adm, err := h.db.GetAdmin(ctx, id); err != nil { + api.WriteError(w, err) + return + } + + adm.Name = body.Name + adm.Provisioner = body.Provisioner + adm.IsSuperAdmin = body.IsSuperAdmin + + if err := h.db.UpdateAdmin(ctx, adm); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, adm) +} diff --git a/authority/mgmt/api/authConfig.go b/authority/mgmt/api/authConfig.go new file mode 100644 index 00000000..fb23f8e8 --- /dev/null +++ b/authority/mgmt/api/authConfig.go @@ -0,0 +1,121 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" +) + +// CreateAuthConfigRequest represents the body for a CreateAuthConfig request. +type CreateAuthConfigRequest struct { + ASN1DN *authority.ASN1DN `json:"asn1dn,omitempty"` + Claims *config.Claims `json:"claims,omitempty"` + DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` + Backdate string `json:"backdate,omitempty"` +} + +// Validate validates a CreateAuthConfig request body. +func (car *CreateAuthConfigRequest) Validate() error { + return nil +} + +// UpdateAuthConfigRequest represents the body for a UpdateAuthConfig request. +type UpdateAuthConfigRequest struct { + ASN1DN *authority.ASN1DN `json:"asn1dn"` + Claims *config.Claims `json:"claims"` + DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` + Backdate string `json:"backdate,omitempty"` +} + +// Validate validates a new-admin request body. +func (uar *UpdateAuthConfigRequest) Validate() error { + return nil +} + +// GetAuthConfig returns the requested admin, or an error. +func (h *Handler) GetAuthConfig(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + ac, err := h.db.GetAuthConfig(ctx, id) + if err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, ac) +} + +// CreateAuthConfig creates a new admin. +func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var body CreateAuthConfigRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + } + + ac := config.AuthConfig{ + Status: config.StatusActive, + DisableIssuedAtCheck: body.DisableIssuedAtCheck, + Backdate: "1m", + } + if body.ASN1DN != nil { + ac.ASN1DN = body.ASN1DN + } + if body.Claims != nil { + ac.Claims = body.Claims + } + if body.Backdate != "" { + ac.Backdate = body.Backdate + } + if err := h.db.CreateAuthConfig(ctx, ac); err != nil { + api.WriteError(w, err) + return + } + api.JSONStatus(w, ac, http.StatusCreated) +} + +// UpdateAuthConfig updates an existing AuthConfig. +func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + var body UpdateAuthConfigRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + if ac, err := h.db.GetAuthConfig(ctx, id); err != nil { + api.WriteError(w, err) + return + } + + ac.DisableIssuedAtCheck = body.DisableIssuedAtCheck + ac.Status = body.Status + if body.ASN1DN != nil { + ac.ASN1DN = body.ASN1DN + } + if body.Claims != nil { + ac.Claims = body.Claims + } + if body.Backdate != "" { + ac.Backdate = body.Backdate + } + + if err := h.db.UpdateAuthConfig(ctx, ac); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, ac) +} diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go new file mode 100644 index 00000000..f30544c5 --- /dev/null +++ b/authority/mgmt/api/handler.go @@ -0,0 +1,50 @@ +package api + +import ( + "time" + + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/config" +) + +// Clock that returns time in UTC rounded to seconds. +type Clock struct{} + +// Now returns the UTC time rounded to seconds. +func (c *Clock) Now() time.Time { + return time.Now().UTC().Truncate(time.Second) +} + +var clock Clock + +// Handler is the ACME API request handler. +type Handler struct { + db config.DB +} + +// NewHandler returns a new Authority Config Handler. +func NewHandler(db config.DB) api.RouterHandler { + return &Handler{ + db: ops.DB, + } +} + +// Route traffic and implement the Router interface. +func (h *Handler) Route(r api.Router) { + // Provisioners + r.MethodFunc("GET", "/provisioner/{id}", h.GetProvisioner) + r.MethodFunc("GET", "/provisioners", h.GetProvisioners) + r.MethodFunc("POST", "/provisioner", h.CreateProvisioner) + r.MethodFunc("PUT", "/provsiioner/{id}", h.UpdateProvisioner) + + // Admins + r.MethodFunc("GET", "/admin/{id}", h.GetAdmin) + r.MethodFunc("GET", "/admins", h.GetAdmins) + r.MethodFunc("POST", "/admin", h.CreateAdmin) + r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin) + + // AuthConfig + r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig) + r.MethodFunc("POST", "/authconfig", h.CreateAuthConfig) + r.MethodFunc("PUT", "/authconfig/{id}", h.UpdateAuthConfig) +} diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go new file mode 100644 index 00000000..76bbe7cf --- /dev/null +++ b/authority/mgmt/api/provisioner.go @@ -0,0 +1,122 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/config" +) + +// CreateProvisionerRequest represents the body for a CreateProvisioner request. +type CreateProvisionerRequest struct { + Type string `json:"type"` + Name string `json:"name"` + Claims *config.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` +} + +// Validate validates a new-provisioner request body. +func (car *CreateProvisionerRequest) Validate() error { + return nil +} + +// UpdateProvisionerRequest represents the body for a UpdateProvisioner request. +type UpdateProvisionerRequest struct { + Claims *config.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` +} + +// Validate validates a new-provisioner request body. +func (uar *UpdateProvisionerRequest) Validate() error { + return nil +} + +// GetProvisioner returns the requested provisioner, or an error. +func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + prov, err := h.db.GetProvisioner(ctx, id) + if err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, prov) +} + +// GetProvisioners returns all provisioners associated with the authority. +func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + provs, err := h.db.GetProvisioners(ctx) + if err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, provs) +} + +// CreateProvisioner creates a new prov. +func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var body CreateProvisionerRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + } + + prov := &config.Provisioner{ + Type: body.Type, + Name: body.Name, + Claims: body.Claims, + Details: body.Details, + X509Template: body.X509Template, + SSHTemplate: body.SSHTemplate, + } + if err := h.db.CreateProvisioner(ctx, prov); err != nil { + api.WriteError(w, err) + return + } + api.JSONStatus(w, prov, http.StatusCreated) +} + +// UpdateProvisioner updates an existing prov. +func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + + var body UpdateProvisionerRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + if prov, err := h.db.GetProvisioner(ctx, id); err != nil { + api.WriteError(w, err) + return + } + + prov.Claims = body.Claims + prov.Details = body.Provisioner + prov.X509Template = body.X509Template + prov.SSHTemplate = body.SSHTemplate + prov.Status = body.Status + + if err := h.db.UpdateProvisioner(ctx, prov); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, prov) +} diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go new file mode 100644 index 00000000..b5bebceb --- /dev/null +++ b/authority/mgmt/config.go @@ -0,0 +1,357 @@ +package mgmt + +import ( + "github.com/smallstep/certificates/authority/config" + authority "github.com/smallstep/certificates/authority/config" +) + +const ( + DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" +) + +// StatusType is the type for status. +type StatusType int + +const ( + // StatusActive active + StatusActive StatusType = iota + // StatusDeleted deleted + StatusDeleted +) + +type Claims struct { + *X509Claims `json:"x509Claims"` + *SSHClaims `json:"sshClaims"` + DisableRenewal *bool `json:"disableRenewal"` +} + +type X509Claims struct { + Durations *Durations `json:"durations"` +} + +type SSHClaims struct { + UserDuration *Durations `json:"userDurations"` + HostDuration *Durations `json:"hostDuration"` +} + +type Durations struct { + Min string `json:"min"` + Max string `json:"max"` + Default string `json:"default"` +} + +// Admin type. +type Admin struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + Name string `json:"name"` + Provisioner string `json:"provisioner"` + IsSuperAdmin bool `json:"isSuperAdmin"` + Status StatusType `json:"status"` +} + +// Provisioner type. +type Provisioner struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Claims *Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` + Status StatusType `json:"status"` +} + +// AuthConfig represents the Authority Configuration. +type AuthConfig struct { + //*cas.Options `json:"cas"` + ID string `json:"id"` + ASN1DN *config.ASN1DN `json:"template,omitempty"` + Provisioners []*Provisioner `json:"-"` + Claims *Claims `json:"claims,omitempty"` + DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` + Backdate string `json:"backdate,omitempty"` + Status StatusType `json:"status,omitempty"` +} + +func (ac *AuthConfig) ToCertificates() (*config.AuthConfig, error) { + return &authority.AuthConfig{}, nil +} + +/* +// ToCertificates converts the landlord provisioner type to the open source +// provisioner type. +func (p *Provisioner) ToCertificates(ctx context.Context, db database.DB) (provisioner.Interface, error) { + claims, err := p.Claims.ToCertificates() + if err != nil { + return nil, err + } + + details := p.Details.GetData() + if details == nil { + return nil, fmt.Errorf("provisioner does not have any details") + } + + options, err := p.getOptions(ctx, db) + if err != nil { + return nil, err + } + + switch d := details.(type) { + case *ProvisionerDetails_JWK: + k := d.JWK.GetKey() + jwk := new(jose.JSONWebKey) + if err := json.Unmarshal(k.Key.Public, &jwk); err != nil { + return nil, err + } + return &provisioner.JWK{ + Type: p.Type.String(), + Name: p.Name, + Key: jwk, + EncryptedKey: string(k.Key.Private), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_OIDC: + cfg := d.OIDC + return &provisioner.OIDC{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ClientID: cfg.ClientId, + ClientSecret: cfg.ClientSecret, + ConfigurationEndpoint: cfg.ConfigurationEndpoint, + Admins: cfg.Admins, + Domains: cfg.Domains, + Groups: cfg.Groups, + ListenAddress: cfg.ListenAddress, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_GCP: + cfg := d.GCP + return &provisioner.GCP{ + Type: p.Type.String(), + Name: p.Name, + ServiceAccounts: cfg.ServiceAccounts, + ProjectIDs: cfg.ProjectIds, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_AWS: + cfg := d.AWS + return &provisioner.AWS{ + Type: p.Type.String(), + Name: p.Name, + Accounts: cfg.Accounts, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_Azure: + cfg := d.Azure + return &provisioner.Azure{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ResourceGroups: cfg.ResourceGroups, + Audience: cfg.Audience, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_X5C: + var roots []byte + for i, k := range d.X5C.GetRoots() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, b...) + } + } + return &provisioner.X5C{ + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_K8SSA: + var publicKeys []byte + for i, k := range d.K8SSA.GetPublicKeys() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + publicKeys = append(publicKeys, '\n') + } + publicKeys = append(publicKeys, k.Key.Public...) + } + } + return &provisioner.K8sSA{ + Type: p.Type.String(), + Name: p.Name, + PubKeys: publicKeys, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_SSHPOP: + return &provisioner.SSHPOP{ + Type: p.Type.String(), + Name: p.Name, + Claims: claims, + }, nil + case *ProvisionerDetails_ACME: + cfg := d.ACME + return &provisioner.ACME{ + Type: p.Type.String(), + Name: p.Name, + ForceCN: cfg.ForceCn, + Claims: claims, + Options: options, + }, nil + default: + return nil, fmt.Errorf("provisioner %s not implemented", p.Type.String()) + } +} + +// ToCertificates converts the landlord provisioner claims type to the open source +// (step-ca) claims type. +func (c *Claims) ToCertificates() (*provisioner.Claims, error) { + x509, ssh := c.GetX509(), c.GetSsh() + x509Durations := x509.GetDurations() + hostDurations := ssh.GetHostDurations() + userDurations := ssh.GetUserDurations() + enableSSHCA := ssh.GetEnabled() + return &provisioner.Claims{ + MinTLSDur: durationPtr(x509Durations.GetMin()), + MaxTLSDur: durationPtr(x509Durations.GetMax()), + DefaultTLSDur: durationPtr(x509Durations.GetDefault()), + DisableRenewal: &c.DisableRenewal, + MinUserSSHDur: durationPtr(userDurations.GetMin()), + MaxUserSSHDur: durationPtr(userDurations.GetMax()), + DefaultUserSSHDur: durationPtr(userDurations.GetDefault()), + MinHostSSHDur: durationPtr(hostDurations.GetMin()), + MaxHostSSHDur: durationPtr(hostDurations.GetMax()), + DefaultHostSSHDur: durationPtr(hostDurations.GetDefault()), + EnableSSHCA: &enableSSHCA, + }, nil +} + +func durationPtr(d *duration.Duration) *provisioner.Duration { + if d == nil { + return nil + } + return &provisioner.Duration{ + Duration: time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond, + } +} + +func durationValue(d *duration.Duration) provisioner.Duration { + if d == nil { + return provisioner.Duration{} + } + return provisioner.Duration{ + Duration: time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond, + } +} + +func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) { + b, err := json.Marshal(d.GetData()) + if err != nil { + return sql.NullString{}, nil + } + return sql.NullString{ + String: string(b), + Valid: len(b) > 0, + }, nil +} + +func unmarshalDetails(ctx context.Context, db database.DB, typ ProvisionerType, s sql.NullString) (*ProvisionerDetails, error) { + if !s.Valid { + return nil, nil + } + var v isProvisionerDetails_Data + switch typ { + case ProvisionerType_JWK: + p := new(ProvisionerDetails_JWK) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + if p.JWK.Key.Key == nil { + key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) + if err != nil { + return nil, err + } + p.JWK.Key = key + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_OIDC: + v = new(ProvisionerDetails_OIDC) + case ProvisionerType_GCP: + v = new(ProvisionerDetails_GCP) + case ProvisionerType_AWS: + v = new(ProvisionerDetails_AWS) + case ProvisionerType_AZURE: + v = new(ProvisionerDetails_Azure) + case ProvisionerType_ACME: + v = new(ProvisionerDetails_ACME) + case ProvisionerType_X5C: + p := new(ProvisionerDetails_X5C) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.X5C.GetRoots() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_K8SSA: + p := new(ProvisionerDetails_K8SSA) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.K8SSA.GetPublicKeys() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_SSHPOP: + v = new(ProvisionerDetails_SSHPOP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal([]byte(s.String), v); err != nil { + return nil, err + } + return &ProvisionerDetails{Data: v}, nil +} + +func marshalClaims(c *Claims) (sql.NullString, error) { + b, err := json.Marshal(c) + if err != nil { + return sql.NullString{}, nil + } + return sql.NullString{ + String: string(b), + Valid: len(b) > 0, + }, nil +} + +func unmarshalClaims(s sql.NullString) (*Claims, error) { + if !s.Valid { + return nil, nil + } + v := new(Claims) + return v, json.Unmarshal([]byte(s.String), v) +} +*/ diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go new file mode 100644 index 00000000..99228a4d --- /dev/null +++ b/authority/mgmt/db.go @@ -0,0 +1,149 @@ +package mgmt + +import ( + "context" + + "github.com/pkg/errors" +) + +// ErrNotFound is an error that should be used by the authority.DB interface to +// indicate that an entity does not exist. +var ErrNotFound = errors.New("not found") + +// DB is the DB interface expected by the step-ca ACME API. +type DB interface { + CreateProvisioner(ctx context.Context, prov *Provisioner) error + GetProvisioner(ctx context.Context, id string) (*Provisioner, error) + GetProvisioners(ctx context.Context) ([]*Provisioner, error) + UpdateProvisioner(ctx context.Context, prov *Provisioner) error + + CreateAdmin(ctx context.Context, admin *Admin) error + GetAdmin(ctx context.Context, id string) error + GetAdmins(ctx context.Context) ([]*Admin, error) + UpdateAdmin(ctx context.Context, admin *Admin) error + + CreateAuthConfig(ctx context.Context, ac *AuthConfig) error + GetAuthConfig(ctx context.Context, id string) (*AuthConfig, error) + UpdateAuthConfig(ctx context.Context, ac *AuthConfig) error +} + +// MockDB is an implementation of the DB interface that should only be used as +// a mock in tests. +type MockDB struct { + MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error + MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) + MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) + MockUpdateProvisioner func(ctx context.Context, prov *Provisioner) error + + MockCreateAdmin func(ctx context.Context, adm *Admin) error + MockGetAdmin func(ctx context.Context, id string) (*Admin, error) + MockGetAdmins func(ctx context.Context) ([]*Admin, error) + MockUpdateAdmin func(ctx context.Context, adm *Admin) error + + MockCreateAuthConfig func(ctx context.Context, ac *AuthConfig) error + MockGetAuthConfig func(ctx context.Context, id string) (*AuthConfig, error) + MockUpdateAuthConfig func(ctx context.Context, ac *AuthConfig) error + + MockError error + MockRet1 interface{} +} + +// CreateProvisioner mock. +func (m *MockDB) CreateProvisioner(ctx context.Context, prov *Provisioner) error { + if m.MockCreateProvisioner != nil { + return m.MockCreateProvisioner(ctx, prov) + } else if m.MockError != nil { + return m.MockError + } + return m.MockError +} + +// GetProvisioner mock. +func (m *MockDB) GetProvisioner(ctx context.Context, id string) (*Provisioner, error) { + if m.MockGetProvisioner != nil { + return m.MockGetProvisioner(ctx, id) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.(*Provisioner), m.MockError +} + +// GetProvisioners mock +func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { + if m.MockGetProvisioners != nil { + return m.MockGetProvisioners(ctx) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.([]*Provisioner), m.MockError +} + +// UpdateProvisioner mock +func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *Provisioner) error { + if m.MockUpdateProvisioner != nil { + return m.MockUpdateProvisioner(ctx, prov) + } + return m.MockError +} + +// CreateAdmin mock +func (m *MockDB) CreateAdmin(ctx context.Context, admin *Admin) error { + if m.MockCreateAdmin != nil { + return m.MockCreateAdmin(ctx, admin) + } + return m.MockError +} + +// GetAdmin mock. +func (m *MockDB) GetAdmin(ctx context.Context, id string) (*Admin, error) { + if m.MockGetAdmin != nil { + return m.MockGetAdmin(ctx, id) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.(*Admin), m.MockError +} + +// GetAdmins mock +func (m *MockDB) GetAdmins(ctx context.Context) ([]*Admin, error) { + if m.MockGetAdmins != nil { + return m.MockGetAdmins(ctx) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.([]*Admin), m.MockError +} + +// UpdateAdmin mock +func (m *MockDB) UpdateAdmin(ctx context.Context, adm *Admin) error { + if m.UpdateAdmin != nil { + return m.MockUpdateAdmin(ctx, adm) + } + return m.MockError +} + +// CreateAuthConfig mock +func (m *MockDB) CreateAuthConfig(ctx context.Context, admin *AuthConfig) error { + if m.MockCreateAuthConfig != nil { + return m.MockCreateAuthConfig(ctx, admin) + } + return m.MockError +} + +// GetAuthConfig mock. +func (m *MockDB) GetAuthConfig(ctx context.Context, id string) (*AuthConfig, error) { + if m.MockGetAuthConfig != nil { + return m.MockGetAuthConfig(ctx, id) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.(*AuthConfig), m.MockError +} + +// UpdateAuthConfig mock +func (m *MockDB) UpdateAuthConfig(ctx context.Context, adm *AuthConfig) error { + if m.UpdateAuthConfig != nil { + return m.MockUpdateAuthConfig(ctx, adm) + } + return m.MockError +} diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go new file mode 100644 index 00000000..8b33110c --- /dev/null +++ b/authority/mgmt/db/nosql/admin.go @@ -0,0 +1,163 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/nosql" +) + +// dbAdmin is the database representation of the Admin type. +type dbAdmin struct { + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Name string `json:"name"` + Provisioner string `json:"provisioner"` + IsSuperAdmin bool `json:"isSuperAdmin"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` +} + +func (dbp *dbAdmin) clone() *dbAdmin { + u := *dbp + return &u +} + +func (db *DB) getDBAdminBytes(ctx context.Context, id string) ([]byte, error) { + data, err := db.db.Get(authorityAdminsTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading admin %s", id) + } + return data, nil +} + +func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { + data, err := db.getDBAdminBytes(ctx, id) + if err != nil { + return nil, err + } + dba, err := unmarshalDBAdmin(data, id) + if err != nil { + return nil, err + } + if dba.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", dba.ID, db.authorityID) + } + return dba, nil +} + +// GetAdmin retrieves and unmarshals a admin from the database. +func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { + data, err := db.getDBAdminBytes(ctx, id) + if err != nil { + return nil, err + } + adm, err := unmarshalAdmin(data, id) + if err != nil { + return nil, err + } + if adm.Status == mgmt.StatusDeleted { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted") + } + if adm.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", adm.ID, db.authorityID) + } + return adm, nil +} + +func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { + var dba = new(dbAdmin) + if err := json.Unmarshal(data, dba); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) + } + return dba, nil +} + +func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { + var dba = new(dbAdmin) + if err := json.Unmarshal(data, dba); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) + } + adm := &mgmt.Admin{ + ID: dba.ID, + Name: dba.Name, + Provisioner: dba.Provisioner, + IsSuperAdmin: dba.IsSuperAdmin, + } + if !dba.DeletedAt.IsZero() { + adm.Status = mgmt.StatusDeleted + } + return adm, nil +} + +// GetAdmins retrieves and unmarshals all active (not deleted) admins +// from the database. +// TODO should we be paginating? +func (db *DB) GetAdmins(ctx context.Context, az *acme.Authorization) ([]*mgmt.Admin, error) { + dbEntries, err := db.db.List(authorityAdminsTable) + if err != nil { + return nil, errors.Wrap(err, "error loading admins") + } + var admins []*mgmt.Admin + for _, entry := range dbEntries { + adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) + if err != nil { + return nil, err + } + if adm.Status == mgmt.StatusDeleted { + continue + } + if adm.AuthorityID != db.authorityID { + continue + } + admins = append(admins, adm) + } + return admins, nil +} + +// CreateAdmin stores a new admin to the database. +func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { + var err error + adm.ID, err = randID() + if err != nil { + return errors.Wrap(err, "error generating random id for admin") + } + + dba := &dbAdmin{ + ID: adm.ID, + AuthorityID: db.authorityID, + Name: adm.Name, + Provisioner: adm.Provisioner, + IsSuperAdmin: adm.IsSuperAdmin, + CreatedAt: clock.Now(), + } + + return db.save(ctx, dba.ID, dba, nil, "admin", authorityAdminsTable) +} + +// UpdateAdmin saves an updated admin to the database. +func (db *DB) UpdateAdmin(ctx context.Context, adm *mgmt.Admin) error { + old, err := db.getDBAdmin(ctx, adm.ID) + if err != nil { + return err + } + + nu := old.clone() + + // If the admin was active but is now deleted ... + if old.DeletedAt.IsZero() && adm.Status == mgmt.StatusDeleted { + nu.DeletedAt = clock.Now() + } + nu.Provisioner = adm.Provisioner + nu.IsSuperAdmin = adm.IsSuperAdmin + + return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) +} diff --git a/authority/mgmt/db/nosql/authConfig.go b/authority/mgmt/db/nosql/authConfig.go new file mode 100644 index 00000000..6fe1266b --- /dev/null +++ b/authority/mgmt/db/nosql/authConfig.go @@ -0,0 +1,113 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/nosql" +) + +type dbAuthConfig struct { + ID string `json:"id"` + ASN1DN *config.ASN1DN `json:"asn1dn"` + Claims *mgmt.Claims `json:"claims"` + DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` + Backdate string `json:"backdate,omitempty"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` +} + +func (dbp *dbAuthConfig) clone() *dbAuthConfig { + u := *dbp + return &u +} + +func (db *DB) getDBAuthConfigBytes(ctx context.Context, id string) ([]byte, error) { + data, err := db.db.Get(authorityConfigsTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "authConfig %s not found", id) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading authConfig %s", id) + } + return data, nil +} + +func (db *DB) getDBAuthConfig(ctx context.Context, id string) (*dbAuthConfig, error) { + data, err := db.getDBAuthConfigBytes(ctx, id) + if err != nil { + return nil, err + } + + var dba = new(dbAuthConfig) + if err = json.Unmarshal(data, dba); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling authority %s into dbAuthConfig", id) + } + + return dba, nil +} + +// GetAuthConfig retrieves an AuthConfig configuration from the DB. +func (db *DB) GetAuthConfig(ctx context.Context, id string) (*mgmt.AuthConfig, error) { + dba, err := db.getDBAuthConfig(ctx, id) + if err != nil { + return nil, err + } + + provs, err := db.GetProvisioners(ctx) + if err != nil { + return nil, err + } + + return &mgmt.AuthConfig{ + ID: dba.ID, + Provisioners: provs, + ASN1DN: dba.ASN1DN, + Backdate: dba.Backdate, + Claims: dba.Claims, + DisableIssuedAtCheck: dba.DisableIssuedAtCheck, + }, nil +} + +// CreateAuthConfig stores a new provisioner to the database. +func (db *DB) CreateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { + var err error + ac.ID, err = randID() + if err != nil { + return errors.Wrap(err, "error generating random id for provisioner") + } + + dba := &dbAuthConfig{ + ID: ac.ID, + ASN1DN: ac.ASN1DN, + Claims: ac.Claims, + DisableIssuedAtCheck: ac.DisableIssuedAtCheck, + Backdate: ac.Backdate, + CreatedAt: clock.Now(), + } + + return db.save(ctx, dba.ID, dba, nil, "authConfig", authorityConfigsTable) +} + +// UpdateAuthConfig saves an updated provisioner to the database. +func (db *DB) UpdateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { + old, err := db.getDBAuthConfig(ctx, ac.ID) + if err != nil { + return err + } + + nu := old.clone() + + // If the authority was active but is now deleted ... + if old.DeletedAt.IsZero() && ac.Status == mgmt.StatusDeleted { + nu.DeletedAt = clock.Now() + } + nu.Claims = ac.Claims + nu.DisableIssuedAtCheck = ac.DisableIssuedAtCheck + nu.Backdate = ac.Backdate + + return db.save(ctx, old.ID, nu, old, "authConfig", authorityProvisionersTable) +} diff --git a/authority/mgmt/db/nosql/nosql.go b/authority/mgmt/db/nosql/nosql.go new file mode 100644 index 00000000..d71b2804 --- /dev/null +++ b/authority/mgmt/db/nosql/nosql.go @@ -0,0 +1,91 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + nosqlDB "github.com/smallstep/nosql/database" + "go.step.sm/crypto/randutil" +) + +var ( + authorityAdminsTable = []byte("authority_admins") + authorityConfigsTable = []byte("authority_configs") + authorityProvisionersTable = []byte("authority_provisioners") +) + +// DB is a struct that implements the AcmeDB interface. +type DB struct { + db nosqlDB.DB + authorityID string +} + +// New configures and returns a new Authority DB backend implemented using a nosql DB. +func New(db nosqlDB.DB, authorityID string) (*DB, error) { + tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable} + for _, b := range tables { + if err := db.CreateTable(b); err != nil { + return nil, errors.Wrapf(err, "error creating table %s", + string(b)) + } + } + return &DB{db, authorityID}, nil +} + +// save writes the new data to the database, overwriting the old data if it +// existed. +func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface{}, typ string, table []byte) error { + var ( + err error + newB []byte + ) + if nu == nil { + newB = nil + } else { + newB, err = json.Marshal(nu) + if err != nil { + return errors.Wrapf(err, "error marshaling authority type: %s, value: %v", typ, nu) + } + } + var oldB []byte + if old == nil { + oldB = nil + } else { + oldB, err = json.Marshal(old) + if err != nil { + return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old) + } + } + + _, swapped, err := db.db.CmpAndSwap(table, []byte(id), oldB, newB) + switch { + case err != nil: + return errors.Wrapf(err, "error saving authority %s", typ) + case !swapped: + return errors.Errorf("error saving authority %s; changed since last read", typ) + default: + return nil + } +} + +var idLen = 32 + +func randID() (val string, err error) { + val, err = randutil.Alphanumeric(idLen) + if err != nil { + return "", errors.Wrap(err, "error generating random alphanumeric ID") + } + return val, nil +} + +// Clock that returns time in UTC rounded to seconds. +type Clock struct{} + +// Now returns the UTC time rounded to seconds. +func (c *Clock) Now() time.Time { + return time.Now().UTC().Truncate(time.Second) +} + +var clock = new(Clock) diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go new file mode 100644 index 00000000..93dcc47b --- /dev/null +++ b/authority/mgmt/db/nosql/provisioner.go @@ -0,0 +1,174 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/nosql" +) + +// dbProvisioner is the database representation of a Provisioner type. +type dbProvisioner struct { + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type string `json:"type"` + Name string `json:"name"` + Claims *mgmt.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` +} + +func (dbp *dbProvisioner) clone() *dbProvisioner { + u := *dbp + return &u +} + +func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { + data, err := db.db.Get(authorityProvisionersTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", id) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading provisioner %s", id) + } + return data, nil +} + +func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + dbp, err := unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + if dbp.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", dbp.ID, db.authorityID) + } + return dbp, nil +} + +// GetProvisioner retrieves and unmarshals a provisioner from the database. +func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + + prov, err := unmarshalProvisioner(data, id) + if err != nil { + return nil, err + } + if prov.Status == mgmt.StatusDeleted { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", prov.ID) + } + if prov.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", prov.ID, db.authorityID) + } + return prov, nil +} + +func unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) { + var dbp = new(dbProvisioner) + if err := json.Unmarshal(data, dbp); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", id) + } + return dbp, nil +} + +func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { + dbp, err := unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + + prov := &mgmt.Provisioner{ + ID: dbp.ID, + Type: dbp.Type, + Name: dbp.Name, + Claims: dbp.Claims, + X509Template: dbp.X509Template, + SSHTemplate: dbp.SSHTemplate, + } + if !dbp.DeletedAt.IsZero() { + prov.Status = mgmt.StatusDeleted + } + return prov, nil +} + +// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners +// from the database. +// TODO should we be paginating? +func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) { + dbEntries, err := db.db.List(authorityProvisionersTable) + if err != nil { + return nil, errors.Wrap(err, "error loading provisioners") + } + var provs []*mgmt.Provisioner + for _, entry := range dbEntries { + prov, err := unmarshalProvisioner(entry.Value, string(entry.Key)) + if err != nil { + return nil, err + } + if prov.Status == mgmt.StatusDeleted { + continue + } + if prov.AuthorityID != db.authorityID { + continue + } + provs = append(provs, prov) + } + return provs, nil +} + +// CreateProvisioner stores a new provisioner to the database. +func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { + var err error + prov.ID, err = randID() + if err != nil { + return errors.Wrap(err, "error generating random id for provisioner") + } + + dbp := &dbProvisioner{ + ID: prov.ID, + AuthorityID: db.authorityID, + Type: prov.Type, + Name: prov.Name, + Claims: prov.Claims, + Details: prov.Details, + X509Template: prov.X509Template, + SSHTemplate: prov.SSHTemplate, + CreatedAt: clock.Now(), + } + + return db.save(ctx, dbp.ID, dbp, nil, "provisioner", authorityProvisionersTable) +} + +// UpdateProvisioner saves an updated provisioner to the database. +func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { + old, err := db.getDBProvisioner(ctx, prov.ID) + if err != nil { + return err + } + + nu := old.clone() + + // If the provisioner was active but is now deleted ... + if old.DeletedAt.IsZero() && prov.Status == mgmt.StatusDeleted { + nu.DeletedAt = clock.Now() + } + nu.Claims = prov.Claims + nu.Details = prov.Details + nu.X509Template = prov.X509Template + nu.SSHTemplate = prov.SSHTemplate + + return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) +} diff --git a/authority/mgmt/errors.go b/authority/mgmt/errors.go new file mode 100644 index 00000000..f8b6fd65 --- /dev/null +++ b/authority/mgmt/errors.go @@ -0,0 +1,191 @@ +package mgmt + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/logging" +) + +// ProblemType is the type of the ACME problem. +type ProblemType int + +const ( + // ErrorNotFoundType resource not found. + ErrorNotFoundType ProblemType = iota + // ErrorAuthorityMismatchType resource Authority ID does not match the + // context Authority ID. + ErrorAuthorityMismatchType + // ErrorDeletedType resource has been deleted. + ErrorDeletedType + // ErrorServerInternalType internal server error. + ErrorServerInternalType +) + +// String returns the string representation of the acme problem type, +// fulfilling the Stringer interface. +func (ap ProblemType) String() string { + switch ap { + case ErrorNotFoundType: + return "notFound" + case ErrorAuthorityMismatchType: + return "authorityMismatch" + case ErrorDeletedType: + return "deleted" + case ErrorServerInternalType: + return "internalServerError" + default: + return fmt.Sprintf("unsupported error type '%d'", int(ap)) + } +} + +type errorMetadata struct { + details string + status int + typ string + String string +} + +var ( + errorServerInternalMetadata = errorMetadata{ + typ: ErrorServerInternalType.String(), + details: "the server experienced an internal error", + status: 500, + } + errorMap = map[ProblemType]errorMetadata{ + ErrorNotFoundType: { + typ: ErrorNotFoundType.String(), + details: "resource not found", + status: 400, + }, + ErrorAuthorityMismatchType: { + typ: ErrorAuthorityMismatchType.String(), + details: "resource not owned by authority", + status: 401, + }, + ErrorDeletedType: { + typ: ErrorNotFoundType.String(), + details: "resource is deleted", + status: 403, + }, + ErrorServerInternalType: errorServerInternalMetadata, + } +) + +// Error represents an ACME +type Error struct { + Type string `json:"type"` + Detail string `json:"detail"` + Subproblems []interface{} `json:"subproblems,omitempty"` + Identifier interface{} `json:"identifier,omitempty"` + Err error `json:"-"` + Status int `json:"-"` +} + +// NewError creates a new Error type. +func NewError(pt ProblemType, msg string, args ...interface{}) *Error { + return newError(pt, errors.Errorf(msg, args...)) +} + +func newError(pt ProblemType, err error) *Error { + meta, ok := errorMap[pt] + if !ok { + meta = errorServerInternalMetadata + return &Error{ + Type: meta.typ, + Detail: meta.details, + Status: meta.status, + Err: err, + } + } + + return &Error{ + Type: meta.typ, + Detail: meta.details, + Status: meta.status, + Err: err, + } +} + +// NewErrorISE creates a new ErrorServerInternalType Error. +func NewErrorISE(msg string, args ...interface{}) *Error { + return NewError(ErrorServerInternalType, msg, args...) +} + +// WrapError attempts to wrap the internal error. +func WrapError(typ ProblemType, err error, msg string, args ...interface{}) *Error { + switch e := err.(type) { + case nil: + return nil + case *Error: + if e.Err == nil { + e.Err = errors.Errorf(msg+"; "+e.Detail, args...) + } else { + e.Err = errors.Wrapf(e.Err, msg, args...) + } + return e + default: + return newError(typ, errors.Wrapf(err, msg, args...)) + } +} + +// WrapErrorISE shortcut to wrap an internal server error type. +func WrapErrorISE(err error, msg string, args ...interface{}) *Error { + return WrapError(ErrorServerInternalType, err, msg, args...) +} + +// StatusCode returns the status code and implements the StatusCoder interface. +func (e *Error) StatusCode() int { + return e.Status +} + +// Error allows AError to implement the error interface. +func (e *Error) Error() string { + return e.Detail +} + +// Cause returns the internal error and implements the Causer interface. +func (e *Error) Cause() error { + if e.Err == nil { + return errors.New(e.Detail) + } + return e.Err +} + +// ToLog implements the EnableLogger interface. +func (e *Error) ToLog() (interface{}, error) { + b, err := json.Marshal(e) + if err != nil { + return nil, WrapErrorISE(err, "error marshaling authority.Error for logging") + } + return string(b), nil +} + +// WriteError writes to w a JSON representation of the given error. +func WriteError(w http.ResponseWriter, err *Error) { + w.Header().Set("Content-Type", "application/problem+json") + w.WriteHeader(err.StatusCode()) + + // Write errors in the response writer + if rl, ok := w.(logging.ResponseLogger); ok { + rl.WithFields(map[string]interface{}{ + "error": err.Err, + }) + if os.Getenv("STEPDEBUG") == "1" { + if e, ok := err.Err.(errs.StackTracer); ok { + rl.WithFields(map[string]interface{}{ + "stack-trace": fmt.Sprintf("%+v", e), + }) + } + } + } + + if err := json.NewEncoder(w).Encode(err); err != nil { + log.Println(err) + } +} diff --git a/authority/options.go b/authority/options.go index 9594f989..aaf8ffb3 100644 --- a/authority/options.go +++ b/authority/options.go @@ -7,6 +7,7 @@ import ( "encoding/pem" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" @@ -20,7 +21,7 @@ type Option func(*Authority) error // WithConfig replaces the current config with the given one. No validation is // performed in the given value. -func WithConfig(config *Config) Option { +func WithConfig(config *config.Config) Option { return func(a *Authority) error { a.config = config return nil @@ -31,7 +32,7 @@ func WithConfig(config *Config) Option { // the current one. No validation is performed in the given configuration. func WithConfigFile(filename string) Option { return func(a *Authority) (err error) { - a.config, err = LoadConfiguration(filename) + a.config, err = config.LoadConfiguration(filename) return } } @@ -56,7 +57,7 @@ func WithGetIdentityFunc(fn func(ctx context.Context, p provisioner.Interface, e // WithSSHBastionFunc sets a custom function to get the bastion for a // given user-host pair. -func WithSSHBastionFunc(fn func(ctx context.Context, user, host string) (*Bastion, error)) Option { +func WithSSHBastionFunc(fn func(ctx context.Context, user, host string) (*config.Bastion, error)) Option { return func(a *Authority) error { a.sshBastionFunc = fn return nil @@ -65,7 +66,7 @@ func WithSSHBastionFunc(fn func(ctx context.Context, user, host string) (*Bastio // WithSSHGetHosts sets a custom function to get the bastion for a // given user-host pair. -func WithSSHGetHosts(fn func(ctx context.Context, cert *x509.Certificate) ([]Host, error)) Option { +func WithSSHGetHosts(fn func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)) Option { return func(a *Authority) error { a.sshGetHostsFunc = fn return nil diff --git a/authority/provisioner/claims.go b/authority/provisioner/claims.go index 997d9ba3..6c87792c 100644 --- a/authority/provisioner/claims.go +++ b/authority/provisioner/claims.go @@ -7,6 +7,27 @@ import ( "golang.org/x/crypto/ssh" ) +type _Claims struct { + *X509Claims `json:"x509Claims"` + *SSHClaims `json:"sshClaims"` + DisableRenewal *bool `json:"disableRenewal"` +} + +type X509Claims struct { + Durations *Durations `json:"durations"` +} + +type SSHClaims struct { + UserDuration *Durations `json:"userDurations"` + HostDuration *Durations `json:"hostDuration"` +} + +type Durations struct { + Min string `json:"min"` + Max string `json:"max"` + Default string `json:"default"` +} + // Claims so that individual provisioners can override global claims. type Claims struct { // TLS CA properties diff --git a/authority/ssh.go b/authority/ssh.go index bb0ff562..335b6702 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -10,11 +10,11 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/templates" - "go.step.sm/crypto/jose" "go.step.sm/crypto/randutil" "go.step.sm/crypto/sshutil" "golang.org/x/crypto/ssh" @@ -32,103 +32,17 @@ const ( SSHAddUserCommand = "sudo useradd -m ; nc -q0 localhost 22" ) -// SSHConfig contains the user and host keys. -type SSHConfig struct { - HostKey string `json:"hostKey"` - UserKey string `json:"userKey"` - Keys []*SSHPublicKey `json:"keys,omitempty"` - AddUserPrincipal string `json:"addUserPrincipal,omitempty"` - AddUserCommand string `json:"addUserCommand,omitempty"` - Bastion *Bastion `json:"bastion,omitempty"` -} - -// Bastion contains the custom properties used on bastion. -type Bastion struct { - Hostname string `json:"hostname"` - User string `json:"user,omitempty"` - Port string `json:"port,omitempty"` - Command string `json:"cmd,omitempty"` - Flags string `json:"flags,omitempty"` -} - -// HostTag are tagged with k,v pairs. These tags are how a user is ultimately -// associated with a host. -type HostTag struct { - ID string - Name string - Value string -} - -// Host defines expected attributes for an ssh host. -type Host struct { - HostID string `json:"hid"` - HostTags []HostTag `json:"host_tags"` - Hostname string `json:"hostname"` -} - -// Validate checks the fields in SSHConfig. -func (c *SSHConfig) Validate() error { - if c == nil { - return nil - } - for _, k := range c.Keys { - if err := k.Validate(); err != nil { - return err - } - } - return nil -} - -// SSHPublicKey contains a public key used by federated CAs to keep old signing -// keys for this ca. -type SSHPublicKey struct { - Type string `json:"type"` - Federated bool `json:"federated"` - Key jose.JSONWebKey `json:"key"` - publicKey ssh.PublicKey -} - -// Validate checks the fields in SSHPublicKey. -func (k *SSHPublicKey) Validate() error { - switch { - case k.Type == "": - return errors.New("type cannot be empty") - case k.Type != provisioner.SSHHostCert && k.Type != provisioner.SSHUserCert: - return errors.Errorf("invalid type %s, it must be user or host", k.Type) - case !k.Key.IsPublic(): - return errors.New("invalid key type, it must be a public key") - } - - key, err := ssh.NewPublicKey(k.Key.Key) - if err != nil { - return errors.Wrap(err, "error creating ssh key") - } - k.publicKey = key - return nil -} - -// PublicKey returns the ssh public key. -func (k *SSHPublicKey) PublicKey() ssh.PublicKey { - return k.publicKey -} - -// SSHKeys represents the SSH User and Host public keys. -type SSHKeys struct { - UserKeys []ssh.PublicKey - HostKeys []ssh.PublicKey -} - // GetSSHRoots returns the SSH User and Host public keys. -func (a *Authority) GetSSHRoots(context.Context) (*SSHKeys, error) { - return &SSHKeys{ +func (a *Authority) GetSSHRoots(context.Context) (*config.SSHKeys, error) { + return &config.SSHKeys{ HostKeys: a.sshCAHostCerts, UserKeys: a.sshCAUserCerts, }, nil } // GetSSHFederation returns the public keys for federated SSH signers. -func (a *Authority) GetSSHFederation(context.Context) (*SSHKeys, error) { - return &SSHKeys{ +func (a *Authority) GetSSHFederation(context.Context) (*config.SSHKeys, error) { + return &config.SSHKeys{ HostKeys: a.sshCAHostFederatedCerts, UserKeys: a.sshCAUserFederatedCerts, }, nil @@ -194,7 +108,7 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin // GetSSHBastion returns the bastion configuration, for the given pair user, // hostname. -func (a *Authority) GetSSHBastion(ctx context.Context, user string, hostname string) (*Bastion, error) { +func (a *Authority) GetSSHBastion(ctx context.Context, user string, hostname string) (*config.Bastion, error) { if a.sshBastionFunc != nil { bs, err := a.sshBastionFunc(ctx, user, hostname) return bs, errs.Wrap(http.StatusInternalServerError, err, "authority.GetSSHBastion") @@ -568,7 +482,7 @@ func (a *Authority) CheckSSHHost(ctx context.Context, principal string, token st } // GetSSHHosts returns a list of valid host principals. -func (a *Authority) GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]Host, error) { +func (a *Authority) GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) { if a.sshGetHostsFunc != nil { hosts, err := a.sshGetHostsFunc(ctx, cert) return hosts, errs.Wrap(http.StatusInternalServerError, err, "getSSHHosts") @@ -578,9 +492,9 @@ func (a *Authority) GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([] return nil, errs.Wrap(http.StatusInternalServerError, err, "getSSHHosts") } - hosts := make([]Host, len(hostnames)) + hosts := make([]config.Host, len(hostnames)) for i, hn := range hostnames { - hosts[i] = Host{Hostname: hn} + hosts[i] = config.Host{Hostname: hn} } return hosts, nil } diff --git a/authority/tls.go b/authority/tls.go index b7b2f936..1c8b3b8c 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -12,6 +12,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" @@ -23,14 +24,14 @@ import ( ) // GetTLSOptions returns the tls options configured. -func (a *Authority) GetTLSOptions() *TLSOptions { +func (a *Authority) GetTLSOptions() *config.TLSOptions { return a.config.TLS } var oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} var oidSubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14} -func withDefaultASN1DN(def *ASN1DN) provisioner.CertificateModifierFunc { +func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { return func(crt *x509.Certificate, opts provisioner.SignOptions) error { if def == nil { return errors.New("default ASN1DN template cannot be nil") diff --git a/ca/bootstrap.go b/ca/bootstrap.go index c9e859bf..5f06e986 100644 --- a/ca/bootstrap.go +++ b/ca/bootstrap.go @@ -39,40 +39,25 @@ func Bootstrap(token string) (*Client, error) { return NewClient(claims.Audience[0], WithRootSHA256(claims.SHA)) } -// BootstrapServer is a helper function that using the given token returns the -// given http.Server configured with a TLS certificate signed by the Certificate -// Authority. By default the server will kick off a routine that will renew the +// BootstrapClient is a helper function that using the given bootstrap token +// return an http.Client configured with a Transport prepared to do TLS +// connections using the client certificate returned by the certificate +// authority. By default the server will kick off a routine that will renew the // certificate after 2/3rd of the certificate's lifetime has expired. // -// Without any extra option the server will be configured for mTLS, it will -// require and verify clients certificates, but options can be used to drop this -// requirement, the most common will be only verify the certs if given with -// ca.VerifyClientCertIfGiven(), or add extra CAs with -// ca.AddClientCA(*x509.Certificate). -// // Usage: // // Default example with certificate rotation. -// srv, err := ca.BootstrapServer(context.Background(), token, &http.Server{ -// Addr: ":443", -// Handler: handler, -// }) +// client, err := ca.BootstrapClient(ctx.Background(), token) // // // Example canceling automatic certificate rotation. // ctx, cancel := context.WithCancel(context.Background()) // defer cancel() -// srv, err := ca.BootstrapServer(ctx, token, &http.Server{ -// Addr: ":443", -// Handler: handler, -// }) +// client, err := ca.BootstrapClient(ctx, token) // if err != nil { -// return err +// return err // } -// srv.ListenAndServeTLS("", "") -func BootstrapServer(ctx context.Context, token string, base *http.Server, options ...TLSOption) (*http.Server, error) { - if base.TLSConfig != nil { - return nil, errors.New("server TLSConfig is already set") - } - +// resp, err := client.Get("https://internal.smallstep.com") +func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (*http.Client, error) { client, err := Bootstrap(token) if err != nil { return nil, err @@ -88,37 +73,53 @@ func BootstrapServer(ctx context.Context, token string, base *http.Server, optio return nil, err } - // Make sure the tlsConfig have all supported roots on ClientCAs and RootCAs - options = append(options, AddRootsToCAs()) + // Make sure the tlsConfig have all supported roots on RootCAs + options = append(options, AddRootsToRootCAs()) - tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...) + transport, err := client.Transport(ctx, sign, pk, options...) if err != nil { return nil, err } - base.TLSConfig = tlsConfig - return base, nil + return &http.Client{ + Transport: transport, + }, nil } -// BootstrapClient is a helper function that using the given bootstrap token -// return an http.Client configured with a Transport prepared to do TLS -// connections using the client certificate returned by the certificate -// authority. By default the server will kick off a routine that will renew the +// BootstrapServer is a helper function that using the given token returns the +// given http.Server configured with a TLS certificate signed by the Certificate +// Authority. By default the server will kick off a routine that will renew the // certificate after 2/3rd of the certificate's lifetime has expired. // +// Without any extra option the server will be configured for mTLS, it will +// require and verify clients certificates, but options can be used to drop this +// requirement, the most common will be only verify the certs if given with +// ca.VerifyClientCertIfGiven(), or add extra CAs with +// ca.AddClientCA(*x509.Certificate). +// // Usage: // // Default example with certificate rotation. -// client, err := ca.BootstrapClient(ctx.Background(), token) +// srv, err := ca.BootstrapServer(context.Background(), token, &http.Server{ +// Addr: ":443", +// Handler: handler, +// }) // // // Example canceling automatic certificate rotation. // ctx, cancel := context.WithCancel(context.Background()) // defer cancel() -// client, err := ca.BootstrapClient(ctx, token) +// srv, err := ca.BootstrapServer(ctx, token, &http.Server{ +// Addr: ":443", +// Handler: handler, +// }) // if err != nil { -// return err +// return err // } -// resp, err := client.Get("https://internal.smallstep.com") -func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (*http.Client, error) { +// srv.ListenAndServeTLS("", "") +func BootstrapServer(ctx context.Context, token string, base *http.Server, options ...TLSOption) (*http.Server, error) { + if base.TLSConfig != nil { + return nil, errors.New("server TLSConfig is already set") + } + client, err := Bootstrap(token) if err != nil { return nil, err @@ -134,17 +135,16 @@ func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (* return nil, err } - // Make sure the tlsConfig have all supported roots on RootCAs - options = append(options, AddRootsToRootCAs()) + // Make sure the tlsConfig have all supported roots on ClientCAs and RootCAs + options = append(options, AddRootsToCAs()) - transport, err := client.Transport(ctx, sign, pk, options...) + tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...) if err != nil { return nil, err } - return &http.Client{ - Transport: transport, - }, nil + base.TLSConfig = tlsConfig + return base, nil } // BootstrapListener is a helper function that using the given token returns a diff --git a/ca/ca.go b/ca/ca.go index e23be140..d9bcdd5e 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -16,6 +16,8 @@ import ( acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" @@ -74,14 +76,14 @@ func WithDatabase(db db.AuthDB) Option { // the HTTP server, set ups the middlewares and the HTTP handlers. type CA struct { auth *authority.Authority - config *authority.Config + config *config.Config srv *server.Server opts *options renewer *TLSRenewer } // New creates and initializes the CA with the given configuration and options. -func New(config *authority.Config, opts ...Option) (*CA, error) { +func New(config *config.Config, opts ...Option) (*CA, error) { ca := &CA{ config: config, opts: new(options), @@ -91,7 +93,7 @@ func New(config *authority.Config, opts ...Option) (*CA, error) { } // Init initializes the CA with the given configuration. -func (ca *CA) Init(config *authority.Config) (*CA, error) { +func (ca *CA) Init(config *config.Config) (*CA, error) { // Intermediate Password. if len(ca.opts.password) > 0 { ca.config.Password = string(ca.opts.password) @@ -146,7 +148,7 @@ func (ca *CA) Init(config *authority.Config) (*CA, error) { if config.DB == nil { acmeDB = nil } else { - acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB)) + acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB), mgmt.DefaultAuthorityID) if err != nil { return nil, errors.Wrap(err, "error configuring ACME DB interface") } @@ -218,7 +220,7 @@ func (ca *CA) Stop() error { // Reload reloads the configuration of the CA and calls to the server Reload // method. func (ca *CA) Reload() error { - config, err := authority.LoadConfiguration(ca.opts.configFile) + config, err := config.LoadConfiguration(ca.opts.configFile) if err != nil { return errors.Wrap(err, "error reloading ca configuration") } diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go new file mode 100644 index 00000000..1172218f --- /dev/null +++ b/ca/mgmtClient.go @@ -0,0 +1,107 @@ +package ca + +import ( + "net/http" + "net/url" + "path" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/mgmt" +) + +// MgmtClient implements an HTTP client for the CA server. +type MgmtClient struct { + client *uaClient + endpoint *url.URL + retryFunc RetryFunc + opts []ClientOption +} + +// NewMgmtClient creates a new MgmtClient with the given endpoint and options. +func NewMgmtClient(endpoint string, opts ...ClientOption) (*MgmtClient, error) { + u, err := parseEndpoint(endpoint) + if err != nil { + return nil, err + } + // Retrieve transport from options. + o := new(clientOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + tr, err := o.getTransport(endpoint) + if err != nil { + return nil, err + } + + return &MgmtClient{ + client: newClient(tr), + endpoint: u, + retryFunc: o.retryFunc, + opts: opts, + }, nil +} + +func (c *MgmtClient) retryOnError(r *http.Response) bool { + if c.retryFunc != nil { + if c.retryFunc(r.StatusCode) { + o := new(clientOptions) + if err := o.apply(c.opts); err != nil { + return false + } + tr, err := o.getTransport(c.endpoint.String()) + if err != nil { + return false + } + r.Body.Close() + c.client.SetTransport(tr) + return true + } + } + return false +} + +// GetAdmin performs the GET /config/admin/{id} request to the CA. +func (c *MgmtClient) GetAdmin(id string) (*mgmt.Admin, error) { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/config/admin", id)}) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readError(resp.Body) + } + var adm = new(mgmt.Admin) + if err := readJSON(resp.Body, adm); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return adm, nil +} + +// GetAdmins performs the GET /config/admins request to the CA. +func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: "/config/admins"}) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readError(resp.Body) + } + var admins = new([]*mgmt.Admin) + if err := readJSON(resp.Body, admins); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return *admins, nil +} diff --git a/ca/tls.go b/ca/tls.go index e4f585fe..cb9f4707 100644 --- a/ca/tls.go +++ b/ca/tls.go @@ -103,7 +103,6 @@ func (c *Client) getClientTLSConfig(ctx context.Context, sign *api.SignResponse, return nil, nil, err } - // Update renew function with transport tr := getDefaultTransport(tlsConfig) // Use mutable tls.Config on renew tr.DialTLS = c.buildDialTLS(tlsCtx) // nolint:staticcheck diff --git a/commands/app.go b/commands/app.go index aff9d473..8833726c 100644 --- a/commands/app.go +++ b/commands/app.go @@ -11,7 +11,7 @@ import ( "unicode" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/ca" "github.com/urfave/cli" "go.step.sm/cli-utils/errs" @@ -56,7 +56,7 @@ func appAction(ctx *cli.Context) error { } configFile := ctx.Args().Get(0) - config, err := authority.LoadConfiguration(configFile) + config, err := config.LoadConfiguration(configFile) if err != nil { fatal(err) } diff --git a/commands/onboard.go b/commands/onboard.go index 13c32304..251a4b47 100644 --- a/commands/onboard.go +++ b/commands/onboard.go @@ -9,7 +9,7 @@ import ( "os" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/ca" "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/pki" @@ -162,7 +162,7 @@ func onboardAction(ctx *cli.Context) error { return nil } -func onboardPKI(config onboardingConfiguration) (*authority.Config, string, error) { +func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) { p, err := pki.New(apiv1.Options{ Type: apiv1.SoftCAS, IsCreator: true, diff --git a/pki/pki.go b/pki/pki.go index c95ca985..e4e7bad3 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -19,7 +19,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority" + authconfig "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/ca" "github.com/smallstep/certificates/cas" @@ -481,12 +481,12 @@ type caDefaults struct { } // Option is the type for modifiers over the auth config object. -type Option func(c *authority.Config) error +type Option func(c *authconfig.Config) error // WithDefaultDB is a configuration modifier that adds a default DB stanza to // the authority config. func WithDefaultDB() Option { - return func(c *authority.Config) error { + return func(c *authconfig.Config) error { c.DB = &db.Config{ Type: "badger", DataSource: GetDBPath(), @@ -498,14 +498,14 @@ func WithDefaultDB() Option { // WithoutDB is a configuration modifier that adds a default DB stanza to // the authority config. func WithoutDB() Option { - return func(c *authority.Config) error { + return func(c *authconfig.Config) error { c.DB = nil return nil } } // GenerateConfig returns the step certificates configuration. -func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) { +func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { key, err := p.ottPrivateKey.CompactSerialize() if err != nil { return nil, errors.Wrap(err, "error serializing private key") @@ -523,7 +523,7 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) { authorityOptions = &p.casOptions } - config := &authority.Config{ + config := &authconfig.Config{ Root: []string{p.root}, FederatedRoots: []string{}, IntermediateCert: p.intermediate, @@ -535,22 +535,22 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) { Type: "badger", DataSource: GetDBPath(), }, - AuthorityConfig: &authority.AuthConfig{ + AuthorityConfig: &authconfig.AuthConfig{ Options: authorityOptions, DisableIssuedAtCheck: false, Provisioners: provisioner.List{prov}, }, - TLS: &authority.TLSOptions{ - MinVersion: authority.DefaultTLSMinVersion, - MaxVersion: authority.DefaultTLSMaxVersion, - Renegotiation: authority.DefaultTLSRenegotiation, - CipherSuites: authority.DefaultTLSCipherSuites, + TLS: &authconfig.TLSOptions{ + MinVersion: authconfig.DefaultTLSMinVersion, + MaxVersion: authconfig.DefaultTLSMaxVersion, + Renegotiation: authconfig.DefaultTLSRenegotiation, + CipherSuites: authconfig.DefaultTLSCipherSuites, }, Templates: p.getTemplates(), } if p.enableSSH { enableSSHCA := true - config.SSH = &authority.SSHConfig{ + config.SSH = &authconfig.SSHConfig{ HostKey: p.sshHostKey, UserKey: p.sshUserKey, } From 2f60f20b0b2e2df785affa893a09e764b2030bd6 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 5 May 2021 23:02:42 -0700 Subject: [PATCH 003/291] lots of codes --- authority/authority.go | 24 +- authority/config/config.go | 13 +- authority/mgmt/admin.go | 31 +++ authority/mgmt/authConfig.go | 77 ++++++ authority/mgmt/config.go | 343 ++++---------------------- authority/mgmt/db/nosql/admin.go | 44 ++-- authority/mgmt/provisioner.go | 401 +++++++++++++++++++++++++++++++ 7 files changed, 597 insertions(+), 336 deletions(-) create mode 100644 authority/mgmt/admin.go create mode 100644 authority/mgmt/authConfig.go create mode 100644 authority/mgmt/provisioner.go diff --git a/authority/authority.go b/authority/authority.go index fcb01a03..53a3ee72 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -146,17 +146,19 @@ func (a *Authority) init() error { // Pull AuthConfig from DB. if true { - mgmtDB, err := authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - _ac, err := mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - a.config.AuthorityConfig, err = _ac.ToCertificates() - if err != nil { - return err + if len(a.config.AuthConfig.AuthorityID)_== 0 { + mgmtDB, err := authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) + if err != nil { + return err + } + mgmtAuthConfig, err := mgmt.CreateAuthority(context.Background, mgmtDB, WithDefaultAuthorityID) + if err != nil { + return err + } + a.config.AuthConfig, err := mgmtAuthConfig.ToCertificates() + if err != nil { + return err + } } } diff --git a/authority/config/config.go b/authority/config/config.go index 1f6b7619..852641c2 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -32,23 +32,23 @@ var ( MaxVersion: 1.2, Renegotiation: false, } - defaultBackdate = time.Minute - defaultDisableRenewal = false - defaultEnableSSHCA = false + DefaultBackdate = time.Minute + DefaultDisableRenewal = false + DefaultEnableSSHCA = false // GlobalProvisionerClaims default claims for the Authority. Can be overriden // by provisioner specific claims. GlobalProvisionerClaims = provisioner.Claims{ MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs MaxTLSDur: &provisioner.Duration{Duration: 24 * time.Hour}, DefaultTLSDur: &provisioner.Duration{Duration: 24 * time.Hour}, - DisableRenewal: &defaultDisableRenewal, + DisableRenewal: &DefaultDisableRenewal, MinUserSSHDur: &provisioner.Duration{Duration: 5 * time.Minute}, // User SSH certs MaxUserSSHDur: &provisioner.Duration{Duration: 24 * time.Hour}, DefaultUserSSHDur: &provisioner.Duration{Duration: 16 * time.Hour}, MinHostSSHDur: &provisioner.Duration{Duration: 5 * time.Minute}, // Host SSH certs MaxHostSSHDur: &provisioner.Duration{Duration: 30 * 24 * time.Hour}, DefaultHostSSHDur: &provisioner.Duration{Duration: 30 * 24 * time.Hour}, - EnableSSHCA: &defaultEnableSSHCA, + EnableSSHCA: &DefaultEnableSSHCA, } ) @@ -88,6 +88,7 @@ type ASN1DN struct { // cas.Options. type AuthConfig struct { *cas.Options + AuthorityID string `json:"authorityID,omitempty"` Provisioners provisioner.List `json:"provisioners"` Template *ASN1DN `json:"template,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` @@ -106,7 +107,7 @@ func (c *AuthConfig) init() { } if c.Backdate == nil { c.Backdate = &provisioner.Duration{ - Duration: defaultBackdate, + Duration: DefaultBackdate, } } } diff --git a/authority/mgmt/admin.go b/authority/mgmt/admin.go new file mode 100644 index 00000000..8a4104a5 --- /dev/null +++ b/authority/mgmt/admin.go @@ -0,0 +1,31 @@ +package mgmt + +import "context" + +// Admin type. +type Admin struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + ProvisionerID string `json:"provisionerID"` + Name string `json:"name"` + ProvisionerName string `json:"provisionerName"` + ProvisionerType string `json:"provisionerType"` + IsSuperAdmin bool `json:"isSuperAdmin"` + Status StatusType `json:"status"` +} + +// CreateAdmin builds and stores an admin type in the DB. +func CreateAdmin(ctx context.Context, db DB, name string, prov *Provisioner, isSuperAdmin bool) (*Admin, error) { + adm := &Admin{ + Name: name, + ProvisionerID: prov.ID, + ProvisionerName: prov.Name, + ProvisionerType: prov.Type, + IsSuperAdmin: isSuperAdmin, + Status: StatusActive, + } + if err := db.CreateAdmin(ctx, adm); err != nil { + return nil, WrapErrorISE(err, "error creating admin") + } + return adm, nil +} diff --git a/authority/mgmt/authConfig.go b/authority/mgmt/authConfig.go new file mode 100644 index 00000000..595b2ed0 --- /dev/null +++ b/authority/mgmt/authConfig.go @@ -0,0 +1,77 @@ +package mgmt + +import ( + "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/provisioner" +) + +// AuthConfig represents the Authority Configuration. +type AuthConfig struct { + //*cas.Options `json:"cas"` + ID string `json:"id"` + ASN1DN *config.ASN1DN `json:"template,omitempty"` + Provisioners []*Provisioner `json:"-"` + Admins []*Admin `json:"-"` + Claims *Claims `json:"claims,omitempty"` + Backdate string `json:"backdate,omitempty"` + Status StatusType `json:"status,omitempty"` +} + +func NewDefaultAuthConfig() *AuthConfig { + return &AuthConfig{ + Claims: &Claims{ + X509: &X509Claims{ + Durations: &Durations{ + Min: config.GlobalProvisionerClaims.MinTLSDur.String(), + Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), + Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), + }, + }, + SSH: &SSHClaims{ + UserDurations: &Durations{ + Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), + }, + HostDurations: &Durations{ + Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), + }, + }, + DisableRenewal: config.DefaultDisableRenewal, + }, + Backdate: config.DefaultBackdate.String(), + Status: StatusActive, + } +} + +// ToCertificates converts a mgmt AuthConfig to configuration that can be +// directly used by the `step-ca` process. Resources are normalized and +// initialized. +func (ac *AuthConfig) ToCertificates() (*config.AuthConfig, error) { + claims, err := ac.Claims.ToCertificates() + if err != nil { + return nil, err + } + backdate, err := provisioner.NewDuration(ac.Backdate) + if err != nil { + return nil, WrapErrorISE(err, "error converting backdate %s to duration", ac.Backdate) + } + var provs []provisioner.Interface + for _, p := range ac.Provisioners { + authProv, err := p.ToCertificates() + if err != nil { + return nil, err + } + provs = append(provs, authProv) + } + return &config.AuthConfig{ + AuthorityID: ac.ID, + Provisioners: provs, + Template: ac.ASN1DN, + Claims: claims, + DisableIssuedAtCheck: false, + Backdate: backdate, + }, nil +} diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index b5bebceb..db70e6e5 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -1,11 +1,15 @@ package mgmt import ( - "github.com/smallstep/certificates/authority/config" - authority "github.com/smallstep/certificates/authority/config" + "context" + + "github.com/pkg/errors" ) const ( + // DefaultAuthorityID is the default AuthorityID. This will be the ID + // of the first Authority created, as well as the default AuthorityID + // if one is not specified in the configuration. DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) @@ -19,339 +23,76 @@ const ( StatusDeleted ) +// Claims encapsulates all x509 and ssh claims applied to the authority +// configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc. type Claims struct { - *X509Claims `json:"x509Claims"` - *SSHClaims `json:"sshClaims"` - DisableRenewal *bool `json:"disableRenewal"` + X509 *X509Claims `json:"x509Claims"` + SSH *SSHClaims `json:"sshClaims"` + DisableRenewal bool `json:"disableRenewal"` } +// X509Claims are the x509 claims applied to the authority. type X509Claims struct { Durations *Durations `json:"durations"` } +// SSHClaims are the ssh claims applied to the authority. type SSHClaims struct { - UserDuration *Durations `json:"userDurations"` - HostDuration *Durations `json:"hostDuration"` + Enabled bool `json:"enabled"` + UserDurations *Durations `json:"userDurations"` + HostDurations *Durations `json:"hostDurations"` } +// Durations represents min, max, default, duration. type Durations struct { Min string `json:"min"` Max string `json:"max"` Default string `json:"default"` } -// Admin type. -type Admin struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Name string `json:"name"` - Provisioner string `json:"provisioner"` - IsSuperAdmin bool `json:"isSuperAdmin"` - Status StatusType `json:"status"` -} +type AuthorityOption func(*AuthConfig) error -// Provisioner type. -type Provisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Type string `json:"type"` - Name string `json:"name"` - Claims *Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` - Status StatusType `json:"status"` +func WithDefaultAuthorityID(ac *AuthConfig) error { + ac.ID = DefaultAuthorityID + return nil } -// AuthConfig represents the Authority Configuration. -type AuthConfig struct { - //*cas.Options `json:"cas"` - ID string `json:"id"` - ASN1DN *config.ASN1DN `json:"template,omitempty"` - Provisioners []*Provisioner `json:"-"` - Claims *Claims `json:"claims,omitempty"` - DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` - Backdate string `json:"backdate,omitempty"` - Status StatusType `json:"status,omitempty"` -} +func CreateDefaultAuthority(ctx context.Context, db DB) (*AuthConfig, error) { + options := []AuthorityOption{WithDefaultAuthorityID} -func (ac *AuthConfig) ToCertificates() (*config.AuthConfig, error) { - return &authority.AuthConfig{}, nil + return CreateAuthority(ctx, db, options...) } -/* -// ToCertificates converts the landlord provisioner type to the open source -// provisioner type. -func (p *Provisioner) ToCertificates(ctx context.Context, db database.DB) (provisioner.Interface, error) { - claims, err := p.Claims.ToCertificates() - if err != nil { - return nil, err - } - - details := p.Details.GetData() - if details == nil { - return nil, fmt.Errorf("provisioner does not have any details") - } - - options, err := p.getOptions(ctx, db) - if err != nil { - return nil, err - } +func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*AuthConfig, error) { + ac := NewDefaultAuthConfig() - switch d := details.(type) { - case *ProvisionerDetails_JWK: - k := d.JWK.GetKey() - jwk := new(jose.JSONWebKey) - if err := json.Unmarshal(k.Key.Public, &jwk); err != nil { + for _, o := range options { + if err := o(ac); err != nil { return nil, err } - return &provisioner.JWK{ - Type: p.Type.String(), - Name: p.Name, - Key: jwk, - EncryptedKey: string(k.Key.Private), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_OIDC: - cfg := d.OIDC - return &provisioner.OIDC{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ClientID: cfg.ClientId, - ClientSecret: cfg.ClientSecret, - ConfigurationEndpoint: cfg.ConfigurationEndpoint, - Admins: cfg.Admins, - Domains: cfg.Domains, - Groups: cfg.Groups, - ListenAddress: cfg.ListenAddress, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_GCP: - cfg := d.GCP - return &provisioner.GCP{ - Type: p.Type.String(), - Name: p.Name, - ServiceAccounts: cfg.ServiceAccounts, - ProjectIDs: cfg.ProjectIds, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_AWS: - cfg := d.AWS - return &provisioner.AWS{ - Type: p.Type.String(), - Name: p.Name, - Accounts: cfg.Accounts, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_Azure: - cfg := d.Azure - return &provisioner.Azure{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ResourceGroups: cfg.ResourceGroups, - Audience: cfg.Audience, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_X5C: - var roots []byte - for i, k := range d.X5C.GetRoots() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - roots = append(roots, '\n') - } - roots = append(roots, b...) - } - } - return &provisioner.X5C{ - Type: p.Type.String(), - Name: p.Name, - Roots: roots, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_K8SSA: - var publicKeys []byte - for i, k := range d.K8SSA.GetPublicKeys() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - publicKeys = append(publicKeys, '\n') - } - publicKeys = append(publicKeys, k.Key.Public...) - } - } - return &provisioner.K8sSA{ - Type: p.Type.String(), - Name: p.Name, - PubKeys: publicKeys, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_SSHPOP: - return &provisioner.SSHPOP{ - Type: p.Type.String(), - Name: p.Name, - Claims: claims, - }, nil - case *ProvisionerDetails_ACME: - cfg := d.ACME - return &provisioner.ACME{ - Type: p.Type.String(), - Name: p.Name, - ForceCN: cfg.ForceCn, - Claims: claims, - Options: options, - }, nil - default: - return nil, fmt.Errorf("provisioner %s not implemented", p.Type.String()) } -} - -// ToCertificates converts the landlord provisioner claims type to the open source -// (step-ca) claims type. -func (c *Claims) ToCertificates() (*provisioner.Claims, error) { - x509, ssh := c.GetX509(), c.GetSsh() - x509Durations := x509.GetDurations() - hostDurations := ssh.GetHostDurations() - userDurations := ssh.GetUserDurations() - enableSSHCA := ssh.GetEnabled() - return &provisioner.Claims{ - MinTLSDur: durationPtr(x509Durations.GetMin()), - MaxTLSDur: durationPtr(x509Durations.GetMax()), - DefaultTLSDur: durationPtr(x509Durations.GetDefault()), - DisableRenewal: &c.DisableRenewal, - MinUserSSHDur: durationPtr(userDurations.GetMin()), - MaxUserSSHDur: durationPtr(userDurations.GetMax()), - DefaultUserSSHDur: durationPtr(userDurations.GetDefault()), - MinHostSSHDur: durationPtr(hostDurations.GetMin()), - MaxHostSSHDur: durationPtr(hostDurations.GetMax()), - DefaultHostSSHDur: durationPtr(hostDurations.GetDefault()), - EnableSSHCA: &enableSSHCA, - }, nil -} -func durationPtr(d *duration.Duration) *provisioner.Duration { - if d == nil { - return nil + if err := db.CreateAuthConfig(ctx, ac); err != nil { + return nil, errors.Wrap(err, "error creating authConfig") } - return &provisioner.Duration{ - Duration: time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond, - } -} -func durationValue(d *duration.Duration) provisioner.Duration { - if d == nil { - return provisioner.Duration{} - } - return provisioner.Duration{ - Duration: time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond, - } -} + // Generate default JWK provisioner. -func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) { - b, err := json.Marshal(d.GetData()) + provOpts := []ProvisionerOption{WithPassword("pass")} + prov, err := CreateProvisioner(ctx, db, "JWK", "changeme", provOpts...) if err != nil { - return sql.NullString{}, nil + // TODO should we try to clean up? + return nil, WrapErrorISE(err, "error creating first provisioner") } - return sql.NullString{ - String: string(b), - Valid: len(b) > 0, - }, nil -} -func unmarshalDetails(ctx context.Context, db database.DB, typ ProvisionerType, s sql.NullString) (*ProvisionerDetails, error) { - if !s.Valid { - return nil, nil - } - var v isProvisionerDetails_Data - switch typ { - case ProvisionerType_JWK: - p := new(ProvisionerDetails_JWK) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - if p.JWK.Key.Key == nil { - key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) - if err != nil { - return nil, err - } - p.JWK.Key = key - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_OIDC: - v = new(ProvisionerDetails_OIDC) - case ProvisionerType_GCP: - v = new(ProvisionerDetails_GCP) - case ProvisionerType_AWS: - v = new(ProvisionerDetails_AWS) - case ProvisionerType_AZURE: - v = new(ProvisionerDetails_Azure) - case ProvisionerType_ACME: - v = new(ProvisionerDetails_ACME) - case ProvisionerType_X5C: - p := new(ProvisionerDetails_X5C) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.X5C.GetRoots() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_K8SSA: - p := new(ProvisionerDetails_K8SSA) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.K8SSA.GetPublicKeys() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_SSHPOP: - v = new(ProvisionerDetails_SSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", typ) - } - - if err := json.Unmarshal([]byte(s.String), v); err != nil { - return nil, err - } - return &ProvisionerDetails{Data: v}, nil -} - -func marshalClaims(c *Claims) (sql.NullString, error) { - b, err := json.Marshal(c) + admin, err := CreateAdmin(ctx, db, "Change Me", prov, true) if err != nil { - return sql.NullString{}, nil + // TODO should we try to clean up? + return nil, WrapErrorISE(err, "error creating first provisioner") } - return sql.NullString{ - String: string(b), - Valid: len(b) > 0, - }, nil -} -func unmarshalClaims(s sql.NullString) (*Claims, error) { - if !s.Valid { - return nil, nil - } - v := new(Claims) - return v, json.Unmarshal([]byte(s.String), v) + ac.Provisioners = []*Provisioner{prov} + ac.Admins = []*Admin{admin} + + return ac, nil } -*/ diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 8b33110c..cba8c59d 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -13,13 +13,13 @@ import ( // dbAdmin is the database representation of the Admin type. type dbAdmin struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Name string `json:"name"` - Provisioner string `json:"provisioner"` - IsSuperAdmin bool `json:"isSuperAdmin"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + ProvisionerID string `json:"provisionerID"` + Name string `json:"name"` + IsSuperAdmin bool `json:"isSuperAdmin"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbAdmin) clone() *dbAdmin { @@ -70,6 +70,13 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, "admin %s is not owned by authority %s", adm.ID, db.authorityID) } + + prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) + if err != nil { + return nil, err + } + adm.ProvisionerName = prov.Name + adm.ProvisionerType = prov.Type return adm, nil } @@ -87,10 +94,11 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) } adm := &mgmt.Admin{ - ID: dba.ID, - Name: dba.Name, - Provisioner: dba.Provisioner, - IsSuperAdmin: dba.IsSuperAdmin, + ID: dba.ID, + AuthorityID: dba.AuthorityID, + ProvisionerID: dba.ProvisionerID, + Name: dba.Name, + IsSuperAdmin: dba.IsSuperAdmin, } if !dba.DeletedAt.IsZero() { adm.Status = mgmt.StatusDeleted @@ -132,12 +140,12 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { } dba := &dbAdmin{ - ID: adm.ID, - AuthorityID: db.authorityID, - Name: adm.Name, - Provisioner: adm.Provisioner, - IsSuperAdmin: adm.IsSuperAdmin, - CreatedAt: clock.Now(), + ID: adm.ID, + AuthorityID: db.authorityID, + ProvisionerID: adm.ProvisionerID, + Name: adm.Name, + IsSuperAdmin: adm.IsSuperAdmin, + CreatedAt: clock.Now(), } return db.save(ctx, dba.ID, dba, nil, "admin", authorityAdminsTable) @@ -156,7 +164,7 @@ func (db *DB) UpdateAdmin(ctx context.Context, adm *mgmt.Admin) error { if old.DeletedAt.IsZero() && adm.Status == mgmt.StatusDeleted { nu.DeletedAt = clock.Now() } - nu.Provisioner = adm.Provisioner + nu.ProvisionerID = adm.ProvisionerID nu.IsSuperAdmin = adm.IsSuperAdmin return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go new file mode 100644 index 00000000..2cb21403 --- /dev/null +++ b/authority/mgmt/provisioner.go @@ -0,0 +1,401 @@ +package mgmt + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/crypto/jose" +) + +type ProvisionerOption func(*ProvisionerCtx) + +type ProvisionerCtx struct { + JWK *jose.JSONWebKey + JWE *jose.JSONWebEncryption + X509Template, SSHTemplate string + X509TemplateData, SSHTemplateData []byte + Claims *Claims + Password string +} + +func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) { + return func(ctx *ProvisionerCtx) { + ctx.JWK = jwk + ctx.JWE = jwe + } +} + +func WithPassword(pass string) func(*ProvisionerCtx) { + return func(ctx *ProvisionerCtx) { + ctx.Password = pass + } +} + +// Provisioner type. +type Provisioner struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Claims *Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` + Status StatusType `json:"status"` +} + +func (p *Provisioner) GetOptions() *provisioner.Options { + return &provisioner.Options{ + X509: &provisioner.X509Options{ + Template: p.X509Template, + TemplateData: p.X509TemplateData, + }, + SSH: &provisioner.SSHOptions{ + Template: p.SSHTemplate, + TemplateData: p.SSHTemplateData, + }, + } +} + +func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...ProvisionerOption) (*Provisioner, error) { + pc := new(ProvisionerCtx) + for _, o := range opts { + o(pc) + } + + details, err := createJWKDetails(pc) + if err != nil { + return nil, err + } + + p := &Provisioner{ + Type: typ, + Name: name, + Claims: pc.Claims, + Details: details, + X509Template: pc.X509Template, + X509TemplateData: pc.X509TemplateData, + SSHTemplate: pc.SSHTemplate, + SSHTemplateData: pc.SSHTemplateData, + Status: StatusActive, + } + + if err := db.CreateProvisioner(ctx, p); err != nil { + return nil, WrapErrorISE(err, "error creating provisioner") + } + return p, nil +} + +type ProvisionerDetails_JWK struct { + PubKey []byte `json:"pubKey"` + PrivKey string `json:"privKey"` +} + +func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetails_JWK, error) { + var err error + + if pc.JWK != nil && pc.JWE == nil { + return nil, NewErrorISE("JWE is required with JWK for createJWKProvisioner") + } + if pc.JWE != nil && pc.JWK == nil { + return nil, NewErrorISE("JWK is required with JWE for createJWKProvisioner") + } + if pc.JWK == nil && pc.JWE == nil { + // Create a new JWK w/ encrypted private key. + if pc.Password == "" { + return nil, NewErrorISE("password is required to provisioner with new keys") + } + pc.JWK, pc.JWE, err = jose.GenerateDefaultKeyPair([]byte(pc.Password)) + if err != nil { + return nil, WrapErrorISE(err, "error generating JWK key pair") + } + } + + jwkPubBytes, err := pc.JWK.MarshalJSON() + if err != nil { + return nil, WrapErrorISE(err, "error marshaling JWK") + } + jwePrivStr, err := pc.JWE.CompactSerialize() + if err != nil { + return nil, WrapErrorISE(err, "error serializing JWE") + } + + return &ProvisionerDetails_JWK{ + PubKey: jwkPubBytes, + PrivKey: jwePrivStr, + }, nil +} + +// ToCertificates converts the landlord provisioner type to the open source +// provisioner type. +func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { + claims, err := p.Claims.ToCertificates() + if err != nil { + return nil, err + } + + if err != nil { + return nil, err + } + + switch details := p.Details.(type) { + case *ProvisionerDetails_JWK: + jwk := new(jose.JSONWebKey) + if err := json.Unmarshal(details.PubKey, &jwk); err != nil { + return nil, err + } + return &provisioner.JWK{ + Type: p.Type, + Name: p.Name, + Key: jwk, + EncryptedKey: details.PrivKey, + Claims: claims, + Options: p.GetOptions(), + }, nil + /* + case *ProvisionerDetails_OIDC: + cfg := d.OIDC + return &provisioner.OIDC{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ClientID: cfg.ClientId, + ClientSecret: cfg.ClientSecret, + ConfigurationEndpoint: cfg.ConfigurationEndpoint, + Admins: cfg.Admins, + Domains: cfg.Domains, + Groups: cfg.Groups, + ListenAddress: cfg.ListenAddress, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_GCP: + cfg := d.GCP + return &provisioner.GCP{ + Type: p.Type.String(), + Name: p.Name, + ServiceAccounts: cfg.ServiceAccounts, + ProjectIDs: cfg.ProjectIds, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_AWS: + cfg := d.AWS + return &provisioner.AWS{ + Type: p.Type.String(), + Name: p.Name, + Accounts: cfg.Accounts, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_Azure: + cfg := d.Azure + return &provisioner.Azure{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ResourceGroups: cfg.ResourceGroups, + Audience: cfg.Audience, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_X5C: + var roots []byte + for i, k := range d.X5C.GetRoots() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, b...) + } + } + return &provisioner.X5C{ + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_K8SSA: + var publicKeys []byte + for i, k := range d.K8SSA.GetPublicKeys() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + publicKeys = append(publicKeys, '\n') + } + publicKeys = append(publicKeys, k.Key.Public...) + } + } + return &provisioner.K8sSA{ + Type: p.Type.String(), + Name: p.Name, + PubKeys: publicKeys, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_SSHPOP: + return &provisioner.SSHPOP{ + Type: p.Type.String(), + Name: p.Name, + Claims: claims, + }, nil + case *ProvisionerDetails_ACME: + cfg := d.ACME + return &provisioner.ACME{ + Type: p.Type.String(), + Name: p.Name, + ForceCN: cfg.ForceCn, + Claims: claims, + Options: options, + }, nil + */ + default: + return nil, fmt.Errorf("provisioner %s not implemented", p.Type) + } +} + +// ToCertificates converts the landlord provisioner claims type to the open source +// (step-ca) claims type. +func (c *Claims) ToCertificates() (*provisioner.Claims, error) { + var durs = map[string]struct { + durStr string + dur *provisioner.Duration + }{ + "minTLSDur": {durStr: c.X509.Durations.Min}, + "maxTLSDur": {durStr: c.X509.Durations.Max}, + "defaultTLSDur": {durStr: c.X509.Durations.Default}, + "minSSHUserDur": {durStr: c.SSH.UserDurations.Min}, + "maxSSHUserDur": {durStr: c.SSH.UserDurations.Max}, + "defaultSSHUserDur": {durStr: c.SSH.UserDurations.Default}, + "minSSHHostDur": {durStr: c.SSH.HostDurations.Min}, + "maxSSHHostDur": {durStr: c.SSH.HostDurations.Max}, + "defaultSSHHostDur": {durStr: c.SSH.HostDurations.Default}, + } + var err error + for k, v := range durs { + v.dur, err = provisioner.NewDuration(v.durStr) + if err != nil { + return nil, WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) + } + } + return &provisioner.Claims{ + MinTLSDur: durs["minTLSDur"].dur, + MaxTLSDur: durs["maxTLSDur"].dur, + DefaultTLSDur: durs["defaultTLSDur"].dur, + DisableRenewal: &c.DisableRenewal, + MinUserSSHDur: durs["minSSHUserDur"].dur, + MaxUserSSHDur: durs["maxSSHUserDur"].dur, + DefaultUserSSHDur: durs["defaultSSHUserDur"].dur, + MinHostSSHDur: durs["minSSHHostDur"].dur, + MaxHostSSHDur: durs["maxSSHHostDur"].dur, + DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, + EnableSSHCA: &c.SSH.Enabled, + }, nil +} + +/* +func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) { + b, err := json.Marshal(d.GetData()) + if err != nil { + return sql.NullString{}, nil + } + return sql.NullString{ + String: string(b), + Valid: len(b) > 0, + }, nil +} + +func unmarshalDetails(ctx context.Context, db database.DB, typ ProvisionerType, s sql.NullString) (*ProvisionerDetails, error) { + if !s.Valid { + return nil, nil + } + var v isProvisionerDetails_Data + switch typ { + case ProvisionerType_JWK: + p := new(ProvisionerDetails_JWK) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + if p.JWK.Key.Key == nil { + key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) + if err != nil { + return nil, err + } + p.JWK.Key = key + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_OIDC: + v = new(ProvisionerDetails_OIDC) + case ProvisionerType_GCP: + v = new(ProvisionerDetails_GCP) + case ProvisionerType_AWS: + v = new(ProvisionerDetails_AWS) + case ProvisionerType_AZURE: + v = new(ProvisionerDetails_Azure) + case ProvisionerType_ACME: + v = new(ProvisionerDetails_ACME) + case ProvisionerType_X5C: + p := new(ProvisionerDetails_X5C) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.X5C.GetRoots() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_K8SSA: + p := new(ProvisionerDetails_K8SSA) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.K8SSA.GetPublicKeys() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_SSHPOP: + v = new(ProvisionerDetails_SSHPOP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal([]byte(s.String), v); err != nil { + return nil, err + } + return &ProvisionerDetails{Data: v}, nil +} + +func marshalClaims(c *Claims) (sql.NullString, error) { + b, err := json.Marshal(c) + if err != nil { + return sql.NullString{}, nil + } + return sql.NullString{ + String: string(b), + Valid: len(b) > 0, + }, nil +} + +func unmarshalClaims(s sql.NullString) (*Claims, error) { + if !s.Valid { + return nil, nil + } + v := new(Claims) + return v, json.Unmarshal([]byte(s.String), v) +} +*/ From af3cf7dae958d99cdf80c6f37119eaa8380730d3 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 6 May 2021 17:03:12 -0700 Subject: [PATCH 004/291] first steps --- authority/authority.go | 37 ++++++---- authority/config/config.go | 11 ++- authority/mgmt/api/admin.go | 98 ++++++++++++++------------- authority/mgmt/api/authConfig.go | 89 ++++++++++++------------ authority/mgmt/api/handler.go | 10 ++- authority/mgmt/api/provisioner.go | 84 ++++++++++++----------- authority/mgmt/authConfig.go | 24 +------ authority/mgmt/config.go | 26 +++++++ authority/mgmt/db.go | 6 +- authority/mgmt/db/nosql/admin.go | 3 +- authority/mgmt/db/nosql/authConfig.go | 44 ++++++------ authority/mgmt/errors.go | 5 ++ authority/mgmt/provisioner.go | 15 ++-- ca/ca.go | 12 ++++ ca/mgmtClient.go | 8 +-- 15 files changed, 259 insertions(+), 213 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 53a3ee72..016b838a 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -31,7 +31,7 @@ import ( // Authority implements the Certificate Authority internal interface. type Authority struct { config *config.Config - mgmtDB *mgmt.DB + mgmtDB mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection db db.AuthDB @@ -146,20 +146,26 @@ func (a *Authority) init() error { // Pull AuthConfig from DB. if true { - if len(a.config.AuthConfig.AuthorityID)_== 0 { - mgmtDB, err := authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - mgmtAuthConfig, err := mgmt.CreateAuthority(context.Background, mgmtDB, WithDefaultAuthorityID) - if err != nil { - return err - } - a.config.AuthConfig, err := mgmtAuthConfig.ToCertificates() - if err != nil { - return err + // Check if AuthConfig already exists + a.mgmtDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) + if err != nil { + return err + } + mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) + if err != nil { + if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) { + mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.mgmtDB, mgmt.WithDefaultAuthorityID) + if err != nil { + return mgmt.WrapErrorISE(err, "error creating authConfig") + } + } else { + return mgmt.WrapErrorISE(err, "error getting authConfig from db") } } + a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates() + if err != nil { + return err + } } // Initialize key manager if it has not been set in the options. @@ -394,6 +400,11 @@ func (a *Authority) GetDatabase() db.AuthDB { return a.db } +// GetMgmtDatabase returns the mgmt database, if one exists. +func (a *Authority) GetMgmtDatabase() mgmt.DB { + return a.mgmtDB +} + // Shutdown safely shuts down any clients, databases, etc. held by the Authority. func (a *Authority) Shutdown() error { if err := a.keyManager.Close(); err != nil { diff --git a/authority/config/config.go b/authority/config/config.go index 852641c2..66b8bbe0 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -32,9 +32,14 @@ var ( MaxVersion: 1.2, Renegotiation: false, } - DefaultBackdate = time.Minute + // DefaultBackdate length of time to backdate certificates to avoid + // clock skew validation issues. + DefaultBackdate = time.Minute + // DefaultDisableRenewal disables renewals per provisioner. DefaultDisableRenewal = false - DefaultEnableSSHCA = false + // DefaultEnableSSHCA enable SSH CA features per provisioner or globally + // for all provisioners. + DefaultEnableSSHCA = false // GlobalProvisionerClaims default claims for the Authority. Can be overriden // by provisioner specific claims. GlobalProvisionerClaims = provisioner.Claims{ @@ -153,7 +158,7 @@ func LoadConfiguration(filename string) (*Config, error) { return nil, errors.Wrapf(err, "error parsing %s", filename) } - c.init() + c.Init() return &c, nil } diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index 6f6aaa93..f544b631 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -58,55 +58,59 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { // CreateAdmin creates a new admin. func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var body CreateAdminRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - } - - adm := &config.Admin{ - Name: body.Name, - Provisioner: body.Provisioner, - IsSuperAdmin: body.IsSuperAdmin, - } - if err := h.db.CreateAdmin(ctx, adm); err != nil { - api.WriteError(w, err) - return - } - api.JSONStatus(w, adm, http.StatusCreated) + /* + ctx := r.Context() + + var body CreateAdminRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + } + + adm := &config.Admin{ + Name: body.Name, + Provisioner: body.Provisioner, + IsSuperAdmin: body.IsSuperAdmin, + } + if err := h.db.CreateAdmin(ctx, adm); err != nil { + api.WriteError(w, err) + return + } + api.JSONStatus(w, adm, http.StatusCreated) + */ } // UpdateAdmin updates an existing admin. func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateAdminRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - if adm, err := h.db.GetAdmin(ctx, id); err != nil { - api.WriteError(w, err) - return - } - - adm.Name = body.Name - adm.Provisioner = body.Provisioner - adm.IsSuperAdmin = body.IsSuperAdmin - - if err := h.db.UpdateAdmin(ctx, adm); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, adm) + /* + ctx := r.Context() + id := chi.URLParam(r, "id") + + var body UpdateAdminRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + if adm, err := h.db.GetAdmin(ctx, id); err != nil { + api.WriteError(w, err) + return + } + + adm.Name = body.Name + adm.Provisioner = body.Provisioner + adm.IsSuperAdmin = body.IsSuperAdmin + + if err := h.db.UpdateAdmin(ctx, adm); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, adm) + */ } diff --git a/authority/mgmt/api/authConfig.go b/authority/mgmt/api/authConfig.go index fb23f8e8..283a4b66 100644 --- a/authority/mgmt/api/authConfig.go +++ b/authority/mgmt/api/authConfig.go @@ -5,16 +5,15 @@ import ( "github.com/go-chi/chi" "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" ) // CreateAuthConfigRequest represents the body for a CreateAuthConfig request. type CreateAuthConfigRequest struct { - ASN1DN *authority.ASN1DN `json:"asn1dn,omitempty"` - Claims *config.Claims `json:"claims,omitempty"` - DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` - Backdate string `json:"backdate,omitempty"` + ASN1DN *config.ASN1DN `json:"asn1dn,omitempty"` + Claims *mgmt.Claims `json:"claims,omitempty"` + Backdate string `json:"backdate,omitempty"` } // Validate validates a CreateAuthConfig request body. @@ -24,10 +23,9 @@ func (car *CreateAuthConfigRequest) Validate() error { // UpdateAuthConfigRequest represents the body for a UpdateAuthConfig request. type UpdateAuthConfigRequest struct { - ASN1DN *authority.ASN1DN `json:"asn1dn"` - Claims *config.Claims `json:"claims"` - DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` - Backdate string `json:"backdate,omitempty"` + ASN1DN *config.ASN1DN `json:"asn1dn"` + Claims *mgmt.Claims `json:"claims"` + Backdate string `json:"backdate,omitempty"` } // Validate validates a new-admin request body. @@ -53,7 +51,7 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var body CreateAuthConfigRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := api.ReadJSON(r.Body, &body); err != nil { api.WriteError(w, err) return } @@ -61,10 +59,9 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) } - ac := config.AuthConfig{ - Status: config.StatusActive, - DisableIssuedAtCheck: body.DisableIssuedAtCheck, - Backdate: "1m", + ac := &mgmt.AuthConfig{ + Status: mgmt.StatusActive, + Backdate: "1m", } if body.ASN1DN != nil { ac.ASN1DN = body.ASN1DN @@ -84,38 +81,40 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) { // UpdateAuthConfig updates an existing AuthConfig. func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - id := chi.URLParam(r, "id") + /* + ctx := r.Context() + id := chi.URLParam(r, "id") - var body UpdateAuthConfigRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - if ac, err := h.db.GetAuthConfig(ctx, id); err != nil { - api.WriteError(w, err) - return - } + var body UpdateAuthConfigRequest + if err := api.ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + ac, err := h.db.GetAuthConfig(ctx, id) + if err != nil { + api.WriteError(w, err) + return + } - ac.DisableIssuedAtCheck = body.DisableIssuedAtCheck - ac.Status = body.Status - if body.ASN1DN != nil { - ac.ASN1DN = body.ASN1DN - } - if body.Claims != nil { - ac.Claims = body.Claims - } - if body.Backdate != "" { - ac.Backdate = body.Backdate - } + ac.Status = body.Status + if body.ASN1DN != nil { + ac.ASN1DN = body.ASN1DN + } + if body.Claims != nil { + ac.Claims = body.Claims + } + if body.Backdate != "" { + ac.Backdate = body.Backdate + } - if err := h.db.UpdateAuthConfig(ctx, ac); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, ac) + if err := h.db.UpdateAuthConfig(ctx, ac); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, ac) + */ } diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index f30544c5..0e512a39 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -4,7 +4,7 @@ import ( "time" "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" ) // Clock that returns time in UTC rounded to seconds. @@ -19,14 +19,12 @@ var clock Clock // Handler is the ACME API request handler. type Handler struct { - db config.DB + db mgmt.DB } // NewHandler returns a new Authority Config Handler. -func NewHandler(db config.DB) api.RouterHandler { - return &Handler{ - db: ops.DB, - } +func NewHandler(db mgmt.DB) api.RouterHandler { + return &Handler{db} } // Route traffic and implement the Router interface. diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index 76bbe7cf..43293221 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -5,17 +5,17 @@ import ( "github.com/go-chi/chi" "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" ) // CreateProvisionerRequest represents the body for a CreateProvisioner request. type CreateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *config.Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` + Type string `json:"type"` + Name string `json:"name"` + Claims *mgmt.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` } // Validate validates a new-provisioner request body. @@ -25,10 +25,10 @@ func (car *CreateProvisionerRequest) Validate() error { // UpdateProvisionerRequest represents the body for a UpdateProvisioner request. type UpdateProvisionerRequest struct { - Claims *config.Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` + Claims *mgmt.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + SSHTemplate string `json:"sshTemplate"` } // Validate validates a new-provisioner request body. @@ -66,7 +66,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var body CreateProvisionerRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := api.ReadJSON(r.Body, &body); err != nil { api.WriteError(w, err) return } @@ -74,7 +74,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) } - prov := &config.Provisioner{ + prov := &mgmt.Provisioner{ Type: body.Type, Name: body.Name, Claims: body.Claims, @@ -91,32 +91,34 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { // UpdateProvisioner updates an existing prov. func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateProvisionerRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - if prov, err := h.db.GetProvisioner(ctx, id); err != nil { - api.WriteError(w, err) - return - } - - prov.Claims = body.Claims - prov.Details = body.Provisioner - prov.X509Template = body.X509Template - prov.SSHTemplate = body.SSHTemplate - prov.Status = body.Status - - if err := h.db.UpdateProvisioner(ctx, prov); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, prov) + /* + ctx := r.Context() + id := chi.URLParam(r, "id") + + var body UpdateProvisionerRequest + if err := ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, err) + return + } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + if prov, err := h.db.GetProvisioner(ctx, id); err != nil { + api.WriteError(w, err) + return + } + + prov.Claims = body.Claims + prov.Details = body.Provisioner + prov.X509Template = body.X509Template + prov.SSHTemplate = body.SSHTemplate + prov.Status = body.Status + + if err := h.db.UpdateProvisioner(ctx, prov); err != nil { + api.WriteError(w, err) + return + } + api.JSON(w, prov) + */ } diff --git a/authority/mgmt/authConfig.go b/authority/mgmt/authConfig.go index 595b2ed0..734bca50 100644 --- a/authority/mgmt/authConfig.go +++ b/authority/mgmt/authConfig.go @@ -19,28 +19,8 @@ type AuthConfig struct { func NewDefaultAuthConfig() *AuthConfig { return &AuthConfig{ - Claims: &Claims{ - X509: &X509Claims{ - Durations: &Durations{ - Min: config.GlobalProvisionerClaims.MinTLSDur.String(), - Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), - Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), - }, - }, - SSH: &SSHClaims{ - UserDurations: &Durations{ - Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), - }, - HostDurations: &Durations{ - Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), - }, - }, - DisableRenewal: config.DefaultDisableRenewal, - }, + Claims: NewDefaultClaims(), + ASN1DN: &config.ASN1DN{}, Backdate: config.DefaultBackdate.String(), Status: StatusActive, } diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index db70e6e5..0cd25aad 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -4,6 +4,7 @@ import ( "context" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/config" ) const ( @@ -50,6 +51,31 @@ type Durations struct { Default string `json:"default"` } +func NewDefaultClaims() *Claims { + return &Claims{ + X509: &X509Claims{ + Durations: &Durations{ + Min: config.GlobalProvisionerClaims.MinTLSDur.String(), + Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), + Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), + }, + }, + SSH: &SSHClaims{ + UserDurations: &Durations{ + Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), + }, + HostDurations: &Durations{ + Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), + }, + }, + DisableRenewal: config.DefaultDisableRenewal, + } +} + type AuthorityOption func(*AuthConfig) error func WithDefaultAuthorityID(ac *AuthConfig) error { diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go index 99228a4d..9cfab3e5 100644 --- a/authority/mgmt/db.go +++ b/authority/mgmt/db.go @@ -18,7 +18,7 @@ type DB interface { UpdateProvisioner(ctx context.Context, prov *Provisioner) error CreateAdmin(ctx context.Context, admin *Admin) error - GetAdmin(ctx context.Context, id string) error + GetAdmin(ctx context.Context, id string) (*Admin, error) GetAdmins(ctx context.Context) ([]*Admin, error) UpdateAdmin(ctx context.Context, admin *Admin) error @@ -116,7 +116,7 @@ func (m *MockDB) GetAdmins(ctx context.Context) ([]*Admin, error) { // UpdateAdmin mock func (m *MockDB) UpdateAdmin(ctx context.Context, adm *Admin) error { - if m.UpdateAdmin != nil { + if m.MockUpdateAdmin != nil { return m.MockUpdateAdmin(ctx, adm) } return m.MockError @@ -142,7 +142,7 @@ func (m *MockDB) GetAuthConfig(ctx context.Context, id string) (*AuthConfig, err // UpdateAuthConfig mock func (m *MockDB) UpdateAuthConfig(ctx context.Context, adm *AuthConfig) error { - if m.UpdateAuthConfig != nil { + if m.MockUpdateAuthConfig != nil { return m.MockUpdateAuthConfig(ctx, adm) } return m.MockError diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index cba8c59d..564ac1c7 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -6,7 +6,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/nosql" ) @@ -109,7 +108,7 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { // GetAdmins retrieves and unmarshals all active (not deleted) admins // from the database. // TODO should we be paginating? -func (db *DB) GetAdmins(ctx context.Context, az *acme.Authorization) ([]*mgmt.Admin, error) { +func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { dbEntries, err := db.db.List(authorityAdminsTable) if err != nil { return nil, errors.Wrap(err, "error loading admins") diff --git a/authority/mgmt/db/nosql/authConfig.go b/authority/mgmt/db/nosql/authConfig.go index 6fe1266b..fe189ce3 100644 --- a/authority/mgmt/db/nosql/authConfig.go +++ b/authority/mgmt/db/nosql/authConfig.go @@ -12,13 +12,12 @@ import ( ) type dbAuthConfig struct { - ID string `json:"id"` - ASN1DN *config.ASN1DN `json:"asn1dn"` - Claims *mgmt.Claims `json:"claims"` - DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` - Backdate string `json:"backdate,omitempty"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + ASN1DN *config.ASN1DN `json:"asn1dn"` + Claims *mgmt.Claims `json:"claims"` + Backdate string `json:"backdate,omitempty"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbAuthConfig) clone() *dbAuthConfig { @@ -63,30 +62,30 @@ func (db *DB) GetAuthConfig(ctx context.Context, id string) (*mgmt.AuthConfig, e } return &mgmt.AuthConfig{ - ID: dba.ID, - Provisioners: provs, - ASN1DN: dba.ASN1DN, - Backdate: dba.Backdate, - Claims: dba.Claims, - DisableIssuedAtCheck: dba.DisableIssuedAtCheck, + ID: dba.ID, + Provisioners: provs, + ASN1DN: dba.ASN1DN, + Backdate: dba.Backdate, + Claims: dba.Claims, }, nil } // CreateAuthConfig stores a new provisioner to the database. func (db *DB) CreateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { var err error - ac.ID, err = randID() - if err != nil { - return errors.Wrap(err, "error generating random id for provisioner") + if ac.ID == "" { + ac.ID, err = randID() + if err != nil { + return errors.Wrap(err, "error generating random id for provisioner") + } } dba := &dbAuthConfig{ - ID: ac.ID, - ASN1DN: ac.ASN1DN, - Claims: ac.Claims, - DisableIssuedAtCheck: ac.DisableIssuedAtCheck, - Backdate: ac.Backdate, - CreatedAt: clock.Now(), + ID: ac.ID, + ASN1DN: ac.ASN1DN, + Claims: ac.Claims, + Backdate: ac.Backdate, + CreatedAt: clock.Now(), } return db.save(ctx, dba.ID, dba, nil, "authConfig", authorityConfigsTable) @@ -106,7 +105,6 @@ func (db *DB) UpdateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { nu.DeletedAt = clock.Now() } nu.Claims = ac.Claims - nu.DisableIssuedAtCheck = ac.DisableIssuedAtCheck nu.Backdate = ac.Backdate return db.save(ctx, old.ID, nu, old, "authConfig", authorityProvisionersTable) diff --git a/authority/mgmt/errors.go b/authority/mgmt/errors.go index f8b6fd65..ae18619c 100644 --- a/authority/mgmt/errors.go +++ b/authority/mgmt/errors.go @@ -87,6 +87,11 @@ type Error struct { Status int `json:"-"` } +// IsType returns true if the error type matches the input type. +func (e *Error) IsType(pt ProblemType) bool { + return pt.String() == e.Type +} + // NewError creates a new Error type. func NewError(pt ProblemType, msg string, args ...interface{}) *Error { return newError(pt, errors.Errorf(msg, args...)) diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 2cb21403..427846fa 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -20,6 +20,16 @@ type ProvisionerCtx struct { Password string } +func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { + pc := &ProvisionerCtx{ + Claims: NewDefaultClaims(), + } + for _, o := range opts { + o(pc) + } + return pc +} + func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) { return func(ctx *ProvisionerCtx) { ctx.JWK = jwk @@ -62,10 +72,7 @@ func (p *Provisioner) GetOptions() *provisioner.Options { } func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...ProvisionerOption) (*Provisioner, error) { - pc := new(ProvisionerCtx) - for _, o := range opts { - o(pc) - } + pc := NewProvisionerCtx(opts...) details, err := createJWKDetails(pc) if err != nil { diff --git a/ca/ca.go b/ca/ca.go index d9bcdd5e..ec10f74c 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -18,6 +18,7 @@ import ( "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/mgmt" + mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" @@ -143,6 +144,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { dns = fmt.Sprintf("%s:%s", dns, port) } + // ACME Router prefix := "acme" var acmeDB acme.DB if config.DB == nil { @@ -169,6 +171,16 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { acmeHandler.Route(r) }) + // MGMT Router + + mgmtDB := auth.GetMgmtDatabase() + if mgmtDB != nil { + mgmtHandler := mgmtAPI.NewHandler(mgmtDB) + mux.Route("/mgmt", func(r chi.Router) { + mgmtHandler.Route(r) + }) + } + // helpful routine for logging all routes // /* walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index 1172218f..ac494d90 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -60,10 +60,10 @@ func (c *MgmtClient) retryOnError(r *http.Response) bool { return false } -// GetAdmin performs the GET /config/admin/{id} request to the CA. +// GetAdmin performs the GET /mgmt/admin/{id} request to the CA. func (c *MgmtClient) GetAdmin(id string) (*mgmt.Admin, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/config/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -83,10 +83,10 @@ retry: return adm, nil } -// GetAdmins performs the GET /config/admins request to the CA. +// GetAdmins performs the GET /mgmt/admins request to the CA. func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: "/config/admins"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admins"}) retry: resp, err := c.client.Get(u.String()) if err != nil { From 98a6e545301bdfe88bc23786982a2d254f4b0b1c Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 11 May 2021 15:25:37 -0700 Subject: [PATCH 005/291] wip --- authority/mgmt/admin.go | 26 +++--- authority/mgmt/api/provisioner.go | 40 +++++---- authority/mgmt/config.go | 14 ++- authority/mgmt/db/nosql/admin.go | 8 +- authority/mgmt/db/nosql/provisioner.go | 115 +++++++++++++++++++++---- authority/mgmt/provisioner.go | 94 +++++--------------- ca/mgmtClient.go | 23 +++++ 7 files changed, 194 insertions(+), 126 deletions(-) diff --git a/authority/mgmt/admin.go b/authority/mgmt/admin.go index 8a4104a5..9ceabe93 100644 --- a/authority/mgmt/admin.go +++ b/authority/mgmt/admin.go @@ -4,25 +4,21 @@ import "context" // Admin type. type Admin struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - ProvisionerID string `json:"provisionerID"` - Name string `json:"name"` - ProvisionerName string `json:"provisionerName"` - ProvisionerType string `json:"provisionerType"` - IsSuperAdmin bool `json:"isSuperAdmin"` - Status StatusType `json:"status"` + ID string `json:"id"` + AuthorityID string `json:"-"` + ProvisionerID string `json:"provisionerID"` + Name string `json:"name"` + IsSuperAdmin bool `json:"isSuperAdmin"` + Status StatusType `json:"status"` } // CreateAdmin builds and stores an admin type in the DB. -func CreateAdmin(ctx context.Context, db DB, name string, prov *Provisioner, isSuperAdmin bool) (*Admin, error) { +func CreateAdmin(ctx context.Context, db DB, name string, provID string, isSuperAdmin bool) (*Admin, error) { adm := &Admin{ - Name: name, - ProvisionerID: prov.ID, - ProvisionerName: prov.Name, - ProvisionerType: prov.Type, - IsSuperAdmin: isSuperAdmin, - Status: StatusActive, + Name: name, + ProvisionerID: provID, + IsSuperAdmin: isSuperAdmin, + Status: StatusActive, } if err := db.CreateAdmin(ctx, adm); err != nil { return nil, WrapErrorISE(err, "error creating admin") diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index 43293221..b6b4c1c7 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -1,6 +1,7 @@ package api import ( + "fmt" "net/http" "github.com/go-chi/chi" @@ -10,12 +11,14 @@ import ( // CreateProvisionerRequest represents the body for a CreateProvisioner request. type CreateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *mgmt.Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` + Type string `json:"type"` + Name string `json:"name"` + Claims *mgmt.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` } // Validate validates a new-provisioner request body. @@ -25,10 +28,12 @@ func (car *CreateProvisionerRequest) Validate() error { // UpdateProvisionerRequest represents the body for a UpdateProvisioner request. type UpdateProvisionerRequest struct { - Claims *mgmt.Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` + Claims *mgmt.Claims `json:"claims"` + Details interface{} `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` } // Validate validates a new-provisioner request body. @@ -58,6 +63,7 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) return } + fmt.Printf("provs = %+v\n", provs) api.JSON(w, provs) } @@ -75,12 +81,14 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { } prov := &mgmt.Provisioner{ - Type: body.Type, - Name: body.Name, - Claims: body.Claims, - Details: body.Details, - X509Template: body.X509Template, - SSHTemplate: body.SSHTemplate, + Type: body.Type, + Name: body.Name, + Claims: body.Claims, + Details: body.Details, + X509Template: body.X509Template, + X509TemplateData: body.X509TemplateData, + SSHTemplate: body.SSHTemplate, + SSHTemplateData: body.SSHTemplateData, } if err := h.db.CreateProvisioner(ctx, prov); err != nil { api.WriteError(w, err) diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index 0cd25aad..b3ece47f 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -2,6 +2,7 @@ package mgmt import ( "context" + "fmt" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" @@ -24,6 +25,17 @@ const ( StatusDeleted ) +func (st StatusType) String() string { + switch st { + case StatusActive: + return "active" + case StatusDeleted: + return "deleted" + default: + return fmt.Sprintf("status %d not found", st) + } +} + // Claims encapsulates all x509 and ssh claims applied to the authority // configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc. type Claims struct { @@ -111,7 +123,7 @@ func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*A return nil, WrapErrorISE(err, "error creating first provisioner") } - admin, err := CreateAdmin(ctx, db, "Change Me", prov, true) + admin, err := CreateAdmin(ctx, db, "Change Me", prov.ID, true) if err != nil { // TODO should we try to clean up? return nil, WrapErrorISE(err, "error creating first provisioner") diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 564ac1c7..97fc81b0 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -63,19 +63,13 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { return nil, err } if adm.Status == mgmt.StatusDeleted { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted") + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID) } if adm.AuthorityID != db.authorityID { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, "admin %s is not owned by authority %s", adm.ID, db.authorityID) } - prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) - if err != nil { - return nil, err - } - adm.ProvisionerName = prov.Name - adm.ProvisionerType = prov.Type return adm, nil } diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index 93dcc47b..dbea49f4 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -3,6 +3,7 @@ package nosql import ( "context" "encoding/json" + "fmt" "time" "github.com/pkg/errors" @@ -12,16 +13,18 @@ import ( // dbProvisioner is the database representation of a Provisioner type. type dbProvisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Type string `json:"type"` - Name string `json:"name"` - Claims *mgmt.Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - SSHTemplate string `json:"sshTemplate"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type string `json:"type"` + Name string `json:"name"` + Claims *mgmt.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbProvisioner) clone() *dbProvisioner { @@ -84,19 +87,36 @@ func unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) { return dbp, nil } +type detailsType struct { + Type mgmt.ProvisionerType +} + func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { dbp, err := unmarshalDBProvisioner(data, id) if err != nil { return nil, err } + dt := new(detailsType) + if err := json.Unmarshal(dbp.Details, dt); err != nil { + return nil, mgmt.WrapErrorISE(err, "error unmarshaling details to detailsType for provisioner %s", id) + } + details, err := unmarshalDetails(dt.Type, dbp.Details) + if err != nil { + return nil, err + } + prov := &mgmt.Provisioner{ - ID: dbp.ID, - Type: dbp.Type, - Name: dbp.Name, - Claims: dbp.Claims, - X509Template: dbp.X509Template, - SSHTemplate: dbp.SSHTemplate, + ID: dbp.ID, + AuthorityID: dbp.AuthorityID, + Type: dbp.Type, + Name: dbp.Name, + Claims: dbp.Claims, + Details: details, + X509Template: dbp.X509Template, + X509TemplateData: dbp.X509TemplateData, + SSHTemplate: dbp.SSHTemplate, + SSHTemplateData: dbp.SSHTemplateData, } if !dbp.DeletedAt.IsZero() { prov.Status = mgmt.StatusDeleted @@ -172,3 +192,66 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) } + +func unmarshalDetails(typ ProvisionerType, details []byte) (interface{}, error) { + if !s.Valid { + return nil, nil + } + var v isProvisionerDetails_Data + switch typ { + case ProvisionerTypeJWK: + p := new(ProvisionerDetailsJWK) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + if p.JWK.Key.Key == nil { + key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) + if err != nil { + return nil, err + } + p.JWK.Key = key + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_OIDC: + v = new(ProvisionerDetails_OIDC) + case ProvisionerType_GCP: + v = new(ProvisionerDetails_GCP) + case ProvisionerType_AWS: + v = new(ProvisionerDetails_AWS) + case ProvisionerType_AZURE: + v = new(ProvisionerDetails_Azure) + case ProvisionerType_ACME: + v = new(ProvisionerDetails_ACME) + case ProvisionerType_X5C: + p := new(ProvisionerDetails_X5C) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.X5C.GetRoots() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_K8SSA: + p := new(ProvisionerDetails_K8SSA) + if err := json.Unmarshal([]byte(s.String), p); err != nil { + return nil, err + } + for _, k := range p.K8SSA.GetPublicKeys() { + if err := k.Select(ctx, db, k.Id.Id); err != nil { + return nil, err + } + } + return &ProvisionerDetails{Data: p}, nil + case ProvisionerType_SSHPOP: + v = new(ProvisionerDetails_SSHPOP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal([]byte(s.String), v); err != nil { + return nil, err + } + return &ProvisionerDetails{Data: v}, nil +} diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 427846fa..c455bf85 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -20,6 +20,17 @@ type ProvisionerCtx struct { Password string } +type ProvisionerType string + +var ( + ProvisionerTypeJWK = ProvisionerType("JWK") + ProvisionerTypeOIDC = ProvisionerType("OIDC") + ProvisionerTypeACME = ProvisionerType("ACME") + ProvisionerTypeX5C = ProvisionerType("X5C") + ProvisionerTypeK8S = ProvisionerType("K8S") + ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP") +) + func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { pc := &ProvisionerCtx{ Claims: NewDefaultClaims(), @@ -97,12 +108,14 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro return p, nil } -type ProvisionerDetails_JWK struct { - PubKey []byte `json:"pubKey"` - PrivKey string `json:"privKey"` +// ProvisionerDetailsJWK represents the values required by a JWK provisioner. +type ProvisionerDetailsJWK struct { + Type ProvisionerType `json:"type"` + PubKey []byte `json:"pubKey"` + EncPrivKey string `json:"privKey"` } -func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetails_JWK, error) { +func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { var err error if pc.JWK != nil && pc.JWE == nil { @@ -131,9 +144,10 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetails_JWK, error) { return nil, WrapErrorISE(err, "error serializing JWE") } - return &ProvisionerDetails_JWK{ - PubKey: jwkPubBytes, - PrivKey: jwePrivStr, + return &ProvisionerDetailsJWK{ + Type: ProvisionerTypeJWK, + PubKey: jwkPubBytes, + EncPrivKey: jwePrivStr, }, nil } @@ -150,7 +164,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { } switch details := p.Details.(type) { - case *ProvisionerDetails_JWK: + case *ProvisionerDetailsJWK: jwk := new(jose.JSONWebKey) if err := json.Unmarshal(details.PubKey, &jwk); err != nil { return nil, err @@ -159,7 +173,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { Type: p.Type, Name: p.Name, Key: jwk, - EncryptedKey: details.PrivKey, + EncryptedKey: details.EncPrivKey, Claims: claims, Options: p.GetOptions(), }, nil @@ -324,68 +338,6 @@ func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) { }, nil } -func unmarshalDetails(ctx context.Context, db database.DB, typ ProvisionerType, s sql.NullString) (*ProvisionerDetails, error) { - if !s.Valid { - return nil, nil - } - var v isProvisionerDetails_Data - switch typ { - case ProvisionerType_JWK: - p := new(ProvisionerDetails_JWK) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - if p.JWK.Key.Key == nil { - key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) - if err != nil { - return nil, err - } - p.JWK.Key = key - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_OIDC: - v = new(ProvisionerDetails_OIDC) - case ProvisionerType_GCP: - v = new(ProvisionerDetails_GCP) - case ProvisionerType_AWS: - v = new(ProvisionerDetails_AWS) - case ProvisionerType_AZURE: - v = new(ProvisionerDetails_Azure) - case ProvisionerType_ACME: - v = new(ProvisionerDetails_ACME) - case ProvisionerType_X5C: - p := new(ProvisionerDetails_X5C) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.X5C.GetRoots() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_K8SSA: - p := new(ProvisionerDetails_K8SSA) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.K8SSA.GetPublicKeys() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_SSHPOP: - v = new(ProvisionerDetails_SSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", typ) - } - - if err := json.Unmarshal([]byte(s.String), v); err != nil { - return nil, err - } - return &ProvisionerDetails{Data: v}, nil -} func marshalClaims(c *Claims) (sql.NullString, error) { b, err := json.Marshal(c) diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index ac494d90..67d6631c 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -105,3 +105,26 @@ retry: } return *admins, nil } + +// GetProvisioners performs the GET /mgmt/provisioners request to the CA. +func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioners"}) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readError(resp.Body) + } + var provs = new([]*mgmt.Provisioner) + if err := readJSON(resp.Body, provs); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return *provs, nil +} From 4d48072746864c44f44349f6f3d419a8a452af3f Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 12 May 2021 00:03:40 -0700 Subject: [PATCH 006/291] wip admin CRUD --- authority/authority.go | 1 + authority/mgmt/api/admin.go | 142 +++++++++++++++---------- authority/mgmt/api/handler.go | 4 +- authority/mgmt/db/nosql/admin.go | 1 + authority/mgmt/db/nosql/provisioner.go | 90 ++++++---------- authority/mgmt/errors.go | 11 +- authority/mgmt/provisioner.go | 112 +++++++++++-------- ca/client.go | 5 + ca/mgmtClient.go | 85 +++++++++++++++ 9 files changed, 295 insertions(+), 156 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 016b838a..ee82eb13 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -162,6 +162,7 @@ func (a *Authority) init() error { return mgmt.WrapErrorISE(err, "error getting authConfig from db") } } + a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates() if err != nil { return err diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index f544b631..ae60b75a 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -5,13 +5,14 @@ import ( "github.com/go-chi/chi" "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/mgmt" ) // CreateAdminRequest represents the body for a CreateAdmin request. type CreateAdminRequest struct { - Name string `json:"name"` - Provisioner string `json:"provisioner"` - IsSuperAdmin bool `json:"isSuperAdmin"` + Name string `json:"name"` + ProvisionerID string `json:"provisionerID"` + IsSuperAdmin bool `json:"isSuperAdmin"` } // Validate validates a new-admin request body. @@ -21,9 +22,10 @@ func (car *CreateAdminRequest) Validate() error { // UpdateAdminRequest represents the body for a UpdateAdmin request. type UpdateAdminRequest struct { - Name string `json:"name"` - Provisioner string `json:"provisioner"` - IsSuperAdmin bool `json:"isSuperAdmin"` + Name string `json:"name"` + ProvisionerID string `json:"provisionerID"` + IsSuperAdmin string `json:"isSuperAdmin"` + Status string `json:"status"` } // Validate validates a new-admin request body. @@ -31,6 +33,11 @@ func (uar *UpdateAdminRequest) Validate() error { return nil } +// DeleteResponse is the resource for successful DELETE responses. +type DeleteResponse struct { + Status string `json:"status"` +} + // GetAdmin returns the requested admin, or an error. func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -58,59 +65,84 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { // CreateAdmin creates a new admin. func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { - /* - ctx := r.Context() - - var body CreateAdminRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - } - - adm := &config.Admin{ - Name: body.Name, - Provisioner: body.Provisioner, - IsSuperAdmin: body.IsSuperAdmin, - } - if err := h.db.CreateAdmin(ctx, adm); err != nil { - api.WriteError(w, err) - return - } - api.JSONStatus(w, adm, http.StatusCreated) - */ + ctx := r.Context() + + var body CreateAdminRequest + if err := api.ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + return + } + + // TODO validate + + adm := &mgmt.Admin{ + ProvisionerID: body.ProvisionerID, + Name: body.Name, + IsSuperAdmin: body.IsSuperAdmin, + Status: mgmt.StatusActive, + } + if err := h.db.CreateAdmin(ctx, adm); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) + return + } + api.JSON(w, adm) +} + +// DeleteAdmin deletes admin. +func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + id := chi.URLParam(r, "id") + + adm, err := h.db.GetAdmin(ctx, id) + if err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) + return + } + adm.Status = mgmt.StatusDeleted + if err := h.db.UpdateAdmin(ctx, adm); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) + return + } + api.JSON(w, &DeleteResponse{Status: "ok"}) } // UpdateAdmin updates an existing admin. func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { - /* - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateAdminRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - if adm, err := h.db.GetAdmin(ctx, id); err != nil { - api.WriteError(w, err) - return - } + ctx := r.Context() + + var body UpdateAdminRequest + if err := api.ReadJSON(r.Body, &body); err != nil { + api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + return + } + + id := chi.URLParam(r, "id") + adm, err := h.db.GetAdmin(ctx, id) + if err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) + return + } + + // TODO validate + + if len(body.Name) > 0 { adm.Name = body.Name - adm.Provisioner = body.Provisioner - adm.IsSuperAdmin = body.IsSuperAdmin - - if err := h.db.UpdateAdmin(ctx, adm); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, adm) - */ + } + if len(body.Status) > 0 { + adm.Status = mgmt.StatusActive // FIXME + } + // Set IsSuperAdmin iff the string was set in the update request. + if len(body.IsSuperAdmin) > 0 { + adm.IsSuperAdmin = (body.IsSuperAdmin == "true") + } + if len(body.ProvisionerID) > 0 { + adm.ProvisionerID = body.ProvisionerID + } + if err := h.db.UpdateAdmin(ctx, adm); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) + return + } + api.JSON(w, adm) } diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index 0e512a39..778cdaea 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -33,13 +33,15 @@ func (h *Handler) Route(r api.Router) { r.MethodFunc("GET", "/provisioner/{id}", h.GetProvisioner) r.MethodFunc("GET", "/provisioners", h.GetProvisioners) r.MethodFunc("POST", "/provisioner", h.CreateProvisioner) - r.MethodFunc("PUT", "/provsiioner/{id}", h.UpdateProvisioner) + r.MethodFunc("PUT", "/provisioner/{id}", h.UpdateProvisioner) + //r.MethodFunc("DELETE", "/provisioner/{id}", h.UpdateAdmin) // Admins r.MethodFunc("GET", "/admin/{id}", h.GetAdmin) r.MethodFunc("GET", "/admins", h.GetAdmins) r.MethodFunc("POST", "/admin", h.CreateAdmin) r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin) + r.MethodFunc("DELETE", "/admin/{id}", h.DeleteAdmin) // AuthConfig r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig) diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 97fc81b0..70cb12d1 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -131,6 +131,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { if err != nil { return errors.Wrap(err, "error generating random id for admin") } + adm.AuthorityID = db.authorityID dba := &dbAdmin{ ID: adm.ID, diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index dbea49f4..6d9f74ab 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -126,7 +126,6 @@ func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { // GetProvisioners retrieves and unmarshals all active (not deleted) provisioners // from the database. -// TODO should we be paginating? func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) { dbEntries, err := db.db.List(authorityProvisionersTable) if err != nil { @@ -157,13 +156,18 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err return errors.Wrap(err, "error generating random id for provisioner") } + details, err := json.Marshal(prov.Details) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner") + } + dbp := &dbProvisioner{ ID: prov.ID, AuthorityID: db.authorityID, Type: prov.Type, Name: prov.Name, Claims: prov.Claims, - Details: prov.Details, + Details: details, X509Template: prov.X509Template, SSHTemplate: prov.SSHTemplate, CreatedAt: clock.Now(), @@ -186,72 +190,44 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err nu.DeletedAt = clock.Now() } nu.Claims = prov.Claims - nu.Details = prov.Details nu.X509Template = prov.X509Template nu.SSHTemplate = prov.SSHTemplate + nu.Details, err = json.Marshal(prov.Details) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner") + } + return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) } -func unmarshalDetails(typ ProvisionerType, details []byte) (interface{}, error) { - if !s.Valid { - return nil, nil - } - var v isProvisionerDetails_Data +func unmarshalDetails(typ mgmt.ProvisionerType, data []byte) (mgmt.ProvisionerDetails, error) { + var v mgmt.ProvisionerDetails switch typ { - case ProvisionerTypeJWK: - p := new(ProvisionerDetailsJWK) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - if p.JWK.Key.Key == nil { - key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id) - if err != nil { - return nil, err - } - p.JWK.Key = key - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_OIDC: - v = new(ProvisionerDetails_OIDC) - case ProvisionerType_GCP: - v = new(ProvisionerDetails_GCP) - case ProvisionerType_AWS: - v = new(ProvisionerDetails_AWS) - case ProvisionerType_AZURE: - v = new(ProvisionerDetails_Azure) - case ProvisionerType_ACME: - v = new(ProvisionerDetails_ACME) - case ProvisionerType_X5C: - p := new(ProvisionerDetails_X5C) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.X5C.GetRoots() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_K8SSA: - p := new(ProvisionerDetails_K8SSA) - if err := json.Unmarshal([]byte(s.String), p); err != nil { - return nil, err - } - for _, k := range p.K8SSA.GetPublicKeys() { - if err := k.Select(ctx, db, k.Id.Id); err != nil { - return nil, err - } - } - return &ProvisionerDetails{Data: p}, nil - case ProvisionerType_SSHPOP: - v = new(ProvisionerDetails_SSHPOP) + case mgmt.ProvisionerTypeJWK: + v = new(mgmt.ProvisionerDetailsJWK) + case mgmt.ProvisionerTypeOIDC: + v = new(mgmt.ProvisionerDetailsOIDC) + case mgmt.ProvisionerTypeGCP: + v = new(mgmt.ProvisionerDetailsGCP) + case mgmt.ProvisionerTypeAWS: + v = new(mgmt.ProvisionerDetailsAWS) + case mgmt.ProvisionerTypeAZURE: + v = new(mgmt.ProvisionerDetailsAzure) + case mgmt.ProvisionerTypeACME: + v = new(mgmt.ProvisionerDetailsACME) + case mgmt.ProvisionerTypeX5C: + v = new(mgmt.ProvisionerDetailsX5C) + case mgmt.ProvisionerTypeK8SSA: + v = new(mgmt.ProvisionerDetailsK8SSA) + case mgmt.ProvisionerTypeSSHPOP: + v = new(mgmt.ProvisionerDetailsSSHPOP) default: return nil, fmt.Errorf("unsupported provisioner type %s", typ) } - if err := json.Unmarshal([]byte(s.String), v); err != nil { + if err := json.Unmarshal(data, v); err != nil { return nil, err } - return &ProvisionerDetails{Data: v}, nil + return v, nil } diff --git a/authority/mgmt/errors.go b/authority/mgmt/errors.go index ae18619c..f0f90400 100644 --- a/authority/mgmt/errors.go +++ b/authority/mgmt/errors.go @@ -23,6 +23,8 @@ const ( ErrorAuthorityMismatchType // ErrorDeletedType resource has been deleted. ErrorDeletedType + // ErrorBadRequestType bad request. + ErrorBadRequestType // ErrorServerInternalType internal server error. ErrorServerInternalType ) @@ -37,6 +39,8 @@ func (ap ProblemType) String() string { return "authorityMismatch" case ErrorDeletedType: return "deleted" + case ErrorBadRequestType: + return "badRequest" case ErrorServerInternalType: return "internalServerError" default: @@ -69,10 +73,15 @@ var ( status: 401, }, ErrorDeletedType: { - typ: ErrorNotFoundType.String(), + typ: ErrorDeletedType.String(), details: "resource is deleted", status: 403, }, + ErrorBadRequestType: { + typ: ErrorBadRequestType.String(), + details: "bad request", + status: 400, + }, ErrorServerInternalType: errorServerInternalMetadata, } ) diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index c455bf85..961907f8 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -23,12 +23,15 @@ type ProvisionerCtx struct { type ProvisionerType string var ( + ProvisionerTypeACME = ProvisionerType("ACME") + ProvisionerTypeAWS = ProvisionerType("AWS") + ProvisionerTypeAZURE = ProvisionerType("AZURE") + ProvisionerTypeGCP = ProvisionerType("GCP") ProvisionerTypeJWK = ProvisionerType("JWK") + ProvisionerTypeK8SSA = ProvisionerType("K8SSA") ProvisionerTypeOIDC = ProvisionerType("OIDC") - ProvisionerTypeACME = ProvisionerType("ACME") - ProvisionerTypeX5C = ProvisionerType("X5C") - ProvisionerTypeK8S = ProvisionerType("K8S") ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP") + ProvisionerTypeX5C = ProvisionerType("X5C") ) func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { @@ -56,8 +59,8 @@ func WithPassword(pass string) func(*ProvisionerCtx) { // Provisioner type. type Provisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` Type string `json:"type"` Name string `json:"name"` Claims *Claims `json:"claims"` @@ -108,6 +111,10 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro return p, nil } +type ProvisionerDetails interface { + isProvisionerDetails() +} + // ProvisionerDetailsJWK represents the values required by a JWK provisioner. type ProvisionerDetailsJWK struct { Type ProvisionerType `json:"type"` @@ -115,6 +122,64 @@ type ProvisionerDetailsJWK struct { EncPrivKey string `json:"privKey"` } +// ProvisionerDetailsOIDC represents the values required by a OIDC provisioner. +type ProvisionerDetailsOIDC struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsGCP represents the values required by a GCP provisioner. +type ProvisionerDetailsGCP struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsAWS represents the values required by a AWS provisioner. +type ProvisionerDetailsAWS struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsAzure represents the values required by a Azure provisioner. +type ProvisionerDetailsAzure struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsACME represents the values required by a ACME provisioner. +type ProvisionerDetailsACME struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsX5C represents the values required by a X5C provisioner. +type ProvisionerDetailsX5C struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsK8SSA represents the values required by a K8SSA provisioner. +type ProvisionerDetailsK8SSA struct { + Type ProvisionerType `json:"type"` +} + +// ProvisionerDetailsSSHPOP represents the values required by a SSHPOP provisioner. +type ProvisionerDetailsSSHPOP struct { + Type ProvisionerType `json:"type"` +} + +func (*ProvisionerDetailsJWK) isProvisionerDetails() {} + +func (*ProvisionerDetailsOIDC) isProvisionerDetails() {} + +func (*ProvisionerDetailsGCP) isProvisionerDetails() {} + +func (*ProvisionerDetailsAWS) isProvisionerDetails() {} + +func (*ProvisionerDetailsAzure) isProvisionerDetails() {} + +func (*ProvisionerDetailsACME) isProvisionerDetails() {} + +func (*ProvisionerDetailsX5C) isProvisionerDetails() {} + +func (*ProvisionerDetailsK8SSA) isProvisionerDetails() {} + +func (*ProvisionerDetailsSSHPOP) isProvisionerDetails() {} + func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { var err error @@ -159,10 +224,6 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { return nil, err } - if err != nil { - return nil, err - } - switch details := p.Details.(type) { case *ProvisionerDetailsJWK: jwk := new(jose.JSONWebKey) @@ -325,36 +386,3 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) { EnableSSHCA: &c.SSH.Enabled, }, nil } - -/* -func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) { - b, err := json.Marshal(d.GetData()) - if err != nil { - return sql.NullString{}, nil - } - return sql.NullString{ - String: string(b), - Valid: len(b) > 0, - }, nil -} - - -func marshalClaims(c *Claims) (sql.NullString, error) { - b, err := json.Marshal(c) - if err != nil { - return sql.NullString{}, nil - } - return sql.NullString{ - String: string(b), - Valid: len(b) > 0, - }, nil -} - -func unmarshalClaims(s sql.NullString) (*Claims, error) { - if !s.Valid { - return nil, nil - } - v := new(Claims) - return v, json.Unmarshal([]byte(s.String), v) -} -*/ diff --git a/ca/client.go b/ca/client.go index 2292c41e..3a3350ac 100644 --- a/ca/client.go +++ b/ca/client.go @@ -88,6 +88,11 @@ func (c *uaClient) Post(url, contentType string, body io.Reader) (*http.Response return c.Client.Do(req) } +func (c *uaClient) Do(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", UserAgent) + return c.Client.Do(req) +} + // RetryFunc defines the method used to retry a request. If it returns true, the // request will be retried once. type RetryFunc func(code int) bool diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index 67d6631c..47316ad6 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -1,12 +1,16 @@ package ca import ( + "bytes" + "encoding/json" "net/http" "net/url" "path" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/mgmt" + mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" + "github.com/smallstep/certificates/errs" ) // MgmtClient implements an HTTP client for the CA server. @@ -83,6 +87,87 @@ retry: return adm, nil } +// CreateAdmin performs the POST /mgmt/admin request to the CA. +func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { + var retried bool + body, err := json.Marshal(req) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admin"}) +retry: + resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "client POST %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readError(resp.Body) + } + var adm = new(mgmt.Admin) + if err := readJSON(resp.Body, adm); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return adm, nil +} + +// RemoveAdmin performs the DELETE /mgmt/admin/{id} request to the CA. +func (c *MgmtClient) RemoveAdmin(id string) error { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) + req, err := http.NewRequest("DELETE", u.String(), nil) + if err != nil { + return errors.Wrapf(err, "create DELETE %s request failed", u) + } +retry: + resp, err := c.client.Do(req) + if err != nil { + return errors.Wrapf(err, "client DELETE %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return readError(resp.Body) + } + return nil +} + +// UpdateAdmin performs the PUT /mgmt/admin/{id} request to the CA. +func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*mgmt.Admin, error) { + var retried bool + body, err := json.Marshal(uar) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) + req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } +retry: + resp, err := c.client.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "client PUT %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readError(resp.Body) + } + var adm = new(mgmt.Admin) + if err := readJSON(resp.Body, adm); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return adm, nil +} + // GetAdmins performs the GET /mgmt/admins request to the CA. func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) { var retried bool From 5d09d04d144bddd56c9d6490a929fedaf8c0d5a1 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 17 May 2021 21:07:25 -0700 Subject: [PATCH 007/291] wip --- api/errors.go | 4 + authority/admin.go | 12 -- authority/admin/admin.go | 15 ++ authority/admin/collection.go | 173 +++++++++++++++ authority/authority.go | 74 +++++++ authority/config/config.go | 2 + authority/mgmt/admin.go | 52 +++-- authority/mgmt/api/admin.go | 64 +++--- authority/mgmt/api/authConfig.go | 33 --- authority/mgmt/api/handler.go | 17 +- authority/mgmt/api/provisioner.go | 91 +++++++- authority/mgmt/authConfig.go | 12 +- authority/mgmt/config.go | 32 ++- authority/mgmt/db.go | 26 ++- authority/mgmt/db/nosql/admin.go | 112 ++++++---- authority/mgmt/db/nosql/authConfig.go | 5 + authority/mgmt/db/nosql/nosql.go | 13 +- authority/mgmt/db/nosql/provisioner.go | 278 ++++++++++++++++++------- authority/mgmt/errors.go | 17 +- authority/mgmt/provisioner.go | 69 +++++- authority/provisioner/collection.go | 12 ++ authority/provisioner/jwk.go | 4 + ca/ca.go | 3 +- ca/mgmtClient.go | 151 +++++++++++++- 24 files changed, 1007 insertions(+), 264 deletions(-) delete mode 100644 authority/admin.go create mode 100644 authority/admin/admin.go create mode 100644 authority/admin/collection.go diff --git a/api/errors.go b/api/errors.go index fa2d6a06..085d05cf 100644 --- a/api/errors.go +++ b/api/errors.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" ) @@ -18,6 +19,9 @@ func WriteError(w http.ResponseWriter, err error) { case *acme.Error: acme.WriteError(w, k) return + case *mgmt.Error: + mgmt.WriteError(w, k) + return default: w.Header().Set("Content-Type", "application/json") } diff --git a/authority/admin.go b/authority/admin.go deleted file mode 100644 index 6c95de4f..00000000 --- a/authority/admin.go +++ /dev/null @@ -1,12 +0,0 @@ -package authority - -// Admin is the type definining Authority admins. Admins can update Authority -// configuration, provisioners, and even other admins. -type Admin struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Name string `json:"name"` - Provisioner string `json:"provisioner"` - IsSuperAdmin bool `json:"isSuperAdmin"` - IsDeleted bool `json:"isDeleted"` -} diff --git a/authority/admin/admin.go b/authority/admin/admin.go new file mode 100644 index 00000000..579538dc --- /dev/null +++ b/authority/admin/admin.go @@ -0,0 +1,15 @@ +package admin + +// Type specifies the type of administrator privileges the admin has. +type Type string + +// Admin type. +type Admin struct { + ID string `json:"id"` + AuthorityID string `json:"-"` + Subject string `json:"subject"` + ProvisionerName string `json:"provisionerName"` + ProvisionerType string `json:"provisionerType"` + ProvisionerID string `json:"provisionerID"` + Type Type `json:"type"` +} diff --git a/authority/admin/collection.go b/authority/admin/collection.go new file mode 100644 index 00000000..87dd63ce --- /dev/null +++ b/authority/admin/collection.go @@ -0,0 +1,173 @@ +package admin + +import ( + "crypto/sha1" + "sync" + + "github.com/pkg/errors" + "go.step.sm/crypto/jose" +) + +// DefaultProvisionersLimit is the default limit for listing provisioners. +const DefaultProvisionersLimit = 20 + +// DefaultProvisionersMax is the maximum limit for listing provisioners. +const DefaultProvisionersMax = 100 + +/* +type uidProvisioner struct { + provisioner Interface + uid string +} + +type provisionerSlice []uidProvisioner + +func (p provisionerSlice) Len() int { return len(p) } +func (p provisionerSlice) Less(i, j int) bool { return p[i].uid < p[j].uid } +func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +*/ + +// loadByTokenPayload is a payload used to extract the id used to load the +// provisioner. +type loadByTokenPayload struct { + jose.Claims + AuthorizedParty string `json:"azp"` // OIDC client id + TenantID string `json:"tid"` // Microsoft Azure tenant id +} + +// Collection is a memory map of admins. +type Collection struct { + byID *sync.Map + bySubProv *sync.Map + byProv *sync.Map + count int + countByProvisioner map[string]int +} + +// NewCollection initializes a collection of provisioners. The given list of +// audiences are the audiences used by the JWT provisioner. +func NewCollection() *Collection { + return &Collection{ + byID: new(sync.Map), + byProv: new(sync.Map), + bySubProv: new(sync.Map), + countByProvisioner: map[string]int{}, + } +} + +// LoadByID a admin by the ID. +func (c *Collection) LoadByID(id string) (*Admin, bool) { + return loadAdmin(c.byID, id) +} + +func subProvNameHash(sub, provName string) string { + subHash := sha1.Sum([]byte(sub)) + provNameHash := sha1.Sum([]byte(provName)) + _res := sha1.Sum(append(subHash[:], provNameHash[:]...)) + return string(_res[:]) +} + +// LoadBySubProv a admin by the subject and provisioner name. +func (c *Collection) LoadBySubProv(sub, provName string) (*Admin, bool) { + return loadAdmin(c.bySubProv, subProvNameHash(sub, provName)) +} + +// LoadByProvisioner a admin by the subject and provisioner name. +func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) { + a, ok := c.byProv.Load(provName) + if !ok { + return nil, false + } + admins, ok := a.([]*Admin) + if !ok { + return nil, false + } + return admins, true +} + +// Store adds an admin to the collection and enforces the uniqueness of +// admin IDs and amdin subject <-> provisioner name combos. +func (c *Collection) Store(adm *Admin) error { + provName := adm.ProvisionerName + // Store admin always in byID. ID must be unique. + if _, loaded := c.byID.LoadOrStore(adm.ID, adm); loaded { + return errors.New("cannot add multiple admins with the same id") + } + + // Store admin alwasy in bySubProv. Subject <-> ProvisionerName must be unique. + if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded { + c.byID.Delete(adm.ID) + return errors.New("cannot add multiple admins with the same subject and provisioner") + } + + if admins, ok := c.LoadByProvisioner(provName); ok { + c.byProv.Store(provName, append(admins, adm)) + c.countByProvisioner[provName]++ + } else { + c.byProv.Store(provName, []*Admin{adm}) + c.countByProvisioner[provName] = 1 + } + c.count++ + + return nil +} + +// Count returns the total number of admins. +func (c *Collection) Count() int { + return c.count +} + +// CountByProvisioner returns the total number of admins. +func (c *Collection) CountByProvisioner(provName string) int { + if cnt, ok := c.countByProvisioner[provName]; ok { + return cnt + } + return 0 +} + +/* +// Find implements pagination on a list of sorted provisioners. +func (c *Collection) Find(cursor string, limit int) (List, string) { + switch { + case limit <= 0: + limit = DefaultProvisionersLimit + case limit > DefaultProvisionersMax: + limit = DefaultProvisionersMax + } + + n := c.sorted.Len() + cursor = fmt.Sprintf("%040s", cursor) + i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor }) + + slice := List{} + for ; i < n && len(slice) < limit; i++ { + slice = append(slice, c.sorted[i].provisioner) + } + + if i < n { + return slice, strings.TrimLeft(c.sorted[i].uid, "0") + } + return slice, "" +} +*/ + +func loadAdmin(m *sync.Map, key string) (*Admin, bool) { + a, ok := m.Load(key) + if !ok { + return nil, false + } + adm, ok := a.(*Admin) + if !ok { + return nil, false + } + return adm, true +} + +/* +// provisionerSum returns the SHA1 of the provisioners ID. From this we will +// create the unique and sorted id. +func provisionerSum(p Interface) []byte { + sum := sha1.Sum([]byte(p.GetID())) + return sum[:] +} +*/ diff --git a/authority/authority.go b/authority/authority.go index ee82eb13..2da5b341 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -13,6 +13,7 @@ import ( "github.com/smallstep/certificates/cas" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/mgmt" authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql" @@ -34,6 +35,7 @@ type Authority struct { mgmtDB mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection + admins *admin.Collection db db.AuthDB templates *templates.Templates @@ -127,6 +129,61 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } +func (a *Authority) ReloadAuthConfig() error { + mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) + if err != nil { + return mgmt.WrapErrorISE(err, "error getting authConfig from db") + } + + a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates() + if err != nil { + return mgmt.WrapErrorISE(err, "error converting mgmt authConfig to certificates authConfig") + } + + // Merge global and configuration claims + claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) + if err != nil { + return err + } + // TODO: should we also be combining the ssh federated roots here? + // If we rotate ssh roots keys, sshpop provisioner will lose ability to + // validate old SSH certificates, unless they are added as federated certs. + sshKeys, err := a.GetSSHRoots(context.Background()) + if err != nil { + return err + } + // Initialize provisioners + audiences := a.config.GetAudiences() + a.provisioners = provisioner.NewCollection(audiences) + config := provisioner.Config{ + Claims: claimer.Claims(), + Audiences: audiences, + DB: a.db, + SSHKeys: &provisioner.SSHKeys{ + UserKeys: sshKeys.UserKeys, + HostKeys: sshKeys.HostKeys, + }, + GetIdentityFunc: a.getIdentityFunc, + } + // Store all the provisioners + for _, p := range a.config.AuthorityConfig.Provisioners { + if err := p.Init(config); err != nil { + return err + } + if err := a.provisioners.Store(p); err != nil { + return err + } + } + // Store all the admins + a.admins = admin.NewCollection() + for _, adm := range a.config.AuthorityConfig.Admins { + if err := a.admins.Store(adm); err != nil { + return err + } + } + return nil +} + // init performs validation and initializes the fields of an Authority struct. func (a *Authority) init() error { // Check if handler has already been validated/initialized. @@ -373,6 +430,13 @@ func (a *Authority) init() error { return err } } + // Store all the admins + a.admins = admin.NewCollection() + for _, adm := range a.config.AuthorityConfig.Admins { + if err := a.admins.Store(adm); err != nil { + return err + } + } // Configure templates, currently only ssh templates are supported. if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { @@ -406,6 +470,16 @@ func (a *Authority) GetMgmtDatabase() mgmt.DB { return a.mgmtDB } +// GetAdminCollection returns the admin collection. +func (a *Authority) GetAdminCollection() *admin.Collection { + return a.admins +} + +// GetProvisionerCollection returns the admin collection. +func (a *Authority) GetProvisionerCollection() *provisioner.Collection { + return a.provisioners +} + // Shutdown safely shuts down any clients, databases, etc. held by the Authority. func (a *Authority) Shutdown() error { if err := a.keyManager.Close(); err != nil { diff --git a/authority/config/config.go b/authority/config/config.go index 66b8bbe0..9fbf18e0 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/provisioner" cas "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" @@ -95,6 +96,7 @@ type AuthConfig struct { *cas.Options AuthorityID string `json:"authorityID,omitempty"` Provisioners provisioner.List `json:"provisioners"` + Admins []*admin.Admin `json:"-"` Template *ASN1DN `json:"template,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` diff --git a/authority/mgmt/admin.go b/authority/mgmt/admin.go index 9ceabe93..f21abdce 100644 --- a/authority/mgmt/admin.go +++ b/authority/mgmt/admin.go @@ -1,27 +1,55 @@ package mgmt -import "context" +import ( + "context" + + "github.com/smallstep/certificates/authority/admin" +) + +// AdminType specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR +type AdminType string + +var ( + // AdminTypeSuper superadmin + AdminTypeSuper = AdminType("SUPER_ADMIN") + // AdminTypeRegular regular + AdminTypeRegular = AdminType("REGULAR") +) // Admin type. type Admin struct { - ID string `json:"id"` - AuthorityID string `json:"-"` - ProvisionerID string `json:"provisionerID"` - Name string `json:"name"` - IsSuperAdmin bool `json:"isSuperAdmin"` - Status StatusType `json:"status"` + ID string `json:"id"` + AuthorityID string `json:"-"` + ProvisionerID string `json:"provisionerID"` + Subject string `json:"subject"` + ProvisionerName string `json:"provisionerName"` + ProvisionerType string `json:"provisionerType"` + Type AdminType `json:"type"` + Status StatusType `json:"status"` } // CreateAdmin builds and stores an admin type in the DB. -func CreateAdmin(ctx context.Context, db DB, name string, provID string, isSuperAdmin bool) (*Admin, error) { +func CreateAdmin(ctx context.Context, db DB, provName, sub string, typ AdminType) (*Admin, error) { adm := &Admin{ - Name: name, - ProvisionerID: provID, - IsSuperAdmin: isSuperAdmin, - Status: StatusActive, + Subject: sub, + ProvisionerName: provName, + Type: typ, + Status: StatusActive, } if err := db.CreateAdmin(ctx, adm); err != nil { return nil, WrapErrorISE(err, "error creating admin") } return adm, nil } + +// ToCertificates converts an Admin to the Admin type expected by the authority. +func (adm *Admin) ToCertificates() (*admin.Admin, error) { + return &admin.Admin{ + ID: adm.ID, + Subject: adm.Subject, + ProvisionerID: adm.ProvisionerID, + ProvisionerName: adm.ProvisionerName, + ProvisionerType: adm.ProvisionerType, + Type: admin.Type(adm.Type), + }, nil +} diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index ae60b75a..f997095a 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -1,31 +1,34 @@ package api import ( + "fmt" "net/http" "github.com/go-chi/chi" "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" ) // CreateAdminRequest represents the body for a CreateAdmin request. type CreateAdminRequest struct { - Name string `json:"name"` - ProvisionerID string `json:"provisionerID"` - IsSuperAdmin bool `json:"isSuperAdmin"` + Subject string `json:"subject"` + Provisioner string `json:"provisioner"` + Type mgmt.AdminType `json:"type"` } // Validate validates a new-admin request body. -func (car *CreateAdminRequest) Validate() error { +func (car *CreateAdminRequest) Validate(c *admin.Collection) error { + if _, ok := c.LoadBySubProv(car.Subject, car.Provisioner); ok { + return mgmt.NewError(mgmt.ErrorBadRequestType, + "admin with subject %s and provisioner name %s already exists", car.Subject, car.Provisioner) + } return nil } // UpdateAdminRequest represents the body for a UpdateAdmin request. type UpdateAdminRequest struct { - Name string `json:"name"` - ProvisionerID string `json:"provisionerID"` - IsSuperAdmin string `json:"isSuperAdmin"` - Status string `json:"status"` + Type mgmt.AdminType `json:"type"` } // Validate validates a new-admin request body. @@ -73,27 +76,37 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { return } - // TODO validate + if err := body.Validate(h.auth.GetAdminCollection()); err != nil { + api.WriteError(w, err) + return + } adm := &mgmt.Admin{ - ProvisionerID: body.ProvisionerID, - Name: body.Name, - IsSuperAdmin: body.IsSuperAdmin, - Status: mgmt.StatusActive, + ProvisionerName: body.Provisioner, + Subject: body.Subject, + Type: body.Type, + Status: mgmt.StatusActive, } if err := h.db.CreateAdmin(ctx, adm); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) return } api.JSON(w, adm) + if err := h.auth.ReloadAuthConfig(); err != nil { + fmt.Printf("err = %+v\n", err) + } } // DeleteAdmin deletes admin. func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - id := chi.URLParam(r, "id") + if h.auth.GetAdminCollection().Count() == 1 { + api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove last admin")) + return + } + + ctx := r.Context() adm, err := h.db.GetAdmin(ctx, id) if err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) @@ -105,6 +118,9 @@ func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { return } api.JSON(w, &DeleteResponse{Status: "ok"}) + if err := h.auth.ReloadAuthConfig(); err != nil { + fmt.Printf("err = %+v\n", err) + } } // UpdateAdmin updates an existing admin. @@ -127,22 +143,14 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { // TODO validate - if len(body.Name) > 0 { - adm.Name = body.Name - } - if len(body.Status) > 0 { - adm.Status = mgmt.StatusActive // FIXME - } - // Set IsSuperAdmin iff the string was set in the update request. - if len(body.IsSuperAdmin) > 0 { - adm.IsSuperAdmin = (body.IsSuperAdmin == "true") - } - if len(body.ProvisionerID) > 0 { - adm.ProvisionerID = body.ProvisionerID - } + adm.Type = body.Type + if err := h.db.UpdateAdmin(ctx, adm); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) return } api.JSON(w, adm) + if err := h.auth.ReloadAuthConfig(); err != nil { + fmt.Printf("err = %+v\n", err) + } } diff --git a/authority/mgmt/api/authConfig.go b/authority/mgmt/api/authConfig.go index 283a4b66..7d5f7afb 100644 --- a/authority/mgmt/api/authConfig.go +++ b/authority/mgmt/api/authConfig.go @@ -46,39 +46,6 @@ func (h *Handler) GetAuthConfig(w http.ResponseWriter, r *http.Request) { api.JSON(w, ac) } -// CreateAuthConfig creates a new admin. -func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var body CreateAuthConfigRequest - if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - } - - ac := &mgmt.AuthConfig{ - Status: mgmt.StatusActive, - Backdate: "1m", - } - if body.ASN1DN != nil { - ac.ASN1DN = body.ASN1DN - } - if body.Claims != nil { - ac.Claims = body.Claims - } - if body.Backdate != "" { - ac.Backdate = body.Backdate - } - if err := h.db.CreateAuthConfig(ctx, ac); err != nil { - api.WriteError(w, err) - return - } - api.JSONStatus(w, ac, http.StatusCreated) -} - // UpdateAuthConfig updates an existing AuthConfig. func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) { /* diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index 778cdaea..cb52736f 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -4,6 +4,7 @@ import ( "time" "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/mgmt" ) @@ -19,32 +20,32 @@ var clock Clock // Handler is the ACME API request handler. type Handler struct { - db mgmt.DB + db mgmt.DB + auth *authority.Authority } // NewHandler returns a new Authority Config Handler. -func NewHandler(db mgmt.DB) api.RouterHandler { - return &Handler{db} +func NewHandler(db mgmt.DB, auth *authority.Authority) api.RouterHandler { + return &Handler{db, auth} } // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { // Provisioners - r.MethodFunc("GET", "/provisioner/{id}", h.GetProvisioner) + r.MethodFunc("GET", "/provisioner/{name}", h.GetProvisioner) r.MethodFunc("GET", "/provisioners", h.GetProvisioners) r.MethodFunc("POST", "/provisioner", h.CreateProvisioner) - r.MethodFunc("PUT", "/provisioner/{id}", h.UpdateProvisioner) - //r.MethodFunc("DELETE", "/provisioner/{id}", h.UpdateAdmin) + r.MethodFunc("PUT", "/provisioner/{name}", h.UpdateProvisioner) + r.MethodFunc("DELETE", "/provisioner/{name}", h.DeleteProvisioner) // Admins r.MethodFunc("GET", "/admin/{id}", h.GetAdmin) r.MethodFunc("GET", "/admins", h.GetAdmins) r.MethodFunc("POST", "/admin", h.CreateAdmin) - r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin) + r.MethodFunc("PATCH", "/admin/{id}", h.UpdateAdmin) r.MethodFunc("DELETE", "/admin/{id}", h.DeleteAdmin) // AuthConfig r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig) - r.MethodFunc("POST", "/authconfig", h.CreateAuthConfig) r.MethodFunc("PUT", "/authconfig/{id}", h.UpdateAuthConfig) } diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index b6b4c1c7..8a8da08f 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -7,6 +7,7 @@ import ( "github.com/go-chi/chi" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/provisioner" ) // CreateProvisionerRequest represents the body for a CreateProvisioner request. @@ -14,7 +15,7 @@ type CreateProvisionerRequest struct { Type string `json:"type"` Name string `json:"name"` Claims *mgmt.Claims `json:"claims"` - Details interface{} `json:"details"` + Details []byte `json:"details"` X509Template string `json:"x509Template"` X509TemplateData []byte `json:"x509TemplateData"` SSHTemplate string `json:"sshTemplate"` @@ -22,31 +23,39 @@ type CreateProvisionerRequest struct { } // Validate validates a new-provisioner request body. -func (car *CreateProvisionerRequest) Validate() error { +func (cpr *CreateProvisionerRequest) Validate(c *provisioner.Collection) error { + if _, ok := c.LoadByName(cpr.Name); ok { + return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", cpr.Name) + } return nil } // UpdateProvisionerRequest represents the body for a UpdateProvisioner request. type UpdateProvisionerRequest struct { + Type string `json:"type"` + Name string `json:"name"` Claims *mgmt.Claims `json:"claims"` - Details interface{} `json:"details"` + Details []byte `json:"details"` X509Template string `json:"x509Template"` X509TemplateData []byte `json:"x509TemplateData"` SSHTemplate string `json:"sshTemplate"` SSHTemplateData []byte `json:"sshTemplateData"` } -// Validate validates a new-provisioner request body. -func (uar *UpdateProvisionerRequest) Validate() error { +// Validate validates a update-provisioner request body. +func (upr *UpdateProvisionerRequest) Validate(c *provisioner.Collection) error { + if _, ok := c.LoadByName(upr.Name); ok { + return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", upr.Name) + } return nil } // GetProvisioner returns the requested provisioner, or an error. func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - id := chi.URLParam(r, "id") + name := chi.URLParam(r, "name") - prov, err := h.db.GetProvisioner(ctx, id) + prov, err := h.db.GetProvisionerByName(ctx, name) if err != nil { api.WriteError(w, err) return @@ -63,7 +72,6 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) return } - fmt.Printf("provs = %+v\n", provs) api.JSON(w, provs) } @@ -76,15 +84,24 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) return } - if err := body.Validate(); err != nil { + if err := body.Validate(h.auth.GetProvisionerCollection()); err != nil { api.WriteError(w, err) + return + } + + details, err := mgmt.UnmarshalProvisionerDetails(body.Details) + if err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error unmarshaling provisioner details")) + return } + claims := mgmt.NewDefaultClaims() + prov := &mgmt.Provisioner{ Type: body.Type, Name: body.Name, - Claims: body.Claims, - Details: body.Details, + Claims: claims, + Details: details, X509Template: body.X509Template, X509TemplateData: body.X509TemplateData, SSHTemplate: body.SSHTemplate, @@ -95,6 +112,58 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { return } api.JSONStatus(w, prov, http.StatusCreated) + + if err := h.auth.ReloadAuthConfig(); err != nil { + fmt.Printf("err = %+v\n", err) + } +} + +// DeleteProvisioner deletes a provisioner. +func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { + name := chi.URLParam(r, "name") + + c := h.auth.GetAdminCollection() + if c.Count() == c.CountByProvisioner(name) { + api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, + "cannot remove provisioner %s because no admins will remain", name)) + return + } + + ctx := r.Context() + prov, err := h.db.GetProvisionerByName(ctx, name) + if err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving provisioner %s", name)) + return + } + fmt.Printf("prov = %+v\n", prov) + prov.Status = mgmt.StatusDeleted + if err := h.db.UpdateProvisioner(ctx, name, prov); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error updating provisioner %s", name)) + return + } + + // Delete all admins associated with the provisioner. + admins, ok := c.LoadByProvisioner(name) + if ok { + for _, adm := range admins { + if err := h.db.UpdateAdmin(ctx, &mgmt.Admin{ + ID: adm.ID, + ProvisionerID: adm.ProvisionerID, + Subject: adm.Subject, + Type: mgmt.AdminType(adm.Type), + Status: mgmt.StatusDeleted, + }); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, name)) + return + } + } + } + + api.JSON(w, &DeleteResponse{Status: "ok"}) + + if err := h.auth.ReloadAuthConfig(); err != nil { + fmt.Printf("err = %+v\n", err) + } } // UpdateProvisioner updates an existing prov. diff --git a/authority/mgmt/authConfig.go b/authority/mgmt/authConfig.go index 734bca50..14448af4 100644 --- a/authority/mgmt/authConfig.go +++ b/authority/mgmt/authConfig.go @@ -1,6 +1,7 @@ package mgmt import ( + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" ) @@ -9,7 +10,7 @@ import ( type AuthConfig struct { //*cas.Options `json:"cas"` ID string `json:"id"` - ASN1DN *config.ASN1DN `json:"template,omitempty"` + ASN1DN *config.ASN1DN `json:"asn1dn,omitempty"` Provisioners []*Provisioner `json:"-"` Admins []*Admin `json:"-"` Claims *Claims `json:"claims,omitempty"` @@ -46,9 +47,18 @@ func (ac *AuthConfig) ToCertificates() (*config.AuthConfig, error) { } provs = append(provs, authProv) } + var admins []*admin.Admin + for _, adm := range ac.Admins { + authAdmin, err := adm.ToCertificates() + if err != nil { + return nil, err + } + admins = append(admins, authAdmin) + } return &config.AuthConfig{ AuthorityID: ac.ID, Provisioners: provs, + Admins: admins, Template: ac.ASN1DN, Claims: claims, DisableIssuedAtCheck: false, diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index b3ece47f..d569ad6f 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -2,7 +2,6 @@ package mgmt import ( "context" - "fmt" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" @@ -16,26 +15,15 @@ const ( ) // StatusType is the type for status. -type StatusType int +type StatusType string -const ( +var ( // StatusActive active - StatusActive StatusType = iota + StatusActive = StatusType("active") // StatusDeleted deleted - StatusDeleted + StatusDeleted = StatusType("deleted") ) -func (st StatusType) String() string { - switch st { - case StatusActive: - return "active" - case StatusDeleted: - return "deleted" - default: - return fmt.Sprintf("status %d not found", st) - } -} - // Claims encapsulates all x509 and ssh claims applied to the authority // configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc. type Claims struct { @@ -123,14 +111,18 @@ func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*A return nil, WrapErrorISE(err, "error creating first provisioner") } - admin, err := CreateAdmin(ctx, db, "Change Me", prov.ID, true) - if err != nil { + adm := &Admin{ + ProvisionerID: prov.ID, + Subject: "Change Me", + Type: AdminTypeSuper, + } + if err := db.CreateAdmin(ctx, adm); err != nil { // TODO should we try to clean up? - return nil, WrapErrorISE(err, "error creating first provisioner") + return nil, WrapErrorISE(err, "error creating first admin") } ac.Provisioners = []*Provisioner{prov} - ac.Admins = []*Admin{admin} + ac.Admins = []*Admin{adm} return ac, nil } diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go index 9cfab3e5..8546c4b4 100644 --- a/authority/mgmt/db.go +++ b/authority/mgmt/db.go @@ -14,8 +14,9 @@ var ErrNotFound = errors.New("not found") type DB interface { CreateProvisioner(ctx context.Context, prov *Provisioner) error GetProvisioner(ctx context.Context, id string) (*Provisioner, error) + GetProvisionerByName(ctx context.Context, name string) (*Provisioner, error) GetProvisioners(ctx context.Context) ([]*Provisioner, error) - UpdateProvisioner(ctx context.Context, prov *Provisioner) error + UpdateProvisioner(ctx context.Context, name string, prov *Provisioner) error CreateAdmin(ctx context.Context, admin *Admin) error GetAdmin(ctx context.Context, id string) (*Admin, error) @@ -30,10 +31,11 @@ type DB interface { // MockDB is an implementation of the DB interface that should only be used as // a mock in tests. type MockDB struct { - MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error - MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) - MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) - MockUpdateProvisioner func(ctx context.Context, prov *Provisioner) error + MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error + MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) + MockGetProvisionerByName func(ctx context.Context, name string) (*Provisioner, error) + MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) + MockUpdateProvisioner func(ctx context.Context, name string, prov *Provisioner) error MockCreateAdmin func(ctx context.Context, adm *Admin) error MockGetAdmin func(ctx context.Context, id string) (*Admin, error) @@ -68,6 +70,16 @@ func (m *MockDB) GetProvisioner(ctx context.Context, id string) (*Provisioner, e return m.MockRet1.(*Provisioner), m.MockError } +// GetProvisionerByName mock. +func (m *MockDB) GetProvisionerByName(ctx context.Context, id string) (*Provisioner, error) { + if m.MockGetProvisionerByName != nil { + return m.MockGetProvisionerByName(ctx, id) + } else if m.MockError != nil { + return nil, m.MockError + } + return m.MockRet1.(*Provisioner), m.MockError +} + // GetProvisioners mock func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { if m.MockGetProvisioners != nil { @@ -79,9 +91,9 @@ func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { } // UpdateProvisioner mock -func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *Provisioner) error { +func (m *MockDB) UpdateProvisioner(ctx context.Context, name string, prov *Provisioner) error { if m.MockUpdateProvisioner != nil { - return m.MockUpdateProvisioner(ctx, prov) + return m.MockUpdateProvisioner(ctx, name, prov) } return m.MockError } diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 70cb12d1..4e465489 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -12,13 +12,13 @@ import ( // dbAdmin is the database representation of the Admin type. type dbAdmin struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - ProvisionerID string `json:"provisionerID"` - Name string `json:"name"` - IsSuperAdmin bool `json:"isSuperAdmin"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + ProvisionerID string `json:"provisionerID"` + Subject string `json:"subject"` + Type mgmt.AdminType `json:"type"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbAdmin) clone() *dbAdmin { @@ -52,27 +52,6 @@ func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { return dba, nil } -// GetAdmin retrieves and unmarshals a admin from the database. -func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { - data, err := db.getDBAdminBytes(ctx, id) - if err != nil { - return nil, err - } - adm, err := unmarshalAdmin(data, id) - if err != nil { - return nil, err - } - if adm.Status == mgmt.StatusDeleted { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID) - } - if adm.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "admin %s is not owned by authority %s", adm.ID, db.authorityID) - } - - return adm, nil -} - func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { var dba = new(dbAdmin) if err := json.Unmarshal(data, dba); err != nil { @@ -90,8 +69,9 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { ID: dba.ID, AuthorityID: dba.AuthorityID, ProvisionerID: dba.ProvisionerID, - Name: dba.Name, - IsSuperAdmin: dba.IsSuperAdmin, + Subject: dba.Subject, + Type: dba.Type, + Status: mgmt.StatusActive, } if !dba.DeletedAt.IsZero() { adm.Status = mgmt.StatusDeleted @@ -99,6 +79,33 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { return adm, nil } +// GetAdmin retrieves and unmarshals a admin from the database. +func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { + data, err := db.getDBAdminBytes(ctx, id) + if err != nil { + return nil, err + } + adm, err := unmarshalAdmin(data, id) + if err != nil { + return nil, err + } + if adm.Status == mgmt.StatusDeleted { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID) + } + if adm.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", adm.ID, db.authorityID) + } + prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) + if err != nil { + return nil, err + } + adm.ProvisionerName = prov.Name + adm.ProvisionerType = prov.Type + + return adm, nil +} + // GetAdmins retrieves and unmarshals all active (not deleted) admins // from the database. // TODO should we be paginating? @@ -107,7 +114,10 @@ func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { if err != nil { return nil, errors.Wrap(err, "error loading admins") } - var admins []*mgmt.Admin + var ( + provCache = map[string]*mgmt.Provisioner{} + admins []*mgmt.Admin + ) for _, entry := range dbEntries { adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) if err != nil { @@ -119,6 +129,19 @@ func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { if adm.AuthorityID != db.authorityID { continue } + var ( + prov *mgmt.Provisioner + ok bool + ) + if prov, ok = provCache[adm.ProvisionerID]; !ok { + prov, err = db.GetProvisioner(ctx, adm.ProvisionerID) + if err != nil { + return nil, err + } + provCache[adm.ProvisionerID] = prov + } + adm.ProvisionerName = prov.Name + adm.ProvisionerType = prov.Type admins = append(admins, adm) } return admins, nil @@ -129,16 +152,34 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { var err error adm.ID, err = randID() if err != nil { - return errors.Wrap(err, "error generating random id for admin") + return mgmt.WrapErrorISE(err, "error generating random id for admin") } adm.AuthorityID = db.authorityID + // If provisionerID is set, then use it, otherwise load the provisioner + // to get the name. + if adm.ProvisionerID == "" { + prov, err := db.GetProvisionerByName(ctx, adm.ProvisionerName) + if err != nil { + return err + } + adm.ProvisionerID = prov.ID + adm.ProvisionerType = prov.Type + } else { + prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) + if err != nil { + return err + } + adm.ProvisionerName = prov.Name + adm.ProvisionerType = prov.Type + } + dba := &dbAdmin{ ID: adm.ID, AuthorityID: db.authorityID, ProvisionerID: adm.ProvisionerID, - Name: adm.Name, - IsSuperAdmin: adm.IsSuperAdmin, + Subject: adm.Subject, + Type: adm.Type, CreatedAt: clock.Now(), } @@ -158,8 +199,7 @@ func (db *DB) UpdateAdmin(ctx context.Context, adm *mgmt.Admin) error { if old.DeletedAt.IsZero() && adm.Status == mgmt.StatusDeleted { nu.DeletedAt = clock.Now() } - nu.ProvisionerID = adm.ProvisionerID - nu.IsSuperAdmin = adm.IsSuperAdmin + nu.Type = adm.Type return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) } diff --git a/authority/mgmt/db/nosql/authConfig.go b/authority/mgmt/db/nosql/authConfig.go index fe189ce3..b9108fdc 100644 --- a/authority/mgmt/db/nosql/authConfig.go +++ b/authority/mgmt/db/nosql/authConfig.go @@ -60,9 +60,14 @@ func (db *DB) GetAuthConfig(ctx context.Context, id string) (*mgmt.AuthConfig, e if err != nil { return nil, err } + admins, err := db.GetAdmins(ctx) + if err != nil { + return nil, err + } return &mgmt.AuthConfig{ ID: dba.ID, + Admins: admins, Provisioners: provs, ASN1DN: dba.ASN1DN, Backdate: dba.Backdate, diff --git a/authority/mgmt/db/nosql/nosql.go b/authority/mgmt/db/nosql/nosql.go index d71b2804..d97b7700 100644 --- a/authority/mgmt/db/nosql/nosql.go +++ b/authority/mgmt/db/nosql/nosql.go @@ -3,6 +3,7 @@ package nosql import ( "context" "encoding/json" + "fmt" "time" "github.com/pkg/errors" @@ -11,9 +12,10 @@ import ( ) var ( - authorityAdminsTable = []byte("authority_admins") - authorityConfigsTable = []byte("authority_configs") - authorityProvisionersTable = []byte("authority_provisioners") + authorityAdminsTable = []byte("authority_admins") + authorityConfigsTable = []byte("authority_configs") + authorityProvisionersTable = []byte("authority_provisioners") + authorityProvisionersNameIDIndexTable = []byte("authority_provisioners_name_id_index") ) // DB is a struct that implements the AcmeDB interface. @@ -24,7 +26,7 @@ type DB struct { // New configures and returns a new Authority DB backend implemented using a nosql DB. func New(db nosqlDB.DB, authorityID string) (*DB, error) { - tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable} + tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable, authorityProvisionersNameIDIndexTable} for _, b := range tables { if err := db.CreateTable(b); err != nil { return nil, errors.Wrapf(err, "error creating table %s", @@ -58,6 +60,7 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old) } } + fmt.Printf("oldB = %+v\n", oldB) _, swapped, err := db.db.CmpAndSwap(table, []byte(id), oldB, newB) switch { @@ -73,7 +76,7 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface var idLen = 32 func randID() (val string, err error) { - val, err = randutil.Alphanumeric(idLen) + val, err = randutil.UUIDv4() if err != nil { return "", errors.Wrap(err, "error generating random alphanumeric ID") } diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index 6d9f74ab..d44c3be1 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -9,13 +9,15 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" ) // dbProvisioner is the database representation of a Provisioner type. type dbProvisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Type string `json:"type"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type string `json:"type"` + // Name is the key Name string `json:"name"` Claims *mgmt.Claims `json:"claims"` Details []byte `json:"details"` @@ -27,11 +29,30 @@ type dbProvisioner struct { DeletedAt time.Time `json:"deletedAt"` } +type provisionerNameID struct { + Name string `json:"name"` + ID string `json:"id"` +} + func (dbp *dbProvisioner) clone() *dbProvisioner { u := *dbp return &u } +func (db *DB) getProvisionerIDByName(ctx context.Context, name string) (string, error) { + data, err := db.db.Get(authorityProvisionersNameIDIndexTable, []byte(name)) + if nosql.IsErrNotFound(err) { + return "", mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name) + } else if err != nil { + return "", mgmt.WrapErrorISE(err, "error loading provisioner %s", name) + } + ni := new(provisionerNameID) + if err := json.Unmarshal(data, ni); err != nil { + return "", mgmt.WrapErrorISE(err, "error unmarshaling provisionerNameID for provisioner %s", name) + } + return ni.ID, nil +} + func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { data, err := db.db.Get(authorityProvisionersTable, []byte(id)) if nosql.IsErrNotFound(err) { @@ -51,6 +72,9 @@ func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, if err != nil { return nil, err } + if !dbp.DeletedAt.IsZero() { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", id) + } if dbp.AuthorityID != db.authorityID { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, "provisioner %s is not owned by authority %s", dbp.ID, db.authorityID) @@ -58,6 +82,25 @@ func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, return dbp, nil } +func (db *DB) getDBProvisionerByName(ctx context.Context, name string) (*dbProvisioner, error) { + id, err := db.getProvisionerIDByName(ctx, name) + if err != nil { + return nil, err + } + dbp, err := db.getDBProvisioner(ctx, id) + if err != nil { + return nil, err + } + if !dbp.DeletedAt.IsZero() { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name) + } + if dbp.AuthorityID != db.authorityID { + return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", name, db.authorityID) + } + return dbp, nil +} + // GetProvisioner retrieves and unmarshals a provisioner from the database. func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, error) { data, err := db.getDBProvisionerBytes(ctx, id) @@ -79,29 +122,30 @@ func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, return prov, nil } -func unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) { +// GetProvisionerByName retrieves a provisioner from the database by name. +func (db *DB) GetProvisionerByName(ctx context.Context, name string) (*mgmt.Provisioner, error) { + p, err := db.getProvisionerIDByName(ctx, name) + if err != nil { + return nil, err + } + return db.GetProvisioner(ctx, id) +} + +func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) { var dbp = new(dbProvisioner) if err := json.Unmarshal(data, dbp); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", id) + return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", name) } return dbp, nil } -type detailsType struct { - Type mgmt.ProvisionerType -} - -func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { - dbp, err := unmarshalDBProvisioner(data, id) +func unmarshalProvisioner(data []byte, name string) (*mgmt.Provisioner, error) { + dbp, err := unmarshalDBProvisioner(data, name) if err != nil { return nil, err } - dt := new(detailsType) - if err := json.Unmarshal(dbp.Details, dt); err != nil { - return nil, mgmt.WrapErrorISE(err, "error unmarshaling details to detailsType for provisioner %s", id) - } - details, err := unmarshalDetails(dt.Type, dbp.Details) + details, err := mgmt.UnmarshalProvisionerDetails(dbp.Details) if err != nil { return nil, err } @@ -113,6 +157,7 @@ func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { Name: dbp.Name, Claims: dbp.Claims, Details: details, + Status: mgmt.StatusActive, X509Template: dbp.X509Template, X509TemplateData: dbp.X509TemplateData, SSHTemplate: dbp.SSHTemplate, @@ -129,7 +174,7 @@ func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) { func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) { dbEntries, err := db.db.List(authorityProvisionersTable) if err != nil { - return nil, errors.Wrap(err, "error loading provisioners") + return nil, mgmt.WrapErrorISE(err, "error loading provisioners") } var provs []*mgmt.Provisioner for _, entry := range dbEntries { @@ -158,76 +203,169 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err details, err := json.Marshal(prov.Details) if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner") + return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) } dbp := &dbProvisioner{ - ID: prov.ID, - AuthorityID: db.authorityID, - Type: prov.Type, - Name: prov.Name, - Claims: prov.Claims, - Details: details, - X509Template: prov.X509Template, - SSHTemplate: prov.SSHTemplate, - CreatedAt: clock.Now(), - } - - return db.save(ctx, dbp.ID, dbp, nil, "provisioner", authorityProvisionersTable) + ID: prov.ID, + AuthorityID: db.authorityID, + Type: prov.Type, + Name: prov.Name, + Claims: prov.Claims, + Details: details, + X509Template: prov.X509Template, + X509TemplateData: prov.X509TemplateData, + SSHTemplate: prov.SSHTemplate, + SSHTemplateData: prov.SSHTemplateData, + CreatedAt: clock.Now(), + } + dbpBytes, err := json.Marshal(dbp) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling dbProvisioner %s", prov.Name) + } + pni := &provisionerNameID{ + Name: prov.Name, + ID: prov.ID, + } + pniBytes, err := json.Marshal(pni) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling provisionerNameIndex %s", prov.Name) + } + + if err := db.db.Update(&database.Tx{ + Operations: []*database.TxEntry{ + { + Bucket: authorityProvisionersTable, + Key: []byte(dbp.ID), + Cmd: database.CmpAndSwap, + Value: dbpBytes, + CmpValue: nil, + }, + { + Bucket: authorityProvisionersNameIDIndexTable, + Key: []byte(dbp.Name), + Cmd: database.CmpAndSwap, + Value: pniBytes, + CmpValue: nil, + }, + }, + }); err != nil { + return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name) + } + + return nil } // UpdateProvisioner saves an updated provisioner to the database. -func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { - old, err := db.getDBProvisioner(ctx, prov.ID) +func (db *DB) UpdateProvisioner(ctx context.Context, name string, prov *mgmt.Provisioner) error { + id, err := db.getProvisionerIDByName(ctx, name) + if err != nil { + return err + } + prov.ID = id + oldBytes, err := db.getDBProvisionerBytes(ctx, id) if err != nil { return err } + fmt.Printf("oldBytes = %+v\n", oldBytes) + old, err := unmarshalDBProvisioner(oldBytes, id) + if err != nil { + return err + } + fmt.Printf("old = %+v\n", old) nu := old.clone() - // If the provisioner was active but is now deleted ... - if old.DeletedAt.IsZero() && prov.Status == mgmt.StatusDeleted { - nu.DeletedAt = clock.Now() - } + nu.Type = prov.Type + nu.Name = prov.Name nu.Claims = prov.Claims - nu.X509Template = prov.X509Template - nu.SSHTemplate = prov.SSHTemplate - nu.Details, err = json.Marshal(prov.Details) if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner") + return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", name) } + nu.X509Template = prov.X509Template + nu.X509TemplateData = prov.X509TemplateData + nu.SSHTemplateData = prov.SSHTemplateData - return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) -} + var txs = []*database.TxEntry{} + // If the provisioner was active but is now deleted ... + if old.DeletedAt.IsZero() && prov.Status == mgmt.StatusDeleted { + nu.DeletedAt = clock.Now() + txs = append(txs, &database.TxEntry{ + Bucket: authorityProvisionersNameIDIndexTable, + Key: []byte(name), + Cmd: database.Delete, + }) + } -func unmarshalDetails(typ mgmt.ProvisionerType, data []byte) (mgmt.ProvisionerDetails, error) { - var v mgmt.ProvisionerDetails - switch typ { - case mgmt.ProvisionerTypeJWK: - v = new(mgmt.ProvisionerDetailsJWK) - case mgmt.ProvisionerTypeOIDC: - v = new(mgmt.ProvisionerDetailsOIDC) - case mgmt.ProvisionerTypeGCP: - v = new(mgmt.ProvisionerDetailsGCP) - case mgmt.ProvisionerTypeAWS: - v = new(mgmt.ProvisionerDetailsAWS) - case mgmt.ProvisionerTypeAZURE: - v = new(mgmt.ProvisionerDetailsAzure) - case mgmt.ProvisionerTypeACME: - v = new(mgmt.ProvisionerDetailsACME) - case mgmt.ProvisionerTypeX5C: - v = new(mgmt.ProvisionerDetailsX5C) - case mgmt.ProvisionerTypeK8SSA: - v = new(mgmt.ProvisionerDetailsK8SSA) - case mgmt.ProvisionerTypeSSHPOP: - v = new(mgmt.ProvisionerDetailsSSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", typ) - } - - if err := json.Unmarshal(data, v); err != nil { - return nil, err + if prov.Name != name { + // If the new name does not match the old name then: + // 1) check that the new name is not already taken + // 2) delete the old name-id index resource + // 3) create a new name-id index resource + // 4) update the provisioner resource + nuBytes, err := json.Marshal(nu) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling dbProvisioner %s", prov.Name) + } + pni := &provisionerNameID{ + Name: prov.Name, + ID: prov.ID, + } + pniBytes, err := json.Marshal(pni) + if err != nil { + return mgmt.WrapErrorISE(err, "error marshaling provisionerNameID for provisioner %s", prov.Name) + } + + _, err = db.db.Get(authorityProvisionersNameIDIndexTable, []byte(name)) + if err == nil { + return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", prov.Name) + } else if !nosql.IsErrNotFound(err) { + return mgmt.WrapErrorISE(err, "error loading provisionerNameID %s", prov.Name) + } + err = db.db.Update(&database.Tx{ + Operations: []*database.TxEntry{ + { + Bucket: authorityProvisionersNameIDIndexTable, + Key: []byte(name), + Cmd: database.Delete, + }, + { + Bucket: authorityProvisionersNameIDIndexTable, + Key: []byte(prov.Name), + Cmd: database.CmpAndSwap, + Value: pniBytes, + CmpValue: nil, + }, + { + Bucket: authorityProvisionersTable, + Key: []byte(nu.ID), + Cmd: database.CmpAndSwap, + Value: nuBytes, + CmpValue: oldBytes, + }, + }, + }) + } else { + err = db.db.Update(&database.Tx{ + Operations: []*database.TxEntry{ + { + Bucket: authorityProvisionersNameIDIndexTable, + Key: []byte(name), + Cmd: database.Delete, + }, + { + Bucket: authorityProvisionersTable, + Key: []byte(nu.ID), + Cmd: database.CmpAndSwap, + Value: nuBytes, + CmpValue: oldBytes, + }, + }, + }) + } + if err != nil { + return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name) } - return v, nil + return nil } diff --git a/authority/mgmt/errors.go b/authority/mgmt/errors.go index f0f90400..63c0652e 100644 --- a/authority/mgmt/errors.go +++ b/authority/mgmt/errors.go @@ -88,12 +88,11 @@ var ( // Error represents an ACME type Error struct { - Type string `json:"type"` - Detail string `json:"detail"` - Subproblems []interface{} `json:"subproblems,omitempty"` - Identifier interface{} `json:"identifier,omitempty"` - Err error `json:"-"` - Status int `json:"-"` + Type string `json:"type"` + Detail string `json:"detail"` + Message string `json:"message"` + Err error `json:"-"` + Status int `json:"-"` } // IsType returns true if the error type matches the input type. @@ -160,7 +159,7 @@ func (e *Error) StatusCode() int { // Error allows AError to implement the error interface. func (e *Error) Error() string { - return e.Detail + return e.Err.Error() } // Cause returns the internal error and implements the Causer interface. @@ -182,9 +181,10 @@ func (e *Error) ToLog() (interface{}, error) { // WriteError writes to w a JSON representation of the given error. func WriteError(w http.ResponseWriter, err *Error) { - w.Header().Set("Content-Type", "application/problem+json") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(err.StatusCode()) + err.Message = err.Err.Error() // Write errors in the response writer if rl, ok := w.(logging.ResponseLogger); ok { rl.WithFields(map[string]interface{}{ @@ -199,6 +199,7 @@ func WriteError(w http.ResponseWriter, err *Error) { } } + fmt.Printf("err = %+v\n", err) if err := json.NewEncoder(w).Encode(err); err != nil { log.Println(err) } diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 961907f8..fb39f6bc 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -59,8 +59,8 @@ func WithPassword(pass string) func(*ProvisionerCtx) { // Provisioner type. type Provisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` + ID string `json:"-"` + AuthorityID string `json:"-"` Type string `json:"type"` Name string `json:"name"` Claims *Claims `json:"claims"` @@ -87,8 +87,7 @@ func (p *Provisioner) GetOptions() *provisioner.Options { func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...ProvisionerOption) (*Provisioner, error) { pc := NewProvisionerCtx(opts...) - - details, err := createJWKDetails(pc) + details, err := NewProvisionerDetails(ProvisionerType(typ), pc) if err != nil { return nil, err } @@ -180,6 +179,27 @@ func (*ProvisionerDetailsK8SSA) isProvisionerDetails() {} func (*ProvisionerDetailsSSHPOP) isProvisionerDetails() {} +func NewProvisionerDetails(typ ProvisionerType, pc *ProvisionerCtx) (ProvisionerDetails, error) { + switch typ { + case ProvisionerTypeJWK: + return createJWKDetails(pc) + /* + case ProvisionerTypeOIDC: + return createOIDCDetails(pc) + case ProvisionerTypeACME: + return createACMEDetails(pc) + case ProvisionerTypeK8SSA: + return createK8SSADetails(pc) + case ProvisionerTypeSSHPOP: + return createSSHPOPDetails(pc) + case ProvisionerTypeX5C: + return createSSHPOPDetails(pc) + */ + default: + return nil, NewErrorISE("unsupported provisioner type %s", typ) + } +} + func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { var err error @@ -231,6 +251,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { return nil, err } return &provisioner.JWK{ + ID: p.ID, Type: p.Type, Name: p.Name, Key: jwk, @@ -386,3 +407,43 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) { EnableSSHCA: &c.SSH.Enabled, }, nil } + +type detailsType struct { + Type ProvisionerType +} + +func UnmarshalProvisionerDetails(data []byte) (ProvisionerDetails, error) { + dt := new(detailsType) + if err := json.Unmarshal(data, dt); err != nil { + return nil, WrapErrorISE(err, "error unmarshaling provisioner details") + } + + var v ProvisionerDetails + switch dt.Type { + case ProvisionerTypeJWK: + v = new(ProvisionerDetailsJWK) + case ProvisionerTypeOIDC: + v = new(ProvisionerDetailsOIDC) + case ProvisionerTypeGCP: + v = new(ProvisionerDetailsGCP) + case ProvisionerTypeAWS: + v = new(ProvisionerDetailsAWS) + case ProvisionerTypeAZURE: + v = new(ProvisionerDetailsAzure) + case ProvisionerTypeACME: + v = new(ProvisionerDetailsACME) + case ProvisionerTypeX5C: + v = new(ProvisionerDetailsX5C) + case ProvisionerTypeK8SSA: + v = new(ProvisionerDetailsK8SSA) + case ProvisionerTypeSSHPOP: + v = new(ProvisionerDetailsSSHPOP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", dt.Type) + } + + if err := json.Unmarshal(data, v); err != nil { + return nil, err + } + return v, nil +} diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index 13b7be4d..b6ff8b9e 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -45,6 +45,7 @@ type loadByTokenPayload struct { type Collection struct { byID *sync.Map byKey *sync.Map + byName *sync.Map sorted provisionerSlice audiences Audiences } @@ -55,6 +56,7 @@ func NewCollection(audiences Audiences) *Collection { return &Collection{ byID: new(sync.Map), byKey: new(sync.Map), + byName: new(sync.Map), audiences: audiences, } } @@ -64,6 +66,11 @@ func (c *Collection) Load(id string) (Interface, bool) { return loadProvisioner(c.byID, id) } +// LoadByName a provisioner by name. +func (c *Collection) LoadByName(name string) (Interface, bool) { + return loadProvisioner(c.byName, name) +} + // LoadByToken parses the token claims and loads the provisioner associated. func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) (Interface, bool) { var audiences []string @@ -173,6 +180,11 @@ func (c *Collection) Store(p Interface) error { if _, loaded := c.byID.LoadOrStore(p.GetID(), p); loaded { return errors.New("cannot add multiple provisioners with the same id") } + // Store provisioner always by name. + if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded { + c.byID.Delete(p.GetID()) + return errors.New("cannot add multiple provisioners with the same id") + } // Store provisioner in byKey if EncryptedKey is defined. if kid, _, ok := p.GetEncryptedKey(); ok { diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index d6a97e2b..a2d3e0b1 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -28,6 +28,7 @@ type stepPayload struct { // signature requests. type JWK struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` Key *jose.JSONWebKey `json:"key"` @@ -41,6 +42,9 @@ type JWK struct { // GetID returns the provisioner unique identifier. The name and credential id // should uniquely identify any JWK provisioner. func (p *JWK) GetID() string { + if p.ID != "" { + return p.ID + } return p.Name + ":" + p.Key.KeyID } diff --git a/ca/ca.go b/ca/ca.go index ec10f74c..ecbc55e2 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -172,10 +172,9 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) // MGMT Router - mgmtDB := auth.GetMgmtDatabase() if mgmtDB != nil { - mgmtHandler := mgmtAPI.NewHandler(mgmtDB) + mgmtHandler := mgmtAPI.NewHandler(mgmtDB, auth) mux.Route("/mgmt", func(r chi.Router) { mgmtHandler.Route(r) }) diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index 47316ad6..96c33f03 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -3,6 +3,7 @@ package ca import ( "bytes" "encoding/json" + "io" "net/http" "net/url" "path" @@ -78,7 +79,7 @@ retry: retried = true goto retry } - return nil, readError(resp.Body) + return nil, readMgmtError(resp.Body) } var adm = new(mgmt.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -105,7 +106,7 @@ retry: retried = true goto retry } - return nil, readError(resp.Body) + return nil, readMgmtError(resp.Body) } var adm = new(mgmt.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -132,7 +133,7 @@ retry: retried = true goto retry } - return readError(resp.Body) + return readMgmtError(resp.Body) } return nil } @@ -145,7 +146,7 @@ func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*m return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) - req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) + req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) } @@ -159,7 +160,7 @@ retry: retried = true goto retry } - return nil, readError(resp.Body) + return nil, readMgmtError(resp.Body) } var adm = new(mgmt.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -182,7 +183,7 @@ retry: retried = true goto retry } - return nil, readError(resp.Body) + return nil, readMgmtError(resp.Body) } var admins = new([]*mgmt.Admin) if err := readJSON(resp.Body, admins); err != nil { @@ -191,6 +192,29 @@ retry: return *admins, nil } +// GetProvisioner performs the GET /mgmt/provisioner/{id} request to the CA. +func (c *MgmtClient) GetProvisioner(id string) (*mgmt.Provisioner, error) { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", id)}) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readMgmtError(resp.Body) + } + var prov = new(mgmt.Provisioner) + if err := readJSON(resp.Body, prov); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return prov, nil +} + // GetProvisioners performs the GET /mgmt/provisioners request to the CA. func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) { var retried bool @@ -205,7 +229,7 @@ retry: retried = true goto retry } - return nil, readError(resp.Body) + return nil, readMgmtError(resp.Body) } var provs = new([]*mgmt.Provisioner) if err := readJSON(resp.Body, provs); err != nil { @@ -213,3 +237,116 @@ retry: } return *provs, nil } + +// RemoveProvisioner performs the DELETE /mgmt/provisioner/{name} request to the CA. +func (c *MgmtClient) RemoveProvisioner(name string) error { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", name)}) + req, err := http.NewRequest("DELETE", u.String(), nil) + if err != nil { + return errors.Wrapf(err, "create DELETE %s request failed", u) + } +retry: + resp, err := c.client.Do(req) + if err != nil { + return errors.Wrapf(err, "client DELETE %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return readMgmtError(resp.Body) + } + return nil +} + +// CreateProvisioner performs the POST /mgmt/provisioner request to the CA. +func (c *MgmtClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { + var retried bool + body, err := json.Marshal(req) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioner"}) +retry: + resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "client POST %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readMgmtError(resp.Body) + } + var prov = new(mgmt.Provisioner) + if err := readJSON(resp.Body, prov); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return prov, nil +} + +// UpdateProvisioner performs the PUT /mgmt/provisioner/{id} request to the CA. +func (c *MgmtClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { + var retried bool + body, err := json.Marshal(upr) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", id)}) + req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } +retry: + resp, err := c.client.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "client PUT %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readMgmtError(resp.Body) + } + var prov = new(mgmt.Provisioner) + if err := readJSON(resp.Body, prov); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return prov, nil +} + +// GetAuthConfig performs the GET /mgmt/authconfig/{id} request to the CA. +func (c *MgmtClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { + var retried bool + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/authconfig", id)}) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readMgmtError(resp.Body) + } + var ac = new(mgmt.AuthConfig) + if err := readJSON(resp.Body, ac); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return ac, nil +} + +func readMgmtError(r io.ReadCloser) error { + defer r.Close() + mgmtErr := new(mgmt.Error) + if err := json.NewDecoder(r).Decode(mgmtErr); err != nil { + return err + } + return errors.New(mgmtErr.Message) +} From 4f3e5ef64df64948795c3be50ed92ee3ab853b8e Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 18 May 2021 16:50:54 -0700 Subject: [PATCH 008/291] wip --- api/api.go | 5 +- authority/admin/admin.go | 26 ++-- authority/admin/collection.go | 81 ++++++----- authority/authority.go | 4 +- authority/mgmt/admin.go | 42 +----- authority/mgmt/api/admin.go | 73 ++++++---- authority/mgmt/api/provisioner.go | 29 ++-- authority/mgmt/authConfig.go | 5 +- authority/mgmt/config.go | 10 -- authority/mgmt/db.go | 26 +--- authority/mgmt/db/nosql/admin.go | 68 ++-------- authority/mgmt/db/nosql/authConfig.go | 3 +- authority/mgmt/db/nosql/nosql.go | 2 - authority/mgmt/db/nosql/provisioner.go | 181 ++----------------------- authority/mgmt/provisioner.go | 5 +- authority/provisioner/collection.go | 2 +- authority/status/status.go | 11 ++ ca/mgmtClient.go | 103 ++++++++++---- 18 files changed, 276 insertions(+), 400 deletions(-) create mode 100644 authority/status/status.go diff --git a/api/api.go b/api/api.go index d6cf9ab7..20ef7e14 100644 --- a/api/api.go +++ b/api/api.go @@ -316,7 +316,7 @@ func certChainToPEM(certChain []*x509.Certificate) []Certificate { // Provisioners returns the list of provisioners configured in the authority. func (h *caHandler) Provisioners(w http.ResponseWriter, r *http.Request) { - cursor, limit, err := parseCursor(r) + cursor, limit, err := ParseCursor(r) if err != nil { WriteError(w, errs.BadRequestErr(err)) return @@ -427,7 +427,8 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { } } -func parseCursor(r *http.Request) (cursor string, limit int, err error) { +// ParseCursor parses the cursor and limit from the request query params. +func ParseCursor(r *http.Request) (cursor string, limit int, err error) { q := r.URL.Query() cursor = q.Get("cursor") if v := q.Get("limit"); len(v) > 0 { diff --git a/authority/admin/admin.go b/authority/admin/admin.go index 579538dc..f0058777 100644 --- a/authority/admin/admin.go +++ b/authority/admin/admin.go @@ -1,15 +1,25 @@ package admin -// Type specifies the type of administrator privileges the admin has. +import "github.com/smallstep/certificates/authority/status" + +// Type specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR type Type string +var ( + // TypeSuper superadmin + TypeSuper = Type("SUPER_ADMIN") + // TypeRegular regular + TypeRegular = Type("REGULAR") +) + // Admin type. type Admin struct { - ID string `json:"id"` - AuthorityID string `json:"-"` - Subject string `json:"subject"` - ProvisionerName string `json:"provisionerName"` - ProvisionerType string `json:"provisionerType"` - ProvisionerID string `json:"provisionerID"` - Type Type `json:"type"` + ID string `json:"id"` + AuthorityID string `json:"-"` + Subject string `json:"subject"` + ProvisionerName string `json:"provisionerName"` + ProvisionerType string `json:"provisionerType"` + ProvisionerID string `json:"provisionerID"` + Type Type `json:"type"` + Status status.Type `json:"status"` } diff --git a/authority/admin/collection.go b/authority/admin/collection.go index 87dd63ce..9e8a2926 100644 --- a/authority/admin/collection.go +++ b/authority/admin/collection.go @@ -2,56 +2,54 @@ package admin import ( "crypto/sha1" + "encoding/binary" + "encoding/hex" + "fmt" + "sort" + "strings" "sync" "github.com/pkg/errors" - "go.step.sm/crypto/jose" + "github.com/smallstep/certificates/authority/provisioner" ) -// DefaultProvisionersLimit is the default limit for listing provisioners. -const DefaultProvisionersLimit = 20 +// DefaultAdminLimit is the default limit for listing provisioners. +const DefaultAdminLimit = 20 -// DefaultProvisionersMax is the maximum limit for listing provisioners. -const DefaultProvisionersMax = 100 +// DefaultAdminMax is the maximum limit for listing provisioners. +const DefaultAdminMax = 100 -/* -type uidProvisioner struct { - provisioner Interface - uid string +type uidAdmin struct { + admin *Admin + uid string } -type provisionerSlice []uidProvisioner - -func (p provisionerSlice) Len() int { return len(p) } -func (p provisionerSlice) Less(i, j int) bool { return p[i].uid < p[j].uid } -func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -*/ +type adminSlice []uidAdmin -// loadByTokenPayload is a payload used to extract the id used to load the -// provisioner. -type loadByTokenPayload struct { - jose.Claims - AuthorizedParty string `json:"azp"` // OIDC client id - TenantID string `json:"tid"` // Microsoft Azure tenant id -} +func (p adminSlice) Len() int { return len(p) } +func (p adminSlice) Less(i, j int) bool { return p[i].uid < p[j].uid } +func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Collection is a memory map of admins. type Collection struct { byID *sync.Map bySubProv *sync.Map byProv *sync.Map + sorted adminSlice + provisioners *provisioner.Collection count int countByProvisioner map[string]int } // NewCollection initializes a collection of provisioners. The given list of // audiences are the audiences used by the JWT provisioner. -func NewCollection() *Collection { +func NewCollection(provisioners *provisioner.Collection) *Collection { return &Collection{ byID: new(sync.Map), byProv: new(sync.Map), bySubProv: new(sync.Map), countByProvisioner: map[string]int{}, + provisioners: provisioners, } } @@ -88,12 +86,18 @@ func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) { // Store adds an admin to the collection and enforces the uniqueness of // admin IDs and amdin subject <-> provisioner name combos. func (c *Collection) Store(adm *Admin) error { - provName := adm.ProvisionerName + p, ok := c.provisioners.Load(adm.ProvisionerID) + if !ok { + return fmt.Errorf("provisioner %s not found", adm.ProvisionerID) + } + adm.ProvisionerName = p.GetName() + adm.ProvisionerType = p.GetType().String() // Store admin always in byID. ID must be unique. if _, loaded := c.byID.LoadOrStore(adm.ID, adm); loaded { return errors.New("cannot add multiple admins with the same id") } + provName := adm.ProvisionerName // Store admin alwasy in bySubProv. Subject <-> ProvisionerName must be unique. if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded { c.byID.Delete(adm.ID) @@ -109,6 +113,21 @@ func (c *Collection) Store(adm *Admin) error { } c.count++ + // Store sorted admins. + // Use the first 4 bytes (32bit) of the sum to insert the order + // Using big endian format to get the strings sorted: + // 0x00000000, 0x00000001, 0x00000002, ... + bi := make([]byte, 4) + _sum := sha1.Sum([]byte(adm.ID)) + sum := _sum[:] + binary.BigEndian.PutUint32(bi, uint32(c.sorted.Len())) + sum[0], sum[1], sum[2], sum[3] = bi[0], bi[1], bi[2], bi[3] + c.sorted = append(c.sorted, uidAdmin{ + admin: adm, + uid: hex.EncodeToString(sum), + }) + sort.Sort(c.sorted) + return nil } @@ -125,23 +144,22 @@ func (c *Collection) CountByProvisioner(provName string) int { return 0 } -/* // Find implements pagination on a list of sorted provisioners. -func (c *Collection) Find(cursor string, limit int) (List, string) { +func (c *Collection) Find(cursor string, limit int) ([]*Admin, string) { switch { case limit <= 0: - limit = DefaultProvisionersLimit - case limit > DefaultProvisionersMax: - limit = DefaultProvisionersMax + limit = DefaultAdminLimit + case limit > DefaultAdminMax: + limit = DefaultAdminMax } n := c.sorted.Len() cursor = fmt.Sprintf("%040s", cursor) i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor }) - slice := List{} + slice := []*Admin{} for ; i < n && len(slice) < limit; i++ { - slice = append(slice, c.sorted[i].provisioner) + slice = append(slice, c.sorted[i].admin) } if i < n { @@ -149,7 +167,6 @@ func (c *Collection) Find(cursor string, limit int) (List, string) { } return slice, "" } -*/ func loadAdmin(m *sync.Map, key string) (*Admin, bool) { a, ok := m.Load(key) diff --git a/authority/authority.go b/authority/authority.go index 2da5b341..2772c444 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -175,7 +175,7 @@ func (a *Authority) ReloadAuthConfig() error { } } // Store all the admins - a.admins = admin.NewCollection() + a.admins = admin.NewCollection(a.provisioners) for _, adm := range a.config.AuthorityConfig.Admins { if err := a.admins.Store(adm); err != nil { return err @@ -431,7 +431,7 @@ func (a *Authority) init() error { } } // Store all the admins - a.admins = admin.NewCollection() + a.admins = admin.NewCollection(a.provisioners) for _, adm := range a.config.AuthorityConfig.Admins { if err := a.admins.Store(adm); err != nil { return err diff --git a/authority/mgmt/admin.go b/authority/mgmt/admin.go index f21abdce..d265a001 100644 --- a/authority/mgmt/admin.go +++ b/authority/mgmt/admin.go @@ -1,55 +1,23 @@ package mgmt import ( - "context" - "github.com/smallstep/certificates/authority/admin" ) // AdminType specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR -type AdminType string +type AdminType admin.Type var ( // AdminTypeSuper superadmin - AdminTypeSuper = AdminType("SUPER_ADMIN") + AdminTypeSuper = admin.TypeSuper // AdminTypeRegular regular - AdminTypeRegular = AdminType("REGULAR") + AdminTypeRegular = admin.TypeRegular ) // Admin type. -type Admin struct { - ID string `json:"id"` - AuthorityID string `json:"-"` - ProvisionerID string `json:"provisionerID"` - Subject string `json:"subject"` - ProvisionerName string `json:"provisionerName"` - ProvisionerType string `json:"provisionerType"` - Type AdminType `json:"type"` - Status StatusType `json:"status"` -} - -// CreateAdmin builds and stores an admin type in the DB. -func CreateAdmin(ctx context.Context, db DB, provName, sub string, typ AdminType) (*Admin, error) { - adm := &Admin{ - Subject: sub, - ProvisionerName: provName, - Type: typ, - Status: StatusActive, - } - if err := db.CreateAdmin(ctx, adm); err != nil { - return nil, WrapErrorISE(err, "error creating admin") - } - return adm, nil -} +type Admin admin.Admin // ToCertificates converts an Admin to the Admin type expected by the authority. func (adm *Admin) ToCertificates() (*admin.Admin, error) { - return &admin.Admin{ - ID: adm.ID, - Subject: adm.Subject, - ProvisionerID: adm.ProvisionerID, - ProvisionerName: adm.ProvisionerName, - ProvisionerType: adm.ProvisionerType, - Type: admin.Type(adm.Type), - }, nil + return (*admin.Admin)(adm), nil } diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index f997095a..b474e131 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -8,27 +8,34 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/status" ) // CreateAdminRequest represents the body for a CreateAdmin request. type CreateAdminRequest struct { - Subject string `json:"subject"` - Provisioner string `json:"provisioner"` - Type mgmt.AdminType `json:"type"` + Subject string `json:"subject"` + Provisioner string `json:"provisioner"` + Type admin.Type `json:"type"` } // Validate validates a new-admin request body. func (car *CreateAdminRequest) Validate(c *admin.Collection) error { if _, ok := c.LoadBySubProv(car.Subject, car.Provisioner); ok { return mgmt.NewError(mgmt.ErrorBadRequestType, - "admin with subject %s and provisioner name %s already exists", car.Subject, car.Provisioner) + "admin with subject: '%s' and provisioner: '%s' already exists", car.Subject, car.Provisioner) } return nil } +// GetAdminsResponse for returning a list of admins. +type GetAdminsResponse struct { + Admins []*admin.Admin `json:"admins"` + NextCursor string `json:"nextCursor"` +} + // UpdateAdminRequest represents the body for a UpdateAdmin request. type UpdateAdminRequest struct { - Type mgmt.AdminType `json:"type"` + Type admin.Type `json:"type"` } // Validate validates a new-admin request body. @@ -43,27 +50,31 @@ type DeleteResponse struct { // GetAdmin returns the requested admin, or an error. func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() id := chi.URLParam(r, "id") - prov, err := h.db.GetAdmin(ctx, id) - if err != nil { - api.WriteError(w, err) + adm, ok := h.auth.GetAdminCollection().LoadByID(id) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, + "admin %s not found", id)) return } - api.JSON(w, prov) + api.JSON(w, adm) } // GetAdmins returns all admins associated with the authority. func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - admins, err := h.db.GetAdmins(ctx) + cursor, limit, err := api.ParseCursor(r) if err != nil { - api.WriteError(w, err) + api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, + "error parsing cursor and limit from query params")) return } - api.JSON(w, admins) + + admins, nextCursor := h.auth.GetAdminCollection().Find(cursor, limit) + api.JSON(w, &GetAdminsResponse{ + Admins: admins, + NextCursor: nextCursor, + }) } // CreateAdmin creates a new admin. @@ -81,16 +92,24 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { return } + p, ok := h.auth.GetProvisionerCollection().LoadByName(body.Provisioner) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", body.Provisioner)) + return + } + adm := &mgmt.Admin{ - ProvisionerName: body.Provisioner, - Subject: body.Subject, - Type: body.Type, - Status: mgmt.StatusActive, + ProvisionerID: p.GetID(), + Subject: body.Subject, + Type: body.Type, + Status: status.Active, } if err := h.db.CreateAdmin(ctx, adm); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) return } + adm.ProvisionerName = p.GetName() + adm.ProvisionerType = p.GetType().String() api.JSON(w, adm) if err := h.auth.ReloadAuthConfig(); err != nil { fmt.Printf("err = %+v\n", err) @@ -112,7 +131,7 @@ func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) return } - adm.Status = mgmt.StatusDeleted + adm.Status = status.Deleted if err := h.db.UpdateAdmin(ctx, adm); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) return @@ -135,17 +154,19 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - adm, err := h.db.GetAdmin(ctx, id) - if err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) + adm, ok := h.auth.GetAdminCollection().LoadByID(id) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id)) + return + } + if adm.Type == body.Type { + api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type)) return } - - // TODO validate adm.Type = body.Type - if err := h.db.UpdateAdmin(ctx, adm); err != nil { + if err := h.db.UpdateAdmin(ctx, (*mgmt.Admin)(adm)); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) return } diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index 8a8da08f..79ef5f74 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -8,6 +8,7 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/authority/status" ) // CreateProvisionerRequest represents the body for a CreateProvisioner request. @@ -55,7 +56,13 @@ func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { ctx := r.Context() name := chi.URLParam(r, "name") - prov, err := h.db.GetProvisionerByName(ctx, name) + p, ok := h.auth.GetProvisionerCollection().LoadByName(name) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) + return + } + + prov, err := h.db.GetProvisioner(ctx, p.GetID()) if err != nil { api.WriteError(w, err) return @@ -123,6 +130,8 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") c := h.auth.GetAdminCollection() + fmt.Printf("c.Count() = %+v\n", c.Count()) + fmt.Printf("c.CountByProvisioner() = %+v\n", c.CountByProvisioner(name)) if c.Count() == c.CountByProvisioner(name) { api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove provisioner %s because no admins will remain", name)) @@ -130,14 +139,18 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { } ctx := r.Context() - prov, err := h.db.GetProvisionerByName(ctx, name) + p, ok := h.auth.GetProvisionerCollection().LoadByName(name) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) + return + } + prov, err := h.db.GetProvisioner(ctx, p.GetID()) if err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving provisioner %s", name)) + api.WriteError(w, mgmt.WrapErrorISE(err, "error loading provisioner %s from db", name)) return } - fmt.Printf("prov = %+v\n", prov) - prov.Status = mgmt.StatusDeleted - if err := h.db.UpdateProvisioner(ctx, name, prov); err != nil { + prov.Status = status.Deleted + if err := h.db.UpdateProvisioner(ctx, prov); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error updating provisioner %s", name)) return } @@ -150,8 +163,8 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { ID: adm.ID, ProvisionerID: adm.ProvisionerID, Subject: adm.Subject, - Type: mgmt.AdminType(adm.Type), - Status: mgmt.StatusDeleted, + Type: adm.Type, + Status: status.Deleted, }); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, name)) return diff --git a/authority/mgmt/authConfig.go b/authority/mgmt/authConfig.go index 14448af4..6284a9e7 100644 --- a/authority/mgmt/authConfig.go +++ b/authority/mgmt/authConfig.go @@ -4,6 +4,7 @@ import ( "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/authority/status" ) // AuthConfig represents the Authority Configuration. @@ -15,7 +16,7 @@ type AuthConfig struct { Admins []*Admin `json:"-"` Claims *Claims `json:"claims,omitempty"` Backdate string `json:"backdate,omitempty"` - Status StatusType `json:"status,omitempty"` + Status status.Type `json:"status,omitempty"` } func NewDefaultAuthConfig() *AuthConfig { @@ -23,7 +24,7 @@ func NewDefaultAuthConfig() *AuthConfig { Claims: NewDefaultClaims(), ASN1DN: &config.ASN1DN{}, Backdate: config.DefaultBackdate.String(), - Status: StatusActive, + Status: status.Active, } } diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index d569ad6f..8b23cad0 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -14,16 +14,6 @@ const ( DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) -// StatusType is the type for status. -type StatusType string - -var ( - // StatusActive active - StatusActive = StatusType("active") - // StatusDeleted deleted - StatusDeleted = StatusType("deleted") -) - // Claims encapsulates all x509 and ssh claims applied to the authority // configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc. type Claims struct { diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go index 8546c4b4..9cfab3e5 100644 --- a/authority/mgmt/db.go +++ b/authority/mgmt/db.go @@ -14,9 +14,8 @@ var ErrNotFound = errors.New("not found") type DB interface { CreateProvisioner(ctx context.Context, prov *Provisioner) error GetProvisioner(ctx context.Context, id string) (*Provisioner, error) - GetProvisionerByName(ctx context.Context, name string) (*Provisioner, error) GetProvisioners(ctx context.Context) ([]*Provisioner, error) - UpdateProvisioner(ctx context.Context, name string, prov *Provisioner) error + UpdateProvisioner(ctx context.Context, prov *Provisioner) error CreateAdmin(ctx context.Context, admin *Admin) error GetAdmin(ctx context.Context, id string) (*Admin, error) @@ -31,11 +30,10 @@ type DB interface { // MockDB is an implementation of the DB interface that should only be used as // a mock in tests. type MockDB struct { - MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error - MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) - MockGetProvisionerByName func(ctx context.Context, name string) (*Provisioner, error) - MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) - MockUpdateProvisioner func(ctx context.Context, name string, prov *Provisioner) error + MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error + MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) + MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) + MockUpdateProvisioner func(ctx context.Context, prov *Provisioner) error MockCreateAdmin func(ctx context.Context, adm *Admin) error MockGetAdmin func(ctx context.Context, id string) (*Admin, error) @@ -70,16 +68,6 @@ func (m *MockDB) GetProvisioner(ctx context.Context, id string) (*Provisioner, e return m.MockRet1.(*Provisioner), m.MockError } -// GetProvisionerByName mock. -func (m *MockDB) GetProvisionerByName(ctx context.Context, id string) (*Provisioner, error) { - if m.MockGetProvisionerByName != nil { - return m.MockGetProvisionerByName(ctx, id) - } else if m.MockError != nil { - return nil, m.MockError - } - return m.MockRet1.(*Provisioner), m.MockError -} - // GetProvisioners mock func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { if m.MockGetProvisioners != nil { @@ -91,9 +79,9 @@ func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { } // UpdateProvisioner mock -func (m *MockDB) UpdateProvisioner(ctx context.Context, name string, prov *Provisioner) error { +func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *Provisioner) error { if m.MockUpdateProvisioner != nil { - return m.MockUpdateProvisioner(ctx, name, prov) + return m.MockUpdateProvisioner(ctx, prov) } return m.MockError } diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 4e465489..9732b3f0 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -6,19 +6,21 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/status" "github.com/smallstep/nosql" ) // dbAdmin is the database representation of the Admin type. type dbAdmin struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - ProvisionerID string `json:"provisionerID"` - Subject string `json:"subject"` - Type mgmt.AdminType `json:"type"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + ProvisionerID string `json:"provisionerID"` + Subject string `json:"subject"` + Type admin.Type `json:"type"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbAdmin) clone() *dbAdmin { @@ -71,10 +73,10 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { ProvisionerID: dba.ProvisionerID, Subject: dba.Subject, Type: dba.Type, - Status: mgmt.StatusActive, + Status: status.Active, } if !dba.DeletedAt.IsZero() { - adm.Status = mgmt.StatusDeleted + adm.Status = status.Deleted } return adm, nil } @@ -89,19 +91,13 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { if err != nil { return nil, err } - if adm.Status == mgmt.StatusDeleted { + if adm.Status == status.Deleted { return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID) } if adm.AuthorityID != db.authorityID { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, "admin %s is not owned by authority %s", adm.ID, db.authorityID) } - prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) - if err != nil { - return nil, err - } - adm.ProvisionerName = prov.Name - adm.ProvisionerType = prov.Type return adm, nil } @@ -114,34 +110,18 @@ func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { if err != nil { return nil, errors.Wrap(err, "error loading admins") } - var ( - provCache = map[string]*mgmt.Provisioner{} - admins []*mgmt.Admin - ) + var admins = []*mgmt.Admin{} for _, entry := range dbEntries { adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) if err != nil { return nil, err } - if adm.Status == mgmt.StatusDeleted { + if adm.Status == status.Deleted { continue } if adm.AuthorityID != db.authorityID { continue } - var ( - prov *mgmt.Provisioner - ok bool - ) - if prov, ok = provCache[adm.ProvisionerID]; !ok { - prov, err = db.GetProvisioner(ctx, adm.ProvisionerID) - if err != nil { - return nil, err - } - provCache[adm.ProvisionerID] = prov - } - adm.ProvisionerName = prov.Name - adm.ProvisionerType = prov.Type admins = append(admins, adm) } return admins, nil @@ -156,24 +136,6 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { } adm.AuthorityID = db.authorityID - // If provisionerID is set, then use it, otherwise load the provisioner - // to get the name. - if adm.ProvisionerID == "" { - prov, err := db.GetProvisionerByName(ctx, adm.ProvisionerName) - if err != nil { - return err - } - adm.ProvisionerID = prov.ID - adm.ProvisionerType = prov.Type - } else { - prov, err := db.GetProvisioner(ctx, adm.ProvisionerID) - if err != nil { - return err - } - adm.ProvisionerName = prov.Name - adm.ProvisionerType = prov.Type - } - dba := &dbAdmin{ ID: adm.ID, AuthorityID: db.authorityID, @@ -196,7 +158,7 @@ func (db *DB) UpdateAdmin(ctx context.Context, adm *mgmt.Admin) error { nu := old.clone() // If the admin was active but is now deleted ... - if old.DeletedAt.IsZero() && adm.Status == mgmt.StatusDeleted { + if old.DeletedAt.IsZero() && adm.Status == status.Deleted { nu.DeletedAt = clock.Now() } nu.Type = adm.Type diff --git a/authority/mgmt/db/nosql/authConfig.go b/authority/mgmt/db/nosql/authConfig.go index b9108fdc..222e729d 100644 --- a/authority/mgmt/db/nosql/authConfig.go +++ b/authority/mgmt/db/nosql/authConfig.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/status" "github.com/smallstep/nosql" ) @@ -106,7 +107,7 @@ func (db *DB) UpdateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { nu := old.clone() // If the authority was active but is now deleted ... - if old.DeletedAt.IsZero() && ac.Status == mgmt.StatusDeleted { + if old.DeletedAt.IsZero() && ac.Status == status.Deleted { nu.DeletedAt = clock.Now() } nu.Claims = ac.Claims diff --git a/authority/mgmt/db/nosql/nosql.go b/authority/mgmt/db/nosql/nosql.go index d97b7700..47f79bde 100644 --- a/authority/mgmt/db/nosql/nosql.go +++ b/authority/mgmt/db/nosql/nosql.go @@ -3,7 +3,6 @@ package nosql import ( "context" "encoding/json" - "fmt" "time" "github.com/pkg/errors" @@ -60,7 +59,6 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old) } } - fmt.Printf("oldB = %+v\n", oldB) _, swapped, err := db.db.CmpAndSwap(table, []byte(id), oldB, newB) switch { diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index d44c3be1..2c2d65b0 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -3,13 +3,12 @@ package nosql import ( "context" "encoding/json" - "fmt" "time" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/status" "github.com/smallstep/nosql" - "github.com/smallstep/nosql/database" ) // dbProvisioner is the database representation of a Provisioner type. @@ -39,20 +38,6 @@ func (dbp *dbProvisioner) clone() *dbProvisioner { return &u } -func (db *DB) getProvisionerIDByName(ctx context.Context, name string) (string, error) { - data, err := db.db.Get(authorityProvisionersNameIDIndexTable, []byte(name)) - if nosql.IsErrNotFound(err) { - return "", mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name) - } else if err != nil { - return "", mgmt.WrapErrorISE(err, "error loading provisioner %s", name) - } - ni := new(provisionerNameID) - if err := json.Unmarshal(data, ni); err != nil { - return "", mgmt.WrapErrorISE(err, "error unmarshaling provisionerNameID for provisioner %s", name) - } - return ni.ID, nil -} - func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { data, err := db.db.Get(authorityProvisionersTable, []byte(id)) if nosql.IsErrNotFound(err) { @@ -82,25 +67,6 @@ func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, return dbp, nil } -func (db *DB) getDBProvisionerByName(ctx context.Context, name string) (*dbProvisioner, error) { - id, err := db.getProvisionerIDByName(ctx, name) - if err != nil { - return nil, err - } - dbp, err := db.getDBProvisioner(ctx, id) - if err != nil { - return nil, err - } - if !dbp.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name) - } - if dbp.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", name, db.authorityID) - } - return dbp, nil -} - // GetProvisioner retrieves and unmarshals a provisioner from the database. func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, error) { data, err := db.getDBProvisionerBytes(ctx, id) @@ -112,7 +78,7 @@ func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, if err != nil { return nil, err } - if prov.Status == mgmt.StatusDeleted { + if prov.Status == status.Deleted { return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", prov.ID) } if prov.AuthorityID != db.authorityID { @@ -122,15 +88,6 @@ func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, return prov, nil } -// GetProvisionerByName retrieves a provisioner from the database by name. -func (db *DB) GetProvisionerByName(ctx context.Context, name string) (*mgmt.Provisioner, error) { - p, err := db.getProvisionerIDByName(ctx, name) - if err != nil { - return nil, err - } - return db.GetProvisioner(ctx, id) -} - func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) { var dbp = new(dbProvisioner) if err := json.Unmarshal(data, dbp); err != nil { @@ -157,14 +114,14 @@ func unmarshalProvisioner(data []byte, name string) (*mgmt.Provisioner, error) { Name: dbp.Name, Claims: dbp.Claims, Details: details, - Status: mgmt.StatusActive, + Status: status.Active, X509Template: dbp.X509Template, X509TemplateData: dbp.X509TemplateData, SSHTemplate: dbp.SSHTemplate, SSHTemplateData: dbp.SSHTemplateData, } if !dbp.DeletedAt.IsZero() { - prov.Status = mgmt.StatusDeleted + prov.Status = status.Deleted } return prov, nil } @@ -182,7 +139,7 @@ func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) if err != nil { return nil, err } - if prov.Status == mgmt.StatusDeleted { + if prov.Status == status.Deleted { continue } if prov.AuthorityID != db.authorityID { @@ -219,37 +176,8 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err SSHTemplateData: prov.SSHTemplateData, CreatedAt: clock.Now(), } - dbpBytes, err := json.Marshal(dbp) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling dbProvisioner %s", prov.Name) - } - pni := &provisionerNameID{ - Name: prov.Name, - ID: prov.ID, - } - pniBytes, err := json.Marshal(pni) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling provisionerNameIndex %s", prov.Name) - } - if err := db.db.Update(&database.Tx{ - Operations: []*database.TxEntry{ - { - Bucket: authorityProvisionersTable, - Key: []byte(dbp.ID), - Cmd: database.CmpAndSwap, - Value: dbpBytes, - CmpValue: nil, - }, - { - Bucket: authorityProvisionersNameIDIndexTable, - Key: []byte(dbp.Name), - Cmd: database.CmpAndSwap, - Value: pniBytes, - CmpValue: nil, - }, - }, - }); err != nil { + if err := db.save(ctx, prov.ID, dbp, nil, "provisioner", authorityProvisionersTable); err != nil { return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name) } @@ -257,22 +185,11 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err } // UpdateProvisioner saves an updated provisioner to the database. -func (db *DB) UpdateProvisioner(ctx context.Context, name string, prov *mgmt.Provisioner) error { - id, err := db.getProvisionerIDByName(ctx, name) - if err != nil { - return err - } - prov.ID = id - oldBytes, err := db.getDBProvisionerBytes(ctx, id) - if err != nil { - return err - } - fmt.Printf("oldBytes = %+v\n", oldBytes) - old, err := unmarshalDBProvisioner(oldBytes, id) +func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { + old, err := db.getDBProvisioner(ctx, prov.ID) if err != nil { return err } - fmt.Printf("old = %+v\n", old) nu := old.clone() @@ -281,91 +198,15 @@ func (db *DB) UpdateProvisioner(ctx context.Context, name string, prov *mgmt.Pro nu.Claims = prov.Claims nu.Details, err = json.Marshal(prov.Details) if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", name) + return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name) } nu.X509Template = prov.X509Template nu.X509TemplateData = prov.X509TemplateData nu.SSHTemplateData = prov.SSHTemplateData - var txs = []*database.TxEntry{} - // If the provisioner was active but is now deleted ... - if old.DeletedAt.IsZero() && prov.Status == mgmt.StatusDeleted { - nu.DeletedAt = clock.Now() - txs = append(txs, &database.TxEntry{ - Bucket: authorityProvisionersNameIDIndexTable, - Key: []byte(name), - Cmd: database.Delete, - }) - } - - if prov.Name != name { - // If the new name does not match the old name then: - // 1) check that the new name is not already taken - // 2) delete the old name-id index resource - // 3) create a new name-id index resource - // 4) update the provisioner resource - nuBytes, err := json.Marshal(nu) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling dbProvisioner %s", prov.Name) - } - pni := &provisionerNameID{ - Name: prov.Name, - ID: prov.ID, - } - pniBytes, err := json.Marshal(pni) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling provisionerNameID for provisioner %s", prov.Name) - } - - _, err = db.db.Get(authorityProvisionersNameIDIndexTable, []byte(name)) - if err == nil { - return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", prov.Name) - } else if !nosql.IsErrNotFound(err) { - return mgmt.WrapErrorISE(err, "error loading provisionerNameID %s", prov.Name) - } - err = db.db.Update(&database.Tx{ - Operations: []*database.TxEntry{ - { - Bucket: authorityProvisionersNameIDIndexTable, - Key: []byte(name), - Cmd: database.Delete, - }, - { - Bucket: authorityProvisionersNameIDIndexTable, - Key: []byte(prov.Name), - Cmd: database.CmpAndSwap, - Value: pniBytes, - CmpValue: nil, - }, - { - Bucket: authorityProvisionersTable, - Key: []byte(nu.ID), - Cmd: database.CmpAndSwap, - Value: nuBytes, - CmpValue: oldBytes, - }, - }, - }) - } else { - err = db.db.Update(&database.Tx{ - Operations: []*database.TxEntry{ - { - Bucket: authorityProvisionersNameIDIndexTable, - Key: []byte(name), - Cmd: database.Delete, - }, - { - Bucket: authorityProvisionersTable, - Key: []byte(nu.ID), - Cmd: database.CmpAndSwap, - Value: nuBytes, - CmpValue: oldBytes, - }, - }, - }) - } - if err != nil { + if err := db.save(ctx, prov.ID, nu, old, "provisioner", authorityProvisionersTable); err != nil { return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name) } + return nil } diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index fb39f6bc..10f472de 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/authority/status" "go.step.sm/crypto/jose" ) @@ -69,7 +70,7 @@ type Provisioner struct { X509TemplateData []byte `json:"x509TemplateData"` SSHTemplate string `json:"sshTemplate"` SSHTemplateData []byte `json:"sshTemplateData"` - Status StatusType `json:"status"` + Status status.Type `json:"status"` } func (p *Provisioner) GetOptions() *provisioner.Options { @@ -101,7 +102,7 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro X509TemplateData: pc.X509TemplateData, SSHTemplate: pc.SSHTemplate, SSHTemplateData: pc.SSHTemplateData, - Status: StatusActive, + Status: status.Active, } if err := db.CreateProvisioner(ctx, p); err != nil { diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index b6ff8b9e..23a4a2a0 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -183,7 +183,7 @@ func (c *Collection) Store(p Interface) error { // Store provisioner always by name. if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded { c.byID.Delete(p.GetID()) - return errors.New("cannot add multiple provisioners with the same id") + return errors.New("cannot add multiple provisioners with the same name") } // Store provisioner in byKey if EncryptedKey is defined. diff --git a/authority/status/status.go b/authority/status/status.go new file mode 100644 index 00000000..49e4c0bb --- /dev/null +++ b/authority/status/status.go @@ -0,0 +1,11 @@ +package status + +// Type is the type for status. +type Type string + +var ( + // Active active + Active = Type("active") + // Deleted deleted + Deleted = Type("deleted") +) diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index 96c33f03..7a50e369 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -7,8 +7,10 @@ import ( "net/http" "net/url" "path" + "strconv" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" "github.com/smallstep/certificates/errs" @@ -88,6 +90,80 @@ retry: return adm, nil } +// AdminOption is the type of options passed to the Provisioner method. +type AdminOption func(o *adminOptions) error + +type adminOptions struct { + cursor string + limit int +} + +func (o *adminOptions) apply(opts []AdminOption) (err error) { + for _, fn := range opts { + if err = fn(o); err != nil { + return + } + } + return +} + +func (o *adminOptions) rawQuery() string { + v := url.Values{} + if len(o.cursor) > 0 { + v.Set("cursor", o.cursor) + } + if o.limit > 0 { + v.Set("limit", strconv.Itoa(o.limit)) + } + return v.Encode() +} + +// WithAdminCursor will request the admins starting with the given cursor. +func WithAdminCursor(cursor string) AdminOption { + return func(o *adminOptions) error { + o.cursor = cursor + return nil + } +} + +// WithAdminLimit will request the given number of admins. +func WithAdminLimit(limit int) AdminOption { + return func(o *adminOptions) error { + o.limit = limit + return nil + } +} + +// GetAdmins performs the GET /mgmt/admins request to the CA. +func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { + var retried bool + o := new(adminOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + u := c.endpoint.ResolveReference(&url.URL{ + Path: "/mgmt/admins", + RawQuery: o.rawQuery(), + }) +retry: + resp, err := c.client.Get(u.String()) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readMgmtError(resp.Body) + } + var body = new(mgmtAPI.GetAdminsResponse) + if err := readJSON(resp.Body, body); err != nil { + return nil, errors.Wrapf(err, "error reading %s", u) + } + return body, nil +} + // CreateAdmin performs the POST /mgmt/admin request to the CA. func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { var retried bool @@ -139,7 +215,7 @@ retry: } // UpdateAdmin performs the PUT /mgmt/admin/{id} request to the CA. -func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*mgmt.Admin, error) { +func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { var retried bool body, err := json.Marshal(uar) if err != nil { @@ -162,36 +238,13 @@ retry: } return nil, readMgmtError(resp.Body) } - var adm = new(mgmt.Admin) + var adm = new(admin.Admin) if err := readJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return adm, nil } -// GetAdmins performs the GET /mgmt/admins request to the CA. -func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) { - var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admins"}) -retry: - resp, err := c.client.Get(u.String()) - if err != nil { - return nil, errors.Wrapf(err, "client GET %s failed", u) - } - if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { - retried = true - goto retry - } - return nil, readMgmtError(resp.Body) - } - var admins = new([]*mgmt.Admin) - if err := readJSON(resp.Body, admins); err != nil { - return nil, errors.Wrapf(err, "error reading %s", u) - } - return *admins, nil -} - // GetProvisioner performs the GET /mgmt/provisioner/{id} request to the CA. func (c *MgmtClient) GetProvisioner(id string) (*mgmt.Provisioner, error) { var retried bool From 638766c615cb313d8f597ef472003fa3156fba0f Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 19 May 2021 18:23:20 -0700 Subject: [PATCH 009/291] wip --- authority/provisioner/acme.go | 10 ++++++++++ authority/provisioner/aws.go | 10 ++++++++++ authority/provisioner/azure.go | 10 ++++++++++ authority/provisioner/collection.go | 28 ++++++++++++++++++++++------ authority/provisioner/gcp.go | 11 +++++++++++ authority/provisioner/jwk.go | 6 ++++++ authority/provisioner/k8sSA.go | 10 ++++++++++ authority/provisioner/noop.go | 4 ++++ authority/provisioner/oidc.go | 10 ++++++++++ authority/provisioner/provisioner.go | 10 ++++++++++ authority/provisioner/sshpop.go | 10 ++++++++++ authority/provisioner/x5c.go | 10 ++++++++++ 12 files changed, 123 insertions(+), 6 deletions(-) diff --git a/authority/provisioner/acme.go b/authority/provisioner/acme.go index a36c496d..4109d217 100644 --- a/authority/provisioner/acme.go +++ b/authority/provisioner/acme.go @@ -13,6 +13,7 @@ import ( // provisioning flow. type ACME struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` ForceCN bool `json:"forceCN,omitempty"` @@ -23,6 +24,15 @@ type ACME struct { // GetID returns the provisioner unique identifier. func (p ACME) 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 *ACME) GetIDForToken() string { return "acme/" + p.Name } diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 75115154..8a443554 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -252,6 +252,7 @@ type awsInstanceIdentityDocument struct { // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html type AWS struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` Accounts []string `json:"accounts"` @@ -269,6 +270,15 @@ type AWS struct { // GetID returns the provisioner unique identifier. func (p *AWS) 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 *AWS) GetIDForToken() string { return "aws/" + p.Name } diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index ea8b08ec..b077d735 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -84,6 +84,7 @@ type azurePayload struct { // and https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service type Azure struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` TenantID string `json:"tenantID"` @@ -101,6 +102,15 @@ type Azure struct { // GetID returns the provisioner unique identifier. func (p *Azure) 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 *Azure) GetIDForToken() string { return p.TenantID } diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index 23a4a2a0..ccfbc60a 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -46,6 +46,7 @@ type Collection struct { byID *sync.Map byKey *sync.Map byName *sync.Map + byTokenID *sync.Map sorted provisionerSlice audiences Audiences } @@ -57,6 +58,7 @@ func NewCollection(audiences Audiences) *Collection { byID: new(sync.Map), byKey: new(sync.Map), byName: new(sync.Map), + byTokenID: new(sync.Map), audiences: audiences, } } @@ -71,6 +73,13 @@ func (c *Collection) LoadByName(name string) (Interface, bool) { return loadProvisioner(c.byName, name) } +// LoadByTokenID a provisioner by identifier found in token. +// For different provisioner types this identifier may be found in in different +// attributes of the token. +func (c *Collection) LoadByTokenID(tokenProvisionerID string) (Interface, bool) { + return loadProvisioner(c.byTokenID, tokenProvisionerID) +} + // LoadByToken parses the token claims and loads the provisioner associated. func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) (Interface, bool) { var audiences []string @@ -86,11 +95,12 @@ func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) if matchesAudience(claims.Audience, audiences) { // Use fragment to get provisioner name (GCP, AWS, SSHPOP) if fragment != "" { - return c.Load(fragment) + return c.LoadByTokenID(fragment) } // If matches with stored audiences it will be a JWT token (default), and // the id would be :. - return c.Load(claims.Issuer + ":" + token.Headers[0].KeyID) + // TODO: is this ok? + return c.LoadByTokenID(claims.Issuer + ":" + token.Headers[0].KeyID) } // The ID will be just the clientID stored in azp, aud or tid. @@ -101,7 +111,7 @@ func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) // Kubernetes Service Account tokens. if payload.Issuer == k8sSAIssuer { - if p, ok := c.Load(K8sSAID); ok { + if p, ok := c.LoadByTokenID(K8sSAID); ok { return p, ok } // Kubernetes service account provisioner not found @@ -115,18 +125,18 @@ func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) // Try with azp (OIDC) if len(payload.AuthorizedParty) > 0 { - if p, ok := c.Load(payload.AuthorizedParty); ok { + if p, ok := c.LoadByTokenID(payload.AuthorizedParty); ok { return p, ok } } // Try with tid (Azure) if payload.TenantID != "" { - if p, ok := c.Load(payload.TenantID); ok { + if p, ok := c.LoadByTokenID(payload.TenantID); ok { return p, ok } } // Fallback to aud - return c.Load(payload.Audience[0]) + return c.LoadByTokenID(payload.Audience[0]) } // LoadByCertificate looks for the provisioner extension and extracts the @@ -185,6 +195,12 @@ func (c *Collection) Store(p Interface) error { c.byID.Delete(p.GetID()) return errors.New("cannot add multiple provisioners with the same name") } + // Store provisioner always by ID presented in token. + if _, loaded := c.byTokenID.LoadOrStore(p.GetIDForToken(), p); loaded { + c.byID.Delete(p.GetID()) + c.byName.Delete(p.GetName()) + return errors.New("cannot add multiple provisioners with the same token identifier") + } // Store provisioner in byKey if EncryptedKey is defined. if kid, _, ok := p.GetEncryptedKey(); ok { diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 830e7965..6d19d052 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -78,6 +78,7 @@ func newGCPConfig() *gcpConfig { // https://cloud.google.com/compute/docs/instances/verifying-instance-identity type GCP struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` ServiceAccounts []string `json:"serviceAccounts"` @@ -96,6 +97,16 @@ type GCP struct { // GetID returns the provisioner unique identifier. The name should uniquely // identify any GCP provisioner. func (p *GCP) 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 *GCP) GetIDForToken() string { return "gcp/" + p.Name } diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index a2d3e0b1..d57ff4c1 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -45,6 +45,12 @@ func (p *JWK) 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 *JWK) GetIDForToken() string { return p.Name + ":" + p.Key.KeyID } diff --git a/authority/provisioner/k8sSA.go b/authority/provisioner/k8sSA.go index 209a7dd4..876131e1 100644 --- a/authority/provisioner/k8sSA.go +++ b/authority/provisioner/k8sSA.go @@ -42,6 +42,7 @@ type k8sSAPayload struct { // entity trusted to make signature requests. type K8sSA struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` PubKeys []byte `json:"publicKeys,omitempty"` @@ -56,6 +57,15 @@ type K8sSA struct { // GetID returns the provisioner unique identifier. The name and credential id // should uniquely identify any K8sSA provisioner. func (p *K8sSA) 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 *K8sSA) GetIDForToken() string { return K8sSAID } diff --git a/authority/provisioner/noop.go b/authority/provisioner/noop.go index ccdeccf4..18a38331 100644 --- a/authority/provisioner/noop.go +++ b/authority/provisioner/noop.go @@ -14,6 +14,10 @@ func (p *noop) GetID() string { return "noop" } +func (p *noop) GetIDForToken() string { + return "noop" +} + func (p *noop) GetTokenID(token string) (string, error) { return "", nil } diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index 46e1c623..df3c1f9e 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -54,6 +54,7 @@ type openIDPayload struct { // ClientSecret is mandatory, but it can be an empty string. type OIDC struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` ClientID string `json:"clientID"` @@ -111,6 +112,15 @@ func sanitizeEmail(email string) string { // GetID returns the provisioner unique identifier, the OIDC provisioner the // uses the clientID for this. func (o *OIDC) GetID() string { + if o.ID != "" { + return o.ID + } + return o.GetIDForToken() +} + +// GetIDForToken returns an identifier that will be used to load the provisioner +// from a token. +func (o *OIDC) GetIDForToken() string { return o.ClientID } diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 197bd26c..981d0b0a 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -17,6 +17,7 @@ import ( // Interface is the interface that all provisioner types must implement. type Interface interface { GetID() string + GetIDForToken() string GetTokenID(token string) (string, error) GetName() string GetType() Type @@ -388,6 +389,7 @@ type MockProvisioner struct { Mret1, Mret2, Mret3 interface{} Merr error MgetID func() string + MgetIDForToken func() string MgetTokenID func(string) (string, error) MgetName func() string MgetType func() Type @@ -410,6 +412,14 @@ func (m *MockProvisioner) GetID() string { return m.Mret1.(string) } +// GetIDForToken mock +func (m *MockProvisioner) GetIDForToken() string { + if m.MgetIDForToken != nil { + return m.MgetIDForToken() + } + return m.Mret1.(string) +} + // GetTokenID mock func (m *MockProvisioner) GetTokenID(token string) (string, error) { if m.MgetTokenID != nil { diff --git a/authority/provisioner/sshpop.go b/authority/provisioner/sshpop.go index 223f0b9e..dd9c7d1f 100644 --- a/authority/provisioner/sshpop.go +++ b/authority/provisioner/sshpop.go @@ -26,6 +26,7 @@ type sshPOPPayload struct { // signature requests. type SSHPOP struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` Claims *Claims `json:"claims,omitempty"` @@ -38,6 +39,15 @@ type SSHPOP struct { // GetID returns the provisioner unique identifier. The name and credential id // should uniquely identify any SSH-POP provisioner. func (p *SSHPOP) 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 *SSHPOP) GetIDForToken() string { return "sshpop/" + p.Name } diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index 2b05f4c8..9e47aaed 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -26,6 +26,7 @@ type x5cPayload struct { // signature requests. type X5C struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` Roots []byte `json:"roots"` @@ -39,6 +40,15 @@ type X5C struct { // GetID returns the provisioner unique identifier. The name and credential id // should uniquely identify any X5C provisioner. func (p *X5C) 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 *X5C) GetIDForToken() string { return "x5c/" + p.Name } From 9bf9bf142d250f580a125bf83f0f2a45c2eb5786 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 20 May 2021 13:01:58 -0700 Subject: [PATCH 010/291] wip --- authority/authority.go | 16 ++++++------ authority/config.go | 37 +++++++++++++++++++++++++++ authority/mgmt/api/handler.go | 4 +-- authority/mgmt/api/provisioner.go | 23 ++++++++++++++--- authority/mgmt/provisioner.go | 36 +++++++++++++------------- authority/options.go | 9 +++++++ ca/ca.go | 10 ++++---- ca/mgmtClient.go | 42 +++++++++++++++---------------- 8 files changed, 120 insertions(+), 57 deletions(-) create mode 100644 authority/config.go diff --git a/authority/authority.go b/authority/authority.go index 2772c444..e26ff22d 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -32,7 +32,7 @@ import ( // Authority implements the Certificate Authority internal interface. type Authority struct { config *config.Config - mgmtDB mgmt.DB + adminDB mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection admins *admin.Collection @@ -130,7 +130,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) { } func (a *Authority) ReloadAuthConfig() error { - mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) + mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) if err != nil { return mgmt.WrapErrorISE(err, "error getting authConfig from db") } @@ -204,14 +204,14 @@ func (a *Authority) init() error { // Pull AuthConfig from DB. if true { // Check if AuthConfig already exists - a.mgmtDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) + a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) if err != nil { return err } - mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) + mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) if err != nil { if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) { - mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.mgmtDB, mgmt.WithDefaultAuthorityID) + mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.adminDB, mgmt.WithDefaultAuthorityID) if err != nil { return mgmt.WrapErrorISE(err, "error creating authConfig") } @@ -465,9 +465,9 @@ func (a *Authority) GetDatabase() db.AuthDB { return a.db } -// GetMgmtDatabase returns the mgmt database, if one exists. -func (a *Authority) GetMgmtDatabase() mgmt.DB { - return a.mgmtDB +// GetAdminDatabase returns the mgmt database, if one exists. +func (a *Authority) GetAdminDatabase() mgmt.DB { + return a.adminDB } // GetAdminCollection returns the admin collection. diff --git a/authority/config.go b/authority/config.go new file mode 100644 index 00000000..79bdc7ff --- /dev/null +++ b/authority/config.go @@ -0,0 +1,37 @@ +package authority + +import "github.com/smallstep/certificates/authority/config" + +// Config is an alias to support older APIs. +type Config = config.Config + +// AuthConfig is an alias to support older APIs. +type AuthConfig = config.AuthConfig + +// ASN1DN is an alias to support older APIs. +type ASN1DN = config.ASN1DN + +// TLS + +// TLSOptions is an alias to support older APIs. +type TLSOptions = config.TLSOptions + +// SSH + +// SSHConfig is an alias to support older APIs. +type SSHConfig = config.SSHConfig + +// Bastion is an alias to support older APIs. +type Bastion = config.Bastion + +// HostTag is an alias to support older APIs. +type HostTag = config.HostTag + +// Host is an alias to support older APIs. +type Host = config.Host + +// SSHPublicKey is an alias to support older APIs. +type SSHPublicKey = config.SSHPublicKey + +// SSHKeys is an alias to support older APIs. +type SSHKeys = config.SSHKeys diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index cb52736f..11dcbd86 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -25,8 +25,8 @@ type Handler struct { } // NewHandler returns a new Authority Config Handler. -func NewHandler(db mgmt.DB, auth *authority.Authority) api.RouterHandler { - return &Handler{db, auth} +func NewHandler(auth *authority.Authority) api.RouterHandler { + return &Handler{auth.GetAdminDatabase(), auth} } // Route traffic and implement the Router interface. diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index 79ef5f74..d383a27f 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -9,6 +9,7 @@ import ( "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/status" + "github.com/smallstep/certificates/errs" ) // CreateProvisionerRequest represents the body for a CreateProvisioner request. @@ -31,6 +32,12 @@ func (cpr *CreateProvisionerRequest) Validate(c *provisioner.Collection) error { return nil } +// GetProvisionersResponse is the type for GET /admin/provisioners responses. +type GetProvisionersResponse struct { + Provisioners provisioner.List `json:"provisioners"` + NextCursor string `json:"nextCursor"` +} + // UpdateProvisionerRequest represents the body for a UpdateProvisioner request. type UpdateProvisionerRequest struct { Type string `json:"type"` @@ -72,14 +79,22 @@ func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { // GetProvisioners returns all provisioners associated with the authority. func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() + cursor, limit, err := api.ParseCursor(r) + if err != nil { + api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, + "error parsing cursor / limt query params")) + return + } - provs, err := h.db.GetProvisioners(ctx) + p, next, err := h.auth.GetProvisioners(cursor, limit) if err != nil { - api.WriteError(w, err) + api.WriteError(w, errs.InternalServerErr(err)) return } - api.JSON(w, provs) + api.JSON(w, &GetProvisionersResponse{ + Provisioners: p, + NextCursor: next, + }) } // CreateProvisioner creates a new prov. diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 10f472de..ea9d0da5 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -60,17 +60,17 @@ func WithPassword(pass string) func(*ProvisionerCtx) { // Provisioner type. type Provisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Type string `json:"type"` - Name string `json:"name"` - Claims *Claims `json:"claims"` - Details interface{} `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - Status status.Type `json:"status"` + ID string `json:"-"` + AuthorityID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Claims *Claims `json:"claims"` + Details ProvisionerDetails `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` + Status status.Type `json:"status"` } func (p *Provisioner) GetOptions() *provisioner.Options { @@ -111,6 +111,8 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro return p, nil } +// ProvisionerDetails is the interface implemented by all provisioner details +// attributes. type ProvisionerDetails interface { isProvisionerDetails() } @@ -118,8 +120,8 @@ type ProvisionerDetails interface { // ProvisionerDetailsJWK represents the values required by a JWK provisioner. type ProvisionerDetailsJWK struct { Type ProvisionerType `json:"type"` - PubKey []byte `json:"pubKey"` - EncPrivKey string `json:"privKey"` + PublicKey []byte `json:"publicKey"` + PrivateKey string `json:"PrivateKey"` } // ProvisionerDetailsOIDC represents the values required by a OIDC provisioner. @@ -232,8 +234,8 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { return &ProvisionerDetailsJWK{ Type: ProvisionerTypeJWK, - PubKey: jwkPubBytes, - EncPrivKey: jwePrivStr, + PublicKey: jwkPubBytes, + PrivateKey: jwePrivStr, }, nil } @@ -248,7 +250,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { switch details := p.Details.(type) { case *ProvisionerDetailsJWK: jwk := new(jose.JSONWebKey) - if err := json.Unmarshal(details.PubKey, &jwk); err != nil { + if err := json.Unmarshal(details.PublicKey, &jwk); err != nil { return nil, err } return &provisioner.JWK{ @@ -256,7 +258,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { Type: p.Type, Name: p.Name, Key: jwk, - EncryptedKey: details.EncPrivKey, + EncryptedKey: details.PrivateKey, Claims: claims, Options: p.GetOptions(), }, nil diff --git a/authority/options.go b/authority/options.go index aaf8ffb3..cc2f64af 100644 --- a/authority/options.go +++ b/authority/options.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" @@ -187,6 +188,14 @@ func WithX509FederatedBundle(pemCerts []byte) Option { } } +// WithAdminDB is an option to set the database backing the admin APIs. +func WithAdminDB(db mgmt.DB) Option { + return func(a *Authority) error { + a.adminDB = db + return nil + } +} + func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) { var block *pem.Block var certs []*x509.Certificate diff --git a/ca/ca.go b/ca/ca.go index ecbc55e2..d97e0ab2 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -171,11 +171,11 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { acmeHandler.Route(r) }) - // MGMT Router - mgmtDB := auth.GetMgmtDatabase() - if mgmtDB != nil { - mgmtHandler := mgmtAPI.NewHandler(mgmtDB, auth) - mux.Route("/mgmt", func(r chi.Router) { + // Admin API Router + adminDB := auth.GetAdminDatabase() + if adminDB != nil { + mgmtHandler := mgmtAPI.NewHandler(auth) + mux.Route("/admin", func(r chi.Router) { mgmtHandler.Route(r) }) } diff --git a/ca/mgmtClient.go b/ca/mgmtClient.go index 7a50e369..7c4207aa 100644 --- a/ca/mgmtClient.go +++ b/ca/mgmtClient.go @@ -134,7 +134,7 @@ func WithAdminLimit(limit int) AdminOption { } } -// GetAdmins performs the GET /mgmt/admins request to the CA. +// GetAdmins performs the GET /admin/admins request to the CA. func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { var retried bool o := new(adminOptions) @@ -142,7 +142,7 @@ func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, return nil, err } u := c.endpoint.ResolveReference(&url.URL{ - Path: "/mgmt/admins", + Path: "/admin/admins", RawQuery: o.rawQuery(), }) retry: @@ -164,14 +164,14 @@ retry: return body, nil } -// CreateAdmin performs the POST /mgmt/admin request to the CA. +// CreateAdmin performs the POST /admin/admin request to the CA. func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { var retried bool body, err := json.Marshal(req) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admin"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admin"}) retry: resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) if err != nil { @@ -191,10 +191,10 @@ retry: return adm, nil } -// RemoveAdmin performs the DELETE /mgmt/admin/{id} request to the CA. +// RemoveAdmin performs the DELETE /admin/admin/{id} request to the CA. func (c *MgmtClient) RemoveAdmin(id string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -214,14 +214,14 @@ retry: return nil } -// UpdateAdmin performs the PUT /mgmt/admin/{id} request to the CA. +// UpdateAdmin performs the PUT /admin/admin/{id} request to the CA. func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { var retried bool body, err := json.Marshal(uar) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -245,10 +245,10 @@ retry: return adm, nil } -// GetProvisioner performs the GET /mgmt/provisioner/{id} request to the CA. -func (c *MgmtClient) GetProvisioner(id string) (*mgmt.Provisioner, error) { +// GetProvisioner performs the GET /admin/provisioner/{name} request to the CA. +func (c *MgmtClient) GetProvisioner(name string) (*mgmt.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -268,10 +268,10 @@ retry: return prov, nil } -// GetProvisioners performs the GET /mgmt/provisioners request to the CA. +// GetProvisioners performs the GET /admin/provisioners request to the CA. func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioners"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -291,10 +291,10 @@ retry: return *provs, nil } -// RemoveProvisioner performs the DELETE /mgmt/provisioner/{name} request to the CA. +// RemoveProvisioner performs the DELETE /admin/provisioner/{name} request to the CA. func (c *MgmtClient) RemoveProvisioner(name string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", name)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -314,14 +314,14 @@ retry: return nil } -// CreateProvisioner performs the POST /mgmt/provisioner request to the CA. +// CreateProvisioner performs the POST /admin/provisioner request to the CA. func (c *MgmtClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { var retried bool body, err := json.Marshal(req) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioner"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioner"}) retry: resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) if err != nil { @@ -341,14 +341,14 @@ retry: return prov, nil } -// UpdateProvisioner performs the PUT /mgmt/provisioner/{id} request to the CA. +// UpdateProvisioner performs the PUT /admin/provisioner/{id} request to the CA. func (c *MgmtClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { var retried bool body, err := json.Marshal(upr) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", id)}) req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -372,10 +372,10 @@ retry: return prov, nil } -// GetAuthConfig performs the GET /mgmt/authconfig/{id} request to the CA. +// GetAuthConfig performs the GET /admin/authconfig/{id} request to the CA. func (c *MgmtClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/authconfig", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/authconfig", id)}) retry: resp, err := c.client.Get(u.String()) if err != nil { From 5929244fda0324be06a2f61fdfacc3804c496a47 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 20 May 2021 13:12:02 -0700 Subject: [PATCH 011/291] wip --- authority/authority.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index e26ff22d..c564f530 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -201,8 +201,9 @@ func (a *Authority) init() error { } } - // Pull AuthConfig from DB. - if true { + // Initialize step-ca Admin Database if it's not already initialized using + // WithAdminDB. + if a.adminDB == nil { // Check if AuthConfig already exists a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) if err != nil { From d8d5d7332b045bd2d1e4f73315e7dd8b68da8949 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 20 May 2021 16:02:20 -0700 Subject: [PATCH 012/291] wip --- authority/mgmt/api/handler.go | 20 +++---- authority/mgmt/provisioner.go | 48 ++++++++++++++- ca/{mgmtClient.go => adminClient.go} | 90 ++++++++++++++-------------- 3 files changed, 102 insertions(+), 56 deletions(-) rename ca/{mgmtClient.go => adminClient.go} (77%) diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index 11dcbd86..8b6f915b 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -32,20 +32,20 @@ func NewHandler(auth *authority.Authority) api.RouterHandler { // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { // Provisioners - r.MethodFunc("GET", "/provisioner/{name}", h.GetProvisioner) + r.MethodFunc("GET", "/provisioners/{name}", h.GetProvisioner) r.MethodFunc("GET", "/provisioners", h.GetProvisioners) - r.MethodFunc("POST", "/provisioner", h.CreateProvisioner) - r.MethodFunc("PUT", "/provisioner/{name}", h.UpdateProvisioner) - r.MethodFunc("DELETE", "/provisioner/{name}", h.DeleteProvisioner) + r.MethodFunc("POST", "/provisioners", h.CreateProvisioner) + r.MethodFunc("PUT", "/provisioners/{name}", h.UpdateProvisioner) + r.MethodFunc("DELETE", "/provisioners/{name}", h.DeleteProvisioner) // Admins - r.MethodFunc("GET", "/admin/{id}", h.GetAdmin) + r.MethodFunc("GET", "/admins/{id}", h.GetAdmin) r.MethodFunc("GET", "/admins", h.GetAdmins) - r.MethodFunc("POST", "/admin", h.CreateAdmin) - r.MethodFunc("PATCH", "/admin/{id}", h.UpdateAdmin) - r.MethodFunc("DELETE", "/admin/{id}", h.DeleteAdmin) + r.MethodFunc("POST", "/admins", h.CreateAdmin) + r.MethodFunc("PATCH", "/admins/{id}", h.UpdateAdmin) + r.MethodFunc("DELETE", "/admins/{id}", h.DeleteAdmin) // AuthConfig - r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig) - r.MethodFunc("PUT", "/authconfig/{id}", h.UpdateAuthConfig) + r.MethodFunc("GET", "/authconfigs/{id}", h.GetAuthConfig) + r.MethodFunc("PUT", "/authconfigs/{id}", h.UpdateAuthConfig) } diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index ea9d0da5..537b7fdd 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -58,6 +58,20 @@ func WithPassword(pass string) func(*ProvisionerCtx) { } } +type unmarshalProvisioner struct { + ID string `json:"-"` + AuthorityID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Claims *Claims `json:"claims"` + Details json.RawMessage `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` + Status status.Type `json:"status"` +} + // Provisioner type. type Provisioner struct { ID string `json:"-"` @@ -73,6 +87,38 @@ type Provisioner struct { Status status.Type `json:"status"` } +type typ struct { + Type ProvisionerType `json:"type"` +} + +// UnmarshalJSON implements the Unmarshal interface. +func (p *Provisioner) UnmarshalJSON(b []byte) error { + var ( + err error + up = new(unmarshalProvisioner) + ) + if err = json.Unmarshal(b, up); err != nil { + return WrapErrorISE(err, "error unmarshaling provisioner to intermediate type") + } + p.Details, err = UnmarshalProvisionerDetails(up.Details) + if err = json.Unmarshal(b, up); err != nil { + return WrapErrorISE(err, "error unmarshaling provisioner details") + } + + p.ID = up.ID + p.AuthorityID = up.AuthorityID + p.Type = up.Type + p.Name = up.Name + p.Claims = up.Claims + p.X509Template = up.X509Template + p.X509TemplateData = up.X509TemplateData + p.SSHTemplate = up.SSHTemplate + p.SSHTemplateData = up.SSHTemplateData + p.Status = up.Status + + return nil +} + func (p *Provisioner) GetOptions() *provisioner.Options { return &provisioner.Options{ X509: &provisioner.X509Options{ @@ -415,7 +461,7 @@ type detailsType struct { Type ProvisionerType } -func UnmarshalProvisionerDetails(data []byte) (ProvisionerDetails, error) { +func UnmarshalProvisionerDetails(data json.RawMessage) (ProvisionerDetails, error) { dt := new(detailsType) if err := json.Unmarshal(data, dt); err != nil { return nil, WrapErrorISE(err, "error unmarshaling provisioner details") diff --git a/ca/mgmtClient.go b/ca/adminClient.go similarity index 77% rename from ca/mgmtClient.go rename to ca/adminClient.go index 7c4207aa..5fc6a7d4 100644 --- a/ca/mgmtClient.go +++ b/ca/adminClient.go @@ -16,16 +16,16 @@ import ( "github.com/smallstep/certificates/errs" ) -// MgmtClient implements an HTTP client for the CA server. -type MgmtClient struct { +// AdminClient implements an HTTP client for the CA server. +type AdminClient struct { client *uaClient endpoint *url.URL retryFunc RetryFunc opts []ClientOption } -// NewMgmtClient creates a new MgmtClient with the given endpoint and options. -func NewMgmtClient(endpoint string, opts ...ClientOption) (*MgmtClient, error) { +// NewAdminClient creates a new AdminClient with the given endpoint and options. +func NewAdminClient(endpoint string, opts ...ClientOption) (*AdminClient, error) { u, err := parseEndpoint(endpoint) if err != nil { return nil, err @@ -40,7 +40,7 @@ func NewMgmtClient(endpoint string, opts ...ClientOption) (*MgmtClient, error) { return nil, err } - return &MgmtClient{ + return &AdminClient{ client: newClient(tr), endpoint: u, retryFunc: o.retryFunc, @@ -48,7 +48,7 @@ func NewMgmtClient(endpoint string, opts ...ClientOption) (*MgmtClient, error) { }, nil } -func (c *MgmtClient) retryOnError(r *http.Response) bool { +func (c *AdminClient) retryOnError(r *http.Response) bool { if c.retryFunc != nil { if c.retryFunc(r.StatusCode) { o := new(clientOptions) @@ -67,10 +67,10 @@ func (c *MgmtClient) retryOnError(r *http.Response) bool { return false } -// GetAdmin performs the GET /mgmt/admin/{id} request to the CA. -func (c *MgmtClient) GetAdmin(id string) (*mgmt.Admin, error) { +// GetAdmin performs the GET /admin/admin/{id} request to the CA. +func (c *AdminClient) GetAdmin(id string) (*mgmt.Admin, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -81,7 +81,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var adm = new(mgmt.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -135,7 +135,7 @@ func WithAdminLimit(limit int) AdminOption { } // GetAdmins performs the GET /admin/admins request to the CA. -func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { +func (c *AdminClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { var retried bool o := new(adminOptions) if err := o.apply(opts); err != nil { @@ -155,7 +155,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var body = new(mgmtAPI.GetAdminsResponse) if err := readJSON(resp.Body, body); err != nil { @@ -164,14 +164,14 @@ retry: return body, nil } -// CreateAdmin performs the POST /admin/admin request to the CA. -func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { +// CreateAdmin performs the POST /admin/admins request to the CA. +func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { var retried bool body, err := json.Marshal(req) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admin"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admins"}) retry: resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) if err != nil { @@ -182,7 +182,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var adm = new(mgmt.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -191,10 +191,10 @@ retry: return adm, nil } -// RemoveAdmin performs the DELETE /admin/admin/{id} request to the CA. -func (c *MgmtClient) RemoveAdmin(id string) error { +// RemoveAdmin performs the DELETE /admin/admins/{id} request to the CA. +func (c *AdminClient) RemoveAdmin(id string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admins", id)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -209,19 +209,19 @@ retry: retried = true goto retry } - return readMgmtError(resp.Body) + return readAdminError(resp.Body) } return nil } -// UpdateAdmin performs the PUT /admin/admin/{id} request to the CA. -func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { +// UpdateAdmin performs the PUT /admin/admins/{id} request to the CA. +func (c *AdminClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { var retried bool body, err := json.Marshal(uar) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admins", id)}) req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -236,7 +236,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var adm = new(admin.Admin) if err := readJSON(resp.Body, adm); err != nil { @@ -245,10 +245,10 @@ retry: return adm, nil } -// GetProvisioner performs the GET /admin/provisioner/{name} request to the CA. -func (c *MgmtClient) GetProvisioner(name string) (*mgmt.Provisioner, error) { +// GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. +func (c *AdminClient) GetProvisioner(name string) (*mgmt.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", name)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -259,7 +259,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var prov = new(mgmt.Provisioner) if err := readJSON(resp.Body, prov); err != nil { @@ -269,7 +269,7 @@ retry: } // GetProvisioners performs the GET /admin/provisioners request to the CA. -func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) { +func (c *AdminClient) GetProvisioners() ([]*mgmt.Provisioner, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) retry: @@ -282,7 +282,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var provs = new([]*mgmt.Provisioner) if err := readJSON(resp.Body, provs); err != nil { @@ -291,10 +291,10 @@ retry: return *provs, nil } -// RemoveProvisioner performs the DELETE /admin/provisioner/{name} request to the CA. -func (c *MgmtClient) RemoveProvisioner(name string) error { +// RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. +func (c *AdminClient) RemoveProvisioner(name string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", name)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -309,19 +309,19 @@ retry: retried = true goto retry } - return readMgmtError(resp.Body) + return readAdminError(resp.Body) } return nil } -// CreateProvisioner performs the POST /admin/provisioner request to the CA. -func (c *MgmtClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { +// CreateProvisioner performs the POST /admin/provisioners request to the CA. +func (c *AdminClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { var retried bool body, err := json.Marshal(req) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioner"}) + u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) retry: resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) if err != nil { @@ -332,7 +332,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var prov = new(mgmt.Provisioner) if err := readJSON(resp.Body, prov); err != nil { @@ -341,14 +341,14 @@ retry: return prov, nil } -// UpdateProvisioner performs the PUT /admin/provisioner/{id} request to the CA. -func (c *MgmtClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { +// UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. +func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { var retried bool body, err := json.Marshal(upr) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", id)}) req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -363,7 +363,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var prov = new(mgmt.Provisioner) if err := readJSON(resp.Body, prov); err != nil { @@ -373,7 +373,7 @@ retry: } // GetAuthConfig performs the GET /admin/authconfig/{id} request to the CA. -func (c *MgmtClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { +func (c *AdminClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/authconfig", id)}) retry: @@ -386,7 +386,7 @@ retry: retried = true goto retry } - return nil, readMgmtError(resp.Body) + return nil, readAdminError(resp.Body) } var ac = new(mgmt.AuthConfig) if err := readJSON(resp.Body, ac); err != nil { @@ -395,7 +395,7 @@ retry: return ac, nil } -func readMgmtError(r io.ReadCloser) error { +func readAdminError(r io.ReadCloser) error { defer r.Close() mgmtErr := new(mgmt.Error) if err := json.NewDecoder(r).Decode(mgmtErr); err != nil { From 9bfb1c2e7b64ebab8bfd1b2f6fc8a95a646a9585 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 21 May 2021 13:31:41 -0700 Subject: [PATCH 013/291] wip --- authority/admin/collection.go | 42 ++++----- authority/mgmt/api/admin.go | 4 +- authority/mgmt/api/provisioner.go | 46 +++------- authority/mgmt/provisioner.go | 139 +++++++++++++++++++++--------- ca/adminClient.go | 10 +-- 5 files changed, 142 insertions(+), 99 deletions(-) diff --git a/authority/admin/collection.go b/authority/admin/collection.go index 9e8a2926..971bbd9c 100644 --- a/authority/admin/collection.go +++ b/authority/admin/collection.go @@ -32,24 +32,24 @@ func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Collection is a memory map of admins. type Collection struct { - byID *sync.Map - bySubProv *sync.Map - byProv *sync.Map - sorted adminSlice - provisioners *provisioner.Collection - count int - countByProvisioner map[string]int + byID *sync.Map + bySubProv *sync.Map + byProv *sync.Map + sorted adminSlice + provisioners *provisioner.Collection + superCount int + superCountByProvisioner map[string]int } // NewCollection initializes a collection of provisioners. The given list of // audiences are the audiences used by the JWT provisioner. func NewCollection(provisioners *provisioner.Collection) *Collection { return &Collection{ - byID: new(sync.Map), - byProv: new(sync.Map), - bySubProv: new(sync.Map), - countByProvisioner: map[string]int{}, - provisioners: provisioners, + byID: new(sync.Map), + byProv: new(sync.Map), + bySubProv: new(sync.Map), + superCountByProvisioner: map[string]int{}, + provisioners: provisioners, } } @@ -106,12 +106,12 @@ func (c *Collection) Store(adm *Admin) error { if admins, ok := c.LoadByProvisioner(provName); ok { c.byProv.Store(provName, append(admins, adm)) - c.countByProvisioner[provName]++ + c.superCountByProvisioner[provName]++ } else { c.byProv.Store(provName, []*Admin{adm}) - c.countByProvisioner[provName] = 1 + c.superCountByProvisioner[provName] = 1 } - c.count++ + c.superCount++ // Store sorted admins. // Use the first 4 bytes (32bit) of the sum to insert the order @@ -131,14 +131,14 @@ func (c *Collection) Store(adm *Admin) error { return nil } -// Count returns the total number of admins. -func (c *Collection) Count() int { - return c.count +// SuperCount returns the total number of admins. +func (c *Collection) SuperCount() int { + return c.superCount } -// CountByProvisioner returns the total number of admins. -func (c *Collection) CountByProvisioner(provName string) int { - if cnt, ok := c.countByProvisioner[provName]; ok { +// SuperCountByProvisioner returns the total number of admins. +func (c *Collection) SuperCountByProvisioner(provName string) int { + if cnt, ok := c.superCountByProvisioner[provName]; ok { return cnt } return 0 diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index b474e131..04ff2a93 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -120,8 +120,8 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - if h.auth.GetAdminCollection().Count() == 1 { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove last admin")) + if h.auth.GetAdminCollection().SuperCount() == 1 { + api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove the last super admin")) return } diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index d383a27f..3697d6e0 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -101,34 +101,17 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - var body CreateProvisionerRequest - if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(h.auth.GetProvisionerCollection()); err != nil { + var prov = new(mgmt.Provisioner) + if err := api.ReadJSON(r.Body, prov); err != nil { api.WriteError(w, err) return } - details, err := mgmt.UnmarshalProvisionerDetails(body.Details) - if err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error unmarshaling provisioner details")) - return - } + // TODO: validate + + // TODO: fix this + prov.Claims = mgmt.NewDefaultClaims() - claims := mgmt.NewDefaultClaims() - - prov := &mgmt.Provisioner{ - Type: body.Type, - Name: body.Name, - Claims: claims, - Details: details, - X509Template: body.X509Template, - X509TemplateData: body.X509TemplateData, - SSHTemplate: body.SSHTemplate, - SSHTemplateData: body.SSHTemplateData, - } if err := h.db.CreateProvisioner(ctx, prov); err != nil { api.WriteError(w, err) return @@ -144,21 +127,20 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") + p, ok := h.auth.GetProvisionerCollection().LoadByName(name) + if !ok { + api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) + return + } + c := h.auth.GetAdminCollection() - fmt.Printf("c.Count() = %+v\n", c.Count()) - fmt.Printf("c.CountByProvisioner() = %+v\n", c.CountByProvisioner(name)) - if c.Count() == c.CountByProvisioner(name) { + if c.SuperCount() == c.SuperCountByProvisioner(name) { api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, - "cannot remove provisioner %s because no admins will remain", name)) + "cannot remove provisioner %s because no super admins will remain", name)) return } ctx := r.Context() - p, ok := h.auth.GetProvisionerCollection().LoadByName(name) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) - return - } prov, err := h.db.GetProvisioner(ctx, p.GetID()) if err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error loading provisioner %s from db", name)) diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 537b7fdd..493f74b1 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -12,15 +12,6 @@ import ( type ProvisionerOption func(*ProvisionerCtx) -type ProvisionerCtx struct { - JWK *jose.JSONWebKey - JWE *jose.JSONWebEncryption - X509Template, SSHTemplate string - X509TemplateData, SSHTemplateData []byte - Claims *Claims - Password string -} - type ProvisionerType string var ( @@ -35,29 +26,6 @@ var ( ProvisionerTypeX5C = ProvisionerType("X5C") ) -func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { - pc := &ProvisionerCtx{ - Claims: NewDefaultClaims(), - } - for _, o := range opts { - o(pc) - } - return pc -} - -func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) { - return func(ctx *ProvisionerCtx) { - ctx.JWK = jwk - ctx.JWE = jwe - } -} - -func WithPassword(pass string) func(*ProvisionerCtx) { - return func(ctx *ProvisionerCtx) { - ctx.Password = pass - } -} - type unmarshalProvisioner struct { ID string `json:"-"` AuthorityID string `json:"-"` @@ -157,6 +125,38 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro return p, nil } +type ProvisionerCtx struct { + JWK *jose.JSONWebKey + JWE *jose.JSONWebEncryption + X509Template, SSHTemplate string + X509TemplateData, SSHTemplateData []byte + Claims *Claims + Password string +} + +func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { + pc := &ProvisionerCtx{ + Claims: NewDefaultClaims(), + } + for _, o := range opts { + o(pc) + } + return pc +} + +func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) { + return func(ctx *ProvisionerCtx) { + ctx.JWK = jwk + ctx.JWE = jwe + } +} + +func WithPassword(pass string) func(*ProvisionerCtx) { + return func(ctx *ProvisionerCtx) { + ctx.Password = pass + } +} + // ProvisionerDetails is the interface implemented by all provisioner details // attributes. type ProvisionerDetails interface { @@ -172,37 +172,61 @@ type ProvisionerDetailsJWK struct { // ProvisionerDetailsOIDC represents the values required by a OIDC provisioner. type ProvisionerDetailsOIDC struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + ConfigurationEndpoint string `json:"configurationEndpoint"` + Admins []string `json:"admins"` + Domains []string `json:"domains"` + Groups []string `json:"groups"` + ListenAddress string `json:"listenAddress"` + TenantID string `json:"tenantID"` } // ProvisionerDetailsGCP represents the values required by a GCP provisioner. type ProvisionerDetailsGCP struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + ServiceAccounts []string `json:"serviceAccounts"` + ProjectIDs []string `json:"projectIDs"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + InstanceAge string `json:"instanceAge"` } // ProvisionerDetailsAWS represents the values required by a AWS provisioner. type ProvisionerDetailsAWS struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + Accounts []string `json:"accounts"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + InstanceAge string `json:"instanceAge"` } // ProvisionerDetailsAzure represents the values required by a Azure provisioner. type ProvisionerDetailsAzure struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + ResourceGroups []string `json:"resourceGroups"` + Audience string `json:"audience"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` } // ProvisionerDetailsACME represents the values required by a ACME provisioner. type ProvisionerDetailsACME struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + ForceCN bool `json:"forceCN"` } // ProvisionerDetailsX5C represents the values required by a X5C provisioner. type ProvisionerDetailsX5C struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + Roots []byte `json:"roots"` } // ProvisionerDetailsK8SSA represents the values required by a K8SSA provisioner. type ProvisionerDetailsK8SSA struct { - Type ProvisionerType `json:"type"` + Type ProvisionerType `json:"type"` + PublicKeys []byte `json:"publicKeys"` } // ProvisionerDetailsSSHPOP represents the values required by a SSHPOP provisioner. @@ -285,6 +309,42 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { }, nil } +func createACMEDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { + var err error + + if pc.JWK != nil && pc.JWE == nil { + return nil, NewErrorISE("JWE is required with JWK for createJWKProvisioner") + } + if pc.JWE != nil && pc.JWK == nil { + return nil, NewErrorISE("JWK is required with JWE for createJWKProvisioner") + } + if pc.JWK == nil && pc.JWE == nil { + // Create a new JWK w/ encrypted private key. + if pc.Password == "" { + return nil, NewErrorISE("password is required to provisioner with new keys") + } + pc.JWK, pc.JWE, err = jose.GenerateDefaultKeyPair([]byte(pc.Password)) + if err != nil { + return nil, WrapErrorISE(err, "error generating JWK key pair") + } + } + + jwkPubBytes, err := pc.JWK.MarshalJSON() + if err != nil { + return nil, WrapErrorISE(err, "error marshaling JWK") + } + jwePrivStr, err := pc.JWE.CompactSerialize() + if err != nil { + return nil, WrapErrorISE(err, "error serializing JWE") + } + + return &ProvisionerDetailsJWK{ + Type: ProvisionerTypeJWK, + PublicKey: jwkPubBytes, + PrivateKey: jwePrivStr, + }, nil +} + // ToCertificates converts the landlord provisioner type to the open source // provisioner type. func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { @@ -461,6 +521,7 @@ type detailsType struct { Type ProvisionerType } +// UnmarshalProvisionerDetails unmarshals bytes into the proper details type. func UnmarshalProvisionerDetails(data json.RawMessage) (ProvisionerDetails, error) { dt := new(detailsType) if err := json.Unmarshal(data, dt); err != nil { diff --git a/ca/adminClient.go b/ca/adminClient.go index 5fc6a7d4..e8374a98 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -315,9 +315,9 @@ retry: } // CreateProvisioner performs the POST /admin/provisioners request to the CA. -func (c *AdminClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { +func (c *AdminClient) CreateProvisioner(prov *mgmt.Provisioner) (*mgmt.Provisioner, error) { var retried bool - body, err := json.Marshal(req) + body, err := json.Marshal(prov) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } @@ -334,11 +334,11 @@ retry: } return nil, readAdminError(resp.Body) } - var prov = new(mgmt.Provisioner) - if err := readJSON(resp.Body, prov); err != nil { + var nuProv = new(mgmt.Provisioner) + if err := readJSON(resp.Body, nuProv); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } - return prov, nil + return nuProv, nil } // UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. From 64ce4e5c91e19bb1e2aeb151d21a8646d4be0e05 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 24 May 2021 12:14:10 -0700 Subject: [PATCH 014/291] Add and generate majordomo protos. --- majordomo/doc.go | 3 + majordomo/majordomo.pb.go | 1094 +++++++++++++++++++ majordomo/majordomo.proto | 102 ++ majordomo/majordomo_grpc.pb.go | 519 +++++++++ majordomo/provisioners.pb.go | 1875 ++++++++++++++++++++++++++++++++ majordomo/provisioners.proto | 137 +++ 6 files changed, 3730 insertions(+) create mode 100644 majordomo/doc.go create mode 100644 majordomo/majordomo.pb.go create mode 100644 majordomo/majordomo.proto create mode 100644 majordomo/majordomo_grpc.pb.go create mode 100644 majordomo/provisioners.pb.go create mode 100644 majordomo/provisioners.proto diff --git a/majordomo/doc.go b/majordomo/doc.go new file mode 100644 index 00000000..ef9fce3a --- /dev/null +++ b/majordomo/doc.go @@ -0,0 +1,3 @@ +package majordomo + +//go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative majordomo/provisioners.proto majordomo/majordomo.proto diff --git a/majordomo/majordomo.pb.go b/majordomo/majordomo.pb.go new file mode 100644 index 00000000..bce189df --- /dev/null +++ b/majordomo/majordomo.pb.go @@ -0,0 +1,1094 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.8 +// source: majordomo/majordomo.proto + +package majordomo + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TODO struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *TODO) Reset() { + *x = TODO{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TODO) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TODO) ProtoMessage() {} + +func (x *TODO) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TODO.ProtoReflect.Descriptor instead. +func (*TODO) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{0} +} + +type LoginRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AuthorityId string `protobuf:"bytes,1,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` + PemCertificateRequest string `protobuf:"bytes,4,opt,name=pem_certificate_request,json=pemCertificateRequest,proto3" json:"pem_certificate_request,omitempty"` +} + +func (x *LoginRequest) Reset() { + *x = LoginRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LoginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginRequest) ProtoMessage() {} + +func (x *LoginRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. +func (*LoginRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{1} +} + +func (x *LoginRequest) GetAuthorityId() string { + if x != nil { + return x.AuthorityId + } + return "" +} + +func (x *LoginRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *LoginRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *LoginRequest) GetPemCertificateRequest() string { + if x != nil { + return x.PemCertificateRequest + } + return "" +} + +type LoginResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PemCertificate string `protobuf:"bytes,1,opt,name=pem_certificate,json=pemCertificate,proto3" json:"pem_certificate,omitempty"` + PemCertificateChain string `protobuf:"bytes,2,opt,name=pem_certificate_chain,json=pemCertificateChain,proto3" json:"pem_certificate_chain,omitempty"` +} + +func (x *LoginResponse) Reset() { + *x = LoginResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LoginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginResponse) ProtoMessage() {} + +func (x *LoginResponse) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. +func (*LoginResponse) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{2} +} + +func (x *LoginResponse) GetPemCertificate() string { + if x != nil { + return x.PemCertificate + } + return "" +} + +func (x *LoginResponse) GetPemCertificateChain() string { + if x != nil { + return x.PemCertificateChain + } + return "" +} + +type ConfigurationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ConfigurationRequest) Reset() { + *x = ConfigurationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationRequest) ProtoMessage() {} + +func (x *ConfigurationRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationRequest.ProtoReflect.Descriptor instead. +func (*ConfigurationRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{3} +} + +type ConfigurationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Provisioners []*Provisioner `protobuf:"bytes,1,rep,name=provisioners,proto3" json:"provisioners,omitempty"` + Admins []*Administrator `protobuf:"bytes,2,rep,name=admins,proto3" json:"admins,omitempty"` +} + +func (x *ConfigurationResponse) Reset() { + *x = ConfigurationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationResponse) ProtoMessage() {} + +func (x *ConfigurationResponse) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationResponse.ProtoReflect.Descriptor instead. +func (*ConfigurationResponse) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{4} +} + +func (x *ConfigurationResponse) GetProvisioners() []*Provisioner { + if x != nil { + return x.Provisioners + } + return nil +} + +func (x *ConfigurationResponse) GetAdmins() []*Administrator { + if x != nil { + return x.Admins + } + return nil +} + +type CreateProvisionerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type Provisioner_Type `protobuf:"varint,1,opt,name=type,proto3,enum=majordomo.Provisioner_Type" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Details *ProvisionerDetails `protobuf:"bytes,3,opt,name=details,proto3" json:"details,omitempty"` + Claims *Claims `protobuf:"bytes,4,opt,name=claims,proto3" json:"claims,omitempty"` +} + +func (x *CreateProvisionerRequest) Reset() { + *x = CreateProvisionerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateProvisionerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateProvisionerRequest) ProtoMessage() {} + +func (x *CreateProvisionerRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateProvisionerRequest.ProtoReflect.Descriptor instead. +func (*CreateProvisionerRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateProvisionerRequest) GetType() Provisioner_Type { + if x != nil { + return x.Type + } + return Provisioner_NOOP +} + +func (x *CreateProvisionerRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateProvisionerRequest) GetDetails() *ProvisionerDetails { + if x != nil { + return x.Details + } + return nil +} + +func (x *CreateProvisionerRequest) GetClaims() *Claims { + if x != nil { + return x.Claims + } + return nil +} + +type DeleteProvisionerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteProvisionerRequest) Reset() { + *x = DeleteProvisionerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteProvisionerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteProvisionerRequest) ProtoMessage() {} + +func (x *DeleteProvisionerRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteProvisionerRequest.ProtoReflect.Descriptor instead. +func (*DeleteProvisionerRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteProvisionerRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type CreateAdministratorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ProvisionerId string `protobuf:"bytes,2,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` + Type Administrator_Type `protobuf:"varint,3,opt,name=type,proto3,enum=majordomo.Administrator_Type" json:"type,omitempty"` +} + +func (x *CreateAdministratorRequest) Reset() { + *x = CreateAdministratorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateAdministratorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdministratorRequest) ProtoMessage() {} + +func (x *CreateAdministratorRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateAdministratorRequest.ProtoReflect.Descriptor instead. +func (*CreateAdministratorRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{7} +} + +func (x *CreateAdministratorRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateAdministratorRequest) GetProvisionerId() string { + if x != nil { + return x.ProvisionerId + } + return "" +} + +func (x *CreateAdministratorRequest) GetType() Administrator_Type { + if x != nil { + return x.Type + } + return Administrator_UNKNOWN +} + +type DeleteAdministratorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteAdministratorRequest) Reset() { + *x = DeleteAdministratorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAdministratorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdministratorRequest) ProtoMessage() {} + +func (x *DeleteAdministratorRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAdministratorRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdministratorRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteAdministratorRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type CertificateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PemCertificate string `protobuf:"bytes,1,opt,name=pem_certificate,json=pemCertificate,proto3" json:"pem_certificate,omitempty"` + PemCertificateChain string `protobuf:"bytes,2,opt,name=pem_certificate_chain,json=pemCertificateChain,proto3" json:"pem_certificate_chain,omitempty"` +} + +func (x *CertificateRequest) Reset() { + *x = CertificateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CertificateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CertificateRequest) ProtoMessage() {} + +func (x *CertificateRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CertificateRequest.ProtoReflect.Descriptor instead. +func (*CertificateRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{9} +} + +func (x *CertificateRequest) GetPemCertificate() string { + if x != nil { + return x.PemCertificate + } + return "" +} + +func (x *CertificateRequest) GetPemCertificateChain() string { + if x != nil { + return x.PemCertificateChain + } + return "" +} + +type CertificateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *CertificateResponse) Reset() { + *x = CertificateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CertificateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CertificateResponse) ProtoMessage() {} + +func (x *CertificateResponse) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CertificateResponse.ProtoReflect.Descriptor instead. +func (*CertificateResponse) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{10} +} + +func (x *CertificateResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type SSHCertificateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Certificate string `protobuf:"bytes,1,opt,name=certificate,proto3" json:"certificate,omitempty"` +} + +func (x *SSHCertificateRequest) Reset() { + *x = SSHCertificateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHCertificateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHCertificateRequest) ProtoMessage() {} + +func (x *SSHCertificateRequest) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SSHCertificateRequest.ProtoReflect.Descriptor instead. +func (*SSHCertificateRequest) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{11} +} + +func (x *SSHCertificateRequest) GetCertificate() string { + if x != nil { + return x.Certificate + } + return "" +} + +type SSHCertificateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *SSHCertificateResponse) Reset() { + *x = SSHCertificateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_majordomo_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHCertificateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHCertificateResponse) ProtoMessage() {} + +func (x *SSHCertificateResponse) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_majordomo_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SSHCertificateResponse.ProtoReflect.Descriptor instead. +func (*SSHCertificateResponse) Descriptor() ([]byte, []int) { + return file_majordomo_majordomo_proto_rawDescGZIP(), []int{12} +} + +func (x *SSHCertificateResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +var File_majordomo_majordomo_proto protoreflect.FileDescriptor + +var file_majordomo_majordomo_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2f, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6d, 0x61, 0x6a, + 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x1a, 0x1c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, + 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x06, 0x0a, 0x04, 0x54, 0x4f, 0x44, 0x4f, 0x22, 0xa1, 0x01, 0x0a, + 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x65, 0x6d, 0x5f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x70, 0x65, 0x6d, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x6c, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x6d, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, + 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x65, 0x6d, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x16, + 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x85, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, + 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x0c, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x06, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, + 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x22, 0xc3, + 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x37, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x73, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x8a, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x0a, + 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x12, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x6d, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, + 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x65, 0x6d, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x25, + 0x0a, 0x13, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x15, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, + 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x22, 0x28, 0x0a, 0x16, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xec, 0x06, 0x0a, 0x09, 0x4d, + 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x12, 0x3a, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x12, 0x17, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, + 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x13, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x6d, + 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x11, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x23, + 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x13, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x12, 0x25, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, + 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x25, 0x2e, 0x6d, 0x61, 0x6a, + 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, + 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, + 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x50, + 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, + 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, + 0x12, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, + 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, + 0x6f, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, + 0x6b, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x2e, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x1a, 0x0f, + 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x12, + 0x38, 0x0a, 0x14, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, + 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x1a, 0x0f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, + 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6d, + 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_majordomo_majordomo_proto_rawDescOnce sync.Once + file_majordomo_majordomo_proto_rawDescData = file_majordomo_majordomo_proto_rawDesc +) + +func file_majordomo_majordomo_proto_rawDescGZIP() []byte { + file_majordomo_majordomo_proto_rawDescOnce.Do(func() { + file_majordomo_majordomo_proto_rawDescData = protoimpl.X.CompressGZIP(file_majordomo_majordomo_proto_rawDescData) + }) + return file_majordomo_majordomo_proto_rawDescData +} + +var file_majordomo_majordomo_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_majordomo_majordomo_proto_goTypes = []interface{}{ + (*TODO)(nil), // 0: majordomo.TODO + (*LoginRequest)(nil), // 1: majordomo.LoginRequest + (*LoginResponse)(nil), // 2: majordomo.LoginResponse + (*ConfigurationRequest)(nil), // 3: majordomo.ConfigurationRequest + (*ConfigurationResponse)(nil), // 4: majordomo.ConfigurationResponse + (*CreateProvisionerRequest)(nil), // 5: majordomo.CreateProvisionerRequest + (*DeleteProvisionerRequest)(nil), // 6: majordomo.DeleteProvisionerRequest + (*CreateAdministratorRequest)(nil), // 7: majordomo.CreateAdministratorRequest + (*DeleteAdministratorRequest)(nil), // 8: majordomo.DeleteAdministratorRequest + (*CertificateRequest)(nil), // 9: majordomo.CertificateRequest + (*CertificateResponse)(nil), // 10: majordomo.CertificateResponse + (*SSHCertificateRequest)(nil), // 11: majordomo.SSHCertificateRequest + (*SSHCertificateResponse)(nil), // 12: majordomo.SSHCertificateResponse + (*Provisioner)(nil), // 13: majordomo.Provisioner + (*Administrator)(nil), // 14: majordomo.Administrator + (Provisioner_Type)(0), // 15: majordomo.Provisioner.Type + (*ProvisionerDetails)(nil), // 16: majordomo.ProvisionerDetails + (*Claims)(nil), // 17: majordomo.Claims + (Administrator_Type)(0), // 18: majordomo.Administrator.Type +} +var file_majordomo_majordomo_proto_depIdxs = []int32{ + 13, // 0: majordomo.ConfigurationResponse.provisioners:type_name -> majordomo.Provisioner + 14, // 1: majordomo.ConfigurationResponse.admins:type_name -> majordomo.Administrator + 15, // 2: majordomo.CreateProvisionerRequest.type:type_name -> majordomo.Provisioner.Type + 16, // 3: majordomo.CreateProvisionerRequest.details:type_name -> majordomo.ProvisionerDetails + 17, // 4: majordomo.CreateProvisionerRequest.claims:type_name -> majordomo.Claims + 18, // 5: majordomo.CreateAdministratorRequest.type:type_name -> majordomo.Administrator.Type + 1, // 6: majordomo.Majordomo.Login:input_type -> majordomo.LoginRequest + 3, // 7: majordomo.Majordomo.GetConfiguration:input_type -> majordomo.ConfigurationRequest + 3, // 8: majordomo.Majordomo.StreamConfiguration:input_type -> majordomo.ConfigurationRequest + 5, // 9: majordomo.Majordomo.CreateProvisioner:input_type -> majordomo.CreateProvisionerRequest + 6, // 10: majordomo.Majordomo.DeleteProvisioner:input_type -> majordomo.DeleteProvisionerRequest + 7, // 11: majordomo.Majordomo.CreateAdministrator:input_type -> majordomo.CreateAdministratorRequest + 8, // 12: majordomo.Majordomo.DeleteAdministrator:input_type -> majordomo.DeleteAdministratorRequest + 9, // 13: majordomo.Majordomo.PostCertificate:input_type -> majordomo.CertificateRequest + 11, // 14: majordomo.Majordomo.PostSSHCertificate:input_type -> majordomo.SSHCertificateRequest + 0, // 15: majordomo.Majordomo.RevokeCertificate:input_type -> majordomo.TODO + 0, // 16: majordomo.Majordomo.RevokeSSHCertificate:input_type -> majordomo.TODO + 2, // 17: majordomo.Majordomo.Login:output_type -> majordomo.LoginResponse + 4, // 18: majordomo.Majordomo.GetConfiguration:output_type -> majordomo.ConfigurationResponse + 4, // 19: majordomo.Majordomo.StreamConfiguration:output_type -> majordomo.ConfigurationResponse + 13, // 20: majordomo.Majordomo.CreateProvisioner:output_type -> majordomo.Provisioner + 13, // 21: majordomo.Majordomo.DeleteProvisioner:output_type -> majordomo.Provisioner + 14, // 22: majordomo.Majordomo.CreateAdministrator:output_type -> majordomo.Administrator + 14, // 23: majordomo.Majordomo.DeleteAdministrator:output_type -> majordomo.Administrator + 10, // 24: majordomo.Majordomo.PostCertificate:output_type -> majordomo.CertificateResponse + 12, // 25: majordomo.Majordomo.PostSSHCertificate:output_type -> majordomo.SSHCertificateResponse + 0, // 26: majordomo.Majordomo.RevokeCertificate:output_type -> majordomo.TODO + 0, // 27: majordomo.Majordomo.RevokeSSHCertificate:output_type -> majordomo.TODO + 17, // [17:28] is the sub-list for method output_type + 6, // [6:17] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_majordomo_majordomo_proto_init() } +func file_majordomo_majordomo_proto_init() { + if File_majordomo_majordomo_proto != nil { + return + } + file_majordomo_provisioners_proto_init() + if !protoimpl.UnsafeEnabled { + file_majordomo_majordomo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TODO); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigurationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigurationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateProvisionerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteProvisionerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateAdministratorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteAdministratorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CertificateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CertificateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SSHCertificateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_majordomo_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SSHCertificateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_majordomo_majordomo_proto_rawDesc, + NumEnums: 0, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_majordomo_majordomo_proto_goTypes, + DependencyIndexes: file_majordomo_majordomo_proto_depIdxs, + MessageInfos: file_majordomo_majordomo_proto_msgTypes, + }.Build() + File_majordomo_majordomo_proto = out.File + file_majordomo_majordomo_proto_rawDesc = nil + file_majordomo_majordomo_proto_goTypes = nil + file_majordomo_majordomo_proto_depIdxs = nil +} diff --git a/majordomo/majordomo.proto b/majordomo/majordomo.proto new file mode 100644 index 00000000..bfdf0047 --- /dev/null +++ b/majordomo/majordomo.proto @@ -0,0 +1,102 @@ +syntax = "proto3"; + +package majordomo; + +option go_package = "github.com/smallstep/certificates/majordomo"; + +import "majordomo/provisioners.proto"; + +// Majordomo is the public service used to sync configurations to CA's and post +// certificates. +service Majordomo { + // Login creates signs a given CSR and returns the certificate that will be + // used for authentication. + rpc Login(LoginRequest) returns (LoginResponse); + + // GetConfiguration returns the full configuration of an authority. + rpc GetConfiguration(ConfigurationRequest) returns (ConfigurationResponse); + // StreamConfiguration streams the full configuration of an authority. This + // method is not yet supported. + rpc StreamConfiguration(ConfigurationRequest) returns (stream ConfigurationResponse); + + // CreateProvisioner adds a new provisioner to the majordomo authority and + // returns the proto representation. + rpc CreateProvisioner(CreateProvisionerRequest) returns (Provisioner); + // DeleteProvisioner deletes a previously created provisioner. + rpc DeleteProvisioner(DeleteProvisionerRequest) returns (Provisioner); + + // CreateAdministrator adds a new admin user to the majordomo authority. + // Admin users can add or delete provisioners. + rpc CreateAdministrator(CreateAdministratorRequest) returns (Administrator); + // DeleteAdministrator deletes a previously created admin user. + rpc DeleteAdministrator(DeleteAdministratorRequest) returns (Administrator); + + // PostCertificate sends a signed X.509 certificate to majordomo. + rpc PostCertificate(CertificateRequest) returns (CertificateResponse); + // PostSSHCertificate sends a signed SSH certificate to majordomo. + rpc PostSSHCertificate(SSHCertificateRequest) returns (SSHCertificateResponse); + // RevokeCertificate marks an X.509 certificate as revoked. + rpc RevokeCertificate(TODO) returns (TODO); + // RevokeSSHCertificate marks an SSH certificate as revoked. + rpc RevokeSSHCertificate(TODO) returns (TODO); +} + +message TODO {} + +message LoginRequest { + string authority_id = 1; + string username = 2; + string password = 3; + string pem_certificate_request = 4; +} + +message LoginResponse { + string pem_certificate = 1; + string pem_certificate_chain = 2; +} + +message ConfigurationRequest { + // todo +} + +message ConfigurationResponse { + repeated Provisioner provisioners = 1; + repeated Administrator admins = 2; +} + +message CreateProvisionerRequest { + Provisioner.Type type = 1; + string name = 2; + ProvisionerDetails details = 3; + Claims claims = 4; +} + +message DeleteProvisionerRequest { + string id = 1; +} + +message CreateAdministratorRequest { + string name = 1; + string provisioner_id = 2; + Administrator.Type type = 3; +} + +message DeleteAdministratorRequest { + string id = 1; +} +message CertificateRequest { + string pem_certificate = 1; + string pem_certificate_chain = 2; +} + +message CertificateResponse { + string id = 1; +} + +message SSHCertificateRequest { + string certificate = 1; +} + +message SSHCertificateResponse { + string id = 1; +} diff --git a/majordomo/majordomo_grpc.pb.go b/majordomo/majordomo_grpc.pb.go new file mode 100644 index 00000000..44fd648d --- /dev/null +++ b/majordomo/majordomo_grpc.pb.go @@ -0,0 +1,519 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package majordomo + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// MajordomoClient is the client API for Majordomo service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type MajordomoClient interface { + // Login creates signs a given CSR and returns the certificate that will be + // used for authentication. + Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) + // GetConfiguration returns the full configuration of an authority. + GetConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (*ConfigurationResponse, error) + // StreamConfiguration streams the full configuration of an authority. This + // method is not yet supported. + StreamConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (Majordomo_StreamConfigurationClient, error) + // CreateProvisioner adds a new provisioner to the majordomo authority and + // returns the proto representation. + CreateProvisioner(ctx context.Context, in *CreateProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) + // DeleteProvisioner deletes a previously created provisioner. + DeleteProvisioner(ctx context.Context, in *DeleteProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) + // CreateAdministrator adds a new admin user to the majordomo authority. + // Admin users can add or delete provisioners. + CreateAdministrator(ctx context.Context, in *CreateAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) + // DeleteAdministrator deletes a previously created admin user. + DeleteAdministrator(ctx context.Context, in *DeleteAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) + // PostCertificate sends a signed X.509 certificate to majordomo. + PostCertificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) + // PostSSHCertificate sends a signed SSH certificate to majordomo. + PostSSHCertificate(ctx context.Context, in *SSHCertificateRequest, opts ...grpc.CallOption) (*SSHCertificateResponse, error) + // RevokeCertificate marks an X.509 certificate as revoked. + RevokeCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) + // RevokeSSHCertificate marks an SSH certificate as revoked. + RevokeSSHCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) +} + +type majordomoClient struct { + cc grpc.ClientConnInterface +} + +func NewMajordomoClient(cc grpc.ClientConnInterface) MajordomoClient { + return &majordomoClient{cc} +} + +func (c *majordomoClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { + out := new(LoginResponse) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/Login", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) GetConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (*ConfigurationResponse, error) { + out := new(ConfigurationResponse) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/GetConfiguration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) StreamConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (Majordomo_StreamConfigurationClient, error) { + stream, err := c.cc.NewStream(ctx, &Majordomo_ServiceDesc.Streams[0], "/majordomo.Majordomo/StreamConfiguration", opts...) + if err != nil { + return nil, err + } + x := &majordomoStreamConfigurationClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Majordomo_StreamConfigurationClient interface { + Recv() (*ConfigurationResponse, error) + grpc.ClientStream +} + +type majordomoStreamConfigurationClient struct { + grpc.ClientStream +} + +func (x *majordomoStreamConfigurationClient) Recv() (*ConfigurationResponse, error) { + m := new(ConfigurationResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *majordomoClient) CreateProvisioner(ctx context.Context, in *CreateProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) { + out := new(Provisioner) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/CreateProvisioner", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) DeleteProvisioner(ctx context.Context, in *DeleteProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) { + out := new(Provisioner) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/DeleteProvisioner", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) CreateAdministrator(ctx context.Context, in *CreateAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) { + out := new(Administrator) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/CreateAdministrator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) DeleteAdministrator(ctx context.Context, in *DeleteAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) { + out := new(Administrator) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/DeleteAdministrator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) PostCertificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) { + out := new(CertificateResponse) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/PostCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) PostSSHCertificate(ctx context.Context, in *SSHCertificateRequest, opts ...grpc.CallOption) (*SSHCertificateResponse, error) { + out := new(SSHCertificateResponse) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/PostSSHCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) RevokeCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) { + out := new(TODO) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/RevokeCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *majordomoClient) RevokeSSHCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) { + out := new(TODO) + err := c.cc.Invoke(ctx, "/majordomo.Majordomo/RevokeSSHCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MajordomoServer is the server API for Majordomo service. +// All implementations must embed UnimplementedMajordomoServer +// for forward compatibility +type MajordomoServer interface { + // Login creates signs a given CSR and returns the certificate that will be + // used for authentication. + Login(context.Context, *LoginRequest) (*LoginResponse, error) + // GetConfiguration returns the full configuration of an authority. + GetConfiguration(context.Context, *ConfigurationRequest) (*ConfigurationResponse, error) + // StreamConfiguration streams the full configuration of an authority. This + // method is not yet supported. + StreamConfiguration(*ConfigurationRequest, Majordomo_StreamConfigurationServer) error + // CreateProvisioner adds a new provisioner to the majordomo authority and + // returns the proto representation. + CreateProvisioner(context.Context, *CreateProvisionerRequest) (*Provisioner, error) + // DeleteProvisioner deletes a previously created provisioner. + DeleteProvisioner(context.Context, *DeleteProvisionerRequest) (*Provisioner, error) + // CreateAdministrator adds a new admin user to the majordomo authority. + // Admin users can add or delete provisioners. + CreateAdministrator(context.Context, *CreateAdministratorRequest) (*Administrator, error) + // DeleteAdministrator deletes a previously created admin user. + DeleteAdministrator(context.Context, *DeleteAdministratorRequest) (*Administrator, error) + // PostCertificate sends a signed X.509 certificate to majordomo. + PostCertificate(context.Context, *CertificateRequest) (*CertificateResponse, error) + // PostSSHCertificate sends a signed SSH certificate to majordomo. + PostSSHCertificate(context.Context, *SSHCertificateRequest) (*SSHCertificateResponse, error) + // RevokeCertificate marks an X.509 certificate as revoked. + RevokeCertificate(context.Context, *TODO) (*TODO, error) + // RevokeSSHCertificate marks an SSH certificate as revoked. + RevokeSSHCertificate(context.Context, *TODO) (*TODO, error) + mustEmbedUnimplementedMajordomoServer() +} + +// UnimplementedMajordomoServer must be embedded to have forward compatible implementations. +type UnimplementedMajordomoServer struct { +} + +func (UnimplementedMajordomoServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") +} +func (UnimplementedMajordomoServer) GetConfiguration(context.Context, *ConfigurationRequest) (*ConfigurationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetConfiguration not implemented") +} +func (UnimplementedMajordomoServer) StreamConfiguration(*ConfigurationRequest, Majordomo_StreamConfigurationServer) error { + return status.Errorf(codes.Unimplemented, "method StreamConfiguration not implemented") +} +func (UnimplementedMajordomoServer) CreateProvisioner(context.Context, *CreateProvisionerRequest) (*Provisioner, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateProvisioner not implemented") +} +func (UnimplementedMajordomoServer) DeleteProvisioner(context.Context, *DeleteProvisionerRequest) (*Provisioner, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteProvisioner not implemented") +} +func (UnimplementedMajordomoServer) CreateAdministrator(context.Context, *CreateAdministratorRequest) (*Administrator, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAdministrator not implemented") +} +func (UnimplementedMajordomoServer) DeleteAdministrator(context.Context, *DeleteAdministratorRequest) (*Administrator, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteAdministrator not implemented") +} +func (UnimplementedMajordomoServer) PostCertificate(context.Context, *CertificateRequest) (*CertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PostCertificate not implemented") +} +func (UnimplementedMajordomoServer) PostSSHCertificate(context.Context, *SSHCertificateRequest) (*SSHCertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PostSSHCertificate not implemented") +} +func (UnimplementedMajordomoServer) RevokeCertificate(context.Context, *TODO) (*TODO, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevokeCertificate not implemented") +} +func (UnimplementedMajordomoServer) RevokeSSHCertificate(context.Context, *TODO) (*TODO, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevokeSSHCertificate not implemented") +} +func (UnimplementedMajordomoServer) mustEmbedUnimplementedMajordomoServer() {} + +// UnsafeMajordomoServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to MajordomoServer will +// result in compilation errors. +type UnsafeMajordomoServer interface { + mustEmbedUnimplementedMajordomoServer() +} + +func RegisterMajordomoServer(s grpc.ServiceRegistrar, srv MajordomoServer) { + s.RegisterService(&Majordomo_ServiceDesc, srv) +} + +func _Majordomo_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoginRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).Login(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/Login", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).Login(ctx, req.(*LoginRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_GetConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigurationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).GetConfiguration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/GetConfiguration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).GetConfiguration(ctx, req.(*ConfigurationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_StreamConfiguration_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ConfigurationRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(MajordomoServer).StreamConfiguration(m, &majordomoStreamConfigurationServer{stream}) +} + +type Majordomo_StreamConfigurationServer interface { + Send(*ConfigurationResponse) error + grpc.ServerStream +} + +type majordomoStreamConfigurationServer struct { + grpc.ServerStream +} + +func (x *majordomoStreamConfigurationServer) Send(m *ConfigurationResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _Majordomo_CreateProvisioner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateProvisionerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).CreateProvisioner(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/CreateProvisioner", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).CreateProvisioner(ctx, req.(*CreateProvisionerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_DeleteProvisioner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteProvisionerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).DeleteProvisioner(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/DeleteProvisioner", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).DeleteProvisioner(ctx, req.(*DeleteProvisionerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_CreateAdministrator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdministratorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).CreateAdministrator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/CreateAdministrator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).CreateAdministrator(ctx, req.(*CreateAdministratorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_DeleteAdministrator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdministratorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).DeleteAdministrator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/DeleteAdministrator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).DeleteAdministrator(ctx, req.(*DeleteAdministratorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_PostCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CertificateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).PostCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/PostCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).PostCertificate(ctx, req.(*CertificateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_PostSSHCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SSHCertificateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).PostSSHCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/PostSSHCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).PostSSHCertificate(ctx, req.(*SSHCertificateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_RevokeCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TODO) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).RevokeCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/RevokeCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).RevokeCertificate(ctx, req.(*TODO)) + } + return interceptor(ctx, in, info, handler) +} + +func _Majordomo_RevokeSSHCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TODO) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MajordomoServer).RevokeSSHCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/majordomo.Majordomo/RevokeSSHCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MajordomoServer).RevokeSSHCertificate(ctx, req.(*TODO)) + } + return interceptor(ctx, in, info, handler) +} + +// Majordomo_ServiceDesc is the grpc.ServiceDesc for Majordomo service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Majordomo_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "majordomo.Majordomo", + HandlerType: (*MajordomoServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Login", + Handler: _Majordomo_Login_Handler, + }, + { + MethodName: "GetConfiguration", + Handler: _Majordomo_GetConfiguration_Handler, + }, + { + MethodName: "CreateProvisioner", + Handler: _Majordomo_CreateProvisioner_Handler, + }, + { + MethodName: "DeleteProvisioner", + Handler: _Majordomo_DeleteProvisioner_Handler, + }, + { + MethodName: "CreateAdministrator", + Handler: _Majordomo_CreateAdministrator_Handler, + }, + { + MethodName: "DeleteAdministrator", + Handler: _Majordomo_DeleteAdministrator_Handler, + }, + { + MethodName: "PostCertificate", + Handler: _Majordomo_PostCertificate_Handler, + }, + { + MethodName: "PostSSHCertificate", + Handler: _Majordomo_PostSSHCertificate_Handler, + }, + { + MethodName: "RevokeCertificate", + Handler: _Majordomo_RevokeCertificate_Handler, + }, + { + MethodName: "RevokeSSHCertificate", + Handler: _Majordomo_RevokeSSHCertificate_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamConfiguration", + Handler: _Majordomo_StreamConfiguration_Handler, + ServerStreams: true, + }, + }, + Metadata: "majordomo/majordomo.proto", +} diff --git a/majordomo/provisioners.pb.go b/majordomo/provisioners.pb.go new file mode 100644 index 00000000..ccdd6f2f --- /dev/null +++ b/majordomo/provisioners.pb.go @@ -0,0 +1,1875 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.8 +// source: majordomo/provisioners.proto + +package majordomo + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Administrator_Type int32 + +const ( + Administrator_UNKNOWN Administrator_Type = 0 + Administrator_ADMIN Administrator_Type = 1 + Administrator_SUPER_ADMIN Administrator_Type = 2 +) + +// Enum value maps for Administrator_Type. +var ( + Administrator_Type_name = map[int32]string{ + 0: "UNKNOWN", + 1: "ADMIN", + 2: "SUPER_ADMIN", + } + Administrator_Type_value = map[string]int32{ + "UNKNOWN": 0, + "ADMIN": 1, + "SUPER_ADMIN": 2, + } +) + +func (x Administrator_Type) Enum() *Administrator_Type { + p := new(Administrator_Type) + *p = x + return p +} + +func (x Administrator_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Administrator_Type) Descriptor() protoreflect.EnumDescriptor { + return file_majordomo_provisioners_proto_enumTypes[0].Descriptor() +} + +func (Administrator_Type) Type() protoreflect.EnumType { + return &file_majordomo_provisioners_proto_enumTypes[0] +} + +func (x Administrator_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Administrator_Type.Descriptor instead. +func (Administrator_Type) EnumDescriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{0, 0} +} + +type Provisioner_Type int32 + +const ( + Provisioner_NOOP Provisioner_Type = 0 + Provisioner_JWK Provisioner_Type = 1 + Provisioner_OIDC Provisioner_Type = 2 + Provisioner_GCP Provisioner_Type = 3 + Provisioner_AWS Provisioner_Type = 4 + Provisioner_AZURE Provisioner_Type = 5 + Provisioner_ACME Provisioner_Type = 6 + Provisioner_X5C Provisioner_Type = 7 + Provisioner_K8SSA Provisioner_Type = 8 + Provisioner_SSHPOP Provisioner_Type = 9 +) + +// Enum value maps for Provisioner_Type. +var ( + Provisioner_Type_name = map[int32]string{ + 0: "NOOP", + 1: "JWK", + 2: "OIDC", + 3: "GCP", + 4: "AWS", + 5: "AZURE", + 6: "ACME", + 7: "X5C", + 8: "K8SSA", + 9: "SSHPOP", + } + Provisioner_Type_value = map[string]int32{ + "NOOP": 0, + "JWK": 1, + "OIDC": 2, + "GCP": 3, + "AWS": 4, + "AZURE": 5, + "ACME": 6, + "X5C": 7, + "K8SSA": 8, + "SSHPOP": 9, + } +) + +func (x Provisioner_Type) Enum() *Provisioner_Type { + p := new(Provisioner_Type) + *p = x + return p +} + +func (x Provisioner_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Provisioner_Type) Descriptor() protoreflect.EnumDescriptor { + return file_majordomo_provisioners_proto_enumTypes[1].Descriptor() +} + +func (Provisioner_Type) Type() protoreflect.EnumType { + return &file_majordomo_provisioners_proto_enumTypes[1] +} + +func (x Provisioner_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Provisioner_Type.Descriptor instead. +func (Provisioner_Type) EnumDescriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{1, 0} +} + +type Administrator struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` + Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + ProvisionerId string `protobuf:"bytes,4,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` + Type Administrator_Type `protobuf:"varint,5,opt,name=type,proto3,enum=majordomo.Administrator_Type" json:"type,omitempty"` +} + +func (x *Administrator) Reset() { + *x = Administrator{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Administrator) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Administrator) ProtoMessage() {} + +func (x *Administrator) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Administrator.ProtoReflect.Descriptor instead. +func (*Administrator) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{0} +} + +func (x *Administrator) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Administrator) GetAuthorityId() string { + if x != nil { + return x.AuthorityId + } + return "" +} + +func (x *Administrator) GetSubject() string { + if x != nil { + return x.Subject + } + return "" +} + +func (x *Administrator) GetProvisionerId() string { + if x != nil { + return x.ProvisionerId + } + return "" +} + +func (x *Administrator) GetType() Administrator_Type { + if x != nil { + return x.Type + } + return Administrator_UNKNOWN +} + +type Provisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` + Type Provisioner_Type `protobuf:"varint,3,opt,name=type,proto3,enum=majordomo.Provisioner_Type" json:"type,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Details *ProvisionerDetails `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` + Claims *Claims `protobuf:"bytes,6,opt,name=claims,proto3" json:"claims,omitempty"` + X509Template []byte `protobuf:"bytes,7,opt,name=x509_template,json=x509Template,proto3" json:"x509_template,omitempty"` + X509TemplateData []byte `protobuf:"bytes,8,opt,name=x509_template_data,json=x509TemplateData,proto3" json:"x509_template_data,omitempty"` + SshTemplate []byte `protobuf:"bytes,9,opt,name=ssh_template,json=sshTemplate,proto3" json:"ssh_template,omitempty"` + SshTemplateData []byte `protobuf:"bytes,10,opt,name=ssh_template_data,json=sshTemplateData,proto3" json:"ssh_template_data,omitempty"` +} + +func (x *Provisioner) Reset() { + *x = Provisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Provisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Provisioner) ProtoMessage() {} + +func (x *Provisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Provisioner.ProtoReflect.Descriptor instead. +func (*Provisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{1} +} + +func (x *Provisioner) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Provisioner) GetAuthorityId() string { + if x != nil { + return x.AuthorityId + } + return "" +} + +func (x *Provisioner) GetType() Provisioner_Type { + if x != nil { + return x.Type + } + return Provisioner_NOOP +} + +func (x *Provisioner) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Provisioner) GetDetails() *ProvisionerDetails { + if x != nil { + return x.Details + } + return nil +} + +func (x *Provisioner) GetClaims() *Claims { + if x != nil { + return x.Claims + } + return nil +} + +func (x *Provisioner) GetX509Template() []byte { + if x != nil { + return x.X509Template + } + return nil +} + +func (x *Provisioner) GetX509TemplateData() []byte { + if x != nil { + return x.X509TemplateData + } + return nil +} + +func (x *Provisioner) GetSshTemplate() []byte { + if x != nil { + return x.SshTemplate + } + return nil +} + +func (x *Provisioner) GetSshTemplateData() []byte { + if x != nil { + return x.SshTemplateData + } + return nil +} + +type ProvisionerDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Data: + // *ProvisionerDetails_JWK + // *ProvisionerDetails_OIDC + // *ProvisionerDetails_GCP + // *ProvisionerDetails_AWS + // *ProvisionerDetails_Azure + // *ProvisionerDetails_ACME + // *ProvisionerDetails_X5C + // *ProvisionerDetails_K8SSA + // *ProvisionerDetails_SSHPOP + Data isProvisionerDetails_Data `protobuf_oneof:"data"` +} + +func (x *ProvisionerDetails) Reset() { + *x = ProvisionerDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProvisionerDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisionerDetails) ProtoMessage() {} + +func (x *ProvisionerDetails) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisionerDetails.ProtoReflect.Descriptor instead. +func (*ProvisionerDetails) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{2} +} + +func (m *ProvisionerDetails) GetData() isProvisionerDetails_Data { + if m != nil { + return m.Data + } + return nil +} + +func (x *ProvisionerDetails) GetJWK() *JWKProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_JWK); ok { + return x.JWK + } + return nil +} + +func (x *ProvisionerDetails) GetOIDC() *OIDCProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_OIDC); ok { + return x.OIDC + } + return nil +} + +func (x *ProvisionerDetails) GetGCP() *GCPProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_GCP); ok { + return x.GCP + } + return nil +} + +func (x *ProvisionerDetails) GetAWS() *AWSProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_AWS); ok { + return x.AWS + } + return nil +} + +func (x *ProvisionerDetails) GetAzure() *AzureProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_Azure); ok { + return x.Azure + } + return nil +} + +func (x *ProvisionerDetails) GetACME() *ACMEProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_ACME); ok { + return x.ACME + } + return nil +} + +func (x *ProvisionerDetails) GetX5C() *X5CProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_X5C); ok { + return x.X5C + } + return nil +} + +func (x *ProvisionerDetails) GetK8SSA() *K8SSAProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_K8SSA); ok { + return x.K8SSA + } + return nil +} + +func (x *ProvisionerDetails) GetSSHPOP() *SSHPOPProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_SSHPOP); ok { + return x.SSHPOP + } + return nil +} + +type isProvisionerDetails_Data interface { + isProvisionerDetails_Data() +} + +type ProvisionerDetails_JWK struct { + JWK *JWKProvisioner `protobuf:"bytes,20,opt,name=JWK,proto3,oneof"` +} + +type ProvisionerDetails_OIDC struct { + OIDC *OIDCProvisioner `protobuf:"bytes,21,opt,name=OIDC,proto3,oneof"` +} + +type ProvisionerDetails_GCP struct { + GCP *GCPProvisioner `protobuf:"bytes,22,opt,name=GCP,proto3,oneof"` +} + +type ProvisionerDetails_AWS struct { + AWS *AWSProvisioner `protobuf:"bytes,23,opt,name=AWS,proto3,oneof"` +} + +type ProvisionerDetails_Azure struct { + Azure *AzureProvisioner `protobuf:"bytes,24,opt,name=Azure,proto3,oneof"` +} + +type ProvisionerDetails_ACME struct { + ACME *ACMEProvisioner `protobuf:"bytes,25,opt,name=ACME,proto3,oneof"` +} + +type ProvisionerDetails_X5C struct { + X5C *X5CProvisioner `protobuf:"bytes,26,opt,name=X5C,proto3,oneof"` +} + +type ProvisionerDetails_K8SSA struct { + K8SSA *K8SSAProvisioner `protobuf:"bytes,27,opt,name=K8sSA,proto3,oneof"` +} + +type ProvisionerDetails_SSHPOP struct { + SSHPOP *SSHPOPProvisioner `protobuf:"bytes,28,opt,name=SSHPOP,proto3,oneof"` +} + +func (*ProvisionerDetails_JWK) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_OIDC) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_GCP) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_AWS) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_Azure) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_ACME) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_X5C) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_K8SSA) isProvisionerDetails_Data() {} + +func (*ProvisionerDetails_SSHPOP) isProvisionerDetails_Data() {} + +type ProvisionerList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Provisioners []*Provisioner `protobuf:"bytes,1,rep,name=provisioners,proto3" json:"provisioners,omitempty"` +} + +func (x *ProvisionerList) Reset() { + *x = ProvisionerList{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProvisionerList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisionerList) ProtoMessage() {} + +func (x *ProvisionerList) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisionerList.ProtoReflect.Descriptor instead. +func (*ProvisionerList) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{3} +} + +func (x *ProvisionerList) GetProvisioners() []*Provisioner { + if x != nil { + return x.Provisioners + } + return nil +} + +type Claims struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + X509 *X509Claims `protobuf:"bytes,1,opt,name=x509,proto3" json:"x509,omitempty"` + Ssh *SSHClaims `protobuf:"bytes,2,opt,name=ssh,proto3" json:"ssh,omitempty"` + DisableRenewal bool `protobuf:"varint,3,opt,name=disable_renewal,json=disableRenewal,proto3" json:"disable_renewal,omitempty"` +} + +func (x *Claims) Reset() { + *x = Claims{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Claims) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Claims) ProtoMessage() {} + +func (x *Claims) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Claims.ProtoReflect.Descriptor instead. +func (*Claims) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{4} +} + +func (x *Claims) GetX509() *X509Claims { + if x != nil { + return x.X509 + } + return nil +} + +func (x *Claims) GetSsh() *SSHClaims { + if x != nil { + return x.Ssh + } + return nil +} + +func (x *Claims) GetDisableRenewal() bool { + if x != nil { + return x.DisableRenewal + } + return false +} + +type X509Claims struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Durations *Durations `protobuf:"bytes,2,opt,name=durations,proto3" json:"durations,omitempty"` +} + +func (x *X509Claims) Reset() { + *x = X509Claims{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *X509Claims) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*X509Claims) ProtoMessage() {} + +func (x *X509Claims) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use X509Claims.ProtoReflect.Descriptor instead. +func (*X509Claims) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{5} +} + +func (x *X509Claims) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *X509Claims) GetDurations() *Durations { + if x != nil { + return x.Durations + } + return nil +} + +type SSHClaims struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + UserDurations *Durations `protobuf:"bytes,2,opt,name=user_durations,json=userDurations,proto3" json:"user_durations,omitempty"` + HostDurations *Durations `protobuf:"bytes,3,opt,name=host_durations,json=hostDurations,proto3" json:"host_durations,omitempty"` +} + +func (x *SSHClaims) Reset() { + *x = SSHClaims{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHClaims) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHClaims) ProtoMessage() {} + +func (x *SSHClaims) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SSHClaims.ProtoReflect.Descriptor instead. +func (*SSHClaims) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{6} +} + +func (x *SSHClaims) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *SSHClaims) GetUserDurations() *Durations { + if x != nil { + return x.UserDurations + } + return nil +} + +func (x *SSHClaims) GetHostDurations() *Durations { + if x != nil { + return x.HostDurations + } + return nil +} + +type Durations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` + Min string `protobuf:"bytes,2,opt,name=min,proto3" json:"min,omitempty"` + Max string `protobuf:"bytes,3,opt,name=max,proto3" json:"max,omitempty"` +} + +func (x *Durations) Reset() { + *x = Durations{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Durations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Durations) ProtoMessage() {} + +func (x *Durations) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Durations.ProtoReflect.Descriptor instead. +func (*Durations) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{7} +} + +func (x *Durations) GetDefault() string { + if x != nil { + return x.Default + } + return "" +} + +func (x *Durations) GetMin() string { + if x != nil { + return x.Min + } + return "" +} + +func (x *Durations) GetMax() string { + if x != nil { + return x.Max + } + return "" +} + +type JWKProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + EncryptedPrivateKey []byte `protobuf:"bytes,2,opt,name=encrypted_private_key,json=encryptedPrivateKey,proto3" json:"encrypted_private_key,omitempty"` +} + +func (x *JWKProvisioner) Reset() { + *x = JWKProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JWKProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JWKProvisioner) ProtoMessage() {} + +func (x *JWKProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JWKProvisioner.ProtoReflect.Descriptor instead. +func (*JWKProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{8} +} + +func (x *JWKProvisioner) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *JWKProvisioner) GetEncryptedPrivateKey() []byte { + if x != nil { + return x.EncryptedPrivateKey + } + return nil +} + +type OIDCProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + ClientSecret string `protobuf:"bytes,2,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"` + ConfigurationEndpoint string `protobuf:"bytes,3,opt,name=configuration_endpoint,json=configurationEndpoint,proto3" json:"configuration_endpoint,omitempty"` + Admins []string `protobuf:"bytes,4,rep,name=admins,proto3" json:"admins,omitempty"` + Domains []string `protobuf:"bytes,5,rep,name=domains,proto3" json:"domains,omitempty"` + Groups []string `protobuf:"bytes,6,rep,name=groups,proto3" json:"groups,omitempty"` + ListenAddress string `protobuf:"bytes,7,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` + TenantId string `protobuf:"bytes,8,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` +} + +func (x *OIDCProvisioner) Reset() { + *x = OIDCProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OIDCProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OIDCProvisioner) ProtoMessage() {} + +func (x *OIDCProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OIDCProvisioner.ProtoReflect.Descriptor instead. +func (*OIDCProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{9} +} + +func (x *OIDCProvisioner) GetClientId() string { + if x != nil { + return x.ClientId + } + return "" +} + +func (x *OIDCProvisioner) GetClientSecret() string { + if x != nil { + return x.ClientSecret + } + return "" +} + +func (x *OIDCProvisioner) GetConfigurationEndpoint() string { + if x != nil { + return x.ConfigurationEndpoint + } + return "" +} + +func (x *OIDCProvisioner) GetAdmins() []string { + if x != nil { + return x.Admins + } + return nil +} + +func (x *OIDCProvisioner) GetDomains() []string { + if x != nil { + return x.Domains + } + return nil +} + +func (x *OIDCProvisioner) GetGroups() []string { + if x != nil { + return x.Groups + } + return nil +} + +func (x *OIDCProvisioner) GetListenAddress() string { + if x != nil { + return x.ListenAddress + } + return "" +} + +func (x *OIDCProvisioner) GetTenantId() string { + if x != nil { + return x.TenantId + } + return "" +} + +type GCPProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServiceAccounts []string `protobuf:"bytes,1,rep,name=service_accounts,json=serviceAccounts,proto3" json:"service_accounts,omitempty"` + ProjectIds []string `protobuf:"bytes,2,rep,name=project_ids,json=projectIds,proto3" json:"project_ids,omitempty"` + DisableCustomSans bool `protobuf:"varint,3,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` + DisableTrustOnFirstUse bool `protobuf:"varint,4,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` + InstanceAge string `protobuf:"bytes,5,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` +} + +func (x *GCPProvisioner) Reset() { + *x = GCPProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GCPProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GCPProvisioner) ProtoMessage() {} + +func (x *GCPProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GCPProvisioner.ProtoReflect.Descriptor instead. +func (*GCPProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{10} +} + +func (x *GCPProvisioner) GetServiceAccounts() []string { + if x != nil { + return x.ServiceAccounts + } + return nil +} + +func (x *GCPProvisioner) GetProjectIds() []string { + if x != nil { + return x.ProjectIds + } + return nil +} + +func (x *GCPProvisioner) GetDisableCustomSans() bool { + if x != nil { + return x.DisableCustomSans + } + return false +} + +func (x *GCPProvisioner) GetDisableTrustOnFirstUse() bool { + if x != nil { + return x.DisableTrustOnFirstUse + } + return false +} + +func (x *GCPProvisioner) GetInstanceAge() string { + if x != nil { + return x.InstanceAge + } + return "" +} + +type AWSProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Accounts []string `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` + DisableCustomSans bool `protobuf:"varint,2,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` + DisableTrustOnFirstUse bool `protobuf:"varint,3,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` + InstanceAge string `protobuf:"bytes,4,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` +} + +func (x *AWSProvisioner) Reset() { + *x = AWSProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AWSProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AWSProvisioner) ProtoMessage() {} + +func (x *AWSProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AWSProvisioner.ProtoReflect.Descriptor instead. +func (*AWSProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{11} +} + +func (x *AWSProvisioner) GetAccounts() []string { + if x != nil { + return x.Accounts + } + return nil +} + +func (x *AWSProvisioner) GetDisableCustomSans() bool { + if x != nil { + return x.DisableCustomSans + } + return false +} + +func (x *AWSProvisioner) GetDisableTrustOnFirstUse() bool { + if x != nil { + return x.DisableTrustOnFirstUse + } + return false +} + +func (x *AWSProvisioner) GetInstanceAge() string { + if x != nil { + return x.InstanceAge + } + return "" +} + +type AzureProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` + ResourceGroups []string `protobuf:"bytes,2,rep,name=resource_groups,json=resourceGroups,proto3" json:"resource_groups,omitempty"` + Audience string `protobuf:"bytes,3,opt,name=audience,proto3" json:"audience,omitempty"` + DisableCustomSans bool `protobuf:"varint,4,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` + DisableTrustOnFirstUse bool `protobuf:"varint,5,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` +} + +func (x *AzureProvisioner) Reset() { + *x = AzureProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AzureProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AzureProvisioner) ProtoMessage() {} + +func (x *AzureProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AzureProvisioner.ProtoReflect.Descriptor instead. +func (*AzureProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{12} +} + +func (x *AzureProvisioner) GetTenantId() string { + if x != nil { + return x.TenantId + } + return "" +} + +func (x *AzureProvisioner) GetResourceGroups() []string { + if x != nil { + return x.ResourceGroups + } + return nil +} + +func (x *AzureProvisioner) GetAudience() string { + if x != nil { + return x.Audience + } + return "" +} + +func (x *AzureProvisioner) GetDisableCustomSans() bool { + if x != nil { + return x.DisableCustomSans + } + return false +} + +func (x *AzureProvisioner) GetDisableTrustOnFirstUse() bool { + if x != nil { + return x.DisableTrustOnFirstUse + } + return false +} + +type ACMEProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` +} + +func (x *ACMEProvisioner) Reset() { + *x = ACMEProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ACMEProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ACMEProvisioner) ProtoMessage() {} + +func (x *ACMEProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ACMEProvisioner.ProtoReflect.Descriptor instead. +func (*ACMEProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{13} +} + +func (x *ACMEProvisioner) GetForceCn() bool { + if x != nil { + return x.ForceCn + } + return false +} + +type X5CProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Roots [][]byte `protobuf:"bytes,1,rep,name=roots,proto3" json:"roots,omitempty"` +} + +func (x *X5CProvisioner) Reset() { + *x = X5CProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *X5CProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*X5CProvisioner) ProtoMessage() {} + +func (x *X5CProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use X5CProvisioner.ProtoReflect.Descriptor instead. +func (*X5CProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{14} +} + +func (x *X5CProvisioner) GetRoots() [][]byte { + if x != nil { + return x.Roots + } + return nil +} + +type K8SSAProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"` +} + +func (x *K8SSAProvisioner) Reset() { + *x = K8SSAProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *K8SSAProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*K8SSAProvisioner) ProtoMessage() {} + +func (x *K8SSAProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use K8SSAProvisioner.ProtoReflect.Descriptor instead. +func (*K8SSAProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{15} +} + +func (x *K8SSAProvisioner) GetPublicKeys() [][]byte { + if x != nil { + return x.PublicKeys + } + return nil +} + +type SSHPOPProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SSHPOPProvisioner) Reset() { + *x = SSHPOPProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_majordomo_provisioners_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHPOPProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHPOPProvisioner) ProtoMessage() {} + +func (x *SSHPOPProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_majordomo_provisioners_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SSHPOPProvisioner.ProtoReflect.Descriptor instead. +func (*SSHPOPProvisioner) Descriptor() ([]byte, []int) { + return file_majordomo_provisioners_proto_rawDescGZIP(), []int{16} +} + +var File_majordomo_provisioners_proto protoreflect.FileDescriptor + +var file_majordomo_provisioners_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x22, 0xe7, 0x01, 0x0a, 0x0d, 0x41, 0x64, + 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x31, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, + 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, + 0x4e, 0x10, 0x02, 0x22, 0xf7, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, + 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, + 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, + 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, + 0x23, 0x0a, 0x0d, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x22, 0x6a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, + 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, 0x12, + 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, + 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, 0x0a, + 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, 0x10, + 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x22, 0xde, 0x03, + 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2d, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4a, 0x57, + 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, + 0x4a, 0x57, 0x4b, 0x12, 0x30, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4f, 0x49, + 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2d, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x47, + 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x03, 0x47, 0x43, 0x50, 0x12, 0x2d, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x57, + 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, + 0x41, 0x57, 0x53, 0x12, 0x33, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, + 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, + 0x6d, 0x6f, 0x2e, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, 0x2d, 0x0a, 0x03, 0x58, 0x35, + 0x43, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, + 0x6f, 0x6d, 0x6f, 0x2e, 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x58, 0x35, 0x43, 0x12, 0x33, 0x0a, 0x05, 0x4b, 0x38, 0x73, + 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x12, 0x36, + 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x53, 0x53, 0x48, 0x50, 0x4f, + 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, + 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4d, + 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, + 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, + 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x84, 0x01, + 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x78, 0x35, 0x30, 0x39, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, + 0x6d, 0x6f, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x04, 0x78, + 0x35, 0x30, 0x39, 0x12, 0x26, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x53, 0x53, 0x48, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6e, + 0x65, 0x77, 0x61, 0x6c, 0x22, 0x5a, 0x0a, 0x0a, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, + 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x09, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x9f, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, + 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x63, 0x0a, + 0x0e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, + 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, + 0x0a, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x65, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 0x22, 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xeb, 0x01, + 0x0a, 0x0e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, + 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, + 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, + 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0e, + 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, + 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x7a, + 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, + 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, + 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, + 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x0f, + 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, + 0x19, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, 0x58, 0x35, + 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x6f, 0x6f, + 0x74, 0x73, 0x22, 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, 0x50, 0x4f, + 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x42, 0x2d, 0x5a, 0x2b, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, + 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x73, 0x2f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_majordomo_provisioners_proto_rawDescOnce sync.Once + file_majordomo_provisioners_proto_rawDescData = file_majordomo_provisioners_proto_rawDesc +) + +func file_majordomo_provisioners_proto_rawDescGZIP() []byte { + file_majordomo_provisioners_proto_rawDescOnce.Do(func() { + file_majordomo_provisioners_proto_rawDescData = protoimpl.X.CompressGZIP(file_majordomo_provisioners_proto_rawDescData) + }) + return file_majordomo_provisioners_proto_rawDescData +} + +var file_majordomo_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_majordomo_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_majordomo_provisioners_proto_goTypes = []interface{}{ + (Administrator_Type)(0), // 0: majordomo.Administrator.Type + (Provisioner_Type)(0), // 1: majordomo.Provisioner.Type + (*Administrator)(nil), // 2: majordomo.Administrator + (*Provisioner)(nil), // 3: majordomo.Provisioner + (*ProvisionerDetails)(nil), // 4: majordomo.ProvisionerDetails + (*ProvisionerList)(nil), // 5: majordomo.ProvisionerList + (*Claims)(nil), // 6: majordomo.Claims + (*X509Claims)(nil), // 7: majordomo.X509Claims + (*SSHClaims)(nil), // 8: majordomo.SSHClaims + (*Durations)(nil), // 9: majordomo.Durations + (*JWKProvisioner)(nil), // 10: majordomo.JWKProvisioner + (*OIDCProvisioner)(nil), // 11: majordomo.OIDCProvisioner + (*GCPProvisioner)(nil), // 12: majordomo.GCPProvisioner + (*AWSProvisioner)(nil), // 13: majordomo.AWSProvisioner + (*AzureProvisioner)(nil), // 14: majordomo.AzureProvisioner + (*ACMEProvisioner)(nil), // 15: majordomo.ACMEProvisioner + (*X5CProvisioner)(nil), // 16: majordomo.X5CProvisioner + (*K8SSAProvisioner)(nil), // 17: majordomo.K8sSAProvisioner + (*SSHPOPProvisioner)(nil), // 18: majordomo.SSHPOPProvisioner +} +var file_majordomo_provisioners_proto_depIdxs = []int32{ + 0, // 0: majordomo.Administrator.type:type_name -> majordomo.Administrator.Type + 1, // 1: majordomo.Provisioner.type:type_name -> majordomo.Provisioner.Type + 4, // 2: majordomo.Provisioner.details:type_name -> majordomo.ProvisionerDetails + 6, // 3: majordomo.Provisioner.claims:type_name -> majordomo.Claims + 10, // 4: majordomo.ProvisionerDetails.JWK:type_name -> majordomo.JWKProvisioner + 11, // 5: majordomo.ProvisionerDetails.OIDC:type_name -> majordomo.OIDCProvisioner + 12, // 6: majordomo.ProvisionerDetails.GCP:type_name -> majordomo.GCPProvisioner + 13, // 7: majordomo.ProvisionerDetails.AWS:type_name -> majordomo.AWSProvisioner + 14, // 8: majordomo.ProvisionerDetails.Azure:type_name -> majordomo.AzureProvisioner + 15, // 9: majordomo.ProvisionerDetails.ACME:type_name -> majordomo.ACMEProvisioner + 16, // 10: majordomo.ProvisionerDetails.X5C:type_name -> majordomo.X5CProvisioner + 17, // 11: majordomo.ProvisionerDetails.K8sSA:type_name -> majordomo.K8sSAProvisioner + 18, // 12: majordomo.ProvisionerDetails.SSHPOP:type_name -> majordomo.SSHPOPProvisioner + 3, // 13: majordomo.ProvisionerList.provisioners:type_name -> majordomo.Provisioner + 7, // 14: majordomo.Claims.x509:type_name -> majordomo.X509Claims + 8, // 15: majordomo.Claims.ssh:type_name -> majordomo.SSHClaims + 9, // 16: majordomo.X509Claims.durations:type_name -> majordomo.Durations + 9, // 17: majordomo.SSHClaims.user_durations:type_name -> majordomo.Durations + 9, // 18: majordomo.SSHClaims.host_durations:type_name -> majordomo.Durations + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name +} + +func init() { file_majordomo_provisioners_proto_init() } +func file_majordomo_provisioners_proto_init() { + if File_majordomo_provisioners_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_majordomo_provisioners_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Administrator); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Provisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvisionerDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvisionerList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Claims); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*X509Claims); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SSHClaims); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Durations); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JWKProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OIDCProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GCPProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AWSProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AzureProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ACMEProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*X5CProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*K8SSAProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_majordomo_provisioners_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SSHPOPProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_majordomo_provisioners_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*ProvisionerDetails_JWK)(nil), + (*ProvisionerDetails_OIDC)(nil), + (*ProvisionerDetails_GCP)(nil), + (*ProvisionerDetails_AWS)(nil), + (*ProvisionerDetails_Azure)(nil), + (*ProvisionerDetails_ACME)(nil), + (*ProvisionerDetails_X5C)(nil), + (*ProvisionerDetails_K8SSA)(nil), + (*ProvisionerDetails_SSHPOP)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_majordomo_provisioners_proto_rawDesc, + NumEnums: 2, + NumMessages: 17, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_majordomo_provisioners_proto_goTypes, + DependencyIndexes: file_majordomo_provisioners_proto_depIdxs, + EnumInfos: file_majordomo_provisioners_proto_enumTypes, + MessageInfos: file_majordomo_provisioners_proto_msgTypes, + }.Build() + File_majordomo_provisioners_proto = out.File + file_majordomo_provisioners_proto_rawDesc = nil + file_majordomo_provisioners_proto_goTypes = nil + file_majordomo_provisioners_proto_depIdxs = nil +} diff --git a/majordomo/provisioners.proto b/majordomo/provisioners.proto new file mode 100644 index 00000000..7dd7f0c1 --- /dev/null +++ b/majordomo/provisioners.proto @@ -0,0 +1,137 @@ +syntax = "proto3"; + +package majordomo; + +option go_package = "github.com/smallstep/certificates/majordomo"; + +message Administrator { + enum Type { + UNKNOWN = 0; + ADMIN = 1; + SUPER_ADMIN = 2; + } + string id = 1; + string authority_id = 2; + string subject = 3; + string provisioner_id = 4; + Type type = 5; +} + +message Provisioner { + enum Type { + NOOP = 0; + JWK = 1; + OIDC = 2; + GCP = 3; + AWS = 4; + AZURE = 5; + ACME = 6; + X5C = 7; + K8SSA = 8; + SSHPOP = 9; + } + string id = 1; + string authority_id = 2; + Type type = 3; + string name = 4; + ProvisionerDetails details = 5; + Claims claims = 6; + bytes x509_template = 7; + bytes x509_template_data = 8; + bytes ssh_template = 9; + bytes ssh_template_data = 10; +} + +message ProvisionerDetails { + oneof data { + JWKProvisioner JWK = 20; + OIDCProvisioner OIDC = 21; + GCPProvisioner GCP = 22; + AWSProvisioner AWS = 23; + AzureProvisioner Azure = 24; + ACMEProvisioner ACME = 25; + X5CProvisioner X5C = 26; + K8sSAProvisioner K8sSA = 27; + SSHPOPProvisioner SSHPOP = 28; + } +} + +message ProvisionerList { + repeated Provisioner provisioners = 1; +} + +message Claims { + X509Claims x509 = 1; + SSHClaims ssh = 2; + bool disable_renewal = 3; +} + +message X509Claims { + bool enabled = 1; + Durations durations = 2; +} + +message SSHClaims { + bool enabled = 1; + Durations user_durations = 2; + Durations host_durations = 3; +} + +message Durations { + string default = 1; + string min = 2; + string max = 3; +} + +message JWKProvisioner { + bytes public_key = 1; + bytes encrypted_private_key = 2; +} + +message OIDCProvisioner { + string client_id = 1; + string client_secret = 2; + string configuration_endpoint = 3; + repeated string admins = 4; + repeated string domains = 5; + repeated string groups = 6; + string listen_address = 7; + string tenant_id = 8; +} + +message GCPProvisioner { + repeated string service_accounts = 1; + repeated string project_ids = 2; + bool disable_custom_sans = 3; + bool disable_trust_on_first_use = 4; + string instance_age = 5; +} + +message AWSProvisioner { + repeated string accounts = 1; + bool disable_custom_sans = 2; + bool disable_trust_on_first_use = 3; + string instance_age = 4; +} + +message AzureProvisioner { + string tenant_id = 1; + repeated string resource_groups = 2; + string audience = 3; + bool disable_custom_sans = 4; + bool disable_trust_on_first_use = 5; +} + +message ACMEProvisioner { + bool force_cn = 1; +} + +message X5CProvisioner { + repeated bytes roots = 1; +} + +message K8sSAProvisioner { + repeated bytes public_keys = 1; +} + +message SSHPOPProvisioner {} From 71afc413bf0294332fceee2eeea24b3edbe27673 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 24 May 2021 12:36:01 -0700 Subject: [PATCH 015/291] Rename majordomo to linkedca. --- {majordomo => linkedca}/doc.go | 0 {majordomo => linkedca}/majordomo.pb.go | 0 {majordomo => linkedca}/majordomo.proto | 0 {majordomo => linkedca}/majordomo_grpc.pb.go | 0 {majordomo => linkedca}/provisioners.pb.go | 0 {majordomo => linkedca}/provisioners.proto | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {majordomo => linkedca}/doc.go (100%) rename {majordomo => linkedca}/majordomo.pb.go (100%) rename {majordomo => linkedca}/majordomo.proto (100%) rename {majordomo => linkedca}/majordomo_grpc.pb.go (100%) rename {majordomo => linkedca}/provisioners.pb.go (100%) rename {majordomo => linkedca}/provisioners.proto (100%) diff --git a/majordomo/doc.go b/linkedca/doc.go similarity index 100% rename from majordomo/doc.go rename to linkedca/doc.go diff --git a/majordomo/majordomo.pb.go b/linkedca/majordomo.pb.go similarity index 100% rename from majordomo/majordomo.pb.go rename to linkedca/majordomo.pb.go diff --git a/majordomo/majordomo.proto b/linkedca/majordomo.proto similarity index 100% rename from majordomo/majordomo.proto rename to linkedca/majordomo.proto diff --git a/majordomo/majordomo_grpc.pb.go b/linkedca/majordomo_grpc.pb.go similarity index 100% rename from majordomo/majordomo_grpc.pb.go rename to linkedca/majordomo_grpc.pb.go diff --git a/majordomo/provisioners.pb.go b/linkedca/provisioners.pb.go similarity index 100% rename from majordomo/provisioners.pb.go rename to linkedca/provisioners.pb.go diff --git a/majordomo/provisioners.proto b/linkedca/provisioners.proto similarity index 100% rename from majordomo/provisioners.proto rename to linkedca/provisioners.proto From 35cfa5b8a298e2a4d96f0daed16f9417052622b7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 24 May 2021 12:43:23 -0700 Subject: [PATCH 016/291] Remove majordomo client and rename administrator to admin. --- linkedca/admin.pb.go | 240 ++++++++ linkedca/admin.proto | 18 + linkedca/doc.go | 2 +- linkedca/majordomo.pb.go | 1094 --------------------------------- linkedca/majordomo.proto | 102 --- linkedca/majordomo_grpc.pb.go | 519 ---------------- linkedca/provisioners.pb.go | 787 ++++++++++-------------- linkedca/provisioners.proto | 17 +- 8 files changed, 575 insertions(+), 2204 deletions(-) create mode 100644 linkedca/admin.pb.go create mode 100644 linkedca/admin.proto delete mode 100644 linkedca/majordomo.pb.go delete mode 100644 linkedca/majordomo.proto delete mode 100644 linkedca/majordomo_grpc.pb.go diff --git a/linkedca/admin.pb.go b/linkedca/admin.pb.go new file mode 100644 index 00000000..02e287c1 --- /dev/null +++ b/linkedca/admin.pb.go @@ -0,0 +1,240 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.8 +// source: linkedca/admin.proto + +package linkedca + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Admin_Type int32 + +const ( + Admin_UNKNOWN Admin_Type = 0 + Admin_ADMIN Admin_Type = 1 + Admin_SUPER_ADMIN Admin_Type = 2 +) + +// Enum value maps for Admin_Type. +var ( + Admin_Type_name = map[int32]string{ + 0: "UNKNOWN", + 1: "ADMIN", + 2: "SUPER_ADMIN", + } + Admin_Type_value = map[string]int32{ + "UNKNOWN": 0, + "ADMIN": 1, + "SUPER_ADMIN": 2, + } +) + +func (x Admin_Type) Enum() *Admin_Type { + p := new(Admin_Type) + *p = x + return p +} + +func (x Admin_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Admin_Type) Descriptor() protoreflect.EnumDescriptor { + return file_linkedca_admin_proto_enumTypes[0].Descriptor() +} + +func (Admin_Type) Type() protoreflect.EnumType { + return &file_linkedca_admin_proto_enumTypes[0] +} + +func (x Admin_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Admin_Type.Descriptor instead. +func (Admin_Type) EnumDescriptor() ([]byte, []int) { + return file_linkedca_admin_proto_rawDescGZIP(), []int{0, 0} +} + +type Admin struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` + Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + ProvisionerId string `protobuf:"bytes,4,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` + Type Admin_Type `protobuf:"varint,5,opt,name=type,proto3,enum=linkedca.Admin_Type" json:"type,omitempty"` +} + +func (x *Admin) Reset() { + *x = Admin{} + if protoimpl.UnsafeEnabled { + mi := &file_linkedca_admin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Admin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Admin) ProtoMessage() {} + +func (x *Admin) ProtoReflect() protoreflect.Message { + mi := &file_linkedca_admin_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Admin.ProtoReflect.Descriptor instead. +func (*Admin) Descriptor() ([]byte, []int) { + return file_linkedca_admin_proto_rawDescGZIP(), []int{0} +} + +func (x *Admin) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Admin) GetAuthorityId() string { + if x != nil { + return x.AuthorityId + } + return "" +} + +func (x *Admin) GetSubject() string { + if x != nil { + return x.Subject + } + return "" +} + +func (x *Admin) GetProvisionerId() string { + if x != nil { + return x.ProvisionerId + } + return "" +} + +func (x *Admin) GetType() Admin_Type { + if x != nil { + return x.Type + } + return Admin_UNKNOWN +} + +var File_linkedca_admin_proto protoreflect.FileDescriptor + +var file_linkedca_admin_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, + 0x22, 0xd6, 0x01, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x55, 0x50, 0x45, + 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, + 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_linkedca_admin_proto_rawDescOnce sync.Once + file_linkedca_admin_proto_rawDescData = file_linkedca_admin_proto_rawDesc +) + +func file_linkedca_admin_proto_rawDescGZIP() []byte { + file_linkedca_admin_proto_rawDescOnce.Do(func() { + file_linkedca_admin_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_admin_proto_rawDescData) + }) + return file_linkedca_admin_proto_rawDescData +} + +var file_linkedca_admin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_linkedca_admin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_linkedca_admin_proto_goTypes = []interface{}{ + (Admin_Type)(0), // 0: linkedca.Admin.Type + (*Admin)(nil), // 1: linkedca.Admin +} +var file_linkedca_admin_proto_depIdxs = []int32{ + 0, // 0: linkedca.Admin.type:type_name -> linkedca.Admin.Type + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_linkedca_admin_proto_init() } +func file_linkedca_admin_proto_init() { + if File_linkedca_admin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_linkedca_admin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Admin); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_linkedca_admin_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_linkedca_admin_proto_goTypes, + DependencyIndexes: file_linkedca_admin_proto_depIdxs, + EnumInfos: file_linkedca_admin_proto_enumTypes, + MessageInfos: file_linkedca_admin_proto_msgTypes, + }.Build() + File_linkedca_admin_proto = out.File + file_linkedca_admin_proto_rawDesc = nil + file_linkedca_admin_proto_goTypes = nil + file_linkedca_admin_proto_depIdxs = nil +} diff --git a/linkedca/admin.proto b/linkedca/admin.proto new file mode 100644 index 00000000..29c76139 --- /dev/null +++ b/linkedca/admin.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package linkedca; + +option go_package = "github.com/smallstep/certificates/linkedca"; + +message Admin { + enum Type { + UNKNOWN = 0; + ADMIN = 1; + SUPER_ADMIN = 2; + } + string id = 1; + string authority_id = 2; + string subject = 3; + string provisioner_id = 4; + Type type = 5; +} diff --git a/linkedca/doc.go b/linkedca/doc.go index ef9fce3a..9a64ca9c 100644 --- a/linkedca/doc.go +++ b/linkedca/doc.go @@ -1,3 +1,3 @@ package majordomo -//go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative majordomo/provisioners.proto majordomo/majordomo.proto +//go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative linkedca/provisioners.proto linkedca/admin.proto diff --git a/linkedca/majordomo.pb.go b/linkedca/majordomo.pb.go deleted file mode 100644 index bce189df..00000000 --- a/linkedca/majordomo.pb.go +++ /dev/null @@ -1,1094 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.8 -// source: majordomo/majordomo.proto - -package majordomo - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type TODO struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *TODO) Reset() { - *x = TODO{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TODO) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TODO) ProtoMessage() {} - -func (x *TODO) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TODO.ProtoReflect.Descriptor instead. -func (*TODO) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{0} -} - -type LoginRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AuthorityId string `protobuf:"bytes,1,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` - Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` - PemCertificateRequest string `protobuf:"bytes,4,opt,name=pem_certificate_request,json=pemCertificateRequest,proto3" json:"pem_certificate_request,omitempty"` -} - -func (x *LoginRequest) Reset() { - *x = LoginRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LoginRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LoginRequest) ProtoMessage() {} - -func (x *LoginRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. -func (*LoginRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{1} -} - -func (x *LoginRequest) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *LoginRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *LoginRequest) GetPassword() string { - if x != nil { - return x.Password - } - return "" -} - -func (x *LoginRequest) GetPemCertificateRequest() string { - if x != nil { - return x.PemCertificateRequest - } - return "" -} - -type LoginResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PemCertificate string `protobuf:"bytes,1,opt,name=pem_certificate,json=pemCertificate,proto3" json:"pem_certificate,omitempty"` - PemCertificateChain string `protobuf:"bytes,2,opt,name=pem_certificate_chain,json=pemCertificateChain,proto3" json:"pem_certificate_chain,omitempty"` -} - -func (x *LoginResponse) Reset() { - *x = LoginResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LoginResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LoginResponse) ProtoMessage() {} - -func (x *LoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. -func (*LoginResponse) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{2} -} - -func (x *LoginResponse) GetPemCertificate() string { - if x != nil { - return x.PemCertificate - } - return "" -} - -func (x *LoginResponse) GetPemCertificateChain() string { - if x != nil { - return x.PemCertificateChain - } - return "" -} - -type ConfigurationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ConfigurationRequest) Reset() { - *x = ConfigurationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ConfigurationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigurationRequest) ProtoMessage() {} - -func (x *ConfigurationRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigurationRequest.ProtoReflect.Descriptor instead. -func (*ConfigurationRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{3} -} - -type ConfigurationResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Provisioners []*Provisioner `protobuf:"bytes,1,rep,name=provisioners,proto3" json:"provisioners,omitempty"` - Admins []*Administrator `protobuf:"bytes,2,rep,name=admins,proto3" json:"admins,omitempty"` -} - -func (x *ConfigurationResponse) Reset() { - *x = ConfigurationResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ConfigurationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigurationResponse) ProtoMessage() {} - -func (x *ConfigurationResponse) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigurationResponse.ProtoReflect.Descriptor instead. -func (*ConfigurationResponse) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{4} -} - -func (x *ConfigurationResponse) GetProvisioners() []*Provisioner { - if x != nil { - return x.Provisioners - } - return nil -} - -func (x *ConfigurationResponse) GetAdmins() []*Administrator { - if x != nil { - return x.Admins - } - return nil -} - -type CreateProvisionerRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type Provisioner_Type `protobuf:"varint,1,opt,name=type,proto3,enum=majordomo.Provisioner_Type" json:"type,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Details *ProvisionerDetails `protobuf:"bytes,3,opt,name=details,proto3" json:"details,omitempty"` - Claims *Claims `protobuf:"bytes,4,opt,name=claims,proto3" json:"claims,omitempty"` -} - -func (x *CreateProvisionerRequest) Reset() { - *x = CreateProvisionerRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateProvisionerRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateProvisionerRequest) ProtoMessage() {} - -func (x *CreateProvisionerRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateProvisionerRequest.ProtoReflect.Descriptor instead. -func (*CreateProvisionerRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{5} -} - -func (x *CreateProvisionerRequest) GetType() Provisioner_Type { - if x != nil { - return x.Type - } - return Provisioner_NOOP -} - -func (x *CreateProvisionerRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CreateProvisionerRequest) GetDetails() *ProvisionerDetails { - if x != nil { - return x.Details - } - return nil -} - -func (x *CreateProvisionerRequest) GetClaims() *Claims { - if x != nil { - return x.Claims - } - return nil -} - -type DeleteProvisionerRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *DeleteProvisionerRequest) Reset() { - *x = DeleteProvisionerRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteProvisionerRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProvisionerRequest) ProtoMessage() {} - -func (x *DeleteProvisionerRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProvisionerRequest.ProtoReflect.Descriptor instead. -func (*DeleteProvisionerRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{6} -} - -func (x *DeleteProvisionerRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type CreateAdministratorRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - ProvisionerId string `protobuf:"bytes,2,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` - Type Administrator_Type `protobuf:"varint,3,opt,name=type,proto3,enum=majordomo.Administrator_Type" json:"type,omitempty"` -} - -func (x *CreateAdministratorRequest) Reset() { - *x = CreateAdministratorRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateAdministratorRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateAdministratorRequest) ProtoMessage() {} - -func (x *CreateAdministratorRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateAdministratorRequest.ProtoReflect.Descriptor instead. -func (*CreateAdministratorRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{7} -} - -func (x *CreateAdministratorRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CreateAdministratorRequest) GetProvisionerId() string { - if x != nil { - return x.ProvisionerId - } - return "" -} - -func (x *CreateAdministratorRequest) GetType() Administrator_Type { - if x != nil { - return x.Type - } - return Administrator_UNKNOWN -} - -type DeleteAdministratorRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *DeleteAdministratorRequest) Reset() { - *x = DeleteAdministratorRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteAdministratorRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteAdministratorRequest) ProtoMessage() {} - -func (x *DeleteAdministratorRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteAdministratorRequest.ProtoReflect.Descriptor instead. -func (*DeleteAdministratorRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{8} -} - -func (x *DeleteAdministratorRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type CertificateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PemCertificate string `protobuf:"bytes,1,opt,name=pem_certificate,json=pemCertificate,proto3" json:"pem_certificate,omitempty"` - PemCertificateChain string `protobuf:"bytes,2,opt,name=pem_certificate_chain,json=pemCertificateChain,proto3" json:"pem_certificate_chain,omitempty"` -} - -func (x *CertificateRequest) Reset() { - *x = CertificateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CertificateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CertificateRequest) ProtoMessage() {} - -func (x *CertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CertificateRequest.ProtoReflect.Descriptor instead. -func (*CertificateRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{9} -} - -func (x *CertificateRequest) GetPemCertificate() string { - if x != nil { - return x.PemCertificate - } - return "" -} - -func (x *CertificateRequest) GetPemCertificateChain() string { - if x != nil { - return x.PemCertificateChain - } - return "" -} - -type CertificateResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *CertificateResponse) Reset() { - *x = CertificateResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CertificateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CertificateResponse) ProtoMessage() {} - -func (x *CertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CertificateResponse.ProtoReflect.Descriptor instead. -func (*CertificateResponse) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{10} -} - -func (x *CertificateResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type SSHCertificateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Certificate string `protobuf:"bytes,1,opt,name=certificate,proto3" json:"certificate,omitempty"` -} - -func (x *SSHCertificateRequest) Reset() { - *x = SSHCertificateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHCertificateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHCertificateRequest) ProtoMessage() {} - -func (x *SSHCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHCertificateRequest.ProtoReflect.Descriptor instead. -func (*SSHCertificateRequest) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{11} -} - -func (x *SSHCertificateRequest) GetCertificate() string { - if x != nil { - return x.Certificate - } - return "" -} - -type SSHCertificateResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *SSHCertificateResponse) Reset() { - *x = SSHCertificateResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_majordomo_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHCertificateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHCertificateResponse) ProtoMessage() {} - -func (x *SSHCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_majordomo_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHCertificateResponse.ProtoReflect.Descriptor instead. -func (*SSHCertificateResponse) Descriptor() ([]byte, []int) { - return file_majordomo_majordomo_proto_rawDescGZIP(), []int{12} -} - -func (x *SSHCertificateResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -var File_majordomo_majordomo_proto protoreflect.FileDescriptor - -var file_majordomo_majordomo_proto_rawDesc = []byte{ - 0x0a, 0x19, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2f, 0x6d, 0x61, 0x6a, 0x6f, - 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6d, 0x61, 0x6a, - 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x1a, 0x1c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, - 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x06, 0x0a, 0x04, 0x54, 0x4f, 0x44, 0x4f, 0x22, 0xa1, 0x01, 0x0a, - 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, - 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x65, 0x6d, 0x5f, - 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x70, 0x65, 0x6d, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x6c, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x6d, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, - 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x65, 0x6d, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x16, - 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x85, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, - 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x0c, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x06, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x22, 0xc3, - 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, - 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x37, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6c, 0x61, - 0x69, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, - 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, - 0x61, 0x69, 0x6d, 0x73, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x8a, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, - 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x0a, - 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x12, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x6d, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, - 0x6d, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x65, 0x6d, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x25, - 0x0a, 0x13, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x15, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, - 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x22, 0x28, 0x0a, 0x16, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xec, 0x06, 0x0a, 0x09, 0x4d, - 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x12, 0x3a, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x12, 0x17, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, - 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, - 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, - 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x13, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x11, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x23, - 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x13, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x12, 0x25, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, - 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, - 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x25, 0x2e, 0x6d, 0x61, 0x6a, - 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x64, 0x6d, - 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, - 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x50, - 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, - 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, - 0x12, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, - 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, - 0x6f, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, - 0x6b, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x2e, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x1a, 0x0f, - 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x12, - 0x38, 0x0a, 0x14, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, - 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x1a, 0x0f, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, - 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x54, 0x4f, 0x44, 0x4f, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, - 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_majordomo_majordomo_proto_rawDescOnce sync.Once - file_majordomo_majordomo_proto_rawDescData = file_majordomo_majordomo_proto_rawDesc -) - -func file_majordomo_majordomo_proto_rawDescGZIP() []byte { - file_majordomo_majordomo_proto_rawDescOnce.Do(func() { - file_majordomo_majordomo_proto_rawDescData = protoimpl.X.CompressGZIP(file_majordomo_majordomo_proto_rawDescData) - }) - return file_majordomo_majordomo_proto_rawDescData -} - -var file_majordomo_majordomo_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_majordomo_majordomo_proto_goTypes = []interface{}{ - (*TODO)(nil), // 0: majordomo.TODO - (*LoginRequest)(nil), // 1: majordomo.LoginRequest - (*LoginResponse)(nil), // 2: majordomo.LoginResponse - (*ConfigurationRequest)(nil), // 3: majordomo.ConfigurationRequest - (*ConfigurationResponse)(nil), // 4: majordomo.ConfigurationResponse - (*CreateProvisionerRequest)(nil), // 5: majordomo.CreateProvisionerRequest - (*DeleteProvisionerRequest)(nil), // 6: majordomo.DeleteProvisionerRequest - (*CreateAdministratorRequest)(nil), // 7: majordomo.CreateAdministratorRequest - (*DeleteAdministratorRequest)(nil), // 8: majordomo.DeleteAdministratorRequest - (*CertificateRequest)(nil), // 9: majordomo.CertificateRequest - (*CertificateResponse)(nil), // 10: majordomo.CertificateResponse - (*SSHCertificateRequest)(nil), // 11: majordomo.SSHCertificateRequest - (*SSHCertificateResponse)(nil), // 12: majordomo.SSHCertificateResponse - (*Provisioner)(nil), // 13: majordomo.Provisioner - (*Administrator)(nil), // 14: majordomo.Administrator - (Provisioner_Type)(0), // 15: majordomo.Provisioner.Type - (*ProvisionerDetails)(nil), // 16: majordomo.ProvisionerDetails - (*Claims)(nil), // 17: majordomo.Claims - (Administrator_Type)(0), // 18: majordomo.Administrator.Type -} -var file_majordomo_majordomo_proto_depIdxs = []int32{ - 13, // 0: majordomo.ConfigurationResponse.provisioners:type_name -> majordomo.Provisioner - 14, // 1: majordomo.ConfigurationResponse.admins:type_name -> majordomo.Administrator - 15, // 2: majordomo.CreateProvisionerRequest.type:type_name -> majordomo.Provisioner.Type - 16, // 3: majordomo.CreateProvisionerRequest.details:type_name -> majordomo.ProvisionerDetails - 17, // 4: majordomo.CreateProvisionerRequest.claims:type_name -> majordomo.Claims - 18, // 5: majordomo.CreateAdministratorRequest.type:type_name -> majordomo.Administrator.Type - 1, // 6: majordomo.Majordomo.Login:input_type -> majordomo.LoginRequest - 3, // 7: majordomo.Majordomo.GetConfiguration:input_type -> majordomo.ConfigurationRequest - 3, // 8: majordomo.Majordomo.StreamConfiguration:input_type -> majordomo.ConfigurationRequest - 5, // 9: majordomo.Majordomo.CreateProvisioner:input_type -> majordomo.CreateProvisionerRequest - 6, // 10: majordomo.Majordomo.DeleteProvisioner:input_type -> majordomo.DeleteProvisionerRequest - 7, // 11: majordomo.Majordomo.CreateAdministrator:input_type -> majordomo.CreateAdministratorRequest - 8, // 12: majordomo.Majordomo.DeleteAdministrator:input_type -> majordomo.DeleteAdministratorRequest - 9, // 13: majordomo.Majordomo.PostCertificate:input_type -> majordomo.CertificateRequest - 11, // 14: majordomo.Majordomo.PostSSHCertificate:input_type -> majordomo.SSHCertificateRequest - 0, // 15: majordomo.Majordomo.RevokeCertificate:input_type -> majordomo.TODO - 0, // 16: majordomo.Majordomo.RevokeSSHCertificate:input_type -> majordomo.TODO - 2, // 17: majordomo.Majordomo.Login:output_type -> majordomo.LoginResponse - 4, // 18: majordomo.Majordomo.GetConfiguration:output_type -> majordomo.ConfigurationResponse - 4, // 19: majordomo.Majordomo.StreamConfiguration:output_type -> majordomo.ConfigurationResponse - 13, // 20: majordomo.Majordomo.CreateProvisioner:output_type -> majordomo.Provisioner - 13, // 21: majordomo.Majordomo.DeleteProvisioner:output_type -> majordomo.Provisioner - 14, // 22: majordomo.Majordomo.CreateAdministrator:output_type -> majordomo.Administrator - 14, // 23: majordomo.Majordomo.DeleteAdministrator:output_type -> majordomo.Administrator - 10, // 24: majordomo.Majordomo.PostCertificate:output_type -> majordomo.CertificateResponse - 12, // 25: majordomo.Majordomo.PostSSHCertificate:output_type -> majordomo.SSHCertificateResponse - 0, // 26: majordomo.Majordomo.RevokeCertificate:output_type -> majordomo.TODO - 0, // 27: majordomo.Majordomo.RevokeSSHCertificate:output_type -> majordomo.TODO - 17, // [17:28] is the sub-list for method output_type - 6, // [6:17] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_majordomo_majordomo_proto_init() } -func file_majordomo_majordomo_proto_init() { - if File_majordomo_majordomo_proto != nil { - return - } - file_majordomo_provisioners_proto_init() - if !protoimpl.UnsafeEnabled { - file_majordomo_majordomo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TODO); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigurationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigurationResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateProvisionerRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteProvisionerRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateAdministratorRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteAdministratorRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CertificateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CertificateResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHCertificateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_majordomo_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHCertificateResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_majordomo_majordomo_proto_rawDesc, - NumEnums: 0, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_majordomo_majordomo_proto_goTypes, - DependencyIndexes: file_majordomo_majordomo_proto_depIdxs, - MessageInfos: file_majordomo_majordomo_proto_msgTypes, - }.Build() - File_majordomo_majordomo_proto = out.File - file_majordomo_majordomo_proto_rawDesc = nil - file_majordomo_majordomo_proto_goTypes = nil - file_majordomo_majordomo_proto_depIdxs = nil -} diff --git a/linkedca/majordomo.proto b/linkedca/majordomo.proto deleted file mode 100644 index bfdf0047..00000000 --- a/linkedca/majordomo.proto +++ /dev/null @@ -1,102 +0,0 @@ -syntax = "proto3"; - -package majordomo; - -option go_package = "github.com/smallstep/certificates/majordomo"; - -import "majordomo/provisioners.proto"; - -// Majordomo is the public service used to sync configurations to CA's and post -// certificates. -service Majordomo { - // Login creates signs a given CSR and returns the certificate that will be - // used for authentication. - rpc Login(LoginRequest) returns (LoginResponse); - - // GetConfiguration returns the full configuration of an authority. - rpc GetConfiguration(ConfigurationRequest) returns (ConfigurationResponse); - // StreamConfiguration streams the full configuration of an authority. This - // method is not yet supported. - rpc StreamConfiguration(ConfigurationRequest) returns (stream ConfigurationResponse); - - // CreateProvisioner adds a new provisioner to the majordomo authority and - // returns the proto representation. - rpc CreateProvisioner(CreateProvisionerRequest) returns (Provisioner); - // DeleteProvisioner deletes a previously created provisioner. - rpc DeleteProvisioner(DeleteProvisionerRequest) returns (Provisioner); - - // CreateAdministrator adds a new admin user to the majordomo authority. - // Admin users can add or delete provisioners. - rpc CreateAdministrator(CreateAdministratorRequest) returns (Administrator); - // DeleteAdministrator deletes a previously created admin user. - rpc DeleteAdministrator(DeleteAdministratorRequest) returns (Administrator); - - // PostCertificate sends a signed X.509 certificate to majordomo. - rpc PostCertificate(CertificateRequest) returns (CertificateResponse); - // PostSSHCertificate sends a signed SSH certificate to majordomo. - rpc PostSSHCertificate(SSHCertificateRequest) returns (SSHCertificateResponse); - // RevokeCertificate marks an X.509 certificate as revoked. - rpc RevokeCertificate(TODO) returns (TODO); - // RevokeSSHCertificate marks an SSH certificate as revoked. - rpc RevokeSSHCertificate(TODO) returns (TODO); -} - -message TODO {} - -message LoginRequest { - string authority_id = 1; - string username = 2; - string password = 3; - string pem_certificate_request = 4; -} - -message LoginResponse { - string pem_certificate = 1; - string pem_certificate_chain = 2; -} - -message ConfigurationRequest { - // todo -} - -message ConfigurationResponse { - repeated Provisioner provisioners = 1; - repeated Administrator admins = 2; -} - -message CreateProvisionerRequest { - Provisioner.Type type = 1; - string name = 2; - ProvisionerDetails details = 3; - Claims claims = 4; -} - -message DeleteProvisionerRequest { - string id = 1; -} - -message CreateAdministratorRequest { - string name = 1; - string provisioner_id = 2; - Administrator.Type type = 3; -} - -message DeleteAdministratorRequest { - string id = 1; -} -message CertificateRequest { - string pem_certificate = 1; - string pem_certificate_chain = 2; -} - -message CertificateResponse { - string id = 1; -} - -message SSHCertificateRequest { - string certificate = 1; -} - -message SSHCertificateResponse { - string id = 1; -} diff --git a/linkedca/majordomo_grpc.pb.go b/linkedca/majordomo_grpc.pb.go deleted file mode 100644 index 44fd648d..00000000 --- a/linkedca/majordomo_grpc.pb.go +++ /dev/null @@ -1,519 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package majordomo - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// MajordomoClient is the client API for Majordomo service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type MajordomoClient interface { - // Login creates signs a given CSR and returns the certificate that will be - // used for authentication. - Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) - // GetConfiguration returns the full configuration of an authority. - GetConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (*ConfigurationResponse, error) - // StreamConfiguration streams the full configuration of an authority. This - // method is not yet supported. - StreamConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (Majordomo_StreamConfigurationClient, error) - // CreateProvisioner adds a new provisioner to the majordomo authority and - // returns the proto representation. - CreateProvisioner(ctx context.Context, in *CreateProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) - // DeleteProvisioner deletes a previously created provisioner. - DeleteProvisioner(ctx context.Context, in *DeleteProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) - // CreateAdministrator adds a new admin user to the majordomo authority. - // Admin users can add or delete provisioners. - CreateAdministrator(ctx context.Context, in *CreateAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) - // DeleteAdministrator deletes a previously created admin user. - DeleteAdministrator(ctx context.Context, in *DeleteAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) - // PostCertificate sends a signed X.509 certificate to majordomo. - PostCertificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) - // PostSSHCertificate sends a signed SSH certificate to majordomo. - PostSSHCertificate(ctx context.Context, in *SSHCertificateRequest, opts ...grpc.CallOption) (*SSHCertificateResponse, error) - // RevokeCertificate marks an X.509 certificate as revoked. - RevokeCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) - // RevokeSSHCertificate marks an SSH certificate as revoked. - RevokeSSHCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) -} - -type majordomoClient struct { - cc grpc.ClientConnInterface -} - -func NewMajordomoClient(cc grpc.ClientConnInterface) MajordomoClient { - return &majordomoClient{cc} -} - -func (c *majordomoClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { - out := new(LoginResponse) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/Login", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) GetConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (*ConfigurationResponse, error) { - out := new(ConfigurationResponse) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/GetConfiguration", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) StreamConfiguration(ctx context.Context, in *ConfigurationRequest, opts ...grpc.CallOption) (Majordomo_StreamConfigurationClient, error) { - stream, err := c.cc.NewStream(ctx, &Majordomo_ServiceDesc.Streams[0], "/majordomo.Majordomo/StreamConfiguration", opts...) - if err != nil { - return nil, err - } - x := &majordomoStreamConfigurationClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Majordomo_StreamConfigurationClient interface { - Recv() (*ConfigurationResponse, error) - grpc.ClientStream -} - -type majordomoStreamConfigurationClient struct { - grpc.ClientStream -} - -func (x *majordomoStreamConfigurationClient) Recv() (*ConfigurationResponse, error) { - m := new(ConfigurationResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *majordomoClient) CreateProvisioner(ctx context.Context, in *CreateProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) { - out := new(Provisioner) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/CreateProvisioner", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) DeleteProvisioner(ctx context.Context, in *DeleteProvisionerRequest, opts ...grpc.CallOption) (*Provisioner, error) { - out := new(Provisioner) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/DeleteProvisioner", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) CreateAdministrator(ctx context.Context, in *CreateAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) { - out := new(Administrator) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/CreateAdministrator", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) DeleteAdministrator(ctx context.Context, in *DeleteAdministratorRequest, opts ...grpc.CallOption) (*Administrator, error) { - out := new(Administrator) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/DeleteAdministrator", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) PostCertificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) { - out := new(CertificateResponse) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/PostCertificate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) PostSSHCertificate(ctx context.Context, in *SSHCertificateRequest, opts ...grpc.CallOption) (*SSHCertificateResponse, error) { - out := new(SSHCertificateResponse) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/PostSSHCertificate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) RevokeCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) { - out := new(TODO) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/RevokeCertificate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *majordomoClient) RevokeSSHCertificate(ctx context.Context, in *TODO, opts ...grpc.CallOption) (*TODO, error) { - out := new(TODO) - err := c.cc.Invoke(ctx, "/majordomo.Majordomo/RevokeSSHCertificate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// MajordomoServer is the server API for Majordomo service. -// All implementations must embed UnimplementedMajordomoServer -// for forward compatibility -type MajordomoServer interface { - // Login creates signs a given CSR and returns the certificate that will be - // used for authentication. - Login(context.Context, *LoginRequest) (*LoginResponse, error) - // GetConfiguration returns the full configuration of an authority. - GetConfiguration(context.Context, *ConfigurationRequest) (*ConfigurationResponse, error) - // StreamConfiguration streams the full configuration of an authority. This - // method is not yet supported. - StreamConfiguration(*ConfigurationRequest, Majordomo_StreamConfigurationServer) error - // CreateProvisioner adds a new provisioner to the majordomo authority and - // returns the proto representation. - CreateProvisioner(context.Context, *CreateProvisionerRequest) (*Provisioner, error) - // DeleteProvisioner deletes a previously created provisioner. - DeleteProvisioner(context.Context, *DeleteProvisionerRequest) (*Provisioner, error) - // CreateAdministrator adds a new admin user to the majordomo authority. - // Admin users can add or delete provisioners. - CreateAdministrator(context.Context, *CreateAdministratorRequest) (*Administrator, error) - // DeleteAdministrator deletes a previously created admin user. - DeleteAdministrator(context.Context, *DeleteAdministratorRequest) (*Administrator, error) - // PostCertificate sends a signed X.509 certificate to majordomo. - PostCertificate(context.Context, *CertificateRequest) (*CertificateResponse, error) - // PostSSHCertificate sends a signed SSH certificate to majordomo. - PostSSHCertificate(context.Context, *SSHCertificateRequest) (*SSHCertificateResponse, error) - // RevokeCertificate marks an X.509 certificate as revoked. - RevokeCertificate(context.Context, *TODO) (*TODO, error) - // RevokeSSHCertificate marks an SSH certificate as revoked. - RevokeSSHCertificate(context.Context, *TODO) (*TODO, error) - mustEmbedUnimplementedMajordomoServer() -} - -// UnimplementedMajordomoServer must be embedded to have forward compatible implementations. -type UnimplementedMajordomoServer struct { -} - -func (UnimplementedMajordomoServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") -} -func (UnimplementedMajordomoServer) GetConfiguration(context.Context, *ConfigurationRequest) (*ConfigurationResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetConfiguration not implemented") -} -func (UnimplementedMajordomoServer) StreamConfiguration(*ConfigurationRequest, Majordomo_StreamConfigurationServer) error { - return status.Errorf(codes.Unimplemented, "method StreamConfiguration not implemented") -} -func (UnimplementedMajordomoServer) CreateProvisioner(context.Context, *CreateProvisionerRequest) (*Provisioner, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateProvisioner not implemented") -} -func (UnimplementedMajordomoServer) DeleteProvisioner(context.Context, *DeleteProvisionerRequest) (*Provisioner, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteProvisioner not implemented") -} -func (UnimplementedMajordomoServer) CreateAdministrator(context.Context, *CreateAdministratorRequest) (*Administrator, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateAdministrator not implemented") -} -func (UnimplementedMajordomoServer) DeleteAdministrator(context.Context, *DeleteAdministratorRequest) (*Administrator, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteAdministrator not implemented") -} -func (UnimplementedMajordomoServer) PostCertificate(context.Context, *CertificateRequest) (*CertificateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method PostCertificate not implemented") -} -func (UnimplementedMajordomoServer) PostSSHCertificate(context.Context, *SSHCertificateRequest) (*SSHCertificateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method PostSSHCertificate not implemented") -} -func (UnimplementedMajordomoServer) RevokeCertificate(context.Context, *TODO) (*TODO, error) { - return nil, status.Errorf(codes.Unimplemented, "method RevokeCertificate not implemented") -} -func (UnimplementedMajordomoServer) RevokeSSHCertificate(context.Context, *TODO) (*TODO, error) { - return nil, status.Errorf(codes.Unimplemented, "method RevokeSSHCertificate not implemented") -} -func (UnimplementedMajordomoServer) mustEmbedUnimplementedMajordomoServer() {} - -// UnsafeMajordomoServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to MajordomoServer will -// result in compilation errors. -type UnsafeMajordomoServer interface { - mustEmbedUnimplementedMajordomoServer() -} - -func RegisterMajordomoServer(s grpc.ServiceRegistrar, srv MajordomoServer) { - s.RegisterService(&Majordomo_ServiceDesc, srv) -} - -func _Majordomo_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LoginRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).Login(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/Login", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).Login(ctx, req.(*LoginRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_GetConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ConfigurationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).GetConfiguration(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/GetConfiguration", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).GetConfiguration(ctx, req.(*ConfigurationRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_StreamConfiguration_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ConfigurationRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(MajordomoServer).StreamConfiguration(m, &majordomoStreamConfigurationServer{stream}) -} - -type Majordomo_StreamConfigurationServer interface { - Send(*ConfigurationResponse) error - grpc.ServerStream -} - -type majordomoStreamConfigurationServer struct { - grpc.ServerStream -} - -func (x *majordomoStreamConfigurationServer) Send(m *ConfigurationResponse) error { - return x.ServerStream.SendMsg(m) -} - -func _Majordomo_CreateProvisioner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateProvisionerRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).CreateProvisioner(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/CreateProvisioner", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).CreateProvisioner(ctx, req.(*CreateProvisionerRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_DeleteProvisioner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProvisionerRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).DeleteProvisioner(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/DeleteProvisioner", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).DeleteProvisioner(ctx, req.(*DeleteProvisionerRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_CreateAdministrator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateAdministratorRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).CreateAdministrator(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/CreateAdministrator", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).CreateAdministrator(ctx, req.(*CreateAdministratorRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_DeleteAdministrator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteAdministratorRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).DeleteAdministrator(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/DeleteAdministrator", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).DeleteAdministrator(ctx, req.(*DeleteAdministratorRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_PostCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CertificateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).PostCertificate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/PostCertificate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).PostCertificate(ctx, req.(*CertificateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_PostSSHCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SSHCertificateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).PostSSHCertificate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/PostSSHCertificate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).PostSSHCertificate(ctx, req.(*SSHCertificateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_RevokeCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TODO) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).RevokeCertificate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/RevokeCertificate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).RevokeCertificate(ctx, req.(*TODO)) - } - return interceptor(ctx, in, info, handler) -} - -func _Majordomo_RevokeSSHCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TODO) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MajordomoServer).RevokeSSHCertificate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/majordomo.Majordomo/RevokeSSHCertificate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MajordomoServer).RevokeSSHCertificate(ctx, req.(*TODO)) - } - return interceptor(ctx, in, info, handler) -} - -// Majordomo_ServiceDesc is the grpc.ServiceDesc for Majordomo service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Majordomo_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "majordomo.Majordomo", - HandlerType: (*MajordomoServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Login", - Handler: _Majordomo_Login_Handler, - }, - { - MethodName: "GetConfiguration", - Handler: _Majordomo_GetConfiguration_Handler, - }, - { - MethodName: "CreateProvisioner", - Handler: _Majordomo_CreateProvisioner_Handler, - }, - { - MethodName: "DeleteProvisioner", - Handler: _Majordomo_DeleteProvisioner_Handler, - }, - { - MethodName: "CreateAdministrator", - Handler: _Majordomo_CreateAdministrator_Handler, - }, - { - MethodName: "DeleteAdministrator", - Handler: _Majordomo_DeleteAdministrator_Handler, - }, - { - MethodName: "PostCertificate", - Handler: _Majordomo_PostCertificate_Handler, - }, - { - MethodName: "PostSSHCertificate", - Handler: _Majordomo_PostSSHCertificate_Handler, - }, - { - MethodName: "RevokeCertificate", - Handler: _Majordomo_RevokeCertificate_Handler, - }, - { - MethodName: "RevokeSSHCertificate", - Handler: _Majordomo_RevokeSSHCertificate_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "StreamConfiguration", - Handler: _Majordomo_StreamConfiguration_Handler, - ServerStreams: true, - }, - }, - Metadata: "majordomo/majordomo.proto", -} diff --git a/linkedca/provisioners.pb.go b/linkedca/provisioners.pb.go index ccdd6f2f..1a8eb681 100644 --- a/linkedca/provisioners.pb.go +++ b/linkedca/provisioners.pb.go @@ -2,9 +2,9 @@ // versions: // protoc-gen-go v1.26.0 // protoc v3.15.8 -// source: majordomo/provisioners.proto +// source: linkedca/provisioners.proto -package majordomo +package linkedca import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -20,55 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Administrator_Type int32 - -const ( - Administrator_UNKNOWN Administrator_Type = 0 - Administrator_ADMIN Administrator_Type = 1 - Administrator_SUPER_ADMIN Administrator_Type = 2 -) - -// Enum value maps for Administrator_Type. -var ( - Administrator_Type_name = map[int32]string{ - 0: "UNKNOWN", - 1: "ADMIN", - 2: "SUPER_ADMIN", - } - Administrator_Type_value = map[string]int32{ - "UNKNOWN": 0, - "ADMIN": 1, - "SUPER_ADMIN": 2, - } -) - -func (x Administrator_Type) Enum() *Administrator_Type { - p := new(Administrator_Type) - *p = x - return p -} - -func (x Administrator_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Administrator_Type) Descriptor() protoreflect.EnumDescriptor { - return file_majordomo_provisioners_proto_enumTypes[0].Descriptor() -} - -func (Administrator_Type) Type() protoreflect.EnumType { - return &file_majordomo_provisioners_proto_enumTypes[0] -} - -func (x Administrator_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Administrator_Type.Descriptor instead. -func (Administrator_Type) EnumDescriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{0, 0} -} - type Provisioner_Type int32 const ( @@ -123,11 +74,11 @@ func (x Provisioner_Type) String() string { } func (Provisioner_Type) Descriptor() protoreflect.EnumDescriptor { - return file_majordomo_provisioners_proto_enumTypes[1].Descriptor() + return file_linkedca_provisioners_proto_enumTypes[0].Descriptor() } func (Provisioner_Type) Type() protoreflect.EnumType { - return &file_majordomo_provisioners_proto_enumTypes[1] + return &file_linkedca_provisioners_proto_enumTypes[0] } func (x Provisioner_Type) Number() protoreflect.EnumNumber { @@ -136,86 +87,7 @@ func (x Provisioner_Type) Number() protoreflect.EnumNumber { // Deprecated: Use Provisioner_Type.Descriptor instead. func (Provisioner_Type) EnumDescriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{1, 0} -} - -type Administrator struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` - ProvisionerId string `protobuf:"bytes,4,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` - Type Administrator_Type `protobuf:"varint,5,opt,name=type,proto3,enum=majordomo.Administrator_Type" json:"type,omitempty"` -} - -func (x *Administrator) Reset() { - *x = Administrator{} - if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Administrator) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Administrator) ProtoMessage() {} - -func (x *Administrator) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Administrator.ProtoReflect.Descriptor instead. -func (*Administrator) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{0} -} - -func (x *Administrator) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Administrator) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *Administrator) GetSubject() string { - if x != nil { - return x.Subject - } - return "" -} - -func (x *Administrator) GetProvisionerId() string { - if x != nil { - return x.ProvisionerId - } - return "" -} - -func (x *Administrator) GetType() Administrator_Type { - if x != nil { - return x.Type - } - return Administrator_UNKNOWN + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0, 0} } type Provisioner struct { @@ -225,7 +97,7 @@ type Provisioner struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Type Provisioner_Type `protobuf:"varint,3,opt,name=type,proto3,enum=majordomo.Provisioner_Type" json:"type,omitempty"` + Type Provisioner_Type `protobuf:"varint,3,opt,name=type,proto3,enum=linkedca.Provisioner_Type" json:"type,omitempty"` Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` Details *ProvisionerDetails `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` Claims *Claims `protobuf:"bytes,6,opt,name=claims,proto3" json:"claims,omitempty"` @@ -238,7 +110,7 @@ type Provisioner struct { func (x *Provisioner) Reset() { *x = Provisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[1] + mi := &file_linkedca_provisioners_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -251,7 +123,7 @@ func (x *Provisioner) String() string { func (*Provisioner) ProtoMessage() {} func (x *Provisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[1] + mi := &file_linkedca_provisioners_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -264,7 +136,7 @@ func (x *Provisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use Provisioner.ProtoReflect.Descriptor instead. func (*Provisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{1} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0} } func (x *Provisioner) GetId() string { @@ -358,7 +230,7 @@ type ProvisionerDetails struct { func (x *ProvisionerDetails) Reset() { *x = ProvisionerDetails{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[2] + mi := &file_linkedca_provisioners_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -371,7 +243,7 @@ func (x *ProvisionerDetails) String() string { func (*ProvisionerDetails) ProtoMessage() {} func (x *ProvisionerDetails) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[2] + mi := &file_linkedca_provisioners_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -384,7 +256,7 @@ func (x *ProvisionerDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use ProvisionerDetails.ProtoReflect.Descriptor instead. func (*ProvisionerDetails) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{2} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{1} } func (m *ProvisionerDetails) GetData() isProvisionerDetails_Data { @@ -526,7 +398,7 @@ type ProvisionerList struct { func (x *ProvisionerList) Reset() { *x = ProvisionerList{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[3] + mi := &file_linkedca_provisioners_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -539,7 +411,7 @@ func (x *ProvisionerList) String() string { func (*ProvisionerList) ProtoMessage() {} func (x *ProvisionerList) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[3] + mi := &file_linkedca_provisioners_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -552,7 +424,7 @@ func (x *ProvisionerList) ProtoReflect() protoreflect.Message { // Deprecated: Use ProvisionerList.ProtoReflect.Descriptor instead. func (*ProvisionerList) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{3} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{2} } func (x *ProvisionerList) GetProvisioners() []*Provisioner { @@ -575,7 +447,7 @@ type Claims struct { func (x *Claims) Reset() { *x = Claims{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[4] + mi := &file_linkedca_provisioners_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -588,7 +460,7 @@ func (x *Claims) String() string { func (*Claims) ProtoMessage() {} func (x *Claims) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[4] + mi := &file_linkedca_provisioners_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -601,7 +473,7 @@ func (x *Claims) ProtoReflect() protoreflect.Message { // Deprecated: Use Claims.ProtoReflect.Descriptor instead. func (*Claims) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{4} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{3} } func (x *Claims) GetX509() *X509Claims { @@ -637,7 +509,7 @@ type X509Claims struct { func (x *X509Claims) Reset() { *x = X509Claims{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[5] + mi := &file_linkedca_provisioners_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -650,7 +522,7 @@ func (x *X509Claims) String() string { func (*X509Claims) ProtoMessage() {} func (x *X509Claims) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[5] + mi := &file_linkedca_provisioners_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -663,7 +535,7 @@ func (x *X509Claims) ProtoReflect() protoreflect.Message { // Deprecated: Use X509Claims.ProtoReflect.Descriptor instead. func (*X509Claims) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{5} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{4} } func (x *X509Claims) GetEnabled() bool { @@ -693,7 +565,7 @@ type SSHClaims struct { func (x *SSHClaims) Reset() { *x = SSHClaims{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[6] + mi := &file_linkedca_provisioners_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -706,7 +578,7 @@ func (x *SSHClaims) String() string { func (*SSHClaims) ProtoMessage() {} func (x *SSHClaims) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[6] + mi := &file_linkedca_provisioners_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -719,7 +591,7 @@ func (x *SSHClaims) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHClaims.ProtoReflect.Descriptor instead. func (*SSHClaims) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{6} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{5} } func (x *SSHClaims) GetEnabled() bool { @@ -756,7 +628,7 @@ type Durations struct { func (x *Durations) Reset() { *x = Durations{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[7] + mi := &file_linkedca_provisioners_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -769,7 +641,7 @@ func (x *Durations) String() string { func (*Durations) ProtoMessage() {} func (x *Durations) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[7] + mi := &file_linkedca_provisioners_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -782,7 +654,7 @@ func (x *Durations) ProtoReflect() protoreflect.Message { // Deprecated: Use Durations.ProtoReflect.Descriptor instead. func (*Durations) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{7} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{6} } func (x *Durations) GetDefault() string { @@ -818,7 +690,7 @@ type JWKProvisioner struct { func (x *JWKProvisioner) Reset() { *x = JWKProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[8] + mi := &file_linkedca_provisioners_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -831,7 +703,7 @@ func (x *JWKProvisioner) String() string { func (*JWKProvisioner) ProtoMessage() {} func (x *JWKProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[8] + mi := &file_linkedca_provisioners_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -844,7 +716,7 @@ func (x *JWKProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use JWKProvisioner.ProtoReflect.Descriptor instead. func (*JWKProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{8} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{7} } func (x *JWKProvisioner) GetPublicKey() []byte { @@ -879,7 +751,7 @@ type OIDCProvisioner struct { func (x *OIDCProvisioner) Reset() { *x = OIDCProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[9] + mi := &file_linkedca_provisioners_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -892,7 +764,7 @@ func (x *OIDCProvisioner) String() string { func (*OIDCProvisioner) ProtoMessage() {} func (x *OIDCProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[9] + mi := &file_linkedca_provisioners_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -905,7 +777,7 @@ func (x *OIDCProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use OIDCProvisioner.ProtoReflect.Descriptor instead. func (*OIDCProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{9} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{8} } func (x *OIDCProvisioner) GetClientId() string { @@ -979,7 +851,7 @@ type GCPProvisioner struct { func (x *GCPProvisioner) Reset() { *x = GCPProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[10] + mi := &file_linkedca_provisioners_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -992,7 +864,7 @@ func (x *GCPProvisioner) String() string { func (*GCPProvisioner) ProtoMessage() {} func (x *GCPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[10] + mi := &file_linkedca_provisioners_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1005,7 +877,7 @@ func (x *GCPProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use GCPProvisioner.ProtoReflect.Descriptor instead. func (*GCPProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{10} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{9} } func (x *GCPProvisioner) GetServiceAccounts() []string { @@ -1057,7 +929,7 @@ type AWSProvisioner struct { func (x *AWSProvisioner) Reset() { *x = AWSProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[11] + mi := &file_linkedca_provisioners_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1070,7 +942,7 @@ func (x *AWSProvisioner) String() string { func (*AWSProvisioner) ProtoMessage() {} func (x *AWSProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[11] + mi := &file_linkedca_provisioners_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1083,7 +955,7 @@ func (x *AWSProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use AWSProvisioner.ProtoReflect.Descriptor instead. func (*AWSProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{11} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{10} } func (x *AWSProvisioner) GetAccounts() []string { @@ -1129,7 +1001,7 @@ type AzureProvisioner struct { func (x *AzureProvisioner) Reset() { *x = AzureProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[12] + mi := &file_linkedca_provisioners_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1142,7 +1014,7 @@ func (x *AzureProvisioner) String() string { func (*AzureProvisioner) ProtoMessage() {} func (x *AzureProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[12] + mi := &file_linkedca_provisioners_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1155,7 +1027,7 @@ func (x *AzureProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use AzureProvisioner.ProtoReflect.Descriptor instead. func (*AzureProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{12} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{11} } func (x *AzureProvisioner) GetTenantId() string { @@ -1204,7 +1076,7 @@ type ACMEProvisioner struct { func (x *ACMEProvisioner) Reset() { *x = ACMEProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[13] + mi := &file_linkedca_provisioners_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1217,7 +1089,7 @@ func (x *ACMEProvisioner) String() string { func (*ACMEProvisioner) ProtoMessage() {} func (x *ACMEProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[13] + mi := &file_linkedca_provisioners_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1230,7 +1102,7 @@ func (x *ACMEProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use ACMEProvisioner.ProtoReflect.Descriptor instead. func (*ACMEProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{13} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{12} } func (x *ACMEProvisioner) GetForceCn() bool { @@ -1251,7 +1123,7 @@ type X5CProvisioner struct { func (x *X5CProvisioner) Reset() { *x = X5CProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[14] + mi := &file_linkedca_provisioners_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1264,7 +1136,7 @@ func (x *X5CProvisioner) String() string { func (*X5CProvisioner) ProtoMessage() {} func (x *X5CProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[14] + mi := &file_linkedca_provisioners_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1277,7 +1149,7 @@ func (x *X5CProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use X5CProvisioner.ProtoReflect.Descriptor instead. func (*X5CProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{14} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{13} } func (x *X5CProvisioner) GetRoots() [][]byte { @@ -1298,7 +1170,7 @@ type K8SSAProvisioner struct { func (x *K8SSAProvisioner) Reset() { *x = K8SSAProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[15] + mi := &file_linkedca_provisioners_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1311,7 +1183,7 @@ func (x *K8SSAProvisioner) String() string { func (*K8SSAProvisioner) ProtoMessage() {} func (x *K8SSAProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[15] + mi := &file_linkedca_provisioners_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1324,7 +1196,7 @@ func (x *K8SSAProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use K8SSAProvisioner.ProtoReflect.Descriptor instead. func (*K8SSAProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{15} + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{14} } func (x *K8SSAProvisioner) GetPublicKeys() [][]byte { @@ -1343,7 +1215,7 @@ type SSHPOPProvisioner struct { func (x *SSHPOPProvisioner) Reset() { *x = SSHPOPProvisioner{} if protoimpl.UnsafeEnabled { - mi := &file_majordomo_provisioners_proto_msgTypes[16] + mi := &file_linkedca_provisioners_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1356,7 +1228,7 @@ func (x *SSHPOPProvisioner) String() string { func (*SSHPOPProvisioner) ProtoMessage() {} func (x *SSHPOPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_majordomo_provisioners_proto_msgTypes[16] + mi := &file_linkedca_provisioners_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1369,287 +1241,256 @@ func (x *SSHPOPProvisioner) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHPOPProvisioner.ProtoReflect.Descriptor instead. func (*SSHPOPProvisioner) Descriptor() ([]byte, []int) { - return file_majordomo_provisioners_proto_rawDescGZIP(), []int{16} -} - -var File_majordomo_provisioners_proto protoreflect.FileDescriptor - -var file_majordomo_provisioners_proto_rawDesc = []byte{ - 0x0a, 0x1c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x22, 0xe7, 0x01, 0x0a, 0x0d, 0x41, 0x64, - 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x31, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, - 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, - 0x4e, 0x10, 0x02, 0x22, 0xf7, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, - 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, - 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, - 0x23, 0x0a, 0x0d, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x10, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x22, 0x6a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, - 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, 0x12, - 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, - 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, 0x0a, - 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, 0x10, - 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x22, 0xde, 0x03, - 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2d, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4a, 0x57, + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{15} +} + +var File_linkedca_provisioners_proto protoreflect.FileDescriptor + +var file_linkedca_provisioners_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x22, 0xf4, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, + 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, + 0x61, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x10, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x22, 0x6a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, + 0x4f, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, + 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, + 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, + 0x52, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, + 0x0a, 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, + 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x22, 0xd5, + 0x03, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2c, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, - 0x4a, 0x57, 0x4b, 0x12, 0x30, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4f, 0x49, - 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, - 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2d, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x47, - 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, - 0x03, 0x47, 0x43, 0x50, 0x12, 0x2d, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, 0x57, - 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, - 0x41, 0x57, 0x53, 0x12, 0x33, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x41, - 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, - 0x00, 0x52, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, - 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, - 0x6d, 0x6f, 0x2e, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, 0x2d, 0x0a, 0x03, 0x58, 0x35, - 0x43, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, - 0x6f, 0x6d, 0x6f, 0x2e, 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x58, 0x35, 0x43, 0x12, 0x33, 0x0a, 0x05, 0x4b, 0x38, 0x73, - 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, - 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x12, 0x36, - 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x53, 0x53, 0x48, 0x50, 0x4f, - 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, - 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4d, - 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, - 0x6f, 0x6d, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, - 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x84, 0x01, - 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x78, 0x35, 0x30, 0x39, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, - 0x6d, 0x6f, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x04, 0x78, - 0x35, 0x30, 0x39, 0x12, 0x26, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x53, 0x53, 0x48, - 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6e, - 0x65, 0x77, 0x61, 0x6c, 0x22, 0x5a, 0x0a, 0x0a, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, + 0x4a, 0x57, 0x4b, 0x12, 0x2f, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4f, 0x49, 0x44, + 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, + 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2c, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x47, 0x43, 0x50, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x47, + 0x43, 0x50, 0x12, 0x2c, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x57, 0x53, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x41, 0x57, 0x53, + 0x12, 0x32, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x41, + 0x7a, 0x75, 0x72, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x43, + 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, 0x2c, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x18, 0x1a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, + 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, + 0x58, 0x35, 0x43, 0x12, 0x32, 0x0a, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4b, 0x38, + 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, + 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, + 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x42, 0x06, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, + 0x28, 0x0a, 0x04, 0x78, 0x35, 0x30, 0x39, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x73, 0x52, 0x04, 0x78, 0x35, 0x30, 0x39, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, + 0x61, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, + 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, + 0x77, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0a, 0x58, 0x35, 0x30, + 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x31, 0x0a, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x09, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x22, 0x9f, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x63, 0x0a, - 0x0e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, - 0x0a, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x65, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x22, 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xeb, 0x01, - 0x0a, 0x0e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0e, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, + 0x03, 0x6d, 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, + 0x63, 0x0a, 0x0e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x13, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x22, 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, + 0xeb, 0x01, 0x0a, 0x0e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, + 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, + 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, + 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, + 0x0a, 0x0e, 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, - 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x61, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, - 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0e, - 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1a, - 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x7a, - 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, - 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, - 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, - 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x0f, - 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, - 0x19, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, 0x58, 0x35, - 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, - 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x6f, 0x6f, - 0x74, 0x73, 0x22, 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, 0x50, 0x4f, - 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x42, 0x2d, 0x5a, 0x2b, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, - 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x73, 0x2f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x64, 0x6f, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, + 0x41, 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, + 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, + 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, + 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, + 0x0a, 0x0f, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, + 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, + 0x0a, 0x05, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, + 0x6f, 0x6f, 0x74, 0x73, 0x22, 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, + 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x42, 0x2c, + 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, + 0x6c, 0x6c, 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( - file_majordomo_provisioners_proto_rawDescOnce sync.Once - file_majordomo_provisioners_proto_rawDescData = file_majordomo_provisioners_proto_rawDesc + file_linkedca_provisioners_proto_rawDescOnce sync.Once + file_linkedca_provisioners_proto_rawDescData = file_linkedca_provisioners_proto_rawDesc ) -func file_majordomo_provisioners_proto_rawDescGZIP() []byte { - file_majordomo_provisioners_proto_rawDescOnce.Do(func() { - file_majordomo_provisioners_proto_rawDescData = protoimpl.X.CompressGZIP(file_majordomo_provisioners_proto_rawDescData) +func file_linkedca_provisioners_proto_rawDescGZIP() []byte { + file_linkedca_provisioners_proto_rawDescOnce.Do(func() { + file_linkedca_provisioners_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_provisioners_proto_rawDescData) }) - return file_majordomo_provisioners_proto_rawDescData -} - -var file_majordomo_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_majordomo_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 17) -var file_majordomo_provisioners_proto_goTypes = []interface{}{ - (Administrator_Type)(0), // 0: majordomo.Administrator.Type - (Provisioner_Type)(0), // 1: majordomo.Provisioner.Type - (*Administrator)(nil), // 2: majordomo.Administrator - (*Provisioner)(nil), // 3: majordomo.Provisioner - (*ProvisionerDetails)(nil), // 4: majordomo.ProvisionerDetails - (*ProvisionerList)(nil), // 5: majordomo.ProvisionerList - (*Claims)(nil), // 6: majordomo.Claims - (*X509Claims)(nil), // 7: majordomo.X509Claims - (*SSHClaims)(nil), // 8: majordomo.SSHClaims - (*Durations)(nil), // 9: majordomo.Durations - (*JWKProvisioner)(nil), // 10: majordomo.JWKProvisioner - (*OIDCProvisioner)(nil), // 11: majordomo.OIDCProvisioner - (*GCPProvisioner)(nil), // 12: majordomo.GCPProvisioner - (*AWSProvisioner)(nil), // 13: majordomo.AWSProvisioner - (*AzureProvisioner)(nil), // 14: majordomo.AzureProvisioner - (*ACMEProvisioner)(nil), // 15: majordomo.ACMEProvisioner - (*X5CProvisioner)(nil), // 16: majordomo.X5CProvisioner - (*K8SSAProvisioner)(nil), // 17: majordomo.K8sSAProvisioner - (*SSHPOPProvisioner)(nil), // 18: majordomo.SSHPOPProvisioner -} -var file_majordomo_provisioners_proto_depIdxs = []int32{ - 0, // 0: majordomo.Administrator.type:type_name -> majordomo.Administrator.Type - 1, // 1: majordomo.Provisioner.type:type_name -> majordomo.Provisioner.Type - 4, // 2: majordomo.Provisioner.details:type_name -> majordomo.ProvisionerDetails - 6, // 3: majordomo.Provisioner.claims:type_name -> majordomo.Claims - 10, // 4: majordomo.ProvisionerDetails.JWK:type_name -> majordomo.JWKProvisioner - 11, // 5: majordomo.ProvisionerDetails.OIDC:type_name -> majordomo.OIDCProvisioner - 12, // 6: majordomo.ProvisionerDetails.GCP:type_name -> majordomo.GCPProvisioner - 13, // 7: majordomo.ProvisionerDetails.AWS:type_name -> majordomo.AWSProvisioner - 14, // 8: majordomo.ProvisionerDetails.Azure:type_name -> majordomo.AzureProvisioner - 15, // 9: majordomo.ProvisionerDetails.ACME:type_name -> majordomo.ACMEProvisioner - 16, // 10: majordomo.ProvisionerDetails.X5C:type_name -> majordomo.X5CProvisioner - 17, // 11: majordomo.ProvisionerDetails.K8sSA:type_name -> majordomo.K8sSAProvisioner - 18, // 12: majordomo.ProvisionerDetails.SSHPOP:type_name -> majordomo.SSHPOPProvisioner - 3, // 13: majordomo.ProvisionerList.provisioners:type_name -> majordomo.Provisioner - 7, // 14: majordomo.Claims.x509:type_name -> majordomo.X509Claims - 8, // 15: majordomo.Claims.ssh:type_name -> majordomo.SSHClaims - 9, // 16: majordomo.X509Claims.durations:type_name -> majordomo.Durations - 9, // 17: majordomo.SSHClaims.user_durations:type_name -> majordomo.Durations - 9, // 18: majordomo.SSHClaims.host_durations:type_name -> majordomo.Durations - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name -} - -func init() { file_majordomo_provisioners_proto_init() } -func file_majordomo_provisioners_proto_init() { - if File_majordomo_provisioners_proto != nil { + return file_linkedca_provisioners_proto_rawDescData +} + +var file_linkedca_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_linkedca_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_linkedca_provisioners_proto_goTypes = []interface{}{ + (Provisioner_Type)(0), // 0: linkedca.Provisioner.Type + (*Provisioner)(nil), // 1: linkedca.Provisioner + (*ProvisionerDetails)(nil), // 2: linkedca.ProvisionerDetails + (*ProvisionerList)(nil), // 3: linkedca.ProvisionerList + (*Claims)(nil), // 4: linkedca.Claims + (*X509Claims)(nil), // 5: linkedca.X509Claims + (*SSHClaims)(nil), // 6: linkedca.SSHClaims + (*Durations)(nil), // 7: linkedca.Durations + (*JWKProvisioner)(nil), // 8: linkedca.JWKProvisioner + (*OIDCProvisioner)(nil), // 9: linkedca.OIDCProvisioner + (*GCPProvisioner)(nil), // 10: linkedca.GCPProvisioner + (*AWSProvisioner)(nil), // 11: linkedca.AWSProvisioner + (*AzureProvisioner)(nil), // 12: linkedca.AzureProvisioner + (*ACMEProvisioner)(nil), // 13: linkedca.ACMEProvisioner + (*X5CProvisioner)(nil), // 14: linkedca.X5CProvisioner + (*K8SSAProvisioner)(nil), // 15: linkedca.K8sSAProvisioner + (*SSHPOPProvisioner)(nil), // 16: linkedca.SSHPOPProvisioner +} +var file_linkedca_provisioners_proto_depIdxs = []int32{ + 0, // 0: linkedca.Provisioner.type:type_name -> linkedca.Provisioner.Type + 2, // 1: linkedca.Provisioner.details:type_name -> linkedca.ProvisionerDetails + 4, // 2: linkedca.Provisioner.claims:type_name -> linkedca.Claims + 8, // 3: linkedca.ProvisionerDetails.JWK:type_name -> linkedca.JWKProvisioner + 9, // 4: linkedca.ProvisionerDetails.OIDC:type_name -> linkedca.OIDCProvisioner + 10, // 5: linkedca.ProvisionerDetails.GCP:type_name -> linkedca.GCPProvisioner + 11, // 6: linkedca.ProvisionerDetails.AWS:type_name -> linkedca.AWSProvisioner + 12, // 7: linkedca.ProvisionerDetails.Azure:type_name -> linkedca.AzureProvisioner + 13, // 8: linkedca.ProvisionerDetails.ACME:type_name -> linkedca.ACMEProvisioner + 14, // 9: linkedca.ProvisionerDetails.X5C:type_name -> linkedca.X5CProvisioner + 15, // 10: linkedca.ProvisionerDetails.K8sSA:type_name -> linkedca.K8sSAProvisioner + 16, // 11: linkedca.ProvisionerDetails.SSHPOP:type_name -> linkedca.SSHPOPProvisioner + 1, // 12: linkedca.ProvisionerList.provisioners:type_name -> linkedca.Provisioner + 5, // 13: linkedca.Claims.x509:type_name -> linkedca.X509Claims + 6, // 14: linkedca.Claims.ssh:type_name -> linkedca.SSHClaims + 7, // 15: linkedca.X509Claims.durations:type_name -> linkedca.Durations + 7, // 16: linkedca.SSHClaims.user_durations:type_name -> linkedca.Durations + 7, // 17: linkedca.SSHClaims.host_durations:type_name -> linkedca.Durations + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name +} + +func init() { file_linkedca_provisioners_proto_init() } +func file_linkedca_provisioners_proto_init() { + if File_linkedca_provisioners_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_majordomo_provisioners_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Administrator); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_majordomo_provisioners_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provisioner); i { case 0: return &v.state @@ -1661,7 +1502,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProvisionerDetails); i { case 0: return &v.state @@ -1673,7 +1514,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProvisionerList); i { case 0: return &v.state @@ -1685,7 +1526,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Claims); i { case 0: return &v.state @@ -1697,7 +1538,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*X509Claims); i { case 0: return &v.state @@ -1709,7 +1550,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SSHClaims); i { case 0: return &v.state @@ -1721,7 +1562,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Durations); i { case 0: return &v.state @@ -1733,7 +1574,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JWKProvisioner); i { case 0: return &v.state @@ -1745,7 +1586,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OIDCProvisioner); i { case 0: return &v.state @@ -1757,7 +1598,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GCPProvisioner); i { case 0: return &v.state @@ -1769,7 +1610,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AWSProvisioner); i { case 0: return &v.state @@ -1781,7 +1622,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AzureProvisioner); i { case 0: return &v.state @@ -1793,7 +1634,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ACMEProvisioner); i { case 0: return &v.state @@ -1805,7 +1646,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*X5CProvisioner); i { case 0: return &v.state @@ -1817,7 +1658,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SSAProvisioner); i { case 0: return &v.state @@ -1829,7 +1670,7 @@ func file_majordomo_provisioners_proto_init() { return nil } } - file_majordomo_provisioners_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_linkedca_provisioners_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SSHPOPProvisioner); i { case 0: return &v.state @@ -1842,7 +1683,7 @@ func file_majordomo_provisioners_proto_init() { } } } - file_majordomo_provisioners_proto_msgTypes[2].OneofWrappers = []interface{}{ + file_linkedca_provisioners_proto_msgTypes[1].OneofWrappers = []interface{}{ (*ProvisionerDetails_JWK)(nil), (*ProvisionerDetails_OIDC)(nil), (*ProvisionerDetails_GCP)(nil), @@ -1857,19 +1698,19 @@ func file_majordomo_provisioners_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_majordomo_provisioners_proto_rawDesc, - NumEnums: 2, - NumMessages: 17, + RawDescriptor: file_linkedca_provisioners_proto_rawDesc, + NumEnums: 1, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_majordomo_provisioners_proto_goTypes, - DependencyIndexes: file_majordomo_provisioners_proto_depIdxs, - EnumInfos: file_majordomo_provisioners_proto_enumTypes, - MessageInfos: file_majordomo_provisioners_proto_msgTypes, + GoTypes: file_linkedca_provisioners_proto_goTypes, + DependencyIndexes: file_linkedca_provisioners_proto_depIdxs, + EnumInfos: file_linkedca_provisioners_proto_enumTypes, + MessageInfos: file_linkedca_provisioners_proto_msgTypes, }.Build() - File_majordomo_provisioners_proto = out.File - file_majordomo_provisioners_proto_rawDesc = nil - file_majordomo_provisioners_proto_goTypes = nil - file_majordomo_provisioners_proto_depIdxs = nil + File_linkedca_provisioners_proto = out.File + file_linkedca_provisioners_proto_rawDesc = nil + file_linkedca_provisioners_proto_goTypes = nil + file_linkedca_provisioners_proto_depIdxs = nil } diff --git a/linkedca/provisioners.proto b/linkedca/provisioners.proto index 7dd7f0c1..53f2c140 100644 --- a/linkedca/provisioners.proto +++ b/linkedca/provisioners.proto @@ -1,21 +1,8 @@ syntax = "proto3"; -package majordomo; +package linkedca; -option go_package = "github.com/smallstep/certificates/majordomo"; - -message Administrator { - enum Type { - UNKNOWN = 0; - ADMIN = 1; - SUPER_ADMIN = 2; - } - string id = 1; - string authority_id = 2; - string subject = 3; - string provisioner_id = 4; - Type type = 5; -} +option go_package = "github.com/smallstep/certificates/linkedca"; message Provisioner { enum Type { From 3f30552b60de2cb67860d3e3c3f9994c426306f1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 24 May 2021 12:46:16 -0700 Subject: [PATCH 017/291] Fix package name. --- linkedca/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linkedca/doc.go b/linkedca/doc.go index 9a64ca9c..ceddecb0 100644 --- a/linkedca/doc.go +++ b/linkedca/doc.go @@ -1,3 +1,3 @@ -package majordomo +package linkedca //go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative linkedca/provisioners.proto linkedca/admin.proto From 423942da4469a0dc41131b484df99692e9483a9d Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 24 May 2021 13:38:24 -0700 Subject: [PATCH 018/291] wip --- authority/admin/admin.go | 25 --- authority/admin/collection.go | 39 ++-- authority/mgmt/admin.go | 23 --- authority/mgmt/authConfig.go | 68 ------- authority/mgmt/config.go | 18 +- authority/mgmt/db.go | 93 +++------- authority/mgmt/provisioner.go | 334 ++++------------------------------ 7 files changed, 84 insertions(+), 516 deletions(-) delete mode 100644 authority/admin/admin.go delete mode 100644 authority/mgmt/admin.go delete mode 100644 authority/mgmt/authConfig.go diff --git a/authority/admin/admin.go b/authority/admin/admin.go deleted file mode 100644 index f0058777..00000000 --- a/authority/admin/admin.go +++ /dev/null @@ -1,25 +0,0 @@ -package admin - -import "github.com/smallstep/certificates/authority/status" - -// Type specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR -type Type string - -var ( - // TypeSuper superadmin - TypeSuper = Type("SUPER_ADMIN") - // TypeRegular regular - TypeRegular = Type("REGULAR") -) - -// Admin type. -type Admin struct { - ID string `json:"id"` - AuthorityID string `json:"-"` - Subject string `json:"subject"` - ProvisionerName string `json:"provisionerName"` - ProvisionerType string `json:"provisionerType"` - ProvisionerID string `json:"provisionerID"` - Type Type `json:"type"` - Status status.Type `json:"status"` -} diff --git a/authority/admin/collection.go b/authority/admin/collection.go index 971bbd9c..11ba35fc 100644 --- a/authority/admin/collection.go +++ b/authority/admin/collection.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/linkedca" ) // DefaultAdminLimit is the default limit for listing provisioners. @@ -20,7 +21,7 @@ const DefaultAdminLimit = 20 const DefaultAdminMax = 100 type uidAdmin struct { - admin *Admin + admin *linkedca.Admin uid string } @@ -54,7 +55,7 @@ func NewCollection(provisioners *provisioner.Collection) *Collection { } // LoadByID a admin by the ID. -func (c *Collection) LoadByID(id string) (*Admin, bool) { +func (c *Collection) LoadByID(id string) (*linkedca.Admin, bool) { return loadAdmin(c.byID, id) } @@ -66,17 +67,17 @@ func subProvNameHash(sub, provName string) string { } // LoadBySubProv a admin by the subject and provisioner name. -func (c *Collection) LoadBySubProv(sub, provName string) (*Admin, bool) { +func (c *Collection) LoadBySubProv(sub, provName string) (*linkedca.Admin, bool) { return loadAdmin(c.bySubProv, subProvNameHash(sub, provName)) } // LoadByProvisioner a admin by the subject and provisioner name. -func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) { +func (c *Collection) LoadByProvisioner(provName string) ([]*linkedca.Admin, bool) { a, ok := c.byProv.Load(provName) if !ok { return nil, false } - admins, ok := a.([]*Admin) + admins, ok := a.([]*linkedca.Admin) if !ok { return nil, false } @@ -85,22 +86,20 @@ func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) { // Store adds an admin to the collection and enforces the uniqueness of // admin IDs and amdin subject <-> provisioner name combos. -func (c *Collection) Store(adm *Admin) error { - p, ok := c.provisioners.Load(adm.ProvisionerID) +func (c *Collection) Store(adm *linkedca.Admin) error { + p, ok := c.provisioners.Load(adm.ProvisionerId) if !ok { - return fmt.Errorf("provisioner %s not found", adm.ProvisionerID) + return fmt.Errorf("provisioner %s not found", adm.ProvisionerId) } - adm.ProvisionerName = p.GetName() - adm.ProvisionerType = p.GetType().String() // Store admin always in byID. ID must be unique. - if _, loaded := c.byID.LoadOrStore(adm.ID, adm); loaded { + if _, loaded := c.byID.LoadOrStore(adm.Id, adm); loaded { return errors.New("cannot add multiple admins with the same id") } - provName := adm.ProvisionerName - // Store admin alwasy in bySubProv. Subject <-> ProvisionerName must be unique. + provName := p.GetName() + // Store admin always in bySubProv. Subject <-> ProvisionerName must be unique. if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded { - c.byID.Delete(adm.ID) + c.byID.Delete(adm.Id) return errors.New("cannot add multiple admins with the same subject and provisioner") } @@ -108,7 +107,7 @@ func (c *Collection) Store(adm *Admin) error { c.byProv.Store(provName, append(admins, adm)) c.superCountByProvisioner[provName]++ } else { - c.byProv.Store(provName, []*Admin{adm}) + c.byProv.Store(provName, []*linkedca.Admin{adm}) c.superCountByProvisioner[provName] = 1 } c.superCount++ @@ -118,7 +117,7 @@ func (c *Collection) Store(adm *Admin) error { // Using big endian format to get the strings sorted: // 0x00000000, 0x00000001, 0x00000002, ... bi := make([]byte, 4) - _sum := sha1.Sum([]byte(adm.ID)) + _sum := sha1.Sum([]byte(adm.Id)) sum := _sum[:] binary.BigEndian.PutUint32(bi, uint32(c.sorted.Len())) sum[0], sum[1], sum[2], sum[3] = bi[0], bi[1], bi[2], bi[3] @@ -145,7 +144,7 @@ func (c *Collection) SuperCountByProvisioner(provName string) int { } // Find implements pagination on a list of sorted provisioners. -func (c *Collection) Find(cursor string, limit int) ([]*Admin, string) { +func (c *Collection) Find(cursor string, limit int) ([]*linkedca.Admin, string) { switch { case limit <= 0: limit = DefaultAdminLimit @@ -157,7 +156,7 @@ func (c *Collection) Find(cursor string, limit int) ([]*Admin, string) { cursor = fmt.Sprintf("%040s", cursor) i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor }) - slice := []*Admin{} + slice := []*linkedca.Admin{} for ; i < n && len(slice) < limit; i++ { slice = append(slice, c.sorted[i].admin) } @@ -168,12 +167,12 @@ func (c *Collection) Find(cursor string, limit int) ([]*Admin, string) { return slice, "" } -func loadAdmin(m *sync.Map, key string) (*Admin, bool) { +func loadAdmin(m *sync.Map, key string) (*linkedca.Admin, bool) { a, ok := m.Load(key) if !ok { return nil, false } - adm, ok := a.(*Admin) + adm, ok := a.(*linkedca.Admin) if !ok { return nil, false } diff --git a/authority/mgmt/admin.go b/authority/mgmt/admin.go deleted file mode 100644 index d265a001..00000000 --- a/authority/mgmt/admin.go +++ /dev/null @@ -1,23 +0,0 @@ -package mgmt - -import ( - "github.com/smallstep/certificates/authority/admin" -) - -// AdminType specifies the type of the admin. e.g. SUPER_ADMIN, REGULAR -type AdminType admin.Type - -var ( - // AdminTypeSuper superadmin - AdminTypeSuper = admin.TypeSuper - // AdminTypeRegular regular - AdminTypeRegular = admin.TypeRegular -) - -// Admin type. -type Admin admin.Admin - -// ToCertificates converts an Admin to the Admin type expected by the authority. -func (adm *Admin) ToCertificates() (*admin.Admin, error) { - return (*admin.Admin)(adm), nil -} diff --git a/authority/mgmt/authConfig.go b/authority/mgmt/authConfig.go deleted file mode 100644 index 6284a9e7..00000000 --- a/authority/mgmt/authConfig.go +++ /dev/null @@ -1,68 +0,0 @@ -package mgmt - -import ( - "github.com/smallstep/certificates/authority/admin" - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/authority/status" -) - -// AuthConfig represents the Authority Configuration. -type AuthConfig struct { - //*cas.Options `json:"cas"` - ID string `json:"id"` - ASN1DN *config.ASN1DN `json:"asn1dn,omitempty"` - Provisioners []*Provisioner `json:"-"` - Admins []*Admin `json:"-"` - Claims *Claims `json:"claims,omitempty"` - Backdate string `json:"backdate,omitempty"` - Status status.Type `json:"status,omitempty"` -} - -func NewDefaultAuthConfig() *AuthConfig { - return &AuthConfig{ - Claims: NewDefaultClaims(), - ASN1DN: &config.ASN1DN{}, - Backdate: config.DefaultBackdate.String(), - Status: status.Active, - } -} - -// ToCertificates converts a mgmt AuthConfig to configuration that can be -// directly used by the `step-ca` process. Resources are normalized and -// initialized. -func (ac *AuthConfig) ToCertificates() (*config.AuthConfig, error) { - claims, err := ac.Claims.ToCertificates() - if err != nil { - return nil, err - } - backdate, err := provisioner.NewDuration(ac.Backdate) - if err != nil { - return nil, WrapErrorISE(err, "error converting backdate %s to duration", ac.Backdate) - } - var provs []provisioner.Interface - for _, p := range ac.Provisioners { - authProv, err := p.ToCertificates() - if err != nil { - return nil, err - } - provs = append(provs, authProv) - } - var admins []*admin.Admin - for _, adm := range ac.Admins { - authAdmin, err := adm.ToCertificates() - if err != nil { - return nil, err - } - admins = append(admins, authAdmin) - } - return &config.AuthConfig{ - AuthorityID: ac.ID, - Provisioners: provs, - Admins: admins, - Template: ac.ASN1DN, - Claims: claims, - DisableIssuedAtCheck: false, - Backdate: backdate, - }, nil -} diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index 8b23cad0..4df53b75 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -1,9 +1,6 @@ package mgmt import ( - "context" - - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" ) @@ -66,19 +63,7 @@ func NewDefaultClaims() *Claims { } } -type AuthorityOption func(*AuthConfig) error - -func WithDefaultAuthorityID(ac *AuthConfig) error { - ac.ID = DefaultAuthorityID - return nil -} - -func CreateDefaultAuthority(ctx context.Context, db DB) (*AuthConfig, error) { - options := []AuthorityOption{WithDefaultAuthorityID} - - return CreateAuthority(ctx, db, options...) -} - +/* func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*AuthConfig, error) { ac := NewDefaultAuthConfig() @@ -116,3 +101,4 @@ func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*A return ac, nil } +*/ diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go index 9cfab3e5..9716bb00 100644 --- a/authority/mgmt/db.go +++ b/authority/mgmt/db.go @@ -4,6 +4,7 @@ import ( "context" "github.com/pkg/errors" + "github.com/smallstep/certificates/linkedca" ) // ErrNotFound is an error that should be used by the authority.DB interface to @@ -12,44 +13,36 @@ var ErrNotFound = errors.New("not found") // DB is the DB interface expected by the step-ca ACME API. type DB interface { - CreateProvisioner(ctx context.Context, prov *Provisioner) error - GetProvisioner(ctx context.Context, id string) (*Provisioner, error) - GetProvisioners(ctx context.Context) ([]*Provisioner, error) - UpdateProvisioner(ctx context.Context, prov *Provisioner) error - - CreateAdmin(ctx context.Context, admin *Admin) error - GetAdmin(ctx context.Context, id string) (*Admin, error) - GetAdmins(ctx context.Context) ([]*Admin, error) - UpdateAdmin(ctx context.Context, admin *Admin) error - - CreateAuthConfig(ctx context.Context, ac *AuthConfig) error - GetAuthConfig(ctx context.Context, id string) (*AuthConfig, error) - UpdateAuthConfig(ctx context.Context, ac *AuthConfig) error + CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error + GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) + GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) + UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error + + CreateAdmin(ctx context.Context, admin *linkedca.Admin) error + GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) + GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) + UpdateAdmin(ctx context.Context, admin *linkedca.Admin) error } // MockDB is an implementation of the DB interface that should only be used as // a mock in tests. type MockDB struct { - MockCreateProvisioner func(ctx context.Context, prov *Provisioner) error - MockGetProvisioner func(ctx context.Context, id string) (*Provisioner, error) - MockGetProvisioners func(ctx context.Context) ([]*Provisioner, error) - MockUpdateProvisioner func(ctx context.Context, prov *Provisioner) error + MockCreateProvisioner func(ctx context.Context, prov *linkedca.Provisioner) error + MockGetProvisioner func(ctx context.Context, id string) (*linkedca.Provisioner, error) + MockGetProvisioners func(ctx context.Context) ([]*linkedca.Provisioner, error) + MockUpdateProvisioner func(ctx context.Context, prov *linkedca.Provisioner) error - MockCreateAdmin func(ctx context.Context, adm *Admin) error - MockGetAdmin func(ctx context.Context, id string) (*Admin, error) - MockGetAdmins func(ctx context.Context) ([]*Admin, error) - MockUpdateAdmin func(ctx context.Context, adm *Admin) error - - MockCreateAuthConfig func(ctx context.Context, ac *AuthConfig) error - MockGetAuthConfig func(ctx context.Context, id string) (*AuthConfig, error) - MockUpdateAuthConfig func(ctx context.Context, ac *AuthConfig) error + MockCreateAdmin func(ctx context.Context, adm *linkedca.Admin) error + MockGetAdmin func(ctx context.Context, id string) (*linkedca.Admin, error) + MockGetAdmins func(ctx context.Context) ([]*linkedca.Admin, error) + MockUpdateAdmin func(ctx context.Context, adm *linkedca.Admin) error MockError error MockRet1 interface{} } // CreateProvisioner mock. -func (m *MockDB) CreateProvisioner(ctx context.Context, prov *Provisioner) error { +func (m *MockDB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { if m.MockCreateProvisioner != nil { return m.MockCreateProvisioner(ctx, prov) } else if m.MockError != nil { @@ -59,27 +52,27 @@ func (m *MockDB) CreateProvisioner(ctx context.Context, prov *Provisioner) error } // GetProvisioner mock. -func (m *MockDB) GetProvisioner(ctx context.Context, id string) (*Provisioner, error) { +func (m *MockDB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { if m.MockGetProvisioner != nil { return m.MockGetProvisioner(ctx, id) } else if m.MockError != nil { return nil, m.MockError } - return m.MockRet1.(*Provisioner), m.MockError + return m.MockRet1.(*linkedca.Provisioner), m.MockError } // GetProvisioners mock -func (m *MockDB) GetProvisioners(ctx context.Context) ([]*Provisioner, error) { +func (m *MockDB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { if m.MockGetProvisioners != nil { return m.MockGetProvisioners(ctx) } else if m.MockError != nil { return nil, m.MockError } - return m.MockRet1.([]*Provisioner), m.MockError + return m.MockRet1.([]*linkedca.Provisioner), m.MockError } // UpdateProvisioner mock -func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *Provisioner) error { +func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { if m.MockUpdateProvisioner != nil { return m.MockUpdateProvisioner(ctx, prov) } @@ -87,7 +80,7 @@ func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *Provisioner) error } // CreateAdmin mock -func (m *MockDB) CreateAdmin(ctx context.Context, admin *Admin) error { +func (m *MockDB) CreateAdmin(ctx context.Context, admin *linkedca.Admin) error { if m.MockCreateAdmin != nil { return m.MockCreateAdmin(ctx, admin) } @@ -95,55 +88,29 @@ func (m *MockDB) CreateAdmin(ctx context.Context, admin *Admin) error { } // GetAdmin mock. -func (m *MockDB) GetAdmin(ctx context.Context, id string) (*Admin, error) { +func (m *MockDB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) { if m.MockGetAdmin != nil { return m.MockGetAdmin(ctx, id) } else if m.MockError != nil { return nil, m.MockError } - return m.MockRet1.(*Admin), m.MockError + return m.MockRet1.(*linkedca.Admin), m.MockError } // GetAdmins mock -func (m *MockDB) GetAdmins(ctx context.Context) ([]*Admin, error) { +func (m *MockDB) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { if m.MockGetAdmins != nil { return m.MockGetAdmins(ctx) } else if m.MockError != nil { return nil, m.MockError } - return m.MockRet1.([]*Admin), m.MockError + return m.MockRet1.([]*linkedca.Admin), m.MockError } // UpdateAdmin mock -func (m *MockDB) UpdateAdmin(ctx context.Context, adm *Admin) error { +func (m *MockDB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { if m.MockUpdateAdmin != nil { return m.MockUpdateAdmin(ctx, adm) } return m.MockError } - -// CreateAuthConfig mock -func (m *MockDB) CreateAuthConfig(ctx context.Context, admin *AuthConfig) error { - if m.MockCreateAuthConfig != nil { - return m.MockCreateAuthConfig(ctx, admin) - } - return m.MockError -} - -// GetAuthConfig mock. -func (m *MockDB) GetAuthConfig(ctx context.Context, id string) (*AuthConfig, error) { - if m.MockGetAuthConfig != nil { - return m.MockGetAuthConfig(ctx, id) - } else if m.MockError != nil { - return nil, m.MockError - } - return m.MockRet1.(*AuthConfig), m.MockError -} - -// UpdateAuthConfig mock -func (m *MockDB) UpdateAuthConfig(ctx context.Context, adm *AuthConfig) error { - if m.MockUpdateAuthConfig != nil { - return m.MockUpdateAuthConfig(ctx, adm) - } - return m.MockError -} diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 493f74b1..3c3042d0 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -1,31 +1,15 @@ package mgmt import ( - "context" "encoding/json" "fmt" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/authority/status" + "github.com/smallstep/certificates/linkedca" "go.step.sm/crypto/jose" ) -type ProvisionerOption func(*ProvisionerCtx) - -type ProvisionerType string - -var ( - ProvisionerTypeACME = ProvisionerType("ACME") - ProvisionerTypeAWS = ProvisionerType("AWS") - ProvisionerTypeAZURE = ProvisionerType("AZURE") - ProvisionerTypeGCP = ProvisionerType("GCP") - ProvisionerTypeJWK = ProvisionerType("JWK") - ProvisionerTypeK8SSA = ProvisionerType("K8SSA") - ProvisionerTypeOIDC = ProvisionerType("OIDC") - ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP") - ProvisionerTypeX5C = ProvisionerType("X5C") -) - +/* type unmarshalProvisioner struct { ID string `json:"-"` AuthorityID string `json:"-"` @@ -40,23 +24,8 @@ type unmarshalProvisioner struct { Status status.Type `json:"status"` } -// Provisioner type. -type Provisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Type string `json:"type"` - Name string `json:"name"` - Claims *Claims `json:"claims"` - Details ProvisionerDetails `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - Status status.Type `json:"status"` -} - type typ struct { - Type ProvisionerType `json:"type"` + Type linkedca.Provisioner_Type `json:"type"` } // UnmarshalJSON implements the Unmarshal interface. @@ -86,287 +55,48 @@ func (p *Provisioner) UnmarshalJSON(b []byte) error { return nil } +*/ -func (p *Provisioner) GetOptions() *provisioner.Options { +func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { return &provisioner.Options{ X509: &provisioner.X509Options{ - Template: p.X509Template, + Template: string(p.X509Template), TemplateData: p.X509TemplateData, }, SSH: &provisioner.SSHOptions{ - Template: p.SSHTemplate, - TemplateData: p.SSHTemplateData, + Template: string(p.SshTemplate), + TemplateData: p.SshTemplateData, }, } } -func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...ProvisionerOption) (*Provisioner, error) { - pc := NewProvisionerCtx(opts...) - details, err := NewProvisionerDetails(ProvisionerType(typ), pc) +// provisionerToCertificates converts the landlord provisioner type to the open source +// provisioner type. +func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { + claims, err := claimsToCertificates(p.Claims) if err != nil { return nil, err } - p := &Provisioner{ - Type: typ, - Name: name, - Claims: pc.Claims, - Details: details, - X509Template: pc.X509Template, - X509TemplateData: pc.X509TemplateData, - SSHTemplate: pc.SSHTemplate, - SSHTemplateData: pc.SSHTemplateData, - Status: status.Active, - } - - if err := db.CreateProvisioner(ctx, p); err != nil { - return nil, WrapErrorISE(err, "error creating provisioner") - } - return p, nil -} - -type ProvisionerCtx struct { - JWK *jose.JSONWebKey - JWE *jose.JSONWebEncryption - X509Template, SSHTemplate string - X509TemplateData, SSHTemplateData []byte - Claims *Claims - Password string -} - -func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx { - pc := &ProvisionerCtx{ - Claims: NewDefaultClaims(), - } - for _, o := range opts { - o(pc) - } - return pc -} - -func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) { - return func(ctx *ProvisionerCtx) { - ctx.JWK = jwk - ctx.JWE = jwe - } -} - -func WithPassword(pass string) func(*ProvisionerCtx) { - return func(ctx *ProvisionerCtx) { - ctx.Password = pass - } -} - -// ProvisionerDetails is the interface implemented by all provisioner details -// attributes. -type ProvisionerDetails interface { - isProvisionerDetails() -} - -// ProvisionerDetailsJWK represents the values required by a JWK provisioner. -type ProvisionerDetailsJWK struct { - Type ProvisionerType `json:"type"` - PublicKey []byte `json:"publicKey"` - PrivateKey string `json:"PrivateKey"` -} - -// ProvisionerDetailsOIDC represents the values required by a OIDC provisioner. -type ProvisionerDetailsOIDC struct { - Type ProvisionerType `json:"type"` - ClientID string `json:"clientID"` - ClientSecret string `json:"clientSecret"` - ConfigurationEndpoint string `json:"configurationEndpoint"` - Admins []string `json:"admins"` - Domains []string `json:"domains"` - Groups []string `json:"groups"` - ListenAddress string `json:"listenAddress"` - TenantID string `json:"tenantID"` -} - -// ProvisionerDetailsGCP represents the values required by a GCP provisioner. -type ProvisionerDetailsGCP struct { - Type ProvisionerType `json:"type"` - ServiceAccounts []string `json:"serviceAccounts"` - ProjectIDs []string `json:"projectIDs"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` - InstanceAge string `json:"instanceAge"` -} - -// ProvisionerDetailsAWS represents the values required by a AWS provisioner. -type ProvisionerDetailsAWS struct { - Type ProvisionerType `json:"type"` - Accounts []string `json:"accounts"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` - InstanceAge string `json:"instanceAge"` -} - -// ProvisionerDetailsAzure represents the values required by a Azure provisioner. -type ProvisionerDetailsAzure struct { - Type ProvisionerType `json:"type"` - ResourceGroups []string `json:"resourceGroups"` - Audience string `json:"audience"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` -} - -// ProvisionerDetailsACME represents the values required by a ACME provisioner. -type ProvisionerDetailsACME struct { - Type ProvisionerType `json:"type"` - ForceCN bool `json:"forceCN"` -} - -// ProvisionerDetailsX5C represents the values required by a X5C provisioner. -type ProvisionerDetailsX5C struct { - Type ProvisionerType `json:"type"` - Roots []byte `json:"roots"` -} - -// ProvisionerDetailsK8SSA represents the values required by a K8SSA provisioner. -type ProvisionerDetailsK8SSA struct { - Type ProvisionerType `json:"type"` - PublicKeys []byte `json:"publicKeys"` -} - -// ProvisionerDetailsSSHPOP represents the values required by a SSHPOP provisioner. -type ProvisionerDetailsSSHPOP struct { - Type ProvisionerType `json:"type"` -} - -func (*ProvisionerDetailsJWK) isProvisionerDetails() {} - -func (*ProvisionerDetailsOIDC) isProvisionerDetails() {} - -func (*ProvisionerDetailsGCP) isProvisionerDetails() {} - -func (*ProvisionerDetailsAWS) isProvisionerDetails() {} - -func (*ProvisionerDetailsAzure) isProvisionerDetails() {} - -func (*ProvisionerDetailsACME) isProvisionerDetails() {} - -func (*ProvisionerDetailsX5C) isProvisionerDetails() {} - -func (*ProvisionerDetailsK8SSA) isProvisionerDetails() {} - -func (*ProvisionerDetailsSSHPOP) isProvisionerDetails() {} - -func NewProvisionerDetails(typ ProvisionerType, pc *ProvisionerCtx) (ProvisionerDetails, error) { - switch typ { - case ProvisionerTypeJWK: - return createJWKDetails(pc) - /* - case ProvisionerTypeOIDC: - return createOIDCDetails(pc) - case ProvisionerTypeACME: - return createACMEDetails(pc) - case ProvisionerTypeK8SSA: - return createK8SSADetails(pc) - case ProvisionerTypeSSHPOP: - return createSSHPOPDetails(pc) - case ProvisionerTypeX5C: - return createSSHPOPDetails(pc) - */ - default: - return nil, NewErrorISE("unsupported provisioner type %s", typ) - } -} - -func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { - var err error - - if pc.JWK != nil && pc.JWE == nil { - return nil, NewErrorISE("JWE is required with JWK for createJWKProvisioner") - } - if pc.JWE != nil && pc.JWK == nil { - return nil, NewErrorISE("JWK is required with JWE for createJWKProvisioner") - } - if pc.JWK == nil && pc.JWE == nil { - // Create a new JWK w/ encrypted private key. - if pc.Password == "" { - return nil, NewErrorISE("password is required to provisioner with new keys") - } - pc.JWK, pc.JWE, err = jose.GenerateDefaultKeyPair([]byte(pc.Password)) - if err != nil { - return nil, WrapErrorISE(err, "error generating JWK key pair") - } - } - - jwkPubBytes, err := pc.JWK.MarshalJSON() - if err != nil { - return nil, WrapErrorISE(err, "error marshaling JWK") - } - jwePrivStr, err := pc.JWE.CompactSerialize() - if err != nil { - return nil, WrapErrorISE(err, "error serializing JWE") - } - - return &ProvisionerDetailsJWK{ - Type: ProvisionerTypeJWK, - PublicKey: jwkPubBytes, - PrivateKey: jwePrivStr, - }, nil -} - -func createACMEDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) { - var err error - - if pc.JWK != nil && pc.JWE == nil { - return nil, NewErrorISE("JWE is required with JWK for createJWKProvisioner") - } - if pc.JWE != nil && pc.JWK == nil { - return nil, NewErrorISE("JWK is required with JWE for createJWKProvisioner") - } - if pc.JWK == nil && pc.JWE == nil { - // Create a new JWK w/ encrypted private key. - if pc.Password == "" { - return nil, NewErrorISE("password is required to provisioner with new keys") - } - pc.JWK, pc.JWE, err = jose.GenerateDefaultKeyPair([]byte(pc.Password)) - if err != nil { - return nil, WrapErrorISE(err, "error generating JWK key pair") - } - } - - jwkPubBytes, err := pc.JWK.MarshalJSON() - if err != nil { - return nil, WrapErrorISE(err, "error marshaling JWK") - } - jwePrivStr, err := pc.JWE.CompactSerialize() - if err != nil { - return nil, WrapErrorISE(err, "error serializing JWE") - } - - return &ProvisionerDetailsJWK{ - Type: ProvisionerTypeJWK, - PublicKey: jwkPubBytes, - PrivateKey: jwePrivStr, - }, nil -} - -// ToCertificates converts the landlord provisioner type to the open source -// provisioner type. -func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { - claims, err := p.Claims.ToCertificates() - if err != nil { - return nil, err + details := p.Details.GetData() + if details == nil { + return nil, fmt.Errorf("provisioner does not have any details") } - switch details := p.Details.(type) { - case *ProvisionerDetailsJWK: + switch d := details.(type) { + case *linkedca.ProvisionerDetails_JWK: jwk := new(jose.JSONWebKey) - if err := json.Unmarshal(details.PublicKey, &jwk); err != nil { + if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { return nil, err } return &provisioner.JWK{ - ID: p.ID, - Type: p.Type, + ID: p.Id, + Type: p.Type.String(), Name: p.Name, Key: jwk, - EncryptedKey: details.PrivateKey, + EncryptedKey: string(d.JWK.EncryptedPrivateKey), Claims: claims, - Options: p.GetOptions(), + Options: provisionerGetOptions(p), }, nil /* case *ProvisionerDetails_OIDC: @@ -478,9 +208,9 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) { } } -// ToCertificates converts the landlord provisioner claims type to the open source +// claimsToCertificates converts the landlord provisioner claims type to the open source // (step-ca) claims type. -func (c *Claims) ToCertificates() (*provisioner.Claims, error) { +func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { var durs = map[string]struct { durStr string dur *provisioner.Duration @@ -488,12 +218,12 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) { "minTLSDur": {durStr: c.X509.Durations.Min}, "maxTLSDur": {durStr: c.X509.Durations.Max}, "defaultTLSDur": {durStr: c.X509.Durations.Default}, - "minSSHUserDur": {durStr: c.SSH.UserDurations.Min}, - "maxSSHUserDur": {durStr: c.SSH.UserDurations.Max}, - "defaultSSHUserDur": {durStr: c.SSH.UserDurations.Default}, - "minSSHHostDur": {durStr: c.SSH.HostDurations.Min}, - "maxSSHHostDur": {durStr: c.SSH.HostDurations.Max}, - "defaultSSHHostDur": {durStr: c.SSH.HostDurations.Default}, + "minSSHUserDur": {durStr: c.Ssh.UserDurations.Min}, + "maxSSHUserDur": {durStr: c.Ssh.UserDurations.Max}, + "defaultSSHUserDur": {durStr: c.Ssh.UserDurations.Default}, + "minSSHHostDur": {durStr: c.Ssh.HostDurations.Min}, + "maxSSHHostDur": {durStr: c.Ssh.HostDurations.Max}, + "defaultSSHHostDur": {durStr: c.Ssh.HostDurations.Default}, } var err error for k, v := range durs { @@ -513,10 +243,11 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) { MinHostSSHDur: durs["minSSHHostDur"].dur, MaxHostSSHDur: durs["maxSSHHostDur"].dur, DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, - EnableSSHCA: &c.SSH.Enabled, + EnableSSHCA: &c.Ssh.Enabled, }, nil } +/* type detailsType struct { Type ProvisionerType } @@ -557,3 +288,4 @@ func UnmarshalProvisionerDetails(data json.RawMessage) (ProvisionerDetails, erro } return v, nil } +*/ From 1726076ea2bc1d1c9466f544af4cf574edee7d2f Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 25 May 2021 16:52:06 -0700 Subject: [PATCH 019/291] wip --- authority/admin/collection.go | 9 -- authority/config/config.go | 4 +- authority/mgmt/config.go | 42 ++------- authority/mgmt/db.go | 20 +++++ authority/mgmt/db/nosql/admin.go | 91 ++++++++++--------- authority/mgmt/db/nosql/authConfig.go | 117 ------------------------- authority/mgmt/db/nosql/provisioner.go | 86 +++++++++--------- authority/mgmt/provisioner.go | 43 --------- linkedca/provisioners.go | 38 ++++++++ 9 files changed, 153 insertions(+), 297 deletions(-) delete mode 100644 authority/mgmt/db/nosql/authConfig.go create mode 100644 linkedca/provisioners.go diff --git a/authority/admin/collection.go b/authority/admin/collection.go index 11ba35fc..e9d41113 100644 --- a/authority/admin/collection.go +++ b/authority/admin/collection.go @@ -178,12 +178,3 @@ func loadAdmin(m *sync.Map, key string) (*linkedca.Admin, bool) { } return adm, true } - -/* -// provisionerSum returns the SHA1 of the provisioners ID. From this we will -// create the unique and sorted id. -func provisionerSum(p Interface) []byte { - sum := sha1.Sum([]byte(p.GetID())) - return sum[:] -} -*/ diff --git a/authority/config/config.go b/authority/config/config.go index 9fbf18e0..55041142 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -8,11 +8,11 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/provisioner" cas "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" kms "github.com/smallstep/certificates/kms/apiv1" + "github.com/smallstep/certificates/linkedca" "github.com/smallstep/certificates/templates" ) @@ -96,7 +96,7 @@ type AuthConfig struct { *cas.Options AuthorityID string `json:"authorityID,omitempty"` Provisioners provisioner.List `json:"provisioners"` - Admins []*admin.Admin `json:"-"` + Admins []*linkedca.Admin `json:"-"` Template *ASN1DN `json:"template,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index 4df53b75..ba821fc7 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -2,6 +2,7 @@ package mgmt import ( "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/linkedca" ) const ( @@ -11,49 +12,22 @@ const ( DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) -// Claims encapsulates all x509 and ssh claims applied to the authority -// configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc. -type Claims struct { - X509 *X509Claims `json:"x509Claims"` - SSH *SSHClaims `json:"sshClaims"` - DisableRenewal bool `json:"disableRenewal"` -} - -// X509Claims are the x509 claims applied to the authority. -type X509Claims struct { - Durations *Durations `json:"durations"` -} - -// SSHClaims are the ssh claims applied to the authority. -type SSHClaims struct { - Enabled bool `json:"enabled"` - UserDurations *Durations `json:"userDurations"` - HostDurations *Durations `json:"hostDurations"` -} - -// Durations represents min, max, default, duration. -type Durations struct { - Min string `json:"min"` - Max string `json:"max"` - Default string `json:"default"` -} - -func NewDefaultClaims() *Claims { - return &Claims{ - X509: &X509Claims{ - Durations: &Durations{ +func NewDefaultClaims() *linkedca.Claims { + return &linkedca.Claims{ + X509: &linkedca.X509Claims{ + Durations: &linkedca.Durations{ Min: config.GlobalProvisionerClaims.MinTLSDur.String(), Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), }, }, - SSH: &SSHClaims{ - UserDurations: &Durations{ + Ssh: &linkedca.SSHClaims{ + UserDurations: &linkedca.Durations{ Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), }, - HostDurations: &Durations{ + HostDurations: &linkedca.Durations{ Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), diff --git a/authority/mgmt/db.go b/authority/mgmt/db.go index 9716bb00..64ed39a2 100644 --- a/authority/mgmt/db.go +++ b/authority/mgmt/db.go @@ -17,11 +17,13 @@ type DB interface { GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error + DeleteProvisioner(ctx context.Context, id string) error CreateAdmin(ctx context.Context, admin *linkedca.Admin) error GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) UpdateAdmin(ctx context.Context, admin *linkedca.Admin) error + DeleteAdmin(ctx context.Context, id string) error } // MockDB is an implementation of the DB interface that should only be used as @@ -31,11 +33,13 @@ type MockDB struct { MockGetProvisioner func(ctx context.Context, id string) (*linkedca.Provisioner, error) MockGetProvisioners func(ctx context.Context) ([]*linkedca.Provisioner, error) MockUpdateProvisioner func(ctx context.Context, prov *linkedca.Provisioner) error + MockDeleteProvisioner func(ctx context.Context, id string) error MockCreateAdmin func(ctx context.Context, adm *linkedca.Admin) error MockGetAdmin func(ctx context.Context, id string) (*linkedca.Admin, error) MockGetAdmins func(ctx context.Context) ([]*linkedca.Admin, error) MockUpdateAdmin func(ctx context.Context, adm *linkedca.Admin) error + MockDeleteAdmin func(ctx context.Context, id string) error MockError error MockRet1 interface{} @@ -79,6 +83,14 @@ func (m *MockDB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provision return m.MockError } +// DeleteProvisioner mock +func (m *MockDB) DeleteProvisioner(ctx context.Context, id string) error { + if m.MockDeleteProvisioner != nil { + return m.MockDeleteProvisioner(ctx, id) + } + return m.MockError +} + // CreateAdmin mock func (m *MockDB) CreateAdmin(ctx context.Context, admin *linkedca.Admin) error { if m.MockCreateAdmin != nil { @@ -114,3 +126,11 @@ func (m *MockDB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { } return m.MockError } + +// DeleteAdmin mock +func (m *MockDB) DeleteAdmin(ctx context.Context, id string) error { + if m.MockDeleteAdmin != nil { + return m.MockDeleteAdmin(ctx, id) + } + return m.MockError +} diff --git a/authority/mgmt/db/nosql/admin.go b/authority/mgmt/db/nosql/admin.go index 9732b3f0..8fd0fea5 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/mgmt/db/nosql/admin.go @@ -6,21 +6,20 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/status" + "github.com/smallstep/certificates/linkedca" "github.com/smallstep/nosql" ) // dbAdmin is the database representation of the Admin type. type dbAdmin struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - ProvisionerID string `json:"provisionerID"` - Subject string `json:"subject"` - Type admin.Type `json:"type"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + ProvisionerID string `json:"provisionerID"` + Subject string `json:"subject"` + Type linkedca.Admin_Type `json:"type"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } func (dbp *dbAdmin) clone() *dbAdmin { @@ -59,30 +58,28 @@ func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { if err := json.Unmarshal(data, dba); err != nil { return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) } + if !dba.DeletedAt.IsZero() { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", id) + } return dba, nil } -func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) { - var dba = new(dbAdmin) - if err := json.Unmarshal(data, dba); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) +func unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { + dba, err := unmarshalDBAdmin(data, id) + if err != nil { + return nil, err } - adm := &mgmt.Admin{ - ID: dba.ID, - AuthorityID: dba.AuthorityID, - ProvisionerID: dba.ProvisionerID, + return &linkedca.Admin{ + Id: dba.ID, + AuthorityId: dba.AuthorityID, + ProvisionerId: dba.ProvisionerID, Subject: dba.Subject, Type: dba.Type, - Status: status.Active, - } - if !dba.DeletedAt.IsZero() { - adm.Status = status.Deleted - } - return adm, nil + }, nil } // GetAdmin retrieves and unmarshals a admin from the database. -func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { +func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) { data, err := db.getDBAdminBytes(ctx, id) if err != nil { return nil, err @@ -91,12 +88,9 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { if err != nil { return nil, err } - if adm.Status == status.Deleted { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID) - } - if adm.AuthorityID != db.authorityID { + if adm.AuthorityId != db.authorityID { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "admin %s is not owned by authority %s", adm.ID, db.authorityID) + "admin %s is not owned by authority %s", adm.Id, db.authorityID) } return adm, nil @@ -105,21 +99,18 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) { // GetAdmins retrieves and unmarshals all active (not deleted) admins // from the database. // TODO should we be paginating? -func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { +func (db *DB) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { dbEntries, err := db.db.List(authorityAdminsTable) if err != nil { return nil, errors.Wrap(err, "error loading admins") } - var admins = []*mgmt.Admin{} + var admins = []*linkedca.Admin{} for _, entry := range dbEntries { adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) if err != nil { return nil, err } - if adm.Status == status.Deleted { - continue - } - if adm.AuthorityID != db.authorityID { + if adm.AuthorityId != db.authorityID { continue } admins = append(admins, adm) @@ -128,18 +119,18 @@ func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) { } // CreateAdmin stores a new admin to the database. -func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { +func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { var err error - adm.ID, err = randID() + adm.Id, err = randID() if err != nil { return mgmt.WrapErrorISE(err, "error generating random id for admin") } - adm.AuthorityID = db.authorityID + adm.AuthorityId = db.authorityID dba := &dbAdmin{ - ID: adm.ID, + ID: adm.Id, AuthorityID: db.authorityID, - ProvisionerID: adm.ProvisionerID, + ProvisionerID: adm.ProvisionerId, Subject: adm.Subject, Type: adm.Type, CreatedAt: clock.Now(), @@ -149,19 +140,27 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error { } // UpdateAdmin saves an updated admin to the database. -func (db *DB) UpdateAdmin(ctx context.Context, adm *mgmt.Admin) error { - old, err := db.getDBAdmin(ctx, adm.ID) +func (db *DB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { + old, err := db.getDBAdmin(ctx, adm.Id) if err != nil { return err } nu := old.clone() + nu.Type = adm.Type + + return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) +} - // If the admin was active but is now deleted ... - if old.DeletedAt.IsZero() && adm.Status == status.Deleted { - nu.DeletedAt = clock.Now() +// DeleteAdmin saves an updated admin to the database. +func (db *DB) DeleteAdmin(ctx context.Context, id string) error { + old, err := db.getDBAdmin(ctx, id) + if err != nil { + return err } - nu.Type = adm.Type + + nu := old.clone() + nu.DeletedAt = clock.Now() return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) } diff --git a/authority/mgmt/db/nosql/authConfig.go b/authority/mgmt/db/nosql/authConfig.go deleted file mode 100644 index 222e729d..00000000 --- a/authority/mgmt/db/nosql/authConfig.go +++ /dev/null @@ -1,117 +0,0 @@ -package nosql - -import ( - "context" - "encoding/json" - "time" - - "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/status" - "github.com/smallstep/nosql" -) - -type dbAuthConfig struct { - ID string `json:"id"` - ASN1DN *config.ASN1DN `json:"asn1dn"` - Claims *mgmt.Claims `json:"claims"` - Backdate string `json:"backdate,omitempty"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` -} - -func (dbp *dbAuthConfig) clone() *dbAuthConfig { - u := *dbp - return &u -} - -func (db *DB) getDBAuthConfigBytes(ctx context.Context, id string) ([]byte, error) { - data, err := db.db.Get(authorityConfigsTable, []byte(id)) - if nosql.IsErrNotFound(err) { - return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "authConfig %s not found", id) - } else if err != nil { - return nil, errors.Wrapf(err, "error loading authConfig %s", id) - } - return data, nil -} - -func (db *DB) getDBAuthConfig(ctx context.Context, id string) (*dbAuthConfig, error) { - data, err := db.getDBAuthConfigBytes(ctx, id) - if err != nil { - return nil, err - } - - var dba = new(dbAuthConfig) - if err = json.Unmarshal(data, dba); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling authority %s into dbAuthConfig", id) - } - - return dba, nil -} - -// GetAuthConfig retrieves an AuthConfig configuration from the DB. -func (db *DB) GetAuthConfig(ctx context.Context, id string) (*mgmt.AuthConfig, error) { - dba, err := db.getDBAuthConfig(ctx, id) - if err != nil { - return nil, err - } - - provs, err := db.GetProvisioners(ctx) - if err != nil { - return nil, err - } - admins, err := db.GetAdmins(ctx) - if err != nil { - return nil, err - } - - return &mgmt.AuthConfig{ - ID: dba.ID, - Admins: admins, - Provisioners: provs, - ASN1DN: dba.ASN1DN, - Backdate: dba.Backdate, - Claims: dba.Claims, - }, nil -} - -// CreateAuthConfig stores a new provisioner to the database. -func (db *DB) CreateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { - var err error - if ac.ID == "" { - ac.ID, err = randID() - if err != nil { - return errors.Wrap(err, "error generating random id for provisioner") - } - } - - dba := &dbAuthConfig{ - ID: ac.ID, - ASN1DN: ac.ASN1DN, - Claims: ac.Claims, - Backdate: ac.Backdate, - CreatedAt: clock.Now(), - } - - return db.save(ctx, dba.ID, dba, nil, "authConfig", authorityConfigsTable) -} - -// UpdateAuthConfig saves an updated provisioner to the database. -func (db *DB) UpdateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error { - old, err := db.getDBAuthConfig(ctx, ac.ID) - if err != nil { - return err - } - - nu := old.clone() - - // If the authority was active but is now deleted ... - if old.DeletedAt.IsZero() && ac.Status == status.Deleted { - nu.DeletedAt = clock.Now() - } - nu.Claims = ac.Claims - nu.Backdate = ac.Backdate - - return db.save(ctx, old.ID, nu, old, "authConfig", authorityProvisionersTable) -} diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index 2c2d65b0..473736e8 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -7,25 +7,25 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/status" + "github.com/smallstep/certificates/linkedca" "github.com/smallstep/nosql" ) // dbProvisioner is the database representation of a Provisioner type. type dbProvisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Type string `json:"type"` + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type linkedca.Provisioner_Type `json:"type"` // Name is the key - Name string `json:"name"` - Claims *mgmt.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` + Name string `json:"name"` + Claims *linkedca.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template []byte `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate []byte `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` } type provisionerNameID struct { @@ -68,7 +68,7 @@ func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, } // GetProvisioner retrieves and unmarshals a provisioner from the database. -func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, error) { +func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { data, err := db.getDBProvisionerBytes(ctx, id) if err != nil { return nil, err @@ -78,12 +78,9 @@ func (db *DB) GetProvisioner(ctx context.Context, id string) (*mgmt.Provisioner, if err != nil { return nil, err } - if prov.Status == status.Deleted { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", prov.ID) - } - if prov.AuthorityID != db.authorityID { + if prov.AuthorityId != db.authorityID { return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", prov.ID, db.authorityID) + "provisioner %s is not owned by authority %s", prov.Id, db.authorityID) } return prov, nil } @@ -93,56 +90,52 @@ func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) { if err := json.Unmarshal(data, dbp); err != nil { return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", name) } + if !dbp.DeletedAt.IsZero() { + return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name) + } return dbp, nil } -func unmarshalProvisioner(data []byte, name string) (*mgmt.Provisioner, error) { +func unmarshalProvisioner(data []byte, name string) (*linkedca.Provisioner, error) { dbp, err := unmarshalDBProvisioner(data, name) if err != nil { return nil, err } - details, err := mgmt.UnmarshalProvisionerDetails(dbp.Details) + details, err := linkedca.UnmarshalProvisionerDetails(dbp.Type, dbp.Details) if err != nil { return nil, err } - prov := &mgmt.Provisioner{ - ID: dbp.ID, - AuthorityID: dbp.AuthorityID, + prov := &linkedca.Provisioner{ + Id: dbp.ID, + AuthorityId: dbp.AuthorityID, Type: dbp.Type, Name: dbp.Name, Claims: dbp.Claims, Details: details, - Status: status.Active, X509Template: dbp.X509Template, X509TemplateData: dbp.X509TemplateData, - SSHTemplate: dbp.SSHTemplate, - SSHTemplateData: dbp.SSHTemplateData, - } - if !dbp.DeletedAt.IsZero() { - prov.Status = status.Deleted + SshTemplate: dbp.SSHTemplate, + SshTemplateData: dbp.SSHTemplateData, } return prov, nil } // GetProvisioners retrieves and unmarshals all active (not deleted) provisioners // from the database. -func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) { +func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { dbEntries, err := db.db.List(authorityProvisionersTable) if err != nil { return nil, mgmt.WrapErrorISE(err, "error loading provisioners") } - var provs []*mgmt.Provisioner + var provs []*linkedca.Provisioner for _, entry := range dbEntries { prov, err := unmarshalProvisioner(entry.Value, string(entry.Key)) if err != nil { return nil, err } - if prov.Status == status.Deleted { - continue - } - if prov.AuthorityID != db.authorityID { + if prov.AuthorityId != db.authorityID { continue } provs = append(provs, prov) @@ -151,9 +144,9 @@ func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) } // CreateProvisioner stores a new provisioner to the database. -func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { +func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { var err error - prov.ID, err = randID() + prov.Id, err = randID() if err != nil { return errors.Wrap(err, "error generating random id for provisioner") } @@ -164,7 +157,7 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err } dbp := &dbProvisioner{ - ID: prov.ID, + ID: prov.Id, AuthorityID: db.authorityID, Type: prov.Type, Name: prov.Name, @@ -172,12 +165,12 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err Details: details, X509Template: prov.X509Template, X509TemplateData: prov.X509TemplateData, - SSHTemplate: prov.SSHTemplate, - SSHTemplateData: prov.SSHTemplateData, + SSHTemplate: prov.SshTemplate, + SSHTemplateData: prov.SshTemplateData, CreatedAt: clock.Now(), } - if err := db.save(ctx, prov.ID, dbp, nil, "provisioner", authorityProvisionersTable); err != nil { + if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", authorityProvisionersTable); err != nil { return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name) } @@ -185,8 +178,8 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err } // UpdateProvisioner saves an updated provisioner to the database. -func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) error { - old, err := db.getDBProvisioner(ctx, prov.ID) +func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + old, err := db.getDBProvisioner(ctx, prov.Id) if err != nil { return err } @@ -202,9 +195,10 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err } nu.X509Template = prov.X509Template nu.X509TemplateData = prov.X509TemplateData - nu.SSHTemplateData = prov.SSHTemplateData + nu.SSHTemplate = prov.SshTemplate + nu.SSHTemplateData = prov.SshTemplateData - if err := db.save(ctx, prov.ID, nu, old, "provisioner", authorityProvisionersTable); err != nil { + if err := db.save(ctx, prov.Id, nu, old, "provisioner", authorityProvisionersTable); err != nil { return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name) } diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 3c3042d0..74e442f4 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -246,46 +246,3 @@ func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { EnableSSHCA: &c.Ssh.Enabled, }, nil } - -/* -type detailsType struct { - Type ProvisionerType -} - -// UnmarshalProvisionerDetails unmarshals bytes into the proper details type. -func UnmarshalProvisionerDetails(data json.RawMessage) (ProvisionerDetails, error) { - dt := new(detailsType) - if err := json.Unmarshal(data, dt); err != nil { - return nil, WrapErrorISE(err, "error unmarshaling provisioner details") - } - - var v ProvisionerDetails - switch dt.Type { - case ProvisionerTypeJWK: - v = new(ProvisionerDetailsJWK) - case ProvisionerTypeOIDC: - v = new(ProvisionerDetailsOIDC) - case ProvisionerTypeGCP: - v = new(ProvisionerDetailsGCP) - case ProvisionerTypeAWS: - v = new(ProvisionerDetailsAWS) - case ProvisionerTypeAZURE: - v = new(ProvisionerDetailsAzure) - case ProvisionerTypeACME: - v = new(ProvisionerDetailsACME) - case ProvisionerTypeX5C: - v = new(ProvisionerDetailsX5C) - case ProvisionerTypeK8SSA: - v = new(ProvisionerDetailsK8SSA) - case ProvisionerTypeSSHPOP: - v = new(ProvisionerDetailsSSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", dt.Type) - } - - if err := json.Unmarshal(data, v); err != nil { - return nil, err - } - return v, nil -} -*/ diff --git a/linkedca/provisioners.go b/linkedca/provisioners.go new file mode 100644 index 00000000..ea46f342 --- /dev/null +++ b/linkedca/provisioners.go @@ -0,0 +1,38 @@ +package linkedca + +import ( + "encoding/json" + "fmt" +) + +// UnmarshalProvisionerDetails unmarshals details type to the specific provisioner details. +func UnmarshalProvisionerDetails(typ Provisioner_Type, data []byte) (*ProvisionerDetails, error) { + var v isProvisionerDetails_Data + switch typ { + case Provisioner_JWK: + v = new(ProvisionerDetails_JWK) + case Provisioner_OIDC: + v = new(ProvisionerDetails_OIDC) + case Provisioner_GCP: + v = new(ProvisionerDetails_GCP) + case Provisioner_AWS: + v = new(ProvisionerDetails_AWS) + case Provisioner_AZURE: + v = new(ProvisionerDetails_Azure) + case Provisioner_ACME: + v = new(ProvisionerDetails_ACME) + case Provisioner_X5C: + v = new(ProvisionerDetails_X5C) + case Provisioner_K8SSA: + v = new(ProvisionerDetails_K8SSA) + case Provisioner_SSHPOP: + v = new(ProvisionerDetails_SSHPOP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal(data, v); err != nil { + return nil, err + } + return &ProvisionerDetails{Data: v}, nil +} From 01a4460812bcdd92b5ca65f059c47b990fb27064 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 25 May 2021 21:13:01 -0700 Subject: [PATCH 020/291] wip --- authority/authority.go | 76 ++++++--- authority/mgmt/api/admin.go | 40 ++--- authority/mgmt/api/authConfig.go | 87 ---------- authority/mgmt/api/handler.go | 4 - authority/mgmt/api/provisioner.go | 58 +++---- authority/mgmt/config.go | 30 ---- authority/mgmt/db/nosql/provisioner.go | 13 ++ authority/mgmt/provisioner.go | 222 +++++-------------------- authority/provisioners.go | 207 +++++++++++++++++++++++ ca/adminClient.go | 67 +++----- 10 files changed, 376 insertions(+), 428 deletions(-) delete mode 100644 authority/mgmt/api/authConfig.go diff --git a/authority/authority.go b/authority/authority.go index c564f530..3e089397 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -11,6 +11,7 @@ import ( "time" "github.com/smallstep/certificates/cas" + "github.com/smallstep/certificates/linkedca" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" @@ -129,15 +130,18 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } -func (a *Authority) ReloadAuthConfig() error { - mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) +func (a *Authority) ReloadAuthConfig(ctx context.Context) error { + provs, err := a.adminDB.GetProvisioners(ctx) if err != nil { - return mgmt.WrapErrorISE(err, "error getting authConfig from db") + return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") } - - a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates() + a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) + if err != nil { + return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") + } + a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(ctx) if err != nil { - return mgmt.WrapErrorISE(err, "error converting mgmt authConfig to certificates authConfig") + return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") } // Merge global and configuration claims @@ -148,7 +152,7 @@ func (a *Authority) ReloadAuthConfig() error { // TODO: should we also be combining the ssh federated roots here? // If we rotate ssh roots keys, sshpop provisioner will lose ability to // validate old SSH certificates, unless they are added as federated certs. - sshKeys, err := a.GetSSHRoots(context.Background()) + sshKeys, err := a.GetSSHRoots(ctx) if err != nil { return err } @@ -201,30 +205,52 @@ func (a *Authority) init() error { } } - // Initialize step-ca Admin Database if it's not already initialized using - // WithAdminDB. - if a.adminDB == nil { - // Check if AuthConfig already exists - a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) - if err != nil { - if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) { - mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.adminDB, mgmt.WithDefaultAuthorityID) - if err != nil { - return mgmt.WrapErrorISE(err, "error creating authConfig") - } - } else { - return mgmt.WrapErrorISE(err, "error getting authConfig from db") + if len(a.config.AuthorityConfig.Provisioners) == 0 { + // Initialize step-ca Admin Database if it's not already initialized using + // WithAdminDB. + if a.adminDB == nil { + // Check if AuthConfig already exists + a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) + if err != nil { + return err } } - a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates() + provs, err := a.adminDB.GetProvisioners(context.Background()) if err != nil { return err } + if len(provs) == 0 { + // Create First Provisioner + prov, err := mgmt.CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) + if err != nil { + return err + } + // Create First Admin + adm := &linkedca.Admin{ + ProvisionerId: prov.Id, + Subject: "step", + Type: linkedca.Admin_SUPER_ADMIN, + } + if err := a.adminDB.CreateAdmin(context.Background(), adm); err != nil { + // TODO should we try to clean up? + return mgmt.WrapErrorISE(err, "error creating first admin") + } + a.config.AuthorityConfig.Admins = []*linkedca.Admin{adm} + } else { + provs, err := a.adminDB.GetProvisioners(context.Background()) + if err != nil { + return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") + } + a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) + if err != nil { + return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") + } + a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(context.Background()) + if err != nil { + return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") + } + } } // Initialize key manager if it has not been set in the options. diff --git a/authority/mgmt/api/admin.go b/authority/mgmt/api/admin.go index 04ff2a93..30c78555 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/mgmt/api/admin.go @@ -8,14 +8,14 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/status" + "github.com/smallstep/certificates/linkedca" ) // CreateAdminRequest represents the body for a CreateAdmin request. type CreateAdminRequest struct { - Subject string `json:"subject"` - Provisioner string `json:"provisioner"` - Type admin.Type `json:"type"` + Subject string `json:"subject"` + Provisioner string `json:"provisioner"` + Type linkedca.Admin_Type `json:"type"` } // Validate validates a new-admin request body. @@ -29,13 +29,13 @@ func (car *CreateAdminRequest) Validate(c *admin.Collection) error { // GetAdminsResponse for returning a list of admins. type GetAdminsResponse struct { - Admins []*admin.Admin `json:"admins"` - NextCursor string `json:"nextCursor"` + Admins []*linkedca.Admin `json:"admins"` + NextCursor string `json:"nextCursor"` } // UpdateAdminRequest represents the body for a UpdateAdmin request. type UpdateAdminRequest struct { - Type admin.Type `json:"type"` + Type linkedca.Admin_Type `json:"type"` } // Validate validates a new-admin request body. @@ -98,20 +98,17 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { return } - adm := &mgmt.Admin{ - ProvisionerID: p.GetID(), + adm := &linkedca.Admin{ + ProvisionerId: p.GetID(), Subject: body.Subject, Type: body.Type, - Status: status.Active, } if err := h.db.CreateAdmin(ctx, adm); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) return } - adm.ProvisionerName = p.GetName() - adm.ProvisionerType = p.GetType().String() api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(); err != nil { + if err := h.auth.ReloadAuthConfig(ctx); err != nil { fmt.Printf("err = %+v\n", err) } } @@ -126,18 +123,13 @@ func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { } ctx := r.Context() - adm, err := h.db.GetAdmin(ctx, id) - if err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id)) - return - } - adm.Status = status.Deleted - if err := h.db.UpdateAdmin(ctx, adm); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) + if err := h.db.DeleteAdmin(ctx, id); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s", id)) return } api.JSON(w, &DeleteResponse{Status: "ok"}) - if err := h.auth.ReloadAuthConfig(); err != nil { + + if err := h.auth.ReloadAuthConfig(ctx); err != nil { fmt.Printf("err = %+v\n", err) } } @@ -166,12 +158,12 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { adm.Type = body.Type - if err := h.db.UpdateAdmin(ctx, (*mgmt.Admin)(adm)); err != nil { + if err := h.db.UpdateAdmin(ctx, (*linkedca.Admin)(adm)); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) return } api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(); err != nil { + if err := h.auth.ReloadAuthConfig(ctx); err != nil { fmt.Printf("err = %+v\n", err) } } diff --git a/authority/mgmt/api/authConfig.go b/authority/mgmt/api/authConfig.go deleted file mode 100644 index 7d5f7afb..00000000 --- a/authority/mgmt/api/authConfig.go +++ /dev/null @@ -1,87 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/go-chi/chi" - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" -) - -// CreateAuthConfigRequest represents the body for a CreateAuthConfig request. -type CreateAuthConfigRequest struct { - ASN1DN *config.ASN1DN `json:"asn1dn,omitempty"` - Claims *mgmt.Claims `json:"claims,omitempty"` - Backdate string `json:"backdate,omitempty"` -} - -// Validate validates a CreateAuthConfig request body. -func (car *CreateAuthConfigRequest) Validate() error { - return nil -} - -// UpdateAuthConfigRequest represents the body for a UpdateAuthConfig request. -type UpdateAuthConfigRequest struct { - ASN1DN *config.ASN1DN `json:"asn1dn"` - Claims *mgmt.Claims `json:"claims"` - Backdate string `json:"backdate,omitempty"` -} - -// Validate validates a new-admin request body. -func (uar *UpdateAuthConfigRequest) Validate() error { - return nil -} - -// GetAuthConfig returns the requested admin, or an error. -func (h *Handler) GetAuthConfig(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - id := chi.URLParam(r, "id") - - ac, err := h.db.GetAuthConfig(ctx, id) - if err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, ac) -} - -// UpdateAuthConfig updates an existing AuthConfig. -func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) { - /* - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateAuthConfigRequest - if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - ac, err := h.db.GetAuthConfig(ctx, id) - if err != nil { - api.WriteError(w, err) - return - } - - ac.Status = body.Status - if body.ASN1DN != nil { - ac.ASN1DN = body.ASN1DN - } - if body.Claims != nil { - ac.Claims = body.Claims - } - if body.Backdate != "" { - ac.Backdate = body.Backdate - } - - if err := h.db.UpdateAuthConfig(ctx, ac); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, ac) - */ -} diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go index 8b6f915b..ad8b35ad 100644 --- a/authority/mgmt/api/handler.go +++ b/authority/mgmt/api/handler.go @@ -44,8 +44,4 @@ func (h *Handler) Route(r api.Router) { r.MethodFunc("POST", "/admins", h.CreateAdmin) r.MethodFunc("PATCH", "/admins/{id}", h.UpdateAdmin) r.MethodFunc("DELETE", "/admins/{id}", h.DeleteAdmin) - - // AuthConfig - r.MethodFunc("GET", "/authconfigs/{id}", h.GetAuthConfig) - r.MethodFunc("PUT", "/authconfigs/{id}", h.UpdateAuthConfig) } diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go index 3697d6e0..7964d36b 100644 --- a/authority/mgmt/api/provisioner.go +++ b/authority/mgmt/api/provisioner.go @@ -8,20 +8,20 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/authority/status" "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/linkedca" ) // CreateProvisionerRequest represents the body for a CreateProvisioner request. type CreateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *mgmt.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` + Type string `json:"type"` + Name string `json:"name"` + Claims *linkedca.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` } // Validate validates a new-provisioner request body. @@ -40,14 +40,14 @@ type GetProvisionersResponse struct { // UpdateProvisionerRequest represents the body for a UpdateProvisioner request. type UpdateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *mgmt.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` + Type string `json:"type"` + Name string `json:"name"` + Claims *linkedca.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template string `json:"x509Template"` + X509TemplateData []byte `json:"x509TemplateData"` + SSHTemplate string `json:"sshTemplate"` + SSHTemplateData []byte `json:"sshTemplateData"` } // Validate validates a update-provisioner request body. @@ -101,7 +101,7 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - var prov = new(mgmt.Provisioner) + var prov = new(linkedca.Provisioner) if err := api.ReadJSON(r.Body, prov); err != nil { api.WriteError(w, err) return @@ -118,7 +118,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { } api.JSONStatus(w, prov, http.StatusCreated) - if err := h.auth.ReloadAuthConfig(); err != nil { + if err := h.auth.ReloadAuthConfig(ctx); err != nil { fmt.Printf("err = %+v\n", err) } } @@ -141,14 +141,8 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { } ctx := r.Context() - prov, err := h.db.GetProvisioner(ctx, p.GetID()) - if err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error loading provisioner %s from db", name)) - return - } - prov.Status = status.Deleted - if err := h.db.UpdateProvisioner(ctx, prov); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error updating provisioner %s", name)) + if err := h.db.DeleteProvisioner(ctx, p.GetID()); err != nil { + api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting provisioner %s", name)) return } @@ -156,13 +150,7 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { admins, ok := c.LoadByProvisioner(name) if ok { for _, adm := range admins { - if err := h.db.UpdateAdmin(ctx, &mgmt.Admin{ - ID: adm.ID, - ProvisionerID: adm.ProvisionerID, - Subject: adm.Subject, - Type: adm.Type, - Status: status.Deleted, - }); err != nil { + if err := h.db.DeleteAdmin(ctx, adm.Id); err != nil { api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, name)) return } @@ -171,7 +159,7 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { api.JSON(w, &DeleteResponse{Status: "ok"}) - if err := h.auth.ReloadAuthConfig(); err != nil { + if err := h.auth.ReloadAuthConfig(ctx); err != nil { fmt.Printf("err = %+v\n", err) } } diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go index ba821fc7..3c322415 100644 --- a/authority/mgmt/config.go +++ b/authority/mgmt/config.go @@ -1,10 +1,5 @@ package mgmt -import ( - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/linkedca" -) - const ( // DefaultAuthorityID is the default AuthorityID. This will be the ID // of the first Authority created, as well as the default AuthorityID @@ -12,31 +7,6 @@ const ( DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) -func NewDefaultClaims() *linkedca.Claims { - return &linkedca.Claims{ - X509: &linkedca.X509Claims{ - Durations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinTLSDur.String(), - Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), - Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), - }, - }, - Ssh: &linkedca.SSHClaims{ - UserDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), - }, - HostDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), - }, - }, - DisableRenewal: config.DefaultDisableRenewal, - } -} - /* func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*AuthConfig, error) { ac := NewDefaultAuthConfig() diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index 473736e8..1924aee3 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -204,3 +204,16 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) return nil } + +// DeleteProvisioner saves an updated admin to the database. +func (db *DB) DeleteProvisioner(ctx context.Context, id string) error { + old, err := db.getDBProvisioner(ctx, id) + if err != nil { + return err + } + + nu := old.clone() + nu.DeletedAt = clock.Now() + + return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) +} diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 74e442f4..0386a894 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -1,10 +1,9 @@ package mgmt import ( - "encoding/json" - "fmt" + "context" - "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/linkedca" "go.step.sm/crypto/jose" ) @@ -57,192 +56,57 @@ func (p *Provisioner) UnmarshalJSON(b []byte) error { } */ -func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { - return &provisioner.Options{ - X509: &provisioner.X509Options{ - Template: string(p.X509Template), - TemplateData: p.X509TemplateData, +func NewDefaultClaims() *linkedca.Claims { + return &linkedca.Claims{ + X509: &linkedca.X509Claims{ + Durations: &linkedca.Durations{ + Min: config.GlobalProvisionerClaims.MinTLSDur.String(), + Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), + Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), + }, }, - SSH: &provisioner.SSHOptions{ - Template: string(p.SshTemplate), - TemplateData: p.SshTemplateData, + Ssh: &linkedca.SSHClaims{ + UserDurations: &linkedca.Durations{ + Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), + }, + HostDurations: &linkedca.Durations{ + Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), + Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), + Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), + }, }, + DisableRenewal: config.DefaultDisableRenewal, } } -// provisionerToCertificates converts the landlord provisioner type to the open source -// provisioner type. -func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { - claims, err := claimsToCertificates(p.Claims) +func CreateFirstProvisioner(ctx context.Context, db DB, password string) (*linkedca.Provisioner, error) { + jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) if err != nil { - return nil, err + return nil, WrapErrorISE(err, "error generating JWK key pair") } - details := p.Details.GetData() - if details == nil { - return nil, fmt.Errorf("provisioner does not have any details") + jwkPubBytes, err := jwk.MarshalJSON() + if err != nil { + return nil, WrapErrorISE(err, "error marshaling JWK") } - - switch d := details.(type) { - case *linkedca.ProvisionerDetails_JWK: - jwk := new(jose.JSONWebKey) - if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { - return nil, err - } - return &provisioner.JWK{ - ID: p.Id, - Type: p.Type.String(), - Name: p.Name, - Key: jwk, - EncryptedKey: string(d.JWK.EncryptedPrivateKey), - Claims: claims, - Options: provisionerGetOptions(p), - }, nil - /* - case *ProvisionerDetails_OIDC: - cfg := d.OIDC - return &provisioner.OIDC{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ClientID: cfg.ClientId, - ClientSecret: cfg.ClientSecret, - ConfigurationEndpoint: cfg.ConfigurationEndpoint, - Admins: cfg.Admins, - Domains: cfg.Domains, - Groups: cfg.Groups, - ListenAddress: cfg.ListenAddress, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_GCP: - cfg := d.GCP - return &provisioner.GCP{ - Type: p.Type.String(), - Name: p.Name, - ServiceAccounts: cfg.ServiceAccounts, - ProjectIDs: cfg.ProjectIds, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_AWS: - cfg := d.AWS - return &provisioner.AWS{ - Type: p.Type.String(), - Name: p.Name, - Accounts: cfg.Accounts, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_Azure: - cfg := d.Azure - return &provisioner.Azure{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ResourceGroups: cfg.ResourceGroups, - Audience: cfg.Audience, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_X5C: - var roots []byte - for i, k := range d.X5C.GetRoots() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - roots = append(roots, '\n') - } - roots = append(roots, b...) - } - } - return &provisioner.X5C{ - Type: p.Type.String(), - Name: p.Name, - Roots: roots, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_K8SSA: - var publicKeys []byte - for i, k := range d.K8SSA.GetPublicKeys() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - publicKeys = append(publicKeys, '\n') - } - publicKeys = append(publicKeys, k.Key.Public...) - } - } - return &provisioner.K8sSA{ - Type: p.Type.String(), - Name: p.Name, - PubKeys: publicKeys, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_SSHPOP: - return &provisioner.SSHPOP{ - Type: p.Type.String(), - Name: p.Name, - Claims: claims, - }, nil - case *ProvisionerDetails_ACME: - cfg := d.ACME - return &provisioner.ACME{ - Type: p.Type.String(), - Name: p.Name, - ForceCN: cfg.ForceCn, - Claims: claims, - Options: options, - }, nil - */ - default: - return nil, fmt.Errorf("provisioner %s not implemented", p.Type) + jwePrivStr, err := jwe.CompactSerialize() + if err != nil { + return nil, WrapErrorISE(err, "error serializing JWE") } -} -// claimsToCertificates converts the landlord provisioner claims type to the open source -// (step-ca) claims type. -func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { - var durs = map[string]struct { - durStr string - dur *provisioner.Duration - }{ - "minTLSDur": {durStr: c.X509.Durations.Min}, - "maxTLSDur": {durStr: c.X509.Durations.Max}, - "defaultTLSDur": {durStr: c.X509.Durations.Default}, - "minSSHUserDur": {durStr: c.Ssh.UserDurations.Min}, - "maxSSHUserDur": {durStr: c.Ssh.UserDurations.Max}, - "defaultSSHUserDur": {durStr: c.Ssh.UserDurations.Default}, - "minSSHHostDur": {durStr: c.Ssh.HostDurations.Min}, - "maxSSHHostDur": {durStr: c.Ssh.HostDurations.Max}, - "defaultSSHHostDur": {durStr: c.Ssh.HostDurations.Default}, - } - var err error - for k, v := range durs { - v.dur, err = provisioner.NewDuration(v.durStr) - if err != nil { - return nil, WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) - } - } - return &provisioner.Claims{ - MinTLSDur: durs["minTLSDur"].dur, - MaxTLSDur: durs["maxTLSDur"].dur, - DefaultTLSDur: durs["defaultTLSDur"].dur, - DisableRenewal: &c.DisableRenewal, - MinUserSSHDur: durs["minSSHUserDur"].dur, - MaxUserSSHDur: durs["maxSSHUserDur"].dur, - DefaultUserSSHDur: durs["defaultSSHUserDur"].dur, - MinHostSSHDur: durs["minSSHHostDur"].dur, - MaxHostSSHDur: durs["maxSSHHostDur"].dur, - DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, - EnableSSHCA: &c.Ssh.Enabled, + return &linkedca.Provisioner{ + Name: "Admin JWK", + Type: linkedca.Provisioner_JWK, + Claims: NewDefaultClaims(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_JWK{ + JWK: &linkedca.JWKProvisioner{ + PublicKey: jwkPubBytes, + EncryptedPrivateKey: []byte(jwePrivStr), + }, + }, + }, }, nil } diff --git a/authority/provisioners.go b/authority/provisioners.go index 99a85d46..599db4bd 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -2,9 +2,14 @@ package authority import ( "crypto/x509" + "encoding/json" + "fmt" + "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/linkedca" + "go.step.sm/crypto/jose" ) // GetEncryptedKey returns the JWE key corresponding to the given kid argument. @@ -41,3 +46,205 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error } return p, nil } + +func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { + return &provisioner.Options{ + X509: &provisioner.X509Options{ + Template: string(p.X509Template), + TemplateData: p.X509TemplateData, + }, + SSH: &provisioner.SSHOptions{ + Template: string(p.SshTemplate), + TemplateData: p.SshTemplateData, + }, + } +} + +func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, error) { + var nu provisioner.List + for _, p := range l { + certProv, err := provisionerToCertificates(p) + if err != nil { + return nil, err + } + nu = append(nu, certProv) + } + return nu, nil +} + +// provisionerToCertificates converts the landlord provisioner type to the open source +// provisioner type. +func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { + claims, err := claimsToCertificates(p.Claims) + if err != nil { + return nil, err + } + + details := p.Details.GetData() + if details == nil { + return nil, fmt.Errorf("provisioner does not have any details") + } + + switch d := details.(type) { + case *linkedca.ProvisionerDetails_JWK: + jwk := new(jose.JSONWebKey) + if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { + return nil, err + } + return &provisioner.JWK{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Key: jwk, + EncryptedKey: string(d.JWK.EncryptedPrivateKey), + Claims: claims, + Options: provisionerGetOptions(p), + }, nil + /* + case *ProvisionerDetails_OIDC: + cfg := d.OIDC + return &provisioner.OIDC{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ClientID: cfg.ClientId, + ClientSecret: cfg.ClientSecret, + ConfigurationEndpoint: cfg.ConfigurationEndpoint, + Admins: cfg.Admins, + Domains: cfg.Domains, + Groups: cfg.Groups, + ListenAddress: cfg.ListenAddress, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_GCP: + cfg := d.GCP + return &provisioner.GCP{ + Type: p.Type.String(), + Name: p.Name, + ServiceAccounts: cfg.ServiceAccounts, + ProjectIDs: cfg.ProjectIds, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_AWS: + cfg := d.AWS + return &provisioner.AWS{ + Type: p.Type.String(), + Name: p.Name, + Accounts: cfg.Accounts, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: durationValue(cfg.InstanceAge), + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_Azure: + cfg := d.Azure + return &provisioner.Azure{ + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ResourceGroups: cfg.ResourceGroups, + Audience: cfg.Audience, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_X5C: + var roots []byte + for i, k := range d.X5C.GetRoots() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, b...) + } + } + return &provisioner.X5C{ + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_K8SSA: + var publicKeys []byte + for i, k := range d.K8SSA.GetPublicKeys() { + if b := k.GetKey().GetPublic(); b != nil { + if i > 0 { + publicKeys = append(publicKeys, '\n') + } + publicKeys = append(publicKeys, k.Key.Public...) + } + } + return &provisioner.K8sSA{ + Type: p.Type.String(), + Name: p.Name, + PubKeys: publicKeys, + Claims: claims, + Options: options, + }, nil + case *ProvisionerDetails_SSHPOP: + return &provisioner.SSHPOP{ + Type: p.Type.String(), + Name: p.Name, + Claims: claims, + }, nil + case *ProvisionerDetails_ACME: + cfg := d.ACME + return &provisioner.ACME{ + Type: p.Type.String(), + Name: p.Name, + ForceCN: cfg.ForceCn, + Claims: claims, + Options: options, + }, nil + */ + default: + return nil, fmt.Errorf("provisioner %s not implemented", p.Type) + } +} + +// claimsToCertificates converts the landlord provisioner claims type to the open source +// (step-ca) claims type. +func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { + var durs = map[string]struct { + durStr string + dur *provisioner.Duration + }{ + "minTLSDur": {durStr: c.X509.Durations.Min}, + "maxTLSDur": {durStr: c.X509.Durations.Max}, + "defaultTLSDur": {durStr: c.X509.Durations.Default}, + "minSSHUserDur": {durStr: c.Ssh.UserDurations.Min}, + "maxSSHUserDur": {durStr: c.Ssh.UserDurations.Max}, + "defaultSSHUserDur": {durStr: c.Ssh.UserDurations.Default}, + "minSSHHostDur": {durStr: c.Ssh.HostDurations.Min}, + "maxSSHHostDur": {durStr: c.Ssh.HostDurations.Max}, + "defaultSSHHostDur": {durStr: c.Ssh.HostDurations.Default}, + } + var err error + for k, v := range durs { + v.dur, err = provisioner.NewDuration(v.durStr) + if err != nil { + return nil, mgmt.WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) + } + } + return &provisioner.Claims{ + MinTLSDur: durs["minTLSDur"].dur, + MaxTLSDur: durs["maxTLSDur"].dur, + DefaultTLSDur: durs["defaultTLSDur"].dur, + DisableRenewal: &c.DisableRenewal, + MinUserSSHDur: durs["minSSHUserDur"].dur, + MaxUserSSHDur: durs["maxSSHUserDur"].dur, + DefaultUserSSHDur: durs["defaultSSHUserDur"].dur, + MinHostSSHDur: durs["minSSHHostDur"].dur, + MaxHostSSHDur: durs["maxSSHHostDur"].dur, + DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, + EnableSSHCA: &c.Ssh.Enabled, + }, nil +} diff --git a/ca/adminClient.go b/ca/adminClient.go index e8374a98..32d6339d 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -10,12 +10,14 @@ import ( "strconv" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/mgmt" mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/linkedca" ) +var adminURLPrefix = "admin" + // AdminClient implements an HTTP client for the CA server. type AdminClient struct { client *uaClient @@ -68,9 +70,9 @@ func (c *AdminClient) retryOnError(r *http.Response) bool { } // GetAdmin performs the GET /admin/admin/{id} request to the CA. -func (c *AdminClient) GetAdmin(id string) (*mgmt.Admin, error) { +func (c *AdminClient) GetAdmin(id string) (*linkedca.Admin, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -83,7 +85,7 @@ retry: } return nil, readAdminError(resp.Body) } - var adm = new(mgmt.Admin) + var adm = new(linkedca.Admin) if err := readJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -165,7 +167,7 @@ retry: } // CreateAdmin performs the POST /admin/admins request to the CA. -func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { +func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*linkedca.Admin, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -184,7 +186,7 @@ retry: } return nil, readAdminError(resp.Body) } - var adm = new(mgmt.Admin) + var adm = new(linkedca.Admin) if err := readJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -194,7 +196,7 @@ retry: // RemoveAdmin performs the DELETE /admin/admins/{id} request to the CA. func (c *AdminClient) RemoveAdmin(id string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admins", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -215,13 +217,13 @@ retry: } // UpdateAdmin performs the PUT /admin/admins/{id} request to the CA. -func (c *AdminClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { +func (c *AdminClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*linkedca.Admin, error) { var retried bool body, err := json.Marshal(uar) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admins", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -238,7 +240,7 @@ retry: } return nil, readAdminError(resp.Body) } - var adm = new(admin.Admin) + var adm = new(linkedca.Admin) if err := readJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -246,9 +248,9 @@ retry: } // GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. -func (c *AdminClient) GetProvisioner(name string) (*mgmt.Provisioner, error) { +func (c *AdminClient) GetProvisioner(name string) (*linkedca.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", name)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -261,7 +263,7 @@ retry: } return nil, readAdminError(resp.Body) } - var prov = new(mgmt.Provisioner) + var prov = new(linkedca.Provisioner) if err := readJSON(resp.Body, prov); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -269,7 +271,7 @@ retry: } // GetProvisioners performs the GET /admin/provisioners request to the CA. -func (c *AdminClient) GetProvisioners() ([]*mgmt.Provisioner, error) { +func (c *AdminClient) GetProvisioners() ([]*linkedca.Provisioner, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) retry: @@ -284,7 +286,7 @@ retry: } return nil, readAdminError(resp.Body) } - var provs = new([]*mgmt.Provisioner) + var provs = new([]*linkedca.Provisioner) if err := readJSON(resp.Body, provs); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -294,7 +296,7 @@ retry: // RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. func (c *AdminClient) RemoveProvisioner(name string) error { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", name)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) @@ -315,7 +317,7 @@ retry: } // CreateProvisioner performs the POST /admin/provisioners request to the CA. -func (c *AdminClient) CreateProvisioner(prov *mgmt.Provisioner) (*mgmt.Provisioner, error) { +func (c *AdminClient) CreateProvisioner(prov *linkedca.Provisioner) (*linkedca.Provisioner, error) { var retried bool body, err := json.Marshal(prov) if err != nil { @@ -334,7 +336,7 @@ retry: } return nil, readAdminError(resp.Body) } - var nuProv = new(mgmt.Provisioner) + var nuProv = new(linkedca.Provisioner) if err := readJSON(resp.Body, nuProv); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -342,13 +344,13 @@ retry: } // UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. -func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { +func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*linkedca.Provisioner, error) { var retried bool body, err := json.Marshal(upr) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioners", id)}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", id)}) req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) @@ -365,36 +367,13 @@ retry: } return nil, readAdminError(resp.Body) } - var prov = new(mgmt.Provisioner) + var prov = new(linkedca.Provisioner) if err := readJSON(resp.Body, prov); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return prov, nil } -// GetAuthConfig performs the GET /admin/authconfig/{id} request to the CA. -func (c *AdminClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { - var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/authconfig", id)}) -retry: - resp, err := c.client.Get(u.String()) - if err != nil { - return nil, errors.Wrapf(err, "client GET %s failed", u) - } - if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { - retried = true - goto retry - } - return nil, readAdminError(resp.Body) - } - var ac = new(mgmt.AuthConfig) - if err := readJSON(resp.Body, ac); err != nil { - return nil, errors.Wrapf(err, "error reading %s", u) - } - return ac, nil -} - func readAdminError(r io.ReadCloser) error { defer r.Close() mgmtErr := new(mgmt.Error) From 94ba057f014d862e77296f9c6b847aa2476917e7 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 26 May 2021 14:55:31 -0700 Subject: [PATCH 021/291] wip --- authority/authority.go | 17 ++++--- authority/mgmt/db/nosql/provisioner.go | 2 +- authority/mgmt/provisioner.go | 8 ++- authority/provisioners.go | 1 + ca/adminClient.go | 70 ++++++++++++++++++++++---- 5 files changed, 77 insertions(+), 21 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 3e089397..6be0ea8f 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -130,6 +130,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } +// ReloadAuthConfig reloads dynamic fields of the AuthConfig. func (a *Authority) ReloadAuthConfig(ctx context.Context) error { provs, err := a.adminDB.GetProvisioners(ctx) if err != nil { @@ -218,14 +219,20 @@ func (a *Authority) init() error { provs, err := a.adminDB.GetProvisioners(context.Background()) if err != nil { - return err + return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") } if len(provs) == 0 { // Create First Provisioner prov, err := mgmt.CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) if err != nil { - return err + return mgmt.WrapErrorISE(err, "error creating first provisioner") + } + certProv, err := provisionerToCertificates(prov) + if err != nil { + return mgmt.WrapErrorISE(err, "error converting provisioner to certificates type") } + a.config.AuthorityConfig.Provisioners = []provisioner.Interface{certProv} + // Create First Admin adm := &linkedca.Admin{ ProvisionerId: prov.Id, @@ -238,13 +245,9 @@ func (a *Authority) init() error { } a.config.AuthorityConfig.Admins = []*linkedca.Admin{adm} } else { - provs, err := a.adminDB.GetProvisioners(context.Background()) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") + return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates type") } a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(context.Background()) if err != nil { diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go index 1924aee3..fa5eaf2a 100644 --- a/authority/mgmt/db/nosql/provisioner.go +++ b/authority/mgmt/db/nosql/provisioner.go @@ -151,7 +151,7 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) return errors.Wrap(err, "error generating random id for provisioner") } - details, err := json.Marshal(prov.Details) + details, err := json.Marshal(prov.Details.GetData()) if err != nil { return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) } diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go index 0386a894..2177e116 100644 --- a/authority/mgmt/provisioner.go +++ b/authority/mgmt/provisioner.go @@ -96,7 +96,7 @@ func CreateFirstProvisioner(ctx context.Context, db DB, password string) (*linke return nil, WrapErrorISE(err, "error serializing JWE") } - return &linkedca.Provisioner{ + p := &linkedca.Provisioner{ Name: "Admin JWK", Type: linkedca.Provisioner_JWK, Claims: NewDefaultClaims(), @@ -108,5 +108,9 @@ func CreateFirstProvisioner(ctx context.Context, db DB, password string) (*linke }, }, }, - }, nil + } + if err := db.CreateProvisioner(ctx, p); err != nil { + return nil, WrapErrorISE(err, "error creating provisioner") + } + return p, nil } diff --git a/authority/provisioners.go b/authority/provisioners.go index 599db4bd..48f67dc8 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -87,6 +87,7 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, switch d := details.(type) { case *linkedca.ProvisionerDetails_JWK: + fmt.Printf("d = %+v\n", d) jwk := new(jose.JSONWebKey) if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { return nil, err diff --git a/ca/adminClient.go b/ca/adminClient.go index 32d6339d..ad708146 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/mgmt" mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" + "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/linkedca" ) @@ -92,7 +93,7 @@ retry: return adm, nil } -// AdminOption is the type of options passed to the Provisioner method. +// AdminOption is the type of options passed to the Admin method. type AdminOption func(o *adminOptions) error type adminOptions struct { @@ -136,8 +137,8 @@ func WithAdminLimit(limit int) AdminOption { } } -// GetAdmins performs the GET /admin/admins request to the CA. -func (c *AdminClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { +// GetAdminsPaginate returns a page from the the GET /admin/admins request to the CA. +func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { var retried bool o := new(adminOptions) if err := o.apply(opts); err != nil { @@ -166,6 +167,26 @@ retry: return body, nil } +// GetAdmins returns all admins from the GET /admin/admins request to the CA. +func (c *AdminClient) GetAdmins(opts ...AdminOption) ([]*linkedca.Admin, error) { + var ( + cursor = "" + admins = []*linkedca.Admin{} + ) + for { + resp, err := c.GetAdminsPaginate(WithAdminCursor(cursor), WithAdminLimit(100)) + if err != nil { + return nil, err + } + admins = append(admins, resp.Admins...) + if resp.NextCursor == "" { + return admins, nil + } + cursor = resp.NextCursor + } + return admins, nil +} + // CreateAdmin performs the POST /admin/admins request to the CA. func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*linkedca.Admin, error) { var retried bool @@ -247,8 +268,8 @@ retry: return adm, nil } -// GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. -func (c *AdminClient) GetProvisioner(name string) (*linkedca.Provisioner, error) { +// GetProvisionerByName performs the GET /admin/provisioners/{name} request to the CA. +func (c *AdminClient) GetProvisionerByName(name string) (*linkedca.Provisioner, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) retry: @@ -270,10 +291,17 @@ retry: return prov, nil } -// GetProvisioners performs the GET /admin/provisioners request to the CA. -func (c *AdminClient) GetProvisioners() ([]*linkedca.Provisioner, error) { +// GetProvisionersPaginate performs the GET /admin/provisioners request to the CA. +func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtAPI.GetProvisionersResponse, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) + o := new(provisionerOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + u := c.endpoint.ResolveReference(&url.URL{ + Path: "/admin/provisioners", + RawQuery: o.rawQuery(), + }) retry: resp, err := c.client.Get(u.String()) if err != nil { @@ -286,11 +314,31 @@ retry: } return nil, readAdminError(resp.Body) } - var provs = new([]*linkedca.Provisioner) - if err := readJSON(resp.Body, provs); err != nil { + var body = new(mgmtAPI.GetProvisionersResponse) + if err := readJSON(resp.Body, body); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } - return *provs, nil + return body, nil +} + +// GetProvisioners returns all admins from the GET /admin/admins request to the CA. +func (c *AdminClient) GetProvisioners(opts ...AdminOption) (provisioner.List, error) { + var ( + cursor = "" + provs = provisioner.List{} + ) + for { + resp, err := c.GetProvisionersPaginate(WithProvisionerCursor(cursor), WithProvisionerLimit(100)) + if err != nil { + return nil, err + } + provs = append(provs, resp.Provisioners...) + if resp.NextCursor == "" { + return provs, nil + } + cursor = resp.NextCursor + } + return provs, nil } // RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. From d7a747b92ba90688eac74c354b1d5ccb89cb38ea Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 26 May 2021 15:49:18 -0700 Subject: [PATCH 022/291] Add SCEP in the provisioners proto. Change provisioner.proto to match protobuf style guide. --- linkedca/provisioners.pb.go | 451 +++++++++++++++++++++++------------- linkedca/provisioners.proto | 131 ++++++----- 2 files changed, 354 insertions(+), 228 deletions(-) diff --git a/linkedca/provisioners.pb.go b/linkedca/provisioners.pb.go index 1a8eb681..4b8ffab0 100644 --- a/linkedca/provisioners.pb.go +++ b/linkedca/provisioners.pb.go @@ -33,21 +33,23 @@ const ( Provisioner_X5C Provisioner_Type = 7 Provisioner_K8SSA Provisioner_Type = 8 Provisioner_SSHPOP Provisioner_Type = 9 + Provisioner_SCEP Provisioner_Type = 10 ) // Enum value maps for Provisioner_Type. var ( Provisioner_Type_name = map[int32]string{ - 0: "NOOP", - 1: "JWK", - 2: "OIDC", - 3: "GCP", - 4: "AWS", - 5: "AZURE", - 6: "ACME", - 7: "X5C", - 8: "K8SSA", - 9: "SSHPOP", + 0: "NOOP", + 1: "JWK", + 2: "OIDC", + 3: "GCP", + 4: "AWS", + 5: "AZURE", + 6: "ACME", + 7: "X5C", + 8: "K8SSA", + 9: "SSHPOP", + 10: "SCEP", } Provisioner_Type_value = map[string]int32{ "NOOP": 0, @@ -60,6 +62,7 @@ var ( "X5C": 7, "K8SSA": 8, "SSHPOP": 9, + "SCEP": 10, } ) @@ -224,6 +227,7 @@ type ProvisionerDetails struct { // *ProvisionerDetails_X5C // *ProvisionerDetails_K8SSA // *ProvisionerDetails_SSHPOP + // *ProvisionerDetails_SCEP Data isProvisionerDetails_Data `protobuf_oneof:"data"` } @@ -329,6 +333,13 @@ func (x *ProvisionerDetails) GetSSHPOP() *SSHPOPProvisioner { return nil } +func (x *ProvisionerDetails) GetSCEP() *SCEPProvisioner { + if x, ok := x.GetData().(*ProvisionerDetails_SCEP); ok { + return x.SCEP + } + return nil +} + type isProvisionerDetails_Data interface { isProvisionerDetails_Data() } @@ -369,6 +380,10 @@ type ProvisionerDetails_SSHPOP struct { SSHPOP *SSHPOPProvisioner `protobuf:"bytes,28,opt,name=SSHPOP,proto3,oneof"` } +type ProvisionerDetails_SCEP struct { + SCEP *SCEPProvisioner `protobuf:"bytes,29,opt,name=SCEP,proto3,oneof"` +} + func (*ProvisionerDetails_JWK) isProvisionerDetails_Data() {} func (*ProvisionerDetails_OIDC) isProvisionerDetails_Data() {} @@ -387,6 +402,8 @@ func (*ProvisionerDetails_K8SSA) isProvisionerDetails_Data() {} func (*ProvisionerDetails_SSHPOP) isProvisionerDetails_Data() {} +func (*ProvisionerDetails_SCEP) isProvisionerDetails_Data() {} + type ProvisionerList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1244,12 +1261,83 @@ func (*SSHPOPProvisioner) Descriptor() ([]byte, []int) { return file_linkedca_provisioners_proto_rawDescGZIP(), []int{15} } +type SCEPProvisioner struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` + Challenge string `protobuf:"bytes,2,opt,name=challenge,proto3" json:"challenge,omitempty"` + Capabilities []string `protobuf:"bytes,3,rep,name=capabilities,proto3" json:"capabilities,omitempty"` + MinimumPublicKeyLength int32 `protobuf:"varint,4,opt,name=minimum_public_key_length,json=minimumPublicKeyLength,proto3" json:"minimum_public_key_length,omitempty"` +} + +func (x *SCEPProvisioner) Reset() { + *x = SCEPProvisioner{} + if protoimpl.UnsafeEnabled { + mi := &file_linkedca_provisioners_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SCEPProvisioner) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SCEPProvisioner) ProtoMessage() {} + +func (x *SCEPProvisioner) ProtoReflect() protoreflect.Message { + mi := &file_linkedca_provisioners_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SCEPProvisioner.ProtoReflect.Descriptor instead. +func (*SCEPProvisioner) Descriptor() ([]byte, []int) { + return file_linkedca_provisioners_proto_rawDescGZIP(), []int{16} +} + +func (x *SCEPProvisioner) GetForceCn() bool { + if x != nil { + return x.ForceCn + } + return false +} + +func (x *SCEPProvisioner) GetChallenge() string { + if x != nil { + return x.Challenge + } + return "" +} + +func (x *SCEPProvisioner) GetCapabilities() []string { + if x != nil { + return x.Capabilities + } + return nil +} + +func (x *SCEPProvisioner) GetMinimumPublicKeyLength() int32 { + if x != nil { + return x.MinimumPublicKeyLength + } + return 0 +} + var File_linkedca_provisioners_proto protoreflect.FileDescriptor var file_linkedca_provisioners_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x22, 0xf4, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x22, 0xfe, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, @@ -1274,155 +1362,169 @@ var file_linkedca_provisioners_proto_rawDesc = []byte{ 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x22, 0x6a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, + 0x74, 0x61, 0x22, 0x74, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, - 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x22, 0xd5, - 0x03, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2c, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4a, 0x57, - 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, - 0x4a, 0x57, 0x4b, 0x12, 0x2f, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4f, 0x49, 0x44, - 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, - 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2c, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x47, 0x43, 0x50, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x47, - 0x43, 0x50, 0x12, 0x2c, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x57, 0x53, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x41, 0x57, 0x53, - 0x12, 0x32, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x41, - 0x7a, 0x75, 0x72, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x19, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x43, - 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, - 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, 0x2c, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x18, 0x1a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, - 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, - 0x58, 0x35, 0x43, 0x12, 0x32, 0x0a, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4b, 0x38, - 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, - 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, - 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x42, 0x06, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, - 0x28, 0x0a, 0x04, 0x78, 0x35, 0x30, 0x39, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, - 0x69, 0x6d, 0x73, 0x52, 0x04, 0x78, 0x35, 0x30, 0x39, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x73, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, - 0x61, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, - 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, - 0x77, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0a, 0x58, 0x35, 0x30, - 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x31, 0x0a, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, + 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x12, 0x08, + 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, 0x10, 0x0a, 0x22, 0x86, 0x04, 0x0a, 0x12, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x2c, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x4a, 0x57, 0x4b, 0x12, 0x2f, 0x0a, + 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2c, + 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x47, 0x43, 0x50, 0x12, 0x2c, 0x0a, 0x03, + 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x41, 0x57, 0x53, 0x12, 0x32, 0x0a, 0x05, 0x41, 0x7a, + 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x2f, + 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, + 0x2c, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x58, 0x35, 0x43, 0x12, 0x32, 0x0a, + 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, + 0x41, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, + 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x12, 0x2f, 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, + 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, + 0x61, 0x2e, 0x53, 0x43, 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x48, 0x00, 0x52, 0x04, 0x53, 0x43, 0x45, 0x50, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x22, 0x4c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x22, + 0x82, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x78, 0x35, + 0x30, 0x39, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x04, + 0x78, 0x35, 0x30, 0x39, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6e, + 0x65, 0x77, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0a, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0e, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, - 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, - 0x03, 0x6d, 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, - 0x63, 0x0a, 0x0e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x13, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x22, 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x06, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, - 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, - 0xeb, 0x01, 0x0a, 0x0e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, - 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, - 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, - 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, - 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, - 0x0a, 0x0e, 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, - 0x61, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, - 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, - 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, - 0x41, 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, - 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, - 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, - 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, - 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, - 0x0a, 0x0f, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, - 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, - 0x6f, 0x6f, 0x74, 0x73, 0x22, 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, - 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x42, 0x2c, - 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, - 0x6c, 0x6c, 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x09, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x9d, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x63, 0x0a, 0x0e, 0x4a, 0x57, + 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x65, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, + 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xeb, 0x01, 0x0a, 0x0e, 0x47, + 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, + 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, + 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0e, 0x41, 0x57, 0x53, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, + 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, + 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, + 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, + 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, + 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x0f, 0x41, 0x43, 0x4d, + 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, + 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, 0x58, 0x35, 0x43, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6f, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x22, + 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x53, 0x43, + 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, + 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, + 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x69, + 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, + 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6d, + 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1438,7 +1540,7 @@ func file_linkedca_provisioners_proto_rawDescGZIP() []byte { } var file_linkedca_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_linkedca_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_linkedca_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_linkedca_provisioners_proto_goTypes = []interface{}{ (Provisioner_Type)(0), // 0: linkedca.Provisioner.Type (*Provisioner)(nil), // 1: linkedca.Provisioner @@ -1457,6 +1559,7 @@ var file_linkedca_provisioners_proto_goTypes = []interface{}{ (*X5CProvisioner)(nil), // 14: linkedca.X5CProvisioner (*K8SSAProvisioner)(nil), // 15: linkedca.K8sSAProvisioner (*SSHPOPProvisioner)(nil), // 16: linkedca.SSHPOPProvisioner + (*SCEPProvisioner)(nil), // 17: linkedca.SCEPProvisioner } var file_linkedca_provisioners_proto_depIdxs = []int32{ 0, // 0: linkedca.Provisioner.type:type_name -> linkedca.Provisioner.Type @@ -1471,17 +1574,18 @@ var file_linkedca_provisioners_proto_depIdxs = []int32{ 14, // 9: linkedca.ProvisionerDetails.X5C:type_name -> linkedca.X5CProvisioner 15, // 10: linkedca.ProvisionerDetails.K8sSA:type_name -> linkedca.K8sSAProvisioner 16, // 11: linkedca.ProvisionerDetails.SSHPOP:type_name -> linkedca.SSHPOPProvisioner - 1, // 12: linkedca.ProvisionerList.provisioners:type_name -> linkedca.Provisioner - 5, // 13: linkedca.Claims.x509:type_name -> linkedca.X509Claims - 6, // 14: linkedca.Claims.ssh:type_name -> linkedca.SSHClaims - 7, // 15: linkedca.X509Claims.durations:type_name -> linkedca.Durations - 7, // 16: linkedca.SSHClaims.user_durations:type_name -> linkedca.Durations - 7, // 17: linkedca.SSHClaims.host_durations:type_name -> linkedca.Durations - 18, // [18:18] is the sub-list for method output_type - 18, // [18:18] is the sub-list for method input_type - 18, // [18:18] is the sub-list for extension type_name - 18, // [18:18] is the sub-list for extension extendee - 0, // [0:18] is the sub-list for field type_name + 17, // 12: linkedca.ProvisionerDetails.SCEP:type_name -> linkedca.SCEPProvisioner + 1, // 13: linkedca.ProvisionerList.provisioners:type_name -> linkedca.Provisioner + 5, // 14: linkedca.Claims.x509:type_name -> linkedca.X509Claims + 6, // 15: linkedca.Claims.ssh:type_name -> linkedca.SSHClaims + 7, // 16: linkedca.X509Claims.durations:type_name -> linkedca.Durations + 7, // 17: linkedca.SSHClaims.user_durations:type_name -> linkedca.Durations + 7, // 18: linkedca.SSHClaims.host_durations:type_name -> linkedca.Durations + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_linkedca_provisioners_proto_init() } @@ -1682,6 +1786,18 @@ func file_linkedca_provisioners_proto_init() { return nil } } + file_linkedca_provisioners_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SCEPProvisioner); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_linkedca_provisioners_proto_msgTypes[1].OneofWrappers = []interface{}{ (*ProvisionerDetails_JWK)(nil), @@ -1693,6 +1809,7 @@ func file_linkedca_provisioners_proto_init() { (*ProvisionerDetails_X5C)(nil), (*ProvisionerDetails_K8SSA)(nil), (*ProvisionerDetails_SSHPOP)(nil), + (*ProvisionerDetails_SCEP)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1700,7 +1817,7 @@ func file_linkedca_provisioners_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_linkedca_provisioners_proto_rawDesc, NumEnums: 1, - NumMessages: 16, + NumMessages: 17, NumExtensions: 0, NumServices: 0, }, diff --git a/linkedca/provisioners.proto b/linkedca/provisioners.proto index 53f2c140..ae047391 100644 --- a/linkedca/provisioners.proto +++ b/linkedca/provisioners.proto @@ -5,28 +5,29 @@ package linkedca; option go_package = "github.com/smallstep/certificates/linkedca"; message Provisioner { - enum Type { - NOOP = 0; - JWK = 1; - OIDC = 2; - GCP = 3; - AWS = 4; - AZURE = 5; - ACME = 6; - X5C = 7; - K8SSA = 8; - SSHPOP = 9; - } - string id = 1; - string authority_id = 2; - Type type = 3; - string name = 4; - ProvisionerDetails details = 5; - Claims claims = 6; - bytes x509_template = 7; - bytes x509_template_data = 8; - bytes ssh_template = 9; - bytes ssh_template_data = 10; + enum Type { + NOOP = 0; + JWK = 1; + OIDC = 2; + GCP = 3; + AWS = 4; + AZURE = 5; + ACME = 6; + X5C = 7; + K8SSA = 8; + SSHPOP = 9; + SCEP = 10; + } + string id = 1; + string authority_id = 2; + Type type = 3; + string name = 4; + ProvisionerDetails details = 5; + Claims claims = 6; + bytes x509_template = 7; + bytes x509_template_data = 8; + bytes ssh_template = 9; + bytes ssh_template_data = 10; } message ProvisionerDetails { @@ -40,85 +41,93 @@ message ProvisionerDetails { X5CProvisioner X5C = 26; K8sSAProvisioner K8sSA = 27; SSHPOPProvisioner SSHPOP = 28; + SCEPProvisioner SCEP = 29; } } message ProvisionerList { - repeated Provisioner provisioners = 1; + repeated Provisioner provisioners = 1; } message Claims { - X509Claims x509 = 1; - SSHClaims ssh = 2; - bool disable_renewal = 3; + X509Claims x509 = 1; + SSHClaims ssh = 2; + bool disable_renewal = 3; } message X509Claims { - bool enabled = 1; - Durations durations = 2; + bool enabled = 1; + Durations durations = 2; } message SSHClaims { - bool enabled = 1; - Durations user_durations = 2; - Durations host_durations = 3; + bool enabled = 1; + Durations user_durations = 2; + Durations host_durations = 3; } message Durations { - string default = 1; - string min = 2; - string max = 3; + string default = 1; + string min = 2; + string max = 3; } message JWKProvisioner { - bytes public_key = 1; - bytes encrypted_private_key = 2; + bytes public_key = 1; + bytes encrypted_private_key = 2; } message OIDCProvisioner { - string client_id = 1; - string client_secret = 2; - string configuration_endpoint = 3; - repeated string admins = 4; - repeated string domains = 5; - repeated string groups = 6; - string listen_address = 7; - string tenant_id = 8; + string client_id = 1; + string client_secret = 2; + string configuration_endpoint = 3; + repeated string admins = 4; + repeated string domains = 5; + repeated string groups = 6; + string listen_address = 7; + string tenant_id = 8; } message GCPProvisioner { - repeated string service_accounts = 1; - repeated string project_ids = 2; - bool disable_custom_sans = 3; - bool disable_trust_on_first_use = 4; - string instance_age = 5; + repeated string service_accounts = 1; + repeated string project_ids = 2; + bool disable_custom_sans = 3; + bool disable_trust_on_first_use = 4; + string instance_age = 5; } message AWSProvisioner { - repeated string accounts = 1; - bool disable_custom_sans = 2; - bool disable_trust_on_first_use = 3; - string instance_age = 4; + repeated string accounts = 1; + bool disable_custom_sans = 2; + bool disable_trust_on_first_use = 3; + string instance_age = 4; } message AzureProvisioner { - string tenant_id = 1; - repeated string resource_groups = 2; - string audience = 3; - bool disable_custom_sans = 4; - bool disable_trust_on_first_use = 5; + string tenant_id = 1; + repeated string resource_groups = 2; + string audience = 3; + bool disable_custom_sans = 4; + bool disable_trust_on_first_use = 5; } message ACMEProvisioner { - bool force_cn = 1; + bool force_cn = 1; } message X5CProvisioner { - repeated bytes roots = 1; + repeated bytes roots = 1; } message K8sSAProvisioner { - repeated bytes public_keys = 1; + repeated bytes public_keys = 1; } message SSHPOPProvisioner {} + +message SCEPProvisioner { + bool force_cn = 1; + string challenge = 2; + repeated string capabilities = 3; + int32 minimum_public_key_length = 4; +} \ No newline at end of file From 114627de9387877c101c4e8c49b719d2ef815295 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 25 May 2021 12:45:40 -0700 Subject: [PATCH 023/291] [action] labeler to v3 and use default config path location --- .github/needs-triage-labeler.yml | 2 -- .github/workflows/labeler.yml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 .github/needs-triage-labeler.yml diff --git a/.github/needs-triage-labeler.yml b/.github/needs-triage-labeler.yml deleted file mode 100644 index 4f507a77..00000000 --- a/.github/needs-triage-labeler.yml +++ /dev/null @@ -1,2 +0,0 @@ -needs triage: -- "**" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 9311145c..f0011406 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -8,7 +8,7 @@ jobs: label: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v2 + - uses: actions/labeler@v3 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/needs-triage-labeler.yml From ff7b829aa27fb28cfbfe34af3de2534d77e37feb Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 25 May 2021 12:49:03 -0700 Subject: [PATCH 024/291] [action] forgot to add default labeler config file --- .github/labeler.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..538aed15 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,4 @@ +needs triage: + - '**' # index.php | src/main.php + - '.*' # .gitignore + - '.*/**' # .github/workflows/label.yml From 48c86716a0a5534eecdf773e907b37476730f608 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Feb 2021 12:03:08 +0100 Subject: [PATCH 025/291] Add rudimentary (and incomplete) support for SCEP --- authority/authority.go | 10 +- authority/provisioner/provisioner.go | 10 + authority/provisioner/scep.go | 116 +++++++++ ca/ca.go | 61 ++++- go.mod | 1 + go.sum | 2 + scep/api.go | 367 +++++++++++++++++++++++++++ scep/authority.go | 88 +++++++ scep/provisioner.go | 17 ++ scep/scep.go | 38 +++ 10 files changed, 698 insertions(+), 12 deletions(-) create mode 100644 authority/provisioner/scep.go create mode 100644 scep/api.go create mode 100644 scep/authority.go create mode 100644 scep/provisioner.go create mode 100644 scep/scep.go diff --git a/authority/authority.go b/authority/authority.go index 6be0ea8f..a0b15649 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -442,9 +442,13 @@ func (a *Authority) init() error { audiences := a.config.GetAudiences() a.provisioners = provisioner.NewCollection(audiences) config := provisioner.Config{ - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, + // TODO: this probably shouldn't happen like this; via SignAuth instead? + IntermediateCert: a.config.IntermediateCert, + SigningKey: a.config.IntermediateKey, + CACertificates: a.rootX509Certs, + Claims: claimer.Claims(), + Audiences: audiences, + DB: a.db, SSHKeys: &provisioner.SSHKeys{ UserKeys: sshKeys.UserKeys, HostKeys: sshKeys.HostKeys, diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 981d0b0a..3824bf6c 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -142,6 +142,8 @@ const ( TypeK8sSA Type = 8 // TypeSSHPOP is used to indicate the SSHPOP provisioners. TypeSSHPOP Type = 9 + // TypeSCEP is used to indicate the SCEP provisioners + TypeSCEP Type = 10 ) // String returns the string representation of the type. @@ -165,6 +167,8 @@ func (t Type) String() string { return "K8sSA" case TypeSSHPOP: return "SSHPOP" + case TypeSCEP: + return "SCEP" default: return "" } @@ -179,6 +183,10 @@ type SSHKeys struct { // Config defines the default parameters used in the initialization of // provisioners. type Config struct { + // TODO: these probably shouldn't be here but passed via SignAuth + IntermediateCert string + SigningKey string + CACertificates []*x509.Certificate // Claims are the default claims. Claims Claims // Audiences are the audiences used in the default provisioner, (JWK). @@ -233,6 +241,8 @@ func (l *List) UnmarshalJSON(data []byte) error { p = &K8sSA{} case "sshpop": p = &SSHPOP{} + case "scep": + p = &SCEP{} default: // Skip unsupported provisioners. A client using this method may be // compiled with a version of smallstep/certificates that does not diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go new file mode 100644 index 00000000..741add16 --- /dev/null +++ b/authority/provisioner/scep.go @@ -0,0 +1,116 @@ +package provisioner + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + + "github.com/pkg/errors" +) + +// SCEP is the SCEP provisioner type, an entity that can authorize the +// SCEP provisioning flow +type SCEP struct { + *base + Type string `json:"type"` + Name string `json:"name"` + // ForceCN bool `json:"forceCN,omitempty"` + // Claims *Claims `json:"claims,omitempty"` + // Options *Options `json:"options,omitempty"` + // claimer *Claimer + + IntermediateCert string + SigningKey string + CACertificates []*x509.Certificate +} + +// GetID returns the provisioner unique identifier. +func (s SCEP) GetID() string { + return "scep/" + s.Name +} + +// GetName returns the name of the provisioner. +func (s *SCEP) GetName() string { + return s.Name +} + +// GetType returns the type of provisioner. +func (s *SCEP) GetType() Type { + return TypeSCEP +} + +// GetEncryptedKey returns the base provisioner encrypted key if it's defined. +func (s *SCEP) GetEncryptedKey() (string, string, bool) { + return "", "", false +} + +// GetTokenID returns the identifier of the token. +func (s *SCEP) GetTokenID(ott string) (string, error) { + return "", errors.New("scep provisioner does not implement GetTokenID") +} + +// GetCACertificates returns the CA certificate chain +// TODO: this should come from the authority instead? +func (s *SCEP) GetCACertificates() []*x509.Certificate { + + pemtxt, _ := ioutil.ReadFile(s.IntermediateCert) // TODO: move reading key to init? That's probably safer. + block, _ := pem.Decode([]byte(pemtxt)) + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + fmt.Println(err) + } + + // TODO: return chain? I'm not sure if the client understands it correctly + return []*x509.Certificate{cert} +} + +func (s *SCEP) GetSigningKey() *rsa.PrivateKey { + + keyBytes, err := ioutil.ReadFile(s.SigningKey) + if err != nil { + return nil + } + + block, _ := pem.Decode([]byte(keyBytes)) + if block == nil { + return nil + } + + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + fmt.Println(err) + return nil + } + + return key +} + +// Init initializes and validates the fields of a JWK type. +func (s *SCEP) Init(config Config) (err error) { + + switch { + case s.Type == "": + return errors.New("provisioner type cannot be empty") + case s.Name == "": + return errors.New("provisioner name cannot be empty") + } + + // // Update claims with global ones + // if p.claimer, err = NewClaimer(p.Claims, config.Claims); err != nil { + // return err + // } + + s.IntermediateCert = config.IntermediateCert + s.SigningKey = config.SigningKey + s.CACertificates = config.CACertificates + + return err +} + +// Interface guards +var ( + _ Interface = (*SCEP)(nil) + //_ scep.Provisioner = (*SCEP)(nil) +) diff --git a/ca/ca.go b/ca/ca.go index d97e0ab2..405ebe47 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -22,6 +22,7 @@ import ( "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" + "github.com/smallstep/certificates/scep" "github.com/smallstep/certificates/server" "github.com/smallstep/nosql" ) @@ -179,17 +180,39 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { mgmtHandler.Route(r) }) } + if ca.shouldServeSCEPEndpoints() { - // helpful routine for logging all routes // - /* - walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { - fmt.Printf("%s %s\n", method, route) - return nil - } - if err := chi.Walk(mux, walkFunc); err != nil { - fmt.Printf("Logging err: %s\n", err.Error()) + scepPrefix := "scep" + scepAuthority, err := scep.New(auth, scep.AuthorityOptions{ + Service: auth.GetSCEPService(), + DNS: dns, + Prefix: scepPrefix, + }) + if err != nil { + return nil, errors.Wrap(err, "error creating SCEP authority") } - */ + scepRouterHandler := scepAPI.New(scepAuthority) + + // According to the RFC (https://tools.ietf.org/html/rfc8894#section-7.10), + // SCEP operations are performed using HTTP, so that's why the API is mounted + // to the insecure mux. + insecureMux.Route("/"+scepPrefix, func(r chi.Router) { + scepRouterHandler.Route(r) + }) + + // The RFC also mentions usage of HTTPS, but seems to advise + // against it, because of potential interoperability issues. + // Currently I think it's not bad to use HTTPS also, so that's + // why I've kept the API endpoints in both muxes and both HTTP + // as well as HTTPS can be used to request certificates + // using SCEP. + mux.Route("/"+scepPrefix, func(r chi.Router) { + scepRouterHandler.Route(r) + }) + } + + // helpful routine for logging all routes // + //dumpRoutes(mux) // Add monitoring if configured if len(config.Monitoring) > 0 { @@ -331,3 +354,23 @@ func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, error) { return tlsConfig, nil } + +// shouldMountSCEPEndpoints returns if the CA should be +// configured with endpoints for SCEP. This is assumed to be +// true if a SCEPService exists, which is true in case a +// SCEP provisioner was configured. +func (ca *CA) shouldServeSCEPEndpoints() bool { + return ca.auth.GetSCEPService() != nil +} + +//nolint // ignore linters to allow keeping this function around for debugging +func dumpRoutes(mux chi.Routes) { + // helpful routine for logging all routes // + walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + fmt.Printf("%s %s\n", method, route) + return nil + } + if err := chi.Walk(mux, walkFunc); err != nil { + fmt.Printf("Logging err: %s\n", err.Error()) + } +} diff --git a/go.mod b/go.mod index 65b395bb..fe0c7f87 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/google/uuid v1.1.2 github.com/googleapis/gax-go/v2 v2.0.5 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/micromdm/scep v1.0.0 github.com/newrelic/go-agent v2.15.0+incompatible github.com/pkg/errors v0.9.1 github.com/rs/xid v1.2.1 diff --git a/go.sum b/go.sum index 9529a1a3..77e0b0cb 100644 --- a/go.sum +++ b/go.sum @@ -234,6 +234,8 @@ github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGe github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 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/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY= +github.com/micromdm/scep v1.0.0/go.mod h1:CID2SixSr5FvoauZdAFUSpQkn5MAuSy9oyURMGOJbag= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= diff --git a/scep/api.go b/scep/api.go new file mode 100644 index 00000000..1f33dfd5 --- /dev/null +++ b/scep/api.go @@ -0,0 +1,367 @@ +package scep + +import ( + "context" + "crypto" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "math/big" + "math/rand" + "net/http" + "strings" + "time" + + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/provisioner" + + microscep "github.com/micromdm/scep/scep" +) + +// Handler is the ACME request handler. +type Handler struct { + Auth Interface +} + +// New returns a new ACME API router. +func NewAPI(scepAuth Interface) api.RouterHandler { + return &Handler{scepAuth} +} + +// Route traffic and implement the Router interface. +func (h *Handler) Route(r api.Router) { + //getLink := h.Auth.GetLinkExplicit + //fmt.Println(getLink) + + //r.MethodFunc("GET", "/bla", h.baseURLFromRequest(h.lookupProvisioner(nil))) + //r.MethodFunc("GET", getLink(acme.NewNonceLink, "{provisionerID}", false, nil), h.baseURLFromRequest(h.lookupProvisioner(h.addNonce(h.GetNonce)))) + + r.MethodFunc(http.MethodGet, "/", h.lookupProvisioner(h.Get)) + r.MethodFunc(http.MethodPost, "/", h.lookupProvisioner(h.Post)) + +} + +func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { + + scepRequest, err := decodeSCEPRequest(r) + if err != nil { + fmt.Println(err) + fmt.Println("not a scep get request") + w.WriteHeader(500) + } + + scepResponse := SCEPResponse{Operation: scepRequest.Operation} + + switch scepRequest.Operation { + case opnGetCACert: + err := h.GetCACert(w, r, scepResponse) + if err != nil { + fmt.Println(err) + } + + case opnGetCACaps: + err := h.GetCACaps(w, r, scepResponse) + if err != nil { + fmt.Println(err) + } + case opnPKIOperation: + + default: + + } +} + +func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { + scepRequest, err := decodeSCEPRequest(r) + if err != nil { + fmt.Println(err) + fmt.Println("not a scep post request") + w.WriteHeader(500) + } + + scepResponse := SCEPResponse{Operation: scepRequest.Operation} + + switch scepRequest.Operation { + case opnPKIOperation: + err := h.PKIOperation(w, r, scepRequest, scepResponse) + if err != nil { + fmt.Println(err) + } + default: + + } + +} + +const maxPayloadSize = 2 << 20 + +func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { + + defer r.Body.Close() + + method := r.Method + query := r.URL.Query() + + var operation string + if _, ok := query["operation"]; ok { + operation = query.Get("operation") + } + + switch method { + case http.MethodGet: + switch operation { + case opnGetCACert, opnGetCACaps: + return SCEPRequest{ + Operation: operation, + Message: []byte{}, + }, nil + case opnPKIOperation: + var message string + if _, ok := query["message"]; ok { + message = query.Get("message") + } + decodedMessage, err := base64.URLEncoding.DecodeString(message) + if err != nil { + return SCEPRequest{}, err + } + return SCEPRequest{ + Operation: operation, + Message: decodedMessage, + }, nil + default: + return SCEPRequest{}, fmt.Errorf("unsupported operation: %s", operation) + } + case http.MethodPost: + body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxPayloadSize)) + if err != nil { + return SCEPRequest{}, err + } + return SCEPRequest{ + Operation: operation, + Message: body, + }, nil + default: + return SCEPRequest{}, fmt.Errorf("unsupported method: %s", method) + } +} + +type nextHTTP = func(http.ResponseWriter, *http.Request) + +// lookupProvisioner loads the provisioner associated with the request. +// Responds 404 if the provisioner does not exist. +func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + // TODO: make this configurable; and we might want to look at being able to provide multiple, + // like the actual ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. + p, err := h.Auth.LoadProvisionerByID("scep/scep1") + if err != nil { + api.WriteError(w, err) + return + } + + scepProvisioner, ok := p.(*provisioner.SCEP) + if !ok { + api.WriteError(w, acme.AccountDoesNotExistErr(errors.New("provisioner must be of type SCEP"))) + return + } + + ctx = context.WithValue(ctx, acme.ProvisionerContextKey, Provisioner(scepProvisioner)) + next(w, r.WithContext(ctx)) + } +} + +func (h *Handler) GetCACert(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { + + ctx := r.Context() + + p, err := ProvisionerFromContext(ctx) + if err != nil { + return err + } + + // TODO: get the CA Certificates from the (signing) authority instead? I think that should be doable + certs := p.GetCACertificates() + + if len(certs) == 0 { + scepResponse.CACertNum = 0 + scepResponse.Err = errors.New("missing CA Cert") + } else if len(certs) == 1 { + scepResponse.Data = certs[0].Raw + scepResponse.CACertNum = 1 + } else { + data, err := microscep.DegenerateCertificates(certs) + scepResponse.Data = data + scepResponse.Err = err + } + + return writeSCEPResponse(w, scepResponse) +} + +func (h *Handler) GetCACaps(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { + + ctx := r.Context() + + _, err := ProvisionerFromContext(ctx) + if err != nil { + return err + } + + // TODO: get the actual capabilities from provisioner config + scepResponse.Data = formatCapabilities(defaultCapabilities) + + return writeSCEPResponse(w, scepResponse) +} + +func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepRequest SCEPRequest, scepResponse SCEPResponse) error { + + msg, err := microscep.ParsePKIMessage(scepRequest.Message) + if err != nil { + return err + } + + ctx := r.Context() + p, err := ProvisionerFromContext(ctx) + if err != nil { + return err + } + certs := p.GetCACertificates() + key := p.GetSigningKey() + + ca := certs[0] + if err := msg.DecryptPKIEnvelope(ca, key); err != nil { + return err + } + + if msg.MessageType == microscep.PKCSReq { + // TODO: CSR validation, like challenge password + } + + csr := msg.CSRReqMessage.CSR + id, err := createKeyIdentifier(csr.PublicKey) + if err != nil { + return err + } + + serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? + + days := 40 + + template := &x509.Certificate{ + SerialNumber: serial, + Subject: csr.Subject, + NotBefore: time.Now().Add(-600).UTC(), + NotAfter: time.Now().AddDate(0, 0, days).UTC(), + SubjectKeyId: id, + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + }, + SignatureAlgorithm: csr.SignatureAlgorithm, + EmailAddresses: csr.EmailAddresses, + } + + certRep, err := msg.SignCSR(ca, key, template) + if err != nil { + return err + } + + //cert := certRep.CertRepMessage.Certificate + //name := certName(cert) + + // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not + // TODO: store the new cert for CN locally + + scepResponse.Data = certRep.Raw + + return writeSCEPResponse(w, scepResponse) +} + +func certName(cert *x509.Certificate) string { + if cert.Subject.CommonName != "" { + return cert.Subject.CommonName + } + return string(cert.Signature) +} + +// createKeyIdentifier create an identifier for public keys +// according to the first method in RFC5280 section 4.2.1.2. +func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { + + keyBytes, err := x509.MarshalPKIXPublicKey(pub) + if err != nil { + return nil, err + } + + id := sha1.Sum(keyBytes) + + return id[:], nil +} + +func formatCapabilities(caps []string) []byte { + return []byte(strings.Join(caps, "\n")) +} + +// writeSCEPResponse writes a SCEP response back to the SCEP client. +func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) error { + if response.Err != nil { + http.Error(w, response.Err.Error(), http.StatusInternalServerError) + return nil + } + w.Header().Set("Content-Type", contentHeader(response.Operation, response.CACertNum)) + w.Write(response.Data) + return nil +} + +var ( + // TODO: check the default capabilities + defaultCapabilities = []string{ + "Renewal", + "SHA-1", + "SHA-256", + "AES", + "DES3", + "SCEPStandard", + "POSTPKIOperation", + } +) + +const ( + certChainHeader = "application/x-x509-ca-ra-cert" + leafHeader = "application/x-x509-ca-cert" + pkiOpHeader = "application/x-pki-message" +) + +func contentHeader(operation string, certNum int) string { + switch operation { + case opnGetCACert: + if certNum > 1 { + return certChainHeader + } + return leafHeader + case opnPKIOperation: + return pkiOpHeader + default: + return "text/plain" + } +} + +// ProvisionerFromContext searches the context for a provisioner. Returns the +// provisioner or an error. +func ProvisionerFromContext(ctx context.Context) (Provisioner, error) { + val := ctx.Value(acme.ProvisionerContextKey) + if val == nil { + return nil, acme.ServerInternalErr(errors.New("provisioner expected in request context")) + } + pval, ok := val.(Provisioner) + if !ok || pval == nil { + return nil, acme.ServerInternalErr(errors.New("provisioner in context is not a SCEP provisioner")) + } + return pval, nil +} diff --git a/scep/authority.go b/scep/authority.go new file mode 100644 index 00000000..9a10357c --- /dev/null +++ b/scep/authority.go @@ -0,0 +1,88 @@ +package scep + +import ( + "crypto/x509" + + "github.com/smallstep/certificates/authority/provisioner" + + "github.com/smallstep/nosql" +) + +// Interface is the SCEP authority interface. +type Interface interface { + // GetDirectory(ctx context.Context) (*Directory, error) + // NewNonce() (string, error) + // UseNonce(string) error + + // DeactivateAccount(ctx context.Context, accID string) (*Account, error) + // GetAccount(ctx context.Context, accID string) (*Account, error) + // GetAccountByKey(ctx context.Context, key *jose.JSONWebKey) (*Account, error) + // NewAccount(ctx context.Context, ao AccountOptions) (*Account, error) + // UpdateAccount(context.Context, string, []string) (*Account, error) + + // GetAuthz(ctx context.Context, accID string, authzID string) (*Authz, error) + // ValidateChallenge(ctx context.Context, accID string, chID string, key *jose.JSONWebKey) (*Challenge, error) + + // FinalizeOrder(ctx context.Context, accID string, orderID string, csr *x509.CertificateRequest) (*Order, error) + // GetOrder(ctx context.Context, accID string, orderID string) (*Order, error) + // GetOrdersByAccount(ctx context.Context, accID string) ([]string, error) + // NewOrder(ctx context.Context, oo OrderOptions) (*Order, error) + + // GetCertificate(string, string) ([]byte, error) + + LoadProvisionerByID(string) (provisioner.Interface, error) + // GetLink(ctx context.Context, linkType Link, absoluteLink bool, inputs ...string) string + // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string + + GetCACerts() ([]*x509.Certificate, error) +} + +// Authority is the layer that handles all SCEP interactions. +type Authority struct { + //certificates []*x509.Certificate + //authConfig authority.AuthConfig + backdate provisioner.Duration + db nosql.DB + // dir *directory + signAuth SignAuthority +} + +// AuthorityOptions required to create a new SCEP Authority. +type AuthorityOptions struct { + Certificates []*x509.Certificate + //AuthConfig authority.AuthConfig + Backdate provisioner.Duration + // DB is the database used by nosql. + DB nosql.DB + // DNS the host used to generate accurate SCEP links. By default the authority + // will use the Host from the request, so this value will only be used if + // request.Host is empty. + DNS string + // Prefix is a URL path prefix under which the SCEP api is served. This + // prefix is required to generate accurate SCEP links. + Prefix string +} + +// SignAuthority is the interface implemented by a CA authority. +type SignAuthority interface { + Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + LoadProvisionerByID(string) (provisioner.Interface, error) +} + +// LoadProvisionerByID calls out to the SignAuthority interface to load a +// provisioner by ID. +func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { + return a.signAuth.LoadProvisionerByID(id) +} + +func (a *Authority) GetCACerts() ([]*x509.Certificate, error) { + + // TODO: implement the SCEP authority + + return []*x509.Certificate{}, nil +} + +// Interface guards +var ( + _ Interface = (*Authority)(nil) +) diff --git a/scep/provisioner.go b/scep/provisioner.go new file mode 100644 index 00000000..4de40621 --- /dev/null +++ b/scep/provisioner.go @@ -0,0 +1,17 @@ +package scep + +import ( + "crypto/rsa" + "crypto/x509" +) + +// Provisioner is an interface that implements a subset of the provisioner.Interface -- +// only those methods required by the SCEP api/authority. +type Provisioner interface { + // AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) + // GetName() string + // DefaultTLSCertDuration() time.Duration + // GetOptions() *provisioner.Options + GetCACertificates() []*x509.Certificate + GetSigningKey() *rsa.PrivateKey +} diff --git a/scep/scep.go b/scep/scep.go new file mode 100644 index 00000000..c88027ff --- /dev/null +++ b/scep/scep.go @@ -0,0 +1,38 @@ +package scep + +import ( + database "github.com/smallstep/certificates/db" +) + +const ( + opnGetCACert = "GetCACert" + opnGetCACaps = "GetCACaps" + opnPKIOperation = "PKIOperation" +) + +// New returns a new Authority that implements the SCEP interface. +func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { + if _, ok := ops.DB.(*database.SimpleDB); !ok { + // TODO: see ACME implementation + } + return &Authority{ + //certificates: ops.Certificates, + backdate: ops.Backdate, + db: ops.DB, + signAuth: signAuth, + }, nil +} + +// SCEPRequest is a SCEP server request. +type SCEPRequest struct { + Operation string + Message []byte +} + +// SCEPResponse is a SCEP server response. +type SCEPResponse struct { + Operation string + CACertNum int + Data []byte + Err error +} From 339039768c9a52265f156e1f9f93cc1355dcc541 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Feb 2021 17:02:39 +0100 Subject: [PATCH 026/291] Refactor SCEP authority initialization and clean some code --- authority/authority.go | 15 +++-- authority/provisioner/collection.go | 2 + authority/provisioner/provisioner.go | 4 -- authority/provisioner/scep.go | 68 +++++-------------- ca/ca.go | 3 +- scep/{ => api}/api.go | 67 ++++++++++++------- scep/authority.go | 98 +++++++++++++++++++++++++--- scep/provisioner.go | 13 ++-- scep/scep.go | 38 ----------- 9 files changed, 168 insertions(+), 140 deletions(-) rename scep/{ => api}/api.go (85%) delete mode 100644 scep/scep.go diff --git a/authority/authority.go b/authority/authority.go index a0b15649..5652c71a 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -442,13 +442,14 @@ func (a *Authority) init() error { audiences := a.config.GetAudiences() a.provisioners = provisioner.NewCollection(audiences) config := provisioner.Config{ - // TODO: this probably shouldn't happen like this; via SignAuth instead? - IntermediateCert: a.config.IntermediateCert, - SigningKey: a.config.IntermediateKey, - CACertificates: a.rootX509Certs, - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, + // TODO: I'm not sure if extending this configuration is a good way to integrate + // It's powerful, but leaks quite some seemingly internal stuff to the provisioner. + // IntermediateCert: a.config.IntermediateCert, + // SigningKey: a.config.IntermediateKey, + // CACertificates: a.rootX509Certs, + Claims: claimer.Claims(), + Audiences: audiences, + DB: a.db, SSHKeys: &provisioner.SSHKeys{ UserKeys: sshKeys.UserKeys, HostKeys: sshKeys.HostKeys, diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index ccfbc60a..eecacf69 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -157,6 +157,8 @@ func (c *Collection) LoadByCertificate(cert *x509.Certificate) (Interface, bool) return c.Load("gcp/" + string(provisioner.Name)) case TypeACME: return c.Load("acme/" + string(provisioner.Name)) + case TypeSCEP: + return c.Load("scep/" + string(provisioner.Name)) case TypeX5C: return c.Load("x5c/" + string(provisioner.Name)) case TypeK8sSA: diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 3824bf6c..83cc6946 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -183,10 +183,6 @@ type SSHKeys struct { // Config defines the default parameters used in the initialization of // provisioners. type Config struct { - // TODO: these probably shouldn't be here but passed via SignAuth - IntermediateCert string - SigningKey string - CACertificates []*x509.Certificate // Claims are the default claims. Claims Claims // Audiences are the audiences used in the default provisioner, (JWK). diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 741add16..30b4a1b2 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -1,11 +1,7 @@ package provisioner import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "io/ioutil" + "time" "github.com/pkg/errors" ) @@ -16,14 +12,11 @@ type SCEP struct { *base Type string `json:"type"` Name string `json:"name"` - // ForceCN bool `json:"forceCN,omitempty"` - // Claims *Claims `json:"claims,omitempty"` - // Options *Options `json:"options,omitempty"` - // claimer *Claimer - IntermediateCert string - SigningKey string - CACertificates []*x509.Certificate + // ForceCN bool `json:"forceCN,omitempty"` + Options *Options `json:"options,omitempty"` + Claims *Claims `json:"claims,omitempty"` + claimer *Claimer } // GetID returns the provisioner unique identifier. @@ -51,40 +44,15 @@ func (s *SCEP) GetTokenID(ott string) (string, error) { return "", errors.New("scep provisioner does not implement GetTokenID") } -// GetCACertificates returns the CA certificate chain -// TODO: this should come from the authority instead? -func (s *SCEP) GetCACertificates() []*x509.Certificate { - - pemtxt, _ := ioutil.ReadFile(s.IntermediateCert) // TODO: move reading key to init? That's probably safer. - block, _ := pem.Decode([]byte(pemtxt)) - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - fmt.Println(err) - } - - // TODO: return chain? I'm not sure if the client understands it correctly - return []*x509.Certificate{cert} +// GetOptions returns the configured provisioner options. +func (s *SCEP) GetOptions() *Options { + return s.Options } -func (s *SCEP) GetSigningKey() *rsa.PrivateKey { - - keyBytes, err := ioutil.ReadFile(s.SigningKey) - if err != nil { - return nil - } - - block, _ := pem.Decode([]byte(keyBytes)) - if block == nil { - return nil - } - - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - fmt.Println(err) - return nil - } - - return key +// DefaultTLSCertDuration returns the default TLS cert duration enforced by +// the provisioner. +func (s *SCEP) DefaultTLSCertDuration() time.Duration { + return s.claimer.DefaultTLSCertDuration() } // Init initializes and validates the fields of a JWK type. @@ -97,14 +65,10 @@ func (s *SCEP) Init(config Config) (err error) { return errors.New("provisioner name cannot be empty") } - // // Update claims with global ones - // if p.claimer, err = NewClaimer(p.Claims, config.Claims); err != nil { - // return err - // } - - s.IntermediateCert = config.IntermediateCert - s.SigningKey = config.SigningKey - s.CACertificates = config.CACertificates + // Update claims with global ones + if s.claimer, err = NewClaimer(s.Claims, config.Claims); err != nil { + return err + } return err } diff --git a/ca/ca.go b/ca/ca.go index 405ebe47..fa71abd6 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -23,6 +23,7 @@ import ( "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" "github.com/smallstep/certificates/scep" + scepAPI "github.com/smallstep/certificates/scep/api" "github.com/smallstep/certificates/server" "github.com/smallstep/nosql" ) @@ -180,8 +181,8 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { mgmtHandler.Route(r) }) } - if ca.shouldServeSCEPEndpoints() { + if ca.shouldServeSCEPEndpoints() { scepPrefix := "scep" scepAuthority, err := scep.New(auth, scep.AuthorityOptions{ Service: auth.GetSCEPService(), diff --git a/scep/api.go b/scep/api/api.go similarity index 85% rename from scep/api.go rename to scep/api/api.go index 1f33dfd5..0e78d544 100644 --- a/scep/api.go +++ b/scep/api/api.go @@ -1,4 +1,4 @@ -package scep +package api import ( "context" @@ -19,17 +19,38 @@ import ( "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/scep" microscep "github.com/micromdm/scep/scep" ) -// Handler is the ACME request handler. +const ( + opnGetCACert = "GetCACert" + opnGetCACaps = "GetCACaps" + opnPKIOperation = "PKIOperation" +) + +// SCEPRequest is a SCEP server request. +type SCEPRequest struct { + Operation string + Message []byte +} + +// SCEPResponse is a SCEP server response. +type SCEPResponse struct { + Operation string + CACertNum int + Data []byte + Err error +} + +// Handler is the SCEP request handler. type Handler struct { - Auth Interface + Auth scep.Interface } -// New returns a new ACME API router. -func NewAPI(scepAuth Interface) api.RouterHandler { +// New returns a new SCEP API router. +func New(scepAuth scep.Interface) api.RouterHandler { return &Handler{scepAuth} } @@ -156,7 +177,6 @@ type nextHTTP = func(http.ResponseWriter, *http.Request) // Responds 404 if the provisioner does not exist. func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() // TODO: make this configurable; and we might want to look at being able to provide multiple, // like the actual ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. @@ -168,27 +188,23 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { scepProvisioner, ok := p.(*provisioner.SCEP) if !ok { - api.WriteError(w, acme.AccountDoesNotExistErr(errors.New("provisioner must be of type SCEP"))) + api.WriteError(w, errors.New("provisioner must be of type SCEP")) return } - ctx = context.WithValue(ctx, acme.ProvisionerContextKey, Provisioner(scepProvisioner)) + ctx := r.Context() + ctx = context.WithValue(ctx, acme.ProvisionerContextKey, scep.Provisioner(scepProvisioner)) next(w, r.WithContext(ctx)) } } func (h *Handler) GetCACert(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { - ctx := r.Context() - - p, err := ProvisionerFromContext(ctx) + certs, err := h.Auth.GetCACertificates() if err != nil { return err } - // TODO: get the CA Certificates from the (signing) authority instead? I think that should be doable - certs := p.GetCACertificates() - if len(certs) == 0 { scepResponse.CACertNum = 0 scepResponse.Err = errors.New("missing CA Cert") @@ -226,13 +242,16 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque return err } - ctx := r.Context() - p, err := ProvisionerFromContext(ctx) + certs, err := h.Auth.GetCACertificates() + if err != nil { + return err + } + + // TODO: instead of getting the key to decrypt, add a decrypt function to the auth; less leaky + key, err := h.Auth.GetSigningKey() if err != nil { return err } - certs := p.GetCACertificates() - key := p.GetSigningKey() ca := certs[0] if err := msg.DecryptPKIEnvelope(ca, key); err != nil { @@ -276,10 +295,12 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque //name := certName(cert) // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not - // TODO: store the new cert for CN locally + // TODO: store the new cert for CN locally; should go into the DB scepResponse.Data = certRep.Raw + api.LogCertificate(w, certRep.Certificate) + return writeSCEPResponse(w, scepResponse) } @@ -354,14 +375,14 @@ func contentHeader(operation string, certNum int) string { // ProvisionerFromContext searches the context for a provisioner. Returns the // provisioner or an error. -func ProvisionerFromContext(ctx context.Context) (Provisioner, error) { +func ProvisionerFromContext(ctx context.Context) (scep.Provisioner, error) { val := ctx.Value(acme.ProvisionerContextKey) if val == nil { - return nil, acme.ServerInternalErr(errors.New("provisioner expected in request context")) + return nil, errors.New("provisioner expected in request context") } - pval, ok := val.(Provisioner) + pval, ok := val.(scep.Provisioner) if !ok || pval == nil { - return nil, acme.ServerInternalErr(errors.New("provisioner in context is not a SCEP provisioner")) + return nil, errors.New("provisioner in context is not a SCEP provisioner") } return pval, nil } diff --git a/scep/authority.go b/scep/authority.go index 9a10357c..27817b0f 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -1,9 +1,15 @@ package scep import ( + "crypto/rsa" "crypto/x509" + "encoding/pem" + "errors" + "io/ioutil" "github.com/smallstep/certificates/authority/provisioner" + database "github.com/smallstep/certificates/db" + "go.step.sm/crypto/pemutil" "github.com/smallstep/nosql" ) @@ -34,23 +40,33 @@ type Interface interface { // GetLink(ctx context.Context, linkType Link, absoluteLink bool, inputs ...string) string // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string - GetCACerts() ([]*x509.Certificate, error) + GetCACertificates() ([]*x509.Certificate, error) + GetSigningKey() (*rsa.PrivateKey, error) } // Authority is the layer that handles all SCEP interactions. type Authority struct { - //certificates []*x509.Certificate - //authConfig authority.AuthConfig backdate provisioner.Duration db nosql.DB + prefix string + dns string + // dir *directory + + intermediateCertificate *x509.Certificate + intermediateKey *rsa.PrivateKey + + //signer crypto.Signer + signAuth SignAuthority } // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { - Certificates []*x509.Certificate - //AuthConfig authority.AuthConfig + IntermediateCertificatePath string + IntermediateKeyPath string + + // Backdate Backdate provisioner.Duration // DB is the database used by nosql. DB nosql.DB @@ -69,17 +85,83 @@ type SignAuthority interface { LoadProvisionerByID(string) (provisioner.Interface, error) } +// New returns a new Authority that implements the SCEP interface. +func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { + if _, ok := ops.DB.(*database.SimpleDB); !ok { + // TODO: see ACME implementation + } + + // TODO: the below is a bit similar as what happens in the core Authority class, which + // creates the full x509 service. However, those aren't accessible directly, which is + // why I reimplemented this (for now). There might be an alternative that I haven't + // found yet. + certificateChain, err := pemutil.ReadCertificateBundle(ops.IntermediateCertificatePath) + if err != nil { + return nil, err + } + + intermediateKey, err := readPrivateKey(ops.IntermediateKeyPath) + if err != nil { + return nil, err + } + + return &Authority{ + backdate: ops.Backdate, + db: ops.DB, + prefix: ops.Prefix, + dns: ops.DNS, + intermediateCertificate: certificateChain[0], + intermediateKey: intermediateKey, + signAuth: signAuth, + }, nil +} + +func readPrivateKey(path string) (*rsa.PrivateKey, error) { + + keyBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + block, _ := pem.Decode([]byte(keyBytes)) + if block == nil { + return nil, nil + } + + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + return key, nil +} + // LoadProvisionerByID calls out to the SignAuthority interface to load a // provisioner by ID. func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { return a.signAuth.LoadProvisionerByID(id) } -func (a *Authority) GetCACerts() ([]*x509.Certificate, error) { +// GetCACertificates returns the certificate (chain) for the CA +func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { + + if a.intermediateCertificate == nil { + return nil, errors.New("no intermediate certificate available in SCEP authority") + } + + return []*x509.Certificate{a.intermediateCertificate}, nil +} + +// GetSigningKey returns the RSA private key for the CA +// TODO: we likely should provide utility functions for decrypting and +// signing instead of providing the signing key directly +func (a *Authority) GetSigningKey() (*rsa.PrivateKey, error) { - // TODO: implement the SCEP authority + if a.intermediateKey == nil { + return nil, errors.New("no intermediate key available in SCEP authority") + } - return []*x509.Certificate{}, nil + return a.intermediateKey, nil } // Interface guards diff --git a/scep/provisioner.go b/scep/provisioner.go index 4de40621..d543d453 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -1,17 +1,16 @@ package scep import ( - "crypto/rsa" - "crypto/x509" + "time" + + "github.com/smallstep/certificates/authority/provisioner" ) // Provisioner is an interface that implements a subset of the provisioner.Interface -- // only those methods required by the SCEP api/authority. type Provisioner interface { // AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) - // GetName() string - // DefaultTLSCertDuration() time.Duration - // GetOptions() *provisioner.Options - GetCACertificates() []*x509.Certificate - GetSigningKey() *rsa.PrivateKey + GetName() string + DefaultTLSCertDuration() time.Duration + GetOptions() *provisioner.Options } diff --git a/scep/scep.go b/scep/scep.go deleted file mode 100644 index c88027ff..00000000 --- a/scep/scep.go +++ /dev/null @@ -1,38 +0,0 @@ -package scep - -import ( - database "github.com/smallstep/certificates/db" -) - -const ( - opnGetCACert = "GetCACert" - opnGetCACaps = "GetCACaps" - opnPKIOperation = "PKIOperation" -) - -// New returns a new Authority that implements the SCEP interface. -func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { - if _, ok := ops.DB.(*database.SimpleDB); !ok { - // TODO: see ACME implementation - } - return &Authority{ - //certificates: ops.Certificates, - backdate: ops.Backdate, - db: ops.DB, - signAuth: signAuth, - }, nil -} - -// SCEPRequest is a SCEP server request. -type SCEPRequest struct { - Operation string - Message []byte -} - -// SCEPResponse is a SCEP server response. -type SCEPResponse struct { - Operation string - CACertNum int - Data []byte - Err error -} From b905d5fead737831f16899eaecc5ffdfd93884f7 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 19 Feb 2021 11:01:19 +0100 Subject: [PATCH 027/291] Improve setup for multiple SCEP providers (slightly) --- scep/api/api.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index 0e78d544..7ac80cdb 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -178,9 +178,18 @@ type nextHTTP = func(http.ResponseWriter, *http.Request) func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { + // name := chi.URLParam(r, "provisionerID") + // provisionerID, err := url.PathUnescape(name) + // if err != nil { + // api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) + // return + // } + // TODO: make this configurable; and we might want to look at being able to provide multiple, - // like the actual ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. - p, err := h.Auth.LoadProvisionerByID("scep/scep1") + // like the ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. + provisionerID := "scep1" + + p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) if err != nil { api.WriteError(w, err) return From 393be5b03a21be4eb518dfb907e18192d0177d3d Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 19 Feb 2021 12:06:24 +0100 Subject: [PATCH 028/291] Add number of certs to return and fix CR LF in CACaps --- scep/api/api.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index 7ac80cdb..5a4cb5b2 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -222,6 +222,7 @@ func (h *Handler) GetCACert(w http.ResponseWriter, r *http.Request, scepResponse scepResponse.CACertNum = 1 } else { data, err := microscep.DegenerateCertificates(certs) + scepResponse.CACertNum = len(certs) scepResponse.Data = data scepResponse.Err = err } @@ -335,7 +336,7 @@ func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { } func formatCapabilities(caps []string) []byte { - return []byte(strings.Join(caps, "\n")) + return []byte(strings.Join(caps, "\r\n")) } // writeSCEPResponse writes a SCEP response back to the SCEP client. @@ -350,7 +351,7 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) error { } var ( - // TODO: check the default capabilities + // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 defaultCapabilities = []string{ "Renewal", "SHA-1", From 99cd3b74fe4d8f3e6eac668a06b81b886a4ec1a0 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 25 Feb 2021 22:28:08 +0100 Subject: [PATCH 029/291] Add full copy of mozilla/pkcs7 module as internal dependency The full contents of the git repository @432b2356ecb... was copied. Only go.mod was removed from it. --- scep/pkcs7/.gitignore | 24 ++ scep/pkcs7/.travis.yml | 10 + scep/pkcs7/LICENSE | 22 ++ scep/pkcs7/Makefile | 20 ++ scep/pkcs7/README.md | 69 ++++ scep/pkcs7/ber.go | 251 +++++++++++++ scep/pkcs7/ber_test.go | 62 ++++ scep/pkcs7/decrypt.go | 177 +++++++++ scep/pkcs7/decrypt_test.go | 60 ++++ scep/pkcs7/encrypt.go | 399 +++++++++++++++++++++ scep/pkcs7/encrypt_test.go | 102 ++++++ scep/pkcs7/pkcs7.go | 291 +++++++++++++++ scep/pkcs7/pkcs7_test.go | 326 +++++++++++++++++ scep/pkcs7/sign.go | 429 ++++++++++++++++++++++ scep/pkcs7/sign_test.go | 266 ++++++++++++++ scep/pkcs7/verify.go | 264 ++++++++++++++ scep/pkcs7/verify_test.go | 713 +++++++++++++++++++++++++++++++++++++ 17 files changed, 3485 insertions(+) create mode 100644 scep/pkcs7/.gitignore create mode 100644 scep/pkcs7/.travis.yml create mode 100644 scep/pkcs7/LICENSE create mode 100644 scep/pkcs7/Makefile create mode 100644 scep/pkcs7/README.md create mode 100644 scep/pkcs7/ber.go create mode 100644 scep/pkcs7/ber_test.go create mode 100644 scep/pkcs7/decrypt.go create mode 100644 scep/pkcs7/decrypt_test.go create mode 100644 scep/pkcs7/encrypt.go create mode 100644 scep/pkcs7/encrypt_test.go create mode 100644 scep/pkcs7/pkcs7.go create mode 100644 scep/pkcs7/pkcs7_test.go create mode 100644 scep/pkcs7/sign.go create mode 100644 scep/pkcs7/sign_test.go create mode 100644 scep/pkcs7/verify.go create mode 100644 scep/pkcs7/verify_test.go diff --git a/scep/pkcs7/.gitignore b/scep/pkcs7/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/scep/pkcs7/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/scep/pkcs7/.travis.yml b/scep/pkcs7/.travis.yml new file mode 100644 index 00000000..eac4c176 --- /dev/null +++ b/scep/pkcs7/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - "1.11" + - "1.12" + - "1.13" + - tip +before_install: + - make gettools +script: + - make diff --git a/scep/pkcs7/LICENSE b/scep/pkcs7/LICENSE new file mode 100644 index 00000000..75f32090 --- /dev/null +++ b/scep/pkcs7/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Smith + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/scep/pkcs7/Makefile b/scep/pkcs7/Makefile new file mode 100644 index 00000000..47c73b86 --- /dev/null +++ b/scep/pkcs7/Makefile @@ -0,0 +1,20 @@ +all: vet staticcheck test + +test: + go test -covermode=count -coverprofile=coverage.out . + +showcoverage: test + go tool cover -html=coverage.out + +vet: + go vet . + +lint: + golint . + +staticcheck: + staticcheck . + +gettools: + go get -u honnef.co/go/tools/... + go get -u golang.org/x/lint/golint diff --git a/scep/pkcs7/README.md b/scep/pkcs7/README.md new file mode 100644 index 00000000..bf37059c --- /dev/null +++ b/scep/pkcs7/README.md @@ -0,0 +1,69 @@ +# pkcs7 + +[![GoDoc](https://godoc.org/go.mozilla.org/pkcs7?status.svg)](https://godoc.org/go.mozilla.org/pkcs7) +[![Build Status](https://travis-ci.org/mozilla-services/pkcs7.svg?branch=master)](https://travis-ci.org/mozilla-services/pkcs7) + +pkcs7 implements parsing and creating signed and enveloped messages. + +```go +package main + +import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "os" + + "go.mozilla.org/pkcs7" +) + +func SignAndDetach(content []byte, cert *x509.Certificate, privkey *rsa.PrivateKey) (signed []byte, err error) { + toBeSigned, err := NewSignedData(content) + if err != nil { + err = fmt.Errorf("Cannot initialize signed data: %s", err) + return + } + if err = toBeSigned.AddSigner(cert, privkey, SignerInfoConfig{}); err != nil { + err = fmt.Errorf("Cannot add signer: %s", err) + return + } + + // Detach signature, omit if you want an embedded signature + toBeSigned.Detach() + + signed, err = toBeSigned.Finish() + if err != nil { + err = fmt.Errorf("Cannot finish signing data: %s", err) + return + } + + // Verify the signature + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) + p7, err := pkcs7.Parse(signed) + if err != nil { + err = fmt.Errorf("Cannot parse our signed data: %s", err) + return + } + + // since the signature was detached, reattach the content here + p7.Content = content + + if bytes.Compare(content, p7.Content) != 0 { + err = fmt.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content) + return + } + if err = p7.Verify(); err != nil { + err = fmt.Errorf("Cannot verify our signed data: %s", err) + return + } + + return signed, nil +} +``` + + + +## Credits +This is a fork of [fullsailor/pkcs7](https://github.com/fullsailor/pkcs7) diff --git a/scep/pkcs7/ber.go b/scep/pkcs7/ber.go new file mode 100644 index 00000000..58525673 --- /dev/null +++ b/scep/pkcs7/ber.go @@ -0,0 +1,251 @@ +package pkcs7 + +import ( + "bytes" + "errors" +) + +var encodeIndent = 0 + +type asn1Object interface { + EncodeTo(writer *bytes.Buffer) error +} + +type asn1Structured struct { + tagBytes []byte + content []asn1Object +} + +func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { + //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) + encodeIndent++ + inner := new(bytes.Buffer) + for _, obj := range s.content { + err := obj.EncodeTo(inner) + if err != nil { + return err + } + } + encodeIndent-- + out.Write(s.tagBytes) + encodeLength(out, inner.Len()) + out.Write(inner.Bytes()) + return nil +} + +type asn1Primitive struct { + tagBytes []byte + length int + content []byte +} + +func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error { + _, err := out.Write(p.tagBytes) + if err != nil { + return err + } + if err = encodeLength(out, p.length); err != nil { + return err + } + //fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length) + //fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content)) + out.Write(p.content) + + return nil +} + +func ber2der(ber []byte) ([]byte, error) { + if len(ber) == 0 { + return nil, errors.New("ber2der: input ber is empty") + } + //fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber)) + out := new(bytes.Buffer) + + obj, _, err := readObject(ber, 0) + if err != nil { + return nil, err + } + obj.EncodeTo(out) + + // if offset < len(ber) { + // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber)) + //} + + return out.Bytes(), nil +} + +// encodes lengths that are longer than 127 into string of bytes +func marshalLongLength(out *bytes.Buffer, i int) (err error) { + n := lengthLength(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +// computes the byte length of an encoded length value +func lengthLength(i int) (numBytes int) { + numBytes = 1 + for i > 255 { + numBytes++ + i >>= 8 + } + return +} + +// encodes the length in DER format +// If the length fits in 7 bits, the value is encoded directly. +// +// Otherwise, the number of bytes to encode the length is first determined. +// This number is likely to be 4 or less for a 32bit length. This number is +// added to 0x80. The length is encoded in big endian encoding follow after +// +// Examples: +// length | byte 1 | bytes n +// 0 | 0x00 | - +// 120 | 0x78 | - +// 200 | 0x81 | 0xC8 +// 500 | 0x82 | 0x01 0xF4 +// +func encodeLength(out *bytes.Buffer, length int) (err error) { + if length >= 128 { + l := lengthLength(length) + err = out.WriteByte(0x80 | byte(l)) + if err != nil { + return + } + err = marshalLongLength(out, length) + if err != nil { + return + } + } else { + err = out.WriteByte(byte(length)) + if err != nil { + return + } + } + return +} + +func readObject(ber []byte, offset int) (asn1Object, int, error) { + berLen := len(ber) + if offset >= berLen { + return nil, 0, errors.New("ber2der: offset is after end of ber data") + } + tagStart := offset + b := ber[offset] + offset++ + if offset >= berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + tag := b & 0x1F // last 5 bits + if tag == 0x1F { + tag = 0 + for ber[offset] >= 0x80 { + tag = tag*128 + ber[offset] - 0x80 + offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + } + // jvehent 20170227: this doesn't appear to be used anywhere... + //tag = tag*128 + ber[offset] - 0x80 + offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + } + tagEnd := offset + + kind := b & 0x20 + if kind == 0 { + debugprint("--> Primitive\n") + } else { + debugprint("--> Constructed\n") + } + // read length + var length int + l := ber[offset] + offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + hack := 0 + if l > 0x80 { + numberOfBytes := (int)(l & 0x7F) + if numberOfBytes > 4 { // int is only guaranteed to be 32bit + return nil, 0, errors.New("ber2der: BER tag length too long") + } + if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { + return nil, 0, errors.New("ber2der: BER tag length is negative") + } + if (int)(ber[offset]) == 0x0 { + return nil, 0, errors.New("ber2der: BER tag length has leading zero") + } + debugprint("--> (compute length) indicator byte: %x\n", l) + debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) + for i := 0; i < numberOfBytes; i++ { + length = length*256 + (int)(ber[offset]) + offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + } + } else if l == 0x80 { + // find length by searching content + markerIndex := bytes.LastIndex(ber[offset:], []byte{0x0, 0x0}) + if markerIndex == -1 { + return nil, 0, errors.New("ber2der: Invalid BER format") + } + length = markerIndex + hack = 2 + debugprint("--> (compute length) marker found at offset: %d\n", markerIndex+offset) + } else { + length = (int)(l) + } + if length < 0 { + return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length") + } + //fmt.Printf("--> length : %d\n", length) + contentEnd := offset + length + if contentEnd > len(ber) { + return nil, 0, errors.New("ber2der: BER tag length is more than available data") + } + debugprint("--> content start : %d\n", offset) + debugprint("--> content end : %d\n", contentEnd) + debugprint("--> content : % X\n", ber[offset:contentEnd]) + var obj asn1Object + if kind == 0 { + obj = asn1Primitive{ + tagBytes: ber[tagStart:tagEnd], + length: length, + content: ber[offset:contentEnd], + } + } else { + var subObjects []asn1Object + for offset < contentEnd { + var subObj asn1Object + var err error + subObj, offset, err = readObject(ber[:contentEnd], offset) + if err != nil { + return nil, 0, err + } + subObjects = append(subObjects, subObj) + } + obj = asn1Structured{ + tagBytes: ber[tagStart:tagEnd], + content: subObjects, + } + } + + return obj, contentEnd + hack, nil +} + +func debugprint(format string, a ...interface{}) { + //fmt.Printf(format, a) +} diff --git a/scep/pkcs7/ber_test.go b/scep/pkcs7/ber_test.go new file mode 100644 index 00000000..fcb4b6a2 --- /dev/null +++ b/scep/pkcs7/ber_test.go @@ -0,0 +1,62 @@ +package pkcs7 + +import ( + "bytes" + "encoding/asn1" + "strings" + "testing" +) + +func TestBer2Der(t *testing.T) { + // indefinite length fixture + ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00} + expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01} + der, err := ber2der(ber) + if err != nil { + t.Fatalf("ber2der failed with error: %v", err) + } + if !bytes.Equal(der, expected) { + t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) + } + + if der2, err := ber2der(der); err != nil { + t.Errorf("ber2der on DER bytes failed with error: %v", err) + } else { + if !bytes.Equal(der, der2) { + t.Error("ber2der is not idempotent") + } + } + var thing struct { + Number int + } + rest, err := asn1.Unmarshal(der, &thing) + if err != nil { + t.Errorf("Cannot parse resulting DER because: %v", err) + } else if len(rest) > 0 { + t.Errorf("Resulting DER has trailing data: % X", rest) + } +} + +func TestBer2Der_Negatives(t *testing.T) { + fixtures := []struct { + Input []byte + ErrorContains string + }{ + {[]byte{0x30, 0x85}, "tag length too long"}, + {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, + {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, + {[]byte{0x30, 0x80, 0x1, 0x2}, "Invalid BER format"}, + {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, + {[]byte{0x30}, "end of ber data reached"}, + } + + for _, fixture := range fixtures { + _, err := ber2der(fixture.Input) + if err == nil { + t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains) + } + if !strings.Contains(err.Error(), fixture.ErrorContains) { + t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error()) + } + } +} diff --git a/scep/pkcs7/decrypt.go b/scep/pkcs7/decrypt.go new file mode 100644 index 00000000..0d088d62 --- /dev/null +++ b/scep/pkcs7/decrypt.go @@ -0,0 +1,177 @@ +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "errors" + "fmt" +) + +// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed +var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") + +// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data +var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") + +// Decrypt decrypts encrypted content info for recipient cert and private key +func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pkey crypto.PrivateKey) ([]byte, error) { + data, ok := p7.raw.(envelopedData) + if !ok { + return nil, ErrNotEncryptedContent + } + recipient := selectRecipientForCertificate(data.RecipientInfos, cert) + if recipient.EncryptedKey == nil { + return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") + } + switch pkey := pkey.(type) { + case *rsa.PrivateKey: + var contentKey []byte + contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey) + if err != nil { + return nil, err + } + return data.EncryptedContentInfo.decrypt(contentKey) + } + return nil, ErrUnsupportedAlgorithm +} + +// DecryptUsingPSK decrypts encrypted data using caller provided +// pre-shared secret +func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { + data, ok := p7.raw.(encryptedData) + if !ok { + return nil, ErrNotEncryptedContent + } + return data.EncryptedContentInfo.decrypt(key) +} + +func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { + alg := eci.ContentEncryptionAlgorithm.Algorithm + if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && + !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && + !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { + fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) + return nil, ErrUnsupportedAlgorithm + } + + // EncryptedContent can either be constructed of multple OCTET STRINGs + // or _be_ a tagged OCTET STRING + var cyphertext []byte + if eci.EncryptedContent.IsCompound { + // Complex case to concat all of the children OCTET STRINGs + var buf bytes.Buffer + cypherbytes := eci.EncryptedContent.Bytes + for { + var part []byte + cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) + buf.Write(part) + if cypherbytes == nil { + break + } + } + cyphertext = buf.Bytes() + } else { + // Simple case, the bytes _are_ the cyphertext + cyphertext = eci.EncryptedContent.Bytes + } + + var block cipher.Block + var err error + + switch { + case alg.Equal(OIDEncryptionAlgorithmDESCBC): + block, err = des.NewCipher(key) + case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): + block, err = des.NewTripleDESCipher(key) + case alg.Equal(OIDEncryptionAlgorithmAES256CBC), alg.Equal(OIDEncryptionAlgorithmAES256GCM): + fallthrough + case alg.Equal(OIDEncryptionAlgorithmAES128GCM), alg.Equal(OIDEncryptionAlgorithmAES128CBC): + block, err = aes.NewCipher(key) + } + + if err != nil { + return nil, err + } + + if alg.Equal(OIDEncryptionAlgorithmAES128GCM) || alg.Equal(OIDEncryptionAlgorithmAES256GCM) { + params := aesGCMParameters{} + paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes + + _, err := asn1.Unmarshal(paramBytes, ¶ms) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + if len(params.Nonce) != gcm.NonceSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + if params.ICVLen != gcm.Overhead() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + + plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil + } + + iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes + if len(iv) != block.BlockSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") + } + mode := cipher.NewCBCDecrypter(block, iv) + plaintext := make([]byte, len(cyphertext)) + mode.CryptBlocks(plaintext, cyphertext) + if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { + return nil, err + } + return plaintext, nil +} + +func unpad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + if len(data)%blocklen != 0 || len(data) == 0 { + return nil, fmt.Errorf("invalid data len %d", len(data)) + } + + // the last byte is the length of padding + padlen := int(data[len(data)-1]) + + // check padding integrity, all bytes should be the same + pad := data[len(data)-padlen:] + for _, padbyte := range pad { + if padbyte != byte(padlen) { + return nil, errors.New("invalid padding") + } + } + + return data[:len(data)-padlen], nil +} + +func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { + for _, recp := range recipients { + if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { + return recp + } + } + return recipientInfo{} +} diff --git a/scep/pkcs7/decrypt_test.go b/scep/pkcs7/decrypt_test.go new file mode 100644 index 00000000..06cc0f80 --- /dev/null +++ b/scep/pkcs7/decrypt_test.go @@ -0,0 +1,60 @@ +package pkcs7 + +import ( + "bytes" + "testing" +) + +func TestDecrypt(t *testing.T) { + fixture := UnmarshalTestFixture(EncryptedTestFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Fatal(err) + } + content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey) + if err != nil { + t.Errorf("Cannot Decrypt with error: %v", err) + } + expected := []byte("This is a test") + if !bytes.Equal(content, expected) { + t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content) + } +} + +// Content is "This is a test" +var EncryptedTestFixture = ` +-----BEGIN PKCS7----- +MIIBFwYJKoZIhvcNAQcDoIIBCDCCAQQCAQAxgcowgccCAQAwMjApMRAwDgYDVQQK +EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMAsGCSqGSIb3 +DQEBAQSBgKyP/5WlRTZD3dWMrLOX6QRNDrXEkQjhmToRwFZdY3LgUh25ZU0S/q4G +dHPV21Fv9lQD+q7l3vfeHw8M6Z1PKi9sHMVfxAkQpvaI96DTIT3YHtuLC1w3geCO +8eFWTq2qS4WChSuS/yhYosjA1kTkE0eLnVZcGw0z/WVuEZznkdyIMDIGCSqGSIb3 +DQEHATARBgUrDgMCBwQImpKsUyMPpQigEgQQRcWWrCRXqpD5Njs0GkJl+g== +-----END PKCS7----- +-----BEGIN CERTIFICATE----- +MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj +bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x +NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT +bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc +LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg +8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP ++Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI +hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18 +sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n +9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy +yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe +KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB +AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu +MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d +H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C +67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv +Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV +i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD +6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA +o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b +dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy +KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA== +-----END PRIVATE KEY-----` diff --git a/scep/pkcs7/encrypt.go b/scep/pkcs7/encrypt.go new file mode 100644 index 00000000..da57ae64 --- /dev/null +++ b/scep/pkcs7/encrypt.go @@ -0,0 +1,399 @@ +package pkcs7 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +type envelopedData struct { + Version int + RecipientInfos []recipientInfo `asn1:"set"` + EncryptedContentInfo encryptedContentInfo +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type recipientInfo struct { + Version int + IssuerAndSerialNumber issuerAndSerial + KeyEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedKey []byte +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent asn1.RawValue `asn1:"tag:0,optional,explicit"` +} + +const ( + // EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm + EncryptionAlgorithmDESCBC = iota + + // EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm + // Avoid this algorithm unless required for interoperability; use AES GCM instead. + EncryptionAlgorithmAES128CBC + + // EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm + // Avoid this algorithm unless required for interoperability; use AES GCM instead. + EncryptionAlgorithmAES256CBC + + // EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm + EncryptionAlgorithmAES128GCM + + // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm + EncryptionAlgorithmAES256GCM +) + +// ContentEncryptionAlgorithm determines the algorithm used to encrypt the +// plaintext message. Change the value of this variable to change which +// algorithm is used in the Encrypt() function. +var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC + +// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt +// content with an unsupported algorithm. +var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported") + +// ErrPSKNotProvided is returned when attempting to encrypt +// using a PSK without actually providing the PSK. +var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided") + +const nonceSize = 12 + +type aesGCMParameters struct { + Nonce []byte `asn1:"tag:4"` + ICVLen int +} + +func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + var keyLen int + var algID asn1.ObjectIdentifier + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmAES128GCM: + keyLen = 16 + algID = OIDEncryptionAlgorithmAES128GCM + case EncryptionAlgorithmAES256GCM: + keyLen = 32 + algID = OIDEncryptionAlgorithmAES256GCM + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm) + } + if key == nil { + // Create AES key + key = make([]byte, keyLen) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create nonce + nonce := make([]byte, nonceSize) + + _, err := rand.Read(nonce) + if err != nil { + return nil, nil, err + } + + // Encrypt content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + ciphertext := gcm.Seal(nil, nonce, content, nil) + + // Prepare ASN.1 Encrypted Content Info + paramSeq := aesGCMParameters{ + Nonce: nonce, + ICVLen: gcm.Overhead(), + } + + paramBytes, err := asn1.Marshal(paramSeq) + if err != nil { + return nil, nil, err + } + + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: algID, + Parameters: asn1.RawValue{ + Tag: asn1.TagSequence, + Bytes: paramBytes, + }, + }, + EncryptedContent: marshalEncryptedContent(ciphertext), + } + + return key, &eci, nil +} + +func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + if key == nil { + // Create DES key + key = make([]byte, 8) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create CBC IV + iv := make([]byte, des.BlockSize) + _, err := rand.Read(iv) + if err != nil { + return nil, nil, err + } + + // Encrypt padded content + block, err := des.NewCipher(key) + if err != nil { + return nil, nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + plaintext, err := pad(content, mode.BlockSize()) + if err != nil { + return nil, nil, err + } + cyphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(cyphertext, plaintext) + + // Prepare ASN.1 Encrypted Content Info + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: OIDEncryptionAlgorithmDESCBC, + Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, + }, + EncryptedContent: marshalEncryptedContent(cyphertext), + } + + return key, &eci, nil +} + +func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + var keyLen int + var algID asn1.ObjectIdentifier + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmAES128CBC: + keyLen = 16 + algID = OIDEncryptionAlgorithmAES128CBC + case EncryptionAlgorithmAES256CBC: + keyLen = 32 + algID = OIDEncryptionAlgorithmAES256CBC + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm) + } + + if key == nil { + // Create AES key + key = make([]byte, keyLen) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create CBC IV + iv := make([]byte, aes.BlockSize) + _, err := rand.Read(iv) + if err != nil { + return nil, nil, err + } + + // Encrypt padded content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + plaintext, err := pad(content, mode.BlockSize()) + if err != nil { + return nil, nil, err + } + cyphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(cyphertext, plaintext) + + // Prepare ASN.1 Encrypted Content Info + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: algID, + Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, + }, + EncryptedContent: marshalEncryptedContent(cyphertext), + } + + return key, &eci, nil +} + +// Encrypt creates and returns an envelope data PKCS7 structure with encrypted +// recipient keys for each recipient public key. +// +// The algorithm used to perform encryption is determined by the current value +// of the global ContentEncryptionAlgorithm package variable. By default, the +// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the +// value before calling Encrypt(). For example: +// +// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM +// +// TODO(fullsailor): Add support for encrypting content with other algorithms +func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { + var eci *encryptedContentInfo + var key []byte + var err error + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + key, eci, err = encryptDESCBC(content, nil) + case EncryptionAlgorithmAES128CBC: + fallthrough + case EncryptionAlgorithmAES256CBC: + key, eci, err = encryptAESCBC(content, nil) + case EncryptionAlgorithmAES128GCM: + fallthrough + case EncryptionAlgorithmAES256GCM: + key, eci, err = encryptAESGCM(content, nil) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + + // Prepare each recipient's encrypted cipher key + recipientInfos := make([]recipientInfo, len(recipients)) + for i, recipient := range recipients { + encrypted, err := encryptKey(key, recipient) + if err != nil { + return nil, err + } + ias, err := cert2issuerAndSerial(recipient) + if err != nil { + return nil, err + } + info := recipientInfo{ + Version: 0, + IssuerAndSerialNumber: ias, + KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: OIDEncryptionAlgorithmRSA, + }, + EncryptedKey: encrypted, + } + recipientInfos[i] = info + } + + // Prepare envelope content + envelope := envelopedData{ + EncryptedContentInfo: *eci, + Version: 0, + RecipientInfos: recipientInfos, + } + innerContent, err := asn1.Marshal(envelope) + if err != nil { + return nil, err + } + + // Prepare outer payload structure + wrapper := contentInfo{ + ContentType: OIDEnvelopedData, + Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, + } + + return asn1.Marshal(wrapper) +} + +// EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, +// encrypted using caller provided pre-shared secret. +func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { + var eci *encryptedContentInfo + var err error + + if key == nil { + return nil, ErrPSKNotProvided + } + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + _, eci, err = encryptDESCBC(content, key) + + case EncryptionAlgorithmAES128GCM: + fallthrough + case EncryptionAlgorithmAES256GCM: + _, eci, err = encryptAESGCM(content, key) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + + // Prepare encrypted-data content + ed := encryptedData{ + Version: 0, + EncryptedContentInfo: *eci, + } + innerContent, err := asn1.Marshal(ed) + if err != nil { + return nil, err + } + + // Prepare outer payload structure + wrapper := contentInfo{ + ContentType: OIDEncryptedData, + Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, + } + + return asn1.Marshal(wrapper) +} + +func marshalEncryptedContent(content []byte) asn1.RawValue { + asn1Content, _ := asn1.Marshal(content) + return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} +} + +func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { + if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { + return rsa.EncryptPKCS1v15(rand.Reader, pub, key) + } + return nil, ErrUnsupportedAlgorithm +} + +func pad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + padlen := blocklen - (len(data) % blocklen) + if padlen == 0 { + padlen = blocklen + } + pad := bytes.Repeat([]byte{byte(padlen)}, padlen) + return append(data, pad...), nil +} diff --git a/scep/pkcs7/encrypt_test.go b/scep/pkcs7/encrypt_test.go new file mode 100644 index 00000000..c64381e2 --- /dev/null +++ b/scep/pkcs7/encrypt_test.go @@ -0,0 +1,102 @@ +package pkcs7 + +import ( + "bytes" + "crypto/x509" + "testing" +) + +func TestEncrypt(t *testing.T) { + modes := []int{ + EncryptionAlgorithmDESCBC, + EncryptionAlgorithmAES128CBC, + EncryptionAlgorithmAES256CBC, + EncryptionAlgorithmAES128GCM, + EncryptionAlgorithmAES256GCM, + } + sigalgs := []x509.SignatureAlgorithm{ + x509.SHA1WithRSA, + x509.SHA256WithRSA, + x509.SHA512WithRSA, + } + for _, mode := range modes { + for _, sigalg := range sigalgs { + ContentEncryptionAlgorithm = mode + + plaintext := []byte("Hello Secret World!") + cert, err := createTestCertificate(sigalg) + if err != nil { + t.Fatal(err) + } + encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) + if err != nil { + t.Fatal(err) + } + p7, err := Parse(encrypted) + if err != nil { + t.Fatalf("cannot Parse encrypted result: %s", err) + } + result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) + if err != nil { + t.Fatalf("cannot Decrypt encrypted result: %s", err) + } + if !bytes.Equal(plaintext, result) { + t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + } + } + } +} + +func TestEncryptUsingPSK(t *testing.T) { + modes := []int{ + EncryptionAlgorithmDESCBC, + EncryptionAlgorithmAES128GCM, + } + + for _, mode := range modes { + ContentEncryptionAlgorithm = mode + plaintext := []byte("Hello Secret World!") + var key []byte + + switch mode { + case EncryptionAlgorithmDESCBC: + key = []byte("64BitKey") + case EncryptionAlgorithmAES128GCM: + key = []byte("128BitKey4AESGCM") + } + ciphertext, err := EncryptUsingPSK(plaintext, key) + if err != nil { + t.Fatal(err) + } + + p7, _ := Parse(ciphertext) + result, err := p7.DecryptUsingPSK(key) + if err != nil { + t.Fatalf("cannot Decrypt encrypted result: %s", err) + } + if !bytes.Equal(plaintext, result) { + t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + } + } +} + +func TestPad(t *testing.T) { + tests := []struct { + Original []byte + Expected []byte + BlockSize int + }{ + {[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8}, + {[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8}, + } + for _, test := range tests { + padded, err := pad(test.Original, test.BlockSize) + if err != nil { + t.Errorf("pad encountered error: %s", err) + continue + } + if !bytes.Equal(test.Expected, padded) { + t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded) + } + } +} diff --git a/scep/pkcs7/pkcs7.go b/scep/pkcs7/pkcs7.go new file mode 100644 index 00000000..ccc6cc6d --- /dev/null +++ b/scep/pkcs7/pkcs7.go @@ -0,0 +1,291 @@ +// Package pkcs7 implements parsing and generation of some PKCS#7 structures. +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "sort" + + _ "crypto/sha1" // for crypto.SHA1 +) + +// PKCS7 Represents a PKCS7 structure +type PKCS7 struct { + Content []byte + Certificates []*x509.Certificate + CRLs []pkix.CertificateList + Signers []signerInfo + raw interface{} +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"explicit,optional,tag:0"` +} + +// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. +// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), +// and Enveloped Data are supported (1.2.840.113549.1.7.3) +var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") + +type unsignedData []byte + +var ( + // Signed Data OIDs + OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} + OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} + OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} + OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} + OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} + OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} + OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} + + // Digest Algorithms + OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} + OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + + OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + // Signature Algorithms + OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + + OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} + + // Encryption Algorithms + OIDEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} + OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} + OIDEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} + OIDEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} + OIDEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} + OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} +) + +func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { + switch { + case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1), + oid.Equal(OIDDigestAlgorithmDSA), oid.Equal(OIDDigestAlgorithmDSASHA1), + oid.Equal(OIDEncryptionAlgorithmRSA): + return crypto.SHA1, nil + case oid.Equal(OIDDigestAlgorithmSHA256), oid.Equal(OIDDigestAlgorithmECDSASHA256): + return crypto.SHA256, nil + case oid.Equal(OIDDigestAlgorithmSHA384), oid.Equal(OIDDigestAlgorithmECDSASHA384): + return crypto.SHA384, nil + case oid.Equal(OIDDigestAlgorithmSHA512), oid.Equal(OIDDigestAlgorithmECDSASHA512): + return crypto.SHA512, nil + } + return crypto.Hash(0), ErrUnsupportedAlgorithm +} + +// getDigestOIDForSignatureAlgorithm takes an x509.SignatureAlgorithm +// and returns the corresponding OID digest algorithm +func getDigestOIDForSignatureAlgorithm(digestAlg x509.SignatureAlgorithm) (asn1.ObjectIdentifier, error) { + switch digestAlg { + case x509.SHA1WithRSA, x509.ECDSAWithSHA1: + return OIDDigestAlgorithmSHA1, nil + case x509.SHA256WithRSA, x509.ECDSAWithSHA256: + return OIDDigestAlgorithmSHA256, nil + case x509.SHA384WithRSA, x509.ECDSAWithSHA384: + return OIDDigestAlgorithmSHA384, nil + case x509.SHA512WithRSA, x509.ECDSAWithSHA512: + return OIDDigestAlgorithmSHA512, nil + } + return nil, fmt.Errorf("pkcs7: cannot convert hash to oid, unknown hash algorithm") +} + +// getOIDForEncryptionAlgorithm takes the private key type of the signer and +// the OID of a digest algorithm to return the appropriate signerInfo.DigestEncryptionAlgorithm +func getOIDForEncryptionAlgorithm(pkey crypto.PrivateKey, OIDDigestAlg asn1.ObjectIdentifier) (asn1.ObjectIdentifier, error) { + switch pkey.(type) { + case *rsa.PrivateKey: + switch { + default: + return OIDEncryptionAlgorithmRSA, nil + case OIDDigestAlg.Equal(OIDEncryptionAlgorithmRSA): + return OIDEncryptionAlgorithmRSA, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): + return OIDEncryptionAlgorithmRSASHA1, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): + return OIDEncryptionAlgorithmRSASHA256, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): + return OIDEncryptionAlgorithmRSASHA384, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): + return OIDEncryptionAlgorithmRSASHA512, nil + } + case *ecdsa.PrivateKey: + switch { + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): + return OIDDigestAlgorithmECDSASHA1, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): + return OIDDigestAlgorithmECDSASHA256, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): + return OIDDigestAlgorithmECDSASHA384, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): + return OIDDigestAlgorithmECDSASHA512, nil + } + case *dsa.PrivateKey: + return OIDDigestAlgorithmDSA, nil + } + return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey) + +} + +// Parse decodes a DER encoded PKCS7 package +func Parse(data []byte) (p7 *PKCS7, err error) { + if len(data) == 0 { + return nil, errors.New("pkcs7: input data is empty") + } + var info contentInfo + der, err := ber2der(data) + if err != nil { + return nil, err + } + rest, err := asn1.Unmarshal(der, &info) + if len(rest) > 0 { + err = asn1.SyntaxError{Msg: "trailing data"} + return + } + if err != nil { + return + } + + // fmt.Printf("--> Content Type: %s", info.ContentType) + switch { + case info.ContentType.Equal(OIDSignedData): + return parseSignedData(info.Content.Bytes) + case info.ContentType.Equal(OIDEnvelopedData): + return parseEnvelopedData(info.Content.Bytes) + case info.ContentType.Equal(OIDEncryptedData): + return parseEncryptedData(info.Content.Bytes) + } + return nil, ErrUnsupportedContentType +} + +func parseEnvelopedData(data []byte) (*PKCS7, error) { + var ed envelopedData + if _, err := asn1.Unmarshal(data, &ed); err != nil { + return nil, err + } + return &PKCS7{ + raw: ed, + }, nil +} + +func parseEncryptedData(data []byte) (*PKCS7, error) { + var ed encryptedData + if _, err := asn1.Unmarshal(data, &ed); err != nil { + return nil, err + } + return &PKCS7{ + raw: ed, + }, nil +} + +func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { + if len(raw.Raw) == 0 { + return nil, nil + } + + var val asn1.RawValue + if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { + return nil, err + } + + return x509.ParseCertificates(val.Bytes) +} + +func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { + return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Equal(cert.RawIssuer, ias.IssuerName.FullBytes) +} + +// Attribute represents a key value pair attribute. Value must be marshalable byte +// `encoding/asn1` +type Attribute struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +type attributes struct { + types []asn1.ObjectIdentifier + values []interface{} +} + +// Add adds the attribute, maintaining insertion order +func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { + attrs.types = append(attrs.types, attrType) + attrs.values = append(attrs.values, value) +} + +type sortableAttribute struct { + SortKey []byte + Attribute attribute +} + +type attributeSet []sortableAttribute + +func (sa attributeSet) Len() int { + return len(sa) +} + +func (sa attributeSet) Less(i, j int) bool { + return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 +} + +func (sa attributeSet) Swap(i, j int) { + sa[i], sa[j] = sa[j], sa[i] +} + +func (sa attributeSet) Attributes() []attribute { + attrs := make([]attribute, len(sa)) + for i, attr := range sa { + attrs[i] = attr.Attribute + } + return attrs +} + +func (attrs *attributes) ForMarshalling() ([]attribute, error) { + sortables := make(attributeSet, len(attrs.types)) + for i := range sortables { + attrType := attrs.types[i] + attrValue := attrs.values[i] + asn1Value, err := asn1.Marshal(attrValue) + if err != nil { + return nil, err + } + attr := attribute{ + Type: attrType, + Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag + } + encoded, err := asn1.Marshal(attr) + if err != nil { + return nil, err + } + sortables[i] = sortableAttribute{ + SortKey: encoded, + Attribute: attr, + } + } + sort.Sort(sortables) + return sortables.Attributes(), nil +} diff --git a/scep/pkcs7/pkcs7_test.go b/scep/pkcs7/pkcs7_test.go new file mode 100644 index 00000000..e743192d --- /dev/null +++ b/scep/pkcs7/pkcs7_test.go @@ -0,0 +1,326 @@ +package pkcs7 + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "log" + "math/big" + "os" + "time" +) + +var test1024Key, test2048Key, test3072Key, test4096Key *rsa.PrivateKey + +func init() { + test1024Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("123024078101403810516614073341068864574068590522569345017786163424062310013967742924377390210586226651760719671658568413826602264886073432535341149584680111145880576802262550990305759285883150470245429547886689754596541046564560506544976611114898883158121012232676781340602508151730773214407220733898059285561"), + E: 65537, + }, + D: fromBase10("118892427340746627750435157989073921703209000249285930635312944544706203626114423392257295670807166199489096863209592887347935991101581502404113203993092422730000157893515953622392722273095289787303943046491132467130346663160540744582438810535626328230098940583296878135092036661410664695896115177534496784545"), + Primes: []*big.Int{ + fromBase10("12172745919282672373981903347443034348576729562395784527365032103134165674508405592530417723266847908118361582847315228810176708212888860333051929276459099"), + fromBase10("10106518193772789699356660087736308350857919389391620140340519320928952625438936098550728858345355053201610649202713962702543058578827268756755006576249339"), + }, + } + test1024Key.Precompute() + test2048Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"), + E: 3, + }, + D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"), + Primes: []*big.Int{ + fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"), + fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"), + }, + } + test2048Key.Precompute() + test3072Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("4799422180968749215324244710281712119910779465109490663934897082847293004098645365195947978124390029272750644394844443980065532911010718425428791498896288210928474905407341584968381379157418577471272697781778686372450913810019702928839200328075568223462554606149618941566459398862673532997592879359280754226882565483298027678735544377401276021471356093819491755877827249763065753555051973844057308627201762456191918852016986546071426986328720794061622370410645440235373576002278045257207695462423797272017386006110722769072206022723167102083033531426777518054025826800254337147514768377949097720074878744769255210076910190151785807232805749219196645305822228090875616900385866236956058984170647782567907618713309775105943700661530312800231153745705977436176908325539234432407050398510090070342851489496464612052853185583222422124535243967989533830816012180864309784486694786581956050902756173889941244024888811572094961378021"), + E: 65537, + }, + D: fromBase10("4068124900056380177006532461065648259352178312499768312132802353620854992915205894105621345694615110794369150964768050224096623567443679436821868510233726084582567244003894477723706516831312989564775159596496449435830457803384416702014837685962523313266832032687145914871879794104404800823188153886925022171560391765913739346955738372354826804228989767120353182641396181570533678315099748218734875742705419933837638038793286534641711407564379950728858267828581787483317040753987167237461567332386718574803231955771633274184646232632371006762852623964054645811527580417392163873708539175349637050049959954373319861427407953413018816604365474462455009323937599275324390953644555294418021286807661559165324810415569396577697316798600308544755741549699523972971375304826663847015905713096287495342701286542193782001358775773848824496321550110946106870685499577993864871847542645561943034990484973293461948058147956373115641615329"), + Primes: []*big.Int{ + fromBase10("2378529069722721185825622840841310902793949682948530343491428052737890236476884657507685118578733560141370511507721598189068683665232991988491561624429938984370132428230072355214627085652359350722926394699707232921674771664421591347888367477300909202851476404132163673865768760147403525700174918450753162242834161458300343282159799476695001920226357456953682236859505243928716782707623075239350380352265954107362618991716602898266999700316937680986690964564264877"), + fromBase10("2017811025336026464312837780072272578817919741496395062543647660689775637351085991504709917848745137013798005682591633910555599626950744674459976829106750083386168859581016361317479081273480343110649405858059581933773354781034946787147300862495438979895430001323443224335618577322449133208754541656374335100929456885995320929464029817626916719434010943205170760536768893924932021302887114400922813817969176636993508191950649313115712159241971065134077636674146073"), + }, + } + test3072Key.Precompute() + test4096Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("633335480064287130853997429184971616419051348693342219741748040433588285601270210251206421401040394238592139790962887290698043839174341843721930134010306454716566698330215646704263665452264344664385995704186692432827662862845900348526672531755932642433662686500295989783595767573119607065791980381547677840410600100715146047382485989885183858757974681241303484641390718944520330953604501686666386926996348457928415093305041429178744778762826377713889019740060910363468343855830206640274442887621960581569183233822878661711798998132931623726434336448716605363514220760343097572198620479297583609779817750646169845195672483600293522186340560792255595411601450766002877850696008003794520089358819042318331840490155176019070646738739580486357084733208876620846449161909966690602374519398451042362690200166144326179405976024265116931974936425064291406950542193873313447617169603706868220189295654943247311295475722243471700112334609817776430552541319671117235957754556272646031356496763094955985615723596562217985372503002989591679252640940571608314743271809251568670314461039035793703429977801961867815257832671786542212589906513979094156334941265621017752516999186481477500481433634914622735206243841674973785078408289183000133399026553"), + E: 65537, + }, + D: fromBase10("439373650557744155078930178606343279553665694488479749802070836418412881168612407941793966086633543867614175621952769177088930851151267623886678906158545451731745754402575409204816390946376103491325109185445659065122640946673660760274557781540431107937331701243915001777636528502669576801704352961341634812275635811512806966908648671988644114352046582195051714797831307925775689566757438907578527366568747104508496278929566712224252103563340770696548181508180254674236716995730292431858611476396845443056967589437890065663497768422598977743046882539288481002449571403783500529740184608873520856954837631427724158592309018382711485601884461168736465751756282510065053161144027097169985941910909130083273691945578478173708396726266170473745329617793866669307716920992380350270584929908460462802627239204245339385636926433446418108504614031393494119344916828744888432279343816084433424594432427362258172264834429525166677273382617457205387388293888430391895615438030066428745187333897518037597413369705720436392869403948934993623418405908467147848576977008003556716087129242155836114780890054057743164411952731290520995017097151300091841286806603044227906213832083363876549637037625314539090155417589796428888619937329669464810549362433"), + Primes: []*big.Int{ + fromBase10("25745433817240673759910623230144796182285844101796353869339294232644316274580053211056707671663014355388701931204078502829809738396303142990312095225333440050808647355535878394534263839500592870406002873182360027755750148248672968563366185348499498613479490545488025779331426515670185366021612402246813511722553210128074701620113404560399242413747318161403908617342170447610792422053460359960010544593668037305465806912471260799852789913123044326555978680190904164976511331681163576833618899773550873682147782263100803907156362439021929408298804955194748640633152519828940133338948391986823456836070708197320166146761"), + fromBase10("24599914864909676687852658457515103765368967514652318497893275892114442089314173678877914038802355565271545910572804267918959612739009937926962653912943833939518967731764560204997062096919833970670512726396663920955497151415639902788974842698619579886297871162402643104696160155894685518587660015182381685605752989716946154299190561137541792784125356553411300817844325739404126956793095254412123887617931225840421856505925283322918693259047428656823141903489964287619982295891439430302405252447010728112098326033634688757933930065610737780413018498561434074501822951716586796047404555397992425143397497639322075233073"), + }, + } + test4096Key.Precompute() +} + +func fromBase10(base10 string) *big.Int { + i, ok := new(big.Int).SetString(base10, 10) + if !ok { + panic("bad number: " + base10) + } + return i +} + +type certKeyPair struct { + Certificate *x509.Certificate + PrivateKey *crypto.PrivateKey +} + +func createTestCertificate(sigAlg x509.SignatureAlgorithm) (certKeyPair, error) { + signer, err := createTestCertificateByIssuer("Eddard Stark", nil, sigAlg, true) + if err != nil { + return certKeyPair{}, err + } + pair, err := createTestCertificateByIssuer("Jon Snow", signer, sigAlg, false) + if err != nil { + return certKeyPair{}, err + } + return *pair, nil +} + +func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) { + var ( + err error + priv crypto.PrivateKey + derCert []byte + issuerCert *x509.Certificate + issuerKey crypto.PrivateKey + ) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: name, + Organization: []string{"Acme Co"}, + }, + NotBefore: time.Now().Add(-1 *time.Second), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, + } + if issuer != nil { + issuerCert = issuer.Certificate + issuerKey = *issuer.PrivateKey + } + switch sigAlg { + case x509.SHA1WithRSA: + priv = test1024Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + case x509.SHA256WithRSA: + priv = test2048Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA256WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA256 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.SHA384WithRSA: + priv = test3072Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA384WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA384 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.SHA512WithRSA: + priv = test4096Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA512WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA512 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA1: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + case x509.ECDSAWithSHA256: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA256WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA256 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA384: + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA384WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA384 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA512: + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA512WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA512 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.DSAWithSHA1: + var dsaPriv dsa.PrivateKey + params := &dsaPriv.Parameters + err = dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160) + if err != nil { + return nil, err + } + err = dsa.GenerateKey(&dsaPriv, rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + priv = &dsaPriv + } + if isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + template.BasicConstraintsValid = true + } + if issuer == nil { + // no issuer given,make this a self-signed root cert + issuerCert = &template + issuerKey = priv + } + + log.Println("creating cert", name, "issued by", issuerCert.Subject.CommonName, "with sigalg", sigAlg) + switch priv.(type) { + case *rsa.PrivateKey: + switch issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) + } + case *ecdsa.PrivateKey: + switch issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) + } + case *dsa.PrivateKey: + pub := &priv.(*dsa.PrivateKey).PublicKey + switch issuerKey := issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, pub, issuerKey) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) + } + } + if err != nil { + return nil, err + } + if len(derCert) == 0 { + return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey) + } + cert, err := x509.ParseCertificate(derCert) + if err != nil { + return nil, err + } + pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) + return &certKeyPair{ + Certificate: cert, + PrivateKey: &priv, + }, nil +} + +type TestFixture struct { + Input []byte + Certificate *x509.Certificate + PrivateKey *rsa.PrivateKey +} + +func UnmarshalTestFixture(testPEMBlock string) TestFixture { + var result TestFixture + var derBlock *pem.Block + var pemBlock = []byte(testPEMBlock) + for { + derBlock, pemBlock = pem.Decode(pemBlock) + if derBlock == nil { + break + } + switch derBlock.Type { + case "PKCS7": + result.Input = derBlock.Bytes + case "CERTIFICATE": + result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) + case "PRIVATE KEY": + result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes) + } + } + + return result +} diff --git a/scep/pkcs7/sign.go b/scep/pkcs7/sign.go new file mode 100644 index 00000000..addd7638 --- /dev/null +++ b/scep/pkcs7/sign.go @@ -0,0 +1,429 @@ +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "math/big" + "time" +) + +// SignedData is an opaque data structure for creating signed data payloads +type SignedData struct { + sd signedData + certs []*x509.Certificate + data, messageDigest []byte + digestOid asn1.ObjectIdentifier + encryptionOid asn1.ObjectIdentifier +} + +// NewSignedData takes data and initializes a PKCS7 SignedData struct that is +// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default +// and can be changed by calling SetDigestAlgorithm. +func NewSignedData(data []byte) (*SignedData, error) { + content, err := asn1.Marshal(data) + if err != nil { + return nil, err + } + ci := contentInfo{ + ContentType: OIDData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + sd := signedData{ + ContentInfo: ci, + Version: 1, + } + return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1}, nil +} + +// SignerInfoConfig are optional values to include when adding a signer +type SignerInfoConfig struct { + ExtraSignedAttributes []Attribute + ExtraUnsignedAttributes []Attribute +} + +type signedData struct { + Version int `asn1:"default:1"` + DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` + ContentInfo contentInfo + Certificates rawCertificates `asn1:"optional,tag:0"` + CRLs []pkix.CertificateList `asn1:"optional,tag:1"` + SignerInfos []signerInfo `asn1:"set"` +} + +type signerInfo struct { + Version int `asn1:"default:1"` + IssuerAndSerialNumber issuerAndSerial + DigestAlgorithm pkix.AlgorithmIdentifier + AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"` + DigestEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedDigest []byte + UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"` +} + +type attribute struct { + Type asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +func marshalAttributes(attrs []attribute) ([]byte, error) { + encodedAttributes, err := asn1.Marshal(struct { + A []attribute `asn1:"set"` + }{A: attrs}) + if err != nil { + return nil, err + } + + // Remove the leading sequence octets + var raw asn1.RawValue + asn1.Unmarshal(encodedAttributes, &raw) + return raw.Bytes, nil +} + +type rawCertificates struct { + Raw asn1.RawContent +} + +type issuerAndSerial struct { + IssuerName asn1.RawValue + SerialNumber *big.Int +} + +// SetDigestAlgorithm sets the digest algorithm to be used in the signing process. +// +// This should be called before adding signers +func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) { + sd.digestOid = d +} + +// SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process. +// +// This should be called before adding signers +func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) { + sd.encryptionOid = d +} + +// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent. +func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { + var parents []*x509.Certificate + return sd.AddSignerChain(ee, pkey, parents, config) +} + +// AddSignerChain signs attributes about the content and adds certificates +// and signers infos to the Signed Data. The certificate and private key +// of the end-entity signer are used to issue the signature, and any +// parent of that end-entity that need to be added to the list of +// certifications can be specified in the parents slice. +// +// The signature algorithm used to hash the data is the one of the end-entity +// certificate. +func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error { +// Following RFC 2315, 9.2 SignerInfo type, the distinguished name of +// the issuer of the end-entity signer is stored in the issuerAndSerialNumber +// section of the SignedData.SignerInfo, alongside the serial number of +// the end-entity. + var ias issuerAndSerial + ias.SerialNumber = ee.SerialNumber + if len(parents) == 0 { + // no parent, the issuer is the end-entity cert itself + ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} + } else { + err := verifyPartialChain(ee, parents) + if err != nil { + return err + } + // the first parent is the issuer + ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject} + } + sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, + pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + ) + hash, err := getHashForOID(sd.digestOid) + if err != nil { + return err + } + h := hash.New() + h.Write(sd.data) + sd.messageDigest = h.Sum(nil) + encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid) + if err != nil { + return err + } + attrs := &attributes{} + attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType) + attrs.Add(OIDAttributeMessageDigest, sd.messageDigest) + attrs.Add(OIDAttributeSigningTime, time.Now().UTC()) + for _, attr := range config.ExtraSignedAttributes { + attrs.Add(attr.Type, attr.Value) + } + finalAttrs, err := attrs.ForMarshalling() + if err != nil { + return err + } + unsignedAttrs := &attributes{} + for _, attr := range config.ExtraUnsignedAttributes { + unsignedAttrs.Add(attr.Type, attr.Value) + } + finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() + if err != nil { + return err + } + // create signature of signed attributes + signature, err := signAttributes(finalAttrs, pkey, hash) + if err != nil { + return err + } + signer := signerInfo{ + AuthenticatedAttributes: finalAttrs, + UnauthenticatedAttributes: finalUnsignedAttrs, + DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid}, + IssuerAndSerialNumber: ias, + EncryptedDigest: signature, + Version: 1, + } + sd.certs = append(sd.certs, ee) + if len(parents) > 0 { + sd.certs = append(sd.certs, parents...) + } + sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) + return nil +} + +// SignWithoutAttr issues a signature on the content of the pkcs7 SignedData. +// Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone +// and does not include any signed attributes like timestamp and so on. +// +// This function is needed to sign old Android APKs, something you probably +// shouldn't do unless you're maintaining backward compatibility for old +// applications. +func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { + var signature []byte + sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}) + hash, err := getHashForOID(sd.digestOid) + if err != nil { + return err + } + h := hash.New() + h.Write(sd.data) + sd.messageDigest = h.Sum(nil) + switch pkey := pkey.(type) { + case *dsa.PrivateKey: + // dsa doesn't implement crypto.Signer so we make a special case + // https://github.com/golang/go/issues/27889 + r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest) + if err != nil { + return err + } + signature, err = asn1.Marshal(dsaSignature{r, s}) + if err != nil { + return err + } + default: + key, ok := pkey.(crypto.Signer) + if !ok { + return errors.New("pkcs7: private key does not implement crypto.Signer") + } + signature, err = key.Sign(rand.Reader, sd.messageDigest, hash) + if err != nil { + return err + } + } + var ias issuerAndSerial + ias.SerialNumber = ee.SerialNumber + // no parent, the issue is the end-entity cert itself + ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} + if sd.encryptionOid == nil { + // if the encryption algorithm wasn't set by SetEncryptionAlgorithm, + // infer it from the digest algorithm + sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid) + } + if err != nil { + return err + } + signer := signerInfo{ + DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid}, + IssuerAndSerialNumber: ias, + EncryptedDigest: signature, + Version: 1, + } + // create signature of signed attributes + sd.certs = append(sd.certs, ee) + sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) + return nil +} + +func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error { + unsignedAttrs := &attributes{} + for _, attr := range extraUnsignedAttrs { + unsignedAttrs.Add(attr.Type, attr.Value) + } + finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() + if err != nil { + return err + } + + si.UnauthenticatedAttributes = finalUnsignedAttrs + + return nil +} + +// AddCertificate adds the certificate to the payload. Useful for parent certificates +func (sd *SignedData) AddCertificate(cert *x509.Certificate) { + sd.certs = append(sd.certs, cert) +} + +// Detach removes content from the signed data struct to make it a detached signature. +// This must be called right before Finish() +func (sd *SignedData) Detach() { + sd.sd.ContentInfo = contentInfo{ContentType: OIDData} +} + +// GetSignedData returns the private Signed Data +func (sd *SignedData) GetSignedData() *signedData { + return &sd.sd +} + +// Finish marshals the content and its signers +func (sd *SignedData) Finish() ([]byte, error) { + sd.sd.Certificates = marshalCertificates(sd.certs) + inner, err := asn1.Marshal(sd.sd) + if err != nil { + return nil, err + } + outer := contentInfo{ + ContentType: OIDSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, + } + return asn1.Marshal(outer) +} + +// RemoveAuthenticatedAttributes removes authenticated attributes from signedData +// similar to OpenSSL's PKCS7_NOATTR or -noattr flags +func (sd *SignedData) RemoveAuthenticatedAttributes() { + for i := range sd.sd.SignerInfos { + sd.sd.SignerInfos[i].AuthenticatedAttributes = nil + } +} + +// RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData +func (sd *SignedData) RemoveUnauthenticatedAttributes() { + for i := range sd.sd.SignerInfos { + sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil + } +} + +// verifyPartialChain checks that a given cert is issued by the first parent in the list, +// then continue down the path. It doesn't require the last parent to be a root CA, +// or to be trusted in any truststore. It simply verifies that the chain provided, albeit +// partial, makes sense. +func verifyPartialChain(cert *x509.Certificate, parents []*x509.Certificate) error { + if len(parents) == 0 { + return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName) + } + err := cert.CheckSignatureFrom(parents[0]) + if err != nil { + return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err) + } + if len(parents) == 1 { + // there is no more parent to check, return + return nil + } + return verifyPartialChain(parents[0], parents[1:]) +} + +func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { + var ias issuerAndSerial + // The issuer RDNSequence has to match exactly the sequence in the certificate + // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence + ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} + ias.SerialNumber = cert.SerialNumber + + return ias, nil +} + +// signs the DER encoded form of the attributes with the private key +func signAttributes(attrs []attribute, pkey crypto.PrivateKey, digestAlg crypto.Hash) ([]byte, error) { + attrBytes, err := marshalAttributes(attrs) + if err != nil { + return nil, err + } + h := digestAlg.New() + h.Write(attrBytes) + hash := h.Sum(nil) + + // dsa doesn't implement crypto.Signer so we make a special case + // https://github.com/golang/go/issues/27889 + switch pkey := pkey.(type) { + case *dsa.PrivateKey: + r, s, err := dsa.Sign(rand.Reader, pkey, hash) + if err != nil { + return nil, err + } + return asn1.Marshal(dsaSignature{r, s}) + } + + key, ok := pkey.(crypto.Signer) + if !ok { + return nil, errors.New("pkcs7: private key does not implement crypto.Signer") + } + return key.Sign(rand.Reader, hash, digestAlg) +} + +type dsaSignature struct { + R, S *big.Int +} + +// concats and wraps the certificates in the RawValue structure +func marshalCertificates(certs []*x509.Certificate) rawCertificates { + var buf bytes.Buffer + for _, cert := range certs { + buf.Write(cert.Raw) + } + rawCerts, _ := marshalCertificateBytes(buf.Bytes()) + return rawCerts +} + +// Even though, the tag & length are stripped out during marshalling the +// RawContent, we have to encode it into the RawContent. If its missing, +// then `asn1.Marshal()` will strip out the certificate wrapper instead. +func marshalCertificateBytes(certs []byte) (rawCertificates, error) { + var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} + b, err := asn1.Marshal(val) + if err != nil { + return rawCertificates{}, err + } + return rawCertificates{Raw: b}, nil +} + +// DegenerateCertificate creates a signed data structure containing only the +// provided certificate or certificate chain. +func DegenerateCertificate(cert []byte) ([]byte, error) { + rawCert, err := marshalCertificateBytes(cert) + if err != nil { + return nil, err + } + emptyContent := contentInfo{ContentType: OIDData} + sd := signedData{ + Version: 1, + ContentInfo: emptyContent, + Certificates: rawCert, + CRLs: []pkix.CertificateList{}, + } + content, err := asn1.Marshal(sd) + if err != nil { + return nil, err + } + signedContent := contentInfo{ + ContentType: OIDSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + return asn1.Marshal(signedContent) +} diff --git a/scep/pkcs7/sign_test.go b/scep/pkcs7/sign_test.go new file mode 100644 index 00000000..0ba6324d --- /dev/null +++ b/scep/pkcs7/sign_test.go @@ -0,0 +1,266 @@ +package pkcs7 + +import ( + "bytes" + "crypto/dsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "fmt" + "io/ioutil" + "log" + "math/big" + "os" + "os/exec" + "testing" +) + +func TestSign(t *testing.T) { + content := []byte("Hello World") + sigalgs := []x509.SignatureAlgorithm{ + x509.SHA1WithRSA, + x509.SHA256WithRSA, + x509.SHA512WithRSA, + x509.ECDSAWithSHA1, + x509.ECDSAWithSHA256, + x509.ECDSAWithSHA384, + x509.ECDSAWithSHA512, + } + for _, sigalgroot := range sigalgs { + rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) + if err != nil { + t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) + } + truststore := x509.NewCertPool() + truststore.AddCert(rootCert.Certificate) + for _, sigalginter := range sigalgs { + interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) + if err != nil { + t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) + } + var parents []*x509.Certificate + parents = append(parents, interCert.Certificate) + for _, sigalgsigner := range sigalgs { + signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + for _, testDetach := range []bool{false, true} { + log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach) + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + + // Set the digest to match the end entity cert + signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm) + toBeSigned.SetDigestAlgorithm(signerDigest) + + if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { + t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if testDetach { + toBeSigned.Detach() + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) + p7, err := Parse(signed) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if testDetach { + p7.Content = content + } + if !bytes.Equal(content, p7.Content) { + t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content) + } + if err := p7.VerifyWithChain(truststore); err != nil { + t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) { + t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q", + sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm) + } + } + } + } + } +} + +func TestDSASignAndVerifyWithOpenSSL(t *testing.T) { + content := []byte("Hello World") + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + + block, _ := pem.Decode([]byte(dsaPublicCert)) + if block == nil { + t.Fatal("failed to parse certificate PEM") + } + signerCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatal("failed to parse certificate: " + err.Error()) + } + + // write the signer cert to a temp file + tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) + + priv := dsa.PrivateKey{ + PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"), + Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"), + G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"), + }, + }, + X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"), + } + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("test case: cannot initialize signed data: %s", err) + } + if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil { + t.Fatalf("Cannot add signer: %s", err) + } + toBeSigned.Detach() + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("test case: cannot finish signing data: %s", err) + } + + // write the signature to a temp file + tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755) + + // call openssl to verify the signature on the content using the root + opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify", + "-in", tmpSignatureFile.Name(), "-inform", "PEM", + "-content", tmpContentFile.Name()) + out, err := opensslCMD.CombinedOutput() + if err != nil { + t.Fatalf("test case: openssl command failed with %s: %s", err, out) + } + os.Remove(tmpSignatureFile.Name()) // clean up + os.Remove(tmpContentFile.Name()) // clean up + os.Remove(tmpSignerCertFile.Name()) // clean up +} + +func ExampleSignedData() { + // generate a signing cert or load a key pair + cert, err := createTestCertificate(x509.SHA256WithRSA) + if err != nil { + fmt.Printf("Cannot create test certificates: %s", err) + } + + // Initialize a SignedData struct with content to be signed + signedData, err := NewSignedData([]byte("Example data to be signed")) + if err != nil { + fmt.Printf("Cannot initialize signed data: %s", err) + } + + // Add the signing cert and private key + if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { + fmt.Printf("Cannot add signer: %s", err) + } + + // Call Detach() is you want to remove content from the signature + // and generate an S/MIME detached signature + signedData.Detach() + + // Finish() to obtain the signature bytes + detachedSignature, err := signedData.Finish() + if err != nil { + fmt.Printf("Cannot finish signing data: %s", err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature}) +} + +func TestUnmarshalSignedAttribute(t *testing.T) { + cert, err := createTestCertificate(x509.SHA512WithRSA) + if err != nil { + t.Fatal(err) + } + content := []byte("Hello World") + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("Cannot initialize signed data: %s", err) + } + oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} + testValue := "TestValue" + if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{ + ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}}, + }); err != nil { + t.Fatalf("Cannot add signer: %s", err) + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("Cannot finish signing data: %s", err) + } + p7, err := Parse(signed) + if err != nil { + t.Fatalf("Cannot parse signed data: %v", err) + } + var actual string + err = p7.UnmarshalSignedAttribute(oidTest, &actual) + if err != nil { + t.Fatalf("Cannot unmarshal test value: %s", err) + } + if testValue != actual { + t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual) + } +} + +func TestDegenerateCertificate(t *testing.T) { + cert, err := createTestCertificate(x509.SHA1WithRSA) + if err != nil { + t.Fatal(err) + } + deg, err := DegenerateCertificate(cert.Certificate.Raw) + if err != nil { + t.Fatal(err) + } + testOpenSSLParse(t, deg) + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg}) +} + +// writes the cert to a temporary file and tests that openssl can read it. +func testOpenSSLParse(t *testing.T, certBytes []byte) { + tmpCertFile, err := ioutil.TempFile("", "testCertificate") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpCertFile.Name()) // clean up + + if _, err := tmpCertFile.Write(certBytes); err != nil { + t.Fatal(err) + } + + opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name()) + _, err = opensslCMD.Output() + if err != nil { + t.Fatal(err) + } + + if err := tmpCertFile.Close(); err != nil { + t.Fatal(err) + } + +} +func fromHex(s string) *big.Int { + result, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(s) + } + return result +} diff --git a/scep/pkcs7/verify.go b/scep/pkcs7/verify.go new file mode 100644 index 00000000..c8ead236 --- /dev/null +++ b/scep/pkcs7/verify.go @@ -0,0 +1,264 @@ +package pkcs7 + +import ( + "crypto/subtle" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "time" +) + +// Verify is a wrapper around VerifyWithChain() that initializes an empty +// trust store, effectively disabling certificate verification when validating +// a signature. +func (p7 *PKCS7) Verify() (err error) { + return p7.VerifyWithChain(nil) +} + +// VerifyWithChain checks the signatures of a PKCS7 object. +// If truststore is not nil, it also verifies the chain of trust of the end-entity +// signer cert to one of the root in the truststore. +func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) { + if len(p7.Signers) == 0 { + return errors.New("pkcs7: Message has no signers") + } + for _, signer := range p7.Signers { + if err := verifySignature(p7, signer, truststore); err != nil { + return err + } + } + return nil +} + +func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) { + signedData := p7.Content + ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) + if ee == nil { + return errors.New("pkcs7: No certificate for signer") + } + signingTime := time.Now().UTC() + if len(signer.AuthenticatedAttributes) > 0 { + // TODO(fullsailor): First check the content type match + var digest []byte + err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) + if err != nil { + return err + } + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } + h := hash.New() + h.Write(p7.Content) + computed := h.Sum(nil) + if subtle.ConstantTimeCompare(digest, computed) != 1 { + return &MessageDigestMismatchError{ + ExpectedDigest: digest, + ActualDigest: computed, + } + } + signedData, err = marshalAttributes(signer.AuthenticatedAttributes) + if err != nil { + return err + } + err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) + if err == nil { + // signing time found, performing validity check + if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { + return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", + signingTime.Format(time.RFC3339), + ee.NotBefore.Format(time.RFC3339), + ee.NotBefore.Format(time.RFC3339)) + } + } + } + if truststore != nil { + _, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime) + if err != nil { + return err + } + } + sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) + if err != nil { + return err + } + return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) +} + +// GetOnlySigner returns an x509.Certificate for the first signer of the signed +// data payload. If there are more or less than one signer, nil is returned +func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { + if len(p7.Signers) != 1 { + return nil + } + signer := p7.Signers[0] + return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) +} + +// UnmarshalSignedAttribute decodes a single attribute from the signer info +func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { + sd, ok := p7.raw.(signedData) + if !ok { + return errors.New("pkcs7: payload is not signedData content") + } + if len(sd.SignerInfos) < 1 { + return errors.New("pkcs7: payload has no signers") + } + attributes := sd.SignerInfos[0].AuthenticatedAttributes + return unmarshalAttribute(attributes, attributeType, out) +} + +func parseSignedData(data []byte) (*PKCS7, error) { + var sd signedData + asn1.Unmarshal(data, &sd) + certs, err := sd.Certificates.Parse() + if err != nil { + return nil, err + } + // fmt.Printf("--> Signed Data Version %d\n", sd.Version) + + var compound asn1.RawValue + var content unsignedData + + // The Content.Bytes maybe empty on PKI responses. + if len(sd.ContentInfo.Content.Bytes) > 0 { + if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { + return nil, err + } + } + // Compound octet string + if compound.IsCompound { + if compound.Tag == 4 { + if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { + return nil, err + } + } else { + content = compound.Bytes + } + } else { + // assuming this is tag 04 + content = compound.Bytes + } + return &PKCS7{ + Content: content, + Certificates: certs, + CRLs: sd.CRLs, + Signers: sd.SignerInfos, + raw: sd}, nil +} + +// verifyCertChain takes an end-entity certs, a list of potential intermediates and a +// truststore, and built all potential chains between the EE and a trusted root. +// +// When verifying chains that may have expired, currentTime can be set to a past date +// to allow the verification to pass. If unset, currentTime is set to the current UTC time. +func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) { + intermediates := x509.NewCertPool() + for _, intermediate := range certs { + intermediates.AddCert(intermediate) + } + verifyOptions := x509.VerifyOptions{ + Roots: truststore, + Intermediates: intermediates, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + CurrentTime: currentTime, + } + chains, err = ee.Verify(verifyOptions) + if err != nil { + return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err) + } + return +} + +// MessageDigestMismatchError is returned when the signer data digest does not +// match the computed digest for the contained content +type MessageDigestMismatchError struct { + ExpectedDigest []byte + ActualDigest []byte +} + +func (err *MessageDigestMismatchError) Error() string { + return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) +} + +func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) { + switch { + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1): + return x509.ECDSAWithSHA1, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256): + return x509.ECDSAWithSHA256, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384): + return x509.ECDSAWithSHA384, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512): + return x509.ECDSAWithSHA512, nil + case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.SHA1WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.SHA256WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): + return x509.SHA384WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): + return x509.SHA512WithRSA, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA), + digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.DSAWithSHA1, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.DSAWithSHA256, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.ECDSAWithSHA1, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.ECDSAWithSHA256, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): + return x509.ECDSAWithSHA384, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): + return x509.ECDSAWithSHA512, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + default: + return -1, fmt.Errorf("pkcs7: unsupported algorithm %q", + digestEncryption.Algorithm.String()) + } +} + +func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { + for _, cert := range certs { + if isCertMatchForIssuerAndSerial(cert, ias) { + return cert + } + } + return nil +} + +func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { + for _, attr := range attrs { + if attr.Type.Equal(attributeType) { + _, err := asn1.Unmarshal(attr.Value.Bytes, out) + return err + } + } + return errors.New("pkcs7: attribute type not in attributes") +} diff --git a/scep/pkcs7/verify_test.go b/scep/pkcs7/verify_test.go new file mode 100644 index 00000000..f80943b2 --- /dev/null +++ b/scep/pkcs7/verify_test.go @@ -0,0 +1,713 @@ +package pkcs7 + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "os/exec" + "testing" + "time" +) + +func TestVerify(t *testing.T) { + fixture := UnmarshalTestFixture(SignedTestFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } + expected := []byte("We the People") + if !bytes.Equal(p7.Content, expected) { + t.Errorf("Signed content does not match.\n\tExpected:%s\n\tActual:%s", expected, p7.Content) + + } +} + +var SignedTestFixture = ` +-----BEGIN PKCS7----- +MIIDVgYJKoZIhvcNAQcCoIIDRzCCA0MCAQExCTAHBgUrDgMCGjAcBgkqhkiG9w0B +BwGgDwQNV2UgdGhlIFBlb3BsZaCCAdkwggHVMIIBQKADAgECAgRpuDctMAsGCSqG +SIb3DQEBCzApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3Rh +cmswHhcNMTUwNTA2MDQyNDQ4WhcNMTYwNTA2MDQyNDQ4WjAlMRAwDgYDVQQKEwdB +Y21lIENvMREwDwYDVQQDEwhKb24gU25vdzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAqr+tTF4mZP5rMwlXp1y+crRtFpuLXF1zvBZiYMfIvAHwo1ta8E1IcyEP +J1jIiKMcwbzeo6kAmZzIJRCTezq9jwXUsKbQTvcfOH9HmjUmXBRWFXZYoQs/OaaF +a45deHmwEeMQkuSWEtYiVKKZXtJOtflKIT3MryJEDiiItMkdybUCAwEAAaMSMBAw +DgYDVR0PAQH/BAQDAgCgMAsGCSqGSIb3DQEBCwOBgQDK1EweZWRL+f7Z+J0kVzY8 +zXptcBaV4Lf5wGZJLJVUgp33bpLNpT3yadS++XQJ+cvtW3wADQzBSTMduyOF8Zf+ +L7TjjrQ2+F2HbNbKUhBQKudxTfv9dJHdKbD+ngCCdQJYkIy2YexsoNG0C8nQkggy +axZd/J69xDVx6pui3Sj8sDGCATYwggEyAgEBMDEwKTEQMA4GA1UEChMHQWNtZSBD +bzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrAgRpuDctMAcGBSsOAwIaoGEwGAYJKoZI +hvcNAQkDMQsGCSqGSIb3DQEHATAgBgkqhkiG9w0BCQUxExcRMTUwNTA2MDAyNDQ4 +LTA0MDAwIwYJKoZIhvcNAQkEMRYEFG9D7gcTh9zfKiYNJ1lgB0yTh4sZMAsGCSqG +SIb3DQEBAQSBgFF3sGDU9PtXty/QMtpcFa35vvIOqmWQAIZt93XAskQOnBq4OloX +iL9Ct7t1m4pzjRm0o9nDkbaSLZe7HKASHdCqijroScGlI8M+alJ8drHSFv6ZIjnM +FIwIf0B2Lko6nh9/6mUXq7tbbIHa3Gd1JUVire/QFFtmgRXMbXYk8SIS +-----END PKCS7----- +-----BEGIN CERTIFICATE----- +MIIB1TCCAUCgAwIBAgIEabg3LTALBgkqhkiG9w0BAQswKTEQMA4GA1UEChMHQWNt +ZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTE1MDUwNjA0MjQ0OFoXDTE2 +MDUwNjA0MjQ0OFowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNu +b3cwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKq/rUxeJmT+azMJV6dcvnK0 +bRabi1xdc7wWYmDHyLwB8KNbWvBNSHMhDydYyIijHMG83qOpAJmcyCUQk3s6vY8F +1LCm0E73Hzh/R5o1JlwUVhV2WKELPzmmhWuOXXh5sBHjEJLklhLWIlSimV7STrX5 +SiE9zK8iRA4oiLTJHcm1AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIAoDALBgkqhkiG +9w0BAQsDgYEAytRMHmVkS/n+2fidJFc2PM16bXAWleC3+cBmSSyVVIKd926SzaU9 +8mnUvvl0CfnL7Vt8AA0MwUkzHbsjhfGX/i+04460Nvhdh2zWylIQUCrncU37/XSR +3Smw/p4AgnUCWJCMtmHsbKDRtAvJ0JIIMmsWXfyevcQ1ceqbot0o/LA= +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICXgIBAAKBgQCqv61MXiZk/mszCVenXL5ytG0Wm4tcXXO8FmJgx8i8AfCjW1rw +TUhzIQ8nWMiIoxzBvN6jqQCZnMglEJN7Or2PBdSwptBO9x84f0eaNSZcFFYVdlih +Cz85poVrjl14ebAR4xCS5JYS1iJUople0k61+UohPcyvIkQOKIi0yR3JtQIDAQAB +AoGBAIPLCR9N+IKxodq11lNXEaUFwMHXc1zqwP8no+2hpz3+nVfplqqubEJ4/PJY +5AgbJoIfnxVhyBXJXu7E+aD/OPneKZrgp58YvHKgGvvPyJg2gpC/1Fh0vQB0HNpI +1ZzIZUl8ZTUtVgtnCBUOh5JGI4bFokAqrT//Uvcfd+idgxqBAkEA1ZbP/Kseld14 +qbWmgmU5GCVxsZRxgR1j4lG3UVjH36KXMtRTm1atAam1uw3OEGa6Y3ANjpU52FaB +Hep5rkk4FQJBAMynMo1L1uiN5GP+KYLEF5kKRxK+FLjXR0ywnMh+gpGcZDcOae+J ++t1gLoWBIESH/Xt639T7smuSfrZSA9V0EyECQA8cvZiWDvLxmaEAXkipmtGPjKzQ +4PsOtkuEFqFl07aKDYKmLUg3aMROWrJidqsIabWxbvQgsNgSvs38EiH3wkUCQQCg +ndxb7piVXb9RBwm3OoU2tE1BlXMX+sVXmAkEhd2dwDsaxrI3sHf1xGXem5AimQRF +JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3 +BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g== +-----END PRIVATE KEY-----` + +func TestVerifyEC2(t *testing.T) { + fixture := UnmarshalTestFixture(EC2IdentityDocumentFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + p7.Certificates = []*x509.Certificate{fixture.Certificate} + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } +} + +var EC2IdentityDocumentFixture = ` +-----BEGIN PKCS7----- +MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA +JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh +eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1 +cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh +bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs +bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg +OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK +ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj +aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy +YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA +AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n +dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi +IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B +CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG +CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w +LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA +AAAAAA== +-----END PKCS7----- +-----BEGIN CERTIFICATE----- +MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw +FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD +VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z +ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u +IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl +cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e +ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3 +VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P +hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j +k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U +hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF +lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf +MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW +MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw +vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw +7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K +-----END CERTIFICATE-----` + +func TestVerifyAppStore(t *testing.T) { + fixture := UnmarshalTestFixture(AppStoreReceiptFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } +} + +var AppStoreReceiptFixture = ` +-----BEGIN PKCS7----- +MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI +hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC +AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL +AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE +AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy +NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz +gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh +YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx +WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a +8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m +MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91 +Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE +AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz +AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM +AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB +AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw +MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt +MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa +MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD +AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR +BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl +bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv +cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw +MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl +IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs +ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg +SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid +xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3 +k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ +uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb +XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI +HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw +LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0 +MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G +A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw +ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u +IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j +ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k +aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0 +aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3 +LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA +MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH +bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut +F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg +BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz +p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o +bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF +MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL +MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB +MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT +MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg +RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl +dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0 +U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV +CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8 +V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl +d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q +arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj +gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF +MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw +JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/ +BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z +viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N +w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ +TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V +AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur ++cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR +pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw +CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew +HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne ++Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz +y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ +Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS +C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB +hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB +djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp +R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/ +CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC +ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB +thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz +c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk +IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5 +IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3 +DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU +sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ +fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr +1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk +wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq +xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ +BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX +b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y +bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide +NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa +ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv +MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD +VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/ +QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD +4B85NkrgvQsWAQ== +-----END PKCS7-----` + +func TestVerifyApkEcdsa(t *testing.T) { + fixture := UnmarshalTestFixture(ApkEcdsaFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + p7.Content, err = base64.StdEncoding.DecodeString(ApkEcdsaContent) + if err != nil { + t.Errorf("Failed to decode base64 signature file: %v", err) + } + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } +} + +var ApkEcdsaFixture = `-----BEGIN PKCS7----- +MIIDAgYJKoZIhvcNAQcCoIIC8zCCAu8CAQExDzANBglghkgBZQMEAgMFADALBgkq +hkiG9w0BBwGgggH3MIIB8zCCAVSgAwIBAgIJAOxXdFsvm3YiMAoGCCqGSM49BAME +MBIxEDAOBgNVBAMMB2VjLXA1MjEwHhcNMTYwMzMxMTUzMTIyWhcNNDMwODE3MTUz +MTIyWjASMRAwDgYDVQQDDAdlYy1wNTIxMIGbMBAGByqGSM49AgEGBSuBBAAjA4GG +AAQAYX95sSjPEQqgyLD04tNUyq9y/w8seblOpfqa/Amx6H4GFdrjGXX0YTfXKr9G +hAyIyQSnNrIg0zDlWQUbBPRW4CYBLFOg1pUn1NBhKFD4NtO1KWvYtNOYDegFjRCP +B0p+fEXDbq8QFDYvlh+NZUJ16+ih8XNIf1C29xuLEqN6oKOnAvajUDBOMB0GA1Ud +DgQWBBT/Ra3kz60gQ7tYk3byZckcLabt8TAfBgNVHSMEGDAWgBT/Ra3kz60gQ7tY +k3byZckcLabt8TAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMEA4GMADCBiAJCAP39 +hYLsWk2H84oEw+HJqGGjexhqeD3vSO1mWhopripE/81oy3yV10puYoJe11xDSfcD +j2VfNCHazuXO3kSxGA/1AkIBLUJxp/WYbYzhBGKr6lcxczKI/wuMfkZ6vL+0EMJV +A/2uEoeqvnl7BsdkicyaOBNEADijuVdaPPIWzKClt9OaVxExgdAwgc0CAQEwHzAS +MRAwDgYDVQQDDAdlYy1wNTIxAgkA7Fd0Wy+bdiIwDQYJYIZIAWUDBAIDBQAwCgYI +KoZIzj0EAwQEgYswgYgCQgD1pVSNo7qTm9A6tpt3SU2yRa+xpJAnUbpZ+Gu36B71 +JnQBUzRgTGevniqHpyagi7b2zjWh1uvfz9FfrITUwGMddgJCAPjiBRcl7rKpxmZn +V1MvcJOX41xRSJu1wmBiYXqaJarL+gQ/Wl7RYsMtqLjmNColvLaHNxCaWOO/8nAE +Hg0OMA60 +-----END PKCS7-----` + +var ApkEcdsaContent = `U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KU0hBLTUxMi1EaWdlc3QtTWFuaWZlc3Q6IFAvVDRqSWtTMjQvNzFxeFE2WW1MeEtNdkRPUUF0WjUxR090dFRzUU9yemhHRQ0KIEpaUGVpWUtyUzZYY090bStYaWlFVC9uS2tYdWVtUVBwZ2RBRzFKUzFnPT0NCkNyZWF0ZWQtQnk6IDEuMCAoQW5kcm9pZCBTaWduQXBrKQ0KDQpOYW1lOiBBbmRyb2lkTWFuaWZlc3QueG1sDQpTSEEtNTEyLURpZ2VzdDogcm9NbWVQZmllYUNQSjFJK2VzMVpsYis0anB2UXowNHZqRWVpL2U0dkN1ald0VVVWSHEzMkNXDQogMUxsOHZiZGMzMCtRc1FlN29ibld4dmhLdXN2K3c1a2c9PQ0KDQpOYW1lOiByZXNvdXJjZXMuYXJzYw0KU0hBLTUxMi1EaWdlc3Q6IG5aYW1aUzlPZTRBRW41cEZaaCtoQ1JFT3krb1N6a3hHdU5YZU0wUFF6WGVBVlVQV3hSVzFPYQ0KIGVLbThRbXdmTmhhaS9HOEcwRUhIbHZEQWdlcy9HUGtBPT0NCg0KTmFtZTogY2xhc3Nlcy5kZXgNClNIQS01MTItRGlnZXN0OiBlbWlDQld2bkVSb0g2N2lCa3EwcUgrdm5tMkpaZDlMWUNEV051N3RNYzJ3bTRtV0dYSUVpWmcNCiBWZkVPV083MFRlZnFjUVhldkNtN2hQMnRpT0U3Y0w5UT09DQoNCg==` + +func TestVerifyFirefoxAddon(t *testing.T) { + fixture := UnmarshalTestFixture(FirefoxAddonFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + p7.Content = FirefoxAddonContent + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(FirefoxAddonRootCert) + if err := p7.VerifyWithChain(certPool); err != nil { + t.Errorf("Verify failed with error: %v", err) + } + // Verify the certificate chain to make sure the identified root + // is the one we expect + ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) + if ee == nil { + t.Errorf("No end-entity certificate found for signer") + } + signingTime, _ := time.Parse(time.RFC3339, "2017-02-23 09:06:16-05:00") + chains, err := verifyCertChain(ee, p7.Certificates, certPool, signingTime) + if err != nil { + t.Error(err) + } + if len(chains) != 1 { + t.Errorf("Expected to find one chain, but found %d", len(chains)) + } + if len(chains[0]) != 3 { + t.Errorf("Expected to find three certificates in chain, but found %d", len(chains[0])) + } + if chains[0][0].Subject.CommonName != "tabscope@xuldev.org" { + t.Errorf("Expected to find EE certificate with subject 'tabscope@xuldev.org', but found '%s'", chains[0][0].Subject.CommonName) + } + if chains[0][1].Subject.CommonName != "production-signing-ca.addons.mozilla.org" { + t.Errorf("Expected to find intermediate certificate with subject 'production-signing-ca.addons.mozilla.org', but found '%s'", chains[0][1].Subject.CommonName) + } + if chains[0][2].Subject.CommonName != "root-ca-production-amo" { + t.Errorf("Expected to find root certificate with subject 'root-ca-production-amo', but found '%s'", chains[0][2].Subject.CommonName) + } +} + +var FirefoxAddonContent = []byte(`Signature-Version: 1.0 +MD5-Digest-Manifest: KjRavc6/KNpuT1QLcB/Gsg== +SHA1-Digest-Manifest: 5Md5nUg+U7hQ/UfzV+xGKWOruVI= + +`) + +var FirefoxAddonFixture = ` +-----BEGIN PKCS7----- +MIIQTAYJKoZIhvcNAQcCoIIQPTCCEDkCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3 +DQEHAaCCDL0wggW6MIIDoqADAgECAgYBVpobWVwwDQYJKoZIhvcNAQELBQAwgcUx +CzAJBgNVBAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYD +VQQLEyZNb3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8G +A1UEAxMocHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0 +MDIGCSqGSIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxh +LmNvbTAeFw0xNjA4MTcyMDA0NThaFw0yMTA4MTYyMDA0NThaMHYxEzARBgNVBAsT +ClByb2R1Y3Rpb24xCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3 +MQ8wDQYDVQQKEwZBZGRvbnMxCzAJBgNVBAgTAkNBMRwwGgYDVQQDFBN0YWJzY29w +ZUB4dWxkZXYub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv6e0 +mPD8dt4J8HTNNq4ODns2DV6Weh1hllCIFvOeu1u3UrR03st0BMY8OXYwr/NvRVjg +bA8gRySWAL+XqLzbhtXNeNegAoxrF+3mYY5rJjsLj/FGI6P6OXjngqwgm9VTBl7m +jh/KXBSwYoUcavJo6cmk8sCFwoblyQiv+tsWaUCOI6zMzubNtIS+GFvET9y/VZMP +j6mk8O10wBgJF5MMtA19va3qXy7aCZ7DnZp1l3equd/L6t324TtXoqx6xWQKo6TM +I0mcTlKvm6TKegTGBCyGn3JRARoIJv4AW1qqgyaHXf9EoY2pKT8Avkri5++NuSJ6 +jtO4k/diBA2MZU20U0KGffYZNTxKDqd6XtI6y1tJPd/OWRFyU+mHntkcm9sar7L3 +nPKujHRox2re10ec1WBnJE3PjlAoesNjxzp+xs2mGGc8DX9NuWn+1uK9xmgGIIMl +OFfyQ4s0G6hKp5goFcrFZxmexu0ZahOs8vZf8xDBW7yR1zToQElOXHvrscM386os +kOF9IxQZfcCoPuNQVg1haCONNkx0oau3RQQlOSAZtC79b+rBjQ5JYfjRLYAworf2 +xQaprCh33TD1dTBrvzEbCGszgkN53Vqh5TFBjbU/NyldOkGvK8Xf6WhT5u+aftnV +lbuE2McAg6x1AlloUZq6PNTBpz7zypcIISnQ+y8CAwEAATANBgkqhkiG9w0BAQsF +AAOCAgEAIBoo2+OEYNCgP/IbUj9azaf/lde1q4AK/uTMoUeS5WcrXd8aqA0Y1qV7 +xUALgDQAExXgqcOMGu4mPMaoZDgwGI4Tj7XPJQq5Z5zYxpRf/Wtzae33T9BF6QPW +v5xiRYuol+FbEtqRHZqxDWtIrd1MWBy3wjO3pLPdzDM9jWh+HLxdGWThJszaZp3T +CqsOx+l9W0Q7qM5ioZpHStgXDfhw38Lg++kLnzcX9MqsjYyezdwE4krqW6hK3+4S +0LZE4dTgsy8JULkyAF3HrPWEXESnD7c4mx6owZe+BNDK5hsVM/obAqH7sJq/igbM +5N1l832p/ws8l5xKOr3qBWSzWn6u7ExvqG6Ckh0foJOVXvzGqvrXcoiBGV8S9Z7c +DghUvMt6b0pZ0ildRCHfTUz7eG3g4MhfbjupR7b+L9FWEJhcd/H0dxpw7SKYha/n +ePuRL7MXmbW8WLMqO/ImxzL8TPOB3pUg3nITfubV6gpPBmn+0nwbqYUmggJuwgvK +I2GpN2Ny6EErZy17EEgyhJygJZMj+UzQjC781xxsl3ljpYEqqwgRLIZBSBUD5dXj +XBuU24w162SeSyHZzkBbuv6lr52pqoZyFrG29DCHECgO9ZmNWgSpiWSkh+vExAG7 +wNs0y61t2HUG+BCMGPQ9sOzouyTfrnLVAWwzswGftFYQfoIBeJIwggb7MIIE46AD +AgECAgMQAAIwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UEBhMCVVMxHDAaBgNVBAoT +E01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1 +Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNhLXByb2R1Y3Rp +b24tYW1vMB4XDTE1MDMxNzIzNTI0MloXDTI1MDMxNDIzNTI0MlowgcUxCzAJBgNV +BAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZN +b3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8GA1UEAxMo +cHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0MDIGCSqG +SIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxhLmNvbTCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMLMM9m2HBLhCiO9mhljpehT +hxpzlCnxluzDZ51I/H7MvBbIvZBm9zSpHdffubSsak2qYE69d+ebTa/CK83WIosM +24/2Qp7n/GGaPJcCC4Y3JkrCsgA8+wV2MbFlKSv+qMdvI/sE3BPYDMCjVPMhHmIP +XaPWd42OoHpI8R3GGUtVnR3Hm76pa2+v6TwgeMiO8om+ogGufiyv6FNMZ5NuY1Z9 +aLNEvehnAzSfddQyki+6FJd7XkgZbP7pb1Kl8yYgiy4piBerJ9H09uPehffE3Ell +3cApQL3+0kjaUX4scMjuNQDMKziRZkYgJAM+qA9WA5Jn77AjerQBWQeEev1PWHYh +0IDlgS/a0bjKmVjNZYG6adrY/R5/whzWGFCIE1UfhPm6PdN0557qvF838C2RFHsI +KzV6KQf0chMjpa02tPaIctjVhnDQZZNKm2ZfLOt9kQ57Is/e6KxH7pYMit46+s99 +lYM7ZquvWbK19b1Ili/6S1BxSzd3wztgfN5jGsc+jCCYLm+AcVtfNKc8cFZHXKrB +CwhGmdbWDSBCicZNA7FKJpO3oIx26VPF2XUldA/T5Mh/POGLilK3t9m9qbjEyDp1 +EwoBToOR/aMrdnNYvSWp0g/GHMzSfJjjXyAqrZY2itam/IJd8r9FoRAzevPt/zTX +BET3INoiCDGRH0XrxUYtAgMGVTejggE5MIIBNTAMBgNVHRMEBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdHxf +FKXipZLjs20GqIUdNkQXH4gwgagGA1UdIwSBoDCBnYAUs7zqWHSr4W54KrKrnCMe +qGMsl7ehgYGkfzB9MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jw +b3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5n +IFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW+CAQEwMwYJ +YIZIAYb4QgEEBCYWJGh0dHA6Ly9hZGRvbnMubW96aWxsYS5vcmcvY2EvY3JsLnBl +bTANBgkqhkiG9w0BAQwFAAOCAgEArde/fdjb7TE0eH7Ij7xU4JbcSyhY3cQhVYCw +Fg+Q/2pj+NAfazcjUuLWA0Y/YZs9HOx6j+ZAqO4C/xfMP4RDs9IypxvzHDU6SXgD +RK6uOKtS07HXLcXgFUBvJEQhbT/h5+IQOA4/GcpCshfD6iyiBBi+IocR+tnKPCuZ +T3m1t60Eja/MkPKG/Gx8vSodHvlTTsJ2GzjUEANveCZOnlAdp9fjTvFZny9qqnbg +sfVbuTqKndbCFW5QLXfkna6jBqMrY0+CpMYY2oJ5gwpHbE/7hhukjxGCTcpv7r/O +M53bb/DZnybDlLLepacljvz7DBA1O1FFtEhf9MR+vyvmBpniAyKQhqG2hsVGurE1 +nBcE+oteZWar2lMp6+etDAb9DRC+jZv0aEQs2o/qQwyD8AGquLgBsJq5Jz3gGxzn +4r3vGu2lV8VdzIm0C8sOFSWTmTZxQmJbF8xSsQBojnsvEah4DPER+eAt6qKolaWe +s4drJQjzFyC7HJn2VqalpCwbe9CdMB7eRqzeP6GujJBi80/gx0pAysUtuKKpH5IJ +WbXAOszfrjb3CaHafYZDnwPoOfj74ogFzjt2f54jwnU+ET/byfjZ7J8SLH316C1V +HrvFXcTzyMV4aRluVPjPg9x1G58hMIbeuT4GpwQUNdJ9uL8t65v0XwG2t6Y7jpRO +sFVxBtgxggNXMIIDUwIBATCB0DCBxTELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE01v +emlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1Y3Rp +b24gU2lnbmluZyBTZXJ2aWNlMTEwLwYDVQQDEyhwcm9kdWN0aW9uLXNpZ25pbmct +Y2EuYWRkb25zLm1vemlsbGEub3JnMTQwMgYJKoZIhvcNAQkBFiVzZXJ2aWNlcy1v +cHMrYWRkb25zaWduaW5nQG1vemlsbGEuY29tAgYBVpobWVwwCQYFKw4DAhoFAKBd +MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE2MDgx +NzIwMDQ1OFowIwYJKoZIhvcNAQkEMRYEFAxlGvNFSx+Jqj70haE8b7UZk+2GMA0G +CSqGSIb3DQEBAQUABIICADsDlrucYRgwq9o2QSsO6X6cRa5Zu6w+1n07PTIyc1zn +Pi1cgkkWZ0kZBHDrJ5CY33yRQPl6I1tHXaq7SkOSdOppKhpUmBiKZxQRAZR21QHk +R3v1XS+st/o0N+0btv3YoplUifLIwtH89oolxqlStChELu7FuOBretdhx/z12ytA +EhIIS53o/XjDL7XKJbQA02vzOtOC/Eq6p8BI7F3y6pvtmJIRkeGv+u6ssJa6g5q8 +74w8hHXaH94Z9+hDPqjNWlsXJHgPdAKiEjzDz9oLkvDyX4Pd8JMK5ILskirpG+hj +Q8jkTc5oYwyuSlBAUTGxW6ZbuOrtfVZvOVtRL/ixuiFiVlJ+JOQOxrtK19ukamsI +iacFlbLgiA7w0HCtm2DsT9aL67/1e4rJ0lv0MjnQYUMmKQy7g0Gd3+nQPU9pn+Lf +Z/UmSNWiJ8Csc/seDMyzT6jrzcGPfoSVaUowH0wGrI9If1snwcr+mMg7dWRGf1fm +y/dcVSzed0ax4LqDmike1EshU+51cKWWlnhyNHK4KH+0fNsBQ0c6clrFpGx9MPmV +YXie6C+LWkh5x12RU0sJt/SmSZV6q9VliIkX+yY3jBrC/pKgRahtcIyq46Da1E6K +lc15Euur3NfGow+nott0Z8XutpYdK/2vBKcIh9JOdkd+oe6pcIP6hnhHRp53wqmG +-----END PKCS7-----` + +var FirefoxAddonRootCert = []byte(` +-----BEGIN CERTIFICATE----- +MIIGYTCCBEmgAwIBAgIBATANBgkqhkiG9w0BAQwFADB9MQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBB +TU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2Et +cHJvZHVjdGlvbi1hbW8wHhcNMTUwMzE3MjI1MzU3WhcNMjUwMzE0MjI1MzU3WjB9 +MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0G +A1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAd +BgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW8wggIgMA0GCSqGSIb3DQEBAQUA +A4ICDQAwggIIAoICAQC0u2HXXbrwy36+MPeKf5jgoASMfMNz7mJWBecJgvlTf4hH +JbLzMPsIUauzI9GEpLfHdZ6wzSyFOb4AM+D1mxAWhuZJ3MDAJOf3B1Rs6QorHrl8 +qqlNtPGqepnpNJcLo7JsSqqE3NUm72MgqIHRgTRsqUs+7LIPGe7262U+N/T0LPYV +Le4rZ2RDHoaZhYY7a9+49mHOI/g2YFB+9yZjE+XdplT2kBgA4P8db7i7I0tIi4b0 +B0N6y9MhL+CRZJyxdFe2wBykJX14LsheKsM1azHjZO56SKNrW8VAJTLkpRxCmsiT +r08fnPyDKmaeZ0BtsugicdipcZpXriIGmsZbI12q5yuwjSELdkDV6Uajo2n+2ws5 +uXrP342X71WiWhC/dF5dz1LKtjBdmUkxaQMOP/uhtXEKBrZo1ounDRQx1j7+SkQ4 +BEwjB3SEtr7XDWGOcOIkoJZWPACfBLC3PJCBWjTAyBlud0C5n3Cy9regAAnOIqI1 +t16GU2laRh7elJ7gPRNgQgwLXeZcFxw6wvyiEcmCjOEQ6PM8UQjthOsKlszMhlKw +vjyOGDoztkqSBy/v+Asx7OW2Q7rlVfKarL0mREZdSMfoy3zTgtMVCM0vhNl6zcvf +5HNNopoEdg5yuXo2chZ1p1J+q86b0G5yJRMeT2+iOVY2EQ37tHrqUURncCy4uwIB +A6OB7TCB6jAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAWBgNVHSUBAf8E +DDAKBggrBgEFBQcDAzCBkgYDVR0jBIGKMIGHoYGBpH8wfTELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEg +QU1PIFByb2R1Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNh +LXByb2R1Y3Rpb24tYW1vggEBMB0GA1UdDgQWBBSzvOpYdKvhbngqsqucIx6oYyyX +tzANBgkqhkiG9w0BAQwFAAOCAgEAaNSRYAaECAePQFyfk12kl8UPLh8hBNidP2H6 +KT6O0vCVBjxmMrwr8Aqz6NL+TgdPmGRPDDLPDpDJTdWzdj7khAjxqWYhutACTew5 +eWEaAzyErbKQl+duKvtThhV2p6F6YHJ2vutu4KIciOMKB8dslIqIQr90IX2Usljq +8Ttdyf+GhUmazqLtoB0GOuESEqT4unX6X7vSGu1oLV20t7t5eCnMMYD67ZBn0YIU +/cm/+pan66hHrja+NeDGF8wabJxdqKItCS3p3GN1zUGuJKrLykxqbOp/21byAGog +Z1amhz6NHUcfE6jki7sM7LHjPostU5ZWs3PEfVVgha9fZUhOrIDsyXEpCWVa3481 +LlAq3GiUMKZ5DVRh9/Nvm4NwrTfB3QkQQJCwfXvO9pwnPKtISYkZUqhEqvXk5nBg +QCkDSLDjXTx39naBBGIVIqBtKKuVTla9enngdq692xX/CgO6QJVrwpqdGjebj5P8 +5fNZPABzTezG3Uls5Vp+4iIWVAEDkK23cUj3c/HhE+Oo7kxfUeu5Y1ZV3qr61+6t +ZARKjbu1TuYQHf0fs+GwID8zeLc2zJL7UzcHFwwQ6Nda9OJN4uPAuC/BKaIpxCLL +26b24/tRam4SJjqpiq20lynhUrmTtt6hbG3E1Hpy3bmkt2DYnuMFwEx2gfXNcnbT +wNuvFqc= +-----END CERTIFICATE-----`) + +// sign a document with openssl and verify the signature with pkcs7. +// this uses a chain of root, intermediate and signer cert, where the +// intermediate is added to the certs but the root isn't. +func TestSignWithOpenSSLAndVerify(t *testing.T) { + content := []byte(` +A ship in port is safe, +but that's not what ships are built for. +-- Grace Hopper`) + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_content") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + sigalgs := []x509.SignatureAlgorithm{ + x509.SHA1WithRSA, + x509.SHA256WithRSA, + x509.SHA512WithRSA, + x509.ECDSAWithSHA1, + x509.ECDSAWithSHA256, + x509.ECDSAWithSHA384, + x509.ECDSAWithSHA512, + } + for _, sigalgroot := range sigalgs { + rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) + if err != nil { + t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) + } + truststore := x509.NewCertPool() + truststore.AddCert(rootCert.Certificate) + for _, sigalginter := range sigalgs { + interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) + if err != nil { + t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) + } + // write the intermediate cert to a temp file + tmpInterCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_intermediate") + if err != nil { + t.Fatal(err) + } + fd, err := os.OpenFile(tmpInterCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatal(err) + } + pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: interCert.Certificate.Raw}) + fd.Close() + for _, sigalgsigner := range sigalgs { + signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + + // write the signer cert to a temp file + tmpSignerCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signer") + if err != nil { + t.Fatal(err) + } + fd, err = os.OpenFile(tmpSignerCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatal(err) + } + pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Certificate.Raw}) + fd.Close() + + // write the signer key to a temp file + tmpSignerKeyFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_key") + if err != nil { + t.Fatal(err) + } + fd, err = os.OpenFile(tmpSignerKeyFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatal(err) + } + var derKey []byte + priv := *signerCert.PrivateKey + switch priv := priv.(type) { + case *rsa.PrivateKey: + derKey = x509.MarshalPKCS1PrivateKey(priv) + pem.Encode(fd, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derKey}) + case *ecdsa.PrivateKey: + derKey, err = x509.MarshalECPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + pem.Encode(fd, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derKey}) + } + fd.Close() + + // write the root cert to a temp file + tmpSignedFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signature") + if err != nil { + t.Fatal(err) + } + // call openssl to sign the content + opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", + "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), + "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), + "-certfile", tmpInterCertFile.Name(), "-outform", "PEM") + out, err := opensslCMD.CombinedOutput() + if err != nil { + t.Fatalf("test %s/%s/%s: openssl command failed with %s: %s", sigalgroot, sigalginter, sigalgsigner, err, out) + } + + // verify the signed content + pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) + if err != nil { + t.Fatal(err) + } + derBlock, _ := pem.Decode(pemSignature) + if derBlock == nil { + break + } + p7, err := Parse(derBlock.Bytes) + if err != nil { + t.Fatalf("Parse encountered unexpected error: %v", err) + } + if err := p7.VerifyWithChain(truststore); err != nil { + t.Fatalf("Verify failed with error: %v", err) + } + // Verify the certificate chain to make sure the identified root + // is the one we expect + ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) + if ee == nil { + t.Fatalf("No end-entity certificate found for signer") + } + chains, err := verifyCertChain(ee, p7.Certificates, truststore, time.Now()) + if err != nil { + t.Fatal(err) + } + if len(chains) != 1 { + t.Fatalf("Expected to find one chain, but found %d", len(chains)) + } + if len(chains[0]) != 3 { + t.Fatalf("Expected to find three certificates in chain, but found %d", len(chains[0])) + } + if chains[0][0].Subject.CommonName != "PKCS7 Test Signer Cert" { + t.Fatalf("Expected to find EE certificate with subject 'PKCS7 Test Signer Cert', but found '%s'", chains[0][0].Subject.CommonName) + } + if chains[0][1].Subject.CommonName != "PKCS7 Test Intermediate Cert" { + t.Fatalf("Expected to find intermediate certificate with subject 'PKCS7 Test Intermediate Cert', but found '%s'", chains[0][1].Subject.CommonName) + } + if chains[0][2].Subject.CommonName != "PKCS7 Test Root CA" { + t.Fatalf("Expected to find root certificate with subject 'PKCS7 Test Root CA', but found '%s'", chains[0][2].Subject.CommonName) + } + os.Remove(tmpSignerCertFile.Name()) // clean up + os.Remove(tmpSignerKeyFile.Name()) // clean up + } + os.Remove(tmpInterCertFile.Name()) // clean up + } + } + os.Remove(tmpContentFile.Name()) // clean up +} + +func TestDSASignWithOpenSSLAndVerify(t *testing.T) { + content := []byte(` +A ship in port is safe, +but that's not what ships are built for. +-- Grace Hopper`) + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + + // write the signer cert to a temp file + tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) + + // write the signer key to a temp file + tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755) + + tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature") + if err != nil { + t.Fatal(err) + } + // call openssl to sign the content + opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1", + "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), + "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), + "-certfile", tmpSignerCertFile.Name(), "-outform", "PEM") + out, err := opensslCMD.CombinedOutput() + if err != nil { + t.Fatalf("openssl command failed with %s: %s", err, out) + } + + // verify the signed content + pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) + if err != nil { + t.Fatal(err) + } + fmt.Printf("%s\n", pemSignature) + derBlock, _ := pem.Decode(pemSignature) + if derBlock == nil { + t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name()) + } + p7, err := Parse(derBlock.Bytes) + if err != nil { + t.Fatalf("Parse encountered unexpected error: %v", err) + } + if err := p7.Verify(); err != nil { + t.Fatalf("Verify failed with error: %v", err) + } + os.Remove(tmpSignerCertFile.Name()) // clean up + os.Remove(tmpSignerKeyFile.Name()) // clean up + os.Remove(tmpContentFile.Name()) // clean up +} + +var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY----- +MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS +PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl +pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith +1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L +vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3 +zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo +g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE= +-----END PRIVATE KEY-----`) + +var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE----- +MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV +bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD +VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du +MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r +bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE +ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC +AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD +Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE +exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii +Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4 +V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI +puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl +nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp +rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt +1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT +ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G +CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688 +qzy/7yePTlhlpj+ahMM= +-----END CERTIFICATE-----`) From 2a249d20deb77a6aea307fd0f8457a92cae005e9 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Feb 2021 00:32:21 +0100 Subject: [PATCH 030/291] Refactor initialization of SCEP authority --- authority/authority.go | 45 ++++++ authority/provisioner/scep.go | 4 +- cas/softcas/softcas_test.go | 4 + go.mod | 2 +- go.sum | 26 ++++ kms/apiv1/options.go | 1 + kms/apiv1/requests.go | 11 ++ kms/awskms/awskms.go | 6 + kms/cloudkms/cloudkms.go | 6 + kms/pkcs11/pkcs11.go | 5 + kms/softkms/softkms.go | 36 +++++ kms/sshagentkms/sshagentkms.go | 6 + kms/yubikey/yubikey.go | 6 + scep/api/api.go | 43 +++-- scep/authority.go | 277 ++++++++++++++++++++++++++++----- scep/scep.go | 54 +++++++ scep/service.go | 9 ++ 17 files changed, 476 insertions(+), 65 deletions(-) create mode 100644 scep/scep.go create mode 100644 scep/service.go diff --git a/authority/authority.go b/authority/authority.go index 5652c71a..e753aeae 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -12,6 +12,7 @@ import ( "github.com/smallstep/certificates/cas" "github.com/smallstep/certificates/linkedca" + "github.com/smallstep/certificates/scep" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" @@ -46,6 +47,9 @@ type Authority struct { federatedX509Certs []*x509.Certificate certificates *sync.Map + // SCEP CA + scepService *scep.Service + // SSH CA sshCAUserCertSignKey ssh.Signer sshCAHostCertSignKey ssh.Signer @@ -309,6 +313,38 @@ func (a *Authority) init() error { } } + // TODO: decide if this is a good approach for providing the SCEP functionality + if a.scepService == nil { + var options casapi.Options + if a.config.AuthorityConfig.Options != nil { + options = *a.config.AuthorityConfig.Options + } + + // Read intermediate and create X509 signer for default CAS. + if options.Is(casapi.SoftCAS) { + options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) + if err != nil { + return err + } + options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } + options.Decrypter, err = a.keyManager.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + } + + a.scepService = &scep.Service{ + Signer: options.Signer, + Decrypter: options.Decrypter, + } + } + // Read root certificates and store them in the certificates map. if len(a.rootX509Certs) == 0 { a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root)) @@ -529,3 +565,12 @@ func (a *Authority) CloseForReload() { log.Printf("error closing the key manager: %v", err) } } + +// GetSCEPService returns the configured SCEP Service +// TODO: this function is intended to exist temporarily +// in order to make SCEP work more easily. It can be +// made more correct by using the right interfaces/abstractions +// after it works as expected. +func (a *Authority) GetSCEPService() scep.Service { + return *a.scepService +} diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 30b4a1b2..6cdfa69f 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -55,7 +55,7 @@ func (s *SCEP) DefaultTLSCertDuration() time.Duration { return s.claimer.DefaultTLSCertDuration() } -// Init initializes and validates the fields of a JWK type. +// Init initializes and validates the fields of a SCEP type. func (s *SCEP) Init(config Config) (err error) { switch { @@ -70,6 +70,8 @@ func (s *SCEP) Init(config Config) (err error) { return err } + // TODO: add other, SCEP specific, options? + return err } diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index 0a50a990..092a0337 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -110,6 +110,10 @@ func (m *mockKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (crypto.S return signer, m.errCreatesigner } +func (m *mockKeyManager) CreateDecrypter(req *kmsapi.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, nil +} + func (m *mockKeyManager) Close() error { return m.errClose } diff --git a/go.mod b/go.mod index fe0c7f87..ea867f8f 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/google/uuid v1.1.2 github.com/googleapis/gax-go/v2 v2.0.5 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/micromdm/scep v1.0.0 + github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 github.com/newrelic/go-agent v2.15.0+incompatible github.com/pkg/errors v0.9.1 github.com/rs/xid v1.2.1 diff --git a/go.sum b/go.sum index 77e0b0cb..934bcfb6 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -116,10 +117,16 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.4.0 h1:KeVK+Emj3c3S4eRztFuzbFYb2BAgf2jmwDwyXEri7Lo= +github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-piv/piv-go v1.7.0 h1:rfjdFdASfGV5KLJhSjgpGJ5lzVZVtRWn8ovy/H9HQ/U= github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8HKy7Gk= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= +github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -185,6 +192,9 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -211,6 +221,12 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o 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= +<<<<<<< HEAD +======= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +>>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -232,6 +248,11 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +<<<<<<< HEAD +======= +github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 h1:QACkVsQ7Qx4PuPDFL2OFD5u4OnYT0TkReWk9IVqaGHA= +github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23/go.mod h1:a82oNZyGV8jj9Do7dh8EkA90+esBls0CZHR6B85Qda8= +>>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) 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/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY= @@ -248,6 +269,7 @@ github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34J github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -314,6 +336,8 @@ github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -371,6 +395,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -417,6 +442,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 705f3633..0e6f32df 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -13,6 +13,7 @@ type KeyManager interface { GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error) CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error) CreateSigner(req *CreateSignerRequest) (crypto.Signer, error) + CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) // TODO: split into separate interface? Close() error } diff --git a/kms/apiv1/requests.go b/kms/apiv1/requests.go index e58c4546..31097040 100644 --- a/kms/apiv1/requests.go +++ b/kms/apiv1/requests.go @@ -133,6 +133,17 @@ type CreateSignerRequest struct { Password []byte } +// CreateDecrypterRequest is the parameter used in the kms.Decrypt method. +type CreateDecrypterRequest struct { + Decrypter crypto.Decrypter + DecryptionKey string + DecryptionKeyPEM []byte + TokenLabel string + PublicKey string + PublicKeyPEM []byte + Password []byte +} + // LoadCertificateRequest is the parameter used in the LoadCertificate method of // a CertificateManager. type LoadCertificateRequest struct { diff --git a/kms/awskms/awskms.go b/kms/awskms/awskms.go index da392989..00d7d0b3 100644 --- a/kms/awskms/awskms.go +++ b/kms/awskms/awskms.go @@ -3,6 +3,7 @@ package awskms import ( "context" "crypto" + "fmt" "net/url" "strings" "time" @@ -221,6 +222,11 @@ func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error return NewSigner(k.service, req.SigningKey) } +// CreateDecrypter creates a new crypto.decrypter backed by AWS KMS +func (k *KMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, fmt.Errorf("not implemented yet") +} + // Close closes the connection of the KMS client. func (k *KMS) Close() error { return nil diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index cfbf8235..ace12a1a 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -3,6 +3,7 @@ package cloudkms import ( "context" "crypto" + "fmt" "log" "strings" "time" @@ -284,6 +285,11 @@ func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe return pk, nil } +// CreateDecrypter creates a new crypto.Decrypter backed by Google Cloud KMS +func (k *CloudKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, fmt.Errorf("not implemented yet") +} + // getPublicKeyWithRetries retries the request if the error is // FailedPrecondition, caused because the key is in the PENDING_GENERATION // status. diff --git a/kms/pkcs11/pkcs11.go b/kms/pkcs11/pkcs11.go index 47c298a5..8c1497e8 100644 --- a/kms/pkcs11/pkcs11.go +++ b/kms/pkcs11/pkcs11.go @@ -352,3 +352,8 @@ func findCertificate(ctx P11, rawuri string) (*x509.Certificate, error) { } return cert, nil } + +// CreateDecrypter creates a new crypto.Decrypter backed by PKCS11 +func (k *PKCS11) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, fmt.Errorf("not implemented yet") +} diff --git a/kms/softkms/softkms.go b/kms/softkms/softkms.go index 23b50849..a2f43c31 100644 --- a/kms/softkms/softkms.go +++ b/kms/softkms/softkms.go @@ -145,3 +145,39 @@ func (k *SoftKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey return nil, errors.Errorf("unsupported public key type %T", v) } } + +// CreateDecrypter creates a new crypto.Decrypter backed by disk/software +func (k *SoftKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + + var opts []pemutil.Options + if req.Password != nil { + opts = append(opts, pemutil.WithPassword(req.Password)) + } + + switch { + case req.Decrypter != nil: + return req.Decrypter, nil + case len(req.DecryptionKeyPEM) != 0: + v, err := pemutil.ParseKey(req.DecryptionKeyPEM, opts...) + if err != nil { + return nil, err + } + decrypter, ok := v.(crypto.Decrypter) + if !ok { + return nil, errors.New("decryptorKeyPEM is not a crypto.Decrypter") + } + return decrypter, nil + case req.DecryptionKey != "": + v, err := pemutil.Read(req.DecryptionKey, opts...) + if err != nil { + return nil, err + } + decrypter, ok := v.(crypto.Decrypter) + if !ok { + return nil, errors.New("decryptionKey is not a crypto.Decrypter") + } + return decrypter, nil + default: + return nil, errors.New("failed to load softKMS: please define decryptionKeyPEM or decryptionKey") + } +} diff --git a/kms/sshagentkms/sshagentkms.go b/kms/sshagentkms/sshagentkms.go index b3627a08..9c8a7866 100644 --- a/kms/sshagentkms/sshagentkms.go +++ b/kms/sshagentkms/sshagentkms.go @@ -7,6 +7,7 @@ import ( "crypto/ed25519" "crypto/rsa" "crypto/x509" + "fmt" "io" "net" "os" @@ -204,3 +205,8 @@ func (k *SSHAgentKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.Publi return nil, errors.Errorf("unsupported public key type %T", v) } } + +// CreateDecrypter creates a crypto.Decrypter backed by ssh-agent +func (k *SSHAgentKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, fmt.Errorf("not implemented yet") +} diff --git a/kms/yubikey/yubikey.go b/kms/yubikey/yubikey.go index 2dde244a..b78ff5e5 100644 --- a/kms/yubikey/yubikey.go +++ b/kms/yubikey/yubikey.go @@ -7,6 +7,7 @@ import ( "crypto" "crypto/x509" "encoding/hex" + "fmt" "net/url" "strings" @@ -189,6 +190,11 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e return signer, nil } +// CreateDecrypter creates a new crypto.Decrypter backed by a YubiKey +func (k *YubiKey) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { + return nil, fmt.Errorf("not implemented yet") +} + // Close releases the connection to the YubiKey. func (k *YubiKey) Close() error { return errors.Wrap(k.yk.Close(), "error closing yubikey") diff --git a/scep/api/api.go b/scep/api/api.go index 5a4cb5b2..ec959519 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -28,6 +28,8 @@ const ( opnGetCACert = "GetCACert" opnGetCACaps = "GetCACaps" opnPKIOperation = "PKIOperation" + + // TODO: add other (more optional) operations and handling ) // SCEPRequest is a SCEP server request. @@ -98,6 +100,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { } func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { + scepRequest, err := decodeSCEPRequest(r) if err != nil { fmt.Println(err) @@ -252,27 +255,22 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque return err } - certs, err := h.Auth.GetCACertificates() - if err != nil { - return err - } - - // TODO: instead of getting the key to decrypt, add a decrypt function to the auth; less leaky - key, err := h.Auth.GetSigningKey() - if err != nil { - return err + pkimsg := &scep.PKIMessage{ + TransactionID: msg.TransactionID, + MessageType: msg.MessageType, + SenderNonce: msg.SenderNonce, + Raw: msg.Raw, } - ca := certs[0] - if err := msg.DecryptPKIEnvelope(ca, key); err != nil { + if err := h.Auth.DecryptPKIEnvelope(pkimsg); err != nil { return err } - if msg.MessageType == microscep.PKCSReq { + if pkimsg.MessageType == microscep.PKCSReq { // TODO: CSR validation, like challenge password } - csr := msg.CSRReqMessage.CSR + csr := pkimsg.CSRReqMessage.CSR id, err := createKeyIdentifier(csr.PublicKey) if err != nil { return err @@ -282,6 +280,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque days := 40 + // TODO: use information from provisioner, like claims template := &x509.Certificate{ SerialNumber: serial, Subject: csr.Subject, @@ -296,16 +295,16 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque EmailAddresses: csr.EmailAddresses, } - certRep, err := msg.SignCSR(ca, key, template) + certRep, err := h.Auth.SignCSR(pkimsg, template) if err != nil { return err } - //cert := certRep.CertRepMessage.Certificate - //name := certName(cert) + // //cert := certRep.CertRepMessage.Certificate + // //name := certName(cert) - // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not - // TODO: store the new cert for CN locally; should go into the DB + // // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not + // // TODO: store the new cert for CN locally; should go into the DB scepResponse.Data = certRep.Raw @@ -321,7 +320,7 @@ func certName(cert *x509.Certificate) string { return string(cert.Signature) } -// createKeyIdentifier create an identifier for public keys +// createKeyIdentifier creates an identifier for public keys // according to the first method in RFC5280 section 4.2.1.2. func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { @@ -390,9 +389,9 @@ func ProvisionerFromContext(ctx context.Context) (scep.Provisioner, error) { if val == nil { return nil, errors.New("provisioner expected in request context") } - pval, ok := val.(scep.Provisioner) - if !ok || pval == nil { + p, ok := val.(scep.Provisioner) + if !ok || p == nil { return nil, errors.New("provisioner in context is not a SCEP provisioner") } - return pval, nil + return p, nil } diff --git a/scep/authority.go b/scep/authority.go index 27817b0f..244a6f92 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -1,17 +1,26 @@ package scep import ( - "crypto/rsa" + "bytes" "crypto/x509" - "encoding/pem" "errors" - "io/ioutil" + "fmt" + "math/big" + "math/rand" "github.com/smallstep/certificates/authority/provisioner" database "github.com/smallstep/certificates/db" + "go.step.sm/crypto/pemutil" "github.com/smallstep/nosql" + + microx509util "github.com/micromdm/scep/crypto/x509util" + microscep "github.com/micromdm/scep/scep" + + "github.com/smallstep/certificates/scep/pkcs7" + + "go.step.sm/crypto/x509util" ) // Interface is the SCEP authority interface. @@ -41,7 +50,10 @@ type Interface interface { // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetCACertificates() ([]*x509.Certificate, error) - GetSigningKey() (*rsa.PrivateKey, error) + //GetSigningKey() (*rsa.PrivateKey, error) + + DecryptPKIEnvelope(*PKIMessage) error + SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) } // Authority is the layer that handles all SCEP interactions. @@ -54,17 +66,16 @@ type Authority struct { // dir *directory intermediateCertificate *x509.Certificate - intermediateKey *rsa.PrivateKey - - //signer crypto.Signer + service Service signAuth SignAuthority } // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { IntermediateCertificatePath string - IntermediateKeyPath string + + Service Service // Backdate Backdate provisioner.Duration @@ -79,7 +90,7 @@ type AuthorityOptions struct { Prefix string } -// SignAuthority is the interface implemented by a CA authority. +// SignAuthority is the interface for a signing authority type SignAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) LoadProvisionerByID(string) (provisioner.Interface, error) @@ -87,6 +98,7 @@ type SignAuthority interface { // New returns a new Authority that implements the SCEP interface. func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { + if _, ok := ops.DB.(*database.SimpleDB); !ok { // TODO: see ACME implementation } @@ -100,68 +112,251 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { return nil, err } - intermediateKey, err := readPrivateKey(ops.IntermediateKeyPath) - if err != nil { - return nil, err - } - return &Authority{ backdate: ops.Backdate, db: ops.DB, prefix: ops.Prefix, dns: ops.DNS, intermediateCertificate: certificateChain[0], - intermediateKey: intermediateKey, + service: ops.Service, signAuth: signAuth, }, nil } -func readPrivateKey(path string) (*rsa.PrivateKey, error) { +// LoadProvisionerByID calls out to the SignAuthority interface to load a +// provisioner by ID. +func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { + return a.signAuth.LoadProvisionerByID(id) +} + +// GetCACertificates returns the certificate (chain) for the CA +func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { + + // TODO: this should return: the "SCEP Server (RA)" certificate, the issuing CA up to and excl. the root + // Some clients do need the root certificate however; also see: https://github.com/openxpki/openxpki/issues/73 + // + // This means we might need to think about if we should use the current intermediate CA + // certificate as the "SCEP Server (RA)" certificate. It might be better to have a distinct + // RA certificate, with a corresponding rsa.PrivateKey, just for SCEP usage, which is signed by + // the intermediate CA. Will need to look how we can provide this nicely within step-ca. + // + // This might also mean that we might want to use a distinct instance of KMS for doing the key operations, + // so that we can use RSA just for SCEP. + // + // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in + // https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now. + + if a.intermediateCertificate == nil { + return nil, errors.New("no intermediate certificate available in SCEP authority") + } + + return []*x509.Certificate{a.intermediateCertificate}, nil +} + +// DecryptPKIEnvelope decrypts an enveloped message +func (a *Authority) DecryptPKIEnvelope(msg *PKIMessage) error { + + data := msg.Raw + + p7, err := pkcs7.Parse(data) + if err != nil { + return err + } + + var tID microscep.TransactionID + if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil { + return err + } + + var msgType microscep.MessageType + if err := p7.UnmarshalSignedAttribute(oidSCEPmessageType, &msgType); err != nil { + return err + } + + msg.p7 = p7 + + p7c, err := pkcs7.Parse(p7.Content) + if err != nil { + return err + } + + envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter) + if err != nil { + return err + } + + msg.pkiEnvelope = envelope + + switch msg.MessageType { + case microscep.CertRep: + certs, err := microscep.CACerts(msg.pkiEnvelope) + if err != nil { + return err + } + msg.CertRepMessage.Certificate = certs[0] // TODO: check correctness of this + return nil + case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq: + csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope) + if err != nil { + return fmt.Errorf("parse CSR from pkiEnvelope") + } + // check for challengePassword + cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) + if err != nil { + return fmt.Errorf("scep: parse challenge password in pkiEnvelope") + } + msg.CSRReqMessage = µscep.CSRReqMessage{ + RawDecrypted: msg.pkiEnvelope, + CSR: csr, + ChallengePassword: cp, + } + //msg.Certificate = p7.Certificates[0] // TODO: check if this is necessary to add (again) + return nil + case microscep.GetCRL, microscep.GetCert, microscep.CertPoll: + return fmt.Errorf("not implemented") //errNotImplemented + } + + return nil +} + +// SignCSR creates an x509.Certificate based on a template and Cert Authority credentials +// returns a new PKIMessage with CertRep data +//func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) { +func (a *Authority) SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { + + // check if CSRReqMessage has already been decrypted + if msg.CSRReqMessage.CSR == nil { + if err := a.DecryptPKIEnvelope(msg); err != nil { + return nil, err + } + } - keyBytes, err := ioutil.ReadFile(path) + csr := msg.CSRReqMessage.CSR + + // Template data + data := x509util.NewTemplateData() + data.SetCommonName(csr.Subject.CommonName) + //data.Set(x509util.SANsKey, sans) + + // templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) + // if err != nil { + // return nil, ServerInternalErr(errors.Wrapf(err, "error creating template options from ACME provisioner")) + // } + // signOps = append(signOps, templateOptions) + + // // Create and store a new certificate. + // certChain, err := auth.Sign(csr, provisioner.SignOptions{ + // NotBefore: provisioner.NewTimeDuration(o.NotBefore), + // NotAfter: provisioner.NewTimeDuration(o.NotAfter), + // }, signOps...) + // if err != nil { + // return nil, ServerInternalErr(errors.Wrapf(err, "error generating certificate for order %s", o.ID)) + // } + + // TODO: proper options + signOps := provisioner.SignOptions{} + signOps2 := []provisioner.SignOption{} + + certs, err := a.signAuth.Sign(csr, signOps, signOps2...) if err != nil { return nil, err } - block, _ := pem.Decode([]byte(keyBytes)) - if block == nil { - return nil, nil + cert := certs[0] + + // fmt.Println("CERT") + // fmt.Println(cert) + // fmt.Println(fmt.Sprintf("%T", cert)) + // fmt.Println(cert.Issuer) + // fmt.Println(cert.Subject) + + serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? + cert.SerialNumber = serial + + // create a degenerate cert structure + deg, err := DegenerateCertificates([]*x509.Certificate{cert}) + if err != nil { + return nil, err } - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + e7, err := pkcs7.Encrypt(deg, msg.p7.Certificates) if err != nil { return nil, err } - return key, nil -} + // PKIMessageAttributes to be signed + config := pkcs7.SignerInfoConfig{ + ExtraSignedAttributes: []pkcs7.Attribute{ + { + Type: oidSCEPtransactionID, + Value: msg.TransactionID, + }, + { + Type: oidSCEPpkiStatus, + Value: microscep.SUCCESS, + }, + { + Type: oidSCEPmessageType, + Value: microscep.CertRep, + }, + { + Type: oidSCEPrecipientNonce, + Value: msg.SenderNonce, + }, + }, + } -// LoadProvisionerByID calls out to the SignAuthority interface to load a -// provisioner by ID. -func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { - return a.signAuth.LoadProvisionerByID(id) -} + signedData, err := pkcs7.NewSignedData(e7) + if err != nil { + return nil, err + } -// GetCACertificates returns the certificate (chain) for the CA -func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { + // add the certificate into the signed data type + // this cert must be added before the signedData because the recipient will expect it + // as the first certificate in the array + signedData.AddCertificate(cert) - if a.intermediateCertificate == nil { - return nil, errors.New("no intermediate certificate available in SCEP authority") + authCert := a.intermediateCertificate + + // sign the attributes + if err := signedData.AddSigner(authCert, a.service.Signer, config); err != nil { + return nil, err } - return []*x509.Certificate{a.intermediateCertificate}, nil -} + certRepBytes, err := signedData.Finish() + if err != nil { + return nil, err + } -// GetSigningKey returns the RSA private key for the CA -// TODO: we likely should provide utility functions for decrypting and -// signing instead of providing the signing key directly -func (a *Authority) GetSigningKey() (*rsa.PrivateKey, error) { + cr := &CertRepMessage{ + PKIStatus: microscep.SUCCESS, + RecipientNonce: microscep.RecipientNonce(msg.SenderNonce), + Certificate: cert, + degenerate: deg, + } - if a.intermediateKey == nil { - return nil, errors.New("no intermediate key available in SCEP authority") + // create a CertRep message from the original + crepMsg := &PKIMessage{ + Raw: certRepBytes, + TransactionID: msg.TransactionID, + MessageType: microscep.CertRep, + CertRepMessage: cr, } - return a.intermediateKey, nil + return crepMsg, nil +} + +// DegenerateCertificates creates degenerate certificates pkcs#7 type +func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) { + var buf bytes.Buffer + for _, cert := range certs { + buf.Write(cert.Raw) + } + degenerate, err := pkcs7.DegenerateCertificate(buf.Bytes()) + if err != nil { + return nil, err + } + return degenerate, nil } // Interface guards diff --git a/scep/scep.go b/scep/scep.go new file mode 100644 index 00000000..bc46cce7 --- /dev/null +++ b/scep/scep.go @@ -0,0 +1,54 @@ +package scep + +import ( + "crypto/x509" + "encoding/asn1" + + microscep "github.com/micromdm/scep/scep" + + "github.com/smallstep/certificates/scep/pkcs7" +) + +// SCEP OIDs +var ( + oidSCEPmessageType = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 2} + oidSCEPpkiStatus = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 3} + oidSCEPfailInfo = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 4} + oidSCEPsenderNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 5} + oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6} + oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7} + oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7} +) + +// PKIMessage defines the possible SCEP message types +type PKIMessage struct { + microscep.TransactionID + microscep.MessageType + microscep.SenderNonce + *microscep.CSRReqMessage + + *CertRepMessage + + // DER Encoded PKIMessage + Raw []byte + + // parsed + p7 *pkcs7.PKCS7 + + // decrypted enveloped content + pkiEnvelope []byte + + // Used to sign message + Recipients []*x509.Certificate +} + +// CertRepMessage is a type of PKIMessage +type CertRepMessage struct { + microscep.PKIStatus + microscep.RecipientNonce + microscep.FailInfo + + Certificate *x509.Certificate + + degenerate []byte +} diff --git a/scep/service.go b/scep/service.go new file mode 100644 index 00000000..1d743dd6 --- /dev/null +++ b/scep/service.go @@ -0,0 +1,9 @@ +package scep + +import "crypto" + +// Service is a (temporary?) wrapper for signer/decrypters +type Service struct { + Signer crypto.Signer + Decrypter crypto.Decrypter +} From 80026e10162bb0c61d9847e5492f19c4cf11af66 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Feb 2021 00:55:37 +0100 Subject: [PATCH 031/291] Remove the copy of mozilla/pkcs7 Apparently the existing library works out of the box, after all. We'll have to see how it works out continuing forward. --- scep/authority.go | 4 +- scep/pkcs7/.gitignore | 24 -- scep/pkcs7/.travis.yml | 10 - scep/pkcs7/LICENSE | 22 -- scep/pkcs7/Makefile | 20 -- scep/pkcs7/README.md | 69 ---- scep/pkcs7/ber.go | 251 ------------- scep/pkcs7/ber_test.go | 62 ---- scep/pkcs7/decrypt.go | 177 --------- scep/pkcs7/decrypt_test.go | 60 ---- scep/pkcs7/encrypt.go | 399 --------------------- scep/pkcs7/encrypt_test.go | 102 ------ scep/pkcs7/pkcs7.go | 291 --------------- scep/pkcs7/pkcs7_test.go | 326 ----------------- scep/pkcs7/sign.go | 429 ---------------------- scep/pkcs7/sign_test.go | 266 -------------- scep/pkcs7/verify.go | 264 -------------- scep/pkcs7/verify_test.go | 713 ------------------------------------- scep/scep.go | 4 +- 19 files changed, 6 insertions(+), 3487 deletions(-) delete mode 100644 scep/pkcs7/.gitignore delete mode 100644 scep/pkcs7/.travis.yml delete mode 100644 scep/pkcs7/LICENSE delete mode 100644 scep/pkcs7/Makefile delete mode 100644 scep/pkcs7/README.md delete mode 100644 scep/pkcs7/ber.go delete mode 100644 scep/pkcs7/ber_test.go delete mode 100644 scep/pkcs7/decrypt.go delete mode 100644 scep/pkcs7/decrypt_test.go delete mode 100644 scep/pkcs7/encrypt.go delete mode 100644 scep/pkcs7/encrypt_test.go delete mode 100644 scep/pkcs7/pkcs7.go delete mode 100644 scep/pkcs7/pkcs7_test.go delete mode 100644 scep/pkcs7/sign.go delete mode 100644 scep/pkcs7/sign_test.go delete mode 100644 scep/pkcs7/verify.go delete mode 100644 scep/pkcs7/verify_test.go diff --git a/scep/authority.go b/scep/authority.go index 244a6f92..864ecbba 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -18,7 +18,9 @@ import ( microx509util "github.com/micromdm/scep/crypto/x509util" microscep "github.com/micromdm/scep/scep" - "github.com/smallstep/certificates/scep/pkcs7" + //"github.com/smallstep/certificates/scep/pkcs7" + + "go.mozilla.org/pkcs7" "go.step.sm/crypto/x509util" ) diff --git a/scep/pkcs7/.gitignore b/scep/pkcs7/.gitignore deleted file mode 100644 index daf913b1..00000000 --- a/scep/pkcs7/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/scep/pkcs7/.travis.yml b/scep/pkcs7/.travis.yml deleted file mode 100644 index eac4c176..00000000 --- a/scep/pkcs7/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go -go: - - "1.11" - - "1.12" - - "1.13" - - tip -before_install: - - make gettools -script: - - make diff --git a/scep/pkcs7/LICENSE b/scep/pkcs7/LICENSE deleted file mode 100644 index 75f32090..00000000 --- a/scep/pkcs7/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/scep/pkcs7/Makefile b/scep/pkcs7/Makefile deleted file mode 100644 index 47c73b86..00000000 --- a/scep/pkcs7/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -all: vet staticcheck test - -test: - go test -covermode=count -coverprofile=coverage.out . - -showcoverage: test - go tool cover -html=coverage.out - -vet: - go vet . - -lint: - golint . - -staticcheck: - staticcheck . - -gettools: - go get -u honnef.co/go/tools/... - go get -u golang.org/x/lint/golint diff --git a/scep/pkcs7/README.md b/scep/pkcs7/README.md deleted file mode 100644 index bf37059c..00000000 --- a/scep/pkcs7/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# pkcs7 - -[![GoDoc](https://godoc.org/go.mozilla.org/pkcs7?status.svg)](https://godoc.org/go.mozilla.org/pkcs7) -[![Build Status](https://travis-ci.org/mozilla-services/pkcs7.svg?branch=master)](https://travis-ci.org/mozilla-services/pkcs7) - -pkcs7 implements parsing and creating signed and enveloped messages. - -```go -package main - -import ( - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "os" - - "go.mozilla.org/pkcs7" -) - -func SignAndDetach(content []byte, cert *x509.Certificate, privkey *rsa.PrivateKey) (signed []byte, err error) { - toBeSigned, err := NewSignedData(content) - if err != nil { - err = fmt.Errorf("Cannot initialize signed data: %s", err) - return - } - if err = toBeSigned.AddSigner(cert, privkey, SignerInfoConfig{}); err != nil { - err = fmt.Errorf("Cannot add signer: %s", err) - return - } - - // Detach signature, omit if you want an embedded signature - toBeSigned.Detach() - - signed, err = toBeSigned.Finish() - if err != nil { - err = fmt.Errorf("Cannot finish signing data: %s", err) - return - } - - // Verify the signature - pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) - p7, err := pkcs7.Parse(signed) - if err != nil { - err = fmt.Errorf("Cannot parse our signed data: %s", err) - return - } - - // since the signature was detached, reattach the content here - p7.Content = content - - if bytes.Compare(content, p7.Content) != 0 { - err = fmt.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content) - return - } - if err = p7.Verify(); err != nil { - err = fmt.Errorf("Cannot verify our signed data: %s", err) - return - } - - return signed, nil -} -``` - - - -## Credits -This is a fork of [fullsailor/pkcs7](https://github.com/fullsailor/pkcs7) diff --git a/scep/pkcs7/ber.go b/scep/pkcs7/ber.go deleted file mode 100644 index 58525673..00000000 --- a/scep/pkcs7/ber.go +++ /dev/null @@ -1,251 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "errors" -) - -var encodeIndent = 0 - -type asn1Object interface { - EncodeTo(writer *bytes.Buffer) error -} - -type asn1Structured struct { - tagBytes []byte - content []asn1Object -} - -func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { - //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) - encodeIndent++ - inner := new(bytes.Buffer) - for _, obj := range s.content { - err := obj.EncodeTo(inner) - if err != nil { - return err - } - } - encodeIndent-- - out.Write(s.tagBytes) - encodeLength(out, inner.Len()) - out.Write(inner.Bytes()) - return nil -} - -type asn1Primitive struct { - tagBytes []byte - length int - content []byte -} - -func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error { - _, err := out.Write(p.tagBytes) - if err != nil { - return err - } - if err = encodeLength(out, p.length); err != nil { - return err - } - //fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length) - //fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content)) - out.Write(p.content) - - return nil -} - -func ber2der(ber []byte) ([]byte, error) { - if len(ber) == 0 { - return nil, errors.New("ber2der: input ber is empty") - } - //fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber)) - out := new(bytes.Buffer) - - obj, _, err := readObject(ber, 0) - if err != nil { - return nil, err - } - obj.EncodeTo(out) - - // if offset < len(ber) { - // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber)) - //} - - return out.Bytes(), nil -} - -// encodes lengths that are longer than 127 into string of bytes -func marshalLongLength(out *bytes.Buffer, i int) (err error) { - n := lengthLength(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -// computes the byte length of an encoded length value -func lengthLength(i int) (numBytes int) { - numBytes = 1 - for i > 255 { - numBytes++ - i >>= 8 - } - return -} - -// encodes the length in DER format -// If the length fits in 7 bits, the value is encoded directly. -// -// Otherwise, the number of bytes to encode the length is first determined. -// This number is likely to be 4 or less for a 32bit length. This number is -// added to 0x80. The length is encoded in big endian encoding follow after -// -// Examples: -// length | byte 1 | bytes n -// 0 | 0x00 | - -// 120 | 0x78 | - -// 200 | 0x81 | 0xC8 -// 500 | 0x82 | 0x01 0xF4 -// -func encodeLength(out *bytes.Buffer, length int) (err error) { - if length >= 128 { - l := lengthLength(length) - err = out.WriteByte(0x80 | byte(l)) - if err != nil { - return - } - err = marshalLongLength(out, length) - if err != nil { - return - } - } else { - err = out.WriteByte(byte(length)) - if err != nil { - return - } - } - return -} - -func readObject(ber []byte, offset int) (asn1Object, int, error) { - berLen := len(ber) - if offset >= berLen { - return nil, 0, errors.New("ber2der: offset is after end of ber data") - } - tagStart := offset - b := ber[offset] - offset++ - if offset >= berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - tag := b & 0x1F // last 5 bits - if tag == 0x1F { - tag = 0 - for ber[offset] >= 0x80 { - tag = tag*128 + ber[offset] - 0x80 - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - // jvehent 20170227: this doesn't appear to be used anywhere... - //tag = tag*128 + ber[offset] - 0x80 - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - tagEnd := offset - - kind := b & 0x20 - if kind == 0 { - debugprint("--> Primitive\n") - } else { - debugprint("--> Constructed\n") - } - // read length - var length int - l := ber[offset] - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - hack := 0 - if l > 0x80 { - numberOfBytes := (int)(l & 0x7F) - if numberOfBytes > 4 { // int is only guaranteed to be 32bit - return nil, 0, errors.New("ber2der: BER tag length too long") - } - if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { - return nil, 0, errors.New("ber2der: BER tag length is negative") - } - if (int)(ber[offset]) == 0x0 { - return nil, 0, errors.New("ber2der: BER tag length has leading zero") - } - debugprint("--> (compute length) indicator byte: %x\n", l) - debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) - for i := 0; i < numberOfBytes; i++ { - length = length*256 + (int)(ber[offset]) - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - } else if l == 0x80 { - // find length by searching content - markerIndex := bytes.LastIndex(ber[offset:], []byte{0x0, 0x0}) - if markerIndex == -1 { - return nil, 0, errors.New("ber2der: Invalid BER format") - } - length = markerIndex - hack = 2 - debugprint("--> (compute length) marker found at offset: %d\n", markerIndex+offset) - } else { - length = (int)(l) - } - if length < 0 { - return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length") - } - //fmt.Printf("--> length : %d\n", length) - contentEnd := offset + length - if contentEnd > len(ber) { - return nil, 0, errors.New("ber2der: BER tag length is more than available data") - } - debugprint("--> content start : %d\n", offset) - debugprint("--> content end : %d\n", contentEnd) - debugprint("--> content : % X\n", ber[offset:contentEnd]) - var obj asn1Object - if kind == 0 { - obj = asn1Primitive{ - tagBytes: ber[tagStart:tagEnd], - length: length, - content: ber[offset:contentEnd], - } - } else { - var subObjects []asn1Object - for offset < contentEnd { - var subObj asn1Object - var err error - subObj, offset, err = readObject(ber[:contentEnd], offset) - if err != nil { - return nil, 0, err - } - subObjects = append(subObjects, subObj) - } - obj = asn1Structured{ - tagBytes: ber[tagStart:tagEnd], - content: subObjects, - } - } - - return obj, contentEnd + hack, nil -} - -func debugprint(format string, a ...interface{}) { - //fmt.Printf(format, a) -} diff --git a/scep/pkcs7/ber_test.go b/scep/pkcs7/ber_test.go deleted file mode 100644 index fcb4b6a2..00000000 --- a/scep/pkcs7/ber_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "encoding/asn1" - "strings" - "testing" -) - -func TestBer2Der(t *testing.T) { - // indefinite length fixture - ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00} - expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01} - der, err := ber2der(ber) - if err != nil { - t.Fatalf("ber2der failed with error: %v", err) - } - if !bytes.Equal(der, expected) { - t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) - } - - if der2, err := ber2der(der); err != nil { - t.Errorf("ber2der on DER bytes failed with error: %v", err) - } else { - if !bytes.Equal(der, der2) { - t.Error("ber2der is not idempotent") - } - } - var thing struct { - Number int - } - rest, err := asn1.Unmarshal(der, &thing) - if err != nil { - t.Errorf("Cannot parse resulting DER because: %v", err) - } else if len(rest) > 0 { - t.Errorf("Resulting DER has trailing data: % X", rest) - } -} - -func TestBer2Der_Negatives(t *testing.T) { - fixtures := []struct { - Input []byte - ErrorContains string - }{ - {[]byte{0x30, 0x85}, "tag length too long"}, - {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, - {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, - {[]byte{0x30, 0x80, 0x1, 0x2}, "Invalid BER format"}, - {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, - {[]byte{0x30}, "end of ber data reached"}, - } - - for _, fixture := range fixtures { - _, err := ber2der(fixture.Input) - if err == nil { - t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains) - } - if !strings.Contains(err.Error(), fixture.ErrorContains) { - t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error()) - } - } -} diff --git a/scep/pkcs7/decrypt.go b/scep/pkcs7/decrypt.go deleted file mode 100644 index 0d088d62..00000000 --- a/scep/pkcs7/decrypt.go +++ /dev/null @@ -1,177 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/asn1" - "errors" - "fmt" -) - -// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed -var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") - -// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data -var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") - -// Decrypt decrypts encrypted content info for recipient cert and private key -func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pkey crypto.PrivateKey) ([]byte, error) { - data, ok := p7.raw.(envelopedData) - if !ok { - return nil, ErrNotEncryptedContent - } - recipient := selectRecipientForCertificate(data.RecipientInfos, cert) - if recipient.EncryptedKey == nil { - return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") - } - switch pkey := pkey.(type) { - case *rsa.PrivateKey: - var contentKey []byte - contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey) - if err != nil { - return nil, err - } - return data.EncryptedContentInfo.decrypt(contentKey) - } - return nil, ErrUnsupportedAlgorithm -} - -// DecryptUsingPSK decrypts encrypted data using caller provided -// pre-shared secret -func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { - data, ok := p7.raw.(encryptedData) - if !ok { - return nil, ErrNotEncryptedContent - } - return data.EncryptedContentInfo.decrypt(key) -} - -func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { - alg := eci.ContentEncryptionAlgorithm.Algorithm - if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && - !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && - !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { - fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) - return nil, ErrUnsupportedAlgorithm - } - - // EncryptedContent can either be constructed of multple OCTET STRINGs - // or _be_ a tagged OCTET STRING - var cyphertext []byte - if eci.EncryptedContent.IsCompound { - // Complex case to concat all of the children OCTET STRINGs - var buf bytes.Buffer - cypherbytes := eci.EncryptedContent.Bytes - for { - var part []byte - cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) - buf.Write(part) - if cypherbytes == nil { - break - } - } - cyphertext = buf.Bytes() - } else { - // Simple case, the bytes _are_ the cyphertext - cyphertext = eci.EncryptedContent.Bytes - } - - var block cipher.Block - var err error - - switch { - case alg.Equal(OIDEncryptionAlgorithmDESCBC): - block, err = des.NewCipher(key) - case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): - block, err = des.NewTripleDESCipher(key) - case alg.Equal(OIDEncryptionAlgorithmAES256CBC), alg.Equal(OIDEncryptionAlgorithmAES256GCM): - fallthrough - case alg.Equal(OIDEncryptionAlgorithmAES128GCM), alg.Equal(OIDEncryptionAlgorithmAES128CBC): - block, err = aes.NewCipher(key) - } - - if err != nil { - return nil, err - } - - if alg.Equal(OIDEncryptionAlgorithmAES128GCM) || alg.Equal(OIDEncryptionAlgorithmAES256GCM) { - params := aesGCMParameters{} - paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes - - _, err := asn1.Unmarshal(paramBytes, ¶ms) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(params.Nonce) != gcm.NonceSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - if params.ICVLen != gcm.Overhead() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - - plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil - } - - iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes - if len(iv) != block.BlockSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") - } - mode := cipher.NewCBCDecrypter(block, iv) - plaintext := make([]byte, len(cyphertext)) - mode.CryptBlocks(plaintext, cyphertext) - if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { - return nil, err - } - return plaintext, nil -} - -func unpad(data []byte, blocklen int) ([]byte, error) { - if blocklen < 1 { - return nil, fmt.Errorf("invalid blocklen %d", blocklen) - } - if len(data)%blocklen != 0 || len(data) == 0 { - return nil, fmt.Errorf("invalid data len %d", len(data)) - } - - // the last byte is the length of padding - padlen := int(data[len(data)-1]) - - // check padding integrity, all bytes should be the same - pad := data[len(data)-padlen:] - for _, padbyte := range pad { - if padbyte != byte(padlen) { - return nil, errors.New("invalid padding") - } - } - - return data[:len(data)-padlen], nil -} - -func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { - for _, recp := range recipients { - if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { - return recp - } - } - return recipientInfo{} -} diff --git a/scep/pkcs7/decrypt_test.go b/scep/pkcs7/decrypt_test.go deleted file mode 100644 index 06cc0f80..00000000 --- a/scep/pkcs7/decrypt_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "testing" -) - -func TestDecrypt(t *testing.T) { - fixture := UnmarshalTestFixture(EncryptedTestFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Fatal(err) - } - content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey) - if err != nil { - t.Errorf("Cannot Decrypt with error: %v", err) - } - expected := []byte("This is a test") - if !bytes.Equal(content, expected) { - t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content) - } -} - -// Content is "This is a test" -var EncryptedTestFixture = ` ------BEGIN PKCS7----- -MIIBFwYJKoZIhvcNAQcDoIIBCDCCAQQCAQAxgcowgccCAQAwMjApMRAwDgYDVQQK -EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMAsGCSqGSIb3 -DQEBAQSBgKyP/5WlRTZD3dWMrLOX6QRNDrXEkQjhmToRwFZdY3LgUh25ZU0S/q4G -dHPV21Fv9lQD+q7l3vfeHw8M6Z1PKi9sHMVfxAkQpvaI96DTIT3YHtuLC1w3geCO -8eFWTq2qS4WChSuS/yhYosjA1kTkE0eLnVZcGw0z/WVuEZznkdyIMDIGCSqGSIb3 -DQEHATARBgUrDgMCBwQImpKsUyMPpQigEgQQRcWWrCRXqpD5Njs0GkJl+g== ------END PKCS7----- ------BEGIN CERTIFICATE----- -MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj -bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x -NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT -bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc -LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg -8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP -+Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI -hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18 -sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n -9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy -yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe -KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB -AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu -MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d -H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C -67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv -Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV -i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD -6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA -o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b -dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy -KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA== ------END PRIVATE KEY-----` diff --git a/scep/pkcs7/encrypt.go b/scep/pkcs7/encrypt.go deleted file mode 100644 index da57ae64..00000000 --- a/scep/pkcs7/encrypt.go +++ /dev/null @@ -1,399 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" -) - -type envelopedData struct { - Version int - RecipientInfos []recipientInfo `asn1:"set"` - EncryptedContentInfo encryptedContentInfo -} - -type encryptedData struct { - Version int - EncryptedContentInfo encryptedContentInfo -} - -type recipientInfo struct { - Version int - IssuerAndSerialNumber issuerAndSerial - KeyEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedKey []byte -} - -type encryptedContentInfo struct { - ContentType asn1.ObjectIdentifier - ContentEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedContent asn1.RawValue `asn1:"tag:0,optional,explicit"` -} - -const ( - // EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm - EncryptionAlgorithmDESCBC = iota - - // EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm - // Avoid this algorithm unless required for interoperability; use AES GCM instead. - EncryptionAlgorithmAES128CBC - - // EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm - // Avoid this algorithm unless required for interoperability; use AES GCM instead. - EncryptionAlgorithmAES256CBC - - // EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm - EncryptionAlgorithmAES128GCM - - // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm - EncryptionAlgorithmAES256GCM -) - -// ContentEncryptionAlgorithm determines the algorithm used to encrypt the -// plaintext message. Change the value of this variable to change which -// algorithm is used in the Encrypt() function. -var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC - -// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt -// content with an unsupported algorithm. -var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported") - -// ErrPSKNotProvided is returned when attempting to encrypt -// using a PSK without actually providing the PSK. -var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided") - -const nonceSize = 12 - -type aesGCMParameters struct { - Nonce []byte `asn1:"tag:4"` - ICVLen int -} - -func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { - var keyLen int - var algID asn1.ObjectIdentifier - switch ContentEncryptionAlgorithm { - case EncryptionAlgorithmAES128GCM: - keyLen = 16 - algID = OIDEncryptionAlgorithmAES128GCM - case EncryptionAlgorithmAES256GCM: - keyLen = 32 - algID = OIDEncryptionAlgorithmAES256GCM - default: - return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm) - } - if key == nil { - // Create AES key - key = make([]byte, keyLen) - - _, err := rand.Read(key) - if err != nil { - return nil, nil, err - } - } - - // Create nonce - nonce := make([]byte, nonceSize) - - _, err := rand.Read(nonce) - if err != nil { - return nil, nil, err - } - - // Encrypt content - block, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, nil, err - } - - ciphertext := gcm.Seal(nil, nonce, content, nil) - - // Prepare ASN.1 Encrypted Content Info - paramSeq := aesGCMParameters{ - Nonce: nonce, - ICVLen: gcm.Overhead(), - } - - paramBytes, err := asn1.Marshal(paramSeq) - if err != nil { - return nil, nil, err - } - - eci := encryptedContentInfo{ - ContentType: OIDData, - ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: algID, - Parameters: asn1.RawValue{ - Tag: asn1.TagSequence, - Bytes: paramBytes, - }, - }, - EncryptedContent: marshalEncryptedContent(ciphertext), - } - - return key, &eci, nil -} - -func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { - if key == nil { - // Create DES key - key = make([]byte, 8) - - _, err := rand.Read(key) - if err != nil { - return nil, nil, err - } - } - - // Create CBC IV - iv := make([]byte, des.BlockSize) - _, err := rand.Read(iv) - if err != nil { - return nil, nil, err - } - - // Encrypt padded content - block, err := des.NewCipher(key) - if err != nil { - return nil, nil, err - } - mode := cipher.NewCBCEncrypter(block, iv) - plaintext, err := pad(content, mode.BlockSize()) - if err != nil { - return nil, nil, err - } - cyphertext := make([]byte, len(plaintext)) - mode.CryptBlocks(cyphertext, plaintext) - - // Prepare ASN.1 Encrypted Content Info - eci := encryptedContentInfo{ - ContentType: OIDData, - ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: OIDEncryptionAlgorithmDESCBC, - Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, - }, - EncryptedContent: marshalEncryptedContent(cyphertext), - } - - return key, &eci, nil -} - -func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { - var keyLen int - var algID asn1.ObjectIdentifier - switch ContentEncryptionAlgorithm { - case EncryptionAlgorithmAES128CBC: - keyLen = 16 - algID = OIDEncryptionAlgorithmAES128CBC - case EncryptionAlgorithmAES256CBC: - keyLen = 32 - algID = OIDEncryptionAlgorithmAES256CBC - default: - return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm) - } - - if key == nil { - // Create AES key - key = make([]byte, keyLen) - - _, err := rand.Read(key) - if err != nil { - return nil, nil, err - } - } - - // Create CBC IV - iv := make([]byte, aes.BlockSize) - _, err := rand.Read(iv) - if err != nil { - return nil, nil, err - } - - // Encrypt padded content - block, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - mode := cipher.NewCBCEncrypter(block, iv) - plaintext, err := pad(content, mode.BlockSize()) - if err != nil { - return nil, nil, err - } - cyphertext := make([]byte, len(plaintext)) - mode.CryptBlocks(cyphertext, plaintext) - - // Prepare ASN.1 Encrypted Content Info - eci := encryptedContentInfo{ - ContentType: OIDData, - ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: algID, - Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, - }, - EncryptedContent: marshalEncryptedContent(cyphertext), - } - - return key, &eci, nil -} - -// Encrypt creates and returns an envelope data PKCS7 structure with encrypted -// recipient keys for each recipient public key. -// -// The algorithm used to perform encryption is determined by the current value -// of the global ContentEncryptionAlgorithm package variable. By default, the -// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the -// value before calling Encrypt(). For example: -// -// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM -// -// TODO(fullsailor): Add support for encrypting content with other algorithms -func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { - var eci *encryptedContentInfo - var key []byte - var err error - - // Apply chosen symmetric encryption method - switch ContentEncryptionAlgorithm { - case EncryptionAlgorithmDESCBC: - key, eci, err = encryptDESCBC(content, nil) - case EncryptionAlgorithmAES128CBC: - fallthrough - case EncryptionAlgorithmAES256CBC: - key, eci, err = encryptAESCBC(content, nil) - case EncryptionAlgorithmAES128GCM: - fallthrough - case EncryptionAlgorithmAES256GCM: - key, eci, err = encryptAESGCM(content, nil) - - default: - return nil, ErrUnsupportedEncryptionAlgorithm - } - - if err != nil { - return nil, err - } - - // Prepare each recipient's encrypted cipher key - recipientInfos := make([]recipientInfo, len(recipients)) - for i, recipient := range recipients { - encrypted, err := encryptKey(key, recipient) - if err != nil { - return nil, err - } - ias, err := cert2issuerAndSerial(recipient) - if err != nil { - return nil, err - } - info := recipientInfo{ - Version: 0, - IssuerAndSerialNumber: ias, - KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: OIDEncryptionAlgorithmRSA, - }, - EncryptedKey: encrypted, - } - recipientInfos[i] = info - } - - // Prepare envelope content - envelope := envelopedData{ - EncryptedContentInfo: *eci, - Version: 0, - RecipientInfos: recipientInfos, - } - innerContent, err := asn1.Marshal(envelope) - if err != nil { - return nil, err - } - - // Prepare outer payload structure - wrapper := contentInfo{ - ContentType: OIDEnvelopedData, - Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, - } - - return asn1.Marshal(wrapper) -} - -// EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, -// encrypted using caller provided pre-shared secret. -func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { - var eci *encryptedContentInfo - var err error - - if key == nil { - return nil, ErrPSKNotProvided - } - - // Apply chosen symmetric encryption method - switch ContentEncryptionAlgorithm { - case EncryptionAlgorithmDESCBC: - _, eci, err = encryptDESCBC(content, key) - - case EncryptionAlgorithmAES128GCM: - fallthrough - case EncryptionAlgorithmAES256GCM: - _, eci, err = encryptAESGCM(content, key) - - default: - return nil, ErrUnsupportedEncryptionAlgorithm - } - - if err != nil { - return nil, err - } - - // Prepare encrypted-data content - ed := encryptedData{ - Version: 0, - EncryptedContentInfo: *eci, - } - innerContent, err := asn1.Marshal(ed) - if err != nil { - return nil, err - } - - // Prepare outer payload structure - wrapper := contentInfo{ - ContentType: OIDEncryptedData, - Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, - } - - return asn1.Marshal(wrapper) -} - -func marshalEncryptedContent(content []byte) asn1.RawValue { - asn1Content, _ := asn1.Marshal(content) - return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} -} - -func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { - if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { - return rsa.EncryptPKCS1v15(rand.Reader, pub, key) - } - return nil, ErrUnsupportedAlgorithm -} - -func pad(data []byte, blocklen int) ([]byte, error) { - if blocklen < 1 { - return nil, fmt.Errorf("invalid blocklen %d", blocklen) - } - padlen := blocklen - (len(data) % blocklen) - if padlen == 0 { - padlen = blocklen - } - pad := bytes.Repeat([]byte{byte(padlen)}, padlen) - return append(data, pad...), nil -} diff --git a/scep/pkcs7/encrypt_test.go b/scep/pkcs7/encrypt_test.go deleted file mode 100644 index c64381e2..00000000 --- a/scep/pkcs7/encrypt_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto/x509" - "testing" -) - -func TestEncrypt(t *testing.T) { - modes := []int{ - EncryptionAlgorithmDESCBC, - EncryptionAlgorithmAES128CBC, - EncryptionAlgorithmAES256CBC, - EncryptionAlgorithmAES128GCM, - EncryptionAlgorithmAES256GCM, - } - sigalgs := []x509.SignatureAlgorithm{ - x509.SHA1WithRSA, - x509.SHA256WithRSA, - x509.SHA512WithRSA, - } - for _, mode := range modes { - for _, sigalg := range sigalgs { - ContentEncryptionAlgorithm = mode - - plaintext := []byte("Hello Secret World!") - cert, err := createTestCertificate(sigalg) - if err != nil { - t.Fatal(err) - } - encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) - if err != nil { - t.Fatal(err) - } - p7, err := Parse(encrypted) - if err != nil { - t.Fatalf("cannot Parse encrypted result: %s", err) - } - result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) - if err != nil { - t.Fatalf("cannot Decrypt encrypted result: %s", err) - } - if !bytes.Equal(plaintext, result) { - t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) - } - } - } -} - -func TestEncryptUsingPSK(t *testing.T) { - modes := []int{ - EncryptionAlgorithmDESCBC, - EncryptionAlgorithmAES128GCM, - } - - for _, mode := range modes { - ContentEncryptionAlgorithm = mode - plaintext := []byte("Hello Secret World!") - var key []byte - - switch mode { - case EncryptionAlgorithmDESCBC: - key = []byte("64BitKey") - case EncryptionAlgorithmAES128GCM: - key = []byte("128BitKey4AESGCM") - } - ciphertext, err := EncryptUsingPSK(plaintext, key) - if err != nil { - t.Fatal(err) - } - - p7, _ := Parse(ciphertext) - result, err := p7.DecryptUsingPSK(key) - if err != nil { - t.Fatalf("cannot Decrypt encrypted result: %s", err) - } - if !bytes.Equal(plaintext, result) { - t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) - } - } -} - -func TestPad(t *testing.T) { - tests := []struct { - Original []byte - Expected []byte - BlockSize int - }{ - {[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8}, - {[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8}, - } - for _, test := range tests { - padded, err := pad(test.Original, test.BlockSize) - if err != nil { - t.Errorf("pad encountered error: %s", err) - continue - } - if !bytes.Equal(test.Expected, padded) { - t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded) - } - } -} diff --git a/scep/pkcs7/pkcs7.go b/scep/pkcs7/pkcs7.go deleted file mode 100644 index ccc6cc6d..00000000 --- a/scep/pkcs7/pkcs7.go +++ /dev/null @@ -1,291 +0,0 @@ -// Package pkcs7 implements parsing and generation of some PKCS#7 structures. -package pkcs7 - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" - "sort" - - _ "crypto/sha1" // for crypto.SHA1 -) - -// PKCS7 Represents a PKCS7 structure -type PKCS7 struct { - Content []byte - Certificates []*x509.Certificate - CRLs []pkix.CertificateList - Signers []signerInfo - raw interface{} -} - -type contentInfo struct { - ContentType asn1.ObjectIdentifier - Content asn1.RawValue `asn1:"explicit,optional,tag:0"` -} - -// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. -// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), -// and Enveloped Data are supported (1.2.840.113549.1.7.3) -var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") - -type unsignedData []byte - -var ( - // Signed Data OIDs - OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} - OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} - OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} - OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} - OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} - OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} - OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} - - // Digest Algorithms - OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} - OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} - OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} - OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} - - OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} - OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - - OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} - - // Signature Algorithms - OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - - OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} - OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} - OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} - - // Encryption Algorithms - OIDEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} - OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} - OIDEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} - OIDEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} - OIDEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} - OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} -) - -func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { - switch { - case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1), - oid.Equal(OIDDigestAlgorithmDSA), oid.Equal(OIDDigestAlgorithmDSASHA1), - oid.Equal(OIDEncryptionAlgorithmRSA): - return crypto.SHA1, nil - case oid.Equal(OIDDigestAlgorithmSHA256), oid.Equal(OIDDigestAlgorithmECDSASHA256): - return crypto.SHA256, nil - case oid.Equal(OIDDigestAlgorithmSHA384), oid.Equal(OIDDigestAlgorithmECDSASHA384): - return crypto.SHA384, nil - case oid.Equal(OIDDigestAlgorithmSHA512), oid.Equal(OIDDigestAlgorithmECDSASHA512): - return crypto.SHA512, nil - } - return crypto.Hash(0), ErrUnsupportedAlgorithm -} - -// getDigestOIDForSignatureAlgorithm takes an x509.SignatureAlgorithm -// and returns the corresponding OID digest algorithm -func getDigestOIDForSignatureAlgorithm(digestAlg x509.SignatureAlgorithm) (asn1.ObjectIdentifier, error) { - switch digestAlg { - case x509.SHA1WithRSA, x509.ECDSAWithSHA1: - return OIDDigestAlgorithmSHA1, nil - case x509.SHA256WithRSA, x509.ECDSAWithSHA256: - return OIDDigestAlgorithmSHA256, nil - case x509.SHA384WithRSA, x509.ECDSAWithSHA384: - return OIDDigestAlgorithmSHA384, nil - case x509.SHA512WithRSA, x509.ECDSAWithSHA512: - return OIDDigestAlgorithmSHA512, nil - } - return nil, fmt.Errorf("pkcs7: cannot convert hash to oid, unknown hash algorithm") -} - -// getOIDForEncryptionAlgorithm takes the private key type of the signer and -// the OID of a digest algorithm to return the appropriate signerInfo.DigestEncryptionAlgorithm -func getOIDForEncryptionAlgorithm(pkey crypto.PrivateKey, OIDDigestAlg asn1.ObjectIdentifier) (asn1.ObjectIdentifier, error) { - switch pkey.(type) { - case *rsa.PrivateKey: - switch { - default: - return OIDEncryptionAlgorithmRSA, nil - case OIDDigestAlg.Equal(OIDEncryptionAlgorithmRSA): - return OIDEncryptionAlgorithmRSA, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): - return OIDEncryptionAlgorithmRSASHA1, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): - return OIDEncryptionAlgorithmRSASHA256, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): - return OIDEncryptionAlgorithmRSASHA384, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): - return OIDEncryptionAlgorithmRSASHA512, nil - } - case *ecdsa.PrivateKey: - switch { - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): - return OIDDigestAlgorithmECDSASHA1, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): - return OIDDigestAlgorithmECDSASHA256, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): - return OIDDigestAlgorithmECDSASHA384, nil - case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): - return OIDDigestAlgorithmECDSASHA512, nil - } - case *dsa.PrivateKey: - return OIDDigestAlgorithmDSA, nil - } - return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey) - -} - -// Parse decodes a DER encoded PKCS7 package -func Parse(data []byte) (p7 *PKCS7, err error) { - if len(data) == 0 { - return nil, errors.New("pkcs7: input data is empty") - } - var info contentInfo - der, err := ber2der(data) - if err != nil { - return nil, err - } - rest, err := asn1.Unmarshal(der, &info) - if len(rest) > 0 { - err = asn1.SyntaxError{Msg: "trailing data"} - return - } - if err != nil { - return - } - - // fmt.Printf("--> Content Type: %s", info.ContentType) - switch { - case info.ContentType.Equal(OIDSignedData): - return parseSignedData(info.Content.Bytes) - case info.ContentType.Equal(OIDEnvelopedData): - return parseEnvelopedData(info.Content.Bytes) - case info.ContentType.Equal(OIDEncryptedData): - return parseEncryptedData(info.Content.Bytes) - } - return nil, ErrUnsupportedContentType -} - -func parseEnvelopedData(data []byte) (*PKCS7, error) { - var ed envelopedData - if _, err := asn1.Unmarshal(data, &ed); err != nil { - return nil, err - } - return &PKCS7{ - raw: ed, - }, nil -} - -func parseEncryptedData(data []byte) (*PKCS7, error) { - var ed encryptedData - if _, err := asn1.Unmarshal(data, &ed); err != nil { - return nil, err - } - return &PKCS7{ - raw: ed, - }, nil -} - -func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { - if len(raw.Raw) == 0 { - return nil, nil - } - - var val asn1.RawValue - if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { - return nil, err - } - - return x509.ParseCertificates(val.Bytes) -} - -func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { - return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Equal(cert.RawIssuer, ias.IssuerName.FullBytes) -} - -// Attribute represents a key value pair attribute. Value must be marshalable byte -// `encoding/asn1` -type Attribute struct { - Type asn1.ObjectIdentifier - Value interface{} -} - -type attributes struct { - types []asn1.ObjectIdentifier - values []interface{} -} - -// Add adds the attribute, maintaining insertion order -func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { - attrs.types = append(attrs.types, attrType) - attrs.values = append(attrs.values, value) -} - -type sortableAttribute struct { - SortKey []byte - Attribute attribute -} - -type attributeSet []sortableAttribute - -func (sa attributeSet) Len() int { - return len(sa) -} - -func (sa attributeSet) Less(i, j int) bool { - return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 -} - -func (sa attributeSet) Swap(i, j int) { - sa[i], sa[j] = sa[j], sa[i] -} - -func (sa attributeSet) Attributes() []attribute { - attrs := make([]attribute, len(sa)) - for i, attr := range sa { - attrs[i] = attr.Attribute - } - return attrs -} - -func (attrs *attributes) ForMarshalling() ([]attribute, error) { - sortables := make(attributeSet, len(attrs.types)) - for i := range sortables { - attrType := attrs.types[i] - attrValue := attrs.values[i] - asn1Value, err := asn1.Marshal(attrValue) - if err != nil { - return nil, err - } - attr := attribute{ - Type: attrType, - Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag - } - encoded, err := asn1.Marshal(attr) - if err != nil { - return nil, err - } - sortables[i] = sortableAttribute{ - SortKey: encoded, - Attribute: attr, - } - } - sort.Sort(sortables) - return sortables.Attributes(), nil -} diff --git a/scep/pkcs7/pkcs7_test.go b/scep/pkcs7/pkcs7_test.go deleted file mode 100644 index e743192d..00000000 --- a/scep/pkcs7/pkcs7_test.go +++ /dev/null @@ -1,326 +0,0 @@ -package pkcs7 - -import ( - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "log" - "math/big" - "os" - "time" -) - -var test1024Key, test2048Key, test3072Key, test4096Key *rsa.PrivateKey - -func init() { - test1024Key = &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromBase10("123024078101403810516614073341068864574068590522569345017786163424062310013967742924377390210586226651760719671658568413826602264886073432535341149584680111145880576802262550990305759285883150470245429547886689754596541046564560506544976611114898883158121012232676781340602508151730773214407220733898059285561"), - E: 65537, - }, - D: fromBase10("118892427340746627750435157989073921703209000249285930635312944544706203626114423392257295670807166199489096863209592887347935991101581502404113203993092422730000157893515953622392722273095289787303943046491132467130346663160540744582438810535626328230098940583296878135092036661410664695896115177534496784545"), - Primes: []*big.Int{ - fromBase10("12172745919282672373981903347443034348576729562395784527365032103134165674508405592530417723266847908118361582847315228810176708212888860333051929276459099"), - fromBase10("10106518193772789699356660087736308350857919389391620140340519320928952625438936098550728858345355053201610649202713962702543058578827268756755006576249339"), - }, - } - test1024Key.Precompute() - test2048Key = &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"), - E: 3, - }, - D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"), - Primes: []*big.Int{ - fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"), - fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"), - }, - } - test2048Key.Precompute() - test3072Key = &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromBase10("4799422180968749215324244710281712119910779465109490663934897082847293004098645365195947978124390029272750644394844443980065532911010718425428791498896288210928474905407341584968381379157418577471272697781778686372450913810019702928839200328075568223462554606149618941566459398862673532997592879359280754226882565483298027678735544377401276021471356093819491755877827249763065753555051973844057308627201762456191918852016986546071426986328720794061622370410645440235373576002278045257207695462423797272017386006110722769072206022723167102083033531426777518054025826800254337147514768377949097720074878744769255210076910190151785807232805749219196645305822228090875616900385866236956058984170647782567907618713309775105943700661530312800231153745705977436176908325539234432407050398510090070342851489496464612052853185583222422124535243967989533830816012180864309784486694786581956050902756173889941244024888811572094961378021"), - E: 65537, - }, - D: fromBase10("4068124900056380177006532461065648259352178312499768312132802353620854992915205894105621345694615110794369150964768050224096623567443679436821868510233726084582567244003894477723706516831312989564775159596496449435830457803384416702014837685962523313266832032687145914871879794104404800823188153886925022171560391765913739346955738372354826804228989767120353182641396181570533678315099748218734875742705419933837638038793286534641711407564379950728858267828581787483317040753987167237461567332386718574803231955771633274184646232632371006762852623964054645811527580417392163873708539175349637050049959954373319861427407953413018816604365474462455009323937599275324390953644555294418021286807661559165324810415569396577697316798600308544755741549699523972971375304826663847015905713096287495342701286542193782001358775773848824496321550110946106870685499577993864871847542645561943034990484973293461948058147956373115641615329"), - Primes: []*big.Int{ - fromBase10("2378529069722721185825622840841310902793949682948530343491428052737890236476884657507685118578733560141370511507721598189068683665232991988491561624429938984370132428230072355214627085652359350722926394699707232921674771664421591347888367477300909202851476404132163673865768760147403525700174918450753162242834161458300343282159799476695001920226357456953682236859505243928716782707623075239350380352265954107362618991716602898266999700316937680986690964564264877"), - fromBase10("2017811025336026464312837780072272578817919741496395062543647660689775637351085991504709917848745137013798005682591633910555599626950744674459976829106750083386168859581016361317479081273480343110649405858059581933773354781034946787147300862495438979895430001323443224335618577322449133208754541656374335100929456885995320929464029817626916719434010943205170760536768893924932021302887114400922813817969176636993508191950649313115712159241971065134077636674146073"), - }, - } - test3072Key.Precompute() - test4096Key = &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromBase10("633335480064287130853997429184971616419051348693342219741748040433588285601270210251206421401040394238592139790962887290698043839174341843721930134010306454716566698330215646704263665452264344664385995704186692432827662862845900348526672531755932642433662686500295989783595767573119607065791980381547677840410600100715146047382485989885183858757974681241303484641390718944520330953604501686666386926996348457928415093305041429178744778762826377713889019740060910363468343855830206640274442887621960581569183233822878661711798998132931623726434336448716605363514220760343097572198620479297583609779817750646169845195672483600293522186340560792255595411601450766002877850696008003794520089358819042318331840490155176019070646738739580486357084733208876620846449161909966690602374519398451042362690200166144326179405976024265116931974936425064291406950542193873313447617169603706868220189295654943247311295475722243471700112334609817776430552541319671117235957754556272646031356496763094955985615723596562217985372503002989591679252640940571608314743271809251568670314461039035793703429977801961867815257832671786542212589906513979094156334941265621017752516999186481477500481433634914622735206243841674973785078408289183000133399026553"), - E: 65537, - }, - D: fromBase10("439373650557744155078930178606343279553665694488479749802070836418412881168612407941793966086633543867614175621952769177088930851151267623886678906158545451731745754402575409204816390946376103491325109185445659065122640946673660760274557781540431107937331701243915001777636528502669576801704352961341634812275635811512806966908648671988644114352046582195051714797831307925775689566757438907578527366568747104508496278929566712224252103563340770696548181508180254674236716995730292431858611476396845443056967589437890065663497768422598977743046882539288481002449571403783500529740184608873520856954837631427724158592309018382711485601884461168736465751756282510065053161144027097169985941910909130083273691945578478173708396726266170473745329617793866669307716920992380350270584929908460462802627239204245339385636926433446418108504614031393494119344916828744888432279343816084433424594432427362258172264834429525166677273382617457205387388293888430391895615438030066428745187333897518037597413369705720436392869403948934993623418405908467147848576977008003556716087129242155836114780890054057743164411952731290520995017097151300091841286806603044227906213832083363876549637037625314539090155417589796428888619937329669464810549362433"), - Primes: []*big.Int{ - fromBase10("25745433817240673759910623230144796182285844101796353869339294232644316274580053211056707671663014355388701931204078502829809738396303142990312095225333440050808647355535878394534263839500592870406002873182360027755750148248672968563366185348499498613479490545488025779331426515670185366021612402246813511722553210128074701620113404560399242413747318161403908617342170447610792422053460359960010544593668037305465806912471260799852789913123044326555978680190904164976511331681163576833618899773550873682147782263100803907156362439021929408298804955194748640633152519828940133338948391986823456836070708197320166146761"), - fromBase10("24599914864909676687852658457515103765368967514652318497893275892114442089314173678877914038802355565271545910572804267918959612739009937926962653912943833939518967731764560204997062096919833970670512726396663920955497151415639902788974842698619579886297871162402643104696160155894685518587660015182381685605752989716946154299190561137541792784125356553411300817844325739404126956793095254412123887617931225840421856505925283322918693259047428656823141903489964287619982295891439430302405252447010728112098326033634688757933930065610737780413018498561434074501822951716586796047404555397992425143397497639322075233073"), - }, - } - test4096Key.Precompute() -} - -func fromBase10(base10 string) *big.Int { - i, ok := new(big.Int).SetString(base10, 10) - if !ok { - panic("bad number: " + base10) - } - return i -} - -type certKeyPair struct { - Certificate *x509.Certificate - PrivateKey *crypto.PrivateKey -} - -func createTestCertificate(sigAlg x509.SignatureAlgorithm) (certKeyPair, error) { - signer, err := createTestCertificateByIssuer("Eddard Stark", nil, sigAlg, true) - if err != nil { - return certKeyPair{}, err - } - pair, err := createTestCertificateByIssuer("Jon Snow", signer, sigAlg, false) - if err != nil { - return certKeyPair{}, err - } - return *pair, nil -} - -func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) { - var ( - err error - priv crypto.PrivateKey - derCert []byte - issuerCert *x509.Certificate - issuerKey crypto.PrivateKey - ) - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: name, - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Now().Add(-1 *time.Second), - NotAfter: time.Now().AddDate(1, 0, 0), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, - } - if issuer != nil { - issuerCert = issuer.Certificate - issuerKey = *issuer.PrivateKey - } - switch sigAlg { - case x509.SHA1WithRSA: - priv = test1024Key - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA1WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA1 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA1 - } - case x509.SHA256WithRSA: - priv = test2048Key - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA256WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA256 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.SHA384WithRSA: - priv = test3072Key - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA384WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA384 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.SHA512WithRSA: - priv = test4096Key - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA512WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA512 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.ECDSAWithSHA1: - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, err - } - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA1WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA1 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA1 - } - case x509.ECDSAWithSHA256: - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, err - } - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA256WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA256 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.ECDSAWithSHA384: - priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA384WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA384 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.ECDSAWithSHA512: - priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - if err != nil { - return nil, err - } - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA512WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA512 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA256 - } - case x509.DSAWithSHA1: - var dsaPriv dsa.PrivateKey - params := &dsaPriv.Parameters - err = dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160) - if err != nil { - return nil, err - } - err = dsa.GenerateKey(&dsaPriv, rand.Reader) - if err != nil { - return nil, err - } - switch issuerKey.(type) { - case *rsa.PrivateKey: - template.SignatureAlgorithm = x509.SHA1WithRSA - case *ecdsa.PrivateKey: - template.SignatureAlgorithm = x509.ECDSAWithSHA1 - case *dsa.PrivateKey: - template.SignatureAlgorithm = x509.DSAWithSHA1 - } - priv = &dsaPriv - } - if isCA { - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - template.BasicConstraintsValid = true - } - if issuer == nil { - // no issuer given,make this a self-signed root cert - issuerCert = &template - issuerKey = priv - } - - log.Println("creating cert", name, "issued by", issuerCert.Subject.CommonName, "with sigalg", sigAlg) - switch priv.(type) { - case *rsa.PrivateKey: - switch issuerKey.(type) { - case *rsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) - case *ecdsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) - case *dsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) - } - case *ecdsa.PrivateKey: - switch issuerKey.(type) { - case *rsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) - case *ecdsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) - case *dsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) - } - case *dsa.PrivateKey: - pub := &priv.(*dsa.PrivateKey).PublicKey - switch issuerKey := issuerKey.(type) { - case *rsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, pub, issuerKey) - case *ecdsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) - case *dsa.PrivateKey: - derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) - } - } - if err != nil { - return nil, err - } - if len(derCert) == 0 { - return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey) - } - cert, err := x509.ParseCertificate(derCert) - if err != nil { - return nil, err - } - pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) - return &certKeyPair{ - Certificate: cert, - PrivateKey: &priv, - }, nil -} - -type TestFixture struct { - Input []byte - Certificate *x509.Certificate - PrivateKey *rsa.PrivateKey -} - -func UnmarshalTestFixture(testPEMBlock string) TestFixture { - var result TestFixture - var derBlock *pem.Block - var pemBlock = []byte(testPEMBlock) - for { - derBlock, pemBlock = pem.Decode(pemBlock) - if derBlock == nil { - break - } - switch derBlock.Type { - case "PKCS7": - result.Input = derBlock.Bytes - case "CERTIFICATE": - result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) - case "PRIVATE KEY": - result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes) - } - } - - return result -} diff --git a/scep/pkcs7/sign.go b/scep/pkcs7/sign.go deleted file mode 100644 index addd7638..00000000 --- a/scep/pkcs7/sign.go +++ /dev/null @@ -1,429 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" - "math/big" - "time" -) - -// SignedData is an opaque data structure for creating signed data payloads -type SignedData struct { - sd signedData - certs []*x509.Certificate - data, messageDigest []byte - digestOid asn1.ObjectIdentifier - encryptionOid asn1.ObjectIdentifier -} - -// NewSignedData takes data and initializes a PKCS7 SignedData struct that is -// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default -// and can be changed by calling SetDigestAlgorithm. -func NewSignedData(data []byte) (*SignedData, error) { - content, err := asn1.Marshal(data) - if err != nil { - return nil, err - } - ci := contentInfo{ - ContentType: OIDData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, - } - sd := signedData{ - ContentInfo: ci, - Version: 1, - } - return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1}, nil -} - -// SignerInfoConfig are optional values to include when adding a signer -type SignerInfoConfig struct { - ExtraSignedAttributes []Attribute - ExtraUnsignedAttributes []Attribute -} - -type signedData struct { - Version int `asn1:"default:1"` - DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` - ContentInfo contentInfo - Certificates rawCertificates `asn1:"optional,tag:0"` - CRLs []pkix.CertificateList `asn1:"optional,tag:1"` - SignerInfos []signerInfo `asn1:"set"` -} - -type signerInfo struct { - Version int `asn1:"default:1"` - IssuerAndSerialNumber issuerAndSerial - DigestAlgorithm pkix.AlgorithmIdentifier - AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"` - DigestEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedDigest []byte - UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"` -} - -type attribute struct { - Type asn1.ObjectIdentifier - Value asn1.RawValue `asn1:"set"` -} - -func marshalAttributes(attrs []attribute) ([]byte, error) { - encodedAttributes, err := asn1.Marshal(struct { - A []attribute `asn1:"set"` - }{A: attrs}) - if err != nil { - return nil, err - } - - // Remove the leading sequence octets - var raw asn1.RawValue - asn1.Unmarshal(encodedAttributes, &raw) - return raw.Bytes, nil -} - -type rawCertificates struct { - Raw asn1.RawContent -} - -type issuerAndSerial struct { - IssuerName asn1.RawValue - SerialNumber *big.Int -} - -// SetDigestAlgorithm sets the digest algorithm to be used in the signing process. -// -// This should be called before adding signers -func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) { - sd.digestOid = d -} - -// SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process. -// -// This should be called before adding signers -func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) { - sd.encryptionOid = d -} - -// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent. -func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { - var parents []*x509.Certificate - return sd.AddSignerChain(ee, pkey, parents, config) -} - -// AddSignerChain signs attributes about the content and adds certificates -// and signers infos to the Signed Data. The certificate and private key -// of the end-entity signer are used to issue the signature, and any -// parent of that end-entity that need to be added to the list of -// certifications can be specified in the parents slice. -// -// The signature algorithm used to hash the data is the one of the end-entity -// certificate. -func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error { -// Following RFC 2315, 9.2 SignerInfo type, the distinguished name of -// the issuer of the end-entity signer is stored in the issuerAndSerialNumber -// section of the SignedData.SignerInfo, alongside the serial number of -// the end-entity. - var ias issuerAndSerial - ias.SerialNumber = ee.SerialNumber - if len(parents) == 0 { - // no parent, the issuer is the end-entity cert itself - ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} - } else { - err := verifyPartialChain(ee, parents) - if err != nil { - return err - } - // the first parent is the issuer - ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject} - } - sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, - pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, - ) - hash, err := getHashForOID(sd.digestOid) - if err != nil { - return err - } - h := hash.New() - h.Write(sd.data) - sd.messageDigest = h.Sum(nil) - encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid) - if err != nil { - return err - } - attrs := &attributes{} - attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType) - attrs.Add(OIDAttributeMessageDigest, sd.messageDigest) - attrs.Add(OIDAttributeSigningTime, time.Now().UTC()) - for _, attr := range config.ExtraSignedAttributes { - attrs.Add(attr.Type, attr.Value) - } - finalAttrs, err := attrs.ForMarshalling() - if err != nil { - return err - } - unsignedAttrs := &attributes{} - for _, attr := range config.ExtraUnsignedAttributes { - unsignedAttrs.Add(attr.Type, attr.Value) - } - finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() - if err != nil { - return err - } - // create signature of signed attributes - signature, err := signAttributes(finalAttrs, pkey, hash) - if err != nil { - return err - } - signer := signerInfo{ - AuthenticatedAttributes: finalAttrs, - UnauthenticatedAttributes: finalUnsignedAttrs, - DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, - DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid}, - IssuerAndSerialNumber: ias, - EncryptedDigest: signature, - Version: 1, - } - sd.certs = append(sd.certs, ee) - if len(parents) > 0 { - sd.certs = append(sd.certs, parents...) - } - sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) - return nil -} - -// SignWithoutAttr issues a signature on the content of the pkcs7 SignedData. -// Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone -// and does not include any signed attributes like timestamp and so on. -// -// This function is needed to sign old Android APKs, something you probably -// shouldn't do unless you're maintaining backward compatibility for old -// applications. -func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { - var signature []byte - sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}) - hash, err := getHashForOID(sd.digestOid) - if err != nil { - return err - } - h := hash.New() - h.Write(sd.data) - sd.messageDigest = h.Sum(nil) - switch pkey := pkey.(type) { - case *dsa.PrivateKey: - // dsa doesn't implement crypto.Signer so we make a special case - // https://github.com/golang/go/issues/27889 - r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest) - if err != nil { - return err - } - signature, err = asn1.Marshal(dsaSignature{r, s}) - if err != nil { - return err - } - default: - key, ok := pkey.(crypto.Signer) - if !ok { - return errors.New("pkcs7: private key does not implement crypto.Signer") - } - signature, err = key.Sign(rand.Reader, sd.messageDigest, hash) - if err != nil { - return err - } - } - var ias issuerAndSerial - ias.SerialNumber = ee.SerialNumber - // no parent, the issue is the end-entity cert itself - ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} - if sd.encryptionOid == nil { - // if the encryption algorithm wasn't set by SetEncryptionAlgorithm, - // infer it from the digest algorithm - sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid) - } - if err != nil { - return err - } - signer := signerInfo{ - DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, - DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid}, - IssuerAndSerialNumber: ias, - EncryptedDigest: signature, - Version: 1, - } - // create signature of signed attributes - sd.certs = append(sd.certs, ee) - sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) - return nil -} - -func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error { - unsignedAttrs := &attributes{} - for _, attr := range extraUnsignedAttrs { - unsignedAttrs.Add(attr.Type, attr.Value) - } - finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() - if err != nil { - return err - } - - si.UnauthenticatedAttributes = finalUnsignedAttrs - - return nil -} - -// AddCertificate adds the certificate to the payload. Useful for parent certificates -func (sd *SignedData) AddCertificate(cert *x509.Certificate) { - sd.certs = append(sd.certs, cert) -} - -// Detach removes content from the signed data struct to make it a detached signature. -// This must be called right before Finish() -func (sd *SignedData) Detach() { - sd.sd.ContentInfo = contentInfo{ContentType: OIDData} -} - -// GetSignedData returns the private Signed Data -func (sd *SignedData) GetSignedData() *signedData { - return &sd.sd -} - -// Finish marshals the content and its signers -func (sd *SignedData) Finish() ([]byte, error) { - sd.sd.Certificates = marshalCertificates(sd.certs) - inner, err := asn1.Marshal(sd.sd) - if err != nil { - return nil, err - } - outer := contentInfo{ - ContentType: OIDSignedData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, - } - return asn1.Marshal(outer) -} - -// RemoveAuthenticatedAttributes removes authenticated attributes from signedData -// similar to OpenSSL's PKCS7_NOATTR or -noattr flags -func (sd *SignedData) RemoveAuthenticatedAttributes() { - for i := range sd.sd.SignerInfos { - sd.sd.SignerInfos[i].AuthenticatedAttributes = nil - } -} - -// RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData -func (sd *SignedData) RemoveUnauthenticatedAttributes() { - for i := range sd.sd.SignerInfos { - sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil - } -} - -// verifyPartialChain checks that a given cert is issued by the first parent in the list, -// then continue down the path. It doesn't require the last parent to be a root CA, -// or to be trusted in any truststore. It simply verifies that the chain provided, albeit -// partial, makes sense. -func verifyPartialChain(cert *x509.Certificate, parents []*x509.Certificate) error { - if len(parents) == 0 { - return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName) - } - err := cert.CheckSignatureFrom(parents[0]) - if err != nil { - return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err) - } - if len(parents) == 1 { - // there is no more parent to check, return - return nil - } - return verifyPartialChain(parents[0], parents[1:]) -} - -func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { - var ias issuerAndSerial - // The issuer RDNSequence has to match exactly the sequence in the certificate - // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence - ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} - ias.SerialNumber = cert.SerialNumber - - return ias, nil -} - -// signs the DER encoded form of the attributes with the private key -func signAttributes(attrs []attribute, pkey crypto.PrivateKey, digestAlg crypto.Hash) ([]byte, error) { - attrBytes, err := marshalAttributes(attrs) - if err != nil { - return nil, err - } - h := digestAlg.New() - h.Write(attrBytes) - hash := h.Sum(nil) - - // dsa doesn't implement crypto.Signer so we make a special case - // https://github.com/golang/go/issues/27889 - switch pkey := pkey.(type) { - case *dsa.PrivateKey: - r, s, err := dsa.Sign(rand.Reader, pkey, hash) - if err != nil { - return nil, err - } - return asn1.Marshal(dsaSignature{r, s}) - } - - key, ok := pkey.(crypto.Signer) - if !ok { - return nil, errors.New("pkcs7: private key does not implement crypto.Signer") - } - return key.Sign(rand.Reader, hash, digestAlg) -} - -type dsaSignature struct { - R, S *big.Int -} - -// concats and wraps the certificates in the RawValue structure -func marshalCertificates(certs []*x509.Certificate) rawCertificates { - var buf bytes.Buffer - for _, cert := range certs { - buf.Write(cert.Raw) - } - rawCerts, _ := marshalCertificateBytes(buf.Bytes()) - return rawCerts -} - -// Even though, the tag & length are stripped out during marshalling the -// RawContent, we have to encode it into the RawContent. If its missing, -// then `asn1.Marshal()` will strip out the certificate wrapper instead. -func marshalCertificateBytes(certs []byte) (rawCertificates, error) { - var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} - b, err := asn1.Marshal(val) - if err != nil { - return rawCertificates{}, err - } - return rawCertificates{Raw: b}, nil -} - -// DegenerateCertificate creates a signed data structure containing only the -// provided certificate or certificate chain. -func DegenerateCertificate(cert []byte) ([]byte, error) { - rawCert, err := marshalCertificateBytes(cert) - if err != nil { - return nil, err - } - emptyContent := contentInfo{ContentType: OIDData} - sd := signedData{ - Version: 1, - ContentInfo: emptyContent, - Certificates: rawCert, - CRLs: []pkix.CertificateList{}, - } - content, err := asn1.Marshal(sd) - if err != nil { - return nil, err - } - signedContent := contentInfo{ - ContentType: OIDSignedData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, - } - return asn1.Marshal(signedContent) -} diff --git a/scep/pkcs7/sign_test.go b/scep/pkcs7/sign_test.go deleted file mode 100644 index 0ba6324d..00000000 --- a/scep/pkcs7/sign_test.go +++ /dev/null @@ -1,266 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto/dsa" - "crypto/x509" - "encoding/asn1" - "encoding/pem" - "fmt" - "io/ioutil" - "log" - "math/big" - "os" - "os/exec" - "testing" -) - -func TestSign(t *testing.T) { - content := []byte("Hello World") - sigalgs := []x509.SignatureAlgorithm{ - x509.SHA1WithRSA, - x509.SHA256WithRSA, - x509.SHA512WithRSA, - x509.ECDSAWithSHA1, - x509.ECDSAWithSHA256, - x509.ECDSAWithSHA384, - x509.ECDSAWithSHA512, - } - for _, sigalgroot := range sigalgs { - rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) - if err != nil { - t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) - } - truststore := x509.NewCertPool() - truststore.AddCert(rootCert.Certificate) - for _, sigalginter := range sigalgs { - interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) - if err != nil { - t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) - } - var parents []*x509.Certificate - parents = append(parents, interCert.Certificate) - for _, sigalgsigner := range sigalgs { - signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) - if err != nil { - t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - for _, testDetach := range []bool{false, true} { - log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach) - toBeSigned, err := NewSignedData(content) - if err != nil { - t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - - // Set the digest to match the end entity cert - signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm) - toBeSigned.SetDigestAlgorithm(signerDigest) - - if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { - t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - if testDetach { - toBeSigned.Detach() - } - signed, err := toBeSigned.Finish() - if err != nil { - t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) - p7, err := Parse(signed) - if err != nil { - t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - if testDetach { - p7.Content = content - } - if !bytes.Equal(content, p7.Content) { - t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content) - } - if err := p7.VerifyWithChain(truststore); err != nil { - t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) { - t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q", - sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm) - } - } - } - } - } -} - -func TestDSASignAndVerifyWithOpenSSL(t *testing.T) { - content := []byte("Hello World") - // write the content to a temp file - tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpContentFile.Name(), content, 0755) - - block, _ := pem.Decode([]byte(dsaPublicCert)) - if block == nil { - t.Fatal("failed to parse certificate PEM") - } - signerCert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatal("failed to parse certificate: " + err.Error()) - } - - // write the signer cert to a temp file - tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) - - priv := dsa.PrivateKey{ - PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"), - Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"), - G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"), - }, - }, - X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"), - } - toBeSigned, err := NewSignedData(content) - if err != nil { - t.Fatalf("test case: cannot initialize signed data: %s", err) - } - if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil { - t.Fatalf("Cannot add signer: %s", err) - } - toBeSigned.Detach() - signed, err := toBeSigned.Finish() - if err != nil { - t.Fatalf("test case: cannot finish signing data: %s", err) - } - - // write the signature to a temp file - tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755) - - // call openssl to verify the signature on the content using the root - opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify", - "-in", tmpSignatureFile.Name(), "-inform", "PEM", - "-content", tmpContentFile.Name()) - out, err := opensslCMD.CombinedOutput() - if err != nil { - t.Fatalf("test case: openssl command failed with %s: %s", err, out) - } - os.Remove(tmpSignatureFile.Name()) // clean up - os.Remove(tmpContentFile.Name()) // clean up - os.Remove(tmpSignerCertFile.Name()) // clean up -} - -func ExampleSignedData() { - // generate a signing cert or load a key pair - cert, err := createTestCertificate(x509.SHA256WithRSA) - if err != nil { - fmt.Printf("Cannot create test certificates: %s", err) - } - - // Initialize a SignedData struct with content to be signed - signedData, err := NewSignedData([]byte("Example data to be signed")) - if err != nil { - fmt.Printf("Cannot initialize signed data: %s", err) - } - - // Add the signing cert and private key - if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { - fmt.Printf("Cannot add signer: %s", err) - } - - // Call Detach() is you want to remove content from the signature - // and generate an S/MIME detached signature - signedData.Detach() - - // Finish() to obtain the signature bytes - detachedSignature, err := signedData.Finish() - if err != nil { - fmt.Printf("Cannot finish signing data: %s", err) - } - pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature}) -} - -func TestUnmarshalSignedAttribute(t *testing.T) { - cert, err := createTestCertificate(x509.SHA512WithRSA) - if err != nil { - t.Fatal(err) - } - content := []byte("Hello World") - toBeSigned, err := NewSignedData(content) - if err != nil { - t.Fatalf("Cannot initialize signed data: %s", err) - } - oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} - testValue := "TestValue" - if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{ - ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}}, - }); err != nil { - t.Fatalf("Cannot add signer: %s", err) - } - signed, err := toBeSigned.Finish() - if err != nil { - t.Fatalf("Cannot finish signing data: %s", err) - } - p7, err := Parse(signed) - if err != nil { - t.Fatalf("Cannot parse signed data: %v", err) - } - var actual string - err = p7.UnmarshalSignedAttribute(oidTest, &actual) - if err != nil { - t.Fatalf("Cannot unmarshal test value: %s", err) - } - if testValue != actual { - t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual) - } -} - -func TestDegenerateCertificate(t *testing.T) { - cert, err := createTestCertificate(x509.SHA1WithRSA) - if err != nil { - t.Fatal(err) - } - deg, err := DegenerateCertificate(cert.Certificate.Raw) - if err != nil { - t.Fatal(err) - } - testOpenSSLParse(t, deg) - pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg}) -} - -// writes the cert to a temporary file and tests that openssl can read it. -func testOpenSSLParse(t *testing.T, certBytes []byte) { - tmpCertFile, err := ioutil.TempFile("", "testCertificate") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tmpCertFile.Name()) // clean up - - if _, err := tmpCertFile.Write(certBytes); err != nil { - t.Fatal(err) - } - - opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name()) - _, err = opensslCMD.Output() - if err != nil { - t.Fatal(err) - } - - if err := tmpCertFile.Close(); err != nil { - t.Fatal(err) - } - -} -func fromHex(s string) *big.Int { - result, ok := new(big.Int).SetString(s, 16) - if !ok { - panic(s) - } - return result -} diff --git a/scep/pkcs7/verify.go b/scep/pkcs7/verify.go deleted file mode 100644 index c8ead236..00000000 --- a/scep/pkcs7/verify.go +++ /dev/null @@ -1,264 +0,0 @@ -package pkcs7 - -import ( - "crypto/subtle" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" - "time" -) - -// Verify is a wrapper around VerifyWithChain() that initializes an empty -// trust store, effectively disabling certificate verification when validating -// a signature. -func (p7 *PKCS7) Verify() (err error) { - return p7.VerifyWithChain(nil) -} - -// VerifyWithChain checks the signatures of a PKCS7 object. -// If truststore is not nil, it also verifies the chain of trust of the end-entity -// signer cert to one of the root in the truststore. -func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) { - if len(p7.Signers) == 0 { - return errors.New("pkcs7: Message has no signers") - } - for _, signer := range p7.Signers { - if err := verifySignature(p7, signer, truststore); err != nil { - return err - } - } - return nil -} - -func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) { - signedData := p7.Content - ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) - if ee == nil { - return errors.New("pkcs7: No certificate for signer") - } - signingTime := time.Now().UTC() - if len(signer.AuthenticatedAttributes) > 0 { - // TODO(fullsailor): First check the content type match - var digest []byte - err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) - if err != nil { - return err - } - hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) - if err != nil { - return err - } - h := hash.New() - h.Write(p7.Content) - computed := h.Sum(nil) - if subtle.ConstantTimeCompare(digest, computed) != 1 { - return &MessageDigestMismatchError{ - ExpectedDigest: digest, - ActualDigest: computed, - } - } - signedData, err = marshalAttributes(signer.AuthenticatedAttributes) - if err != nil { - return err - } - err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) - if err == nil { - // signing time found, performing validity check - if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { - return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", - signingTime.Format(time.RFC3339), - ee.NotBefore.Format(time.RFC3339), - ee.NotBefore.Format(time.RFC3339)) - } - } - } - if truststore != nil { - _, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime) - if err != nil { - return err - } - } - sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) - if err != nil { - return err - } - return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) -} - -// GetOnlySigner returns an x509.Certificate for the first signer of the signed -// data payload. If there are more or less than one signer, nil is returned -func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { - if len(p7.Signers) != 1 { - return nil - } - signer := p7.Signers[0] - return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) -} - -// UnmarshalSignedAttribute decodes a single attribute from the signer info -func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { - sd, ok := p7.raw.(signedData) - if !ok { - return errors.New("pkcs7: payload is not signedData content") - } - if len(sd.SignerInfos) < 1 { - return errors.New("pkcs7: payload has no signers") - } - attributes := sd.SignerInfos[0].AuthenticatedAttributes - return unmarshalAttribute(attributes, attributeType, out) -} - -func parseSignedData(data []byte) (*PKCS7, error) { - var sd signedData - asn1.Unmarshal(data, &sd) - certs, err := sd.Certificates.Parse() - if err != nil { - return nil, err - } - // fmt.Printf("--> Signed Data Version %d\n", sd.Version) - - var compound asn1.RawValue - var content unsignedData - - // The Content.Bytes maybe empty on PKI responses. - if len(sd.ContentInfo.Content.Bytes) > 0 { - if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { - return nil, err - } - } - // Compound octet string - if compound.IsCompound { - if compound.Tag == 4 { - if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { - return nil, err - } - } else { - content = compound.Bytes - } - } else { - // assuming this is tag 04 - content = compound.Bytes - } - return &PKCS7{ - Content: content, - Certificates: certs, - CRLs: sd.CRLs, - Signers: sd.SignerInfos, - raw: sd}, nil -} - -// verifyCertChain takes an end-entity certs, a list of potential intermediates and a -// truststore, and built all potential chains between the EE and a trusted root. -// -// When verifying chains that may have expired, currentTime can be set to a past date -// to allow the verification to pass. If unset, currentTime is set to the current UTC time. -func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) { - intermediates := x509.NewCertPool() - for _, intermediate := range certs { - intermediates.AddCert(intermediate) - } - verifyOptions := x509.VerifyOptions{ - Roots: truststore, - Intermediates: intermediates, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - CurrentTime: currentTime, - } - chains, err = ee.Verify(verifyOptions) - if err != nil { - return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err) - } - return -} - -// MessageDigestMismatchError is returned when the signer data digest does not -// match the computed digest for the contained content -type MessageDigestMismatchError struct { - ExpectedDigest []byte - ActualDigest []byte -} - -func (err *MessageDigestMismatchError) Error() string { - return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) -} - -func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) { - switch { - case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1): - return x509.ECDSAWithSHA1, nil - case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256): - return x509.ECDSAWithSHA256, nil - case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384): - return x509.ECDSAWithSHA384, nil - case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512): - return x509.ECDSAWithSHA512, nil - case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512): - switch { - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): - return x509.SHA1WithRSA, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): - return x509.SHA256WithRSA, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): - return x509.SHA384WithRSA, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): - return x509.SHA512WithRSA, nil - default: - return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", - digest.Algorithm.String(), digestEncryption.Algorithm.String()) - } - case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA), - digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1): - switch { - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): - return x509.DSAWithSHA1, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): - return x509.DSAWithSHA256, nil - default: - return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", - digest.Algorithm.String(), digestEncryption.Algorithm.String()) - } - case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384), - digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521): - switch { - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): - return x509.ECDSAWithSHA1, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): - return x509.ECDSAWithSHA256, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): - return x509.ECDSAWithSHA384, nil - case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): - return x509.ECDSAWithSHA512, nil - default: - return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", - digest.Algorithm.String(), digestEncryption.Algorithm.String()) - } - default: - return -1, fmt.Errorf("pkcs7: unsupported algorithm %q", - digestEncryption.Algorithm.String()) - } -} - -func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { - for _, cert := range certs { - if isCertMatchForIssuerAndSerial(cert, ias) { - return cert - } - } - return nil -} - -func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { - for _, attr := range attrs { - if attr.Type.Equal(attributeType) { - _, err := asn1.Unmarshal(attr.Value.Bytes, out) - return err - } - } - return errors.New("pkcs7: attribute type not in attributes") -} diff --git a/scep/pkcs7/verify_test.go b/scep/pkcs7/verify_test.go deleted file mode 100644 index f80943b2..00000000 --- a/scep/pkcs7/verify_test.go +++ /dev/null @@ -1,713 +0,0 @@ -package pkcs7 - -import ( - "bytes" - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - "io/ioutil" - "os" - "os/exec" - "testing" - "time" -) - -func TestVerify(t *testing.T) { - fixture := UnmarshalTestFixture(SignedTestFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Errorf("Parse encountered unexpected error: %v", err) - } - - if err := p7.Verify(); err != nil { - t.Errorf("Verify failed with error: %v", err) - } - expected := []byte("We the People") - if !bytes.Equal(p7.Content, expected) { - t.Errorf("Signed content does not match.\n\tExpected:%s\n\tActual:%s", expected, p7.Content) - - } -} - -var SignedTestFixture = ` ------BEGIN PKCS7----- -MIIDVgYJKoZIhvcNAQcCoIIDRzCCA0MCAQExCTAHBgUrDgMCGjAcBgkqhkiG9w0B -BwGgDwQNV2UgdGhlIFBlb3BsZaCCAdkwggHVMIIBQKADAgECAgRpuDctMAsGCSqG -SIb3DQEBCzApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3Rh -cmswHhcNMTUwNTA2MDQyNDQ4WhcNMTYwNTA2MDQyNDQ4WjAlMRAwDgYDVQQKEwdB -Y21lIENvMREwDwYDVQQDEwhKb24gU25vdzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEAqr+tTF4mZP5rMwlXp1y+crRtFpuLXF1zvBZiYMfIvAHwo1ta8E1IcyEP -J1jIiKMcwbzeo6kAmZzIJRCTezq9jwXUsKbQTvcfOH9HmjUmXBRWFXZYoQs/OaaF -a45deHmwEeMQkuSWEtYiVKKZXtJOtflKIT3MryJEDiiItMkdybUCAwEAAaMSMBAw -DgYDVR0PAQH/BAQDAgCgMAsGCSqGSIb3DQEBCwOBgQDK1EweZWRL+f7Z+J0kVzY8 -zXptcBaV4Lf5wGZJLJVUgp33bpLNpT3yadS++XQJ+cvtW3wADQzBSTMduyOF8Zf+ -L7TjjrQ2+F2HbNbKUhBQKudxTfv9dJHdKbD+ngCCdQJYkIy2YexsoNG0C8nQkggy -axZd/J69xDVx6pui3Sj8sDGCATYwggEyAgEBMDEwKTEQMA4GA1UEChMHQWNtZSBD -bzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrAgRpuDctMAcGBSsOAwIaoGEwGAYJKoZI -hvcNAQkDMQsGCSqGSIb3DQEHATAgBgkqhkiG9w0BCQUxExcRMTUwNTA2MDAyNDQ4 -LTA0MDAwIwYJKoZIhvcNAQkEMRYEFG9D7gcTh9zfKiYNJ1lgB0yTh4sZMAsGCSqG -SIb3DQEBAQSBgFF3sGDU9PtXty/QMtpcFa35vvIOqmWQAIZt93XAskQOnBq4OloX -iL9Ct7t1m4pzjRm0o9nDkbaSLZe7HKASHdCqijroScGlI8M+alJ8drHSFv6ZIjnM -FIwIf0B2Lko6nh9/6mUXq7tbbIHa3Gd1JUVire/QFFtmgRXMbXYk8SIS ------END PKCS7----- ------BEGIN CERTIFICATE----- -MIIB1TCCAUCgAwIBAgIEabg3LTALBgkqhkiG9w0BAQswKTEQMA4GA1UEChMHQWNt -ZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTE1MDUwNjA0MjQ0OFoXDTE2 -MDUwNjA0MjQ0OFowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNu -b3cwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKq/rUxeJmT+azMJV6dcvnK0 -bRabi1xdc7wWYmDHyLwB8KNbWvBNSHMhDydYyIijHMG83qOpAJmcyCUQk3s6vY8F -1LCm0E73Hzh/R5o1JlwUVhV2WKELPzmmhWuOXXh5sBHjEJLklhLWIlSimV7STrX5 -SiE9zK8iRA4oiLTJHcm1AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIAoDALBgkqhkiG -9w0BAQsDgYEAytRMHmVkS/n+2fidJFc2PM16bXAWleC3+cBmSSyVVIKd926SzaU9 -8mnUvvl0CfnL7Vt8AA0MwUkzHbsjhfGX/i+04460Nvhdh2zWylIQUCrncU37/XSR -3Smw/p4AgnUCWJCMtmHsbKDRtAvJ0JIIMmsWXfyevcQ1ceqbot0o/LA= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIICXgIBAAKBgQCqv61MXiZk/mszCVenXL5ytG0Wm4tcXXO8FmJgx8i8AfCjW1rw -TUhzIQ8nWMiIoxzBvN6jqQCZnMglEJN7Or2PBdSwptBO9x84f0eaNSZcFFYVdlih -Cz85poVrjl14ebAR4xCS5JYS1iJUople0k61+UohPcyvIkQOKIi0yR3JtQIDAQAB -AoGBAIPLCR9N+IKxodq11lNXEaUFwMHXc1zqwP8no+2hpz3+nVfplqqubEJ4/PJY -5AgbJoIfnxVhyBXJXu7E+aD/OPneKZrgp58YvHKgGvvPyJg2gpC/1Fh0vQB0HNpI -1ZzIZUl8ZTUtVgtnCBUOh5JGI4bFokAqrT//Uvcfd+idgxqBAkEA1ZbP/Kseld14 -qbWmgmU5GCVxsZRxgR1j4lG3UVjH36KXMtRTm1atAam1uw3OEGa6Y3ANjpU52FaB -Hep5rkk4FQJBAMynMo1L1uiN5GP+KYLEF5kKRxK+FLjXR0ywnMh+gpGcZDcOae+J -+t1gLoWBIESH/Xt639T7smuSfrZSA9V0EyECQA8cvZiWDvLxmaEAXkipmtGPjKzQ -4PsOtkuEFqFl07aKDYKmLUg3aMROWrJidqsIabWxbvQgsNgSvs38EiH3wkUCQQCg -ndxb7piVXb9RBwm3OoU2tE1BlXMX+sVXmAkEhd2dwDsaxrI3sHf1xGXem5AimQRF -JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3 -BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g== ------END PRIVATE KEY-----` - -func TestVerifyEC2(t *testing.T) { - fixture := UnmarshalTestFixture(EC2IdentityDocumentFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Errorf("Parse encountered unexpected error: %v", err) - } - p7.Certificates = []*x509.Certificate{fixture.Certificate} - if err := p7.Verify(); err != nil { - t.Errorf("Verify failed with error: %v", err) - } -} - -var EC2IdentityDocumentFixture = ` ------BEGIN PKCS7----- -MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA -JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh -eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1 -cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh -bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs -bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg -OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK -ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj -aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy -YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA -AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n -dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi -IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B -CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG -CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w -LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA -AAAAAA== ------END PKCS7----- ------BEGIN CERTIFICATE----- -MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw -FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD -VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z -ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u -IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl -cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e -ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3 -VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P -hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j -k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U -hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF -lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf -MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW -MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw -vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw -7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K ------END CERTIFICATE-----` - -func TestVerifyAppStore(t *testing.T) { - fixture := UnmarshalTestFixture(AppStoreReceiptFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Errorf("Parse encountered unexpected error: %v", err) - } - if err := p7.Verify(); err != nil { - t.Errorf("Verify failed with error: %v", err) - } -} - -var AppStoreReceiptFixture = ` ------BEGIN PKCS7----- -MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI -hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC -AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL -AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE -AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy -NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz -gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh -YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx -WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a -8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m -MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91 -Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE -AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz -AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM -AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB -AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw -MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt -MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa -MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD -AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR -BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl -bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv -cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw -MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl -IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs -ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg -SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid -xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3 -k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ -uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb -XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI -HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw -LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0 -MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G -A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw -ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u -IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j -ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k -aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0 -aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3 -LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA -MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH -bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut -F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg -BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz -p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o -bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF -MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL -MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB -MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT -MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg -RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl -dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0 -U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV -CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8 -V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl -d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q -arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj -gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF -MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw -JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/ -BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z -viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N -w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ -TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V -AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur -+cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR -pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw -CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew -HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET -MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne -+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz -y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ -Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS -C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB -hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB -djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp -R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/ -CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC -ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB -thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz -c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk -IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5 -IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3 -DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU -sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ -fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr -1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk -wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq -xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ -BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX -b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y -bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide -NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa -ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv -MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD -VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/ -QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD -4B85NkrgvQsWAQ== ------END PKCS7-----` - -func TestVerifyApkEcdsa(t *testing.T) { - fixture := UnmarshalTestFixture(ApkEcdsaFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Errorf("Parse encountered unexpected error: %v", err) - } - p7.Content, err = base64.StdEncoding.DecodeString(ApkEcdsaContent) - if err != nil { - t.Errorf("Failed to decode base64 signature file: %v", err) - } - if err := p7.Verify(); err != nil { - t.Errorf("Verify failed with error: %v", err) - } -} - -var ApkEcdsaFixture = `-----BEGIN PKCS7----- -MIIDAgYJKoZIhvcNAQcCoIIC8zCCAu8CAQExDzANBglghkgBZQMEAgMFADALBgkq -hkiG9w0BBwGgggH3MIIB8zCCAVSgAwIBAgIJAOxXdFsvm3YiMAoGCCqGSM49BAME -MBIxEDAOBgNVBAMMB2VjLXA1MjEwHhcNMTYwMzMxMTUzMTIyWhcNNDMwODE3MTUz -MTIyWjASMRAwDgYDVQQDDAdlYy1wNTIxMIGbMBAGByqGSM49AgEGBSuBBAAjA4GG -AAQAYX95sSjPEQqgyLD04tNUyq9y/w8seblOpfqa/Amx6H4GFdrjGXX0YTfXKr9G -hAyIyQSnNrIg0zDlWQUbBPRW4CYBLFOg1pUn1NBhKFD4NtO1KWvYtNOYDegFjRCP -B0p+fEXDbq8QFDYvlh+NZUJ16+ih8XNIf1C29xuLEqN6oKOnAvajUDBOMB0GA1Ud -DgQWBBT/Ra3kz60gQ7tYk3byZckcLabt8TAfBgNVHSMEGDAWgBT/Ra3kz60gQ7tY -k3byZckcLabt8TAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMEA4GMADCBiAJCAP39 -hYLsWk2H84oEw+HJqGGjexhqeD3vSO1mWhopripE/81oy3yV10puYoJe11xDSfcD -j2VfNCHazuXO3kSxGA/1AkIBLUJxp/WYbYzhBGKr6lcxczKI/wuMfkZ6vL+0EMJV -A/2uEoeqvnl7BsdkicyaOBNEADijuVdaPPIWzKClt9OaVxExgdAwgc0CAQEwHzAS -MRAwDgYDVQQDDAdlYy1wNTIxAgkA7Fd0Wy+bdiIwDQYJYIZIAWUDBAIDBQAwCgYI -KoZIzj0EAwQEgYswgYgCQgD1pVSNo7qTm9A6tpt3SU2yRa+xpJAnUbpZ+Gu36B71 -JnQBUzRgTGevniqHpyagi7b2zjWh1uvfz9FfrITUwGMddgJCAPjiBRcl7rKpxmZn -V1MvcJOX41xRSJu1wmBiYXqaJarL+gQ/Wl7RYsMtqLjmNColvLaHNxCaWOO/8nAE -Hg0OMA60 ------END PKCS7-----` - -var ApkEcdsaContent = `U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KU0hBLTUxMi1EaWdlc3QtTWFuaWZlc3Q6IFAvVDRqSWtTMjQvNzFxeFE2WW1MeEtNdkRPUUF0WjUxR090dFRzUU9yemhHRQ0KIEpaUGVpWUtyUzZYY090bStYaWlFVC9uS2tYdWVtUVBwZ2RBRzFKUzFnPT0NCkNyZWF0ZWQtQnk6IDEuMCAoQW5kcm9pZCBTaWduQXBrKQ0KDQpOYW1lOiBBbmRyb2lkTWFuaWZlc3QueG1sDQpTSEEtNTEyLURpZ2VzdDogcm9NbWVQZmllYUNQSjFJK2VzMVpsYis0anB2UXowNHZqRWVpL2U0dkN1ald0VVVWSHEzMkNXDQogMUxsOHZiZGMzMCtRc1FlN29ibld4dmhLdXN2K3c1a2c9PQ0KDQpOYW1lOiByZXNvdXJjZXMuYXJzYw0KU0hBLTUxMi1EaWdlc3Q6IG5aYW1aUzlPZTRBRW41cEZaaCtoQ1JFT3krb1N6a3hHdU5YZU0wUFF6WGVBVlVQV3hSVzFPYQ0KIGVLbThRbXdmTmhhaS9HOEcwRUhIbHZEQWdlcy9HUGtBPT0NCg0KTmFtZTogY2xhc3Nlcy5kZXgNClNIQS01MTItRGlnZXN0OiBlbWlDQld2bkVSb0g2N2lCa3EwcUgrdm5tMkpaZDlMWUNEV051N3RNYzJ3bTRtV0dYSUVpWmcNCiBWZkVPV083MFRlZnFjUVhldkNtN2hQMnRpT0U3Y0w5UT09DQoNCg==` - -func TestVerifyFirefoxAddon(t *testing.T) { - fixture := UnmarshalTestFixture(FirefoxAddonFixture) - p7, err := Parse(fixture.Input) - if err != nil { - t.Errorf("Parse encountered unexpected error: %v", err) - } - p7.Content = FirefoxAddonContent - certPool := x509.NewCertPool() - certPool.AppendCertsFromPEM(FirefoxAddonRootCert) - if err := p7.VerifyWithChain(certPool); err != nil { - t.Errorf("Verify failed with error: %v", err) - } - // Verify the certificate chain to make sure the identified root - // is the one we expect - ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) - if ee == nil { - t.Errorf("No end-entity certificate found for signer") - } - signingTime, _ := time.Parse(time.RFC3339, "2017-02-23 09:06:16-05:00") - chains, err := verifyCertChain(ee, p7.Certificates, certPool, signingTime) - if err != nil { - t.Error(err) - } - if len(chains) != 1 { - t.Errorf("Expected to find one chain, but found %d", len(chains)) - } - if len(chains[0]) != 3 { - t.Errorf("Expected to find three certificates in chain, but found %d", len(chains[0])) - } - if chains[0][0].Subject.CommonName != "tabscope@xuldev.org" { - t.Errorf("Expected to find EE certificate with subject 'tabscope@xuldev.org', but found '%s'", chains[0][0].Subject.CommonName) - } - if chains[0][1].Subject.CommonName != "production-signing-ca.addons.mozilla.org" { - t.Errorf("Expected to find intermediate certificate with subject 'production-signing-ca.addons.mozilla.org', but found '%s'", chains[0][1].Subject.CommonName) - } - if chains[0][2].Subject.CommonName != "root-ca-production-amo" { - t.Errorf("Expected to find root certificate with subject 'root-ca-production-amo', but found '%s'", chains[0][2].Subject.CommonName) - } -} - -var FirefoxAddonContent = []byte(`Signature-Version: 1.0 -MD5-Digest-Manifest: KjRavc6/KNpuT1QLcB/Gsg== -SHA1-Digest-Manifest: 5Md5nUg+U7hQ/UfzV+xGKWOruVI= - -`) - -var FirefoxAddonFixture = ` ------BEGIN PKCS7----- -MIIQTAYJKoZIhvcNAQcCoIIQPTCCEDkCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3 -DQEHAaCCDL0wggW6MIIDoqADAgECAgYBVpobWVwwDQYJKoZIhvcNAQELBQAwgcUx -CzAJBgNVBAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYD -VQQLEyZNb3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8G -A1UEAxMocHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0 -MDIGCSqGSIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxh -LmNvbTAeFw0xNjA4MTcyMDA0NThaFw0yMTA4MTYyMDA0NThaMHYxEzARBgNVBAsT -ClByb2R1Y3Rpb24xCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3 -MQ8wDQYDVQQKEwZBZGRvbnMxCzAJBgNVBAgTAkNBMRwwGgYDVQQDFBN0YWJzY29w -ZUB4dWxkZXYub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv6e0 -mPD8dt4J8HTNNq4ODns2DV6Weh1hllCIFvOeu1u3UrR03st0BMY8OXYwr/NvRVjg -bA8gRySWAL+XqLzbhtXNeNegAoxrF+3mYY5rJjsLj/FGI6P6OXjngqwgm9VTBl7m -jh/KXBSwYoUcavJo6cmk8sCFwoblyQiv+tsWaUCOI6zMzubNtIS+GFvET9y/VZMP -j6mk8O10wBgJF5MMtA19va3qXy7aCZ7DnZp1l3equd/L6t324TtXoqx6xWQKo6TM -I0mcTlKvm6TKegTGBCyGn3JRARoIJv4AW1qqgyaHXf9EoY2pKT8Avkri5++NuSJ6 -jtO4k/diBA2MZU20U0KGffYZNTxKDqd6XtI6y1tJPd/OWRFyU+mHntkcm9sar7L3 -nPKujHRox2re10ec1WBnJE3PjlAoesNjxzp+xs2mGGc8DX9NuWn+1uK9xmgGIIMl -OFfyQ4s0G6hKp5goFcrFZxmexu0ZahOs8vZf8xDBW7yR1zToQElOXHvrscM386os -kOF9IxQZfcCoPuNQVg1haCONNkx0oau3RQQlOSAZtC79b+rBjQ5JYfjRLYAworf2 -xQaprCh33TD1dTBrvzEbCGszgkN53Vqh5TFBjbU/NyldOkGvK8Xf6WhT5u+aftnV -lbuE2McAg6x1AlloUZq6PNTBpz7zypcIISnQ+y8CAwEAATANBgkqhkiG9w0BAQsF -AAOCAgEAIBoo2+OEYNCgP/IbUj9azaf/lde1q4AK/uTMoUeS5WcrXd8aqA0Y1qV7 -xUALgDQAExXgqcOMGu4mPMaoZDgwGI4Tj7XPJQq5Z5zYxpRf/Wtzae33T9BF6QPW -v5xiRYuol+FbEtqRHZqxDWtIrd1MWBy3wjO3pLPdzDM9jWh+HLxdGWThJszaZp3T -CqsOx+l9W0Q7qM5ioZpHStgXDfhw38Lg++kLnzcX9MqsjYyezdwE4krqW6hK3+4S -0LZE4dTgsy8JULkyAF3HrPWEXESnD7c4mx6owZe+BNDK5hsVM/obAqH7sJq/igbM -5N1l832p/ws8l5xKOr3qBWSzWn6u7ExvqG6Ckh0foJOVXvzGqvrXcoiBGV8S9Z7c -DghUvMt6b0pZ0ildRCHfTUz7eG3g4MhfbjupR7b+L9FWEJhcd/H0dxpw7SKYha/n -ePuRL7MXmbW8WLMqO/ImxzL8TPOB3pUg3nITfubV6gpPBmn+0nwbqYUmggJuwgvK -I2GpN2Ny6EErZy17EEgyhJygJZMj+UzQjC781xxsl3ljpYEqqwgRLIZBSBUD5dXj -XBuU24w162SeSyHZzkBbuv6lr52pqoZyFrG29DCHECgO9ZmNWgSpiWSkh+vExAG7 -wNs0y61t2HUG+BCMGPQ9sOzouyTfrnLVAWwzswGftFYQfoIBeJIwggb7MIIE46AD -AgECAgMQAAIwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UEBhMCVVMxHDAaBgNVBAoT -E01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1 -Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNhLXByb2R1Y3Rp -b24tYW1vMB4XDTE1MDMxNzIzNTI0MloXDTI1MDMxNDIzNTI0MlowgcUxCzAJBgNV -BAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZN -b3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8GA1UEAxMo -cHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0MDIGCSqG -SIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxhLmNvbTCC -AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMLMM9m2HBLhCiO9mhljpehT -hxpzlCnxluzDZ51I/H7MvBbIvZBm9zSpHdffubSsak2qYE69d+ebTa/CK83WIosM -24/2Qp7n/GGaPJcCC4Y3JkrCsgA8+wV2MbFlKSv+qMdvI/sE3BPYDMCjVPMhHmIP -XaPWd42OoHpI8R3GGUtVnR3Hm76pa2+v6TwgeMiO8om+ogGufiyv6FNMZ5NuY1Z9 -aLNEvehnAzSfddQyki+6FJd7XkgZbP7pb1Kl8yYgiy4piBerJ9H09uPehffE3Ell -3cApQL3+0kjaUX4scMjuNQDMKziRZkYgJAM+qA9WA5Jn77AjerQBWQeEev1PWHYh -0IDlgS/a0bjKmVjNZYG6adrY/R5/whzWGFCIE1UfhPm6PdN0557qvF838C2RFHsI -KzV6KQf0chMjpa02tPaIctjVhnDQZZNKm2ZfLOt9kQ57Is/e6KxH7pYMit46+s99 -lYM7ZquvWbK19b1Ili/6S1BxSzd3wztgfN5jGsc+jCCYLm+AcVtfNKc8cFZHXKrB -CwhGmdbWDSBCicZNA7FKJpO3oIx26VPF2XUldA/T5Mh/POGLilK3t9m9qbjEyDp1 -EwoBToOR/aMrdnNYvSWp0g/GHMzSfJjjXyAqrZY2itam/IJd8r9FoRAzevPt/zTX -BET3INoiCDGRH0XrxUYtAgMGVTejggE5MIIBNTAMBgNVHRMEBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdHxf -FKXipZLjs20GqIUdNkQXH4gwgagGA1UdIwSBoDCBnYAUs7zqWHSr4W54KrKrnCMe -qGMsl7ehgYGkfzB9MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jw -b3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5n -IFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW+CAQEwMwYJ -YIZIAYb4QgEEBCYWJGh0dHA6Ly9hZGRvbnMubW96aWxsYS5vcmcvY2EvY3JsLnBl -bTANBgkqhkiG9w0BAQwFAAOCAgEArde/fdjb7TE0eH7Ij7xU4JbcSyhY3cQhVYCw -Fg+Q/2pj+NAfazcjUuLWA0Y/YZs9HOx6j+ZAqO4C/xfMP4RDs9IypxvzHDU6SXgD -RK6uOKtS07HXLcXgFUBvJEQhbT/h5+IQOA4/GcpCshfD6iyiBBi+IocR+tnKPCuZ -T3m1t60Eja/MkPKG/Gx8vSodHvlTTsJ2GzjUEANveCZOnlAdp9fjTvFZny9qqnbg -sfVbuTqKndbCFW5QLXfkna6jBqMrY0+CpMYY2oJ5gwpHbE/7hhukjxGCTcpv7r/O -M53bb/DZnybDlLLepacljvz7DBA1O1FFtEhf9MR+vyvmBpniAyKQhqG2hsVGurE1 -nBcE+oteZWar2lMp6+etDAb9DRC+jZv0aEQs2o/qQwyD8AGquLgBsJq5Jz3gGxzn -4r3vGu2lV8VdzIm0C8sOFSWTmTZxQmJbF8xSsQBojnsvEah4DPER+eAt6qKolaWe -s4drJQjzFyC7HJn2VqalpCwbe9CdMB7eRqzeP6GujJBi80/gx0pAysUtuKKpH5IJ -WbXAOszfrjb3CaHafYZDnwPoOfj74ogFzjt2f54jwnU+ET/byfjZ7J8SLH316C1V -HrvFXcTzyMV4aRluVPjPg9x1G58hMIbeuT4GpwQUNdJ9uL8t65v0XwG2t6Y7jpRO -sFVxBtgxggNXMIIDUwIBATCB0DCBxTELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE01v -emlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1Y3Rp -b24gU2lnbmluZyBTZXJ2aWNlMTEwLwYDVQQDEyhwcm9kdWN0aW9uLXNpZ25pbmct -Y2EuYWRkb25zLm1vemlsbGEub3JnMTQwMgYJKoZIhvcNAQkBFiVzZXJ2aWNlcy1v -cHMrYWRkb25zaWduaW5nQG1vemlsbGEuY29tAgYBVpobWVwwCQYFKw4DAhoFAKBd -MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE2MDgx -NzIwMDQ1OFowIwYJKoZIhvcNAQkEMRYEFAxlGvNFSx+Jqj70haE8b7UZk+2GMA0G -CSqGSIb3DQEBAQUABIICADsDlrucYRgwq9o2QSsO6X6cRa5Zu6w+1n07PTIyc1zn -Pi1cgkkWZ0kZBHDrJ5CY33yRQPl6I1tHXaq7SkOSdOppKhpUmBiKZxQRAZR21QHk -R3v1XS+st/o0N+0btv3YoplUifLIwtH89oolxqlStChELu7FuOBretdhx/z12ytA -EhIIS53o/XjDL7XKJbQA02vzOtOC/Eq6p8BI7F3y6pvtmJIRkeGv+u6ssJa6g5q8 -74w8hHXaH94Z9+hDPqjNWlsXJHgPdAKiEjzDz9oLkvDyX4Pd8JMK5ILskirpG+hj -Q8jkTc5oYwyuSlBAUTGxW6ZbuOrtfVZvOVtRL/ixuiFiVlJ+JOQOxrtK19ukamsI -iacFlbLgiA7w0HCtm2DsT9aL67/1e4rJ0lv0MjnQYUMmKQy7g0Gd3+nQPU9pn+Lf -Z/UmSNWiJ8Csc/seDMyzT6jrzcGPfoSVaUowH0wGrI9If1snwcr+mMg7dWRGf1fm -y/dcVSzed0ax4LqDmike1EshU+51cKWWlnhyNHK4KH+0fNsBQ0c6clrFpGx9MPmV -YXie6C+LWkh5x12RU0sJt/SmSZV6q9VliIkX+yY3jBrC/pKgRahtcIyq46Da1E6K -lc15Euur3NfGow+nott0Z8XutpYdK/2vBKcIh9JOdkd+oe6pcIP6hnhHRp53wqmG ------END PKCS7-----` - -var FirefoxAddonRootCert = []byte(` ------BEGIN CERTIFICATE----- -MIIGYTCCBEmgAwIBAgIBATANBgkqhkiG9w0BAQwFADB9MQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBB -TU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2Et -cHJvZHVjdGlvbi1hbW8wHhcNMTUwMzE3MjI1MzU3WhcNMjUwMzE0MjI1MzU3WjB9 -MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0G -A1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAd -BgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW8wggIgMA0GCSqGSIb3DQEBAQUA -A4ICDQAwggIIAoICAQC0u2HXXbrwy36+MPeKf5jgoASMfMNz7mJWBecJgvlTf4hH -JbLzMPsIUauzI9GEpLfHdZ6wzSyFOb4AM+D1mxAWhuZJ3MDAJOf3B1Rs6QorHrl8 -qqlNtPGqepnpNJcLo7JsSqqE3NUm72MgqIHRgTRsqUs+7LIPGe7262U+N/T0LPYV -Le4rZ2RDHoaZhYY7a9+49mHOI/g2YFB+9yZjE+XdplT2kBgA4P8db7i7I0tIi4b0 -B0N6y9MhL+CRZJyxdFe2wBykJX14LsheKsM1azHjZO56SKNrW8VAJTLkpRxCmsiT -r08fnPyDKmaeZ0BtsugicdipcZpXriIGmsZbI12q5yuwjSELdkDV6Uajo2n+2ws5 -uXrP342X71WiWhC/dF5dz1LKtjBdmUkxaQMOP/uhtXEKBrZo1ounDRQx1j7+SkQ4 -BEwjB3SEtr7XDWGOcOIkoJZWPACfBLC3PJCBWjTAyBlud0C5n3Cy9regAAnOIqI1 -t16GU2laRh7elJ7gPRNgQgwLXeZcFxw6wvyiEcmCjOEQ6PM8UQjthOsKlszMhlKw -vjyOGDoztkqSBy/v+Asx7OW2Q7rlVfKarL0mREZdSMfoy3zTgtMVCM0vhNl6zcvf -5HNNopoEdg5yuXo2chZ1p1J+q86b0G5yJRMeT2+iOVY2EQ37tHrqUURncCy4uwIB -A6OB7TCB6jAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAWBgNVHSUBAf8E -DDAKBggrBgEFBQcDAzCBkgYDVR0jBIGKMIGHoYGBpH8wfTELMAkGA1UEBhMCVVMx -HDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEg -QU1PIFByb2R1Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNh -LXByb2R1Y3Rpb24tYW1vggEBMB0GA1UdDgQWBBSzvOpYdKvhbngqsqucIx6oYyyX -tzANBgkqhkiG9w0BAQwFAAOCAgEAaNSRYAaECAePQFyfk12kl8UPLh8hBNidP2H6 -KT6O0vCVBjxmMrwr8Aqz6NL+TgdPmGRPDDLPDpDJTdWzdj7khAjxqWYhutACTew5 -eWEaAzyErbKQl+duKvtThhV2p6F6YHJ2vutu4KIciOMKB8dslIqIQr90IX2Usljq -8Ttdyf+GhUmazqLtoB0GOuESEqT4unX6X7vSGu1oLV20t7t5eCnMMYD67ZBn0YIU -/cm/+pan66hHrja+NeDGF8wabJxdqKItCS3p3GN1zUGuJKrLykxqbOp/21byAGog -Z1amhz6NHUcfE6jki7sM7LHjPostU5ZWs3PEfVVgha9fZUhOrIDsyXEpCWVa3481 -LlAq3GiUMKZ5DVRh9/Nvm4NwrTfB3QkQQJCwfXvO9pwnPKtISYkZUqhEqvXk5nBg -QCkDSLDjXTx39naBBGIVIqBtKKuVTla9enngdq692xX/CgO6QJVrwpqdGjebj5P8 -5fNZPABzTezG3Uls5Vp+4iIWVAEDkK23cUj3c/HhE+Oo7kxfUeu5Y1ZV3qr61+6t -ZARKjbu1TuYQHf0fs+GwID8zeLc2zJL7UzcHFwwQ6Nda9OJN4uPAuC/BKaIpxCLL -26b24/tRam4SJjqpiq20lynhUrmTtt6hbG3E1Hpy3bmkt2DYnuMFwEx2gfXNcnbT -wNuvFqc= ------END CERTIFICATE-----`) - -// sign a document with openssl and verify the signature with pkcs7. -// this uses a chain of root, intermediate and signer cert, where the -// intermediate is added to the certs but the root isn't. -func TestSignWithOpenSSLAndVerify(t *testing.T) { - content := []byte(` -A ship in port is safe, -but that's not what ships are built for. --- Grace Hopper`) - // write the content to a temp file - tmpContentFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_content") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpContentFile.Name(), content, 0755) - sigalgs := []x509.SignatureAlgorithm{ - x509.SHA1WithRSA, - x509.SHA256WithRSA, - x509.SHA512WithRSA, - x509.ECDSAWithSHA1, - x509.ECDSAWithSHA256, - x509.ECDSAWithSHA384, - x509.ECDSAWithSHA512, - } - for _, sigalgroot := range sigalgs { - rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) - if err != nil { - t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) - } - truststore := x509.NewCertPool() - truststore.AddCert(rootCert.Certificate) - for _, sigalginter := range sigalgs { - interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) - if err != nil { - t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) - } - // write the intermediate cert to a temp file - tmpInterCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_intermediate") - if err != nil { - t.Fatal(err) - } - fd, err := os.OpenFile(tmpInterCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - t.Fatal(err) - } - pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: interCert.Certificate.Raw}) - fd.Close() - for _, sigalgsigner := range sigalgs { - signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) - if err != nil { - t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) - } - - // write the signer cert to a temp file - tmpSignerCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signer") - if err != nil { - t.Fatal(err) - } - fd, err = os.OpenFile(tmpSignerCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - t.Fatal(err) - } - pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Certificate.Raw}) - fd.Close() - - // write the signer key to a temp file - tmpSignerKeyFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_key") - if err != nil { - t.Fatal(err) - } - fd, err = os.OpenFile(tmpSignerKeyFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - t.Fatal(err) - } - var derKey []byte - priv := *signerCert.PrivateKey - switch priv := priv.(type) { - case *rsa.PrivateKey: - derKey = x509.MarshalPKCS1PrivateKey(priv) - pem.Encode(fd, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derKey}) - case *ecdsa.PrivateKey: - derKey, err = x509.MarshalECPrivateKey(priv) - if err != nil { - t.Fatal(err) - } - pem.Encode(fd, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derKey}) - } - fd.Close() - - // write the root cert to a temp file - tmpSignedFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signature") - if err != nil { - t.Fatal(err) - } - // call openssl to sign the content - opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", - "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), - "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), - "-certfile", tmpInterCertFile.Name(), "-outform", "PEM") - out, err := opensslCMD.CombinedOutput() - if err != nil { - t.Fatalf("test %s/%s/%s: openssl command failed with %s: %s", sigalgroot, sigalginter, sigalgsigner, err, out) - } - - // verify the signed content - pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) - if err != nil { - t.Fatal(err) - } - derBlock, _ := pem.Decode(pemSignature) - if derBlock == nil { - break - } - p7, err := Parse(derBlock.Bytes) - if err != nil { - t.Fatalf("Parse encountered unexpected error: %v", err) - } - if err := p7.VerifyWithChain(truststore); err != nil { - t.Fatalf("Verify failed with error: %v", err) - } - // Verify the certificate chain to make sure the identified root - // is the one we expect - ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) - if ee == nil { - t.Fatalf("No end-entity certificate found for signer") - } - chains, err := verifyCertChain(ee, p7.Certificates, truststore, time.Now()) - if err != nil { - t.Fatal(err) - } - if len(chains) != 1 { - t.Fatalf("Expected to find one chain, but found %d", len(chains)) - } - if len(chains[0]) != 3 { - t.Fatalf("Expected to find three certificates in chain, but found %d", len(chains[0])) - } - if chains[0][0].Subject.CommonName != "PKCS7 Test Signer Cert" { - t.Fatalf("Expected to find EE certificate with subject 'PKCS7 Test Signer Cert', but found '%s'", chains[0][0].Subject.CommonName) - } - if chains[0][1].Subject.CommonName != "PKCS7 Test Intermediate Cert" { - t.Fatalf("Expected to find intermediate certificate with subject 'PKCS7 Test Intermediate Cert', but found '%s'", chains[0][1].Subject.CommonName) - } - if chains[0][2].Subject.CommonName != "PKCS7 Test Root CA" { - t.Fatalf("Expected to find root certificate with subject 'PKCS7 Test Root CA', but found '%s'", chains[0][2].Subject.CommonName) - } - os.Remove(tmpSignerCertFile.Name()) // clean up - os.Remove(tmpSignerKeyFile.Name()) // clean up - } - os.Remove(tmpInterCertFile.Name()) // clean up - } - } - os.Remove(tmpContentFile.Name()) // clean up -} - -func TestDSASignWithOpenSSLAndVerify(t *testing.T) { - content := []byte(` -A ship in port is safe, -but that's not what ships are built for. --- Grace Hopper`) - // write the content to a temp file - tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpContentFile.Name(), content, 0755) - - // write the signer cert to a temp file - tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) - - // write the signer key to a temp file - tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key") - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755) - - tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature") - if err != nil { - t.Fatal(err) - } - // call openssl to sign the content - opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1", - "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), - "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), - "-certfile", tmpSignerCertFile.Name(), "-outform", "PEM") - out, err := opensslCMD.CombinedOutput() - if err != nil { - t.Fatalf("openssl command failed with %s: %s", err, out) - } - - // verify the signed content - pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) - if err != nil { - t.Fatal(err) - } - fmt.Printf("%s\n", pemSignature) - derBlock, _ := pem.Decode(pemSignature) - if derBlock == nil { - t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name()) - } - p7, err := Parse(derBlock.Bytes) - if err != nil { - t.Fatalf("Parse encountered unexpected error: %v", err) - } - if err := p7.Verify(); err != nil { - t.Fatalf("Verify failed with error: %v", err) - } - os.Remove(tmpSignerCertFile.Name()) // clean up - os.Remove(tmpSignerKeyFile.Name()) // clean up - os.Remove(tmpContentFile.Name()) // clean up -} - -var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY----- -MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS -PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl -pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith -1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L -vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3 -zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo -g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE= ------END PRIVATE KEY-----`) - -var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE----- -MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV -bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD -VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du -MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r -bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE -ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC -AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD -Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE -exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii -Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4 -V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI -puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl -nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp -rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt -1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT -ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G -CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688 -qzy/7yePTlhlpj+ahMM= ------END CERTIFICATE-----`) diff --git a/scep/scep.go b/scep/scep.go index bc46cce7..6a636aee 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -6,7 +6,9 @@ import ( microscep "github.com/micromdm/scep/scep" - "github.com/smallstep/certificates/scep/pkcs7" + //"github.com/smallstep/certificates/scep/pkcs7" + + "go.mozilla.org/pkcs7" ) // SCEP OIDs From 812e1c72181cdf8d49ff7125a304198897623baa Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Feb 2021 12:32:43 +0100 Subject: [PATCH 032/291] Add handling of options --- scep/api/api.go | 35 ++++++++++++----------------------- scep/authority.go | 40 +++++++++++++++++++++++----------------- scep/common.go | 22 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 scep/common.go diff --git a/scep/api/api.go b/scep/api/api.go index ec959519..c73bb53a 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -235,12 +235,12 @@ func (h *Handler) GetCACert(w http.ResponseWriter, r *http.Request, scepResponse func (h *Handler) GetCACaps(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { - ctx := r.Context() + //ctx := r.Context() - _, err := ProvisionerFromContext(ctx) - if err != nil { - return err - } + // _, err := ProvisionerFromContext(ctx) + // if err != nil { + // return err + // } // TODO: get the actual capabilities from provisioner config scepResponse.Data = formatCapabilities(defaultCapabilities) @@ -250,6 +250,8 @@ func (h *Handler) GetCACaps(w http.ResponseWriter, r *http.Request, scepResponse func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepRequest SCEPRequest, scepResponse SCEPResponse) error { + ctx := r.Context() + msg, err := microscep.ParsePKIMessage(scepRequest.Message) if err != nil { return err @@ -262,7 +264,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque Raw: msg.Raw, } - if err := h.Auth.DecryptPKIEnvelope(pkimsg); err != nil { + if err := h.Auth.DecryptPKIEnvelope(ctx, pkimsg); err != nil { return err } @@ -271,7 +273,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque } csr := pkimsg.CSRReqMessage.CSR - id, err := createKeyIdentifier(csr.PublicKey) + subjectKeyID, err := createKeyIdentifier(csr.PublicKey) if err != nil { return err } @@ -286,16 +288,17 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque Subject: csr.Subject, NotBefore: time.Now().Add(-600).UTC(), NotAfter: time.Now().AddDate(0, 0, days).UTC(), - SubjectKeyId: id, + SubjectKeyId: subjectKeyID, KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{ x509.ExtKeyUsageClientAuth, }, SignatureAlgorithm: csr.SignatureAlgorithm, EmailAddresses: csr.EmailAddresses, + DNSNames: csr.DNSNames, } - certRep, err := h.Auth.SignCSR(pkimsg, template) + certRep, err := h.Auth.SignCSR(ctx, pkimsg, template) if err != nil { return err } @@ -381,17 +384,3 @@ func contentHeader(operation string, certNum int) string { return "text/plain" } } - -// ProvisionerFromContext searches the context for a provisioner. Returns the -// provisioner or an error. -func ProvisionerFromContext(ctx context.Context) (scep.Provisioner, error) { - val := ctx.Value(acme.ProvisionerContextKey) - if val == nil { - return nil, errors.New("provisioner expected in request context") - } - p, ok := val.(scep.Provisioner) - if !ok || p == nil { - return nil, errors.New("provisioner in context is not a SCEP provisioner") - } - return p, nil -} diff --git a/scep/authority.go b/scep/authority.go index 864ecbba..cb191d34 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -2,6 +2,7 @@ package scep import ( "bytes" + "context" "crypto/x509" "errors" "fmt" @@ -54,8 +55,8 @@ type Interface interface { GetCACertificates() ([]*x509.Certificate, error) //GetSigningKey() (*rsa.PrivateKey, error) - DecryptPKIEnvelope(*PKIMessage) error - SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) + DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error + SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) } // Authority is the layer that handles all SCEP interactions. @@ -156,7 +157,7 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { } // DecryptPKIEnvelope decrypts an enveloped message -func (a *Authority) DecryptPKIEnvelope(msg *PKIMessage) error { +func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error { data := msg.Raw @@ -221,14 +222,19 @@ func (a *Authority) DecryptPKIEnvelope(msg *PKIMessage) error { return nil } -// SignCSR creates an x509.Certificate based on a template and Cert Authority credentials +// SignCSR creates an x509.Certificate based on a CSR template and Cert Authority credentials // returns a new PKIMessage with CertRep data //func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) { -func (a *Authority) SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { +func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { + + p, err := ProvisionerFromContext(ctx) + if err != nil { + return nil, err + } // check if CSRReqMessage has already been decrypted if msg.CSRReqMessage.CSR == nil { - if err := a.DecryptPKIEnvelope(msg); err != nil { + if err := a.DecryptPKIEnvelope(ctx, msg); err != nil { return nil, err } } @@ -238,13 +244,17 @@ func (a *Authority) SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMe // Template data data := x509util.NewTemplateData() data.SetCommonName(csr.Subject.CommonName) - //data.Set(x509util.SANsKey, sans) + data.SetSANs(csr.DNSNames) - // templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) - // if err != nil { - // return nil, ServerInternalErr(errors.Wrapf(err, "error creating template options from ACME provisioner")) - // } - // signOps = append(signOps, templateOptions) + // TODO: proper options + opts := provisioner.SignOptions{} + signOps := []provisioner.SignOption{} + + templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) + if err != nil { + return nil, fmt.Errorf("error creating template options from SCEP provisioner") + } + signOps = append(signOps, templateOptions) // // Create and store a new certificate. // certChain, err := auth.Sign(csr, provisioner.SignOptions{ @@ -255,11 +265,7 @@ func (a *Authority) SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMe // return nil, ServerInternalErr(errors.Wrapf(err, "error generating certificate for order %s", o.ID)) // } - // TODO: proper options - signOps := provisioner.SignOptions{} - signOps2 := []provisioner.SignOption{} - - certs, err := a.signAuth.Sign(csr, signOps, signOps2...) + certs, err := a.signAuth.Sign(csr, opts, signOps...) if err != nil { return nil, err } diff --git a/scep/common.go b/scep/common.go new file mode 100644 index 00000000..123c8d82 --- /dev/null +++ b/scep/common.go @@ -0,0 +1,22 @@ +package scep + +import ( + "context" + "errors" + + "github.com/smallstep/certificates/acme" +) + +// ProvisionerFromContext searches the context for a SCEP provisioner. +// Returns the provisioner or an error. +func ProvisionerFromContext(ctx context.Context) (Provisioner, error) { + val := ctx.Value(acme.ProvisionerContextKey) + if val == nil { + return nil, errors.New("provisioner expected in request context") + } + p, ok := val.(Provisioner) + if !ok || p == nil { + return nil, errors.New("provisioner in context is not a SCEP provisioner") + } + return p, nil +} From da65f46d0f99f08bf1b0e83649fd184672e1b0bb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Feb 2021 14:00:47 +0100 Subject: [PATCH 033/291] Add AuthorizeSign method to SCEP authority --- authority/provisioner/scep.go | 18 ++++++++++++- scep/authority.go | 48 +++++++++++++++++------------------ scep/provisioner.go | 3 ++- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 6cdfa69f..10414b5e 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -1,6 +1,7 @@ package provisioner import ( + "context" "time" "github.com/pkg/errors" @@ -13,7 +14,7 @@ type SCEP struct { Type string `json:"type"` Name string `json:"name"` - // ForceCN bool `json:"forceCN,omitempty"` + ForceCN bool `json:"forceCN,omitempty"` Options *Options `json:"options,omitempty"` Claims *Claims `json:"claims,omitempty"` claimer *Claimer @@ -75,6 +76,21 @@ func (s *SCEP) Init(config Config) (err error) { return err } +// AuthorizeSign does not do any validation, because all validation is handled +// in the SCEP protocol. This method returns a list of modifiers / constraints +// on the resulting certificate. +func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) { + return []SignOption{ + // modifiers / withOptions + newProvisionerExtensionOption(TypeSCEP, s.Name, ""), + newForceCNOption(s.ForceCN), + profileDefaultDuration(s.claimer.DefaultTLSCertDuration()), + // validators + defaultPublicKeyValidator{}, + newValidityValidator(s.claimer.MinTLSCertDuration(), s.claimer.MaxTLSCertDuration()), + }, nil +} + // Interface guards var ( _ Interface = (*SCEP)(nil) diff --git a/scep/authority.go b/scep/authority.go index cb191d34..c9a2cdb8 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -6,8 +6,6 @@ import ( "crypto/x509" "errors" "fmt" - "math/big" - "math/rand" "github.com/smallstep/certificates/authority/provisioner" database "github.com/smallstep/certificates/db" @@ -53,8 +51,6 @@ type Interface interface { // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetCACertificates() ([]*x509.Certificate, error) - //GetSigningKey() (*rsa.PrivateKey, error) - DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) } @@ -201,12 +197,12 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq: csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope) if err != nil { - return fmt.Errorf("parse CSR from pkiEnvelope") + return fmt.Errorf("parse CSR from pkiEnvelope: %w", err) } // check for challengePassword cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) if err != nil { - return fmt.Errorf("scep: parse challenge password in pkiEnvelope") + return fmt.Errorf("scep: parse challenge password in pkiEnvelope: %w", err) } msg.CSRReqMessage = µscep.CSRReqMessage{ RawDecrypted: msg.pkiEnvelope, @@ -227,6 +223,11 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err //func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) { func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { + // TODO: intermediate storage of the request? In SCEP it's possible to request a csr/certificate + // to be signed, which can be performed asynchronously / out-of-band. In that case a client can + // poll for the status. It seems to be similar as what can happen in ACME, so might want to model + // the implementation after the one in the ACME authority. Requires storage, etc. + p, err := ProvisionerFromContext(ctx) if err != nil { return nil, err @@ -245,32 +246,32 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509 data := x509util.NewTemplateData() data.SetCommonName(csr.Subject.CommonName) data.SetSANs(csr.DNSNames) + data.SetCertificateRequest(csr) - // TODO: proper options - opts := provisioner.SignOptions{} - signOps := []provisioner.SignOption{} + // Get authorizations from the SCEP provisioner. + ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) + signOps, err := p.AuthorizeSign(ctx, "") + if err != nil { + return nil, fmt.Errorf("error retrieving authorization options from SCEP provisioner: %w", err) + } + + opts := provisioner.SignOptions{ + // NotBefore: provisioner.NewTimeDuration(o.NotBefore), + // NotAfter: provisioner.NewTimeDuration(o.NotAfter), + } templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) if err != nil { - return nil, fmt.Errorf("error creating template options from SCEP provisioner") + return nil, fmt.Errorf("error creating template options from SCEP provisioner: %w", err) } signOps = append(signOps, templateOptions) - // // Create and store a new certificate. - // certChain, err := auth.Sign(csr, provisioner.SignOptions{ - // NotBefore: provisioner.NewTimeDuration(o.NotBefore), - // NotAfter: provisioner.NewTimeDuration(o.NotAfter), - // }, signOps...) - // if err != nil { - // return nil, ServerInternalErr(errors.Wrapf(err, "error generating certificate for order %s", o.ID)) - // } - - certs, err := a.signAuth.Sign(csr, opts, signOps...) + certChain, err := a.signAuth.Sign(csr, opts, signOps...) if err != nil { - return nil, err + return nil, fmt.Errorf("error generating certificate for order %w", err) } - cert := certs[0] + cert := certChain[0] // fmt.Println("CERT") // fmt.Println(cert) @@ -278,9 +279,6 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509 // fmt.Println(cert.Issuer) // fmt.Println(cert.Subject) - serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? - cert.SerialNumber = serial - // create a degenerate cert structure deg, err := DegenerateCertificates([]*x509.Certificate{cert}) if err != nil { diff --git a/scep/provisioner.go b/scep/provisioner.go index d543d453..64a787d4 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -1,6 +1,7 @@ package scep import ( + "context" "time" "github.com/smallstep/certificates/authority/provisioner" @@ -9,7 +10,7 @@ import ( // Provisioner is an interface that implements a subset of the provisioner.Interface -- // only those methods required by the SCEP api/authority. type Provisioner interface { - // AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) + AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) GetName() string DefaultTLSCertDuration() time.Duration GetOptions() *provisioner.Options From 30d3a26c20f14e3940f3551e0fe0afe55916e7da Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Feb 2021 18:07:50 +0100 Subject: [PATCH 034/291] Remove x509 template from API --- scep/api/api.go | 64 ++++++++--------------------------------------- scep/authority.go | 48 ++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index c73bb53a..16278075 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -2,19 +2,14 @@ package api import ( "context" - "crypto" - "crypto/sha1" "crypto/x509" "encoding/base64" "errors" "fmt" "io" "io/ioutil" - "math/big" - "math/rand" "net/http" "strings" - "time" "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" @@ -252,53 +247,29 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque ctx := r.Context() - msg, err := microscep.ParsePKIMessage(scepRequest.Message) + microMsg, err := microscep.ParsePKIMessage(scepRequest.Message) if err != nil { return err } - pkimsg := &scep.PKIMessage{ - TransactionID: msg.TransactionID, - MessageType: msg.MessageType, - SenderNonce: msg.SenderNonce, - Raw: msg.Raw, + msg := &scep.PKIMessage{ + TransactionID: microMsg.TransactionID, + MessageType: microMsg.MessageType, + SenderNonce: microMsg.SenderNonce, + Raw: microMsg.Raw, } - if err := h.Auth.DecryptPKIEnvelope(ctx, pkimsg); err != nil { + if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { return err } - if pkimsg.MessageType == microscep.PKCSReq { + if msg.MessageType == microscep.PKCSReq { // TODO: CSR validation, like challenge password } - csr := pkimsg.CSRReqMessage.CSR - subjectKeyID, err := createKeyIdentifier(csr.PublicKey) - if err != nil { - return err - } + csr := msg.CSRReqMessage.CSR - serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? - - days := 40 - - // TODO: use information from provisioner, like claims - template := &x509.Certificate{ - SerialNumber: serial, - Subject: csr.Subject, - NotBefore: time.Now().Add(-600).UTC(), - NotAfter: time.Now().AddDate(0, 0, days).UTC(), - SubjectKeyId: subjectKeyID, - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageClientAuth, - }, - SignatureAlgorithm: csr.SignatureAlgorithm, - EmailAddresses: csr.EmailAddresses, - DNSNames: csr.DNSNames, - } - - certRep, err := h.Auth.SignCSR(ctx, pkimsg, template) + certRep, err := h.Auth.SignCSR(ctx, csr, msg) if err != nil { return err } @@ -323,20 +294,6 @@ func certName(cert *x509.Certificate) string { return string(cert.Signature) } -// createKeyIdentifier creates an identifier for public keys -// according to the first method in RFC5280 section 4.2.1.2. -func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { - - keyBytes, err := x509.MarshalPKIXPublicKey(pub) - if err != nil { - return nil, err - } - - id := sha1.Sum(keyBytes) - - return id[:], nil -} - func formatCapabilities(caps []string) []byte { return []byte(strings.Join(caps, "\r\n")) } @@ -354,6 +311,7 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) error { var ( // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 + // TODO: move capabilities to Authority or Provisioner, so that they can be configured? defaultCapabilities = []string{ "Renewal", "SHA-1", diff --git a/scep/authority.go b/scep/authority.go index c9a2cdb8..013550a6 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -3,6 +3,8 @@ package scep import ( "bytes" "context" + "crypto" + "crypto/sha1" "crypto/x509" "errors" "fmt" @@ -52,7 +54,7 @@ type Interface interface { GetCACertificates() ([]*x509.Certificate, error) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error - SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) + SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) } // Authority is the layer that handles all SCEP interactions. @@ -221,7 +223,8 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err // SignCSR creates an x509.Certificate based on a CSR template and Cert Authority credentials // returns a new PKIMessage with CertRep data //func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) { -func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { +//func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { +func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) { // TODO: intermediate storage of the request? In SCEP it's possible to request a csr/certificate // to be signed, which can be performed asynchronously / out-of-band. In that case a client can @@ -238,9 +241,32 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509 if err := a.DecryptPKIEnvelope(ctx, msg); err != nil { return nil, err } + csr = msg.CSRReqMessage.CSR } - csr := msg.CSRReqMessage.CSR + // subjectKeyID, err := createKeyIdentifier(csr.PublicKey) + // if err != nil { + // return nil, err + // } + + // serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? + // days := 40 // TODO: days + + // // TODO: use information from provisioner, like claims + // template := &x509.Certificate{ + // SerialNumber: serial, + // Subject: csr.Subject, + // NotBefore: time.Now().Add(-600).UTC(), + // NotAfter: time.Now().AddDate(0, 0, days).UTC(), + // SubjectKeyId: subjectKeyID, + // KeyUsage: x509.KeyUsageDigitalSignature, + // ExtKeyUsage: []x509.ExtKeyUsage{ + // x509.ExtKeyUsageClientAuth, + // }, + // SignatureAlgorithm: csr.SignatureAlgorithm, + // EmailAddresses: csr.EmailAddresses, + // DNSNames: csr.DNSNames, + // } // Template data data := x509util.NewTemplateData() @@ -278,6 +304,8 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509 // fmt.Println(fmt.Sprintf("%T", cert)) // fmt.Println(cert.Issuer) // fmt.Println(cert.Subject) + // fmt.Println(cert.SerialNumber) + // fmt.Println(string(cert.SubjectKeyId)) // create a degenerate cert structure deg, err := DegenerateCertificates([]*x509.Certificate{cert}) @@ -365,6 +393,20 @@ func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) { return degenerate, nil } +// createKeyIdentifier creates an identifier for public keys +// according to the first method in RFC5280 section 4.2.1.2. +func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { + + keyBytes, err := x509.MarshalPKIXPublicKey(pub) + if err != nil { + return nil, err + } + + id := sha1.Sum(keyBytes) + + return id[:], nil +} + // Interface guards var ( _ Interface = (*Authority)(nil) From a191319da9c8f36249bf343e6e048a0bec88f78c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 27 Feb 2021 00:34:50 +0100 Subject: [PATCH 035/291] Improve SCEP API logic and error handling --- api/errors.go | 3 + scep/api/api.go | 207 +++++++++++++++++++++++++----------------------- scep/common.go | 11 ++- scep/errors.go | 19 +++++ 4 files changed, 141 insertions(+), 99 deletions(-) create mode 100644 scep/errors.go diff --git a/api/errors.go b/api/errors.go index 085d05cf..f9bcb199 100644 --- a/api/errors.go +++ b/api/errors.go @@ -11,6 +11,7 @@ import ( "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" + "github.com/smallstep/certificates/scep" ) // WriteError writes to w a JSON representation of the given error. @@ -22,6 +23,8 @@ func WriteError(w http.ResponseWriter, err error) { case *mgmt.Error: mgmt.WriteError(w, k) return + case *scep.Error: + w.Header().Set("Content-Type", "text/plain") default: w.Header().Set("Content-Type", "application/json") } diff --git a/scep/api/api.go b/scep/api/api.go index 16278075..f2d11fb7 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -11,7 +11,6 @@ import ( "net/http" "strings" - "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/scep" @@ -27,6 +26,30 @@ const ( // TODO: add other (more optional) operations and handling ) +const maxPayloadSize = 2 << 20 + +type nextHTTP = func(http.ResponseWriter, *http.Request) + +var ( + // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 + // TODO: move capabilities to Authority or Provisioner, so that they can be configured? + defaultCapabilities = []string{ + "Renewal", + "SHA-1", + "SHA-256", + "AES", + "DES3", + "SCEPStandard", + "POSTPKIOperation", + } +) + +const ( + certChainHeader = "application/x-x509-ca-ra-cert" + leafHeader = "application/x-x509-ca-cert" + pkiOpHeader = "application/x-pki-message" +) + // SCEPRequest is a SCEP server request. type SCEPRequest struct { Operation string @@ -35,10 +58,10 @@ type SCEPRequest struct { // SCEPResponse is a SCEP server response. type SCEPResponse struct { - Operation string - CACertNum int - Data []byte - Err error + Operation string + CACertNum int + Data []byte + Certificate *x509.Certificate } // Handler is the SCEP request handler. @@ -64,60 +87,65 @@ func (h *Handler) Route(r api.Router) { } +// Get handles all SCEP GET requests func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { - scepRequest, err := decodeSCEPRequest(r) + request, err := decodeSCEPRequest(r) if err != nil { - fmt.Println(err) - fmt.Println("not a scep get request") - w.WriteHeader(500) + writeError(w, fmt.Errorf("not a scep get request: %w", err)) + return } - scepResponse := SCEPResponse{Operation: scepRequest.Operation} + ctx := r.Context() + var response SCEPResponse - switch scepRequest.Operation { + switch request.Operation { case opnGetCACert: - err := h.GetCACert(w, r, scepResponse) - if err != nil { - fmt.Println(err) - } - + response, err = h.GetCACert(ctx) case opnGetCACaps: - err := h.GetCACaps(w, r, scepResponse) - if err != nil { - fmt.Println(err) - } + response, err = h.GetCACaps(ctx) case opnPKIOperation: - + // TODO: implement the GET for PKI operation default: + err = fmt.Errorf("unknown operation: %s", request.Operation) + } + if err != nil { + writeError(w, fmt.Errorf("get request failed: %w", err)) + return } + + writeSCEPResponse(w, response) } +// Post handles all SCEP POST requests func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { - scepRequest, err := decodeSCEPRequest(r) + request, err := decodeSCEPRequest(r) if err != nil { - fmt.Println(err) - fmt.Println("not a scep post request") - w.WriteHeader(500) + writeError(w, fmt.Errorf("not a scep post request: %w", err)) + return } - scepResponse := SCEPResponse{Operation: scepRequest.Operation} + ctx := r.Context() + var response SCEPResponse - switch scepRequest.Operation { + switch request.Operation { case opnPKIOperation: - err := h.PKIOperation(w, r, scepRequest, scepResponse) - if err != nil { - fmt.Println(err) - } + response, err = h.PKIOperation(ctx, request) default: + err = fmt.Errorf("unknown operation: %s", request.Operation) + } + if err != nil { + writeError(w, fmt.Errorf("post request failed: %w", err)) + return } -} + api.LogCertificate(w, response.Certificate) -const maxPayloadSize = 2 << 20 + writeSCEPResponse(w, response) +} func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { @@ -169,8 +197,6 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { } } -type nextHTTP = func(http.ResponseWriter, *http.Request) - // lookupProvisioner loads the provisioner associated with the request. // Responds 404 if the provisioner does not exist. func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { @@ -189,67 +215,69 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) if err != nil { - api.WriteError(w, err) + writeError(w, err) return } - scepProvisioner, ok := p.(*provisioner.SCEP) + provisioner, ok := p.(*provisioner.SCEP) if !ok { - api.WriteError(w, errors.New("provisioner must be of type SCEP")) + writeError(w, errors.New("provisioner must be of type SCEP")) return } ctx := r.Context() - ctx = context.WithValue(ctx, acme.ProvisionerContextKey, scep.Provisioner(scepProvisioner)) + ctx = context.WithValue(ctx, scep.ProvisionerContextKey, scep.Provisioner(provisioner)) next(w, r.WithContext(ctx)) } } -func (h *Handler) GetCACert(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { +// GetCACert returns the CA certificates in a SCEP response +func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) { certs, err := h.Auth.GetCACertificates() if err != nil { - return err + return SCEPResponse{}, err } if len(certs) == 0 { - scepResponse.CACertNum = 0 - scepResponse.Err = errors.New("missing CA Cert") - } else if len(certs) == 1 { - scepResponse.Data = certs[0].Raw - scepResponse.CACertNum = 1 + return SCEPResponse{}, errors.New("missing CA cert") + } + + response := SCEPResponse{Operation: opnGetCACert} + response.CACertNum = len(certs) + + if len(certs) == 1 { + response.Data = certs[0].Raw } else { data, err := microscep.DegenerateCertificates(certs) - scepResponse.CACertNum = len(certs) - scepResponse.Data = data - scepResponse.Err = err + if err != nil { + return SCEPResponse{}, err + } + response.Data = data } - return writeSCEPResponse(w, scepResponse) + return response, nil } -func (h *Handler) GetCACaps(w http.ResponseWriter, r *http.Request, scepResponse SCEPResponse) error { - - //ctx := r.Context() +// GetCACaps returns the CA capabilities in a SCEP response +func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) { - // _, err := ProvisionerFromContext(ctx) - // if err != nil { - // return err - // } + response := SCEPResponse{Operation: opnGetCACaps} // TODO: get the actual capabilities from provisioner config - scepResponse.Data = formatCapabilities(defaultCapabilities) + response.Data = formatCapabilities(defaultCapabilities) - return writeSCEPResponse(w, scepResponse) + return response, nil } -func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepRequest SCEPRequest, scepResponse SCEPResponse) error { +// PKIOperation performs PKI operations and returns a SCEP response +func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPResponse, error) { - ctx := r.Context() + response := SCEPResponse{Operation: opnPKIOperation} - microMsg, err := microscep.ParsePKIMessage(scepRequest.Message) + microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { - return err + return SCEPResponse{}, err } msg := &scep.PKIMessage{ @@ -260,7 +288,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque } if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { - return err + return SCEPResponse{}, err } if msg.MessageType == microscep.PKCSReq { @@ -271,7 +299,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque certRep, err := h.Auth.SignCSR(ctx, csr, msg) if err != nil { - return err + return SCEPResponse{}, err } // //cert := certRep.CertRepMessage.Certificate @@ -280,11 +308,10 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque // // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not // // TODO: store the new cert for CN locally; should go into the DB - scepResponse.Data = certRep.Raw - - api.LogCertificate(w, certRep.Certificate) + response.Data = certRep.Raw + response.Certificate = certRep.Certificate - return writeSCEPResponse(w, scepResponse) + return response, nil } func certName(cert *x509.Certificate) string { @@ -299,40 +326,26 @@ func formatCapabilities(caps []string) []byte { } // writeSCEPResponse writes a SCEP response back to the SCEP client. -func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) error { - if response.Err != nil { - http.Error(w, response.Err.Error(), http.StatusInternalServerError) - return nil +func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { + w.Header().Set("Content-Type", contentHeader(response)) + _, err := w.Write(response.Data) + if err != nil { + writeError(w, fmt.Errorf("error when writing scep response: %w", err)) // This could end up as an error again } - w.Header().Set("Content-Type", contentHeader(response.Operation, response.CACertNum)) - w.Write(response.Data) - return nil } -var ( - // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 - // TODO: move capabilities to Authority or Provisioner, so that they can be configured? - defaultCapabilities = []string{ - "Renewal", - "SHA-1", - "SHA-256", - "AES", - "DES3", - "SCEPStandard", - "POSTPKIOperation", +func writeError(w http.ResponseWriter, err error) { + scepError := &scep.Error{ + Err: fmt.Errorf("post request failed: %w", err), + Status: http.StatusInternalServerError, // TODO: make this a param? } -) - -const ( - certChainHeader = "application/x-x509-ca-ra-cert" - leafHeader = "application/x-x509-ca-cert" - pkiOpHeader = "application/x-pki-message" -) + api.WriteError(w, scepError) +} -func contentHeader(operation string, certNum int) string { - switch operation { +func contentHeader(r SCEPResponse) string { + switch r.Operation { case opnGetCACert: - if certNum > 1 { + if r.CACertNum > 1 { return certChainHeader } return leafHeader diff --git a/scep/common.go b/scep/common.go index 123c8d82..ca87841f 100644 --- a/scep/common.go +++ b/scep/common.go @@ -3,14 +3,21 @@ package scep import ( "context" "errors" +) + +// ContextKey is the key type for storing and searching for SCEP request +// essentials in the context of a request. +type ContextKey string - "github.com/smallstep/certificates/acme" +const ( + // ProvisionerContextKey provisioner key + ProvisionerContextKey = ContextKey("provisioner") ) // ProvisionerFromContext searches the context for a SCEP provisioner. // Returns the provisioner or an error. func ProvisionerFromContext(ctx context.Context) (Provisioner, error) { - val := ctx.Value(acme.ProvisionerContextKey) + val := ctx.Value(ProvisionerContextKey) if val == nil { return nil, errors.New("provisioner expected in request context") } diff --git a/scep/errors.go b/scep/errors.go new file mode 100644 index 00000000..52fff8ae --- /dev/null +++ b/scep/errors.go @@ -0,0 +1,19 @@ +package scep + +// Error is an SCEP error type +type Error struct { + // Type ProbType + // Detail string + Err error + Status int + // Sub []*Error + // Identifier *Identifier +} + +// Error implements the error interface. +func (e *Error) Error() string { + // if e.Err == nil { + // return e.Detail + // } + return e.Err.Error() +} From 5df60c5a9b4466e5250a0b5e34896377d0172b26 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 5 Mar 2021 12:40:42 +0100 Subject: [PATCH 036/291] Add support for multiple SCEP provisioners Similarly to how ACME suppors multiple provisioners, it's now possible to load the right provisioner based on the URL. --- ca/ca.go | 2 +- scep/api/api.go | 41 +++++++++++++++++++---------------- scep/authority.go | 55 ++++++++++++++++++++++++++++++++++++++++++++--- scep/errors.go | 6 +++--- 4 files changed, 78 insertions(+), 26 deletions(-) diff --git a/ca/ca.go b/ca/ca.go index fa71abd6..a60346ee 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -212,7 +212,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) } - // helpful routine for logging all routes // + // helpful routine for logging all routes //dumpRoutes(mux) // Add monitoring if configured diff --git a/scep/api/api.go b/scep/api/api.go index f2d11fb7..4df5d6a1 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -9,8 +9,10 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "strings" + "github.com/go-chi/chi" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/scep" @@ -76,14 +78,10 @@ func New(scepAuth scep.Interface) api.RouterHandler { // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { - //getLink := h.Auth.GetLinkExplicit - //fmt.Println(getLink) + getLink := h.Auth.GetLinkExplicit - //r.MethodFunc("GET", "/bla", h.baseURLFromRequest(h.lookupProvisioner(nil))) - //r.MethodFunc("GET", getLink(acme.NewNonceLink, "{provisionerID}", false, nil), h.baseURLFromRequest(h.lookupProvisioner(h.addNonce(h.GetNonce)))) - - r.MethodFunc(http.MethodGet, "/", h.lookupProvisioner(h.Get)) - r.MethodFunc(http.MethodPost, "/", h.lookupProvisioner(h.Post)) + r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) + r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) } @@ -202,16 +200,12 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - // name := chi.URLParam(r, "provisionerID") - // provisionerID, err := url.PathUnescape(name) - // if err != nil { - // api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) - // return - // } - - // TODO: make this configurable; and we might want to look at being able to provide multiple, - // like the ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. - provisionerID := "scep1" + name := chi.URLParam(r, "provisionerID") + provisionerID, err := url.PathUnescape(name) + if err != nil { + api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) + return + } p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) if err != nil { @@ -275,6 +269,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response := SCEPResponse{Operation: opnPKIOperation} + fmt.Println("BEFORE PARSING") + microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { return SCEPResponse{}, err @@ -287,7 +283,12 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe Raw: microMsg.Raw, } + fmt.Println("len raw:", len(microMsg.Raw)) + + fmt.Println("AFTER PARSING") + if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { + fmt.Println("ERROR IN DECRYPTPKIENVELOPE") return SCEPResponse{}, err } @@ -311,6 +312,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response.Data = certRep.Raw response.Certificate = certRep.Certificate + fmt.Println("HERE!!!") + return response, nil } @@ -336,8 +339,8 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { func writeError(w http.ResponseWriter, err error) { scepError := &scep.Error{ - Err: fmt.Errorf("post request failed: %w", err), - Status: http.StatusInternalServerError, // TODO: make this a param? + Message: err.Error(), + Status: http.StatusInternalServerError, // TODO: make this a param? } api.WriteError(w, scepError) } diff --git a/scep/authority.go b/scep/authority.go index 013550a6..e5d1ea48 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -8,6 +8,7 @@ import ( "crypto/x509" "errors" "fmt" + "net/url" "github.com/smallstep/certificates/authority/provisioner" database "github.com/smallstep/certificates/db" @@ -55,6 +56,8 @@ type Interface interface { GetCACertificates() ([]*x509.Certificate, error) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) + + GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string } // Authority is the layer that handles all SCEP interactions. @@ -130,6 +133,44 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error return a.signAuth.LoadProvisionerByID(id) } +// GetLinkExplicit returns the requested link from the directory. +func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, inputs ...string) string { + // TODO: taken from ACME; move it to directory (if we need a directory in SCEP)? + return a.getLinkExplicit(provName, abs, baseURL, inputs...) +} + +// getLinkExplicit returns an absolute or partial path to the given resource and a base +// URL dynamically obtained from the request for which the link is being calculated. +func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *url.URL, inputs ...string) string { + + // TODO: do we need to provide a way to provide a different suffix/base? + // Like "/cgi-bin/pkiclient.exe"? Or would it be enough to have that as the name? + link := fmt.Sprintf("/%s", provisionerName) + + if abs { + // Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351 + u := url.URL{} + if baseURL != nil { + u = *baseURL + } + + // If no Scheme is set, then default to https. + if u.Scheme == "" { + u.Scheme = "https" + } + + // If no Host is set, then use the default (first DNS attr in the ca.json). + if u.Host == "" { + u.Host = a.dns + } + + u.Path = a.prefix + link + return u.String() + } + + return link +} + // GetCACertificates returns the certificate (chain) for the CA func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { @@ -164,6 +205,8 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return err } + fmt.Println("len content:", len(p7.Content)) + var tID microscep.TransactionID if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil { return err @@ -176,11 +219,17 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err msg.p7 = p7 + //p7c, err := pkcs7.Parse(p7.Content) p7c, err := pkcs7.Parse(p7.Content) if err != nil { return err } + fmt.Println(tID) + fmt.Println(msgType) + + fmt.Println("len p7c content:", len(p7c.Content)) + envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter) if err != nil { return err @@ -308,7 +357,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // fmt.Println(string(cert.SubjectKeyId)) // create a degenerate cert structure - deg, err := DegenerateCertificates([]*x509.Certificate{cert}) + deg, err := degenerateCertificates([]*x509.Certificate{cert}) if err != nil { return nil, err } @@ -380,8 +429,8 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return crepMsg, nil } -// DegenerateCertificates creates degenerate certificates pkcs#7 type -func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) { +// degenerateCertificates creates degenerate certificates pkcs#7 type +func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { var buf bytes.Buffer for _, cert := range certs { buf.Write(cert.Raw) diff --git a/scep/errors.go b/scep/errors.go index 52fff8ae..8454e16d 100644 --- a/scep/errors.go +++ b/scep/errors.go @@ -4,8 +4,8 @@ package scep type Error struct { // Type ProbType // Detail string - Err error - Status int + Message string `json:"message"` + Status int `json:"-"` // Sub []*Error // Identifier *Identifier } @@ -15,5 +15,5 @@ func (e *Error) Error() string { // if e.Err == nil { // return e.Detail // } - return e.Err.Error() + return e.Message } From 75cd3ab0ac503f64271362dc5da7d370649a8adc Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 6 Mar 2021 22:35:41 +0100 Subject: [PATCH 037/291] Change to a fixed fork of go.mozilla.org/pkcs7 Hopefully this will be a temporary change until the fix is merged in the upstream module. --- go.mod | 7 +++++-- go.sum | 2 ++ scep/api/api.go | 13 ++----------- scep/authority.go | 7 ------- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index ea867f8f..5ccc3aa8 100644 --- a/go.mod +++ b/go.mod @@ -21,10 +21,11 @@ require ( github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/nosql v0.3.6 github.com/urfave/cli v1.22.4 + go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.2.0 go.step.sm/crypto v0.8.3 - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 google.golang.org/api v0.33.0 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 google.golang.org/grpc v1.32.0 @@ -34,3 +35,5 @@ require ( // replace github.com/smallstep/nosql => ../nosql // replace go.step.sm/crypto => ../crypto + +replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index 934bcfb6..4883af5e 100644 --- a/go.sum +++ b/go.sum @@ -267,6 +267,8 @@ github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/I github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= +github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/scep/api/api.go b/scep/api/api.go index 4df5d6a1..fc134a95 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -82,7 +82,6 @@ func (h *Handler) Route(r api.Router) { r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) - } // Get handles all SCEP GET requests @@ -103,7 +102,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { case opnGetCACaps: response, err = h.GetCACaps(ctx) case opnPKIOperation: - // TODO: implement the GET for PKI operation + // TODO: implement the GET for PKI operation? Default CACAPS doesn't specify this is in use, though default: err = fmt.Errorf("unknown operation: %s", request.Operation) } @@ -170,6 +169,7 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { if _, ok := query["message"]; ok { message = query.Get("message") } + // TODO: verify this; it seems like it should be StdEncoding instead of URLEncoding decodedMessage, err := base64.URLEncoding.DecodeString(message) if err != nil { return SCEPRequest{}, err @@ -269,8 +269,6 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response := SCEPResponse{Operation: opnPKIOperation} - fmt.Println("BEFORE PARSING") - microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { return SCEPResponse{}, err @@ -283,12 +281,7 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe Raw: microMsg.Raw, } - fmt.Println("len raw:", len(microMsg.Raw)) - - fmt.Println("AFTER PARSING") - if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { - fmt.Println("ERROR IN DECRYPTPKIENVELOPE") return SCEPResponse{}, err } @@ -312,8 +305,6 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response.Data = certRep.Raw response.Certificate = certRep.Certificate - fmt.Println("HERE!!!") - return response, nil } diff --git a/scep/authority.go b/scep/authority.go index e5d1ea48..a1d47700 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -205,8 +205,6 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return err } - fmt.Println("len content:", len(p7.Content)) - var tID microscep.TransactionID if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil { return err @@ -225,11 +223,6 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return err } - fmt.Println(tID) - fmt.Println(msgType) - - fmt.Println("len p7c content:", len(p7c.Content)) - envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter) if err != nil { return err From 017e56c9fb29a0b3d55096c57e7b60de085503ee Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 6 Mar 2021 23:24:49 +0100 Subject: [PATCH 038/291] Remove some duplicate and unnecessary logic --- scep/api/api.go | 9 +++++++++ scep/authority.go | 25 ++----------------------- scep/scep.go | 2 +- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index fc134a95..09ffddd0 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -16,6 +16,7 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/scep" + "go.mozilla.org/pkcs7" microscep "github.com/micromdm/scep/scep" ) @@ -269,16 +270,24 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response := SCEPResponse{Operation: opnPKIOperation} + // parse the message using microscep implementation microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { return SCEPResponse{}, err } + p7, err := pkcs7.Parse(microMsg.Raw) + if err != nil { + return SCEPResponse{}, err + } + + // copy over properties to our internal PKIMessage msg := &scep.PKIMessage{ TransactionID: microMsg.TransactionID, MessageType: microMsg.MessageType, SenderNonce: microMsg.SenderNonce, Raw: microMsg.Raw, + P7: p7, } if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { diff --git a/scep/authority.go b/scep/authority.go index a1d47700..a61c093a 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -198,27 +198,7 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { // DecryptPKIEnvelope decrypts an enveloped message func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error { - data := msg.Raw - - p7, err := pkcs7.Parse(data) - if err != nil { - return err - } - - var tID microscep.TransactionID - if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil { - return err - } - - var msgType microscep.MessageType - if err := p7.UnmarshalSignedAttribute(oidSCEPmessageType, &msgType); err != nil { - return err - } - - msg.p7 = p7 - - //p7c, err := pkcs7.Parse(p7.Content) - p7c, err := pkcs7.Parse(p7.Content) + p7c, err := pkcs7.Parse(msg.P7.Content) if err != nil { return err } @@ -253,7 +233,6 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err CSR: csr, ChallengePassword: cp, } - //msg.Certificate = p7.Certificates[0] // TODO: check if this is necessary to add (again) return nil case microscep.GetCRL, microscep.GetCert, microscep.CertPoll: return fmt.Errorf("not implemented") //errNotImplemented @@ -355,7 +334,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return nil, err } - e7, err := pkcs7.Encrypt(deg, msg.p7.Certificates) + e7, err := pkcs7.Encrypt(deg, msg.P7.Certificates) if err != nil { return nil, err } diff --git a/scep/scep.go b/scep/scep.go index 6a636aee..7fc4c261 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -35,7 +35,7 @@ type PKIMessage struct { Raw []byte // parsed - p7 *pkcs7.PKCS7 + P7 *pkcs7.PKCS7 // decrypted enveloped content pkiEnvelope []byte From 3b86550dbfffb136ad9280f3f89e1f723c9f8e45 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sun, 7 Mar 2021 00:30:37 +0100 Subject: [PATCH 039/291] Add support for challenge password --- authority/provisioner/scep.go | 16 +++++++++++----- scep/api/api.go | 29 +++++++++++++++++++++-------- scep/authority.go | 20 ++++++++++++++++++++ scep/provisioner.go | 1 + 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 10414b5e..031241c4 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -14,10 +14,11 @@ type SCEP struct { Type string `json:"type"` Name string `json:"name"` - ForceCN bool `json:"forceCN,omitempty"` - Options *Options `json:"options,omitempty"` - Claims *Claims `json:"claims,omitempty"` - claimer *Claimer + ForceCN bool `json:"forceCN,omitempty"` + ChallengePassword string `json:"challenge,omitempty"` + Options *Options `json:"options,omitempty"` + Claims *Claims `json:"claims,omitempty"` + claimer *Claimer } // GetID returns the provisioner unique identifier. @@ -76,7 +77,7 @@ func (s *SCEP) Init(config Config) (err error) { return err } -// AuthorizeSign does not do any validation, because all validation is handled +// AuthorizeSign does not do any verification, because all verification is handled // in the SCEP protocol. This method returns a list of modifiers / constraints // on the resulting certificate. func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) { @@ -91,6 +92,11 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e }, nil } +// GetChallengePassword returns the challenge password +func (s *SCEP) GetChallengePassword() string { + return s.ChallengePassword +} + // Interface guards var ( _ Interface = (*SCEP)(nil) diff --git a/scep/api/api.go b/scep/api/api.go index 09ffddd0..3f49f7a3 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -80,7 +80,6 @@ func New(scepAuth scep.Interface) api.RouterHandler { // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { getLink := h.Auth.GetLinkExplicit - r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) } @@ -140,6 +139,8 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { return } + // TODO: fix cases in which we get here and there's no certificate (i.e. wrong password, waiting for cert, etc) + // We should generate an appropriate response and it should be signed api.LogCertificate(w, response.Certificate) writeSCEPResponse(w, response) @@ -238,8 +239,10 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) { return SCEPResponse{}, errors.New("missing CA cert") } - response := SCEPResponse{Operation: opnGetCACert} - response.CACertNum = len(certs) + response := SCEPResponse{ + Operation: opnGetCACert, + CACertNum: len(certs), + } if len(certs) == 1 { response.Data = certs[0].Raw @@ -268,8 +271,6 @@ func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) { // PKIOperation performs PKI operations and returns a SCEP response func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPResponse, error) { - response := SCEPResponse{Operation: opnPKIOperation} - // parse the message using microscep implementation microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { @@ -295,7 +296,15 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe } if msg.MessageType == microscep.PKCSReq { - // TODO: CSR validation, like challenge password + + challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword) + if err != nil { + return SCEPResponse{}, err + } + + if !challengeMatches { + return SCEPResponse{}, errors.New("wrong password provided") + } } csr := msg.CSRReqMessage.CSR @@ -311,8 +320,11 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe // // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not // // TODO: store the new cert for CN locally; should go into the DB - response.Data = certRep.Raw - response.Certificate = certRep.Certificate + response := SCEPResponse{ + Operation: opnPKIOperation, + Data: certRep.Raw, + Certificate: certRep.Certificate, + } return response, nil } @@ -338,6 +350,7 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { } func writeError(w http.ResponseWriter, err error) { + // TODO: this probably needs to use SCEP specific errors (i.e. failInfo) scepError := &scep.Error{ Message: err.Error(), Status: http.StatusInternalServerError, // TODO: make this a param? diff --git a/scep/authority.go b/scep/authority.go index a61c093a..a2b3b8b9 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -56,6 +56,7 @@ type Interface interface { GetCACertificates() ([]*x509.Certificate, error) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) + MatchChallengePassword(ctx context.Context, password string) (bool, error) GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string } @@ -401,6 +402,25 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return crepMsg, nil } +// MatchChallengePassword verifies a SCEP challenge password +func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) { + + p, err := ProvisionerFromContext(ctx) + if err != nil { + return false, err + } + + if p.GetChallengePassword() == password { + return true, nil + } + + // TODO: support dynamic challenges, i.e. a list of challenges instead of one? + // That's probably a bit harder to configure, though; likely requires some data store + // that can be interacted with more easily, via some internal API, for example. + + return false, nil +} + // degenerateCertificates creates degenerate certificates pkcs#7 type func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { var buf bytes.Buffer diff --git a/scep/provisioner.go b/scep/provisioner.go index 64a787d4..5c665a1f 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -14,4 +14,5 @@ type Provisioner interface { GetName() string DefaultTLSCertDuration() time.Duration GetOptions() *provisioner.Options + GetChallengePassword() string } From 4fe7179b95682f1fdd157fc92a274c09567901b0 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sun, 7 Mar 2021 00:50:00 +0100 Subject: [PATCH 040/291] Add support for configuring capabilities (cacaps) --- authority/provisioner/scep.go | 6 +++++ scep/api/api.go | 22 ++++-------------- scep/authority.go | 43 +++++++++++++++++++++++++++++++++-- scep/provisioner.go | 1 + 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 031241c4..49d057ff 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -16,6 +16,7 @@ type SCEP struct { ForceCN bool `json:"forceCN,omitempty"` ChallengePassword string `json:"challenge,omitempty"` + Capabilities []string `json:"capabilities,omitempty"` Options *Options `json:"options,omitempty"` Claims *Claims `json:"claims,omitempty"` claimer *Claimer @@ -97,6 +98,11 @@ func (s *SCEP) GetChallengePassword() string { return s.ChallengePassword } +// GetCapabilities returns the CA capabilities +func (s *SCEP) GetCapabilities() []string { + return s.Capabilities +} + // Interface guards var ( _ Interface = (*SCEP)(nil) diff --git a/scep/api/api.go b/scep/api/api.go index 3f49f7a3..f6294f06 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -33,20 +33,6 @@ const maxPayloadSize = 2 << 20 type nextHTTP = func(http.ResponseWriter, *http.Request) -var ( - // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 - // TODO: move capabilities to Authority or Provisioner, so that they can be configured? - defaultCapabilities = []string{ - "Renewal", - "SHA-1", - "SHA-256", - "AES", - "DES3", - "SCEPStandard", - "POSTPKIOperation", - } -) - const ( certChainHeader = "application/x-x509-ca-ra-cert" leafHeader = "application/x-x509-ca-cert" @@ -260,10 +246,12 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) { // GetCACaps returns the CA capabilities in a SCEP response func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) { - response := SCEPResponse{Operation: opnGetCACaps} + caps := h.Auth.GetCACaps(ctx) - // TODO: get the actual capabilities from provisioner config - response.Data = formatCapabilities(defaultCapabilities) + response := SCEPResponse{ + Operation: opnGetCACaps, + Data: formatCapabilities(caps), + } return response, nil } diff --git a/scep/authority.go b/scep/authority.go index a2b3b8b9..f8e5a76f 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -57,6 +57,7 @@ type Interface interface { DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) MatchChallengePassword(ctx context.Context, password string) (bool, error) + GetCACaps(ctx context.Context) []string GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string } @@ -128,6 +129,19 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { }, nil } +var ( + // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 + defaultCapabilities = []string{ + "Renewal", + "SHA-1", + "SHA-256", + "AES", + "DES3", + "SCEPStandard", + "POSTPKIOperation", + } +) + // LoadProvisionerByID calls out to the SignAuthority interface to load a // provisioner by ID. func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { @@ -155,9 +169,9 @@ func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *u u = *baseURL } - // If no Scheme is set, then default to https. + // If no Scheme is set, then default to http (in case of SCEP) if u.Scheme == "" { - u.Scheme = "https" + u.Scheme = "http" } // If no Host is set, then use the default (first DNS attr in the ca.json). @@ -188,6 +202,9 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { // // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in // https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now. + // + // The certificate to use should probably depend on the (configured) Provisioner and may + // use a distinct certificate, apart from the intermediate. if a.intermediateCertificate == nil { return nil, errors.New("no intermediate certificate available in SCEP authority") @@ -421,6 +438,28 @@ func (a *Authority) MatchChallengePassword(ctx context.Context, password string) return false, nil } +// GetCACaps returns the CA capabilities +func (a *Authority) GetCACaps(ctx context.Context) []string { + + p, err := ProvisionerFromContext(ctx) + if err != nil { + return defaultCapabilities + } + + caps := p.GetCapabilities() + if len(caps) == 0 { + return defaultCapabilities + } + + // TODO: validate the caps? Ensure they are the right format according to RFC? + // TODO: ensure that the capabilities are actually "enforced"/"verified" in code too: + // check that only parts of the spec are used in the implementation belonging to the capabilities. + // For example for renewals, which we could disable in the provisioner, should then also + // not be reported in cacaps operation. + + return caps +} + // degenerateCertificates creates degenerate certificates pkcs#7 type func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { var buf bytes.Buffer diff --git a/scep/provisioner.go b/scep/provisioner.go index 5c665a1f..e1b7f8b1 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -15,4 +15,5 @@ type Provisioner interface { DefaultTLSCertDuration() time.Duration GetOptions() *provisioner.Options GetChallengePassword() string + GetCapabilities() []string } From f0050e5ca98be94f3c2c614d08fd83f498ee6812 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 10 Mar 2021 21:13:05 +0100 Subject: [PATCH 041/291] Add signed failure responses --- scep/api/api.go | 44 +++++++++++++++++++--------- scep/authority.go | 73 +++++++++++++++++++++++++++++++++++++++++++++++ scep/scep.go | 13 +++++++++ 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index f6294f06..ced6fa05 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -34,9 +34,9 @@ const maxPayloadSize = 2 << 20 type nextHTTP = func(http.ResponseWriter, *http.Request) const ( - certChainHeader = "application/x-x509-ca-ra-cert" - leafHeader = "application/x-x509-ca-cert" - pkiOpHeader = "application/x-pki-message" + certChainHeader = "application/x-x509-ca-ra-cert" + leafHeader = "application/x-x509-ca-cert" + pkiOperationHeader = "application/x-pki-message" ) // SCEPRequest is a SCEP server request. @@ -125,10 +125,6 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { return } - // TODO: fix cases in which we get here and there's no certificate (i.e. wrong password, waiting for cert, etc) - // We should generate an appropriate response and it should be signed - api.LogCertificate(w, response.Certificate) - writeSCEPResponse(w, response) } @@ -262,9 +258,13 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe // parse the message using microscep implementation microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { + // return the error, because we can't use the msg for creating a CertRep return SCEPResponse{}, err } + // this is essentially doing the same as microscep.ParsePKIMessage, but + // gives us access to the p7 itself in scep.PKIMessage. Essentially a small + // wrapper for the microscep implementation. p7, err := pkcs7.Parse(microMsg.Raw) if err != nil { return SCEPResponse{}, err @@ -283,23 +283,25 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe return SCEPResponse{}, err } + // NOTE: at this point we have sufficient information for returning nicely signed CertReps + csr := msg.CSRReqMessage.CSR + if msg.MessageType == microscep.PKCSReq { challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword) if err != nil { - return SCEPResponse{}, err + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when checking password") } if !challengeMatches { - return SCEPResponse{}, errors.New("wrong password provided") + // TODO: can this be returned safely to the client? In the end, if the password was correct, that gains a bit of info too. + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "wrong password provided") } } - csr := msg.CSRReqMessage.CSR - certRep, err := h.Auth.SignCSR(ctx, csr, msg) if err != nil { - return SCEPResponse{}, err + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when signing new certificate") } // //cert := certRep.CertRepMessage.Certificate @@ -330,6 +332,11 @@ func formatCapabilities(caps []string) []byte { // writeSCEPResponse writes a SCEP response back to the SCEP client. func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { + + if response.Certificate != nil { + api.LogCertificate(w, response.Certificate) + } + w.Header().Set("Content-Type", contentHeader(response)) _, err := w.Write(response.Data) if err != nil { @@ -346,6 +353,17 @@ func writeError(w http.ResponseWriter, err error) { api.WriteError(w, scepError) } +func (h *Handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, infoText string) (SCEPResponse, error) { + certRepMsg, err := h.Auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), infoText) + if err != nil { + return SCEPResponse{}, err + } + return SCEPResponse{ + Operation: opnPKIOperation, + Data: certRepMsg.Raw, + }, nil +} + func contentHeader(r SCEPResponse) string { switch r.Operation { case opnGetCACert: @@ -354,7 +372,7 @@ func contentHeader(r SCEPResponse) string { } return leafHeader case opnPKIOperation: - return pkiOpHeader + return pkiOperationHeader default: return "text/plain" } diff --git a/scep/authority.go b/scep/authority.go index f8e5a76f..e277a5af 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -56,6 +56,7 @@ type Interface interface { GetCACertificates() ([]*x509.Certificate, error) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) + CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) MatchChallengePassword(ctx context.Context, password string) (bool, error) GetCACaps(ctx context.Context) []string @@ -376,6 +377,10 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m Type: oidSCEPrecipientNonce, Value: msg.SenderNonce, }, + pkcs7.Attribute{ + Type: oidSCEPsenderNonce, + Value: msg.SenderNonce, + }, }, } @@ -419,6 +424,74 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return crepMsg, nil } +// CreateFailureResponse creates an appropriately signed reply for PKI operations +func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) { + + config := pkcs7.SignerInfoConfig{ + ExtraSignedAttributes: []pkcs7.Attribute{ + pkcs7.Attribute{ + Type: oidSCEPtransactionID, + Value: msg.TransactionID, + }, + pkcs7.Attribute{ + Type: oidSCEPpkiStatus, + Value: microscep.FAILURE, + }, + pkcs7.Attribute{ + Type: oidSCEPfailInfo, + Value: info, + }, + pkcs7.Attribute{ + Type: oidSCEPfailInfoText, + Value: infoText, + }, + pkcs7.Attribute{ + Type: oidSCEPmessageType, + Value: microscep.CertRep, + }, + pkcs7.Attribute{ + Type: oidSCEPsenderNonce, + Value: msg.SenderNonce, + }, + pkcs7.Attribute{ + Type: oidSCEPrecipientNonce, + Value: msg.SenderNonce, + }, + }, + } + + signedData, err := pkcs7.NewSignedData(nil) + if err != nil { + return nil, err + } + + // sign the attributes + if err := signedData.AddSigner(a.intermediateCertificate, a.service.Signer, config); err != nil { + return nil, err + } + + certRepBytes, err := signedData.Finish() + if err != nil { + return nil, err + } + + cr := &CertRepMessage{ + PKIStatus: microscep.FAILURE, + FailInfo: microscep.BadRequest, + RecipientNonce: microscep.RecipientNonce(msg.SenderNonce), + } + + // create a CertRep message from the original + crepMsg := &PKIMessage{ + Raw: certRepBytes, + TransactionID: msg.TransactionID, + MessageType: microscep.CertRep, + CertRepMessage: cr, + } + + return crepMsg, nil +} + // MatchChallengePassword verifies a SCEP challenge password func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) { diff --git a/scep/scep.go b/scep/scep.go index 7fc4c261..0c25ec4c 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -11,6 +11,18 @@ import ( "go.mozilla.org/pkcs7" ) +// FailInfoName models the name/value of failInfo +type FailInfoName microscep.FailInfo + +// FailInfo models a failInfo object consisting of a +// name/identifier and a failInfoText, the latter of +// which can be more descriptive and is intended to be +// read by humans. +type FailInfo struct { + Name FailInfoName + Text string +} + // SCEP OIDs var ( oidSCEPmessageType = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 2} @@ -20,6 +32,7 @@ var ( oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6} oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7} oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7} + oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24} ) // PKIMessage defines the possible SCEP message types From aa2ce0a2a553a1a9078882749eba2f84bb9521a2 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 10 Mar 2021 22:20:02 +0100 Subject: [PATCH 042/291] Store new certificates in database --- scep/api/api.go | 8 +--- scep/authority.go | 96 +++++++++++++++------------------------------ scep/certificate.go | 80 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 71 deletions(-) create mode 100644 scep/certificate.go diff --git a/scep/api/api.go b/scep/api/api.go index ced6fa05..13aeec21 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -299,17 +299,13 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe } } + // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not + certRep, err := h.Auth.SignCSR(ctx, csr, msg) if err != nil { return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when signing new certificate") } - // //cert := certRep.CertRepMessage.Certificate - // //name := certName(cert) - - // // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not - // // TODO: store the new cert for CN locally; should go into the DB - response := SCEPResponse{ Operation: opnPKIOperation, Data: certRep.Raw, diff --git a/scep/authority.go b/scep/authority.go index e277a5af..157854f1 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -27,31 +27,14 @@ import ( "go.step.sm/crypto/x509util" ) +var ( + certTable = []byte("scep_certs") +) + // Interface is the SCEP authority interface. type Interface interface { - // GetDirectory(ctx context.Context) (*Directory, error) - // NewNonce() (string, error) - // UseNonce(string) error - - // DeactivateAccount(ctx context.Context, accID string) (*Account, error) - // GetAccount(ctx context.Context, accID string) (*Account, error) - // GetAccountByKey(ctx context.Context, key *jose.JSONWebKey) (*Account, error) - // NewAccount(ctx context.Context, ao AccountOptions) (*Account, error) - // UpdateAccount(context.Context, string, []string) (*Account, error) - - // GetAuthz(ctx context.Context, accID string, authzID string) (*Authz, error) - // ValidateChallenge(ctx context.Context, accID string, chID string, key *jose.JSONWebKey) (*Challenge, error) - - // FinalizeOrder(ctx context.Context, accID string, orderID string, csr *x509.CertificateRequest) (*Order, error) - // GetOrder(ctx context.Context, accID string, orderID string) (*Order, error) - // GetOrdersByAccount(ctx context.Context, accID string) ([]string, error) - // NewOrder(ctx context.Context, oo OrderOptions) (*Order, error) - - // GetCertificate(string, string) ([]byte, error) - LoadProvisionerByID(string) (provisioner.Interface, error) - // GetLink(ctx context.Context, linkType Link, absoluteLink bool, inputs ...string) string - // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string + GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetCACertificates() ([]*x509.Certificate, error) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error @@ -59,8 +42,6 @@ type Interface interface { CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) MatchChallengePassword(ctx context.Context, password string) (bool, error) GetCACaps(ctx context.Context) []string - - GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string } // Authority is the layer that handles all SCEP interactions. @@ -107,7 +88,14 @@ type SignAuthority interface { func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { if _, ok := ops.DB.(*database.SimpleDB); !ok { - // TODO: see ACME implementation + // If it's not a SimpleDB then go ahead and bootstrap the DB with the + // necessary SCEP tables. SimpleDB should ONLY be used for testing. + tables := [][]byte{certTable} + for _, b := range tables { + if err := ops.DB.CreateTable(b); err != nil { + return nil, fmt.Errorf("%w: error creating table %s", err, string(b)) + } + } } // TODO: the below is a bit similar as what happens in the core Authority class, which @@ -284,30 +272,6 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m csr = msg.CSRReqMessage.CSR } - // subjectKeyID, err := createKeyIdentifier(csr.PublicKey) - // if err != nil { - // return nil, err - // } - - // serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic? - // days := 40 // TODO: days - - // // TODO: use information from provisioner, like claims - // template := &x509.Certificate{ - // SerialNumber: serial, - // Subject: csr.Subject, - // NotBefore: time.Now().Add(-600).UTC(), - // NotAfter: time.Now().AddDate(0, 0, days).UTC(), - // SubjectKeyId: subjectKeyID, - // KeyUsage: x509.KeyUsageDigitalSignature, - // ExtKeyUsage: []x509.ExtKeyUsage{ - // x509.ExtKeyUsageClientAuth, - // }, - // SignatureAlgorithm: csr.SignatureAlgorithm, - // EmailAddresses: csr.EmailAddresses, - // DNSNames: csr.DNSNames, - // } - // Template data data := x509util.NewTemplateData() data.SetCommonName(csr.Subject.CommonName) @@ -339,14 +303,6 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m cert := certChain[0] - // fmt.Println("CERT") - // fmt.Println(cert) - // fmt.Println(fmt.Sprintf("%T", cert)) - // fmt.Println(cert.Issuer) - // fmt.Println(cert.Subject) - // fmt.Println(cert.SerialNumber) - // fmt.Println(string(cert.SubjectKeyId)) - // create a degenerate cert structure deg, err := degenerateCertificates([]*x509.Certificate{cert}) if err != nil { @@ -377,7 +333,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m Type: oidSCEPrecipientNonce, Value: msg.SenderNonce, }, - pkcs7.Attribute{ + { Type: oidSCEPsenderNonce, Value: msg.SenderNonce, }, @@ -421,6 +377,16 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m CertRepMessage: cr, } + // TODO: save more data? + _, err = newCert(a.db, CertOptions{ + Leaf: certChain[0], + Intermediates: certChain[1:], + }) + if err != nil { + fmt.Println(err) + return nil, err + } + return crepMsg, nil } @@ -429,31 +395,31 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi config := pkcs7.SignerInfoConfig{ ExtraSignedAttributes: []pkcs7.Attribute{ - pkcs7.Attribute{ + { Type: oidSCEPtransactionID, Value: msg.TransactionID, }, - pkcs7.Attribute{ + { Type: oidSCEPpkiStatus, Value: microscep.FAILURE, }, - pkcs7.Attribute{ + { Type: oidSCEPfailInfo, Value: info, }, - pkcs7.Attribute{ + { Type: oidSCEPfailInfoText, Value: infoText, }, - pkcs7.Attribute{ + { Type: oidSCEPmessageType, Value: microscep.CertRep, }, - pkcs7.Attribute{ + { Type: oidSCEPsenderNonce, Value: msg.SenderNonce, }, - pkcs7.Attribute{ + { Type: oidSCEPrecipientNonce, Value: msg.SenderNonce, }, diff --git a/scep/certificate.go b/scep/certificate.go new file mode 100644 index 00000000..fe48d29e --- /dev/null +++ b/scep/certificate.go @@ -0,0 +1,80 @@ +package scep + +import ( + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "time" + + "github.com/smallstep/nosql" +) + +type certificate struct { + ID string `json:"id"` + Created time.Time `json:"created"` + Leaf []byte `json:"leaf"` + Intermediates []byte `json:"intermediates"` +} + +// CertOptions options with which to create and store a cert object. +type CertOptions struct { + Leaf *x509.Certificate + Intermediates []*x509.Certificate +} + +func newCert(db nosql.DB, ops CertOptions) (*certificate, error) { + + // TODO: according to the RFC this should be IssuerAndSerialNumber, + // but sscep seems to use just the serial number for getcert + + id := ops.Leaf.SerialNumber.String() + + leaf := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: ops.Leaf.Raw, + }) + var intermediates []byte + for _, cert := range ops.Intermediates { + intermediates = append(intermediates, pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + })...) + } + + cert := &certificate{ + ID: id, + Leaf: leaf, + Intermediates: intermediates, + Created: time.Now().UTC(), + } + certB, err := json.Marshal(cert) + if err != nil { + return nil, fmt.Errorf("%w: error marshaling certificate", err) + } + + _, swapped, err := db.CmpAndSwap(certTable, []byte(id), nil, certB) + switch { + case err != nil: + return nil, fmt.Errorf("%w: error storing certificate", err) + case !swapped: + return nil, fmt.Errorf("error storing certificate; " + + "value has changed since last read") + default: + return cert, nil + } +} + +func getCert(db nosql.DB, id string) (*certificate, error) { + b, err := db.Get(certTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, fmt.Errorf("certificate %s not found", id) + } else if err != nil { + return nil, fmt.Errorf("error loading certificate") + } + var cert certificate + if err := json.Unmarshal(b, &cert); err != nil { + return nil, fmt.Errorf("%w: error unmarshaling certificate", err) + } + return &cert, nil +} From e7cb80f88015d23e387571de9f932e7fd0e04146 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 10 Mar 2021 22:39:20 +0100 Subject: [PATCH 043/291] Fix linter issues --- authority/authority.go | 3 +++ scep/api/api.go | 7 ------- scep/authority.go | 16 ---------------- scep/certificate.go | 26 +++++++++++++------------- scep/scep.go | 3 ++- 5 files changed, 18 insertions(+), 37 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index e753aeae..f9f723a4 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -337,6 +337,9 @@ func (a *Authority) init() error { DecryptionKey: a.config.IntermediateKey, Password: []byte(a.config.Password), }) + if err != nil { + return err + } } a.scepService = &scep.Service{ diff --git a/scep/api/api.go b/scep/api/api.go index 13aeec21..a9e4d840 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -315,13 +315,6 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe return response, nil } -func certName(cert *x509.Certificate) string { - if cert.Subject.CommonName != "" { - return cert.Subject.CommonName - } - return string(cert.Signature) -} - func formatCapabilities(caps []string) []byte { return []byte(strings.Join(caps, "\r\n")) } diff --git a/scep/authority.go b/scep/authority.go index 157854f1..69d83554 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -3,8 +3,6 @@ package scep import ( "bytes" "context" - "crypto" - "crypto/sha1" "crypto/x509" "errors" "fmt" @@ -512,20 +510,6 @@ func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { return degenerate, nil } -// createKeyIdentifier creates an identifier for public keys -// according to the first method in RFC5280 section 4.2.1.2. -func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) { - - keyBytes, err := x509.MarshalPKIXPublicKey(pub) - if err != nil { - return nil, err - } - - id := sha1.Sum(keyBytes) - - return id[:], nil -} - // Interface guards var ( _ Interface = (*Authority)(nil) diff --git a/scep/certificate.go b/scep/certificate.go index fe48d29e..5e43b762 100644 --- a/scep/certificate.go +++ b/scep/certificate.go @@ -65,16 +65,16 @@ func newCert(db nosql.DB, ops CertOptions) (*certificate, error) { } } -func getCert(db nosql.DB, id string) (*certificate, error) { - b, err := db.Get(certTable, []byte(id)) - if nosql.IsErrNotFound(err) { - return nil, fmt.Errorf("certificate %s not found", id) - } else if err != nil { - return nil, fmt.Errorf("error loading certificate") - } - var cert certificate - if err := json.Unmarshal(b, &cert); err != nil { - return nil, fmt.Errorf("%w: error unmarshaling certificate", err) - } - return &cert, nil -} +// func getCert(db nosql.DB, id string) (*certificate, error) { +// b, err := db.Get(certTable, []byte(id)) +// if nosql.IsErrNotFound(err) { +// return nil, fmt.Errorf("certificate %s not found", id) +// } else if err != nil { +// return nil, fmt.Errorf("error loading certificate") +// } +// var cert certificate +// if err := json.Unmarshal(b, &cert); err != nil { +// return nil, fmt.Errorf("%w: error unmarshaling certificate", err) +// } +// return &cert, nil +// } diff --git a/scep/scep.go b/scep/scep.go index 0c25ec4c..f56176d7 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -31,8 +31,9 @@ var ( oidSCEPsenderNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 5} oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6} oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7} - oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7} oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24} + //oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7} + ) // PKIMessage defines the possible SCEP message types From 2d85d4c1c1791ee3345af78fddd8c355fa21ef03 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 14:18:36 +0100 Subject: [PATCH 044/291] Add non-TLS server and improve crypto.Decrypter interface A server without TLS was added to serve the SCEP endpoints. According to the RFC, SCEP has to be served via HTTP. The `sscep` client, for example, will stop any URL that does not start with `http://` from being used, so serving SCEP seems to be the right way to do it. This commit adds a second server for which no TLS configuration is configured. A distinct field in the configuration, `insecureAddress` was added to specify the address for the insecure server. The SCEP endpoints will also still be served via HTTPS. Some clients may be able to work with that. This commit also improves how the crypto.Decrypter interface is handled for the different types of KMSes supported by step. The apiv1.Decrypter interface was added. Currently only SoftKMS implements this interface, providing a crypto.Decrypter required for SCEP operations. --- authority/authority.go | 46 +++++++++++++++++++++++++--------- kms/apiv1/options.go | 5 +++- kms/awskms/awskms.go | 6 ----- kms/cloudkms/cloudkms.go | 6 ----- kms/pkcs11/pkcs11.go | 5 ---- kms/sshagentkms/sshagentkms.go | 6 ----- kms/yubikey/yubikey.go | 6 ----- 7 files changed, 38 insertions(+), 42 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index f9f723a4..32d26470 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -23,6 +23,7 @@ import ( casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/kms" + "github.com/smallstep/certificates/kms/apiv1" kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/sshagentkms" "github.com/smallstep/certificates/templates" @@ -314,13 +315,14 @@ func (a *Authority) init() error { } // TODO: decide if this is a good approach for providing the SCEP functionality + // It currently mirrors the logic for the x509CAServer if a.scepService == nil { var options casapi.Options if a.config.AuthorityConfig.Options != nil { options = *a.config.AuthorityConfig.Options } - // Read intermediate and create X509 signer for default CAS. + // Read intermediate and create X509 signer and decrypter for default CAS. if options.Is(casapi.SoftCAS) { options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) if err != nil { @@ -333,12 +335,15 @@ func (a *Authority) init() error { if err != nil { return err } - options.Decrypter, err = a.keyManager.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), - }) - if err != nil { - return err + + if km, ok := a.keyManager.(apiv1.Decrypter); ok { + options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } } } @@ -481,11 +486,6 @@ func (a *Authority) init() error { audiences := a.config.GetAudiences() a.provisioners = provisioner.NewCollection(audiences) config := provisioner.Config{ - // TODO: I'm not sure if extending this configuration is a good way to integrate - // It's powerful, but leaks quite some seemingly internal stuff to the provisioner. - // IntermediateCert: a.config.IntermediateCert, - // SigningKey: a.config.IntermediateKey, - // CACertificates: a.rootX509Certs, Claims: claimer.Claims(), Audiences: audiences, DB: a.db, @@ -495,6 +495,14 @@ func (a *Authority) init() error { }, GetIdentityFunc: a.getIdentityFunc, } + + // Check if a KMS with decryption capability is required and available + if a.requiresDecrypter() { + if _, ok := a.keyManager.(apiv1.Decrypter); !ok { + return errors.New("keymanager doesn't provide crypto.Decrypter") + } + } + // Store all the provisioners for _, p := range a.config.AuthorityConfig.Provisioners { if err := p.Init(config); err != nil { @@ -569,6 +577,20 @@ func (a *Authority) CloseForReload() { } } +// requiresDecrypter iterates over the configured provisioners +// and determines if the Authority requires a KMS that provides +// a crypto.Decrypter by implementing the apiv1.Decrypter +// interface. Currently only the SCEP provider requires this, +// but others may be added in the future. +func (a *Authority) requiresDecrypter() bool { + for _, p := range a.config.AuthorityConfig.Provisioners { + if p.GetType() == provisioner.TypeSCEP { + return true + } + } + return false +} + // GetSCEPService returns the configured SCEP Service // TODO: this function is intended to exist temporarily // in order to make SCEP work more easily. It can be diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 0e6f32df..1574a426 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -13,10 +13,13 @@ type KeyManager interface { GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error) CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error) CreateSigner(req *CreateSignerRequest) (crypto.Signer, error) - CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) // TODO: split into separate interface? Close() error } +type Decrypter interface { + CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) +} + // CertificateManager is the interface implemented by the KMS that can load and // store x509.Certificates. type CertificateManager interface { diff --git a/kms/awskms/awskms.go b/kms/awskms/awskms.go index 00d7d0b3..da392989 100644 --- a/kms/awskms/awskms.go +++ b/kms/awskms/awskms.go @@ -3,7 +3,6 @@ package awskms import ( "context" "crypto" - "fmt" "net/url" "strings" "time" @@ -222,11 +221,6 @@ func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error return NewSigner(k.service, req.SigningKey) } -// CreateDecrypter creates a new crypto.decrypter backed by AWS KMS -func (k *KMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { - return nil, fmt.Errorf("not implemented yet") -} - // Close closes the connection of the KMS client. func (k *KMS) Close() error { return nil diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index ace12a1a..cfbf8235 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -3,7 +3,6 @@ package cloudkms import ( "context" "crypto" - "fmt" "log" "strings" "time" @@ -285,11 +284,6 @@ func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe return pk, nil } -// CreateDecrypter creates a new crypto.Decrypter backed by Google Cloud KMS -func (k *CloudKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { - return nil, fmt.Errorf("not implemented yet") -} - // getPublicKeyWithRetries retries the request if the error is // FailedPrecondition, caused because the key is in the PENDING_GENERATION // status. diff --git a/kms/pkcs11/pkcs11.go b/kms/pkcs11/pkcs11.go index 8c1497e8..47c298a5 100644 --- a/kms/pkcs11/pkcs11.go +++ b/kms/pkcs11/pkcs11.go @@ -352,8 +352,3 @@ func findCertificate(ctx P11, rawuri string) (*x509.Certificate, error) { } return cert, nil } - -// CreateDecrypter creates a new crypto.Decrypter backed by PKCS11 -func (k *PKCS11) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { - return nil, fmt.Errorf("not implemented yet") -} diff --git a/kms/sshagentkms/sshagentkms.go b/kms/sshagentkms/sshagentkms.go index 9c8a7866..b3627a08 100644 --- a/kms/sshagentkms/sshagentkms.go +++ b/kms/sshagentkms/sshagentkms.go @@ -7,7 +7,6 @@ import ( "crypto/ed25519" "crypto/rsa" "crypto/x509" - "fmt" "io" "net" "os" @@ -205,8 +204,3 @@ func (k *SSHAgentKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.Publi return nil, errors.Errorf("unsupported public key type %T", v) } } - -// CreateDecrypter creates a crypto.Decrypter backed by ssh-agent -func (k *SSHAgentKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { - return nil, fmt.Errorf("not implemented yet") -} diff --git a/kms/yubikey/yubikey.go b/kms/yubikey/yubikey.go index b78ff5e5..2dde244a 100644 --- a/kms/yubikey/yubikey.go +++ b/kms/yubikey/yubikey.go @@ -7,7 +7,6 @@ import ( "crypto" "crypto/x509" "encoding/hex" - "fmt" "net/url" "strings" @@ -190,11 +189,6 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e return signer, nil } -// CreateDecrypter creates a new crypto.Decrypter backed by a YubiKey -func (k *YubiKey) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) { - return nil, fmt.Errorf("not implemented yet") -} - // Close releases the connection to the YubiKey. func (k *YubiKey) Close() error { return errors.Wrap(k.yk.Close(), "error closing yubikey") From 491c2b8d939cdf09c9e7d7aec773dbc688fb8662 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 15:49:39 +0100 Subject: [PATCH 045/291] Improve initialization of SCEP authority --- authority/authority.go | 8 +++++--- cas/apiv1/registry.go | 2 +- scep/authority.go | 23 +++++------------------ scep/service.go | 35 +++++++++++++++++++++++++++++++---- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 32d26470..67fccf53 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -347,10 +347,12 @@ func (a *Authority) init() error { } } - a.scepService = &scep.Service{ - Signer: options.Signer, - Decrypter: options.Decrypter, + a.scepService, err = scep.NewService(context.Background(), options) + if err != nil { + return err } + + // TODO: mimick the x509CAService GetCertificateAuthority here too? } // Read root certificates and store them in the certificates map. diff --git a/cas/apiv1/registry.go b/cas/apiv1/registry.go index b74103b7..5876e9d7 100644 --- a/cas/apiv1/registry.go +++ b/cas/apiv1/registry.go @@ -18,7 +18,7 @@ func Register(t Type, fn CertificateAuthorityServiceNewFunc) { registry.Store(t.String(), fn) } -// LoadCertificateAuthorityServiceNewFunc returns the function initialize a KayManager. +// LoadCertificateAuthorityServiceNewFunc returns the function to initialize a KeyManager. func LoadCertificateAuthorityServiceNewFunc(t Type) (CertificateAuthorityServiceNewFunc, bool) { v, ok := registry.Load(t.String()) if !ok { diff --git a/scep/authority.go b/scep/authority.go index 69d83554..03bd6919 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -11,8 +11,6 @@ import ( "github.com/smallstep/certificates/authority/provisioner" database "github.com/smallstep/certificates/db" - "go.step.sm/crypto/pemutil" - "github.com/smallstep/nosql" microx509util "github.com/micromdm/scep/crypto/x509util" @@ -59,10 +57,8 @@ type Authority struct { // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { - IntermediateCertificatePath string - + // Service provides the SCEP functions to Authority Service Service - // Backdate Backdate provisioner.Duration // DB is the database used by nosql. @@ -96,21 +92,12 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { } } - // TODO: the below is a bit similar as what happens in the core Authority class, which - // creates the full x509 service. However, those aren't accessible directly, which is - // why I reimplemented this (for now). There might be an alternative that I haven't - // found yet. - certificateChain, err := pemutil.ReadCertificateBundle(ops.IntermediateCertificatePath) - if err != nil { - return nil, err - } - return &Authority{ backdate: ops.Backdate, db: ops.DB, prefix: ops.Prefix, dns: ops.DNS, - intermediateCertificate: certificateChain[0], + intermediateCertificate: ops.Service.certificateChain[0], service: ops.Service, signAuth: signAuth, }, nil @@ -208,7 +195,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return err } - envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter) + envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.decrypter) if err != nil { return err } @@ -351,7 +338,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m authCert := a.intermediateCertificate // sign the attributes - if err := signedData.AddSigner(authCert, a.service.Signer, config); err != nil { + if err := signedData.AddSigner(authCert, a.service.signer, config); err != nil { return nil, err } @@ -430,7 +417,7 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi } // sign the attributes - if err := signedData.AddSigner(a.intermediateCertificate, a.service.Signer, config); err != nil { + if err := signedData.AddSigner(a.intermediateCertificate, a.service.signer, config); err != nil { return nil, err } diff --git a/scep/service.go b/scep/service.go index 1d743dd6..0b37716d 100644 --- a/scep/service.go +++ b/scep/service.go @@ -1,9 +1,36 @@ package scep -import "crypto" +import ( + "context" + "crypto" + "crypto/x509" + "strings" -// Service is a (temporary?) wrapper for signer/decrypters + "github.com/smallstep/certificates/cas/apiv1" +) + +// Service is a wrapper for crypto.Signer and crypto.Decrypter type Service struct { - Signer crypto.Signer - Decrypter crypto.Decrypter + certificateChain []*x509.Certificate + signer crypto.Signer + decrypter crypto.Decrypter +} + +func NewService(ctx context.Context, opts apiv1.Options) (*Service, error) { + + if err := opts.Validate(); err != nil { + return nil, err + } + + t := apiv1.Type(strings.ToLower(opts.Type)) + if t == apiv1.DefaultCAS { + t = apiv1.SoftCAS + } + + // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? + return &Service{ + chain: opts.CertificateChain, + signer: opts.Signer, + decrypter: opts.Decrypter, + }, nil } From dd4f5486507c988740f9d24e49b404b399f2c71c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 15:51:16 +0100 Subject: [PATCH 046/291] Fix certificateChain property --- scep/service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scep/service.go b/scep/service.go index 0b37716d..d485b7c3 100644 --- a/scep/service.go +++ b/scep/service.go @@ -29,8 +29,8 @@ func NewService(ctx context.Context, opts apiv1.Options) (*Service, error) { // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? return &Service{ - chain: opts.CertificateChain, - signer: opts.Signer, - decrypter: opts.Decrypter, + certificateChain: opts.CertificateChain, + signer: opts.Signer, + decrypter: opts.Decrypter, }, nil } From 5a80bc3ced2a0b569223fd6f668fb0a7adbe30e4 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 16:02:00 +0100 Subject: [PATCH 047/291] Make linter happy --- scep/service.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scep/service.go b/scep/service.go index d485b7c3..e9a58646 100644 --- a/scep/service.go +++ b/scep/service.go @@ -27,6 +27,9 @@ func NewService(ctx context.Context, opts apiv1.Options) (*Service, error) { t = apiv1.SoftCAS } + // TODO: silence the linter (temporarily) + _ = t + // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? return &Service{ certificateChain: opts.CertificateChain, From 57a62964b12bcd4ce31c3fa8af2129e0b09a5793 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 16:27:26 +0100 Subject: [PATCH 048/291] Make tests not fail hard on ECDSA keys All tests for the Authority failed because the test data contains ECDSA keys. ECDSA keys are no crypto.Decrypter, resulting in a failure when instantiating the Authority. --- authority/authority.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 67fccf53..4779c920 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -7,6 +7,8 @@ import ( "crypto/x509" "encoding/hex" "log" + "os" + "strings" "sync" "time" @@ -23,7 +25,6 @@ import ( casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/kms" - "github.com/smallstep/certificates/kms/apiv1" kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/sshagentkms" "github.com/smallstep/certificates/templates" @@ -336,13 +337,19 @@ func (a *Authority) init() error { return err } - if km, ok := a.keyManager.(apiv1.Decrypter); ok { - options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), - }) - if err != nil { - return err + // TODO: this is not exactly nice to do, but ensures that tests will still run while + // ECDSA keys are in the testdata. ECDSA keys are no crypto.Decrypters, resulting + // in many errors in the test suite. Needs a better solution, I think. + underTest := strings.HasSuffix(os.Args[0], ".test") + if !underTest { + if km, ok := a.keyManager.(kmsapi.Decrypter); ok { + options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } } } } @@ -500,7 +507,7 @@ func (a *Authority) init() error { // Check if a KMS with decryption capability is required and available if a.requiresDecrypter() { - if _, ok := a.keyManager.(apiv1.Decrypter); !ok { + if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { return errors.New("keymanager doesn't provide crypto.Decrypter") } } From be528da7094b4e9ca36f0a667824eaa5e5ca1d1e Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 12 Mar 2021 16:58:52 +0100 Subject: [PATCH 049/291] Make tests green --- authority/authority.go | 107 ++++++++++++++++++++--------------------- scep/authority.go | 30 +++++++----- 2 files changed, 70 insertions(+), 67 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 4779c920..57a6293b 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -7,8 +7,6 @@ import ( "crypto/x509" "encoding/hex" "log" - "os" - "strings" "sync" "time" @@ -315,53 +313,6 @@ func (a *Authority) init() error { } } - // TODO: decide if this is a good approach for providing the SCEP functionality - // It currently mirrors the logic for the x509CAServer - if a.scepService == nil { - var options casapi.Options - if a.config.AuthorityConfig.Options != nil { - options = *a.config.AuthorityConfig.Options - } - - // Read intermediate and create X509 signer and decrypter for default CAS. - if options.Is(casapi.SoftCAS) { - options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) - if err != nil { - return err - } - options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), - }) - if err != nil { - return err - } - - // TODO: this is not exactly nice to do, but ensures that tests will still run while - // ECDSA keys are in the testdata. ECDSA keys are no crypto.Decrypters, resulting - // in many errors in the test suite. Needs a better solution, I think. - underTest := strings.HasSuffix(os.Args[0], ".test") - if !underTest { - if km, ok := a.keyManager.(kmsapi.Decrypter); ok { - options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), - }) - if err != nil { - return err - } - } - } - } - - a.scepService, err = scep.NewService(context.Background(), options) - if err != nil { - return err - } - - // TODO: mimick the x509CAService GetCertificateAuthority here too? - } - // Read root certificates and store them in the certificates map. if len(a.rootX509Certs) == 0 { a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root)) @@ -512,6 +463,47 @@ func (a *Authority) init() error { } } + // TODO: decide if this is a good approach for providing the SCEP functionality + // It currently mirrors the logic for the x509CAService + if a.requiresSCEPService() && a.scepService == nil { + var options casapi.Options + if a.config.AuthorityConfig.Options != nil { + options = *a.config.AuthorityConfig.Options + } + + // Read intermediate and create X509 signer and decrypter for default CAS. + if options.Is(casapi.SoftCAS) { + options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) + if err != nil { + return err + } + options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } + + if km, ok := a.keyManager.(kmsapi.Decrypter); ok { + options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } + } + } + + a.scepService, err = scep.NewService(context.Background(), options) + if err != nil { + return err + } + + // TODO: mimick the x509CAService GetCertificateAuthority here too? + } + // Store all the provisioners for _, p := range a.config.AuthorityConfig.Provisioners { if err := p.Init(config); err != nil { @@ -586,12 +578,15 @@ func (a *Authority) CloseForReload() { } } -// requiresDecrypter iterates over the configured provisioners -// and determines if the Authority requires a KMS that provides -// a crypto.Decrypter by implementing the apiv1.Decrypter -// interface. Currently only the SCEP provider requires this, -// but others may be added in the future. +// requiresDecrypter returns whether the Authority +// requires a KMS that provides a crypto.Decrypter func (a *Authority) requiresDecrypter() bool { + return a.requiresSCEPService() +} + +// requiresSCEPService iterates over the configured provisioners +// and determines if one of them is a SCEP provisioner. +func (a *Authority) requiresSCEPService() bool { for _, p := range a.config.AuthorityConfig.Provisioners { if p.GetType() == provisioner.TypeSCEP { return true @@ -605,6 +600,6 @@ func (a *Authority) requiresDecrypter() bool { // in order to make SCEP work more easily. It can be // made more correct by using the right interfaces/abstractions // after it works as expected. -func (a *Authority) GetSCEPService() scep.Service { - return *a.scepService +func (a *Authority) GetSCEPService() *scep.Service { + return a.scepService } diff --git a/scep/authority.go b/scep/authority.go index 03bd6919..2b6686fd 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -51,14 +51,14 @@ type Authority struct { intermediateCertificate *x509.Certificate - service Service + service *Service signAuth SignAuthority } // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { // Service provides the SCEP functions to Authority - Service Service + Service *Service // Backdate Backdate provisioner.Duration // DB is the database used by nosql. @@ -92,15 +92,23 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { } } - return &Authority{ - backdate: ops.Backdate, - db: ops.DB, - prefix: ops.Prefix, - dns: ops.DNS, - intermediateCertificate: ops.Service.certificateChain[0], - service: ops.Service, - signAuth: signAuth, - }, nil + authority := &Authority{ + backdate: ops.Backdate, + db: ops.DB, + prefix: ops.Prefix, + dns: ops.DNS, + signAuth: signAuth, + } + + // TODO: this is not really nice to do; the Service should be removed + // in its entirety to make this more interoperable with the rest of + // step-ca. + if ops.Service != nil { + authority.intermediateCertificate = ops.Service.certificateChain[0] + authority.service = ops.Service + } + + return authority, nil } var ( From 97b88c4d5826c2661ebad74172011ac6455c4847 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sun, 21 Mar 2021 16:42:41 +0100 Subject: [PATCH 050/291] Address (most) PR comments --- authority/authority.go | 39 ++++++------- authority/provisioner/scep.go | 6 -- ca/ca.go | 9 +++ kms/apiv1/options.go | 2 + kms/apiv1/requests.go | 3 - scep/api/api.go | 28 ++++----- scep/authority.go | 106 ++++++++++------------------------ scep/certificate.go | 69 ++-------------------- scep/database.go | 7 +++ scep/options.go | 31 ++++++++++ scep/service.go | 13 +---- 11 files changed, 116 insertions(+), 197 deletions(-) create mode 100644 scep/database.go create mode 100644 scep/options.go diff --git a/authority/authority.go b/authority/authority.go index 57a6293b..a0eaf871 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -466,34 +466,29 @@ func (a *Authority) init() error { // TODO: decide if this is a good approach for providing the SCEP functionality // It currently mirrors the logic for the x509CAService if a.requiresSCEPService() && a.scepService == nil { - var options casapi.Options - if a.config.AuthorityConfig.Options != nil { - options = *a.config.AuthorityConfig.Options - } + var options scep.Options // Read intermediate and create X509 signer and decrypter for default CAS. - if options.Is(casapi.SoftCAS) { - options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) - if err != nil { - return err - } - options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), + options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) + if err != nil { + return err + } + options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), + }) + if err != nil { + return err + } + + if km, ok := a.keyManager.(kmsapi.Decrypter); ok { + options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: []byte(a.config.Password), }) if err != nil { return err } - - if km, ok := a.keyManager.(kmsapi.Decrypter); ok { - options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), - }) - if err != nil { - return err - } - } } a.scepService, err = scep.NewService(context.Background(), options) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 49d057ff..6af3dc83 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -102,9 +102,3 @@ func (s *SCEP) GetChallengePassword() string { func (s *SCEP) GetCapabilities() []string { return s.Capabilities } - -// Interface guards -var ( - _ Interface = (*SCEP)(nil) - //_ scep.Provisioner = (*SCEP)(nil) -) diff --git a/ca/ca.go b/ca/ca.go index a60346ee..6eb223eb 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -235,6 +235,15 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { ca.auth = auth ca.srv = server.New(config.Address, handler, tlsConfig) + + // TODO: instead opt for having a single server.Server but two + // http.Servers handling the HTTP and HTTPS handler? The latter + // will probably introduce more complexity in terms of graceful + // reload. + if config.InsecureAddress != "" { + ca.insecureSrv = server.New(config.InsecureAddress, insecureHandler, nil) + } + return ca, nil } diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 1574a426..7cc7f748 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -16,6 +16,8 @@ type KeyManager interface { Close() error } +// Decrypter is an interface implemented by KMSes that are used +// in operations that require decryption type Decrypter interface { CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) } diff --git a/kms/apiv1/requests.go b/kms/apiv1/requests.go index 31097040..f6fe7dd2 100644 --- a/kms/apiv1/requests.go +++ b/kms/apiv1/requests.go @@ -138,9 +138,6 @@ type CreateDecrypterRequest struct { Decrypter crypto.Decrypter DecryptionKey string DecryptionKeyPEM []byte - TokenLabel string - PublicKey string - PublicKeyPEM []byte Password []byte } diff --git a/scep/api/api.go b/scep/api/api.go index a9e4d840..cff3d32b 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -4,8 +4,6 @@ import ( "context" "crypto/x509" "encoding/base64" - "errors" - "fmt" "io" "io/ioutil" "net/http" @@ -18,6 +16,8 @@ import ( "github.com/smallstep/certificates/scep" "go.mozilla.org/pkcs7" + "github.com/pkg/errors" + microscep "github.com/micromdm/scep/scep" ) @@ -75,7 +75,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { request, err := decodeSCEPRequest(r) if err != nil { - writeError(w, fmt.Errorf("not a scep get request: %w", err)) + writeError(w, errors.Wrap(err, "not a scep get request")) return } @@ -90,11 +90,11 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { case opnPKIOperation: // TODO: implement the GET for PKI operation? Default CACAPS doesn't specify this is in use, though default: - err = fmt.Errorf("unknown operation: %s", request.Operation) + err = errors.Errorf("unknown operation: %s", request.Operation) } if err != nil { - writeError(w, fmt.Errorf("get request failed: %w", err)) + writeError(w, errors.Wrap(err, "get request failed")) return } @@ -106,7 +106,7 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { request, err := decodeSCEPRequest(r) if err != nil { - writeError(w, fmt.Errorf("not a scep post request: %w", err)) + writeError(w, errors.Wrap(err, "not a scep post request")) return } @@ -117,11 +117,11 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { case opnPKIOperation: response, err = h.PKIOperation(ctx, request) default: - err = fmt.Errorf("unknown operation: %s", request.Operation) + err = errors.Errorf("unknown operation: %s", request.Operation) } if err != nil { - writeError(w, fmt.Errorf("post request failed: %w", err)) + writeError(w, errors.Wrap(err, "post request failed")) return } @@ -163,7 +163,7 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { Message: decodedMessage, }, nil default: - return SCEPRequest{}, fmt.Errorf("unsupported operation: %s", operation) + return SCEPRequest{}, errors.Errorf("unsupported operation: %s", operation) } case http.MethodPost: body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxPayloadSize)) @@ -175,7 +175,7 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { Message: body, }, nil default: - return SCEPRequest{}, fmt.Errorf("unsupported method: %s", method) + return SCEPRequest{}, errors.Errorf("unsupported method: %s", method) } } @@ -187,7 +187,7 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { name := chi.URLParam(r, "provisionerID") provisionerID, err := url.PathUnescape(name) if err != nil { - api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) + api.WriteError(w, errors.Errorf("error url unescaping provisioner id '%s'", name)) return } @@ -229,6 +229,9 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) { if len(certs) == 1 { response.Data = certs[0].Raw } else { + // create degenerate pkcs7 certificate structure, according to + // https://tools.ietf.org/html/rfc8894#section-4.2.1.2, because + // not signed or encrypted data has to be returned. data, err := microscep.DegenerateCertificates(certs) if err != nil { return SCEPResponse{}, err @@ -329,12 +332,11 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { w.Header().Set("Content-Type", contentHeader(response)) _, err := w.Write(response.Data) if err != nil { - writeError(w, fmt.Errorf("error when writing scep response: %w", err)) // This could end up as an error again + writeError(w, errors.Wrap(err, "error when writing scep response")) // This could end up as an error again } } func writeError(w http.ResponseWriter, err error) { - // TODO: this probably needs to use SCEP specific errors (i.e. failInfo) scepError := &scep.Error{ Message: err.Error(), Status: http.StatusInternalServerError, // TODO: make this a param? diff --git a/scep/authority.go b/scep/authority.go index 2b6686fd..c73885e5 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -1,32 +1,24 @@ package scep import ( - "bytes" "context" + "crypto/subtle" "crypto/x509" - "errors" "fmt" "net/url" "github.com/smallstep/certificates/authority/provisioner" - database "github.com/smallstep/certificates/db" - - "github.com/smallstep/nosql" microx509util "github.com/micromdm/scep/crypto/x509util" microscep "github.com/micromdm/scep/scep" - //"github.com/smallstep/certificates/scep/pkcs7" + "github.com/pkg/errors" "go.mozilla.org/pkcs7" "go.step.sm/crypto/x509util" ) -var ( - certTable = []byte("scep_certs") -) - // Interface is the SCEP authority interface. type Interface interface { LoadProvisionerByID(string) (provisioner.Interface, error) @@ -42,28 +34,21 @@ type Interface interface { // Authority is the layer that handles all SCEP interactions. type Authority struct { - backdate provisioner.Duration - db nosql.DB - prefix string - dns string - - // dir *directory - + db DB + prefix string + dns string intermediateCertificate *x509.Certificate - - service *Service - signAuth SignAuthority + service *Service + signAuth SignAuthority } // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { - // Service provides the SCEP functions to Authority + // Service provides the certificate chain, the signer and the decrypter to the Authority Service *Service - // Backdate - Backdate provisioner.Duration - // DB is the database used by nosql. - DB nosql.DB - // DNS the host used to generate accurate SCEP links. By default the authority + // DB is the database used by SCEP + DB DB + // DNS is the host used to generate accurate SCEP links. By default the authority // will use the Host from the request, so this value will only be used if // request.Host is empty. DNS string @@ -81,19 +66,7 @@ type SignAuthority interface { // New returns a new Authority that implements the SCEP interface. func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { - if _, ok := ops.DB.(*database.SimpleDB); !ok { - // If it's not a SimpleDB then go ahead and bootstrap the DB with the - // necessary SCEP tables. SimpleDB should ONLY be used for testing. - tables := [][]byte{certTable} - for _, b := range tables { - if err := ops.DB.CreateTable(b); err != nil { - return nil, fmt.Errorf("%w: error creating table %s", err, string(b)) - } - } - } - authority := &Authority{ - backdate: ops.Backdate, db: ops.DB, prefix: ops.Prefix, dns: ops.DNS, @@ -102,7 +75,7 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { // TODO: this is not really nice to do; the Service should be removed // in its entirety to make this more interoperable with the rest of - // step-ca. + // step-ca, I think. if ops.Service != nil { authority.intermediateCertificate = ops.Service.certificateChain[0] authority.service = ops.Service @@ -200,12 +173,12 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err p7c, err := pkcs7.Parse(msg.P7.Content) if err != nil { - return err + return errors.Wrap(err, "error parsing pkcs7 content") } envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.decrypter) if err != nil { - return err + return errors.Wrap(err, "error decrypting encrypted pkcs7 content") } msg.pkiEnvelope = envelope @@ -214,19 +187,19 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err case microscep.CertRep: certs, err := microscep.CACerts(msg.pkiEnvelope) if err != nil { - return err + return errors.Wrap(err, "error extracting CA certs from pkcs7 degenerate data") } - msg.CertRepMessage.Certificate = certs[0] // TODO: check correctness of this + msg.CertRepMessage.Certificate = certs[0] return nil case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq: csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope) if err != nil { - return fmt.Errorf("parse CSR from pkiEnvelope: %w", err) + return errors.Wrap(err, "parse CSR from pkiEnvelope") } // check for challengePassword cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) if err != nil { - return fmt.Errorf("scep: parse challenge password in pkiEnvelope: %w", err) + return errors.Wrap(err, "parse challenge password in pkiEnvelope") } msg.CSRReqMessage = µscep.CSRReqMessage{ RawDecrypted: msg.pkiEnvelope, @@ -235,7 +208,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err } return nil case microscep.GetCRL, microscep.GetCert, microscep.CertPoll: - return fmt.Errorf("not implemented") //errNotImplemented + return errors.Errorf("not implemented") } return nil @@ -266,16 +239,13 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m } // Template data - data := x509util.NewTemplateData() - data.SetCommonName(csr.Subject.CommonName) - data.SetSANs(csr.DNSNames) - data.SetCertificateRequest(csr) + data := x509util.CreateTemplateData(csr.Subject.CommonName, csr.DNSNames) // Get authorizations from the SCEP provisioner. ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) signOps, err := p.AuthorizeSign(ctx, "") if err != nil { - return nil, fmt.Errorf("error retrieving authorization options from SCEP provisioner: %w", err) + return nil, errors.Wrap(err, "error retrieving authorization options from SCEP provisioner") } opts := provisioner.SignOptions{ @@ -285,19 +255,20 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) if err != nil { - return nil, fmt.Errorf("error creating template options from SCEP provisioner: %w", err) + return nil, errors.Wrap(err, "error creating template options from SCEP provisioner") } signOps = append(signOps, templateOptions) certChain, err := a.signAuth.Sign(csr, opts, signOps...) if err != nil { - return nil, fmt.Errorf("error generating certificate for order %w", err) + return nil, errors.Wrap(err, "error generating certificate for order") } + // take the issued certificate (only); https://tools.ietf.org/html/rfc8894#section-3.3.2 cert := certChain[0] - // create a degenerate cert structure - deg, err := degenerateCertificates([]*x509.Certificate{cert}) + // and create a degenerate cert structure + deg, err := microscep.DegenerateCertificates([]*x509.Certificate{cert}) if err != nil { return nil, err } @@ -370,13 +341,12 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m CertRepMessage: cr, } - // TODO: save more data? - _, err = newCert(a.db, CertOptions{ + // store the newly created certificate + err = newCert(a.db, CertOptions{ Leaf: certChain[0], Intermediates: certChain[1:], }) if err != nil { - fmt.Println(err) return nil, err } @@ -459,7 +429,7 @@ func (a *Authority) MatchChallengePassword(ctx context.Context, password string) return false, err } - if p.GetChallengePassword() == password { + if subtle.ConstantTimeCompare([]byte(p.GetChallengePassword()), []byte(password)) == 1 { return true, nil } @@ -491,21 +461,3 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { return caps } - -// degenerateCertificates creates degenerate certificates pkcs#7 type -func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { - var buf bytes.Buffer - for _, cert := range certs { - buf.Write(cert.Raw) - } - degenerate, err := pkcs7.DegenerateCertificate(buf.Bytes()) - if err != nil { - return nil, err - } - return degenerate, nil -} - -// Interface guards -var ( - _ Interface = (*Authority)(nil) -) diff --git a/scep/certificate.go b/scep/certificate.go index 5e43b762..39015af5 100644 --- a/scep/certificate.go +++ b/scep/certificate.go @@ -2,79 +2,20 @@ package scep import ( "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - "time" - "github.com/smallstep/nosql" + "github.com/pkg/errors" ) -type certificate struct { - ID string `json:"id"` - Created time.Time `json:"created"` - Leaf []byte `json:"leaf"` - Intermediates []byte `json:"intermediates"` -} - // CertOptions options with which to create and store a cert object. type CertOptions struct { Leaf *x509.Certificate Intermediates []*x509.Certificate } -func newCert(db nosql.DB, ops CertOptions) (*certificate, error) { - - // TODO: according to the RFC this should be IssuerAndSerialNumber, - // but sscep seems to use just the serial number for getcert - - id := ops.Leaf.SerialNumber.String() - - leaf := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: ops.Leaf.Raw, - }) - var intermediates []byte - for _, cert := range ops.Intermediates { - intermediates = append(intermediates, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: cert.Raw, - })...) - } - - cert := &certificate{ - ID: id, - Leaf: leaf, - Intermediates: intermediates, - Created: time.Now().UTC(), - } - certB, err := json.Marshal(cert) +func newCert(db DB, ops CertOptions) error { + err := db.StoreCertificate(ops.Leaf) if err != nil { - return nil, fmt.Errorf("%w: error marshaling certificate", err) - } - - _, swapped, err := db.CmpAndSwap(certTable, []byte(id), nil, certB) - switch { - case err != nil: - return nil, fmt.Errorf("%w: error storing certificate", err) - case !swapped: - return nil, fmt.Errorf("error storing certificate; " + - "value has changed since last read") - default: - return cert, nil + errors.Wrap(err, "error while storing certificate") } + return nil } - -// func getCert(db nosql.DB, id string) (*certificate, error) { -// b, err := db.Get(certTable, []byte(id)) -// if nosql.IsErrNotFound(err) { -// return nil, fmt.Errorf("certificate %s not found", id) -// } else if err != nil { -// return nil, fmt.Errorf("error loading certificate") -// } -// var cert certificate -// if err := json.Unmarshal(b, &cert); err != nil { -// return nil, fmt.Errorf("%w: error unmarshaling certificate", err) -// } -// return &cert, nil -// } diff --git a/scep/database.go b/scep/database.go new file mode 100644 index 00000000..f73573fd --- /dev/null +++ b/scep/database.go @@ -0,0 +1,7 @@ +package scep + +import "crypto/x509" + +type DB interface { + StoreCertificate(crt *x509.Certificate) error +} diff --git a/scep/options.go b/scep/options.go new file mode 100644 index 00000000..61dbe024 --- /dev/null +++ b/scep/options.go @@ -0,0 +1,31 @@ +package scep + +import ( + "crypto" + "crypto/x509" +) + +type Options struct { + // CertificateChain is the issuer certificate, along with any other bundled certificates + // to be returned in the chain for consumers. Configured in ca.json crt property. + CertificateChain []*x509.Certificate + // Signer signs CSRs in SCEP. Configured in ca.json key property. + Signer crypto.Signer `json:"-"` + // Decrypter decrypts encrypted SCEP messages. Configured in ca.json key property. + Decrypter crypto.Decrypter `json:"-"` +} + +// Validate checks the fields in Options. +func (o *Options) Validate() error { + // var typ Type + // if o == nil { + // typ = Type(SoftCAS) + // } else { + // typ = Type(o.Type) + // } + // // Check that the type can be loaded. + // if _, ok := LoadCertificateAuthorityServiceNewFunc(typ); !ok { + // return errors.Errorf("unsupported cas type %s", typ) + // } + return nil +} diff --git a/scep/service.go b/scep/service.go index e9a58646..508bcf77 100644 --- a/scep/service.go +++ b/scep/service.go @@ -4,9 +4,6 @@ import ( "context" "crypto" "crypto/x509" - "strings" - - "github.com/smallstep/certificates/cas/apiv1" ) // Service is a wrapper for crypto.Signer and crypto.Decrypter @@ -16,20 +13,12 @@ type Service struct { decrypter crypto.Decrypter } -func NewService(ctx context.Context, opts apiv1.Options) (*Service, error) { +func NewService(ctx context.Context, opts Options) (*Service, error) { if err := opts.Validate(); err != nil { return nil, err } - t := apiv1.Type(strings.ToLower(opts.Type)) - if t == apiv1.DefaultCAS { - t = apiv1.SoftCAS - } - - // TODO: silence the linter (temporarily) - _ = t - // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? return &Service{ certificateChain: opts.CertificateChain, From 4cd45f6374c9e8b9389a74e74fbf7c4e9b6ba5ab Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 14:02:52 +0100 Subject: [PATCH 051/291] Remove superfluous call to StoreCertificate --- scep/authority.go | 13 ------------- scep/certificate.go | 21 --------------------- scep/errors.go | 7 ------- 3 files changed, 41 deletions(-) delete mode 100644 scep/certificate.go diff --git a/scep/authority.go b/scep/authority.go index c73885e5..9a0a2058 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -34,7 +34,6 @@ type Interface interface { // Authority is the layer that handles all SCEP interactions. type Authority struct { - db DB prefix string dns string intermediateCertificate *x509.Certificate @@ -46,8 +45,6 @@ type Authority struct { type AuthorityOptions struct { // Service provides the certificate chain, the signer and the decrypter to the Authority Service *Service - // DB is the database used by SCEP - DB DB // DNS is the host used to generate accurate SCEP links. By default the authority // will use the Host from the request, so this value will only be used if // request.Host is empty. @@ -67,7 +64,6 @@ type SignAuthority interface { func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { authority := &Authority{ - db: ops.DB, prefix: ops.Prefix, dns: ops.DNS, signAuth: signAuth, @@ -341,15 +337,6 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m CertRepMessage: cr, } - // store the newly created certificate - err = newCert(a.db, CertOptions{ - Leaf: certChain[0], - Intermediates: certChain[1:], - }) - if err != nil { - return nil, err - } - return crepMsg, nil } diff --git a/scep/certificate.go b/scep/certificate.go deleted file mode 100644 index 39015af5..00000000 --- a/scep/certificate.go +++ /dev/null @@ -1,21 +0,0 @@ -package scep - -import ( - "crypto/x509" - - "github.com/pkg/errors" -) - -// CertOptions options with which to create and store a cert object. -type CertOptions struct { - Leaf *x509.Certificate - Intermediates []*x509.Certificate -} - -func newCert(db DB, ops CertOptions) error { - err := db.StoreCertificate(ops.Leaf) - if err != nil { - errors.Wrap(err, "error while storing certificate") - } - return nil -} diff --git a/scep/errors.go b/scep/errors.go index 8454e16d..4287403b 100644 --- a/scep/errors.go +++ b/scep/errors.go @@ -2,18 +2,11 @@ package scep // Error is an SCEP error type type Error struct { - // Type ProbType - // Detail string Message string `json:"message"` Status int `json:"-"` - // Sub []*Error - // Identifier *Identifier } // Error implements the error interface. func (e *Error) Error() string { - // if e.Err == nil { - // return e.Detail - // } return e.Message } From a0242ad6ce7a764d1b2f13f07c78c7ed54a36466 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 15:22:04 +0100 Subject: [PATCH 052/291] Add validation to SCEP Options --- scep/options.go | 66 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/scep/options.go b/scep/options.go index 61dbe024..a8aad659 100644 --- a/scep/options.go +++ b/scep/options.go @@ -2,30 +2,70 @@ package scep import ( "crypto" + "crypto/rsa" "crypto/x509" + + "github.com/pkg/errors" ) type Options struct { // CertificateChain is the issuer certificate, along with any other bundled certificates - // to be returned in the chain for consumers. Configured in ca.json crt property. + // to be returned in the chain for consumers. Configured in the ca.json crt property. CertificateChain []*x509.Certificate - // Signer signs CSRs in SCEP. Configured in ca.json key property. + // Signer signs CSRs in SCEP. Configured in the ca.json key property. Signer crypto.Signer `json:"-"` - // Decrypter decrypts encrypted SCEP messages. Configured in ca.json key property. + // Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property. Decrypter crypto.Decrypter `json:"-"` } // Validate checks the fields in Options. func (o *Options) Validate() error { - // var typ Type - // if o == nil { - // typ = Type(SoftCAS) - // } else { - // typ = Type(o.Type) - // } - // // Check that the type can be loaded. - // if _, ok := LoadCertificateAuthorityServiceNewFunc(typ); !ok { - // return errors.Errorf("unsupported cas type %s", typ) - // } + + if o.CertificateChain == nil { + return errors.New("certificate chain not configured correctly") + } + + if len(o.CertificateChain) < 1 { + return errors.New("certificate chain should at least have one certificate") + } + + // According to the RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP + // can be used with something different than RSA, but requires the encryption + // to be performed using the challenge password. An older version of specification + // states that only RSA is supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1 + // Other algorithms than RSA do not seem to be supported in certnanny/sscep, but it might work + // in micromdm/scep. Currently only RSA is allowed, but it might be an option + // to try other algorithms in the future. + intermediate := o.CertificateChain[0] + if intermediate.PublicKeyAlgorithm != x509.RSA { + return errors.New("only the RSA algorithm is (currently) supported") + } + + // TODO: add checks for key usage? + + signerPublicKey, ok := o.Signer.Public().(*rsa.PublicKey) + if !ok { + return errors.New("only RSA public keys are (currently) supported as signers") + } + + // check if the intermediate ca certificate has the same public key as the signer. + // According to the RFC it seems valid to have different keys for the intermediate + // and the CA signing new certificates, so this might change in the future. + if !signerPublicKey.Equal(intermediate.PublicKey) { + return errors.New("mismatch between certificate chain and signer public keys") + } + + decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) + if !ok { + return errors.New("only RSA public keys are (currently) supported as decrypters") + } + + // check if intermedate public key is the same as the decrypter public key. + // In certnanny/sscep it's mentioned that the signing key can be different + // from the decrypting (and encrypting) key. Currently that's not supported. + if !decrypterPublicKey.Equal(intermediate.PublicKey) { + return errors.New("mismatch between certificate chain and decrypter public keys") + } + return nil } From bcacd2f4da407bd781c1af50230cd3aace7e54bd Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 15:24:27 +0100 Subject: [PATCH 053/291] Fix typo --- scep/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scep/options.go b/scep/options.go index a8aad659..752b309a 100644 --- a/scep/options.go +++ b/scep/options.go @@ -60,7 +60,7 @@ func (o *Options) Validate() error { return errors.New("only RSA public keys are (currently) supported as decrypters") } - // check if intermedate public key is the same as the decrypter public key. + // check if intermediate public key is the same as the decrypter public key. // In certnanny/sscep it's mentioned that the signing key can be different // from the decrypting (and encrypting) key. Currently that's not supported. if !decrypterPublicKey.Equal(intermediate.PublicKey) { From 13fe7a01213150a10c7b2d305b26a9b007db528b Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 15:44:45 +0100 Subject: [PATCH 054/291] Make serving SCEP endpoints optional Only when a SCEP provisioner is enabled, the SCEP endpoints will now be available. The SCEP endpoints will be served on an "insecure" server, without TLS, only when an additional "insecureAddress" and a SCEP provisioner are configured for the CA. --- authority/authority.go | 2 ++ ca/ca.go | 14 ++++++++------ scep/scep.go | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index a0eaf871..b5924061 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -575,6 +575,8 @@ func (a *Authority) CloseForReload() { // requiresDecrypter returns whether the Authority // requires a KMS that provides a crypto.Decrypter +// Currently this is only required when SCEP is +// enabled. func (a *Authority) requiresDecrypter() bool { return a.requiresSCEPService() } diff --git a/ca/ca.go b/ca/ca.go index 6eb223eb..4998b45b 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -118,6 +118,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { if err != nil { return nil, err } + ca.auth = auth tlsConfig, err := ca.getTLSConfig(auth) if err != nil { @@ -233,14 +234,15 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { handler = logger.Middleware(handler) } - ca.auth = auth ca.srv = server.New(config.Address, handler, tlsConfig) - // TODO: instead opt for having a single server.Server but two - // http.Servers handling the HTTP and HTTPS handler? The latter - // will probably introduce more complexity in terms of graceful - // reload. - if config.InsecureAddress != "" { + // only start the insecure server if the insecure address is configured + // and, currently, also only when it should serve SCEP endpoints. + if ca.shouldServeSCEPEndpoints() && config.InsecureAddress != "" { + // TODO: instead opt for having a single server.Server but two + // http.Servers handling the HTTP and HTTPS handler? The latter + // will probably introduce more complexity in terms of graceful + // reload. ca.insecureSrv = server.New(config.InsecureAddress, insecureHandler, nil) } diff --git a/scep/scep.go b/scep/scep.go index f56176d7..3323ac1d 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -33,7 +33,6 @@ var ( oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7} oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24} //oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7} - ) // PKIMessage defines the possible SCEP message types From 1cd0cb99f638fc20c6db494b935965ffbc6c353c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 16:11:35 +0100 Subject: [PATCH 055/291] Add more template data --- scep/authority.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/scep/authority.go b/scep/authority.go index 9a0a2058..3443eb51 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -4,7 +4,6 @@ import ( "context" "crypto/subtle" "crypto/x509" - "fmt" "net/url" "github.com/smallstep/certificates/authority/provisioner" @@ -109,9 +108,9 @@ func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, // URL dynamically obtained from the request for which the link is being calculated. func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *url.URL, inputs ...string) string { - // TODO: do we need to provide a way to provide a different suffix/base? + // TODO: do we need to provide a way to provide a different suffix? // Like "/cgi-bin/pkiclient.exe"? Or would it be enough to have that as the name? - link := fmt.Sprintf("/%s", provisionerName) + link := "/" + provisionerName if abs { // Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351 @@ -235,7 +234,31 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m } // Template data - data := x509util.CreateTemplateData(csr.Subject.CommonName, csr.DNSNames) + sans := []string{} + sans = append(sans, csr.DNSNames...) + sans = append(sans, csr.EmailAddresses...) + for _, v := range csr.IPAddresses { + sans = append(sans, v.String()) + } + for _, v := range csr.URIs { + sans = append(sans, v.String()) + } + if len(sans) == 0 { + sans = append(sans, csr.Subject.CommonName) + } + data := x509util.CreateTemplateData(csr.Subject.CommonName, sans) + data.SetCertificateRequest(csr) + data.SetSubject(x509util.Subject{ + Country: csr.Subject.Country, + Organization: csr.Subject.Organization, + OrganizationalUnit: csr.Subject.OrganizationalUnit, + Locality: csr.Subject.Locality, + Province: csr.Subject.Province, + StreetAddress: csr.Subject.StreetAddress, + PostalCode: csr.Subject.PostalCode, + SerialNumber: csr.Subject.SerialNumber, + CommonName: csr.Subject.CommonName, + }) // Get authorizations from the SCEP provisioner. ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) From 03c472359c65a93fb1cf5c329d4b8495398d1088 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 16:21:02 +0100 Subject: [PATCH 056/291] Add sync.WaitGroup for proper error handling in Run() --- ca/ca.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/ca/ca.go b/ca/ca.go index 4998b45b..1882e8a6 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "reflect" + "sync" "github.com/go-chi/chi" "github.com/pkg/errors" @@ -251,7 +252,29 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { // Run starts the CA calling to the server ListenAndServe method. func (ca *CA) Run() error { - return ca.srv.ListenAndServe() + var wg sync.WaitGroup + errors := make(chan error, 1) + + if ca.insecureSrv != nil { + wg.Add(1) + go func() { + defer wg.Done() + errors <- ca.insecureSrv.ListenAndServe() + }() + } + + wg.Add(1) + go func() { + defer wg.Done() + errors <- ca.srv.ListenAndServe() + }() + + // wait till error occurs; ensures the servers keep listening + err := <-errors + + wg.Wait() + + return err } // Stop stops the CA calling to the server Shutdown method. From 66a67ed691948de8aa7cc90a23bc0e102e85263a Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 Mar 2021 22:04:18 +0100 Subject: [PATCH 057/291] Update to v2.0.0 of github.com/micromdm/scep --- go.mod | 3 +- go.sum | 252 ++++++++++++++++++++++++++++++++++++++++++++++ scep/api/api.go | 2 +- scep/authority.go | 4 +- scep/scep.go | 2 +- 5 files changed, 258 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5ccc3aa8..8a2fe4a1 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,13 @@ require ( github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 github.com/go-chi/chi v4.0.2+incompatible + github.com/go-kit/kit v0.10.0 // indirect github.com/go-piv/piv-go v1.7.0 github.com/golang/mock v1.4.4 github.com/google/uuid v1.1.2 github.com/googleapis/gax-go/v2 v2.0.5 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 + github.com/micromdm/scep/v2 v2.0.0 github.com/newrelic/go-agent v2.15.0+incompatible github.com/pkg/errors v0.9.1 github.com/rs/xid v1.2.1 diff --git a/go.sum b/go.sum index 4883af5e..80b154e1 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= @@ -55,38 +56,69 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +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/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= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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/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= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/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= @@ -97,19 +129,30 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= 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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= @@ -119,16 +162,31 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.4.0 h1:KeVK+Emj3c3S4eRztFuzbFYb2BAgf2jmwDwyXEri7Lo= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +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-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 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= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-piv/piv-go v1.7.0 h1:rfjdFdASfGV5KLJhSjgpGJ5lzVZVtRWn8ovy/H9HQ/U= github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8HKy7Gk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +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/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= @@ -156,6 +214,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -171,6 +230,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= @@ -186,22 +246,51 @@ github.com/google/pprof v0.0.0-20201009210932-67992a1a5a35 h1:WL9iUw2tSwvaCb3++2 github.com/google/pprof v0.0.0-20201009210932-67992a1a5a35/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -209,13 +298,22 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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/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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= 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/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -234,9 +332,12 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= @@ -244,11 +345,13 @@ github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEX github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= <<<<<<< HEAD +<<<<<<< HEAD ======= github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 h1:QACkVsQ7Qx4PuPDFL2OFD5u4OnYT0TkReWk9IVqaGHA= github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23/go.mod h1:a82oNZyGV8jj9Do7dh8EkA90+esBls0CZHR6B85Qda8= @@ -257,28 +360,95 @@ github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQB github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY= github.com/micromdm/scep v1.0.0/go.mod h1:CID2SixSr5FvoauZdAFUSpQkn5MAuSy9oyURMGOJbag= +======= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= +github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +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= +>>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/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= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +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/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= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +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_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 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= 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/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/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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= @@ -287,10 +457,14 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE= @@ -298,6 +472,10 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= +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/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= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -306,14 +484,19 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -324,11 +507,15 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= 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= @@ -336,10 +523,17 @@ 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 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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= +<<<<<<< HEAD go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +======= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +>>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -350,12 +544,27 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= +<<<<<<< HEAD go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +======= +go.step.sm/crypto v0.8.0 h1:S4qBPyy3hR7KWLybSkHB0H14pwFfYkom4RZ96JzmXig= +go.step.sm/crypto v0.8.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +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= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +>>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -400,16 +609,24 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -445,7 +662,13 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -459,9 +682,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -490,16 +715,20 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -509,6 +738,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -516,6 +747,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -542,6 +774,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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= +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= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -561,6 +794,7 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.33.0 h1:+gL0XvACeMIvpwLZ5rQZzLn5cwOsgg8dIcfJ2SYfBVw= google.golang.org/api v0.33.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -572,6 +806,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -599,10 +834,15 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 h1:bFFRpT+e8JJVY7lMMfvezL1ZIwqiwmPl2bsE2yx4HqM= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -625,17 +865,27 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -650,3 +900,5 @@ rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/scep/api/api.go b/scep/api/api.go index cff3d32b..c19c090f 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -18,7 +18,7 @@ import ( "github.com/pkg/errors" - microscep "github.com/micromdm/scep/scep" + microscep "github.com/micromdm/scep/v2/scep" ) const ( diff --git a/scep/authority.go b/scep/authority.go index 3443eb51..34913a84 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -8,8 +8,8 @@ import ( "github.com/smallstep/certificates/authority/provisioner" - microx509util "github.com/micromdm/scep/crypto/x509util" - microscep "github.com/micromdm/scep/scep" + microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util" + microscep "github.com/micromdm/scep/v2/scep" "github.com/pkg/errors" diff --git a/scep/scep.go b/scep/scep.go index 3323ac1d..afabf368 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -4,7 +4,7 @@ import ( "crypto/x509" "encoding/asn1" - microscep "github.com/micromdm/scep/scep" + microscep "github.com/micromdm/scep/v2/scep" //"github.com/smallstep/certificates/scep/pkcs7" From fa100a513823bafe925c77649f0df90f80f8f3fc Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 16 Apr 2021 14:09:34 +0200 Subject: [PATCH 058/291] Mask challenge password after it has been read --- authority/provisioner/scep.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 6af3dc83..7f3cce8f 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -20,6 +20,8 @@ type SCEP struct { Options *Options `json:"options,omitempty"` Claims *Claims `json:"claims,omitempty"` claimer *Claimer + + secretChallengePassword string } // GetID returns the provisioner unique identifier. @@ -73,6 +75,10 @@ func (s *SCEP) Init(config Config) (err error) { return err } + // Mask the actual challenge value, so it won't be marshalled + s.secretChallengePassword = s.ChallengePassword + s.ChallengePassword = "*** redacted ***" + // TODO: add other, SCEP specific, options? return err @@ -95,7 +101,7 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // GetChallengePassword returns the challenge password func (s *SCEP) GetChallengePassword() string { - return s.ChallengePassword + return s.secretChallengePassword } // GetCapabilities returns the CA capabilities From 4168449935dfe630e294855909e3a59786a2c504 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 16 Apr 2021 15:49:33 +0200 Subject: [PATCH 059/291] Fix typo --- authority/provisioner/scep.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 7f3cce8f..49f53860 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -75,7 +75,7 @@ func (s *SCEP) Init(config Config) (err error) { return err } - // Mask the actual challenge value, so it won't be marshalled + // Mask the actual challenge value, so it won't be marshaled s.secretChallengePassword = s.ChallengePassword s.ChallengePassword = "*** redacted ***" From 2beea1aa89ecbc4a9c6a300d8ffbada98faf0d4d Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 6 May 2021 22:56:28 +0200 Subject: [PATCH 060/291] Add configuration option for specifying the minimum public key length Instead of using the defaultPublicKeyValidator a new validator called publicKeyMinimumLengthValidator has been implemented that uses a configurable minimum length for public keys in CSRs. It's also an option to alter the defaultPublicKeyValidator to also take a parameter, but that would touch quite some lines of code. This might be a viable option after merging SCEP support. --- authority/provisioner/scep.go | 20 +++++++++++++---- authority/provisioner/sign_options.go | 31 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 49f53860..a5825b9b 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -2,6 +2,7 @@ package provisioner import ( "context" + "fmt" "time" "github.com/pkg/errors" @@ -17,9 +18,11 @@ type SCEP struct { ForceCN bool `json:"forceCN,omitempty"` ChallengePassword string `json:"challenge,omitempty"` Capabilities []string `json:"capabilities,omitempty"` - Options *Options `json:"options,omitempty"` - Claims *Claims `json:"claims,omitempty"` - claimer *Claimer + // MinimumPublicKeyLength is the minimum length for public keys in CSRs + MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` + Options *Options `json:"options,omitempty"` + Claims *Claims `json:"claims,omitempty"` + claimer *Claimer secretChallengePassword string } @@ -79,6 +82,15 @@ func (s *SCEP) Init(config Config) (err error) { s.secretChallengePassword = s.ChallengePassword s.ChallengePassword = "*** redacted ***" + // Default to 2048 bits minimum public key length (for CSRs) if not set + if s.MinimumPublicKeyLength == 0 { + s.MinimumPublicKeyLength = 2048 + } + + if s.MinimumPublicKeyLength%8 != 0 { + return fmt.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisibly by 8", s.MinimumPublicKeyLength) + } + // TODO: add other, SCEP specific, options? return err @@ -94,7 +106,7 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e newForceCNOption(s.ForceCN), profileDefaultDuration(s.claimer.DefaultTLSCertDuration()), // validators - defaultPublicKeyValidator{}, + newPublicKeyMinimumLengthValidator(s.MinimumPublicKeyLength), newValidityValidator(s.claimer.MinTLSCertDuration(), s.claimer.MaxTLSCertDuration()), }, nil } diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index 3b52d497..20b6f8c0 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -8,6 +8,7 @@ import ( "crypto/x509/pkix" "encoding/asn1" "encoding/json" + "fmt" "net" "net/url" "reflect" @@ -117,6 +118,36 @@ func (v defaultPublicKeyValidator) Valid(req *x509.CertificateRequest) error { return nil } +// publicKeyMinimumLengthValidator validates the length (in bits) of the public key +// of a certificate request is at least a certain length +type publicKeyMinimumLengthValidator struct { + length int +} + +// newPublicKeyMinimumLengthValidator creates a new publicKeyMinimumLengthValidator +// with the given length as its minimum value +// TODO: change the defaultPublicKeyValidator to have a configurable length instead? +func newPublicKeyMinimumLengthValidator(length int) publicKeyMinimumLengthValidator { + return publicKeyMinimumLengthValidator{ + length: length, + } +} + +// Valid checks that certificate request common name matches the one configured. +func (v publicKeyMinimumLengthValidator) Valid(req *x509.CertificateRequest) error { + switch k := req.PublicKey.(type) { + case *rsa.PublicKey: + minimumLengthInBytes := v.length / 8 + if k.Size() < minimumLengthInBytes { + return fmt.Errorf("rsa key in CSR must be at least %d bits (%d bytes)", v.length, minimumLengthInBytes) + } + case *ecdsa.PublicKey, ed25519.PublicKey: + default: + return errors.Errorf("unrecognized public key of type '%T' in CSR", k) + } + return nil +} + // commonNameValidator validates the common name of a certificate request. type commonNameValidator string From d46a4eaca4a33c49bc5468a0dda6419a4cbdfbff Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 7 May 2021 00:22:06 +0200 Subject: [PATCH 061/291] Change fmt to errors package for formatting errors --- authority/provisioner/scep.go | 3 +-- authority/provisioner/sign_options.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index a5825b9b..bedf1733 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -2,7 +2,6 @@ package provisioner import ( "context" - "fmt" "time" "github.com/pkg/errors" @@ -88,7 +87,7 @@ func (s *SCEP) Init(config Config) (err error) { } if s.MinimumPublicKeyLength%8 != 0 { - return fmt.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisibly by 8", s.MinimumPublicKeyLength) + return errors.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisibly by 8", s.MinimumPublicKeyLength) } // TODO: add other, SCEP specific, options? diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index 20b6f8c0..764916b6 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -8,7 +8,6 @@ import ( "crypto/x509/pkix" "encoding/asn1" "encoding/json" - "fmt" "net" "net/url" "reflect" @@ -139,7 +138,7 @@ func (v publicKeyMinimumLengthValidator) Valid(req *x509.CertificateRequest) err case *rsa.PublicKey: minimumLengthInBytes := v.length / 8 if k.Size() < minimumLengthInBytes { - return fmt.Errorf("rsa key in CSR must be at least %d bits (%d bytes)", v.length, minimumLengthInBytes) + return errors.Errorf("rsa key in CSR must be at least %d bits (%d bytes)", v.length, minimumLengthInBytes) } case *ecdsa.PublicKey, ed25519.PublicKey: default: From 382b6f977cb4b3ba9ea407c6b554000efb972f70 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 7 May 2021 00:23:09 +0200 Subject: [PATCH 062/291] Improve error logging --- scep/api/api.go | 28 +++++++++++++++++----------- scep/authority.go | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scep/api/api.go b/scep/api/api.go index c19c090f..e64eef83 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -51,6 +51,7 @@ type SCEPResponse struct { CACertNum int Data []byte Certificate *x509.Certificate + Error error } // Handler is the SCEP request handler. @@ -75,7 +76,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { request, err := decodeSCEPRequest(r) if err != nil { - writeError(w, errors.Wrap(err, "not a scep get request")) + writeError(w, errors.Wrap(err, "invalid scep get request")) return } @@ -94,7 +95,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { } if err != nil { - writeError(w, errors.Wrap(err, "get request failed")) + writeError(w, errors.Wrap(err, "scep get request failed")) return } @@ -106,7 +107,7 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { request, err := decodeSCEPRequest(r) if err != nil { - writeError(w, errors.Wrap(err, "not a scep post request")) + writeError(w, errors.Wrap(err, "invalid scep post request")) return } @@ -121,7 +122,7 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) { } if err != nil { - writeError(w, errors.Wrap(err, "post request failed")) + writeError(w, errors.Wrap(err, "scep post request failed")) return } @@ -193,13 +194,13 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) if err != nil { - writeError(w, err) + api.WriteError(w, err) return } provisioner, ok := p.(*provisioner.SCEP) if !ok { - writeError(w, errors.New("provisioner must be of type SCEP")) + api.WriteError(w, errors.New("provisioner must be of type SCEP")) return } @@ -293,12 +294,12 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword) if err != nil { - return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when checking password") + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("error when checking password")) } if !challengeMatches { // TODO: can this be returned safely to the client? In the end, if the password was correct, that gains a bit of info too. - return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "wrong password provided") + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("wrong password provided")) } } @@ -306,7 +307,7 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe certRep, err := h.Auth.SignCSR(ctx, csr, msg) if err != nil { - return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when signing new certificate") + return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.Wrap(err, "error when signing new certificate")) } response := SCEPResponse{ @@ -325,6 +326,10 @@ func formatCapabilities(caps []string) []byte { // writeSCEPResponse writes a SCEP response back to the SCEP client. func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { + if response.Error != nil { + api.LogError(w, response.Error) + } + if response.Certificate != nil { api.LogCertificate(w, response.Certificate) } @@ -344,14 +349,15 @@ func writeError(w http.ResponseWriter, err error) { api.WriteError(w, scepError) } -func (h *Handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, infoText string) (SCEPResponse, error) { - certRepMsg, err := h.Auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), infoText) +func (h *Handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, failError error) (SCEPResponse, error) { + certRepMsg, err := h.Auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), failError.Error()) if err != nil { return SCEPResponse{}, err } return SCEPResponse{ Operation: opnPKIOperation, Data: certRepMsg.Raw, + Error: failError, }, nil } diff --git a/scep/authority.go b/scep/authority.go index 34913a84..47d1d41c 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -416,7 +416,7 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi cr := &CertRepMessage{ PKIStatus: microscep.FAILURE, - FailInfo: microscep.BadRequest, + FailInfo: microscep.FailInfo(info), RecipientNonce: microscep.RecipientNonce(msg.SenderNonce), } From a64974c1796d938efb71c9221a1b58bbb5e0722e Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 7 May 2021 00:31:34 +0200 Subject: [PATCH 063/291] Fix small typo in divisible --- authority/provisioner/scep.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index bedf1733..7673ecc2 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -87,7 +87,7 @@ func (s *SCEP) Init(config Config) (err error) { } if s.MinimumPublicKeyLength%8 != 0 { - return errors.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisibly by 8", s.MinimumPublicKeyLength) + return errors.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisible by 8", s.MinimumPublicKeyLength) } // TODO: add other, SCEP specific, options? From 74d8bdc298a8578baa63be3bb83165107190c613 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 7 May 2021 15:32:07 +0200 Subject: [PATCH 064/291] Add tests for CreateDecrypter --- kms/softkms/softkms_test.go | 69 +++++++++++++++++++++++++++++++ kms/softkms/testdata/rsa.priv.pem | 30 ++++++++++++++ kms/softkms/testdata/rsa.pub.pem | 9 ++++ 3 files changed, 108 insertions(+) create mode 100644 kms/softkms/testdata/rsa.priv.pem create mode 100644 kms/softkms/testdata/rsa.pub.pem diff --git a/kms/softkms/softkms_test.go b/kms/softkms/softkms_test.go index 607a5a51..9e293b07 100644 --- a/kms/softkms/softkms_test.go +++ b/kms/softkms/softkms_test.go @@ -310,3 +310,72 @@ func Test_generateKey(t *testing.T) { }) } } + +func TestSoftKMS_CreateDecrypter(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + pemBlock, err := pemutil.Serialize(privateKey) + if err != nil { + t.Fatal(err) + } + pemBlockPassword, err := pemutil.Serialize(privateKey, pemutil.WithPassword([]byte("pass"))) + if err != nil { + t.Fatal(err) + } + ecdsaPK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + ecdsaPemBlock, err := pemutil.Serialize(ecdsaPK) + if err != nil { + t.Fatal(err) + } + b, err := ioutil.ReadFile("testdata/rsa.priv.pem") + if err != nil { + t.Fatal(err) + } + block, _ := pem.Decode(b) + block.Bytes, err = x509.DecryptPEMBlock(block, []byte("pass")) //nolint + if err != nil { + t.Fatal(err) + } + keyFromFile, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + t.Fatal(err) + } + type args struct { + req *apiv1.CreateDecrypterRequest + } + tests := []struct { + name string + args args + want crypto.Decrypter + wantErr bool + }{ + {"decrypter", args{&apiv1.CreateDecrypterRequest{Decrypter: privateKey}}, privateKey, false}, + {"file", args{&apiv1.CreateDecrypterRequest{DecryptionKey: "testdata/rsa.priv.pem", Password: []byte("pass")}}, keyFromFile, false}, + {"pem", args{&apiv1.CreateDecrypterRequest{DecryptionKeyPEM: pem.EncodeToMemory(pemBlock)}}, privateKey, false}, + {"pem password", args{&apiv1.CreateDecrypterRequest{DecryptionKeyPEM: pem.EncodeToMemory(pemBlockPassword), Password: []byte("pass")}}, privateKey, false}, + {"fail none", args{&apiv1.CreateDecrypterRequest{}}, nil, true}, + {"fail missing", args{&apiv1.CreateDecrypterRequest{DecryptionKey: "testdata/missing"}}, nil, true}, + {"fail bad pem", args{&apiv1.CreateDecrypterRequest{DecryptionKeyPEM: []byte("bad pem")}}, nil, true}, + {"fail bad password", args{&apiv1.CreateDecrypterRequest{DecryptionKeyPEM: pem.EncodeToMemory(pemBlockPassword), Password: []byte("bad-pass")}}, nil, true}, + {"fail not a decrypter (ecdsa key)", args{&apiv1.CreateDecrypterRequest{DecryptionKeyPEM: pem.EncodeToMemory(ecdsaPemBlock)}}, nil, true}, + {"fail not a decrypter from file", args{&apiv1.CreateDecrypterRequest{DecryptionKey: "testdata/rsa.pub.pem"}}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &SoftKMS{} + got, err := k.CreateDecrypter(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("SoftKMS.CreateDecrypter(), error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("SoftKMS.CreateDecrypter() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/kms/softkms/testdata/rsa.priv.pem b/kms/softkms/testdata/rsa.priv.pem new file mode 100644 index 00000000..497ec8d2 --- /dev/null +++ b/kms/softkms/testdata/rsa.priv.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,dff7bfd0e0163a4cd7ade8f68b966699 + +jtmOhr2zo244Oq2fVsShZAUoQZ1gi6Iwc4i0sReU66XP9CFkdvJasAfkjQGrbCEy +m2+r7W6aH+L3j/4sXcJe8h4UVnnC4DHCozmtqqFCq7cFS4TiVpco26wEVH5WLm7Y +3Ew/pL0k24E+Ycf+yV5c1tQXRlmsKubjwzrZtGZP2yn3Dxsu97mzOXAfx7r+DIKI +5a4S3m1/yXw76tt6Iho9h4huA25UUDHKUQvOGd5gmOKqJRV9djoyu85ODbmz5nt0 +pB2EzdHOrefgd0rcQQPI1uFBWqASJxTn+uS7ZBP4rlCcs932lI1mPerMh1ujo51F +3aibrwhKE6kaJyOOnUbvyBnaiTb5i4WwTqx/jfsOsggXQb3UlxgDph48VXw8O2jF +CQmle+TR8yr1A14/Dno5Dd4cqPv6AmWWU2zolvLxKQixFcvjsyQYCDajWWRPkOgj +RTKXDqL1mpjrlDqcSXzemCWk6FzqdUQhimhFgARDRfRwwDeWQN5ua4a3gnem/cpA +ZS8J45H0ZC/CxGPfp+qx75n5a875+n4VMmCZerXPzEIj1CzS7D6BVAXTHJaNIB6S +0WNfQnftp09O2l6iXBE+MHt5bVxqt46+vgcceSu7Gsb3ZfD79vnQ7tR+wb+xmHKk +8rVcMrB+kDRXVguH/a3zUGYAEnb6hPkIJywJVD4G65oM+D9D67Mdka8wIMK48doV +my8a0MfT/9AidR6XJVxIkHlPsPzlxirm/NKF7oSlzurcvYcPAYnHYLW2uB8dyidq +1zB+3rxbSYCVqrhqzN4prydGvkIE3/+AJyIGn7uGSTSSyF6BC9APXQaHplRGKwLz +efOIMoEwXJ1DIcKmk9GB65xxrZxMu3Cclcbc4PgY4370G0PfCHuUQNQL2RUWCQn0 +aax+qDiFg1LsLRaI75OaLJ+uKs6rRfytQMmFGqK/b6iVbktiYWMtrDJDo4OUTtZ6 +LBBySH7sAFgI3IIxct2Fwg8X1J4kfHr9jWTLjMEIE2o8cyqvSQ8rdwA25MxRcn75 +DGqSlGE6Sx0XhWCVUiZidVRSYGKmOmH9yw8cjKm17qL23t8Gwns4Xunl7V6YlTCG +BPw5f1jWCQ94TwvUSuHMPYoXlYwRoe+jfDAzp2AQwXqvWX5Qno5PKz9gQ5iYacZ/ +k82fyPbk2XLDkPnaNJKnyiIc252O0WffUlX6Rlv3aF8ZgVvWfZbuHEK6g1W+IKSA +pXAQ+iZBl+fjs/wT0yZSNTB0P1InD9Ve536L94gxXoeMr6F0Eouk3J2R9qdFp0Av +31xylRKSmzUf87/sRxjy3FzSTjIal77y1euJoAEU/nShmNrAZ6B8wnlvHfVwbgmt +xWqxYIi/j/C8Led9uhEhX2WjPsO7ckGA41Tw6hZk/5hr4jmPoZQKHf9OauJFujMh +ybPRQ6SGZJaYQAgpEGHSHFm8lwf5/DcezdSMdzqAKBWJBv6MediMuS60wcJ0Tebk +rdLkNE4bsxfc889BkXBrSqfd+Auu5RcF/kF44gLL7oj4ojQyV44vLZbC4+liGThT +bhayYGV64hsY+zL03u5wVfF1Y+33/uc8o/0JjbfuW5AIdikVES/jnKKFXSTMNL69 +-----END RSA PRIVATE KEY----- diff --git a/kms/softkms/testdata/rsa.pub.pem b/kms/softkms/testdata/rsa.pub.pem new file mode 100644 index 00000000..4bf5de9b --- /dev/null +++ b/kms/softkms/testdata/rsa.pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn2Oh7/uWB5RH40la1a43 +IRaLZ8EnJVw5DCKE3BUre8xflVY2wTIS7XHcY0fEGprtq7hzFKors9AIGGn2yGrf +bZX2I+1g+RtQ6cLL6koeLuhRDqCuae0lZPulWc5ixBmM9mpl4ARRcpQFldxFRhis +xUaHMx8VqdZjFSDc5CJHYYK1n2G5DyuzJCk6yOfyMpwxizZJF4IUyqV7zKmZv1z9 +/Xd8X0ag7jRdaTBpupJ1WLaq7LlvyB4nr47JXXkLFbRIL1F/gTcPtg0tdEZiKnxs +VLKwOs3VjhEorUwhmVxr4NnNX/0tuOY1FJ0mx5jKLAevqLVwK2JIg/f3h7JcNxDy +tQIDAQAB +-----END PUBLIC KEY----- From 7e82bd6ef394490c48dfb77527003b688fbd375f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 20 May 2021 21:31:52 +0200 Subject: [PATCH 065/291] Add setup for Authority tests --- authority/authority_test.go | 148 +++++++++++++++++++++++ authority/testdata/scep/intermediate.crt | 15 +++ authority/testdata/scep/intermediate.key | 30 +++++ authority/testdata/scep/root.crt | 10 ++ authority/testdata/scep/root.key | 8 ++ 5 files changed, 211 insertions(+) create mode 100644 authority/testdata/scep/intermediate.crt create mode 100644 authority/testdata/scep/intermediate.key create mode 100644 authority/testdata/scep/root.crt create mode 100644 authority/testdata/scep/root.key diff --git a/authority/authority_test.go b/authority/authority_test.go index e6625d6a..618e7939 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -6,6 +6,7 @@ import ( "crypto/sha256" "crypto/x509" "encoding/hex" + "fmt" "io/ioutil" "net" "reflect" @@ -320,3 +321,150 @@ func TestAuthority_CloseForReload(t *testing.T) { }) } } + +func testScepAuthority(t *testing.T, opts ...Option) *Authority { + + p := provisioner.List{ + &provisioner.SCEP{ + Name: "scep1", + Type: "SCEP", + }, + } + c := &Config{ + Address: "127.0.0.1:8443", + InsecureAddress: "127.0.0.1:8080", + Root: []string{"testdata/scep/root.crt"}, + IntermediateCert: "testdata/scep/intermediate.crt", + IntermediateKey: "testdata/scep/intermediate.key", + DNSNames: []string{"example.com"}, + Password: "pass", + AuthorityConfig: &AuthConfig{ + Provisioners: p, + }, + } + a, err := New(c, opts...) + assert.FatalError(t, err) + return a +} + +func TestAuthority_GetSCEPService(t *testing.T) { + auth := testScepAuthority(t) + fmt.Println(auth) + + p := provisioner.List{ + &provisioner.SCEP{ + Name: "scep1", + Type: "SCEP", + }, + } + + type fields struct { + config *Config + // keyManager kms.KeyManager + // provisioners *provisioner.Collection + // db db.AuthDB + // templates *templates.Templates + // x509CAService cas.CertificateAuthorityService + // rootX509Certs []*x509.Certificate + // federatedX509Certs []*x509.Certificate + // certificates *sync.Map + // scepService *scep.Service + // sshCAUserCertSignKey ssh.Signer + // sshCAHostCertSignKey ssh.Signer + // sshCAUserCerts []ssh.PublicKey + // sshCAHostCerts []ssh.PublicKey + // sshCAUserFederatedCerts []ssh.PublicKey + // sshCAHostFederatedCerts []ssh.PublicKey + // initOnce bool + // startTime time.Time + // sshBastionFunc func(ctx context.Context, user, hostname string) (*Bastion, error) + // sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error) + // sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]Host, error) + // getIdentityFunc provisioner.GetIdentityFunc + } + tests := []struct { + name string + fields fields + wantService bool + wantErr bool + }{ + { + name: "ok", + fields: fields{ + config: &Config{ + Address: "127.0.0.1:8443", + InsecureAddress: "127.0.0.1:8080", + Root: []string{"testdata/scep/root.crt"}, + IntermediateCert: "testdata/scep/intermediate.crt", + IntermediateKey: "testdata/scep/intermediate.key", + DNSNames: []string{"example.com"}, + Password: "pass", + AuthorityConfig: &AuthConfig{ + Provisioners: p, + }, + }, + }, + wantService: true, + wantErr: false, + }, + { + name: "wrong password", + fields: fields{ + config: &Config{ + Address: "127.0.0.1:8443", + InsecureAddress: "127.0.0.1:8080", + Root: []string{"testdata/scep/root.crt"}, + IntermediateCert: "testdata/scep/intermediate.crt", + IntermediateKey: "testdata/scep/intermediate.key", + DNSNames: []string{"example.com"}, + Password: "wrongpass", + AuthorityConfig: &AuthConfig{ + Provisioners: p, + }, + }, + }, + wantService: false, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // a := &Authority{ + // config: tt.fields.config, + // keyManager: tt.fields.keyManager, + // provisioners: tt.fields.provisioners, + // db: tt.fields.db, + // templates: tt.fields.templates, + // x509CAService: tt.fields.x509CAService, + // rootX509Certs: tt.fields.rootX509Certs, + // federatedX509Certs: tt.fields.federatedX509Certs, + // certificates: tt.fields.certificates, + // scepService: tt.fields.scepService, + // sshCAUserCertSignKey: tt.fields.sshCAUserCertSignKey, + // sshCAHostCertSignKey: tt.fields.sshCAHostCertSignKey, + // sshCAUserCerts: tt.fields.sshCAUserCerts, + // sshCAHostCerts: tt.fields.sshCAHostCerts, + // sshCAUserFederatedCerts: tt.fields.sshCAUserFederatedCerts, + // sshCAHostFederatedCerts: tt.fields.sshCAHostFederatedCerts, + // initOnce: tt.fields.initOnce, + // startTime: tt.fields.startTime, + // sshBastionFunc: tt.fields.sshBastionFunc, + // sshCheckHostFunc: tt.fields.sshCheckHostFunc, + // sshGetHostsFunc: tt.fields.sshGetHostsFunc, + // getIdentityFunc: tt.fields.getIdentityFunc, + // } + a, err := New(tt.fields.config) + fmt.Println(err) + fmt.Println(a) + if (err != nil) != tt.wantErr { + t.Errorf("Authority.New(), error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantService { + if got := a.GetSCEPService(); (got != nil) != tt.wantService { + t.Errorf("Authority.GetSCEPService() = %v, wantService %v", got, tt.wantService) + } + } + }) + } +} diff --git a/authority/testdata/scep/intermediate.crt b/authority/testdata/scep/intermediate.crt new file mode 100644 index 00000000..42ac4867 --- /dev/null +++ b/authority/testdata/scep/intermediate.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICZTCCAgugAwIBAgIQDPpOQXW7OLMFNR/+iOUdQjAKBggqhkjOPQQDAjAXMRUw +EwYDVQQDEwxzY2VwdGVzdHJvb3QwHhcNMjEwNTA3MTUyMjU2WhcNMzEwNTA1MTUy +MjU2WjAfMR0wGwYDVQQDExRzY2VwdGVzdGludGVybWVkaWF0ZTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJTw49z9/MeZ/YeRO89ylMV3HnYpw52/Vs2G +NsgYZRKiPz2RjixUp1iWRPoDONdlEOIAo0TALNOqz4EqJHB+FpBPBA1ZfwG/PlP/ +eWFubNXLXIhZPSQOiHmL4dIw0FS/VFGZm1eqc9JPG/V2G6UaKvOa8+W9/nhi4eeL ++/9nTwG4cTav9ltaVxQ55kcoJtMcvouYQ4oPSZ6yNuVYbFAoaqZnJqNQhxDvKsFH +lHmvl28FAVM+otmEQNTm91uPwXuVusxEGn9N/d7M4iojCiMGg0S3luBS8IrGRI1Y +bSKZvGsFnqUjHh2cLL1lqqo5+QvhvP9ut6+g8QGoq8NTc2yCRy8CAwEAAaNmMGQw +DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGfO +jTNTKTAyra+rAd/NL2ydarSFMB8GA1UdIwQYMBaAFKJr1p5QRfkHzewG3YEhPAtv +FQNrMAoGCCqGSM49BAMCA0gAMEUCIEYK76FN9a/hWkMZcQ+NXyzGtfW+bnwsX3oN +wT6jfyO0AiEAojTeSwf/H2l/E1lvsWJfNr8nOokWz+ZsbmMm5PU0Y+g= +-----END CERTIFICATE----- diff --git a/authority/testdata/scep/intermediate.key b/authority/testdata/scep/intermediate.key new file mode 100644 index 00000000..ae564056 --- /dev/null +++ b/authority/testdata/scep/intermediate.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,a54ae9388ce050f0a479a258d105fbb7 + +VkJp9kKZQ7O9Gy9orvXaO+klt4Lrqp9oSABSBy8yFcc3neniLixqcyZZ4+CC/OG2 +TGTm4TiB9RBucrUyPwoxBraWbtTLHvS4nfPwr2feSTKoHDhSIr4Z1VMDF8PWiOSg +vD3iYs5F1lz78hcB/SNdSZ2jm0ze84DFC2E49agWeiFLwezcLhXKQ2HHRJ6PmJv7 +IYB7+aLw8cUis/eJquWv7vrmlnshXBXLOrDekNq/mGhdpUmguDNEGX/3yT+8QYRv +yeCqLVWcfkQ7KkXAeet0tVPNGQQF0+yS80Hv2/LBcskhL467qa79Xm+QPbBbhsEB +aa4rettMLEdxk3IB1dgXdWhdJ4zBD+RFjczJbQlZRfmPb8sR20V/xp3x9i+SLqKp +seVoNF+LhLhEwJdMF23t2KpuiOShzC60ApjALN6/O2/XGCl0KQ+NzucX+wpirS6z +d2XfEYpsUaUFEFraOwfGXxLmluRtS6Q3+0+NPgwVQuH7EE7KuoTDUoSrUG4OFjaq +CeUeZv1IVf0sYqZQVRiMxxdoFBKUSgcaR1gzzLZgHeoZCGP0PewmZDfJMQ5rWe0D +zYYIKXUg8+oytHsz+5pQ277psXsl7iApZu56s6w3rD45w/zBeEyBhyL5JMBP8Y6y +7ReaUGsoFu3WEvrMcOsN+0Vag/SdQsvEH0PGA/ltlrlhaHKq+4t/ZwP6WxUmnaVV +JNtTWB8IqxtO0zbwK1owxjrO7t42K2isSryg/y2sQb4wgokoOzg1PqEaM8PIUvjl +qkGhwrOz4lNNQ9b6Hgy81DpnXnJkRNY7B5yKi62TCc6K/DHrFs0fHKb9Qxac5KKf +paasGWuEC5IP0lUyn81BmAVlfByBvnGmYiDmmGXLmfsyqtGFL9fpOl1Txq3/URfT +f705lzeUt9r2BT5FJtV5lkTntRzjpi5QeRiJsvfXA7nCPZj2hoLWgIm/D/HRgfVR +PIX1M7nxefRgES+T6UJNsBbGjSTgEVIPqVnyWs0JUyg4+KQ5VMU8g8SGA0dtnJyF +9JrZHy2OA/AYt/c96vJj4WdFvqw3kodIKOipBbKjBBGokaOTsLADFEYgOr51BfvO +QmxGZoXsRpD4sBOAwW039Ka5uCfuBETa+XQPtlHailaRZLlK9cZaDlzQr/K9jAgM +qOmZIKr3L8YPK3mQV+mWVYchPXTf+UyTFiWIt30z1JlyrTw1H+h62pV9f1QXDB6P +FIlfWHUK2mohWqzBnv4zFRBTVUnUDC9ONT+cVLh0cvlbRt2yy2ZgR4+d6IGH6mRH +VLgWAFpS3KS1/4NfwWRBaMvIBfqfXCzXSqVJsq7RlBSW/EBwe9TDXhcTzOLHjx4E +vdp+hqyXT62cTd7oWe78BBw3xOgpQwQ8bUdhye0kXMLNpU9j70pA7CjLVoVsdzH6 +n1EG7Mz/5NmXLy7LP8RuVU90mNQzNu8PFWtfjZ/jr3/OxoOc0Wx6mFykXkZbxKXI +xOlaOnUHKnEmsCLnZUkIxEqwKo+RYWBRtKxYsS8x8TLXyFGEfHidI75ulZM7eAS8 +jWtVNKbPIyal+nQMpqa/lKW6fiGGUVp0u2x3Pnd8luRCs2htBmXSB7W7mJ2SMCui +-----END RSA PRIVATE KEY----- diff --git a/authority/testdata/scep/root.crt b/authority/testdata/scep/root.crt new file mode 100644 index 00000000..58f9820d --- /dev/null +++ b/authority/testdata/scep/root.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBczCCARigAwIBAgIRAImbSwfqrrI6p72t0b9f6l4wCgYIKoZIzj0EAwIwFzEV +MBMGA1UEAxMMc2NlcHRlc3Ryb290MB4XDTIxMDUwNzE1MjEzMFoXDTMxMDUwNTE1 +MjEzMFowFzEVMBMGA1UEAxMMc2NlcHRlc3Ryb290MFkwEwYHKoZIzj0CAQYIKoZI +zj0DAQcDQgAE3fyAgJsDICrnXhhoxHKmXMHLoW0EM9bYiBmx1xRyol0Qa3SZMW43 +rtTykqVP3HUA3rIrLdX106s9IFcA3eIYiaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIG +A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFKJr1p5QRfkHzewG3YEhPAtvFQNr +MAoGCCqGSM49BAMCA0kAMEYCIQDlXU695zKmSSfVPaPbM2cx7OlKr2n6NSyifatH +9zDITwIhAJUbbHzRJVgscxx+VSMqC2TkFvug6ryNu6kQIKNRwolr +-----END CERTIFICATE----- diff --git a/authority/testdata/scep/root.key b/authority/testdata/scep/root.key new file mode 100644 index 00000000..7dd4582b --- /dev/null +++ b/authority/testdata/scep/root.key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,0ea78864d21de199d3a737e4337589c2 + +ZD3ggzw3eDYJp8NovTWgTxk6MagLutgU2UfwbYliAl7wKvVyzwkPytwRkyAXPBM6 +jMfiAdq6wY2wEpc8OSfrvAXrGuYqlCakDhdMaFDPcS3K29VLl4BaO2X2Rfk55nBd +ASBNREKVb+hg2HV22DO7r6t+EYXTSD6iO7EB90bvKdE= +-----END EC PRIVATE KEY----- From 6d9710c88d9798727da91545dcd54bc4d6b4c6ef Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 28 May 2021 16:40:46 +0200 Subject: [PATCH 066/291] Add initial support for ACME IP validation --- acme/api/order.go | 31 +++++--- acme/api/order_test.go | 113 ++++++++++++++++++++++++++ acme/order.go | 176 +++++++++++++++++++++++++++++++++-------- acme/order_test.go | 77 ++++++++++++++++++ 4 files changed, 354 insertions(+), 43 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index 9d410173..b0b9cb43 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -28,7 +28,7 @@ func (n *NewOrderRequest) Validate() error { return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty") } for _, id := range n.Identifiers { - if id.Type != "dns" { + if !(id.Type == "dns" || id.Type == "ip") { return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type) } } @@ -149,15 +149,9 @@ func (h *Handler) newAuthorization(ctx context.Context, az *acme.Authorization) } } - var ( - err error - chTypes = []string{"dns-01"} - ) - // HTTP and TLS challenges can only be used for identifiers without wildcards. - if !az.Wildcard { - chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) - } + chTypes := challengeTypes(az) + var err error az.Token, err = randutil.Alphanumeric(32) if err != nil { return acme.WrapErrorISE(err, "error generating random alphanumeric ID") @@ -275,3 +269,22 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", h.linker.GetLink(ctx, OrderLinkType, o.ID)) api.JSON(w, o) } + +// challengeTypes determines the types of challenges that should be used +// for the ACME authorization request. +func challengeTypes(az *acme.Authorization) []string { + chTypes := []string{} + + // DNS challenge can not be used for identifiers with type IP + if az.Identifier.Type != "ip" { + chTypes = append(chTypes, "dns-01") // TODO: make these types consts/enum? + } + + // HTTP and TLS challenges can only be used for identifiers without wildcards. + if !az.Wildcard { + //chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) + chTypes = append(chTypes, []string{"http-01"}...) // TODO: fix tls-alpn-01 + } + + return chTypes +} diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 300aa61b..6782de75 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "net/http/httptest" "net/url" + "reflect" "testing" "time" @@ -60,6 +61,68 @@ func TestNewOrderRequest_Validate(t *testing.T) { naf: naf, } }, + "ok/ipv4": func(t *testing.T) test { + nbf := time.Now().UTC().Add(time.Minute) + naf := time.Now().UTC().Add(5 * time.Minute) + return test{ + nor: &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + }, + NotAfter: naf, + NotBefore: nbf, + }, + nbf: nbf, + naf: naf, + } + }, + "ok/ipv6": func(t *testing.T) test { + nbf := time.Now().UTC().Add(time.Minute) + naf := time.Now().UTC().Add(5 * time.Minute) + return test{ + nor: &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "ip", Value: "2001:db8::1"}, + }, + NotAfter: naf, + NotBefore: nbf, + }, + nbf: nbf, + naf: naf, + } + }, + "ok/mixed-dns-and-ipv4": func(t *testing.T) test { // TODO: verify that this is allowed and what we want to be possible (in Validate()) + nbf := time.Now().UTC().Add(time.Minute) + naf := time.Now().UTC().Add(5 * time.Minute) + return test{ + nor: &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "dns", Value: "example.com"}, + {Type: "ip", Value: "192.168.42.42"}, + }, + NotAfter: naf, + NotBefore: nbf, + }, + nbf: nbf, + naf: naf, + } + }, + "ok/mixed-ipv4-and-ipv6": func(t *testing.T) test { + nbf := time.Now().UTC().Add(time.Minute) + naf := time.Now().UTC().Add(5 * time.Minute) + return test{ + nor: &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "2001:db8::1"}, + }, + NotAfter: naf, + NotBefore: nbf, + }, + nbf: nbf, + naf: naf, + } + }, } for name, run := range tests { tc := run(t) @@ -1581,3 +1644,53 @@ func TestHandler_FinalizeOrder(t *testing.T) { }) } } + +func TestHandler_challengeTypes(t *testing.T) { + type args struct { + az *acme.Authorization + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "ok/dns", + args: args{ + az: &acme.Authorization{ + Identifier: acme.Identifier{Type: "dns", Value: "example.com"}, + Wildcard: false, + }, + }, + want: []string{"dns-01", "http-01", "tls-alpn-01"}, + }, + { + name: "ok/wildcard", + args: args{ + az: &acme.Authorization{ + Identifier: acme.Identifier{Type: "dns", Value: "*.example.com"}, + Wildcard: true, + }, + }, + want: []string{"dns-01"}, + }, + { + name: "ok/ip", + args: args{ + az: &acme.Authorization{ + Identifier: acme.Identifier{Type: "ip", Value: "192.168.42.42"}, + Wildcard: false, + }, + }, + want: []string{"http-01", "tls-alpn-01"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if got := challengeTypes(tt.args.az); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Handler.challengeTypes() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/acme/order.go b/acme/order.go index a003fe9a..fdab182f 100644 --- a/acme/order.go +++ b/acme/order.go @@ -1,9 +1,11 @@ package acme import ( + "bytes" "context" "crypto/x509" "encoding/json" + "net" "sort" "strings" "time" @@ -131,41 +133,13 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) } - // RFC8555: The CSR MUST indicate the exact same set of requested - // identifiers as the initial newOrder request. Identifiers of type "dns" - // MUST appear either in the commonName portion of the requested subject - // name or in an extensionRequest attribute [RFC2985] requesting a - // subjectAltName extension, or both. - if csr.Subject.CommonName != "" { - csr.DNSNames = append(csr.DNSNames, csr.Subject.CommonName) - } - csr.DNSNames = uniqueSortedLowerNames(csr.DNSNames) - orderNames := make([]string, len(o.Identifiers)) - for i, n := range o.Identifiers { - orderNames[i] = n.Value - } - orderNames = uniqueSortedLowerNames(orderNames) - - // Validate identifier names against CSR alternative names. - // - // Note that with certificate templates we are not going to check for the - // absence of other SANs as they will only be set if the templates allows - // them. - if len(csr.DNSNames) != len(orderNames) { - return NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) - } + // canonicalize the CSR to allow for comparison + csr = canonicalize(csr) - sans := make([]x509util.SubjectAlternativeName, len(csr.DNSNames)) - for i := range csr.DNSNames { - if csr.DNSNames[i] != orderNames[i] { - return NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) - } - sans[i] = x509util.SubjectAlternativeName{ - Type: x509util.DNSType, - Value: csr.DNSNames[i], - } + // retrieve the requested SANs for the Order + sans, err := o.sans(csr) + if err != nil { + return WrapErrorISE(err, "error determining SANs for the CSR") } // Get authorizations from the ACME provisioner. @@ -213,6 +187,120 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques return nil } +func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativeName, error) { + + var sans []x509util.SubjectAlternativeName + + // order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR + orderNames := make([]string, len(o.Identifiers)) + orderIPs := make([]net.IP, len(o.Identifiers)) + for i, n := range o.Identifiers { + switch n.Type { + case "dns": + orderNames[i] = n.Value + case "ip": + orderIPs[i] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs or will result in nil entries + default: + return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type) + } + } + orderNames = uniqueSortedLowerNames(orderNames) + orderIPs = uniqueSortedIPs(orderIPs) + + // TODO: check whether this order was requested with identifier-type IP, + // if so, handle it as an IP order; not as a DNSName order, so the logic + // for verifying the contents MAY not be necessary. + // TODO: limit what IP addresses can be used? Only private? Only certain ranges + // based on configuration? Public vs. private range? That logic should be configurable somewhere. + // TODO: how to handler orders that have DNSNames AND IPs? I guess it could + // happen in cases where there are multiple "identifiers" to order a cert for + // and http or tls-alpn-1 is used (NOT DNS, because that can't be used for IPs). + // TODO: ensure that DNSNames indeed MUST NEVER have an IP + // TODO: only allow IP based identifier based on configuration? + // TODO: validation of the input (if IP; should be valid IPv4/v6) + + // Determine if DNS names or IPs should be processed. + // At this time, orders in which DNS names and IPs are mixed are not supported. // TODO: ensure that's OK and/or should we support more, RFC-wise + shouldProcessIPAddresses := len(csr.DNSNames) == 0 && len(orderIPs) != 0 // TODO: verify that this logic is OK and sufficient + if shouldProcessIPAddresses { + // Validate identifier IPs against CSR alternative names (IPs). + if len(csr.IPAddresses) != len(orderIPs) { + return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) + } + + sans = make([]x509util.SubjectAlternativeName, len(csr.IPAddresses)) + for i := range csr.IPAddresses { + if !ipsAreEqual(csr.IPAddresses[i], orderIPs[i]) { + return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) + } + sans[i] = x509util.SubjectAlternativeName{ + Type: x509util.IPType, + Value: csr.IPAddresses[i].String(), + } + } + } else { + // Validate identifier names against CSR alternative names. + // + // Note that with certificate templates we are not going to check for the + // absence of other SANs as they will only be set if the templates allows + // them. + if len(csr.DNSNames) != len(orderNames) { + return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) + } + + sans = make([]x509util.SubjectAlternativeName, len(csr.DNSNames)) + for i := range csr.DNSNames { + if csr.DNSNames[i] != orderNames[i] { + return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) + } + sans[i] = x509util.SubjectAlternativeName{ + Type: x509util.DNSType, + Value: csr.DNSNames[i], + } + } + } + + return sans, nil +} + +func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.CertificateRequest) { + + // for clarity only; we're operating on the same object by pointer + canonicalized = csr + + // RFC8555: The CSR MUST indicate the exact same set of requested + // identifiers as the initial newOrder request. Identifiers of type "dns" + // MUST appear either in the commonName portion of the requested subject + // name or in an extensionRequest attribute [RFC2985] requesting a + // subjectAltName extension, or both. + if csr.Subject.CommonName != "" { + canonicalized.DNSNames = append(csr.DNSNames, csr.Subject.CommonName) + } + canonicalized.DNSNames = uniqueSortedLowerNames(csr.DNSNames) + canonicalized.IPAddresses = uniqueSortedIPs(csr.IPAddresses) // TODO: sorting and setting this value MAY result in different values in CSR (and probably also ending up in cert); is that behavior wanted? + + return canonicalized +} + +// ipsAreEqual compares IPs to be equal. IPv6 representations of IPv4 +// adresses are NOT considered equal to the IPv4 address in this case. +// Both IPs should be the same version AND equal to each other. +func ipsAreEqual(x, y net.IP) bool { + if isIPv4(x) && isIPv4(y) { + return x.Equal(y) + } + return x.Equal(y) +} + +// isIPv4 returns if an IP is IPv4 or not. +func isIPv4(ip net.IP) bool { + return ip.To4() != nil +} + // uniqueSortedLowerNames returns the set of all unique names in the input after all // of them are lowercased. The returned names will be in their lowercased form // and sorted alphabetically. @@ -228,3 +316,23 @@ func uniqueSortedLowerNames(names []string) (unique []string) { sort.Strings(unique) return } + +// uniqueSortedIPs returns the set of all unique net.IPs in the input. They +// are sorted by their bytes (octet) representation. +func uniqueSortedIPs(ips []net.IP) (unique []net.IP) { + type entry struct { + ip net.IP + } + ipEntryMap := make(map[string]entry, len(ips)) + for _, ip := range ips { + ipEntryMap[ip.String()] = entry{ip: ip} + } + unique = make([]net.IP, 0, len(ipEntryMap)) + for _, entry := range ipEntryMap { + unique = append(unique, entry.ip) + } + sort.Slice(unique, func(i, j int) bool { + return bytes.Compare(unique[i], unique[j]) < 0 + }) + return +} diff --git a/acme/order_test.go b/acme/order_test.go index 993a92f2..c0461dc6 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -5,6 +5,8 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/json" + "net" + "reflect" "testing" "time" @@ -737,3 +739,78 @@ func TestOrder_Finalize(t *testing.T) { }) } } + +func Test_uniqueSortedIPs(t *testing.T) { + type args struct { + ips []net.IP + } + tests := []struct { + name string + args args + wantUnique []net.IP + }{ + { + name: "ok/empty", + args: args{ + ips: []net.IP{}, + }, + wantUnique: []net.IP{}, + }, + { + name: "ok/single-ipv4", + args: args{ + ips: []net.IP{net.ParseIP("192.168.42.42")}, + }, + wantUnique: []net.IP{net.ParseIP("192.168.42.42")}, + }, + { + name: "ok/multiple-ipv4", + args: args{ + ips: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.10"), net.ParseIP("192.168.42.1")}, + }, + wantUnique: []net.IP{net.ParseIP("192.168.42.1"), net.ParseIP("192.168.42.10"), net.ParseIP("192.168.42.42")}, + }, + { + name: "ok/unique-ipv4", + args: args{ + ips: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.42")}, + }, + wantUnique: []net.IP{net.ParseIP("192.168.42.42")}, + }, + { + name: "ok/single-ipv6", + args: args{ + ips: []net.IP{net.ParseIP("2001:db8::30")}, + }, + wantUnique: []net.IP{net.ParseIP("2001:db8::30")}, + }, + { + name: "ok/multiple-ipv6", + args: args{ + ips: []net.IP{net.ParseIP("2001:db8::30"), net.ParseIP("2001:db8::20"), net.ParseIP("2001:db8::10")}, + }, + wantUnique: []net.IP{net.ParseIP("2001:db8::10"), net.ParseIP("2001:db8::20"), net.ParseIP("2001:db8::30")}, + }, + { + name: "ok/unique-ipv6", + args: args{ + ips: []net.IP{net.ParseIP("2001:db8::1"), net.ParseIP("2001:db8::1")}, + }, + wantUnique: []net.IP{net.ParseIP("2001:db8::1")}, + }, + { + name: "ok/mixed-ipv4-and-ipv6", + args: args{ + ips: []net.IP{net.ParseIP("2001:db8::1"), net.ParseIP("2001:db8::1"), net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.42")}, + }, + wantUnique: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("2001:db8::1")}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotUnique := uniqueSortedIPs(tt.args.ips); !reflect.DeepEqual(gotUnique, tt.wantUnique) { + t.Errorf("uniqueSortedIPs() = %v, want %v", gotUnique, tt.wantUnique) + } + }) + } +} From 3e36522329aeef0009547c50d30e0f54e0081d89 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 29 May 2021 00:19:14 +0200 Subject: [PATCH 067/291] Add preliminary support for TLS-ALPN-01 challenge for IP identifiers --- acme/api/order.go | 7 +++---- acme/challenge.go | 24 +++++++++++++++++++++--- acme/order.go | 9 ++------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index b0b9cb43..78a4f9c6 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -275,15 +275,14 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) { func challengeTypes(az *acme.Authorization) []string { chTypes := []string{} - // DNS challenge can not be used for identifiers with type IP - if az.Identifier.Type != "ip" { + // DNS challenge can only be used for identifiers with type dns + if az.Identifier.Type == "dns" { chTypes = append(chTypes, "dns-01") // TODO: make these types consts/enum? } // HTTP and TLS challenges can only be used for identifiers without wildcards. if !az.Wildcard { - //chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) - chTypes = append(chTypes, []string{"http-01"}...) // TODO: fix tls-alpn-01 + chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) } return chTypes diff --git a/acme/challenge.go b/acme/challenge.go index 1059e437..90960fc4 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -119,8 +119,14 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON hostPort := net.JoinHostPort(ch.Value, "443") + fmt.Println(hostPort) + fmt.Println(fmt.Sprintf("%#+v", config)) + + time.Sleep(5 * time.Second) // TODO: remove this; client seems to take a while to start serving; the server does not seem to retry the conn + conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { + fmt.Println(err) return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, "error doing TLS dial for %s", hostPort)) } @@ -129,6 +135,9 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON cs := conn.ConnectionState() certs := cs.PeerCertificates + fmt.Println(fmt.Sprintf("%#+v", cs)) + fmt.Println(fmt.Sprintf("%#+v", certs)) + if len(certs) == 0 { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "%s challenge for %s resulted in no certificates", ch.Type, ch.Value)) @@ -140,10 +149,19 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON } leafCert := certs[0] + fmt.Println(fmt.Sprintf("%#+v", leafCert)) - if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) { - return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, - "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value)) + // if no DNS names present, look for IP address and verify that exactly one exists + if len(leafCert.DNSNames) == 0 { + if len(leafCert.IPAddresses) != 1 || !strings.EqualFold(leafCert.IPAddresses[0].String(), ch.Value) { + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address, %v", ch.Value)) + } + } else { + if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) { + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value)) + } } idPeAcmeIdentifier := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} diff --git a/acme/order.go b/acme/order.go index fdab182f..e9e161f9 100644 --- a/acme/order.go +++ b/acme/order.go @@ -207,17 +207,12 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ orderNames = uniqueSortedLowerNames(orderNames) orderIPs = uniqueSortedIPs(orderIPs) - // TODO: check whether this order was requested with identifier-type IP, - // if so, handle it as an IP order; not as a DNSName order, so the logic - // for verifying the contents MAY not be necessary. // TODO: limit what IP addresses can be used? Only private? Only certain ranges // based on configuration? Public vs. private range? That logic should be configurable somewhere. - // TODO: how to handler orders that have DNSNames AND IPs? I guess it could - // happen in cases where there are multiple "identifiers" to order a cert for - // and http or tls-alpn-1 is used (NOT DNS, because that can't be used for IPs). // TODO: ensure that DNSNames indeed MUST NEVER have an IP // TODO: only allow IP based identifier based on configuration? - // TODO: validation of the input (if IP; should be valid IPv4/v6) + // TODO: validation of the input (if IP; should be valid IPv4/v6; Incoming request should have Host header set / ALPN IN-ADDR.ARPA) + // TODO: limit the IP address identifier to a single IP address? RFC _can_ be read like that, but there can be multiple identifiers, of course // Determine if DNS names or IPs should be processed. // At this time, orders in which DNS names and IPs are mixed are not supported. // TODO: ensure that's OK and/or should we support more, RFC-wise From 6486e6016bebf596879da34774c7b37e06e41a37 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 29 May 2021 00:37:22 +0200 Subject: [PATCH 068/291] Make logic for which challenge types to use clearer --- acme/api/order.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index 78a4f9c6..7d43c2d9 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -273,16 +273,19 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) { // challengeTypes determines the types of challenges that should be used // for the ACME authorization request. func challengeTypes(az *acme.Authorization) []string { - chTypes := []string{} + var chTypes []string - // DNS challenge can only be used for identifiers with type dns - if az.Identifier.Type == "dns" { - chTypes = append(chTypes, "dns-01") // TODO: make these types consts/enum? - } - - // HTTP and TLS challenges can only be used for identifiers without wildcards. - if !az.Wildcard { - chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) + switch az.Identifier.Type { + case "ip": // TODO: make these types consts/enum? + chTypes = []string{"http-01", "tls-alpn-01"} + case "dns": + chTypes = []string{"dns-01"} + // HTTP and TLS challenges can only be used for identifiers without wildcards. + if !az.Wildcard { + chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) + } + default: + chTypes = []string{} } return chTypes From a0e92f8e99268fcb26f919fae973ae9af3c3b4c8 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Jun 2021 22:02:13 +0200 Subject: [PATCH 069/291] Verify IP identifier contains valid IP --- acme/api/order.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acme/api/order.go b/acme/api/order.go index 7d43c2d9..e1ac1aa1 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "encoding/base64" "encoding/json" + "net" "net/http" "strings" "time" @@ -31,6 +32,9 @@ func (n *NewOrderRequest) Validate() error { if !(id.Type == "dns" || id.Type == "ip") { return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type) } + if id.Type == "ip" && net.ParseIP(id.Value) == nil { + return acme.NewError(acme.ErrorMalformedType, "%s is not a valid IP address", id.Value) + } } return nil } @@ -85,6 +89,7 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) { "failed to unmarshal new-order request payload")) return } + if err := nor.Validate(); err != nil { api.WriteError(w, err) return From af615db6b51f4fd912e55957b82ae1b02addd3c6 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Jun 2021 22:03:21 +0200 Subject: [PATCH 070/291] Support DNS and IPs as SANs in single Order --- acme/order.go | 91 ++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/acme/order.go b/acme/order.go index e9e161f9..e1d77ece 100644 --- a/acme/order.go +++ b/acme/order.go @@ -199,7 +199,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ case "dns": orderNames[i] = n.Value case "ip": - orderIPs[i] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs or will result in nil entries + orderIPs[i] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries default: return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type) } @@ -207,55 +207,49 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ orderNames = uniqueSortedLowerNames(orderNames) orderIPs = uniqueSortedIPs(orderIPs) - // TODO: limit what IP addresses can be used? Only private? Only certain ranges + totalNumberOfSANs := len(csr.DNSNames) + len(csr.IPAddresses) + sans = make([]x509util.SubjectAlternativeName, totalNumberOfSANs) + + // TODO: limit what IP addresses can be used? Only private? Only certain ranges (i.e. only allow the specific ranges by default, configuration for all?) + // TODO: can DNS already be limited to a certain domain? That would probably be nice to have too, but maybe not as part of this PR + // TODO: if it seems not too big of a change, make consts/enums out of the stringly typed identifiers (challenge types, identifier types) // based on configuration? Public vs. private range? That logic should be configurable somewhere. - // TODO: ensure that DNSNames indeed MUST NEVER have an IP - // TODO: only allow IP based identifier based on configuration? - // TODO: validation of the input (if IP; should be valid IPv4/v6; Incoming request should have Host header set / ALPN IN-ADDR.ARPA) - // TODO: limit the IP address identifier to a single IP address? RFC _can_ be read like that, but there can be multiple identifiers, of course - - // Determine if DNS names or IPs should be processed. - // At this time, orders in which DNS names and IPs are mixed are not supported. // TODO: ensure that's OK and/or should we support more, RFC-wise - shouldProcessIPAddresses := len(csr.DNSNames) == 0 && len(orderIPs) != 0 // TODO: verify that this logic is OK and sufficient - if shouldProcessIPAddresses { - // Validate identifier IPs against CSR alternative names (IPs). - if len(csr.IPAddresses) != len(orderIPs) { + // TODO: only allow IP based identifier based on configuration? Some additional configuration and validation on the provisioner for this case. + + // Validate identifier names against CSR alternative names. + // + // Note that with certificate templates we are not going to check for the + // absence of other SANs as they will only be set if the templates allows + // them. + if len(csr.IPAddresses) != len(orderIPs) { + return sans, NewError(ErrorBadCSRType, "number of CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) + } + + for i := range csr.IPAddresses { + if !ipsAreEqual(csr.IPAddresses[i], orderIPs[i]) { return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) } - - sans = make([]x509util.SubjectAlternativeName, len(csr.IPAddresses)) - for i := range csr.IPAddresses { - if !ipsAreEqual(csr.IPAddresses[i], orderIPs[i]) { - return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ - "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) - } - sans[i] = x509util.SubjectAlternativeName{ - Type: x509util.IPType, - Value: csr.IPAddresses[i].String(), - } + sans[i] = x509util.SubjectAlternativeName{ + Type: x509util.IPType, + Value: csr.IPAddresses[i].String(), } - } else { - // Validate identifier names against CSR alternative names. - // - // Note that with certificate templates we are not going to check for the - // absence of other SANs as they will only be set if the templates allows - // them. - if len(csr.DNSNames) != len(orderNames) { + } + + if len(csr.DNSNames) != len(orderNames) { + return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) + } + + for i := range csr.DNSNames { + if csr.DNSNames[i] != orderNames[i] { return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) } - - sans = make([]x509util.SubjectAlternativeName, len(csr.DNSNames)) - for i := range csr.DNSNames { - if csr.DNSNames[i] != orderNames[i] { - return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) - } - sans[i] = x509util.SubjectAlternativeName{ - Type: x509util.DNSType, - Value: csr.DNSNames[i], - } + sans[i] = x509util.SubjectAlternativeName{ + Type: x509util.DNSType, + Value: csr.DNSNames[i], } } @@ -276,7 +270,7 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate canonicalized.DNSNames = append(csr.DNSNames, csr.Subject.CommonName) } canonicalized.DNSNames = uniqueSortedLowerNames(csr.DNSNames) - canonicalized.IPAddresses = uniqueSortedIPs(csr.IPAddresses) // TODO: sorting and setting this value MAY result in different values in CSR (and probably also ending up in cert); is that behavior wanted? + canonicalized.IPAddresses = uniqueSortedIPs(csr.IPAddresses) return canonicalized } @@ -285,15 +279,16 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate // adresses are NOT considered equal to the IPv4 address in this case. // Both IPs should be the same version AND equal to each other. func ipsAreEqual(x, y net.IP) bool { - if isIPv4(x) && isIPv4(y) { + if matchAddrFamily(x, y) { return x.Equal(y) } - return x.Equal(y) + return false } -// isIPv4 returns if an IP is IPv4 or not. -func isIPv4(ip net.IP) bool { - return ip.To4() != nil +// matchAddrFamily returns if two IPs are both IPv4 OR IPv6 +// Implementation taken and adapted from https://golang.org/src/net/ip.go +func matchAddrFamily(x net.IP, y net.IP) bool { + return x.To4() != nil && y.To4() != nil || x.To16() != nil && x.To4() == nil && y.To16() != nil && y.To4() == nil } // uniqueSortedLowerNames returns the set of all unique names in the input after all From 76dcf542d42f66531aeb1dec13ddb8340a9c52ab Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Jun 2021 22:45:24 +0200 Subject: [PATCH 071/291] Fix mixed DNS and IP SANs in Order --- acme/api/order.go | 2 +- acme/order.go | 61 +++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index e1ac1aa1..936e9573 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -33,7 +33,7 @@ func (n *NewOrderRequest) Validate() error { return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type) } if id.Type == "ip" && net.ParseIP(id.Value) == nil { - return acme.NewError(acme.ErrorMalformedType, "%s is not a valid IP address", id.Value) + return acme.NewError(acme.ErrorMalformedType, "invalid IP address: %s", id.Value) } } return nil diff --git a/acme/order.go b/acme/order.go index e1d77ece..add90e1a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -192,14 +192,17 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ var sans []x509util.SubjectAlternativeName // order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR - orderNames := make([]string, len(o.Identifiers)) - orderIPs := make([]net.IP, len(o.Identifiers)) - for i, n := range o.Identifiers { + orderNames := make([]string, numberOfIdentifierType("dns", o.Identifiers)) + orderIPs := make([]net.IP, numberOfIdentifierType("ip", o.Identifiers)) + indexDNS, indexIP := 0, 0 + for _, n := range o.Identifiers { switch n.Type { case "dns": - orderNames[i] = n.Value + orderNames[indexDNS] = n.Value + indexDNS++ case "ip": - orderIPs[i] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries + orderIPs[indexIP] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries + indexIP++ default: return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type) } @@ -209,6 +212,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ totalNumberOfSANs := len(csr.DNSNames) + len(csr.IPAddresses) sans = make([]x509util.SubjectAlternativeName, totalNumberOfSANs) + index := 0 // TODO: limit what IP addresses can be used? Only private? Only certain ranges (i.e. only allow the specific ranges by default, configuration for all?) // TODO: can DNS already be limited to a certain domain? That would probably be nice to have too, but maybe not as part of this PR @@ -221,6 +225,23 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ // Note that with certificate templates we are not going to check for the // absence of other SANs as they will only be set if the templates allows // them. + if len(csr.DNSNames) != len(orderNames) { + return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) + } + + for i := range csr.DNSNames { + if csr.DNSNames[i] != orderNames[i] { + return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) + } + sans[index] = x509util.SubjectAlternativeName{ + Type: x509util.DNSType, + Value: csr.DNSNames[i], + } + index++ + } + if len(csr.IPAddresses) != len(orderIPs) { return sans, NewError(ErrorBadCSRType, "number of CSR IPs do not match identifiers exactly: "+ "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) @@ -231,31 +252,31 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) } - sans[i] = x509util.SubjectAlternativeName{ + sans[index] = x509util.SubjectAlternativeName{ Type: x509util.IPType, Value: csr.IPAddresses[i].String(), } + index++ } - if len(csr.DNSNames) != len(orderNames) { - return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) - } + return sans, nil +} - for i := range csr.DNSNames { - if csr.DNSNames[i] != orderNames[i] { - return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) - } - sans[i] = x509util.SubjectAlternativeName{ - Type: x509util.DNSType, - Value: csr.DNSNames[i], +// numberOfIdentifierType returns the number of Identifiers that +// are of type typ. +func numberOfIdentifierType(typ string, ids []Identifier) int { + c := 0 + for _, id := range ids { + if id.Type == typ { + c++ } } - - return sans, nil + return c } +// canonicalize canonicalizes a CSR so that it can be compared against an Order +// NOTE: this effectively changes the order of SANs in the CSR, which may be OK, +// but may not be expected. func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.CertificateRequest) { // for clarity only; we're operating on the same object by pointer From 2f40011da87cacdd73a51bd7ae9c915fa40f567d Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Jun 2021 00:01:43 +0200 Subject: [PATCH 072/291] Add support for TLS-ALPN-01 challenge --- acme/challenge.go | 67 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 90960fc4..d541bed2 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -107,23 +107,30 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb } func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { + + var serverName string + + // RFC8738 states that, if HostName is IP, it should be the ARPA + // address https://datatracker.ietf.org/doc/html/rfc8738#section-6. + // It also references TLS Extensions [RFC6066]. + if ip := net.ParseIP(ch.Value); ip != nil { + serverName = reverseAddr(ip) + } else { + serverName = ch.Value + } + config := &tls.Config{ NextProtos: []string{"acme-tls/1"}, // https://tools.ietf.org/html/rfc8737#section-4 // ACME servers that implement "acme-tls/1" MUST only negotiate TLS 1.2 // [RFC5246] or higher when connecting to clients for validation. MinVersion: tls.VersionTLS12, - ServerName: ch.Value, + ServerName: serverName, InsecureSkipVerify: true, // we expect a self-signed challenge certificate } hostPort := net.JoinHostPort(ch.Value, "443") - fmt.Println(hostPort) - fmt.Println(fmt.Sprintf("%#+v", config)) - - time.Sleep(5 * time.Second) // TODO: remove this; client seems to take a while to start serving; the server does not seem to retry the conn - conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { fmt.Println(err) @@ -135,9 +142,6 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON cs := conn.ConnectionState() certs := cs.PeerCertificates - fmt.Println(fmt.Sprintf("%#+v", cs)) - fmt.Println(fmt.Sprintf("%#+v", certs)) - if len(certs) == 0 { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "%s challenge for %s resulted in no certificates", ch.Type, ch.Value)) @@ -149,7 +153,6 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON } leafCert := certs[0] - fmt.Println(fmt.Sprintf("%#+v", leafCert)) // if no DNS names present, look for IP address and verify that exactly one exists if len(leafCert.DNSNames) == 0 { @@ -262,6 +265,50 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK return nil } +// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP +// address addr suitable for rDNS (PTR) record lookup or an error if it fails +// to parse the IP address. +// Implementation taken and adapted from https://golang.org/src/net/dnsclient.go?s=780:834#L20 +func reverseAddr(ip net.IP) (arpa string) { + if ip.To4() != nil { + return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa." + } + // Must be IPv6 + buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) + // Add it, in reverse, to the buffer + for i := len(ip) - 1; i >= 0; i-- { + v := ip[i] + buf = append(buf, hexit[v&0xF], + '.', + hexit[v>>4], + '.') + } + // Append "ip6.arpa." and return (buf already has the final .) + buf = append(buf, "ip6.arpa."...) + return string(buf) +} + +// Convert unsigned integer to decimal string. +// Implementation taken from https://golang.org/src/net/parse.go +func uitoa(val uint) string { + if val == 0 { // avoid string allocation + return "0" + } + var buf [20]byte // big enough for 64bit value base 10 + i := len(buf) - 1 + for val >= 10 { + q := val / 10 + buf[i] = byte('0' + val - q*10) + i-- + val = q + } + // val < 10 + buf[i] = byte('0' + val) + return string(buf[i:]) +} + +const hexit = "0123456789abcdef" + // KeyAuthorization creates the ACME key authorization value from a token // and a jwk. func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { From a6405e98a9939519ebc8012d263aed1ff1113b1b Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Jun 2021 00:06:15 +0200 Subject: [PATCH 073/291] Remove fmt. --- acme/challenge.go | 1 - acme/order.go | 1 - 2 files changed, 2 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index d541bed2..105fcbc8 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -133,7 +133,6 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { - fmt.Println(err) return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, "error doing TLS dial for %s", hostPort)) } diff --git a/acme/order.go b/acme/order.go index add90e1a..b11d51c7 100644 --- a/acme/order.go +++ b/acme/order.go @@ -217,7 +217,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ // TODO: limit what IP addresses can be used? Only private? Only certain ranges (i.e. only allow the specific ranges by default, configuration for all?) // TODO: can DNS already be limited to a certain domain? That would probably be nice to have too, but maybe not as part of this PR // TODO: if it seems not too big of a change, make consts/enums out of the stringly typed identifiers (challenge types, identifier types) - // based on configuration? Public vs. private range? That logic should be configurable somewhere. // TODO: only allow IP based identifier based on configuration? Some additional configuration and validation on the provisioner for this case. // Validate identifier names against CSR alternative names. From 0c79914d0d417fcc4df97663e2e4acb6162108bf Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Jun 2021 00:12:49 +0200 Subject: [PATCH 074/291] Improve check for single IP in TLS-ALPN-01 challenge --- acme/challenge.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 105fcbc8..eabdaaed 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -108,12 +108,12 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { - var serverName string - // RFC8738 states that, if HostName is IP, it should be the ARPA // address https://datatracker.ietf.org/doc/html/rfc8738#section-6. // It also references TLS Extensions [RFC6066]. - if ip := net.ParseIP(ch.Value); ip != nil { + var serverName string + ip := net.ParseIP(ch.Value) + if ip != nil { serverName = reverseAddr(ip) } else { serverName = ch.Value @@ -155,7 +155,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON // if no DNS names present, look for IP address and verify that exactly one exists if len(leafCert.DNSNames) == 0 { - if len(leafCert.IPAddresses) != 1 || !strings.EqualFold(leafCert.IPAddresses[0].String(), ch.Value) { + if len(leafCert.IPAddresses) != 1 || !leafCert.IPAddresses[0].Equal(ip) { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address, %v", ch.Value)) } From 072bd0dcf430b14ca3308ae538477a4ea6733026 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 3 Jun 2021 19:31:19 -0700 Subject: [PATCH 075/291] Add support for Google CAS v1 --- cas/apiv1/options.go | 1 + cas/cloudcas/certificate.go | 45 +++++------- cas/cloudcas/cloudcas.go | 141 +++++++++++++++++++++++++++++------ go.mod | 15 ++-- go.sum | 143 ++++++++++++++++++++++++++++++------ 5 files changed, 267 insertions(+), 78 deletions(-) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index 4810d1f3..e1adcecb 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -49,6 +49,7 @@ type Options struct { // certificate authority. Project string `json:"-"` Location string `json:"-"` + CAPool string `json:"-"` } // CertificateIssuer contains the properties used to use the StepCAS certificate diff --git a/cas/cloudcas/certificate.go b/cas/cloudcas/certificate.go index d7789992..6f229702 100644 --- a/cas/cloudcas/certificate.go +++ b/cas/cloudcas/certificate.go @@ -12,8 +12,7 @@ import ( "github.com/pkg/errors" kmsapi "github.com/smallstep/certificates/kms/apiv1" - pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" ) var ( @@ -67,11 +66,10 @@ func createCertificateConfig(tpl *x509.Certificate) (*pb.Certificate_Config, err config := &pb.CertificateConfig{ SubjectConfig: &pb.CertificateConfig_SubjectConfig{ Subject: createSubject(tpl), - CommonName: tpl.Subject.CommonName, SubjectAltName: createSubjectAlternativeNames(tpl), }, - ReusableConfig: createReusableConfig(tpl), - PublicKey: pk, + X509Config: createX509Parameters(tpl), + PublicKey: pk, } return &pb.Certificate_Config{ Config: config, @@ -86,7 +84,7 @@ func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) { return nil, errors.Wrap(err, "error marshaling public key") } return &pb.PublicKey{ - Type: pb.PublicKey_PEM_EC_KEY, + Format: pb.PublicKey_PEM, Key: pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Bytes: asn1Bytes, @@ -94,7 +92,7 @@ func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) { }, nil case *rsa.PublicKey: return &pb.PublicKey{ - Type: pb.PublicKey_PEM_RSA_KEY, + Format: pb.PublicKey_PEM, Key: pem.EncodeToMemory(&pem.Block{ Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(key), @@ -107,7 +105,9 @@ func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) { func createSubject(cert *x509.Certificate) *pb.Subject { sub := cert.Subject - ret := new(pb.Subject) + ret := &pb.Subject{ + CommonName: sub.CommonName, + } if len(sub.Country) > 0 { ret.CountryCode = sub.Country[0] } @@ -196,7 +196,7 @@ func createSubjectAlternativeNames(cert *x509.Certificate) *pb.SubjectAltNames { return ret } -func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper { +func createX509Parameters(cert *x509.Certificate) *pb.X509Parameters { var unknownEKUs []*pb.ObjectId var ekuOptions = &pb.KeyUsage_ExtendedKeyUsageOptions{} for _, eku := range cert.ExtKeyUsage { @@ -241,22 +241,19 @@ func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper { policyIDs = append(policyIDs, createObjectID(oid)) } - var caOptions *pb.ReusableConfigValues_CaOptions + var caOptions *pb.X509Parameters_CaOptions if cert.BasicConstraintsValid { - var maxPathLength *wrapperspb.Int32Value + caOptions = new(pb.X509Parameters_CaOptions) + var maxPathLength int32 switch { case cert.MaxPathLenZero: - maxPathLength = wrapperspb.Int32(0) + maxPathLength = 0 + caOptions.MaxIssuerPathLength = &maxPathLength case cert.MaxPathLen > 0: - maxPathLength = wrapperspb.Int32(int32(cert.MaxPathLen)) - default: - maxPathLength = nil - } - - caOptions = &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(cert.IsCA), - MaxIssuerPathLength: maxPathLength, + maxPathLength = int32(cert.MaxPathLen) + caOptions.MaxIssuerPathLength = &maxPathLength } + caOptions.IsCa = &cert.IsCA } var extraExtensions []*pb.X509Extension @@ -270,7 +267,7 @@ func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper { } } - values := &pb.ReusableConfigValues{ + return &pb.X509Parameters{ KeyUsage: &pb.KeyUsage{ BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{ DigitalSignature: cert.KeyUsage&x509.KeyUsageDigitalSignature > 0, @@ -291,12 +288,6 @@ func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper { AiaOcspServers: cert.OCSPServer, AdditionalExtensions: extraExtensions, } - - return &pb.ReusableConfigWrapper{ - ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{ - ReusableConfigValues: values, - }, - } } // isExtraExtension returns true if the extension oid is not managed in a diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index 695258c9..602d2942 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -10,14 +10,16 @@ import ( "strings" "time" - privateca "cloud.google.com/go/security/privateca/apiv1beta1" + privateca "cloud.google.com/go/security/privateca/apiv1" "github.com/google/uuid" gax "github.com/googleapis/gax-go/v2" "github.com/pkg/errors" "github.com/smallstep/certificates/cas/apiv1" "go.step.sm/crypto/x509util" "google.golang.org/api/option" - pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1" + pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" durationpb "google.golang.org/protobuf/types/known/durationpb" ) @@ -32,9 +34,9 @@ var now = func() time.Time { } // The actual regular expression that matches a certificate authority is: -// ^projects/[a-z][a-z0-9-]{4,28}[a-z0-9]/locations/[a-z0-9-]+/certificateAuthorities/[a-zA-Z0-9-_]+$ +// ^projects/[a-z][a-z0-9-]{4,28}[a-z0-9]/locations/[a-z0-9-]+/caPools/[a-zA-Z0-9-_]+/certificateAuthorities/[a-zA-Z0-9-_]+$ // But we will allow a more flexible one to fail if this changes. -var caRegexp = regexp.MustCompile("^projects/[^/]+/locations/[^/]+/certificateAuthorities/[^/]+$") +var caRegexp = regexp.MustCompile("^projects/[^/]+/locations/[^/]+/caPools/[^/]+/certificateAuthorities/[^/]+$") // CertificateAuthorityClient is the interface implemented by the Google CAS // client. @@ -45,6 +47,9 @@ type CertificateAuthorityClient interface { CreateCertificateAuthority(ctx context.Context, req *pb.CreateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.CreateCertificateAuthorityOperation, error) FetchCertificateAuthorityCsr(ctx context.Context, req *pb.FetchCertificateAuthorityCsrRequest, opts ...gax.CallOption) (*pb.FetchCertificateAuthorityCsrResponse, error) ActivateCertificateAuthority(ctx context.Context, req *pb.ActivateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.ActivateCertificateAuthorityOperation, error) + EnableCertificateAuthority(ctx context.Context, req *pb.EnableCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.EnableCertificateAuthorityOperation, error) + GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest, opts ...gax.CallOption) (*pb.CaPool, error) + CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequest, opts ...gax.CallOption) (*privateca.CreateCaPoolOperation, error) } // recocationCodeMap maps revocation reason codes from RFC 5280, to Google CAS @@ -68,6 +73,7 @@ type CloudCAS struct { certificateAuthority string project string location string + caPool string } // newCertificateAuthorityClient creates the certificate authority client. This @@ -93,6 +99,8 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { return nil, errors.New("cloudCAS 'project' cannot be empty") case opts.Location == "": return nil, errors.New("cloudCAS 'location' cannot be empty") + case opts.CAPool == "": + return nil, errors.New("cloudCAS 'caPool' cannot be empty") } } else { if opts.CertificateAuthority == "" { @@ -102,13 +110,16 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { return nil, errors.New("cloudCAS 'certificateAuthority' is not valid certificate authority resource") } // Extract project and location from CertificateAuthority - if parts := strings.Split(opts.CertificateAuthority, "/"); len(parts) == 6 { + if parts := strings.Split(opts.CertificateAuthority, "/"); len(parts) == 8 { if opts.Project == "" { opts.Project = parts[1] } if opts.Location == "" { opts.Location = parts[3] } + if opts.CAPool == "" { + opts.CAPool = parts[5] + } } } @@ -122,6 +133,7 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { certificateAuthority: opts.CertificateAuthority, project: opts.Project, location: opts.Location, + caPool: opts.CAPool, }, nil } @@ -301,28 +313,30 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor } req.Template.ExtraExtensions = append(req.Template.ExtraExtensions, casExtension) + // Create the caPool if necessary + parent, err := c.createCaPoolIfNecessary() + if err != nil { + return nil, err + } + // Prepare CreateCertificateAuthorityRequest pbReq := &pb.CreateCertificateAuthorityRequest{ - Parent: "projects/" + c.project + "/locations/" + c.location, + Parent: parent, CertificateAuthorityId: caID, RequestId: req.RequestID, CertificateAuthority: &pb.CertificateAuthority{ Type: caType, - Tier: pb.CertificateAuthority_ENTERPRISE, Config: &pb.CertificateConfig{ SubjectConfig: &pb.CertificateConfig_SubjectConfig{ - Subject: createSubject(req.Template), - CommonName: req.Template.Subject.CommonName, + Subject: createSubject(req.Template), + SubjectAltName: createSubjectAlternativeNames(req.Template), }, - ReusableConfig: createReusableConfig(req.Template), - }, - Lifetime: durationpb.New(req.Lifetime), - KeySpec: keySpec, - IssuingOptions: &pb.CertificateAuthority_IssuingOptions{ - IncludeCaCertUrl: true, - IncludeCrlAccessUrl: true, + X509Config: createX509Parameters(req.Template), }, - Labels: map[string]string{}, + Lifetime: durationpb.New(req.Lifetime), + KeySpec: keySpec, + GcsBucket: "", + Labels: map[string]string{}, }, } @@ -344,9 +358,17 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor return nil, errors.Wrap(err, "cloudCAS CreateCertificateAuthority failed") } + // Enable root certificate + if req.Type == apiv1.RootCA { + ca, err = c.enableCertificateAuthority(ca) + if err != nil { + return nil, err + } + } + // Sign Intermediate CAs with the parent. if req.Type == apiv1.IntermediateCA { - ca, err = c.signIntermediateCA(ca.Name, req) + ca, err = c.signIntermediateCA(parent, ca.Name, req) if err != nil { return nil, err } @@ -378,6 +400,77 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor }, nil } +func (c *CloudCAS) createCaPoolIfNecessary() (string, error) { + ctx, cancel := defaultContext() + defer cancel() + + pool, err := c.client.GetCaPool(ctx, &pb.GetCaPoolRequest{ + Name: "projects/" + c.project + "/locations/" + c.location + "/caPools/" + c.caPool, + }) + if err == nil { + return pool.Name, nil + } + + if status.Code(err) != codes.NotFound { + return "", errors.Wrap(err, "cloudCAS GetCaPool failed") + } + + ctx, cancel = defaultContext() + defer cancel() + + op, err := c.client.CreateCaPool(ctx, &pb.CreateCaPoolRequest{ + Parent: "projects/" + c.project + "/locations/" + c.location, + CaPoolId: c.caPool, + CaPool: &pb.CaPool{ + Tier: pb.CaPool_ENTERPRISE, + IssuancePolicy: nil, + PublishingOptions: &pb.CaPool_PublishingOptions{ + PublishCaCert: true, + PublishCrl: true, + }, + }, + }) + if err != nil { + return "", errors.Wrap(err, "cloudCAS CreateCaPool failed") + } + + ctx, cancel = defaultInitiatorContext() + defer cancel() + + pool, err = op.Wait(ctx) + if err != nil { + return "", errors.Wrap(err, "cloudCAS CreateCaPool failed") + } + + return pool.Name, nil +} + +func (c *CloudCAS) enableCertificateAuthority(ca *pb.CertificateAuthority) (*pb.CertificateAuthority, error) { + if ca.State == pb.CertificateAuthority_ENABLED { + return ca, nil + } + + ctx, cancel := defaultContext() + defer cancel() + + resp, err := c.client.EnableCertificateAuthority(ctx, &pb.EnableCertificateAuthorityRequest{ + Name: ca.Name, + }) + if err != nil { + return nil, errors.Wrap(err, "cloudCAS EnableCertificateAuthority failed") + } + + ctx, cancel = defaultInitiatorContext() + defer cancel() + + ca, err = resp.Wait(ctx) + if err != nil { + return nil, errors.Wrap(err, "cloudCAS EnableCertificateAuthority failed") + } + + return ca, nil +} + func (c *CloudCAS) createCertificate(tpl *x509.Certificate, lifetime time.Duration, requestID string) (*x509.Certificate, []*x509.Certificate, error) { // Removes the CAS extension if it exists. apiv1.RemoveCertificateAuthorityExtension(tpl) @@ -403,7 +496,7 @@ func (c *CloudCAS) createCertificate(tpl *x509.Certificate, lifetime time.Durati defer cancel() cert, err := c.client.CreateCertificate(ctx, &pb.CreateCertificateRequest{ - Parent: c.certificateAuthority, + Parent: "projects/" + c.project + "/locations/" + c.location + "/caPools/" + c.caPool, CertificateId: id, Certificate: &pb.Certificate{ CertificateConfig: certConfig, @@ -420,7 +513,7 @@ func (c *CloudCAS) createCertificate(tpl *x509.Certificate, lifetime time.Durati return getCertificateAndChain(cert) } -func (c *CloudCAS) signIntermediateCA(name string, req *apiv1.CreateCertificateAuthorityRequest) (*pb.CertificateAuthority, error) { +func (c *CloudCAS) signIntermediateCA(parent, name string, req *apiv1.CreateCertificateAuthorityRequest) (*pb.CertificateAuthority, error) { id, err := createCertificateID() if err != nil { return nil, err @@ -477,7 +570,7 @@ func (c *CloudCAS) signIntermediateCA(name string, req *apiv1.CreateCertificateA defer cancel() cert, err = c.client.CreateCertificate(ctx, &pb.CreateCertificateRequest{ - Parent: req.Parent.Name, + Parent: parent, CertificateId: id, Certificate: &pb.Certificate{ CertificateConfig: &pb.Certificate_PemCsr{ @@ -521,6 +614,12 @@ func (c *CloudCAS) signIntermediateCA(name string, req *apiv1.CreateCertificateA return nil, errors.Wrap(err, "cloudCAS ActivateCertificateAuthority failed") } + // Enable certificate authority + ca, err = c.enableCertificateAuthority(ca) + if err != nil { + return nil, err + } + return ca, nil } diff --git a/go.mod b/go.mod index ac3d54fb..9fc6bb3a 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/smallstep/certificates go 1.14 require ( - cloud.google.com/go v0.70.0 + cloud.google.com/go v0.83.0 github.com/Masterminds/sprig/v3 v3.1.0 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 github.com/go-chi/chi v4.0.2+incompatible github.com/go-kit/kit v0.10.0 // indirect github.com/go-piv/piv-go v1.7.0 - github.com/golang/mock v1.4.4 + github.com/golang/mock v1.5.0 github.com/google/uuid v1.1.2 github.com/googleapis/gax-go/v2 v2.0.5 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect @@ -26,11 +26,12 @@ require ( go.step.sm/cli-utils v0.2.0 go.step.sm/crypto v0.8.3 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 - google.golang.org/api v0.33.0 - google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 - google.golang.org/grpc v1.32.0 - google.golang.org/protobuf v1.25.0 + golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 + golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect + google.golang.org/api v0.47.0 + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c + google.golang.org/grpc v1.38.0 + google.golang.org/protobuf v1.26.0 gopkg.in/square/go-jose.v2 v2.5.1 ) diff --git a/go.sum b/go.sum index 60c37a32..0752f40d 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,13 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.70.0 h1:ujhG1RejZYi+HYfJNlgBh3j/bVKD8DewM7AkJ5UPyBc= -cloud.google.com/go v0.70.0/go.mod h1:/UTKYRQTWjVnSe7nGvoSzxEFUELzSI/yAYd0JQT6cRo= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0 h1:bAMqZidYkmIsUqe6PtkEPT7Q+vfizScn+jfNA6jwK9c= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -91,6 +96,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -129,6 +136,9 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -173,8 +183,9 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -188,12 +199,16 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -203,11 +218,17 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/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= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -215,7 +236,11 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201009210932-67992a1a5a35/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -438,8 +463,9 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 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/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= @@ -455,6 +481,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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= 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= @@ -466,8 +493,9 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= @@ -515,6 +543,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -523,6 +553,9 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -558,17 +591,29 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/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= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -577,6 +622,9 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -621,16 +669,31 @@ golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7w 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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +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-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= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 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 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= @@ -682,7 +745,13 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc 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= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201017001424-6003fad69a88/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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.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/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= @@ -705,16 +774,22 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.33.0 h1:+gL0XvACeMIvpwLZ5rQZzLn5cwOsgg8dIcfJ2SYfBVw= -google.golang.org/api v0.33.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0 h1:sQLWZQvP6jPGIP4JGPkJu4zHswrv81iobiyszr3b/0I= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -746,8 +821,18 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 h1:bFFRpT+e8JJVY7lMMfvezL1ZIwqiwmPl2bsE2yx4HqM= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -766,8 +851,16 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -777,8 +870,10 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -798,6 +893,8 @@ 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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From af4803b8b883c39322cc440cfd512de3b45281ab Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Jun 2021 08:42:24 +0200 Subject: [PATCH 076/291] Fix tests --- acme/challenge.go | 4 ++-- acme/challenge_test.go | 10 +++++----- acme/order.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index eabdaaed..cb15a188 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -157,12 +157,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON if len(leafCert.DNSNames) == 0 { if len(leafCert.IPAddresses) != 1 || !leafCert.IPAddresses[0].Equal(ip) { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, - "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address, %v", ch.Value)) + "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value)) } } else { if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, - "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value)) + "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value)) } } diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 14287945..8e03a414 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -1544,7 +1544,7 @@ func TestTLSALPN01Validate(t *testing.T) { err: NewErrorISE("failure saving error to acme challenge: force"), } }, - "ok/no-names-error": func(t *testing.T) test { + "ok/no-names-nor-ips-error": func(t *testing.T) test { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -1573,7 +1573,7 @@ func TestTLSALPN01Validate(t *testing.T) { assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) - err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value) + err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -1616,7 +1616,7 @@ func TestTLSALPN01Validate(t *testing.T) { assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) - err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value) + err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -1660,7 +1660,7 @@ func TestTLSALPN01Validate(t *testing.T) { assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) - err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value) + err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -1703,7 +1703,7 @@ func TestTLSALPN01Validate(t *testing.T) { assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) - err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value) + err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) diff --git a/acme/order.go b/acme/order.go index b11d51c7..73d5e636 100644 --- a/acme/order.go +++ b/acme/order.go @@ -139,7 +139,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques // retrieve the requested SANs for the Order sans, err := o.sans(csr) if err != nil { - return WrapErrorISE(err, "error determining SANs for the CSR") + return err } // Get authorizations from the ACME provisioner. @@ -242,7 +242,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ } if len(csr.IPAddresses) != len(orderIPs) { - return sans, NewError(ErrorBadCSRType, "number of CSR IPs do not match identifiers exactly: "+ + return sans, NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ "CSR IPs = %v, Order IPs = %v", csr.IPAddresses, orderIPs) } From 48bc4e549d568e19524ac668b015cf27235762c6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 7 Jun 2021 15:53:29 -0700 Subject: [PATCH 077/291] Fix cloudcas tests. --- cas/cloudcas/certificate_test.go | 140 ++++++------- cas/cloudcas/cloudcas.go | 5 +- cas/cloudcas/cloudcas_test.go | 183 ++++++++++++++--- cas/cloudcas/mock_client_test.go | 166 +++++++++++----- cas/cloudcas/mock_operation_server_test.go | 217 +++------------------ 5 files changed, 365 insertions(+), 346 deletions(-) diff --git a/cas/cloudcas/certificate_test.go b/cas/cloudcas/certificate_test.go index 0822e4c1..8bf67fb6 100644 --- a/cas/cloudcas/certificate_test.go +++ b/cas/cloudcas/certificate_test.go @@ -15,8 +15,7 @@ import ( "testing" kmsapi "github.com/smallstep/certificates/kms/apiv1" - pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" ) var ( @@ -67,30 +66,27 @@ func Test_createCertificateConfig(t *testing.T) { {"ok", args{cert}, &pb.Certificate_Config{ Config: &pb.CertificateConfig{ SubjectConfig: &pb.CertificateConfig_SubjectConfig{ - Subject: &pb.Subject{}, - CommonName: "test.smallstep.com", + Subject: &pb.Subject{ + CommonName: "test.smallstep.com", + }, SubjectAltName: &pb.SubjectAltNames{ DnsNames: []string{"test.smallstep.com"}, }, }, - ReusableConfig: &pb.ReusableConfigWrapper{ - ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{ - ReusableConfigValues: &pb.ReusableConfigValues{ - KeyUsage: &pb.KeyUsage{ - BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{ - DigitalSignature: true, - }, - ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{ - ClientAuth: true, - ServerAuth: true, - }, - }, + X509Config: &pb.X509Parameters{ + KeyUsage: &pb.KeyUsage{ + BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{ + DigitalSignature: true, + }, + ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{ + ClientAuth: true, + ServerAuth: true, }, }, }, PublicKey: &pb.PublicKey{ - Type: pb.PublicKey_PEM_EC_KEY, - Key: []byte(testLeafPublicKey), + Key: []byte(testLeafPublicKey), + Format: pb.PublicKey_PEM, }, }, }, false}, @@ -104,7 +100,7 @@ func Test_createCertificateConfig(t *testing.T) { return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("createCertificateConfig() = %v, want %v", got.Config.ReusableConfig, tt.want.Config.ReusableConfig) + t.Errorf("createCertificateConfig() = %v, want %v", got.Config, tt.want.Config) } }) } @@ -127,12 +123,12 @@ func Test_createPublicKey(t *testing.T) { wantErr bool }{ {"ok ec", args{ecCert.PublicKey}, &pb.PublicKey{ - Type: pb.PublicKey_PEM_EC_KEY, - Key: []byte(testLeafPublicKey), + Format: pb.PublicKey_PEM, + Key: []byte(testLeafPublicKey), }, false}, {"ok rsa", args{rsaCert.PublicKey}, &pb.PublicKey{ - Type: pb.PublicKey_PEM_RSA_KEY, - Key: []byte(testRSAPublicKey), + Format: pb.PublicKey_PEM, + Key: []byte(testRSAPublicKey), }, false}, {"fail ed25519", args{edpub}, nil, true}, {"fail ec marshal", args{&ecdsa.PublicKey{ @@ -185,6 +181,7 @@ func Test_createSubject(t *testing.T) { Province: "California", StreetAddress: "1 A St.", PostalCode: "12345", + CommonName: "test.smallstep.com", }}, } for _, tt := range tests { @@ -289,62 +286,55 @@ func Test_createSubjectAlternativeNames(t *testing.T) { } } -func Test_createReusableConfig(t *testing.T) { - withKU := func(ku *pb.KeyUsage) *pb.ReusableConfigWrapper { +func Test_createX509Parameters(t *testing.T) { + withKU := func(ku *pb.KeyUsage) *pb.X509Parameters { if ku.BaseKeyUsage == nil { ku.BaseKeyUsage = &pb.KeyUsage_KeyUsageOptions{} } if ku.ExtendedKeyUsage == nil { ku.ExtendedKeyUsage = &pb.KeyUsage_ExtendedKeyUsageOptions{} } - return &pb.ReusableConfigWrapper{ - ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{ - ReusableConfigValues: &pb.ReusableConfigValues{ - KeyUsage: ku, - }, - }, + return &pb.X509Parameters{ + KeyUsage: ku, } } - withRCV := func(rcv *pb.ReusableConfigValues) *pb.ReusableConfigWrapper { + withRCV := func(rcv *pb.X509Parameters) *pb.X509Parameters { if rcv.KeyUsage == nil { rcv.KeyUsage = &pb.KeyUsage{ BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{}, ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{}, } } - return &pb.ReusableConfigWrapper{ - ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{ - ReusableConfigValues: rcv, - }, - } + return rcv } + vTrue := true + vFalse := false + vZero := int32(0) + vOne := int32(1) + type args struct { cert *x509.Certificate } tests := []struct { name string args args - want *pb.ReusableConfigWrapper + want *pb.X509Parameters }{ {"keyUsageDigitalSignature", args{&x509.Certificate{ KeyUsage: x509.KeyUsageDigitalSignature, - }}, &pb.ReusableConfigWrapper{ - ConfigValues: &pb.ReusableConfigWrapper_ReusableConfigValues{ - ReusableConfigValues: &pb.ReusableConfigValues{ - KeyUsage: &pb.KeyUsage{ - BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{ - DigitalSignature: true, - }, - ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{}, - UnknownExtendedKeyUsages: nil, - }, - CaOptions: nil, - PolicyIds: nil, - AiaOcspServers: nil, - AdditionalExtensions: nil, + }}, &pb.X509Parameters{ + KeyUsage: &pb.KeyUsage{ + BaseKeyUsage: &pb.KeyUsage_KeyUsageOptions{ + DigitalSignature: true, }, + ExtendedKeyUsage: &pb.KeyUsage_ExtendedKeyUsageOptions{}, + UnknownExtendedKeyUsages: nil, }, + CaOptions: nil, + PolicyIds: nil, + AiaOcspServers: nil, + AdditionalExtensions: nil, }}, // KeyUsage {"KeyUsageDigitalSignature", args{&x509.Certificate{KeyUsage: x509.KeyUsageDigitalSignature}}, withKU(&pb.KeyUsage{ @@ -455,48 +445,48 @@ func Test_createReusableConfig(t *testing.T) { }, })}, // BasicCre - {"BasicConstraintsCAMax0", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 0, MaxPathLenZero: true}}, withRCV(&pb.ReusableConfigValues{ - CaOptions: &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(true), - MaxIssuerPathLength: wrapperspb.Int32(0), + {"BasicConstraintsCAMax0", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 0, MaxPathLenZero: true}}, withRCV(&pb.X509Parameters{ + CaOptions: &pb.X509Parameters_CaOptions{ + IsCa: &vTrue, + MaxIssuerPathLength: &vZero, }, })}, - {"BasicConstraintsCAMax1", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 1, MaxPathLenZero: false}}, withRCV(&pb.ReusableConfigValues{ - CaOptions: &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(true), - MaxIssuerPathLength: wrapperspb.Int32(1), + {"BasicConstraintsCAMax1", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 1, MaxPathLenZero: false}}, withRCV(&pb.X509Parameters{ + CaOptions: &pb.X509Parameters_CaOptions{ + IsCa: &vTrue, + MaxIssuerPathLength: &vOne, }, })}, - {"BasicConstraintsCANoMax", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: -1, MaxPathLenZero: false}}, withRCV(&pb.ReusableConfigValues{ - CaOptions: &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(true), + {"BasicConstraintsCANoMax", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: -1, MaxPathLenZero: false}}, withRCV(&pb.X509Parameters{ + CaOptions: &pb.X509Parameters_CaOptions{ + IsCa: &vTrue, MaxIssuerPathLength: nil, }, })}, - {"BasicConstraintsCANoMax0", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.ReusableConfigValues{ - CaOptions: &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(true), + {"BasicConstraintsCANoMax0", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: true, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.X509Parameters{ + CaOptions: &pb.X509Parameters_CaOptions{ + IsCa: &vTrue, MaxIssuerPathLength: nil, }, })}, - {"BasicConstraintsNoCA", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: false, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.ReusableConfigValues{ - CaOptions: &pb.ReusableConfigValues_CaOptions{ - IsCa: wrapperspb.Bool(false), + {"BasicConstraintsNoCA", args{&x509.Certificate{BasicConstraintsValid: true, IsCA: false, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.X509Parameters{ + CaOptions: &pb.X509Parameters_CaOptions{ + IsCa: &vFalse, MaxIssuerPathLength: nil, }, })}, - {"BasicConstraintsNoValid", args{&x509.Certificate{BasicConstraintsValid: false, IsCA: false, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.ReusableConfigValues{ + {"BasicConstraintsNoValid", args{&x509.Certificate{BasicConstraintsValid: false, IsCA: false, MaxPathLen: 0, MaxPathLenZero: false}}, withRCV(&pb.X509Parameters{ CaOptions: nil, })}, // PolicyIdentifiers - {"PolicyIdentifiers", args{&x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}}}, withRCV(&pb.ReusableConfigValues{ + {"PolicyIdentifiers", args{&x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}}}, withRCV(&pb.X509Parameters{ PolicyIds: []*pb.ObjectId{ {ObjectIdPath: []int32{1, 2, 3, 4}}, {ObjectIdPath: []int32{4, 3, 2, 1}}, }, })}, // OCSPServer - {"OCPServers", args{&x509.Certificate{OCSPServer: []string{"https://oscp.doe.com", "https://doe.com/ocsp"}}}, withRCV(&pb.ReusableConfigValues{ + {"OCPServers", args{&x509.Certificate{OCSPServer: []string{"https://oscp.doe.com", "https://doe.com/ocsp"}}}, withRCV(&pb.X509Parameters{ AiaOcspServers: []string{"https://oscp.doe.com", "https://doe.com/ocsp"}, })}, // Extensions @@ -505,7 +495,7 @@ func Test_createReusableConfig(t *testing.T) { {Id: []int{2, 5, 29, 17}, Critical: true, Value: []byte("SANs")}, // {Id: []int{4, 3, 2, 1}, Critical: false, Value: []byte("zoobar")}, {Id: []int{2, 5, 29, 31}, Critical: false, Value: []byte("CRL Distribution points")}, - }}}, withRCV(&pb.ReusableConfigValues{ + }}}, withRCV(&pb.X509Parameters{ AdditionalExtensions: []*pb.X509Extension{ {ObjectId: &pb.ObjectId{ObjectIdPath: []int32{1, 2, 3, 4}}, Critical: true, Value: []byte("foobar")}, {ObjectId: &pb.ObjectId{ObjectIdPath: []int32{4, 3, 2, 1}}, Critical: false, Value: []byte("zoobar")}, @@ -514,8 +504,8 @@ func Test_createReusableConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := createReusableConfig(tt.args.cert); !reflect.DeepEqual(got, tt.want) { - t.Errorf("createReusableConfig() = %v, want %v", got, tt.want) + if got := createX509Parameters(tt.args.cert); !reflect.DeepEqual(got, tt.want) { + t.Errorf("createX509Parameters() = %v, want %v", got, tt.want) } }) } diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index 602d2942..9df8b5c6 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -74,6 +74,7 @@ type CloudCAS struct { project string location string caPool string + gcsBucket string } // newCertificateAuthorityClient creates the certificate authority client. This @@ -263,6 +264,8 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor return nil, errors.New("cloudCAS `project` cannot be empty") case c.location == "": return nil, errors.New("cloudCAS `location` cannot be empty") + case c.caPool == "": + return nil, errors.New("cloudCAS `caPool` cannot be empty") case req.Template == nil: return nil, errors.New("createCertificateAuthorityRequest `template` cannot be nil") case req.Lifetime == 0: @@ -335,7 +338,7 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor }, Lifetime: durationpb.New(req.Lifetime), KeySpec: keySpec, - GcsBucket: "", + GcsBucket: c.gcsBucket, Labels: map[string]string{}, }, } diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index eb682e28..ac120d81 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -20,7 +20,7 @@ import ( "time" lroauto "cloud.google.com/go/longrunning/autogen" - privateca "cloud.google.com/go/security/privateca/apiv1beta1" + privateca "cloud.google.com/go/security/privateca/apiv1" gomock "github.com/golang/mock/gomock" "github.com/google/uuid" gax "github.com/googleapis/gax-go/v2" @@ -28,19 +28,23 @@ import ( "github.com/smallstep/certificates/cas/apiv1" kmsapi "github.com/smallstep/certificates/kms/apiv1" "google.golang.org/api/option" - pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1" + pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" longrunningpb "google.golang.org/genproto/googleapis/longrunning" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" "google.golang.org/protobuf/types/known/anypb" ) var ( errTest = errors.New("test error") - testAuthorityName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca" - testCertificateName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca/certificates/test-certificate" + testCaPoolName = "projects/test-project/locations/us-west1/caPools/test-capool" + testAuthorityName = "projects/test-project/locations/us-west1/caPools/test-capool/certificateAuthorities/test-ca" + testCertificateName = "projects/test-project/locations/us-west1/caPools/test-capool/certificateAuthorities/test-ca/certificates/test-certificate" testProject = "test-project" testLocation = "us-west1" + testCaPool = "test-capool" testRootCertificate = `-----BEGIN CERTIFICATE----- MIIBeDCCAR+gAwIBAgIQcXWWjtSZ/PAyH8D1Ou4L9jAKBggqhkjOPQQDAjAbMRkw FwYDVQQDExBDbG91ZENBUyBSb290IENBMB4XDTIwMTAyNzIyNTM1NFoXDTMwMTAy @@ -214,6 +218,18 @@ func (c *testClient) ActivateCertificateAuthority(ctx context.Context, req *pb.A return nil, errors.New("use NewMockCertificateAuthorityClient") } +func (c *testClient) EnableCertificateAuthority(ctx context.Context, req *pb.EnableCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.EnableCertificateAuthorityOperation, error) { + return nil, errors.New("use NewMockCertificateAuthorityClient") +} + +func (c *testClient) GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest, opts ...gax.CallOption) (*pb.CaPool, error) { + return nil, errors.New("use NewMockCertificateAuthorityClient") +} + +func (c *testClient) CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequest, opts ...gax.CallOption) (*privateca.CreateCaPoolOperation, error) { + return nil, errors.New("use NewMockCertificateAuthorityClient") +} + func mustParseCertificate(t *testing.T, pemCert string) *x509.Certificate { t.Helper() crt, err := parseCertificate(pemCert) @@ -262,6 +278,7 @@ func TestNew(t *testing.T) { certificateAuthority: testAuthorityName, project: testProject, location: testLocation, + caPool: testCaPool, }, false}, {"ok with credentials", args{context.Background(), apiv1.Options{ CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json", @@ -270,16 +287,18 @@ func TestNew(t *testing.T) { certificateAuthority: testAuthorityName, project: testProject, location: testLocation, + caPool: testCaPool, }, false}, {"ok creator", args{context.Background(), apiv1.Options{ - IsCreator: true, Project: testProject, Location: testLocation, + IsCreator: true, Project: testProject, Location: testLocation, CAPool: testCaPool, }}, &CloudCAS{ client: &testClient{}, project: testProject, location: testLocation, + caPool: testCaPool, }, false}, {"fail certificate authority", args{context.Background(), apiv1.Options{ - CertificateAuthority: "projects/ok1234/locations/ok1234/certificateAuthorities/ok1234/bad", + CertificateAuthority: "projects/ok1234/locations/ok1234/caPools/ok1234/certificateAuthorities/ok1234/bad", }}, nil, true}, {"fail certificate authority regex", args{context.Background(), apiv1.Options{}}, nil, true}, {"fail with credentials", args{context.Background(), apiv1.Options{ @@ -291,6 +310,9 @@ func TestNew(t *testing.T) { {"fail creator location", args{context.Background(), apiv1.Options{ IsCreator: true, Project: testProject, Location: "", }}, nil, true}, + {"fail caPool", args{context.Background(), apiv1.Options{ + IsCreator: true, Project: testProject, Location: testLocation, CAPool: "", + }}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -320,6 +342,7 @@ func TestNew_register(t *testing.T) { certificateAuthority: testAuthorityName, project: testProject, location: testLocation, + caPool: testCaPool, } newFn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS) @@ -338,7 +361,6 @@ func TestNew_register(t *testing.T) { if !reflect.DeepEqual(got, want) { t.Errorf("New() = %v, want %v", got, want) } - } func TestNew_real(t *testing.T) { @@ -812,14 +834,27 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { if err != nil { t.Fatal(err) } - fake := &privateca.CertificateAuthorityClient{ - LROClient: client, + fake, err := privateca.NewCertificateAuthorityClient(context.Background(), option.WithGRPCConn(conn)) + if err != nil { + t.Fatal(err) } + fake.LROClient = client // Configure mocks any := gomock.Any() // ok root + m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found")) + m.EXPECT().CreateCaPool(any, any).Return(fake.CreateCaPoolOperation("CreateCaPool"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "CreateCaPool", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CaPool{ + Name: testCaPoolName, + })).(*anypb.Any), + }, + }, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -831,8 +866,20 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { })).(*anypb.Any), }, }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "EnableCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) // ok intermediate + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -857,7 +904,20 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { })).(*anypb.Any), }, }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "EnableCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + // ok intermediate local signer + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -886,8 +946,20 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { })).(*anypb.Any), }, }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "EnableCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) // ok create key + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -899,15 +971,41 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { })).(*anypb.Any), }, }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "EnableCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + + // fail GetCaPool + m.EXPECT().GetCaPool(any, any).Return(nil, errTest) + + // fail CreateCaPool + m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found")) + m.EXPECT().CreateCaPool(any, any).Return(nil, errTest) + + // fail CreateCaPool.Wait + m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found")) + m.EXPECT().CreateCaPool(any, any).Return(fake.CreateCaPoolOperation("CreateCaPool"), nil) + mos.EXPECT().GetOperation(any, any).Return(nil, errTest) // fail CreateCertificateAuthority + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(nil, errTest) // fail CreateCertificateAuthority.Wait + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(nil, errTest) // fail FetchCertificateAuthorityCsr + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -921,6 +1019,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(nil, errTest) // fail CreateCertificate + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -937,6 +1036,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().CreateCertificate(any, any).Return(nil, errTest) // fail ActivateCertificateAuthority + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -957,6 +1057,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().ActivateCertificateAuthority(any, any).Return(nil, errTest) // fail ActivateCertificateAuthority.Wait + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -978,6 +1079,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { mos.EXPECT().GetOperation(any, any).Return(nil, errTest) // fail x509util.CreateCertificate + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -993,6 +1095,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { }, nil) // fail parseCertificateRequest + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "CreateCertificateAuthority", @@ -1015,6 +1118,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { certificateAuthority string project string location string + caPool string } type args struct { req *apiv1.CreateCertificateAuthorityRequest @@ -1026,7 +1130,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { want *apiv1.CreateCertificateAuthorityResponse wantErr bool }{ - {"ok root", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok root", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1034,7 +1138,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Name: testAuthorityName, Certificate: rootCrt, }, false}, - {"ok intermediate", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1047,7 +1151,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: intCrt, CertificateChain: []*x509.Certificate{rootCrt}, }, false}, - {"ok intermediate local signer", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok intermediate local signer", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1060,7 +1164,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: intCrt, CertificateChain: []*x509.Certificate{rootCrt}, }, false}, - {"ok create key", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok create key", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1071,41 +1175,46 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Name: testAuthorityName, Certificate: rootCrt, }, false}, - {"fail project", fields{m, "", "", testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail project", fields{m, "", "", testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + {"fail location", fields{m, "", testProject, "", testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail location", fields{m, "", testProject, ""}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail caPool", fields{m, "", testProject, testLocation, ""}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail template", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail template", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail lifetime", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail lifetime", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), }}, nil, true}, - {"fail parent", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parent", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail parent name", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parent name", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, Parent: &apiv1.CreateCertificateAuthorityResponse{}, }}, nil, true}, - {"fail type", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail type", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: 0, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail create key", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail create key", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1113,17 +1222,32 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { SignatureAlgorithm: kmsapi.PureEd25519, }, }}, nil, true}, - {"fail CreateCertificateAuthority", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail GetCaPool", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + {"fail CreateCaPool", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + {"fail CreateCaPool.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + {"fail CreateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail CreateCertificateAuthority.Wait", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1132,7 +1256,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail CreateCertificate", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCertificate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1141,7 +1265,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail ActivateCertificateAuthority", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail ActivateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1150,7 +1274,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail ActivateCertificateAuthority.Wait", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail ActivateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1159,7 +1283,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail x509util.CreateCertificate", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail x509util.CreateCertificate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1168,7 +1292,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Signer: createBadSigner(t), }, }}, nil, true}, - {"fail parseCertificateRequest", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parseCertificateRequest", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1185,6 +1309,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { certificateAuthority: tt.fields.certificateAuthority, project: tt.fields.project, location: tt.fields.location, + caPool: tt.fields.caPool, } got, err := c.CreateCertificateAuthority(tt.args.req) if (err != nil) != tt.wantErr { diff --git a/cas/cloudcas/mock_client_test.go b/cas/cloudcas/mock_client_test.go index b81d3135..de5c2acb 100644 --- a/cas/cloudcas/mock_client_test.go +++ b/cas/cloudcas/mock_client_test.go @@ -1,15 +1,15 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: ./cas/cloudcas/cloudcas.go +// Source: github.com/smallstep/certificates/cas/cloudcas (interfaces: CertificateAuthorityClient) // Package cloudcas is a generated GoMock package. package cloudcas import ( - privateca "cloud.google.com/go/security/privateca/apiv1beta1" + privateca "cloud.google.com/go/security/privateca/apiv1" context "context" gomock "github.com/golang/mock/gomock" gax "github.com/googleapis/gax-go/v2" - privateca0 "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1" + privateca0 "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" reflect "reflect" ) @@ -36,71 +36,71 @@ func (m *MockCertificateAuthorityClient) EXPECT() *MockCertificateAuthorityClien return m.recorder } -// CreateCertificate mocks base method -func (m *MockCertificateAuthorityClient) CreateCertificate(ctx context.Context, req *privateca0.CreateCertificateRequest, opts ...gax.CallOption) (*privateca0.Certificate, error) { +// ActivateCertificateAuthority mocks base method +func (m *MockCertificateAuthorityClient) ActivateCertificateAuthority(arg0 context.Context, arg1 *privateca0.ActivateCertificateAuthorityRequest, arg2 ...gax.CallOption) (*privateca.ActivateCertificateAuthorityOperation, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "CreateCertificate", varargs...) - ret0, _ := ret[0].(*privateca0.Certificate) + ret := m.ctrl.Call(m, "ActivateCertificateAuthority", varargs...) + ret0, _ := ret[0].(*privateca.ActivateCertificateAuthorityOperation) ret1, _ := ret[1].(error) return ret0, ret1 } -// CreateCertificate indicates an expected call of CreateCertificate -func (mr *MockCertificateAuthorityClientMockRecorder) CreateCertificate(ctx, req interface{}, opts ...interface{}) *gomock.Call { +// ActivateCertificateAuthority indicates an expected call of ActivateCertificateAuthority +func (mr *MockCertificateAuthorityClientMockRecorder) ActivateCertificateAuthority(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCertificate", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).CreateCertificate), varargs...) + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivateCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).ActivateCertificateAuthority), varargs...) } -// RevokeCertificate mocks base method -func (m *MockCertificateAuthorityClient) RevokeCertificate(ctx context.Context, req *privateca0.RevokeCertificateRequest, opts ...gax.CallOption) (*privateca0.Certificate, error) { +// CreateCaPool mocks base method +func (m *MockCertificateAuthorityClient) CreateCaPool(arg0 context.Context, arg1 *privateca0.CreateCaPoolRequest, arg2 ...gax.CallOption) (*privateca.CreateCaPoolOperation, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "RevokeCertificate", varargs...) - ret0, _ := ret[0].(*privateca0.Certificate) + ret := m.ctrl.Call(m, "CreateCaPool", varargs...) + ret0, _ := ret[0].(*privateca.CreateCaPoolOperation) ret1, _ := ret[1].(error) return ret0, ret1 } -// RevokeCertificate indicates an expected call of RevokeCertificate -func (mr *MockCertificateAuthorityClientMockRecorder) RevokeCertificate(ctx, req interface{}, opts ...interface{}) *gomock.Call { +// CreateCaPool indicates an expected call of CreateCaPool +func (mr *MockCertificateAuthorityClientMockRecorder) CreateCaPool(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeCertificate", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).RevokeCertificate), varargs...) + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCaPool", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).CreateCaPool), varargs...) } -// GetCertificateAuthority mocks base method -func (m *MockCertificateAuthorityClient) GetCertificateAuthority(ctx context.Context, req *privateca0.GetCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca0.CertificateAuthority, error) { +// CreateCertificate mocks base method +func (m *MockCertificateAuthorityClient) CreateCertificate(arg0 context.Context, arg1 *privateca0.CreateCertificateRequest, arg2 ...gax.CallOption) (*privateca0.Certificate, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "GetCertificateAuthority", varargs...) - ret0, _ := ret[0].(*privateca0.CertificateAuthority) + ret := m.ctrl.Call(m, "CreateCertificate", varargs...) + ret0, _ := ret[0].(*privateca0.Certificate) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetCertificateAuthority indicates an expected call of GetCertificateAuthority -func (mr *MockCertificateAuthorityClientMockRecorder) GetCertificateAuthority(ctx, req interface{}, opts ...interface{}) *gomock.Call { +// CreateCertificate indicates an expected call of CreateCertificate +func (mr *MockCertificateAuthorityClientMockRecorder) CreateCertificate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).GetCertificateAuthority), varargs...) + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCertificate", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).CreateCertificate), varargs...) } // CreateCertificateAuthority mocks base method -func (m *MockCertificateAuthorityClient) CreateCertificateAuthority(ctx context.Context, req *privateca0.CreateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.CreateCertificateAuthorityOperation, error) { +func (m *MockCertificateAuthorityClient) CreateCertificateAuthority(arg0 context.Context, arg1 *privateca0.CreateCertificateAuthorityRequest, arg2 ...gax.CallOption) (*privateca.CreateCertificateAuthorityOperation, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CreateCertificateAuthority", varargs...) @@ -110,17 +110,37 @@ func (m *MockCertificateAuthorityClient) CreateCertificateAuthority(ctx context. } // CreateCertificateAuthority indicates an expected call of CreateCertificateAuthority -func (mr *MockCertificateAuthorityClientMockRecorder) CreateCertificateAuthority(ctx, req interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockCertificateAuthorityClientMockRecorder) CreateCertificateAuthority(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) + varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).CreateCertificateAuthority), varargs...) } +// EnableCertificateAuthority mocks base method +func (m *MockCertificateAuthorityClient) EnableCertificateAuthority(arg0 context.Context, arg1 *privateca0.EnableCertificateAuthorityRequest, arg2 ...gax.CallOption) (*privateca.EnableCertificateAuthorityOperation, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "EnableCertificateAuthority", varargs...) + ret0, _ := ret[0].(*privateca.EnableCertificateAuthorityOperation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnableCertificateAuthority indicates an expected call of EnableCertificateAuthority +func (mr *MockCertificateAuthorityClientMockRecorder) EnableCertificateAuthority(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).EnableCertificateAuthority), varargs...) +} + // FetchCertificateAuthorityCsr mocks base method -func (m *MockCertificateAuthorityClient) FetchCertificateAuthorityCsr(ctx context.Context, req *privateca0.FetchCertificateAuthorityCsrRequest, opts ...gax.CallOption) (*privateca0.FetchCertificateAuthorityCsrResponse, error) { +func (m *MockCertificateAuthorityClient) FetchCertificateAuthorityCsr(arg0 context.Context, arg1 *privateca0.FetchCertificateAuthorityCsrRequest, arg2 ...gax.CallOption) (*privateca0.FetchCertificateAuthorityCsrResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "FetchCertificateAuthorityCsr", varargs...) @@ -130,28 +150,68 @@ func (m *MockCertificateAuthorityClient) FetchCertificateAuthorityCsr(ctx contex } // FetchCertificateAuthorityCsr indicates an expected call of FetchCertificateAuthorityCsr -func (mr *MockCertificateAuthorityClientMockRecorder) FetchCertificateAuthorityCsr(ctx, req interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockCertificateAuthorityClientMockRecorder) FetchCertificateAuthorityCsr(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) + varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCertificateAuthorityCsr", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).FetchCertificateAuthorityCsr), varargs...) } -// ActivateCertificateAuthority mocks base method -func (m *MockCertificateAuthorityClient) ActivateCertificateAuthority(ctx context.Context, req *privateca0.ActivateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.ActivateCertificateAuthorityOperation, error) { +// GetCaPool mocks base method +func (m *MockCertificateAuthorityClient) GetCaPool(arg0 context.Context, arg1 *privateca0.GetCaPoolRequest, arg2 ...gax.CallOption) (*privateca0.CaPool, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, req} - for _, a := range opts { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "ActivateCertificateAuthority", varargs...) - ret0, _ := ret[0].(*privateca.ActivateCertificateAuthorityOperation) + ret := m.ctrl.Call(m, "GetCaPool", varargs...) + ret0, _ := ret[0].(*privateca0.CaPool) ret1, _ := ret[1].(error) return ret0, ret1 } -// ActivateCertificateAuthority indicates an expected call of ActivateCertificateAuthority -func (mr *MockCertificateAuthorityClientMockRecorder) ActivateCertificateAuthority(ctx, req interface{}, opts ...interface{}) *gomock.Call { +// GetCaPool indicates an expected call of GetCaPool +func (mr *MockCertificateAuthorityClientMockRecorder) GetCaPool(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, req}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivateCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).ActivateCertificateAuthority), varargs...) + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCaPool", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).GetCaPool), varargs...) +} + +// GetCertificateAuthority mocks base method +func (m *MockCertificateAuthorityClient) GetCertificateAuthority(arg0 context.Context, arg1 *privateca0.GetCertificateAuthorityRequest, arg2 ...gax.CallOption) (*privateca0.CertificateAuthority, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetCertificateAuthority", varargs...) + ret0, _ := ret[0].(*privateca0.CertificateAuthority) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCertificateAuthority indicates an expected call of GetCertificateAuthority +func (mr *MockCertificateAuthorityClientMockRecorder) GetCertificateAuthority(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateAuthority", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).GetCertificateAuthority), varargs...) +} + +// RevokeCertificate mocks base method +func (m *MockCertificateAuthorityClient) RevokeCertificate(arg0 context.Context, arg1 *privateca0.RevokeCertificateRequest, arg2 ...gax.CallOption) (*privateca0.Certificate, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RevokeCertificate", varargs...) + ret0, _ := ret[0].(*privateca0.Certificate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RevokeCertificate indicates an expected call of RevokeCertificate +func (mr *MockCertificateAuthorityClientMockRecorder) RevokeCertificate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeCertificate", reflect.TypeOf((*MockCertificateAuthorityClient)(nil).RevokeCertificate), varargs...) } diff --git a/cas/cloudcas/mock_operation_server_test.go b/cas/cloudcas/mock_operation_server_test.go index 48564cd1..ee2743d4 100644 --- a/cas/cloudcas/mock_operation_server_test.go +++ b/cas/cloudcas/mock_operation_server_test.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/mariano/go/pkg/mod/google.golang.org/genproto@v0.0.0-20200904004341-0bd0a958aa1d/googleapis/longrunning/operations.pb.go +// Source: google.golang.org/genproto/googleapis/longrunning (interfaces: OperationsServer) // Package cloudcas is a generated GoMock package. package cloudcas @@ -8,169 +8,10 @@ import ( context "context" gomock "github.com/golang/mock/gomock" longrunning "google.golang.org/genproto/googleapis/longrunning" - grpc "google.golang.org/grpc" emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" ) -// MockisOperation_Result is a mock of isOperation_Result interface -type MockisOperation_Result struct { - ctrl *gomock.Controller - recorder *MockisOperation_ResultMockRecorder -} - -// MockisOperation_ResultMockRecorder is the mock recorder for MockisOperation_Result -type MockisOperation_ResultMockRecorder struct { - mock *MockisOperation_Result -} - -// NewMockisOperation_Result creates a new mock instance -func NewMockisOperation_Result(ctrl *gomock.Controller) *MockisOperation_Result { - mock := &MockisOperation_Result{ctrl: ctrl} - mock.recorder = &MockisOperation_ResultMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockisOperation_Result) EXPECT() *MockisOperation_ResultMockRecorder { - return m.recorder -} - -// isOperation_Result mocks base method -func (m *MockisOperation_Result) isOperation_Result() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "isOperation_Result") -} - -// isOperation_Result indicates an expected call of isOperation_Result -func (mr *MockisOperation_ResultMockRecorder) isOperation_Result() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "isOperation_Result", reflect.TypeOf((*MockisOperation_Result)(nil).isOperation_Result)) -} - -// MockOperationsClient is a mock of OperationsClient interface -type MockOperationsClient struct { - ctrl *gomock.Controller - recorder *MockOperationsClientMockRecorder -} - -// MockOperationsClientMockRecorder is the mock recorder for MockOperationsClient -type MockOperationsClientMockRecorder struct { - mock *MockOperationsClient -} - -// NewMockOperationsClient creates a new mock instance -func NewMockOperationsClient(ctrl *gomock.Controller) *MockOperationsClient { - mock := &MockOperationsClient{ctrl: ctrl} - mock.recorder = &MockOperationsClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockOperationsClient) EXPECT() *MockOperationsClientMockRecorder { - return m.recorder -} - -// ListOperations mocks base method -func (m *MockOperationsClient) ListOperations(ctx context.Context, in *longrunning.ListOperationsRequest, opts ...grpc.CallOption) (*longrunning.ListOperationsResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "ListOperations", varargs...) - ret0, _ := ret[0].(*longrunning.ListOperationsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListOperations indicates an expected call of ListOperations -func (mr *MockOperationsClientMockRecorder) ListOperations(ctx, in interface{}, opts ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOperations", reflect.TypeOf((*MockOperationsClient)(nil).ListOperations), varargs...) -} - -// GetOperation mocks base method -func (m *MockOperationsClient) GetOperation(ctx context.Context, in *longrunning.GetOperationRequest, opts ...grpc.CallOption) (*longrunning.Operation, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GetOperation", varargs...) - ret0, _ := ret[0].(*longrunning.Operation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOperation indicates an expected call of GetOperation -func (mr *MockOperationsClientMockRecorder) GetOperation(ctx, in interface{}, opts ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperation", reflect.TypeOf((*MockOperationsClient)(nil).GetOperation), varargs...) -} - -// DeleteOperation mocks base method -func (m *MockOperationsClient) DeleteOperation(ctx context.Context, in *longrunning.DeleteOperationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "DeleteOperation", varargs...) - ret0, _ := ret[0].(*emptypb.Empty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteOperation indicates an expected call of DeleteOperation -func (mr *MockOperationsClientMockRecorder) DeleteOperation(ctx, in interface{}, opts ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOperation", reflect.TypeOf((*MockOperationsClient)(nil).DeleteOperation), varargs...) -} - -// CancelOperation mocks base method -func (m *MockOperationsClient) CancelOperation(ctx context.Context, in *longrunning.CancelOperationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "CancelOperation", varargs...) - ret0, _ := ret[0].(*emptypb.Empty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CancelOperation indicates an expected call of CancelOperation -func (mr *MockOperationsClientMockRecorder) CancelOperation(ctx, in interface{}, opts ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOperation", reflect.TypeOf((*MockOperationsClient)(nil).CancelOperation), varargs...) -} - -// WaitOperation mocks base method -func (m *MockOperationsClient) WaitOperation(ctx context.Context, in *longrunning.WaitOperationRequest, opts ...grpc.CallOption) (*longrunning.Operation, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "WaitOperation", varargs...) - ret0, _ := ret[0].(*longrunning.Operation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// WaitOperation indicates an expected call of WaitOperation -func (mr *MockOperationsClientMockRecorder) WaitOperation(ctx, in interface{}, opts ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitOperation", reflect.TypeOf((*MockOperationsClient)(nil).WaitOperation), varargs...) -} - // MockOperationsServer is a mock of OperationsServer interface type MockOperationsServer struct { ctrl *gomock.Controller @@ -194,64 +35,64 @@ func (m *MockOperationsServer) EXPECT() *MockOperationsServerMockRecorder { return m.recorder } -// ListOperations mocks base method -func (m *MockOperationsServer) ListOperations(arg0 context.Context, arg1 *longrunning.ListOperationsRequest) (*longrunning.ListOperationsResponse, error) { +// CancelOperation mocks base method +func (m *MockOperationsServer) CancelOperation(arg0 context.Context, arg1 *longrunning.CancelOperationRequest) (*emptypb.Empty, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListOperations", arg0, arg1) - ret0, _ := ret[0].(*longrunning.ListOperationsResponse) + ret := m.ctrl.Call(m, "CancelOperation", arg0, arg1) + ret0, _ := ret[0].(*emptypb.Empty) ret1, _ := ret[1].(error) return ret0, ret1 } -// ListOperations indicates an expected call of ListOperations -func (mr *MockOperationsServerMockRecorder) ListOperations(arg0, arg1 interface{}) *gomock.Call { +// CancelOperation indicates an expected call of CancelOperation +func (mr *MockOperationsServerMockRecorder) CancelOperation(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOperations", reflect.TypeOf((*MockOperationsServer)(nil).ListOperations), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOperation", reflect.TypeOf((*MockOperationsServer)(nil).CancelOperation), arg0, arg1) } -// GetOperation mocks base method -func (m *MockOperationsServer) GetOperation(arg0 context.Context, arg1 *longrunning.GetOperationRequest) (*longrunning.Operation, error) { +// DeleteOperation mocks base method +func (m *MockOperationsServer) DeleteOperation(arg0 context.Context, arg1 *longrunning.DeleteOperationRequest) (*emptypb.Empty, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOperation", arg0, arg1) - ret0, _ := ret[0].(*longrunning.Operation) + ret := m.ctrl.Call(m, "DeleteOperation", arg0, arg1) + ret0, _ := ret[0].(*emptypb.Empty) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetOperation indicates an expected call of GetOperation -func (mr *MockOperationsServerMockRecorder) GetOperation(arg0, arg1 interface{}) *gomock.Call { +// DeleteOperation indicates an expected call of DeleteOperation +func (mr *MockOperationsServerMockRecorder) DeleteOperation(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperation", reflect.TypeOf((*MockOperationsServer)(nil).GetOperation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOperation", reflect.TypeOf((*MockOperationsServer)(nil).DeleteOperation), arg0, arg1) } -// DeleteOperation mocks base method -func (m *MockOperationsServer) DeleteOperation(arg0 context.Context, arg1 *longrunning.DeleteOperationRequest) (*emptypb.Empty, error) { +// GetOperation mocks base method +func (m *MockOperationsServer) GetOperation(arg0 context.Context, arg1 *longrunning.GetOperationRequest) (*longrunning.Operation, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteOperation", arg0, arg1) - ret0, _ := ret[0].(*emptypb.Empty) + ret := m.ctrl.Call(m, "GetOperation", arg0, arg1) + ret0, _ := ret[0].(*longrunning.Operation) ret1, _ := ret[1].(error) return ret0, ret1 } -// DeleteOperation indicates an expected call of DeleteOperation -func (mr *MockOperationsServerMockRecorder) DeleteOperation(arg0, arg1 interface{}) *gomock.Call { +// GetOperation indicates an expected call of GetOperation +func (mr *MockOperationsServerMockRecorder) GetOperation(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOperation", reflect.TypeOf((*MockOperationsServer)(nil).DeleteOperation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperation", reflect.TypeOf((*MockOperationsServer)(nil).GetOperation), arg0, arg1) } -// CancelOperation mocks base method -func (m *MockOperationsServer) CancelOperation(arg0 context.Context, arg1 *longrunning.CancelOperationRequest) (*emptypb.Empty, error) { +// ListOperations mocks base method +func (m *MockOperationsServer) ListOperations(arg0 context.Context, arg1 *longrunning.ListOperationsRequest) (*longrunning.ListOperationsResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CancelOperation", arg0, arg1) - ret0, _ := ret[0].(*emptypb.Empty) + ret := m.ctrl.Call(m, "ListOperations", arg0, arg1) + ret0, _ := ret[0].(*longrunning.ListOperationsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } -// CancelOperation indicates an expected call of CancelOperation -func (mr *MockOperationsServerMockRecorder) CancelOperation(arg0, arg1 interface{}) *gomock.Call { +// ListOperations indicates an expected call of ListOperations +func (mr *MockOperationsServerMockRecorder) ListOperations(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOperation", reflect.TypeOf((*MockOperationsServer)(nil).CancelOperation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOperations", reflect.TypeOf((*MockOperationsServer)(nil).ListOperations), arg0, arg1) } // WaitOperation mocks base method From 9db68db5093a0705eabb25c9619f08d554796504 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 7 Jun 2021 19:17:30 -0700 Subject: [PATCH 078/291] Add tests with cloudCAS EnableCertificateAuthority. --- cas/cloudcas/cloudcas_test.go | 134 +++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index ac120d81..e6e89ce3 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -893,6 +893,10 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{ PemCsr: testIntermediateCsr, }, nil) + m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{ + PemCertificate: testIntermediateCertificate, + PemCertificateChain: []string{testRootCertificate}, + }, nil) m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "ActivateCertificateAuthority", @@ -931,10 +935,6 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{ PemCsr: testIntermediateCsr, }, nil) - m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{ - PemCertificate: testIntermediateCertificate, - PemCertificateChain: []string{testRootCertificate}, - }, nil) m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ Name: "ActivateCertificateAuthority", @@ -1004,6 +1004,102 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) mos.EXPECT().GetOperation(any, any).Return(nil, errTest) + // fail EnableCertificateAuthority + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) + m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "CreateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(nil, errTest) + + // fail EnableCertificateAuthority.Wait + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) + m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "CreateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(nil, errTest) + + // fail EnableCertificateAuthority intermediate + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) + m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "CreateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{ + PemCsr: testIntermediateCsr, + }, nil) + m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{ + PemCertificate: testIntermediateCertificate, + PemCertificateChain: []string{testRootCertificate}, + }, nil) + m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "ActivateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(nil, errTest) + + // fail EnableCertificateAuthority.Wait intermediate + m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) + m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "CreateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{ + PemCsr: testIntermediateCsr, + }, nil) + m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{ + PemCertificate: testIntermediateCertificate, + PemCertificateChain: []string{testRootCertificate}, + }, nil) + m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil) + mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{ + Name: "ActivateCertificateAuthority", + Done: true, + Result: &longrunningpb.Operation_Response{ + Response: must(anypb.New(&pb.CertificateAuthority{ + Name: testAuthorityName, + PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate}, + })).(*anypb.Any), + }, + }, nil) + m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil) + mos.EXPECT().GetOperation(any, any).Return(nil, errTest) + // fail FetchCertificateAuthorityCsr m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil) m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil) @@ -1247,6 +1343,36 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, + {"fail EnableCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + {"fail EnableCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: mustParseCertificate(t, testRootCertificate), + Lifetime: 24 * time.Hour, + }}, nil, true}, + + {"fail EnableCertificateAuthority intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.IntermediateCA, + Template: mustParseCertificate(t, testIntermediateCertificate), + Lifetime: 24 * time.Hour, + Parent: &apiv1.CreateCertificateAuthorityResponse{ + Name: testAuthorityName, + Certificate: rootCrt, + }, + }}, nil, true}, + {"fail EnableCertificateAuthority.Wait intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.IntermediateCA, + Template: mustParseCertificate(t, testIntermediateCertificate), + Lifetime: 24 * time.Hour, + Parent: &apiv1.CreateCertificateAuthorityResponse{ + Name: testAuthorityName, + Certificate: rootCrt, + }, + }}, nil, true}, + {"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), From 529eb4bae9c4d083e673523fd6bf8d1aa1b83a3b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 7 Jun 2021 19:20:23 -0700 Subject: [PATCH 079/291] Rename CAPool to CaPool. --- cas/apiv1/options.go | 2 +- cas/cloudcas/cloudcas.go | 9 +++++---- cas/cloudcas/cloudcas_test.go | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index e1adcecb..80d42d90 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -49,7 +49,7 @@ type Options struct { // certificate authority. Project string `json:"-"` Location string `json:"-"` - CAPool string `json:"-"` + CaPool string `json:"-"` } // CertificateIssuer contains the properties used to use the StepCAS certificate diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index 9df8b5c6..a4e1552c 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -100,9 +100,10 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { return nil, errors.New("cloudCAS 'project' cannot be empty") case opts.Location == "": return nil, errors.New("cloudCAS 'location' cannot be empty") - case opts.CAPool == "": + case opts.CaPool == "": return nil, errors.New("cloudCAS 'caPool' cannot be empty") } + } else { if opts.CertificateAuthority == "" { return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty") @@ -118,8 +119,8 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { if opts.Location == "" { opts.Location = parts[3] } - if opts.CAPool == "" { - opts.CAPool = parts[5] + if opts.CaPool == "" { + opts.CaPool = parts[5] } } } @@ -134,7 +135,7 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { certificateAuthority: opts.CertificateAuthority, project: opts.Project, location: opts.Location, - caPool: opts.CAPool, + caPool: opts.CaPool, }, nil } diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index e6e89ce3..6fb5e520 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -290,7 +290,7 @@ func TestNew(t *testing.T) { caPool: testCaPool, }, false}, {"ok creator", args{context.Background(), apiv1.Options{ - IsCreator: true, Project: testProject, Location: testLocation, CAPool: testCaPool, + IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, }}, &CloudCAS{ client: &testClient{}, project: testProject, @@ -311,7 +311,7 @@ func TestNew(t *testing.T) { IsCreator: true, Project: testProject, Location: "", }}, nil, true}, {"fail caPool", args{context.Background(), apiv1.Options{ - IsCreator: true, Project: testProject, Location: testLocation, CAPool: "", + IsCreator: true, Project: testProject, Location: testLocation, CaPool: "", }}, nil, true}, } for _, tt := range tests { From 2ac53f7c69e22ef86b43ae367db259178ef90796 Mon Sep 17 00:00:00 2001 From: Kevin Chen <49530888+devadvocado@users.noreply.github.com> Date: Tue, 8 Jun 2021 09:42:20 -0700 Subject: [PATCH 080/291] update gitter to discord --- docs/CONTRIBUTING.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 93749026..d7356fd9 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -7,12 +7,20 @@ to manage issues, etc. ## Table of Contents -* [Building From Source](#building-from-source) -* [Asking Support Questions](#asking-support-questions) -* [Reporting Issues](#reporting-issues) -* [Submitting Patches](#submitting-patches) - * [Code Contribution Guidelines](#code-contribution-guidelines) - * [Git Commit Message Guidelines](#git-commit-message-guidelines) +- [Contributing to `step certificates`](#contributing-to-step-certificates) + - [Table of Contents](#table-of-contents) + - [Building From Source](#building-from-source) + - [Build a standard `step-ca`](#build-a-standard-step-ca) + - [Build `step-ca` using CGO](#build-step-ca-using-cgo) + - [The CGO build enables PKCS #11 and YubiKey PIV support](#the-cgo-build-enables-pkcs-11-and-yubikey-piv-support) + - [1. Install PCSC support](#1-install-pcsc-support) + - [2. Build `step-ca`](#2-build-step-ca) + - [Asking Support Questions](#asking-support-questions) + - [Reporting Issues](#reporting-issues) + - [Code Contribution](#code-contribution) + - [Submitting Patches](#submitting-patches) + - [Code Contribution Guidelines](#code-contribution-guidelines) + - [Git Commit Message Guidelines](#git-commit-message-guidelines) ## Building From Source @@ -73,7 +81,7 @@ When the build is complete, you will find binaries in `bin/`. ## Asking Support Questions -Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Gitter](https://gitter.im/smallstep/community). +Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Discord](https://bit.ly/stepdiscord). ## Reporting Issues From ac3c754a6dc572e3541ba6cca8eea60bcf7a8410 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 8 Jun 2021 17:43:52 -0700 Subject: [PATCH 081/291] Use known CA and add tier and gcs bucket options. --- cas/apiv1/options.go | 14 +++-- cas/cloudcas/cloudcas.go | 66 ++++++++++++++++-------- cas/cloudcas/cloudcas_test.go | 97 +++++++++++++++++++++++------------ 3 files changed, 120 insertions(+), 57 deletions(-) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index 80d42d90..412cc654 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -45,11 +45,15 @@ type Options struct { // KeyManager is the KMS used to generate keys in SoftCAS. KeyManager kms.KeyManager `json:"-"` - // Project and Location are parameters used in CloudCAS to create a new - // certificate authority. - Project string `json:"-"` - Location string `json:"-"` - CaPool string `json:"-"` + // Project, Location, CaPool and GCSBucket are parameters used in CloudCAS + // to create a new certificate authority. If a CaPool does not exists it + // will be created. GCSBucket is optional, if not provided GCloud will + // create a managed bucket. + Project string `json:"-"` + Location string `json:"-"` + CaPool string `json:"-"` + CaPoolTier string `json:"-"` + GCSBucket string `json:"-"` } // CertificateIssuer contains the properties used to use the StepCAS certificate diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index a4e1552c..b5fb309c 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "crypto/x509" "encoding/asn1" + "encoding/json" "encoding/pem" "regexp" "strings" @@ -67,6 +68,13 @@ var revocationCodeMap = map[int]pb.RevocationReason{ 10: pb.RevocationReason_ATTRIBUTE_AUTHORITY_COMPROMISE, } +// caPoolTierMap contains the map between apv1.Options.Tier and the pb type. +var caPoolTierMap = map[string]pb.CaPool_Tier{ + "": pb.CaPool_DEVOPS, + "ENTERPRISE": pb.CaPool_ENTERPRISE, + "DEVOPS": pb.CaPool_DEVOPS, +} + // CloudCAS implements a Certificate Authority Service using Google Cloud CAS. type CloudCAS struct { client CertificateAuthorityClient @@ -74,6 +82,7 @@ type CloudCAS struct { project string location string caPool string + caPoolTier pb.CaPool_Tier gcsBucket string } @@ -94,7 +103,8 @@ var newCertificateAuthorityClient = func(ctx context.Context, credentialsFile st // New creates a new CertificateAuthorityService implementation using Google // Cloud CAS. func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { - if opts.IsCreator { + var caPoolTier pb.CaPool_Tier + if opts.IsCreator && opts.CertificateAuthority == "" { switch { case opts.Project == "": return nil, errors.New("cloudCAS 'project' cannot be empty") @@ -103,7 +113,10 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { case opts.CaPool == "": return nil, errors.New("cloudCAS 'caPool' cannot be empty") } - + var ok bool + if caPoolTier, ok = caPoolTierMap[strings.ToUpper(opts.CaPoolTier)]; !ok { + return nil, errors.New("cloudCAS 'caPoolTier' is not a valid tier") + } } else { if opts.CertificateAuthority == "" { return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty") @@ -130,12 +143,15 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) { return nil, err } + // GCSBucket is the the bucket name or empty for a managed bucket. return &CloudCAS{ client: client, certificateAuthority: opts.CertificateAuthority, project: opts.Project, location: opts.Location, caPool: opts.CaPool, + gcsBucket: opts.GCSBucket, + caPoolTier: caPoolTier, }, nil } @@ -267,6 +283,8 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor return nil, errors.New("cloudCAS `location` cannot be empty") case c.caPool == "": return nil, errors.New("cloudCAS `caPool` cannot be empty") + case c.caPoolTier == 0: + return nil, errors.New("cloudCAS `caPoolTier` cannot be empty") case req.Template == nil: return nil, errors.New("createCertificateAuthorityRequest `template` cannot be nil") case req.Lifetime == 0: @@ -362,14 +380,6 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor return nil, errors.Wrap(err, "cloudCAS CreateCertificateAuthority failed") } - // Enable root certificate - if req.Type == apiv1.RootCA { - ca, err = c.enableCertificateAuthority(ca) - if err != nil { - return nil, err - } - } - // Sign Intermediate CAs with the parent. if req.Type == apiv1.IntermediateCA { ca, err = c.signIntermediateCA(parent, ca.Name, req) @@ -378,6 +388,12 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor } } + // Enable Certificate Authority. + ca, err = c.enableCertificateAuthority(ca) + if err != nil { + return nil, err + } + if len(ca.PemCaCertificates) == 0 { return nil, errors.New("cloudCAS CreateCertificateAuthority failed: PemCaCertificates is empty") } @@ -397,6 +413,9 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor } } + b, _ := json.MarshalIndent(ca, "", "\t") + println(string(b)) + return &apiv1.CreateCertificateAuthorityResponse{ Name: ca.Name, Certificate: cert, @@ -419,6 +438,12 @@ func (c *CloudCAS) createCaPoolIfNecessary() (string, error) { return "", errors.Wrap(err, "cloudCAS GetCaPool failed") } + // PublishCrl is only supported by the enterprise tier + var publishCrl bool + if c.caPoolTier == pb.CaPool_ENTERPRISE { + publishCrl = true + } + ctx, cancel = defaultContext() defer cancel() @@ -426,11 +451,11 @@ func (c *CloudCAS) createCaPoolIfNecessary() (string, error) { Parent: "projects/" + c.project + "/locations/" + c.location, CaPoolId: c.caPool, CaPool: &pb.CaPool{ - Tier: pb.CaPool_ENTERPRISE, + Tier: c.caPoolTier, IssuancePolicy: nil, PublishingOptions: &pb.CaPool_PublishingOptions{ PublishCaCert: true, - PublishCrl: true, + PublishCrl: publishCrl, }, }, }) @@ -507,7 +532,8 @@ func (c *CloudCAS) createCertificate(tpl *x509.Certificate, lifetime time.Durati Lifetime: durationpb.New(lifetime), Labels: map[string]string{}, }, - RequestId: requestID, + IssuingCertificateAuthorityId: getResourceName(c.certificateAuthority), + RequestId: requestID, }) if err != nil { return nil, nil, errors.Wrap(err, "cloudCAS CreateCertificate failed") @@ -583,7 +609,8 @@ func (c *CloudCAS) signIntermediateCA(parent, name string, req *apiv1.CreateCert Lifetime: durationpb.New(req.Lifetime), Labels: map[string]string{}, }, - RequestId: req.RequestID, + IssuingCertificateAuthorityId: getResourceName(req.Parent.Name), + RequestId: req.RequestID, }) if err != nil { return nil, errors.Wrap(err, "cloudCAS CreateCertificate failed") @@ -618,12 +645,6 @@ func (c *CloudCAS) signIntermediateCA(parent, name string, req *apiv1.CreateCert return nil, errors.Wrap(err, "cloudCAS ActivateCertificateAuthority failed") } - // Enable certificate authority - ca, err = c.enableCertificateAuthority(ca) - if err != nil { - return nil, err - } - return ca, nil } @@ -690,7 +711,12 @@ func getCertificateAndChain(certpb *pb.Certificate) (*x509.Certificate, []*x509. } return cert, chain, nil +} +// getResourceName returns the last part of a resource. +func getResourceName(name string) string { + parts := strings.Split(name, "/") + return parts[len(parts)-1] } // Normalize a certificate authority name to comply with [a-zA-Z0-9-_]. diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index 6fb5e520..0561000c 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -279,6 +279,17 @@ func TestNew(t *testing.T) { project: testProject, location: testLocation, caPool: testCaPool, + caPoolTier: 0, + }, false}, + {"ok authority and creator", args{context.Background(), apiv1.Options{ + CertificateAuthority: testAuthorityName, IsCreator: true, + }}, &CloudCAS{ + client: &testClient{}, + certificateAuthority: testAuthorityName, + project: testProject, + location: testLocation, + caPool: testCaPool, + caPoolTier: 0, }, false}, {"ok with credentials", args{context.Background(), apiv1.Options{ CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json", @@ -288,14 +299,34 @@ func TestNew(t *testing.T) { project: testProject, location: testLocation, caPool: testCaPool, + caPoolTier: 0, }, false}, {"ok creator", args{context.Background(), apiv1.Options{ IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, }}, &CloudCAS{ - client: &testClient{}, - project: testProject, - location: testLocation, - caPool: testCaPool, + client: &testClient{}, + project: testProject, + location: testLocation, + caPool: testCaPool, + caPoolTier: pb.CaPool_DEVOPS, + }, false}, + {"ok creator devops", args{context.Background(), apiv1.Options{ + IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, CaPoolTier: "DevOps", + }}, &CloudCAS{ + client: &testClient{}, + project: testProject, + location: testLocation, + caPool: testCaPool, + caPoolTier: pb.CaPool_DEVOPS, + }, false}, + {"ok creator enterprise", args{context.Background(), apiv1.Options{ + IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, CaPoolTier: "ENTERPRISE", + }}, &CloudCAS{ + client: &testClient{}, + project: testProject, + location: testLocation, + caPool: testCaPool, + caPoolTier: pb.CaPool_ENTERPRISE, }, false}, {"fail certificate authority", args{context.Background(), apiv1.Options{ CertificateAuthority: "projects/ok1234/locations/ok1234/caPools/ok1234/certificateAuthorities/ok1234/bad", @@ -1215,6 +1246,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { project string location string caPool string + caPoolTier pb.CaPool_Tier } type args struct { req *apiv1.CreateCertificateAuthorityRequest @@ -1226,7 +1258,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { want *apiv1.CreateCertificateAuthorityResponse wantErr bool }{ - {"ok root", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok root", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_ENTERPRISE}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1234,7 +1266,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Name: testAuthorityName, Certificate: rootCrt, }, false}, - {"ok intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1247,7 +1279,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: intCrt, CertificateChain: []*x509.Certificate{rootCrt}, }, false}, - {"ok intermediate local signer", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok intermediate local signer", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_ENTERPRISE}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1260,7 +1292,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: intCrt, CertificateChain: []*x509.Certificate{rootCrt}, }, false}, - {"ok create key", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"ok create key", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1271,46 +1303,46 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Name: testAuthorityName, Certificate: rootCrt, }, false}, - {"fail project", fields{m, "", "", testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail project", fields{m, "", "", testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail location", fields{m, "", testProject, "", testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail location", fields{m, "", testProject, "", testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail caPool", fields{m, "", testProject, testLocation, ""}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail caPool", fields{m, "", testProject, testLocation, "", pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail template", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail template", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail lifetime", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail lifetime", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), }}, nil, true}, - {"fail parent", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parent", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail parent name", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parent name", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, Parent: &apiv1.CreateCertificateAuthorityResponse{}, }}, nil, true}, - {"fail type", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail type", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: 0, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail create key", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail create key", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, @@ -1318,43 +1350,43 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { SignatureAlgorithm: kmsapi.PureEd25519, }, }}, nil, true}, - {"fail GetCaPool", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail GetCaPool", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail CreateCaPool", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCaPool", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail CreateCaPool.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCaPool.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail CreateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail CreateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail EnableCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail EnableCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail EnableCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail EnableCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Template: mustParseCertificate(t, testRootCertificate), Lifetime: 24 * time.Hour, }}, nil, true}, - {"fail EnableCertificateAuthority intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail EnableCertificateAuthority intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1363,7 +1395,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail EnableCertificateAuthority.Wait intermediate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail EnableCertificateAuthority.Wait intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1373,7 +1405,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { }, }}, nil, true}, - {"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1382,7 +1414,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail CreateCertificate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail CreateCertificate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1391,7 +1423,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail ActivateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail ActivateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1400,7 +1432,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail ActivateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail ActivateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1409,7 +1441,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Certificate: rootCrt, }, }}, nil, true}, - {"fail x509util.CreateCertificate", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail x509util.CreateCertificate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1418,7 +1450,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { Signer: createBadSigner(t), }, }}, nil, true}, - {"fail parseCertificateRequest", fields{m, "", testProject, testLocation, testCaPool}, args{&apiv1.CreateCertificateAuthorityRequest{ + {"fail parseCertificateRequest", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.IntermediateCA, Template: mustParseCertificate(t, testIntermediateCertificate), Lifetime: 24 * time.Hour, @@ -1436,6 +1468,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { project: tt.fields.project, location: tt.fields.location, caPool: tt.fields.caPool, + caPoolTier: tt.fields.caPoolTier, } got, err := c.CreateCertificateAuthority(tt.args.req) if (err != nil) != tt.wantErr { From 2a97389f1b529401fb9ae4a042e05900f79835eb Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 8 Jun 2021 17:47:26 -0700 Subject: [PATCH 082/291] Upgrade dependencies. --- go.mod | 6 +- go.sum | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 208 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 9fc6bb3a..aa52e663 100644 --- a/go.mod +++ b/go.mod @@ -26,10 +26,10 @@ require ( go.step.sm/cli-utils v0.2.0 go.step.sm/crypto v0.8.3 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 - golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect + golang.org/x/net v0.0.0-20210525063256-abc453219eb5 + golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect google.golang.org/api v0.47.0 - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c + google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d google.golang.org/grpc v1.38.0 google.golang.org/protobuf v1.26.0 gopkg.in/square/go-jose.v2 v2.5.1 diff --git a/go.sum b/go.sum index 0752f40d..77fff75c 100644 --- a/go.sum +++ b/go.sum @@ -25,26 +25,34 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= 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 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -54,38 +62,59 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/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= @@ -93,23 +122,34 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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= @@ -121,6 +161,7 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -128,9 +169,13 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -138,17 +183,26 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -167,10 +221,13 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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= @@ -210,6 +267,7 @@ github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -224,10 +282,13 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -240,7 +301,9 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22 h1:ub2sxhs2A0HRa2dWHavvmWxiVGXNfE9wI+gcTMwED8A= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -249,79 +312,124 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 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 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= 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 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -333,89 +441,132 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/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 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.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 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 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 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -423,10 +574,13 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -438,27 +592,39 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= 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= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -468,23 +634,29 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/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 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= 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= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= github.com/yuin/goldmark v1.3.5/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= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -502,11 +674,15 @@ go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -530,8 +706,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -544,8 +722,10 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -555,6 +735,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -599,8 +780,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= @@ -624,6 +806,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -681,8 +864,8 @@ 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-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8= +golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -697,6 +880,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -751,6 +935,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/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 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= @@ -831,8 +1016,9 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d h1:KzwjikDymrEmYYbdyfievTwjEeGlu+OM6oiKBkF3Jfg= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -860,6 +1046,7 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -874,19 +1061,27 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -902,9 +1097,15 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From dce1b290bd38793a93af963681c2643658d77d26 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 8 Jun 2021 17:57:24 -0700 Subject: [PATCH 083/291] Remove debug statements. --- cas/cloudcas/cloudcas.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index b5fb309c..1be47694 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -5,7 +5,6 @@ import ( "crypto/rand" "crypto/x509" "encoding/asn1" - "encoding/json" "encoding/pem" "regexp" "strings" @@ -413,9 +412,6 @@ func (c *CloudCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthor } } - b, _ := json.MarshalIndent(ca, "", "\t") - println(string(b)) - return &apiv1.CreateCertificateAuthorityResponse{ Name: ca.Name, Certificate: cert, From 16e0cffd8b675751a5433774c161d7bbfa15e00c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 8 Jun 2021 18:02:54 -0700 Subject: [PATCH 084/291] Fix path for labeler. --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index f0011406..72b01a92 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -11,4 +11,4 @@ jobs: - uses: actions/labeler@v3 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/needs-triage-labeler.yml + configuration-path: .github/labeler.yml From 84ea8bd67aa18909ff6d78b7b46eb926cb584076 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 12:03:46 +0200 Subject: [PATCH 085/291] Fix PR comments --- acme/api/order_test.go | 18 +++++++++++++++++- acme/order.go | 17 ++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 6782de75..a0096419 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -45,6 +45,22 @@ func TestNewOrderRequest_Validate(t *testing.T) { err: acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: foo"), } }, + "fail/bad-ip": func(t *testing.T) test { + nbf := time.Now().UTC().Add(time.Minute) + naf := time.Now().UTC().Add(5 * time.Minute) + return test{ + nor: &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "ip", Value: "192.168.42.1000"}, + }, + NotAfter: naf, + NotBefore: nbf, + }, + nbf: nbf, + naf: naf, + err: acme.NewError(acme.ErrorMalformedType, "invalid IP address: %s", "192.168.42.1000"), + } + }, "ok": func(t *testing.T) test { nbf := time.Now().UTC().Add(time.Minute) naf := time.Now().UTC().Add(5 * time.Minute) @@ -91,7 +107,7 @@ func TestNewOrderRequest_Validate(t *testing.T) { naf: naf, } }, - "ok/mixed-dns-and-ipv4": func(t *testing.T) test { // TODO: verify that this is allowed and what we want to be possible (in Validate()) + "ok/mixed-dns-and-ipv4": func(t *testing.T) test { nbf := time.Now().UTC().Add(time.Minute) naf := time.Now().UTC().Add(5 * time.Minute) return test{ diff --git a/acme/order.go b/acme/order.go index 73d5e636..86b3c43a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -14,10 +14,17 @@ import ( "go.step.sm/crypto/x509util" ) +type IdentifierType string + +const ( + IP IdentifierType = "ip" + DNS IdentifierType = "dns" +) + // Identifier encodes the type that an order pertains to. type Identifier struct { - Type string `json:"type"` - Value string `json:"value"` + Type IdentifierType `json:"type"` + Value string `json:"value"` } // Order contains order metadata for the ACME protocol order type. @@ -222,7 +229,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ // Validate identifier names against CSR alternative names. // // Note that with certificate templates we are not going to check for the - // absence of other SANs as they will only be set if the templates allows + // absence of other SANs as they will only be set if the template allows // them. if len(csr.DNSNames) != len(orderNames) { return sans, NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ @@ -263,7 +270,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ // numberOfIdentifierType returns the number of Identifiers that // are of type typ. -func numberOfIdentifierType(typ string, ids []Identifier) int { +func numberOfIdentifierType(typ IdentifierType, ids []Identifier) int { c := 0 for _, id := range ids { if id.Type == typ { @@ -305,7 +312,7 @@ func ipsAreEqual(x, y net.IP) bool { return false } -// matchAddrFamily returns if two IPs are both IPv4 OR IPv6 +// matchAddrFamily returns true if two IPs are both IPv4 OR IPv6 // Implementation taken and adapted from https://golang.org/src/net/ip.go func matchAddrFamily(x net.IP, y net.IP) bool { return x.To4() != nil && y.To4() != nil || x.To16() != nil && x.To4() == nil && y.To16() != nil && y.To4() == nil From 523ae96749cdd187d6228646d6d7f6397da4a0da Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 12:39:36 +0200 Subject: [PATCH 086/291] Change identifier and challenge types to consts --- acme/api/handler_test.go | 10 +++--- acme/api/order.go | 20 ++++++------ acme/api/order_test.go | 66 +++++++++++++++++++------------------- acme/challenge.go | 34 ++++++++++++-------- acme/db/nosql/challenge.go | 18 +++++------ 5 files changed, 78 insertions(+), 70 deletions(-) diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index 5501479d..f354bbac 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -574,13 +574,13 @@ func TestHandler_GetChallenge(t *testing.T) { assert.Equals(t, azID, "authzID") return &acme.Challenge{ Status: acme.StatusPending, - Type: "http-01", + Type: acme.HTTP01, AccountID: "accID", }, nil }, MockUpdateChallenge: func(ctx context.Context, ch *acme.Challenge) error { assert.Equals(t, ch.Status, acme.StatusPending) - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) assert.Equals(t, ch.AccountID, "accID") assert.Equals(t, ch.AuthorizationID, "authzID") assert.HasSuffix(t, ch.Error.Type, acme.ErrorConnectionType.String()) @@ -616,13 +616,13 @@ func TestHandler_GetChallenge(t *testing.T) { return &acme.Challenge{ ID: "chID", Status: acme.StatusPending, - Type: "http-01", + Type: acme.HTTP01, AccountID: "accID", }, nil }, MockUpdateChallenge: func(ctx context.Context, ch *acme.Challenge) error { assert.Equals(t, ch.Status, acme.StatusPending) - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) assert.Equals(t, ch.AccountID, "accID") assert.Equals(t, ch.AuthorizationID, "authzID") assert.HasSuffix(t, ch.Error.Type, acme.ErrorConnectionType.String()) @@ -633,7 +633,7 @@ func TestHandler_GetChallenge(t *testing.T) { ID: "chID", Status: acme.StatusPending, AuthorizationID: "authzID", - Type: "http-01", + Type: acme.HTTP01, AccountID: "accID", URL: url, Error: acme.NewError(acme.ErrorConnectionType, "force"), diff --git a/acme/api/order.go b/acme/api/order.go index 936e9573..9cf2c1eb 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -29,10 +29,10 @@ func (n *NewOrderRequest) Validate() error { return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty") } for _, id := range n.Identifiers { - if !(id.Type == "dns" || id.Type == "ip") { + if !(id.Type == acme.DNS || id.Type == acme.IP) { return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type) } - if id.Type == "ip" && net.ParseIP(id.Value) == nil { + if id.Type == acme.IP && net.ParseIP(id.Value) == nil { return acme.NewError(acme.ErrorMalformedType, "invalid IP address: %s", id.Value) } } @@ -277,20 +277,20 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) { // challengeTypes determines the types of challenges that should be used // for the ACME authorization request. -func challengeTypes(az *acme.Authorization) []string { - var chTypes []string +func challengeTypes(az *acme.Authorization) []acme.ChallengeType { + var chTypes []acme.ChallengeType switch az.Identifier.Type { - case "ip": // TODO: make these types consts/enum? - chTypes = []string{"http-01", "tls-alpn-01"} - case "dns": - chTypes = []string{"dns-01"} + case acme.IP: + chTypes = []acme.ChallengeType{acme.HTTP01, acme.TLSALPN01} + case acme.DNS: + chTypes = []acme.ChallengeType{acme.DNS01} // HTTP and TLS challenges can only be used for identifiers without wildcards. if !az.Wildcard { - chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...) + chTypes = append(chTypes, []acme.ChallengeType{acme.HTTP01, acme.TLSALPN01}...) } default: - chTypes = []string{} + chTypes = []acme.ChallengeType{} } return chTypes diff --git a/acme/api/order_test.go b/acme/api/order_test.go index a0096419..38c4c3f0 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -474,7 +474,7 @@ func TestHandler_newAuthorization(t *testing.T) { db: &acme.MockDB{ MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error { assert.Equals(t, ch.AccountID, az.AccountID) - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) assert.Equals(t, ch.Token, az.Token) assert.Equals(t, ch.Status, acme.StatusPending) assert.Equals(t, ch.Value, az.Identifier.Value) @@ -503,15 +503,15 @@ func TestHandler_newAuthorization(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -557,15 +557,15 @@ func TestHandler_newAuthorization(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -607,7 +607,7 @@ func TestHandler_newAuthorization(t *testing.T) { db: &acme.MockDB{ MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error { ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) assert.Equals(t, ch.AccountID, az.AccountID) assert.Equals(t, ch.Token, az.Token) assert.Equals(t, ch.Status, acme.StatusPending) @@ -774,7 +774,7 @@ func TestHandler_NewOrder(t *testing.T) { db: &acme.MockDB{ MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error { assert.Equals(t, ch.AccountID, "accID") - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) assert.NotEquals(t, ch.Token, "") assert.Equals(t, ch.Status, acme.StatusPending) assert.Equals(t, ch.Value, "zap.internal") @@ -809,15 +809,15 @@ func TestHandler_NewOrder(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -881,22 +881,22 @@ func TestHandler_NewOrder(t *testing.T) { switch chCount { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) assert.Equals(t, ch.Value, "zap.internal") ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) assert.Equals(t, ch.Value, "zap.internal") ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) assert.Equals(t, ch.Value, "zap.internal") ch3 = &ch case 3: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) assert.Equals(t, ch.Value, "zar.internal") ch4 = &ch default: @@ -921,7 +921,7 @@ func TestHandler_NewOrder(t *testing.T) { az.ID = "az2ID" az2ID = &az.ID assert.Equals(t, az.Identifier, acme.Identifier{ - Type: "dns", + Type: acme.DNS, Value: "zar.internal", }) assert.Equals(t, az.Wildcard, true) @@ -996,15 +996,15 @@ func TestHandler_NewOrder(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -1088,15 +1088,15 @@ func TestHandler_NewOrder(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -1179,15 +1179,15 @@ func TestHandler_NewOrder(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -1271,15 +1271,15 @@ func TestHandler_NewOrder(t *testing.T) { switch count { case 0: ch.ID = "dns" - assert.Equals(t, ch.Type, "dns-01") + assert.Equals(t, ch.Type, acme.DNS01) ch1 = &ch case 1: ch.ID = "http" - assert.Equals(t, ch.Type, "http-01") + assert.Equals(t, ch.Type, acme.HTTP01) ch2 = &ch case 2: ch.ID = "tls" - assert.Equals(t, ch.Type, "tls-alpn-01") + assert.Equals(t, ch.Type, acme.TLSALPN01) ch3 = &ch default: assert.FatalError(t, errors.New("test logic error")) @@ -1668,7 +1668,7 @@ func TestHandler_challengeTypes(t *testing.T) { tests := []struct { name string args args - want []string + want []acme.ChallengeType }{ { name: "ok/dns", @@ -1678,7 +1678,7 @@ func TestHandler_challengeTypes(t *testing.T) { Wildcard: false, }, }, - want: []string{"dns-01", "http-01", "tls-alpn-01"}, + want: []acme.ChallengeType{acme.DNS01, acme.HTTP01, acme.TLSALPN01}, //[]string{"dns-01", "http-01", "tls-alpn-01"}, }, { name: "ok/wildcard", @@ -1688,7 +1688,7 @@ func TestHandler_challengeTypes(t *testing.T) { Wildcard: true, }, }, - want: []string{"dns-01"}, + want: []acme.ChallengeType{acme.DNS01}, }, { name: "ok/ip", @@ -1698,7 +1698,7 @@ func TestHandler_challengeTypes(t *testing.T) { Wildcard: false, }, }, - want: []string{"http-01", "tls-alpn-01"}, + want: []acme.ChallengeType{acme.HTTP01, acme.TLSALPN01}, }, } for _, tt := range tests { diff --git a/acme/challenge.go b/acme/challenge.go index cb15a188..706d12a1 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -21,18 +21,26 @@ import ( "go.step.sm/crypto/jose" ) +type ChallengeType string + +const ( + HTTP01 ChallengeType = "http-01" + DNS01 ChallengeType = "dns-01" + TLSALPN01 ChallengeType = "tls-alpn-01" +) + // Challenge represents an ACME response Challenge type. type Challenge struct { - ID string `json:"-"` - AccountID string `json:"-"` - AuthorizationID string `json:"-"` - Value string `json:"-"` - Type string `json:"type"` - Status Status `json:"status"` - Token string `json:"token"` - ValidatedAt string `json:"validated,omitempty"` - URL string `json:"url"` - Error *Error `json:"error,omitempty"` + ID string `json:"-"` + AccountID string `json:"-"` + AuthorizationID string `json:"-"` + Value string `json:"-"` + Type ChallengeType `json:"type"` + Status Status `json:"status"` + Token string `json:"token"` + ValidatedAt string `json:"validated,omitempty"` + URL string `json:"url"` + Error *Error `json:"error,omitempty"` } // ToLog enables response logging. @@ -54,11 +62,11 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, return nil } switch ch.Type { - case "http-01": + case HTTP01: return http01Validate(ctx, ch, db, jwk, vo) - case "dns-01": + case DNS01: return dns01Validate(ctx, ch, db, jwk, vo) - case "tls-alpn-01": + case TLSALPN01: return tlsalpn01Validate(ctx, ch, db, jwk, vo) default: return NewErrorISE("unexpected challenge type '%s'", ch.Type) diff --git a/acme/db/nosql/challenge.go b/acme/db/nosql/challenge.go index f3a3cfca..f84a6f4e 100644 --- a/acme/db/nosql/challenge.go +++ b/acme/db/nosql/challenge.go @@ -11,15 +11,15 @@ import ( ) type dbChallenge struct { - ID string `json:"id"` - AccountID string `json:"accountID"` - Type string `json:"type"` - Status acme.Status `json:"status"` - Token string `json:"token"` - Value string `json:"value"` - ValidatedAt string `json:"validatedAt"` - CreatedAt time.Time `json:"createdAt"` - Error *acme.Error `json:"error"` + ID string `json:"id"` + AccountID string `json:"accountID"` + Type acme.ChallengeType `json:"type"` + Status acme.Status `json:"status"` + Token string `json:"token"` + Value string `json:"value"` + ValidatedAt string `json:"validatedAt"` + CreatedAt time.Time `json:"createdAt"` + Error *acme.Error `json:"error"` } func (dbc *dbChallenge) clone() *dbChallenge { From f33bdee5e06770fd4b1c1aae0f856789444f1a39 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 12:55:50 +0200 Subject: [PATCH 087/291] Fix linter issue S1025 --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 6a0a7e8f..5cf9f593 100644 --- a/api/api.go +++ b/api/api.go @@ -417,7 +417,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if len(val.CredentialID) > 0 { m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) } else { - m["provisioner"] = fmt.Sprintf("%s", val.Name) + m["provisioner"] = string(val.Name) } break } From db416a45aea466a8c7fde7fae4ef71c23e3d4c71 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 8 Jun 2021 18:02:54 -0700 Subject: [PATCH 088/291] Fix path for labeler. --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index f0011406..72b01a92 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -11,4 +11,4 @@ jobs: - uses: actions/labeler@v3 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/needs-triage-labeler.yml + configuration-path: .github/labeler.yml From 218a2adb9fc5778678fff7b323ee4943e2d4d7a7 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 16:09:48 +0200 Subject: [PATCH 089/291] Add tests for IP Order validations --- acme/order.go | 12 +- acme/order_test.go | 375 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 380 insertions(+), 7 deletions(-) diff --git a/acme/order.go b/acme/order.go index 86b3c43a..71884603 100644 --- a/acme/order.go +++ b/acme/order.go @@ -199,15 +199,15 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ var sans []x509util.SubjectAlternativeName // order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR - orderNames := make([]string, numberOfIdentifierType("dns", o.Identifiers)) - orderIPs := make([]net.IP, numberOfIdentifierType("ip", o.Identifiers)) + orderNames := make([]string, numberOfIdentifierType(DNS, o.Identifiers)) + orderIPs := make([]net.IP, numberOfIdentifierType(IP, o.Identifiers)) indexDNS, indexIP := 0, 0 for _, n := range o.Identifiers { switch n.Type { - case "dns": + case DNS: orderNames[indexDNS] = n.Value indexDNS++ - case "ip": + case IP: orderIPs[indexIP] = net.ParseIP(n.Value) // NOTE: this assumes are all valid IPs at this time; or will result in nil entries indexIP++ default: @@ -303,8 +303,8 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate } // ipsAreEqual compares IPs to be equal. IPv6 representations of IPv4 -// adresses are NOT considered equal to the IPv4 address in this case. -// Both IPs should be the same version AND equal to each other. +// adresses are considered equal to the IPv4 address in this case. +// TODO: is this behavior OK to keep? func ipsAreEqual(x, y net.IP) bool { if matchAddrFamily(x, y) { return x.Equal(y) diff --git a/acme/order_test.go b/acme/order_test.go index c0461dc6..e1d3744b 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -393,6 +393,31 @@ func TestOrder_Finalize(t *testing.T) { "CSR names = %v, Order names = %v", []string{"foo.internal"}, orderNames), } }, + "fail/error-ips-length-mismatch": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + } + orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")} + csr := &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, + } + + return test{ + o: o, + csr: csr, + err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.42")}, orderIPs), + } + }, "fail/error-names-mismatch": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -421,6 +446,31 @@ func TestOrder_Finalize(t *testing.T) { "CSR names = %v, Order names = %v", []string{"foo.internal", "zap.internal"}, orderNames), } }, + "fail/error-ips-mismatch": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + } + orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")} + csr := &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.32")}, + } + + return test{ + o: o, + csr: csr, + err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.32"), net.ParseIP("192.168.42.42")}, orderIPs), + } + }, "fail/error-provisioner-auth": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -652,7 +702,7 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("error updating order oID: force"), } }, - "ok/new-cert": func(t *testing.T) test { + "ok/new-cert-dns": func(t *testing.T) test { now := clock.Now() o := &Order{ ID: "oID", @@ -676,6 +726,131 @@ func TestOrder_Finalize(t *testing.T) { bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}} baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}} + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{foo, bar, baz}, nil + }, + }, + db: &MockDB{ + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, foo) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{bar, baz}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, + "ok/new-cert-ip": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + } + csr := &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}, // in case of IPs, no Common Name + } + + foo := &x509.Certificate{Subject: pkix.Name{CommonName: "foo"}} + bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}} + baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{foo, bar, baz}, nil + }, + }, + db: &MockDB{ + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, foo) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{bar, baz}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, + "ok/new-cert-dns-and-ip": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "ip", Value: "192.168.42.42"}, + }, + } + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "foo.internal", + }, + IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, + } + + foo := &x509.Certificate{Subject: pkix.Name{CommonName: "foo"}} + bar := &x509.Certificate{Subject: pkix.Name{CommonName: "bar"}} + baz := &x509.Certificate{Subject: pkix.Name{CommonName: "baz"}} + return test{ o: o, csr: csr, @@ -814,3 +989,201 @@ func Test_uniqueSortedIPs(t *testing.T) { }) } } + +func Test_numberOfIdentifierType(t *testing.T) { + type args struct { + typ IdentifierType + ids []Identifier + } + tests := []struct { + name string + args args + want int + }{ + { + name: "ok/no-identifiers", + args: args{ + typ: DNS, + ids: []Identifier{}, + }, + want: 0, + }, + { + name: "ok/no-dns", + args: args{ + typ: DNS, + ids: []Identifier{ + { + Type: IP, + Value: "192.168.42.42", + }, + }, + }, + want: 0, + }, + { + name: "ok/no-ips", + args: args{ + typ: IP, + ids: []Identifier{ + { + Type: DNS, + Value: "example.com", + }, + }, + }, + want: 0, + }, + { + name: "ok/one-dns", + args: args{ + typ: DNS, + ids: []Identifier{ + { + Type: DNS, + Value: "example.com", + }, + { + Type: IP, + Value: "192.168.42.42", + }, + }, + }, + want: 1, + }, + { + name: "ok/one-ip", + args: args{ + typ: IP, + ids: []Identifier{ + { + Type: DNS, + Value: "example.com", + }, + { + Type: IP, + Value: "192.168.42.42", + }, + }, + }, + want: 1, + }, + { + name: "ok/more-dns", + args: args{ + typ: DNS, + ids: []Identifier{ + { + Type: DNS, + Value: "example.com", + }, + { + Type: DNS, + Value: "*.example.com", + }, + { + Type: IP, + Value: "192.168.42.42", + }, + }, + }, + want: 2, + }, + { + name: "ok/more-ips", + args: args{ + typ: IP, + ids: []Identifier{ + { + Type: DNS, + Value: "example.com", + }, + { + Type: IP, + Value: "192.168.42.42", + }, + { + Type: IP, + Value: "192.168.42.43", + }, + }, + }, + want: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := numberOfIdentifierType(tt.args.typ, tt.args.ids); got != tt.want { + t.Errorf("numberOfIdentifierType() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_ipsAreEqual(t *testing.T) { + type args struct { + x net.IP + y net.IP + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "ok/ipv4", + args: args{ + x: net.ParseIP("192.168.42.42"), + y: net.ParseIP("192.168.42.42"), + }, + want: true, + }, + { + name: "ok/ipv6", + args: args{ + x: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + }, + want: true, + }, + { + name: "ok/ipv4-and-ipv6", + args: args{ + x: net.ParseIP("192.168.42.42"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + }, + want: false, + }, + { + name: "ok/ipv4-mapped-to-ipv6", + args: args{ + x: net.ParseIP("192.168.42.42"), + y: net.ParseIP("::ffff:192.168.42.42"), // parsed to the same IPv4 by Go + }, + want: true, // TODO: is this behavior OK? + }, + { + name: "ok/invalid-ipv4-and-valid-ipv6", + args: args{ + x: net.ParseIP("192.168.42.1000"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + }, + want: false, + }, + { + name: "ok/invalid-ipv4-and-invalid-ipv6", + args: args{ + x: net.ParseIP("192.168.42.1000"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:1000000"), + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ipsAreEqual(tt.args.x, tt.args.y); got != tt.want { + t.Errorf("ipsAreEqual() = %v, want %v", got, tt.want) + } + }) + } +} From 135e912ac8fe118fe1cf22dd49777b2813b9e7ba Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 17:27:35 +0200 Subject: [PATCH 090/291] Improve coverage for TLS-ALPN-01 challenge --- acme/challenge.go | 31 ++++++----- acme/challenge_test.go | 117 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 14 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 706d12a1..fe643c85 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -115,25 +115,13 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb } func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { - - // RFC8738 states that, if HostName is IP, it should be the ARPA - // address https://datatracker.ietf.org/doc/html/rfc8738#section-6. - // It also references TLS Extensions [RFC6066]. - var serverName string - ip := net.ParseIP(ch.Value) - if ip != nil { - serverName = reverseAddr(ip) - } else { - serverName = ch.Value - } - config := &tls.Config{ NextProtos: []string{"acme-tls/1"}, // https://tools.ietf.org/html/rfc8737#section-4 // ACME servers that implement "acme-tls/1" MUST only negotiate TLS 1.2 // [RFC5246] or higher when connecting to clients for validation. MinVersion: tls.VersionTLS12, - ServerName: serverName, + ServerName: serverName(ch), InsecureSkipVerify: true, // we expect a self-signed challenge certificate } @@ -163,7 +151,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON // if no DNS names present, look for IP address and verify that exactly one exists if len(leafCert.DNSNames) == 0 { - if len(leafCert.IPAddresses) != 1 || !leafCert.IPAddresses[0].Equal(ip) { + if len(leafCert.IPAddresses) != 1 || !leafCert.IPAddresses[0].Equal(net.ParseIP(ch.Value)) { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value)) } @@ -272,6 +260,21 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK return nil } +// serverName determines the SNI HostName to set based on an acme.Challenge +// for TLS-ALPN-01 challenges. RFC8738 states that, if HostName is an IP, it +// should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6. +// It also references TLS Extensions [RFC6066]. +func serverName(ch *Challenge) string { + var serverName string + ip := net.ParseIP(ch.Value) + if ip != nil { + serverName = reverseAddr(ip) + } else { + serverName = ch.Value + } + return serverName +} + // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP // address addr suitable for rDNS (PTR) record lookup or an error if it fails // to parse the IP address. diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 8e03a414..727e9ef3 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -2187,6 +2187,43 @@ func TestTLSALPN01Validate(t *testing.T) { srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() + return test{ + ch: ch, + vo: &ValidateChallengeOptions{ + TLSDial: tlsDial, + }, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equals(t, updch.ID, ch.ID) + assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Status, StatusValid) + assert.Equals(t, updch.Type, ch.Type) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Error, nil) + return nil + }, + }, + srv: srv, + jwk: jwk, + } + }, + "ok/ip": func(t *testing.T) test { + ch := makeTLSCh() + ch.Value = "127.0.0.1" + + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + assert.FatalError(t, err) + + expKeyAuth, err := KeyAuthorization(ch.Token, jwk) + assert.FatalError(t, err) + expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) + + cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) + assert.FatalError(t, err) + + srv, tlsDial := newTestTLSALPNServer(cert) + srv.Start() + return test{ ch: ch, vo: &ValidateChallengeOptions{ @@ -2234,4 +2271,84 @@ func TestTLSALPN01Validate(t *testing.T) { } }) } + t.Fail() +} + +func Test_reverseAddr(t *testing.T) { + type args struct { + ip net.IP + } + tests := []struct { + name string + args args + wantArpa string + }{ + { + name: "ok/ipv4", + args: args{ + ip: net.ParseIP("127.0.0.1"), + }, + wantArpa: "1.0.0.127.in-addr.arpa.", + }, + { + name: "ok/ipv6", + args: args{ + ip: net.ParseIP("2001:db8::567:89ab"), + }, + wantArpa: "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotArpa := reverseAddr(tt.args.ip); gotArpa != tt.wantArpa { + t.Errorf("reverseAddr() = %v, want %v", gotArpa, tt.wantArpa) + } + }) + } +} + +func Test_serverName(t *testing.T) { + type args struct { + ch *Challenge + } + tests := []struct { + name string + args args + want string + }{ + { + name: "ok/dns", + args: args{ + ch: &Challenge{ + Value: "example.com", + }, + }, + want: "example.com", + }, + { + name: "ok/ipv4", + args: args{ + ch: &Challenge{ + Value: "127.0.0.1", + }, + }, + want: "1.0.0.127.in-addr.arpa.", + }, + { + name: "ok/ipv4", + args: args{ + ch: &Challenge{ + Value: "2001:db8::567:89ab", + }, + }, + want: "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := serverName(tt.args.ch); got != tt.want { + t.Errorf("serverName() = %v, want %v", got, tt.want) + } + }) + } } From c514a187b287abb3ad94610c671a42b25f78696c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 18 Jun 2021 17:37:56 +0200 Subject: [PATCH 091/291] Fix Fail() -_-b --- acme/challenge_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 727e9ef3..423951e8 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -2271,7 +2271,6 @@ func TestTLSALPN01Validate(t *testing.T) { } }) } - t.Fail() } func Test_reverseAddr(t *testing.T) { From 35e6cc275aeafffdc7010042f4251821693f4d3a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 23 Jun 2021 09:35:14 +0200 Subject: [PATCH 092/291] Fix typos in comments. --- cas/apiv1/options.go | 6 +++--- cas/cloudcas/cloudcas.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index 412cc654..61cac9a2 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -46,9 +46,9 @@ type Options struct { KeyManager kms.KeyManager `json:"-"` // Project, Location, CaPool and GCSBucket are parameters used in CloudCAS - // to create a new certificate authority. If a CaPool does not exists it - // will be created. GCSBucket is optional, if not provided GCloud will - // create a managed bucket. + // to create a new certificate authority. If a CaPool does not exist it will + // be created. GCSBucket is optional, if not provided GCloud will create a + // managed bucket. Project string `json:"-"` Location string `json:"-"` CaPool string `json:"-"` diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index 1be47694..2e9da260 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -67,7 +67,7 @@ var revocationCodeMap = map[int]pb.RevocationReason{ 10: pb.RevocationReason_ATTRIBUTE_AUTHORITY_COMPROMISE, } -// caPoolTierMap contains the map between apv1.Options.Tier and the pb type. +// caPoolTierMap contains the map between apiv1.Options.Tier and the pb type. var caPoolTierMap = map[string]pb.CaPool_Tier{ "": pb.CaPool_DEVOPS, "ENTERPRISE": pb.CaPool_ENTERPRISE, From 65dacc27952c5676b89685c64c1f269a57cd3b8f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 23 Jun 2021 09:53:26 +0200 Subject: [PATCH 093/291] Replace golint with revive --- .golangci.yml | 4 +- api/api.go | 2 +- go.mod | 5 +- go.sum | 214 +++----------------------------------------------- 4 files changed, 16 insertions(+), 209 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 1bab3ba3..178cba47 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,8 +8,6 @@ linters-settings: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - golint: - min-confidence: 0 gocyclo: min-complexity: 10 maligned: @@ -44,7 +42,7 @@ linters: disable-all: true enable: - gofmt - - golint + - revive - govet - misspell - ineffassign diff --git a/api/api.go b/api/api.go index 6a0a7e8f..9f2ff86f 100644 --- a/api/api.go +++ b/api/api.go @@ -417,7 +417,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if len(val.CredentialID) > 0 { m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) } else { - m["provisioner"] = fmt.Sprintf("%s", val.Name) + m["provisioner"] = val.Name } break } diff --git a/go.mod b/go.mod index aa52e663..20e1be11 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/google/uuid v1.1.2 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.0.0 github.com/newrelic/go-agent v2.15.0+incompatible github.com/pkg/errors v0.9.1 @@ -21,13 +23,14 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/nosql v0.3.6 + github.com/stretchr/testify v1.7.0 // indirect github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.2.0 go.step.sm/crypto v0.8.3 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/net v0.0.0-20210525063256-abc453219eb5 - golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect google.golang.org/api v0.47.0 google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d google.golang.org/grpc v1.38.0 diff --git a/go.sum b/go.sum index 77fff75c..7f591142 100644 --- a/go.sum +++ b/go.sum @@ -25,34 +25,26 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= 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 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -62,59 +54,38 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= -github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/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= @@ -122,34 +93,23 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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= @@ -161,7 +121,6 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -169,13 +128,9 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -183,26 +138,17 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -221,13 +167,10 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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= @@ -267,7 +210,6 @@ github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -282,13 +224,10 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -301,9 +240,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22 h1:ub2sxhs2A0HRa2dWHavvmWxiVGXNfE9wI+gcTMwED8A= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -312,261 +249,176 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 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 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= 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 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/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 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.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 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 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 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -574,13 +426,10 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -592,71 +441,54 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= 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= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= 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= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= github.com/yuin/goldmark v1.3.5/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= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -674,15 +506,11 @@ go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -706,10 +534,8 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -722,10 +548,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -735,7 +559,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -806,7 +629,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -836,6 +658,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w 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-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= @@ -864,8 +687,8 @@ 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-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8= -golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -880,7 +703,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -935,7 +757,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/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 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= @@ -1046,7 +867,6 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1061,27 +881,19 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1097,15 +909,9 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 6476eb45a7e97ac43a37da7ab4893555625de249 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 23 Jun 2021 13:30:30 -0700 Subject: [PATCH 094/291] Need RELEASE variable defined in make debian --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 882a0122..d3c0573f 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ PREFIX?= SRC=$(shell find . -type f -name '*.go' -not -path "./vendor/*") GOOS_OVERRIDE ?= OUTPUT_ROOT=output/ +RELEASE=./.releases all: lint test build From 64c15fde7eb1358546ecf4f9692042ae65226424 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 25 Jun 2021 14:07:40 +0200 Subject: [PATCH 095/291] Add tests for canonicalize function --- acme/api/order_test.go | 3 +- acme/challenge.go | 2 +- acme/challenge_test.go | 2 +- acme/order.go | 3 - acme/order_test.go | 153 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 7 deletions(-) diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 38c4c3f0..afb23c3f 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -1678,7 +1678,7 @@ func TestHandler_challengeTypes(t *testing.T) { Wildcard: false, }, }, - want: []acme.ChallengeType{acme.DNS01, acme.HTTP01, acme.TLSALPN01}, //[]string{"dns-01", "http-01", "tls-alpn-01"}, + want: []acme.ChallengeType{acme.DNS01, acme.HTTP01, acme.TLSALPN01}, }, { name: "ok/wildcard", @@ -1703,7 +1703,6 @@ func TestHandler_challengeTypes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := challengeTypes(tt.args.az); !reflect.DeepEqual(got, tt.want) { t.Errorf("Handler.challengeTypes() = %v, want %v", got, tt.want) } diff --git a/acme/challenge.go b/acme/challenge.go index fe643c85..1d5f0ec9 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -261,7 +261,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK } // serverName determines the SNI HostName to set based on an acme.Challenge -// for TLS-ALPN-01 challenges. RFC8738 states that, if HostName is an IP, it +// for TLS-ALPN-01 challenges RFC8738 states that, if HostName is an IP, it // should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6. // It also references TLS Extensions [RFC6066]. func serverName(ch *Challenge) string { diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 423951e8..bb9a2507 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -2334,7 +2334,7 @@ func Test_serverName(t *testing.T) { want: "1.0.0.127.in-addr.arpa.", }, { - name: "ok/ipv4", + name: "ok/ipv6", args: args{ ch: &Challenge{ Value: "2001:db8::567:89ab", diff --git a/acme/order.go b/acme/order.go index 71884603..2a869e78 100644 --- a/acme/order.go +++ b/acme/order.go @@ -221,9 +221,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ sans = make([]x509util.SubjectAlternativeName, totalNumberOfSANs) index := 0 - // TODO: limit what IP addresses can be used? Only private? Only certain ranges (i.e. only allow the specific ranges by default, configuration for all?) - // TODO: can DNS already be limited to a certain domain? That would probably be nice to have too, but maybe not as part of this PR - // TODO: if it seems not too big of a change, make consts/enums out of the stringly typed identifiers (challenge types, identifier types) // TODO: only allow IP based identifier based on configuration? Some additional configuration and validation on the provisioner for this case. // Validate identifier names against CSR alternative names. diff --git a/acme/order_test.go b/acme/order_test.go index e1d3744b..67d01f6d 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/crypto/x509util" ) func TestOrder_UpdateStatus(t *testing.T) { @@ -1187,3 +1188,155 @@ func Test_ipsAreEqual(t *testing.T) { }) } } + +func Test_canonicalize(t *testing.T) { + type args struct { + csr *x509.CertificateRequest + } + tests := []struct { + name string + args args + wantCanonicalized *x509.CertificateRequest + }{ + { + name: "ok/dns", + args: args{ + csr: &x509.CertificateRequest{ + DNSNames: []string{"www.example.com", "example.com"}, + }, + }, + wantCanonicalized: &x509.CertificateRequest{ + DNSNames: []string{"example.com", "www.example.com"}, + IPAddresses: []net.IP{}, + }, + }, + { + name: "ok/common-name", + args: args{ + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "example.com", + }, + DNSNames: []string{"www.example.com"}, + }, + }, + wantCanonicalized: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "example.com", + }, + DNSNames: []string{"example.com", "www.example.com"}, + IPAddresses: []net.IP{}, + }, + }, + { + name: "ok/ipv4", + args: args{ + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + }, + }, + wantCanonicalized: &x509.CertificateRequest{ + DNSNames: []string{}, + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}, + }, + }, + { + name: "ok/mixed", + args: args{ + csr: &x509.CertificateRequest{ + DNSNames: []string{"www.example.com", "example.com"}, + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + }, + }, + wantCanonicalized: &x509.CertificateRequest{ + DNSNames: []string{"example.com", "www.example.com"}, + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}, + }, + }, + { + name: "ok/mixed-common-name", + args: args{ + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "example.com", + }, + DNSNames: []string{"www.example.com"}, + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + }, + }, + wantCanonicalized: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "example.com", + }, + DNSNames: []string{"example.com", "www.example.com"}, + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotCanonicalized := canonicalize(tt.args.csr); !reflect.DeepEqual(gotCanonicalized, tt.wantCanonicalized) { + t.Errorf("canonicalize() = %v, want %v", gotCanonicalized, tt.wantCanonicalized) + } + }) + } +} + +func TestOrder_sans(t *testing.T) { + type fields struct { + ID string + AccountID string + ProvisionerID string + Status Status + ExpiresAt time.Time + Identifiers []Identifier + NotBefore time.Time + NotAfter time.Time + Error *Error + AuthorizationIDs []string + AuthorizationURLs []string + FinalizeURL string + CertificateID string + CertificateURL string + } + type args struct { + csr *x509.CertificateRequest + } + tests := []struct { + name string + fields fields + args args + want []x509util.SubjectAlternativeName + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &Order{ + ID: tt.fields.ID, + AccountID: tt.fields.AccountID, + ProvisionerID: tt.fields.ProvisionerID, + Status: tt.fields.Status, + ExpiresAt: tt.fields.ExpiresAt, + Identifiers: tt.fields.Identifiers, + NotBefore: tt.fields.NotBefore, + NotAfter: tt.fields.NotAfter, + Error: tt.fields.Error, + AuthorizationIDs: tt.fields.AuthorizationIDs, + AuthorizationURLs: tt.fields.AuthorizationURLs, + FinalizeURL: tt.fields.FinalizeURL, + CertificateID: tt.fields.CertificateID, + CertificateURL: tt.fields.CertificateURL, + } + got, err := o.sans(tt.args.csr) + if (err != nil) != tt.wantErr { + t.Errorf("Order.sans() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Order.sans() = %v, want %v", got, tt.want) + } + }) + } +} From a6d33b7d0656b004291f73524df8447fda40004f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 25 Jun 2021 17:21:22 +0200 Subject: [PATCH 096/291] Add tests for sans() --- acme/order_test.go | 189 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 41 deletions(-) diff --git a/acme/order_test.go b/acme/order_test.go index 67d01f6d..c0682fee 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -1284,54 +1284,161 @@ func Test_canonicalize(t *testing.T) { func TestOrder_sans(t *testing.T) { type fields struct { - ID string - AccountID string - ProvisionerID string - Status Status - ExpiresAt time.Time - Identifiers []Identifier - NotBefore time.Time - NotAfter time.Time - Error *Error - AuthorizationIDs []string - AuthorizationURLs []string - FinalizeURL string - CertificateID string - CertificateURL string - } - type args struct { - csr *x509.CertificateRequest + Identifiers []Identifier } tests := []struct { - name string - fields fields - args args - want []x509util.SubjectAlternativeName - wantErr bool + name string + fields fields + csr *x509.CertificateRequest + want []x509util.SubjectAlternativeName + err error }{ - // TODO: Add test cases. + { + name: "ok/dns", + fields: fields{ + Identifiers: []Identifier{ + {Type: "dns", Value: "example.com"}, + }, + }, + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "example.com", + }, + }, + want: []x509util.SubjectAlternativeName{ + {Type: "dns", Value: "example.com"}, + }, + err: nil, + }, + { + name: "fail/error-names-length-mismatch", + fields: fields{ + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "dns", Value: "bar.internal"}, + }, + }, + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "foo.internal", + }, + }, + want: []x509util.SubjectAlternativeName{}, + err: NewError(ErrorBadCSRType, "..."), + }, + { + name: "fail/error-names-mismatch", + fields: fields{ + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "dns", Value: "bar.internal"}, + }, + }, + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "foo.internal", + }, + DNSNames: []string{"zap.internal"}, + }, + want: []x509util.SubjectAlternativeName{}, + err: NewError(ErrorBadCSRType, "..."), + }, + { + name: "ok/ipv4", + fields: fields{ + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.43.42"}, + {Type: "ip", Value: "192.168.42.42"}, + }, + }, + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + }, + want: []x509util.SubjectAlternativeName{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + err: nil, + }, + { + name: "fail/error-ips-length-mismatch", + fields: fields{ + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + }, + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, + }, + want: []x509util.SubjectAlternativeName{}, + err: NewError(ErrorBadCSRType, "..."), + }, + { + name: "fail/error-ips-mismatch", + fields: fields{ + Identifiers: []Identifier{ + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + }, + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.32")}, + }, + want: []x509util.SubjectAlternativeName{}, + err: NewError(ErrorBadCSRType, "..."), + }, + { + name: "ok/mixed", + fields: fields{ + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "dns", Value: "bar.internal"}, + {Type: "ip", Value: "192.168.43.42"}, + {Type: "ip", Value: "192.168.42.42"}, + }, + }, + csr: &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "bar.internal", + }, + DNSNames: []string{"foo.internal"}, + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + }, + want: []x509util.SubjectAlternativeName{ + {Type: "dns", Value: "bar.internal"}, + {Type: "dns", Value: "foo.internal"}, + {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "192.168.43.42"}, + }, + err: nil, + }, + { + name: "fail/unsupported-identifier-type", + fields: fields{ + Identifiers: []Identifier{ + {Type: "ipv4", Value: "192.168.42.42"}, + }, + }, + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, + }, + want: []x509util.SubjectAlternativeName{}, + err: NewError(ErrorServerInternalType, "..."), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { o := &Order{ - ID: tt.fields.ID, - AccountID: tt.fields.AccountID, - ProvisionerID: tt.fields.ProvisionerID, - Status: tt.fields.Status, - ExpiresAt: tt.fields.ExpiresAt, - Identifiers: tt.fields.Identifiers, - NotBefore: tt.fields.NotBefore, - NotAfter: tt.fields.NotAfter, - Error: tt.fields.Error, - AuthorizationIDs: tt.fields.AuthorizationIDs, - AuthorizationURLs: tt.fields.AuthorizationURLs, - FinalizeURL: tt.fields.FinalizeURL, - CertificateID: tt.fields.CertificateID, - CertificateURL: tt.fields.CertificateURL, - } - got, err := o.sans(tt.args.csr) - if (err != nil) != tt.wantErr { - t.Errorf("Order.sans() error = %v, wantErr %v", err, tt.wantErr) + Identifiers: tt.fields.Identifiers, + } + canonicalizedCSR := canonicalize(tt.csr) + got, err := o.sans(canonicalizedCSR) + if err != nil && tt.err != nil { + if tt.err.Error() != err.Error() { + t.Errorf("Order.sans() error = %v, wantErr %v", err, tt.err) + return + } return } if !reflect.DeepEqual(got, tt.want) { From 87b72afa25b88f74a4fc9847f76eae4d2e988250 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 26 Jun 2021 00:13:44 +0200 Subject: [PATCH 097/291] Fix IP equality check and add more tests --- acme/order.go | 21 +++++++++------------ acme/order_test.go | 32 ++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/acme/order.go b/acme/order.go index 2a869e78..9c823f78 100644 --- a/acme/order.go +++ b/acme/order.go @@ -299,20 +299,17 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate return canonicalized } -// ipsAreEqual compares IPs to be equal. IPv6 representations of IPv4 -// adresses are considered equal to the IPv4 address in this case. -// TODO: is this behavior OK to keep? +// ipsAreEqual compares IPs to be equal. Nil values (i.e. invalid IPs) are +// not considered equal. IPv6 representations of IPv4 addresses are +// considered equal to the IPv4 address in this implementation, which is +// standard Go behavior. An example is "::ffff:192.168.42.42", which +// is equal to "192.168.42.42". This is considered a known issue within +// step and is tracked here too: https://github.com/golang/go/issues/37921. func ipsAreEqual(x, y net.IP) bool { - if matchAddrFamily(x, y) { - return x.Equal(y) + if x == nil || y == nil { + return false } - return false -} - -// matchAddrFamily returns true if two IPs are both IPv4 OR IPv6 -// Implementation taken and adapted from https://golang.org/src/net/ip.go -func matchAddrFamily(x net.IP, y net.IP) bool { - return x.To4() != nil && y.To4() != nil || x.To16() != nil && x.To4() == nil && y.To16() != nil && y.To4() == nil + return x.Equal(y) } // uniqueSortedLowerNames returns the set of all unique names in the input after all diff --git a/acme/order_test.go b/acme/order_test.go index c0682fee..b9609cf9 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -1139,6 +1139,14 @@ func Test_ipsAreEqual(t *testing.T) { }, want: true, }, + { + name: "fail/ipv4", + args: args{ + x: net.ParseIP("192.168.42.42"), + y: net.ParseIP("192.168.42.43"), + }, + want: false, + }, { name: "ok/ipv6", args: args{ @@ -1148,7 +1156,15 @@ func Test_ipsAreEqual(t *testing.T) { want: true, }, { - name: "ok/ipv4-and-ipv6", + name: "fail/ipv6", + args: args{ + x: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7335"), + }, + want: false, + }, + { + name: "fail/ipv4-and-ipv6", args: args{ x: net.ParseIP("192.168.42.42"), y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), @@ -1161,10 +1177,10 @@ func Test_ipsAreEqual(t *testing.T) { x: net.ParseIP("192.168.42.42"), y: net.ParseIP("::ffff:192.168.42.42"), // parsed to the same IPv4 by Go }, - want: true, // TODO: is this behavior OK? + want: true, // we expect this to happen; a known issue in which ipv4 mapped ipv6 addresses are considered the same as their ipv4 counterpart }, { - name: "ok/invalid-ipv4-and-valid-ipv6", + name: "fail/invalid-ipv4-and-valid-ipv6", args: args{ x: net.ParseIP("192.168.42.1000"), y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), @@ -1172,7 +1188,15 @@ func Test_ipsAreEqual(t *testing.T) { want: false, }, { - name: "ok/invalid-ipv4-and-invalid-ipv6", + name: "fail/valid-ipv4-and-invalid-ipv6", + args: args{ + x: net.ParseIP("192.168.42.42"), + y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:733400"), + }, + want: false, + }, + { + name: "fail/invalid-ipv4-and-invalid-ipv6", args: args{ x: net.ParseIP("192.168.42.1000"), y: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:1000000"), From 8e4a4ecc1f79e4ccfecdbf4528d1599e483afda7 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Sat, 26 Jun 2021 00:48:40 +0200 Subject: [PATCH 098/291] Refactor tests for sans --- acme/order.go | 2 - acme/order_test.go | 158 ++++++++++++--------------------------------- 2 files changed, 43 insertions(+), 117 deletions(-) diff --git a/acme/order.go b/acme/order.go index 9c823f78..fd8956f7 100644 --- a/acme/order.go +++ b/acme/order.go @@ -221,8 +221,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ sans = make([]x509util.SubjectAlternativeName, totalNumberOfSANs) index := 0 - // TODO: only allow IP based identifier based on configuration? Some additional configuration and validation on the provisioner for this case. - // Validate identifier names against CSR alternative names. // // Note that with certificate templates we are not going to check for the diff --git a/acme/order_test.go b/acme/order_test.go index b9609cf9..b9a43e5d 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -367,111 +367,6 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("unrecognized order status: %s", o.Status), } }, - "fail/error-names-length-mismatch": func(t *testing.T) test { - now := clock.Now() - o := &Order{ - ID: "oID", - AccountID: "accID", - Status: StatusReady, - ExpiresAt: now.Add(5 * time.Minute), - AuthorizationIDs: []string{"a", "b"}, - Identifiers: []Identifier{ - {Type: "dns", Value: "foo.internal"}, - {Type: "dns", Value: "bar.internal"}, - }, - } - orderNames := []string{"bar.internal", "foo.internal"} - csr := &x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: "foo.internal", - }, - } - - return test{ - o: o, - csr: csr, - err: NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", []string{"foo.internal"}, orderNames), - } - }, - "fail/error-ips-length-mismatch": func(t *testing.T) test { - now := clock.Now() - o := &Order{ - ID: "oID", - AccountID: "accID", - Status: StatusReady, - ExpiresAt: now.Add(5 * time.Minute), - AuthorizationIDs: []string{"a", "b"}, - Identifiers: []Identifier{ - {Type: "ip", Value: "192.168.42.42"}, - {Type: "ip", Value: "192.168.43.42"}, - }, - } - orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")} - csr := &x509.CertificateRequest{ - IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, - } - - return test{ - o: o, - csr: csr, - err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ - "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.42")}, orderIPs), - } - }, - "fail/error-names-mismatch": func(t *testing.T) test { - now := clock.Now() - o := &Order{ - ID: "oID", - AccountID: "accID", - Status: StatusReady, - ExpiresAt: now.Add(5 * time.Minute), - AuthorizationIDs: []string{"a", "b"}, - Identifiers: []Identifier{ - {Type: "dns", Value: "foo.internal"}, - {Type: "dns", Value: "bar.internal"}, - }, - } - orderNames := []string{"bar.internal", "foo.internal"} - csr := &x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: "foo.internal", - }, - DNSNames: []string{"zap.internal"}, - } - - return test{ - o: o, - csr: csr, - err: NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ - "CSR names = %v, Order names = %v", []string{"foo.internal", "zap.internal"}, orderNames), - } - }, - "fail/error-ips-mismatch": func(t *testing.T) test { - now := clock.Now() - o := &Order{ - ID: "oID", - AccountID: "accID", - Status: StatusReady, - ExpiresAt: now.Add(5 * time.Minute), - AuthorizationIDs: []string{"a", "b"}, - Identifiers: []Identifier{ - {Type: "ip", Value: "192.168.42.42"}, - {Type: "ip", Value: "192.168.43.42"}, - }, - } - orderIPs := []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")} - csr := &x509.CertificateRequest{ - IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.32")}, - } - - return test{ - o: o, - csr: csr, - err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ - "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.32"), net.ParseIP("192.168.42.42")}, orderIPs), - } - }, "fail/error-provisioner-auth": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -1315,7 +1210,7 @@ func TestOrder_sans(t *testing.T) { fields fields csr *x509.CertificateRequest want []x509util.SubjectAlternativeName - err error + err *Error }{ { name: "ok/dns", @@ -1348,7 +1243,8 @@ func TestOrder_sans(t *testing.T) { }, }, want: []x509util.SubjectAlternativeName{}, - err: NewError(ErrorBadCSRType, "..."), + err: NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", []string{"foo.internal"}, []string{"bar.internal", "foo.internal"}), }, { name: "fail/error-names-mismatch", @@ -1365,7 +1261,8 @@ func TestOrder_sans(t *testing.T) { DNSNames: []string{"zap.internal"}, }, want: []x509util.SubjectAlternativeName{}, - err: NewError(ErrorBadCSRType, "..."), + err: NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", []string{"foo.internal", "zap.internal"}, []string{"bar.internal", "foo.internal"}), }, { name: "ok/ipv4", @@ -1384,6 +1281,23 @@ func TestOrder_sans(t *testing.T) { }, err: nil, }, + { + name: "ok/ipv6", + fields: fields{ + Identifiers: []Identifier{ + {Type: "ip", Value: "2001:0db8:85a3::8a2e:0370:7335"}, + {Type: "ip", Value: "2001:0db8:85a3::8a2e:0370:7334"}, + }, + }, + csr: &x509.CertificateRequest{ + IPAddresses: []net.IP{net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7335"), net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")}, + }, + want: []x509util.SubjectAlternativeName{ + {Type: "ip", Value: "2001:db8:85a3::8a2e:370:7334"}, + {Type: "ip", Value: "2001:db8:85a3::8a2e:370:7335"}, + }, + err: nil, + }, { name: "fail/error-ips-length-mismatch", fields: fields{ @@ -1396,7 +1310,8 @@ func TestOrder_sans(t *testing.T) { IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, }, want: []x509util.SubjectAlternativeName{}, - err: NewError(ErrorBadCSRType, "..."), + err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.42")}, []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}), }, { name: "fail/error-ips-mismatch", @@ -1410,7 +1325,8 @@ func TestOrder_sans(t *testing.T) { IPAddresses: []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.42.32")}, }, want: []x509util.SubjectAlternativeName{}, - err: NewError(ErrorBadCSRType, "..."), + err: NewError(ErrorBadCSRType, "CSR IPs do not match identifiers exactly: "+ + "CSR IPs = %v, Order IPs = %v", []net.IP{net.ParseIP("192.168.42.32"), net.ParseIP("192.168.42.42")}, []net.IP{net.ParseIP("192.168.42.42"), net.ParseIP("192.168.43.42")}), }, { name: "ok/mixed", @@ -1420,6 +1336,7 @@ func TestOrder_sans(t *testing.T) { {Type: "dns", Value: "bar.internal"}, {Type: "ip", Value: "192.168.43.42"}, {Type: "ip", Value: "192.168.42.42"}, + {Type: "ip", Value: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, }, }, csr: &x509.CertificateRequest{ @@ -1427,13 +1344,14 @@ func TestOrder_sans(t *testing.T) { CommonName: "bar.internal", }, DNSNames: []string{"foo.internal"}, - IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42")}, + IPAddresses: []net.IP{net.ParseIP("192.168.43.42"), net.ParseIP("192.168.42.42"), net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")}, }, want: []x509util.SubjectAlternativeName{ {Type: "dns", Value: "bar.internal"}, {Type: "dns", Value: "foo.internal"}, {Type: "ip", Value: "192.168.42.42"}, {Type: "ip", Value: "192.168.43.42"}, + {Type: "ip", Value: "2001:db8:85a3::8a2e:370:7334"}, }, err: nil, }, @@ -1448,7 +1366,7 @@ func TestOrder_sans(t *testing.T) { IPAddresses: []net.IP{net.ParseIP("192.168.42.42")}, }, want: []x509util.SubjectAlternativeName{}, - err: NewError(ErrorServerInternalType, "..."), + err: NewError(ErrorServerInternalType, "unsupported identifier type in order: ipv4"), }, } for _, tt := range tests { @@ -1458,11 +1376,21 @@ func TestOrder_sans(t *testing.T) { } canonicalizedCSR := canonicalize(tt.csr) got, err := o.sans(canonicalizedCSR) - if err != nil && tt.err != nil { - if tt.err.Error() != err.Error() { - t.Errorf("Order.sans() error = %v, wantErr %v", err, tt.err) + if tt.err != nil { + if err == nil { + t.Errorf("Order.sans() = %v, want error; got none", got) return } + switch k := err.(type) { + case *Error: + assert.Equals(t, k.Type, tt.err.Type) + assert.Equals(t, k.Detail, tt.err.Detail) + assert.Equals(t, k.Status, tt.err.Status) + assert.Equals(t, k.Err.Error(), tt.err.Err.Error()) + assert.Equals(t, k.Detail, tt.err.Detail) + default: + assert.FatalError(t, errors.New("unexpected error type")) + } return } if !reflect.DeepEqual(got, tt.want) { From 9d4e6e315a4a1caefc64b3ba9e3b1216517ca12f Mon Sep 17 00:00:00 2001 From: Kevin Chen <49530888+devadvocado@users.noreply.github.com> Date: Tue, 29 Jun 2021 11:01:53 -0700 Subject: [PATCH 099/291] update readme page --- docs/CONTRIBUTING.md | 2 +- docs/revocation.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index d7356fd9..35f75159 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -81,7 +81,7 @@ When the build is complete, you will find binaries in `bin/`. ## Asking Support Questions -Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Discord](https://bit.ly/stepdiscord). +Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Discord](https://bit.ly/step-discord). ## Reporting Issues diff --git a/docs/revocation.md b/docs/revocation.md index e994940d..4f3a7d5e 100644 --- a/docs/revocation.md +++ b/docs/revocation.md @@ -202,7 +202,8 @@ through an example. [Use TLS Everywhere](https://smallstep.com/blog/use-tls.html) and let us know what you think of our tools. Get in touch over [Twitter](twitter.com/smallsteplabs) or through our -[GitHub Discussions](https://github.com/smallstep/certificates/discussions) to chat with us in real time. +[GitHub Discussions](https://github.com/smallstep/certificates/discussions) to find answers to frequently asked questions. +[Discord](https://bit.ly/step-discord) to chat with us in real time. ## Further Reading From 9fdef647099dd91b53683bff49e205f16f743a57 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 3 May 2021 12:48:20 -0700 Subject: [PATCH 100/291] Admin level API for provisioner mgmt v1 --- .golangci.yml | 4 +- acme/api/middleware.go | 8 +- acme/common.go | 2 +- acme/db/nosql/nosql.go | 7 +- acme/order_test.go | 14 +- api/api.go | 4 +- api/api_test.go | 16 +- api/errors.go | 6 +- api/utils.go | 36 + authority/{mgmt => admin}/api/admin.go | 82 +- authority/admin/api/handler.go | 41 + authority/admin/api/middleware.go | 66 + authority/admin/api/provisioner.go | 175 ++ authority/admin/collection.go | 180 -- authority/{mgmt => admin}/db.go | 47 +- authority/{mgmt => admin}/db/nosql/admin.go | 94 +- authority/admin/db/nosql/admin_test.go | 1121 +++++++++++ authority/{mgmt => admin}/db/nosql/nosql.go | 10 +- authority/admin/db/nosql/provisioner.go | 211 ++ authority/admin/db/nosql/provisioner_test.go | 1221 ++++++++++++ authority/{mgmt => admin}/errors.go | 25 +- authority/administrator/collection.go | 243 +++ authority/admins.go | 97 + authority/authority.go | 241 +-- authority/authority_test.go | 2 - authority/authorize.go | 125 +- authority/authorize_test.go | 2 +- authority/config.go | 11 +- authority/config/config.go | 32 +- authority/config/config_test.go | 66 +- authority/config/ssh_test.go | 73 + authority/config/tls_options.go | 12 + authority/mgmt/api/handler.go | 47 - authority/mgmt/api/provisioner.go | 199 -- authority/mgmt/config.go | 48 - authority/mgmt/db/nosql/provisioner.go | 219 --- authority/mgmt/provisioner.go | 116 -- authority/options.go | 4 +- authority/provisioner/acme.go | 2 +- authority/provisioner/acme_test.go | 4 +- authority/provisioner/aws.go | 12 +- authority/provisioner/azure.go | 4 +- authority/provisioner/claims.go | 39 +- authority/provisioner/collection.go | 89 +- authority/provisioner/collection_test.go | 25 +- authority/provisioner/gcp.go | 10 +- authority/provisioner/jwk.go | 4 +- authority/provisioner/jwk_test.go | 2 +- authority/provisioner/k8sSA.go | 8 +- authority/provisioner/k8sSA_test.go | 4 +- authority/provisioner/oidc.go | 4 +- authority/provisioner/scep.go | 14 +- authority/provisioner/sshpop.go | 2 +- authority/provisioner/x5c.go | 11 +- authority/provisioner/x5c_test.go | 8 +- authority/provisioners.go | 668 +++++-- authority/provisioners_test.go | 2 +- authority/ssh_test.go | 63 - authority/tls.go | 7 +- authority/tls_test.go | 4 +- ca/adminClient.go | 238 ++- ca/ca.go | 33 +- ca/client.go | 105 +- ca/testdata/ca.json | 4 +- cas/stepcas/x5c_issuer.go | 8 +- go.mod | 14 +- go.sum | 197 +- linkedca/admin.pb.go | 240 --- linkedca/admin.proto | 18 - linkedca/doc.go | 3 - linkedca/provisioners.go | 38 - linkedca/provisioners.pb.go | 1833 ------------------ linkedca/provisioners.proto | 133 -- scep/api/api.go | 10 +- scep/authority.go | 12 +- 75 files changed, 4906 insertions(+), 3873 deletions(-) rename authority/{mgmt => admin}/api/admin.go (50%) create mode 100644 authority/admin/api/handler.go create mode 100644 authority/admin/api/middleware.go create mode 100644 authority/admin/api/provisioner.go delete mode 100644 authority/admin/collection.go rename authority/{mgmt => admin}/db.go (72%) rename authority/{mgmt => admin}/db/nosql/admin.go (68%) create mode 100644 authority/admin/db/nosql/admin_test.go rename authority/{mgmt => admin}/db/nosql/nosql.go (81%) create mode 100644 authority/admin/db/nosql/provisioner.go create mode 100644 authority/admin/db/nosql/provisioner_test.go rename authority/{mgmt => admin}/errors.go (88%) create mode 100644 authority/administrator/collection.go create mode 100644 authority/admins.go create mode 100644 authority/config/ssh_test.go delete mode 100644 authority/mgmt/api/handler.go delete mode 100644 authority/mgmt/api/provisioner.go delete mode 100644 authority/mgmt/config.go delete mode 100644 authority/mgmt/db/nosql/provisioner.go delete mode 100644 authority/mgmt/provisioner.go delete mode 100644 linkedca/admin.pb.go delete mode 100644 linkedca/admin.proto delete mode 100644 linkedca/doc.go delete mode 100644 linkedca/provisioners.go delete mode 100644 linkedca/provisioners.pb.go delete mode 100644 linkedca/provisioners.proto diff --git a/.golangci.yml b/.golangci.yml index 1bab3ba3..92af7723 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,7 +8,7 @@ linters-settings: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - golint: + revive: min-confidence: 0 gocyclo: min-complexity: 10 @@ -44,7 +44,7 @@ linters: disable-all: true enable: - gofmt - - golint + - revive - govet - misspell - ineffassign diff --git a/acme/api/middleware.go b/acme/api/middleware.go index 50f7146f..b2244dd7 100644 --- a/acme/api/middleware.go +++ b/acme/api/middleware.go @@ -288,13 +288,13 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - name := chi.URLParam(r, "provisionerID") - provID, err := url.PathUnescape(name) + nameEscaped := chi.URLParam(r, "provisionerID") + name, err := url.PathUnescape(nameEscaped) if err != nil { - api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner id '%s'", name)) + api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped)) return } - p, err := h.ca.LoadProvisionerByID("acme/" + provID) + p, err := h.ca.LoadProvisionerByName(name) if err != nil { api.WriteError(w, err) return diff --git a/acme/common.go b/acme/common.go index 26552c61..f18907fe 100644 --- a/acme/common.go +++ b/acme/common.go @@ -11,7 +11,7 @@ import ( // CertificateAuthority is the interface implemented by a CA authority. type CertificateAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) } // Clock that returns time in UTC rounded to seconds. diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go index e2edd050..052f5729 100644 --- a/acme/db/nosql/nosql.go +++ b/acme/db/nosql/nosql.go @@ -23,12 +23,11 @@ var ( // DB is a struct that implements the AcmeDB interface. type DB struct { - db nosqlDB.DB - authorityID string + db nosqlDB.DB } // New configures and returns a new ACME DB backend implemented using a nosql DB. -func New(db nosqlDB.DB, authorityID string) (*DB, error) { +func New(db nosqlDB.DB) (*DB, error) { tables := [][]byte{accountTable, accountByKeyIDTable, authzTable, challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable} for _, b := range tables { @@ -37,7 +36,7 @@ func New(db nosqlDB.DB, authorityID string) (*DB, error) { string(b)) } } - return &DB{db, authorityID}, nil + return &DB{db}, nil } // save writes the new data to the database, overwriting the old data if it diff --git a/acme/order_test.go b/acme/order_test.go index 993a92f2..849f0daa 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -261,10 +261,10 @@ func TestOrder_UpdateStatus(t *testing.T) { } type mockSignAuth struct { - sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - loadProvisionerByID func(string) (provisioner.Interface, error) - ret1, ret2 interface{} - err error + sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + loadProvisionerByName func(string) (provisioner.Interface, error) + ret1, ret2 interface{} + err error } func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { @@ -276,9 +276,9 @@ func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.S return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err } -func (m *mockSignAuth) LoadProvisionerByID(id string) (provisioner.Interface, error) { - if m.loadProvisionerByID != nil { - return m.loadProvisionerByID(id) +func (m *mockSignAuth) LoadProvisionerByName(name string) (provisioner.Interface, error) { + if m.loadProvisionerByName != nil { + return m.loadProvisionerByName(name) } return m.ret1.(provisioner.Interface), m.err } diff --git a/api/api.go b/api/api.go index 20ef7e14..b542f473 100644 --- a/api/api.go +++ b/api/api.go @@ -39,7 +39,7 @@ type Authority interface { Renew(peer *x509.Certificate) ([]*x509.Certificate, error) Rekey(peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) Revoke(context.Context, *authority.RevokeOptions) error GetEncryptedKey(kid string) (string, error) @@ -418,7 +418,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if len(val.CredentialID) > 0 { m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) } else { - m["provisioner"] = fmt.Sprintf("%s", val.Name) + m["provisioner"] = string(val.Name) } break } diff --git a/api/api_test.go b/api/api_test.go index 62ef7740..33d2bae7 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -430,6 +430,7 @@ type mockProvisioner struct { ret1, ret2, ret3 interface{} err error getID func() string + getIDForToken func() string getTokenID func(string) (string, error) getName func() string getType func() provisioner.Type @@ -452,6 +453,13 @@ func (m *mockProvisioner) GetID() string { return m.ret1.(string) } +func (m *mockProvisioner) GetIDForToken() string { + if m.getIDForToken != nil { + return m.getIDForToken() + } + return m.ret1.(string) +} + func (m *mockProvisioner) GetTokenID(token string) (string, error) { if m.getTokenID != nil { return m.getTokenID(token) @@ -553,7 +561,7 @@ type mockAuthority struct { renew func(cert *x509.Certificate) ([]*x509.Certificate, error) rekey func(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) loadProvisionerByCertificate func(cert *x509.Certificate) (provisioner.Interface, error) - loadProvisionerByID func(provID string) (provisioner.Interface, error) + loadProvisionerByName func(name string) (provisioner.Interface, error) getProvisioners func(nextCursor string, limit int) (provisioner.List, string, error) revoke func(context.Context, *authority.RevokeOptions) error getEncryptedKey func(kid string) (string, error) @@ -633,9 +641,9 @@ func (m *mockAuthority) LoadProvisionerByCertificate(cert *x509.Certificate) (pr return m.ret1.(provisioner.Interface), m.err } -func (m *mockAuthority) LoadProvisionerByID(provID string) (provisioner.Interface, error) { - if m.loadProvisionerByID != nil { - return m.loadProvisionerByID(provID) +func (m *mockAuthority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + if m.loadProvisionerByName != nil { + return m.loadProvisionerByName(name) } return m.ret1.(provisioner.Interface), m.err } diff --git a/api/errors.go b/api/errors.go index f9bcb199..db3bc3e2 100644 --- a/api/errors.go +++ b/api/errors.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/acme" - "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/scep" @@ -20,8 +20,8 @@ func WriteError(w http.ResponseWriter, err error) { case *acme.Error: acme.WriteError(w, k) return - case *mgmt.Error: - mgmt.WriteError(w, k) + case *admin.Error: + admin.WriteError(w, k) return case *scep.Error: w.Header().Set("Content-Type", "text/plain") diff --git a/api/utils.go b/api/utils.go index 0d87a065..bf45db53 100644 --- a/api/utils.go +++ b/api/utils.go @@ -3,11 +3,14 @@ package api import ( "encoding/json" "io" + "io/ioutil" "log" "net/http" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" ) // EnableLogger is an interface that enables response logging for an object. @@ -64,6 +67,29 @@ func JSONStatus(w http.ResponseWriter, v interface{}, status int) { LogEnabledResponse(w, v) } +// ProtoJSON writes the passed value into the http.ResponseWriter. +func ProtoJSON(w http.ResponseWriter, m proto.Message) { + ProtoJSONStatus(w, m, http.StatusOK) +} + +// ProtoJSONStatus writes the given value into the http.ResponseWriter and the +// given status is written as the status code of the response. +func ProtoJSONStatus(w http.ResponseWriter, m proto.Message, status int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + + b, err := protojson.Marshal(m) + if err != nil { + LogError(w, err) + return + } + if _, err := w.Write(b); err != nil { + LogError(w, err) + return + } + //LogEnabledResponse(w, v) +} + // ReadJSON reads JSON from the request body and stores it in the value // pointed by v. func ReadJSON(r io.Reader, v interface{}) error { @@ -72,3 +98,13 @@ func ReadJSON(r io.Reader, v interface{}) error { } return nil } + +// ReadProtoJSON reads JSON from the request body and stores it in the value +// pointed by v. +func ReadProtoJSON(r io.Reader, m proto.Message) error { + data, err := ioutil.ReadAll(r) + if err != nil { + return errs.Wrap(http.StatusBadRequest, err, "error reading request body") + } + return protojson.Unmarshal(data, m) +} diff --git a/authority/mgmt/api/admin.go b/authority/admin/api/admin.go similarity index 50% rename from authority/mgmt/api/admin.go rename to authority/admin/api/admin.go index 30c78555..d92af5b2 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/admin/api/admin.go @@ -1,14 +1,12 @@ package api import ( - "fmt" "net/http" "github.com/go-chi/chi" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/admin" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/linkedca" ) // CreateAdminRequest represents the body for a CreateAdmin request. @@ -19,11 +17,7 @@ type CreateAdminRequest struct { } // Validate validates a new-admin request body. -func (car *CreateAdminRequest) Validate(c *admin.Collection) error { - if _, ok := c.LoadBySubProv(car.Subject, car.Provisioner); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, - "admin with subject: '%s' and provisioner: '%s' already exists", car.Subject, car.Provisioner) - } +func (car *CreateAdminRequest) Validate() error { return nil } @@ -52,25 +46,29 @@ type DeleteResponse struct { func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - adm, ok := h.auth.GetAdminCollection().LoadByID(id) + adm, ok := h.auth.LoadAdminByID(id) if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, + api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id)) return } api.JSON(w, adm) } -// GetAdmins returns all admins associated with the authority. +// GetAdmins returns a segment of admins associated with the authority. func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { cursor, limit, err := api.ParseCursor(r) if err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error parsing cursor and limit from query params")) return } - admins, nextCursor := h.auth.GetAdminCollection().Find(cursor, limit) + admins, nextCursor, err := h.auth.GetAdmins(cursor, limit) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error retrieving paginated admins")) + return + } api.JSON(w, &GetAdminsResponse{ Admins: admins, NextCursor: nextCursor, @@ -79,91 +77,63 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { // CreateAdmin creates a new admin. func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - var body CreateAdminRequest if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } - if err := body.Validate(h.auth.GetAdminCollection()); err != nil { + if err := body.Validate(); err != nil { api.WriteError(w, err) return } - p, ok := h.auth.GetProvisionerCollection().LoadByName(body.Provisioner) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", body.Provisioner)) + p, err := h.auth.LoadProvisionerByName(body.Provisioner) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner)) return } - adm := &linkedca.Admin{ ProvisionerId: p.GetID(), Subject: body.Subject, Type: body.Type, } - if err := h.db.CreateAdmin(ctx, adm); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) + // Store to authority collection. + if err := h.auth.StoreAdmin(r.Context(), adm, p); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error storing admin")) return } + api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } // DeleteAdmin deletes admin. func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - if h.auth.GetAdminCollection().SuperCount() == 1 { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove the last super admin")) + if err := h.auth.RemoveAdmin(r.Context(), id); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error deleting admin %s", id)) return } - ctx := r.Context() - if err := h.db.DeleteAdmin(ctx, id); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s", id)) - return - } api.JSON(w, &DeleteResponse{Status: "ok"}) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } // UpdateAdmin updates an existing admin. func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - var body UpdateAdminRequest if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } id := chi.URLParam(r, "id") - adm, ok := h.auth.GetAdminCollection().LoadByID(id) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id)) - return - } - if adm.Type == body.Type { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type)) + adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type}) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error updating admin %s", id)) return } - adm.Type = body.Type - - if err := h.db.UpdateAdmin(ctx, (*linkedca.Admin)(adm)); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) - return - } api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } diff --git a/authority/admin/api/handler.go b/authority/admin/api/handler.go new file mode 100644 index 00000000..d88edfa1 --- /dev/null +++ b/authority/admin/api/handler.go @@ -0,0 +1,41 @@ +package api + +import ( + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/admin" +) + +// Handler is the ACME API request handler. +type Handler struct { + db admin.DB + auth *authority.Authority +} + +// NewHandler returns a new Authority Config Handler. +func NewHandler(auth *authority.Authority) api.RouterHandler { + h := &Handler{db: auth.GetAdminDatabase(), auth: auth} + + return h +} + +// Route traffic and implement the Router interface. +func (h *Handler) Route(r api.Router) { + authnz := func(next nextHTTP) nextHTTP { + return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next)) + } + + // Provisioners + r.MethodFunc("GET", "/provisioners/{name}", authnz(h.GetProvisioner)) + r.MethodFunc("GET", "/provisioners", authnz(h.GetProvisioners)) + r.MethodFunc("POST", "/provisioners", authnz(h.CreateProvisioner)) + r.MethodFunc("PUT", "/provisioners/{name}", authnz(h.UpdateProvisioner)) + r.MethodFunc("DELETE", "/provisioners/{name}", authnz(h.DeleteProvisioner)) + + // Admins + r.MethodFunc("GET", "/admins/{id}", authnz(h.GetAdmin)) + r.MethodFunc("GET", "/admins", authnz(h.GetAdmins)) + r.MethodFunc("POST", "/admins", authnz(h.CreateAdmin)) + r.MethodFunc("PATCH", "/admins/{id}", authnz(h.UpdateAdmin)) + r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin)) +} diff --git a/authority/admin/api/middleware.go b/authority/admin/api/middleware.go new file mode 100644 index 00000000..137bd6f7 --- /dev/null +++ b/authority/admin/api/middleware.go @@ -0,0 +1,66 @@ +package api + +import ( + "context" + "net/http" + + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/admin" +) + +type nextHTTP = func(http.ResponseWriter, *http.Request) + +// requireAPIEnabled is a middleware that ensures the Administration API +// is enabled before servicing requests. +func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP { + return func(w http.ResponseWriter, r *http.Request) { + if h.db == nil { + api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType, + "administration API not enabled")) + return + } + next(w, r) + } +} + +// extractAuthorizeTokenAdmin is a middleware that extracts and caches the bearer token. +func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP { + return func(w http.ResponseWriter, r *http.Request) { + tok := r.Header.Get("Authorization") + if len(tok) == 0 { + api.WriteError(w, admin.NewError(admin.ErrorUnauthorizedType, + "missing authorization header token")) + return + } + + adm, err := h.auth.AuthorizeAdminToken(r, tok) + if err != nil { + api.WriteError(w, err) + return + } + + ctx := context.WithValue(r.Context(), adminContextKey, adm) + next(w, r.WithContext(ctx)) + } +} + +// ContextKey is the key type for storing and searching for ACME request +// essentials in the context of a request. +type ContextKey string + +const ( + // adminContextKey account key + adminContextKey = ContextKey("admin") +) + +/* +// adminFromContext searches the context for the token. Returns the +// token or an error. +func adminFromContext(ctx context.Context) (*linkedca.Admin, error) { + val, ok := ctx.Value(adminContextKey).(*linkedca.Admin) + if !ok || val == nil { + return nil, admin.NewErrorISE("admin not in context") + } + return val, nil +} +*/ diff --git a/authority/admin/api/provisioner.go b/authority/admin/api/provisioner.go new file mode 100644 index 00000000..e63a4417 --- /dev/null +++ b/authority/admin/api/provisioner.go @@ -0,0 +1,175 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/errs" + "go.step.sm/linkedca" +) + +// GetProvisionersResponse is the type for GET /admin/provisioners responses. +type GetProvisionersResponse struct { + Provisioners provisioner.List `json:"provisioners"` + NextCursor string `json:"nextCursor"` +} + +// GetProvisioner returns the requested provisioner, or an error. +func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + id := r.URL.Query().Get("id") + name := chi.URLParam(r, "name") + + var ( + p provisioner.Interface + err error + ) + if len(id) > 0 { + if p, err = h.auth.LoadProvisionerByID(id); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", id)) + return + } + } else { + if p, err = h.auth.LoadProvisionerByName(name); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name)) + return + } + } + + prov, err := h.db.GetProvisioner(ctx, p.GetID()) + if err != nil { + api.WriteError(w, err) + return + } + api.ProtoJSON(w, prov) +} + +// GetProvisioners returns the given segment of provisioners associated with the authority. +func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { + cursor, limit, err := api.ParseCursor(r) + if err != nil { + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, + "error parsing cursor & limit query params")) + return + } + + p, next, err := h.auth.GetProvisioners(cursor, limit) + if err != nil { + api.WriteError(w, errs.InternalServerErr(err)) + return + } + api.JSON(w, &GetProvisionersResponse{ + Provisioners: p, + NextCursor: next, + }) +} + +// CreateProvisioner creates a new prov. +func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { + var prov = new(linkedca.Provisioner) + if err := api.ReadProtoJSON(r.Body, prov); err != nil { + api.WriteError(w, err) + return + } + + // TODO: Validate inputs + if err := authority.ValidateClaims(prov.Claims); err != nil { + api.WriteError(w, err) + return + } + + if err := h.auth.StoreProvisioner(r.Context(), prov); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name)) + return + } + api.ProtoJSONStatus(w, prov, http.StatusCreated) +} + +// DeleteProvisioner deletes a provisioner. +func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { + id := r.URL.Query().Get("id") + name := chi.URLParam(r, "name") + + var ( + p provisioner.Interface + err error + ) + if len(id) > 0 { + if p, err = h.auth.LoadProvisionerByID(id); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", id)) + return + } + } else { + if p, err = h.auth.LoadProvisionerByName(name); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name)) + return + } + } + + if err := h.auth.RemoveProvisioner(r.Context(), p.GetID()); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error removing provisioner %s", p.GetName())) + return + } + + api.JSON(w, &DeleteResponse{Status: "ok"}) +} + +// UpdateProvisioner updates an existing prov. +func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { + var nu = new(linkedca.Provisioner) + if err := api.ReadProtoJSON(r.Body, nu); err != nil { + api.WriteError(w, err) + return + } + + name := chi.URLParam(r, "name") + _old, err := h.auth.LoadProvisionerByName(name) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner from cached configuration '%s'", name)) + return + } + + old, err := h.db.GetProvisioner(r.Context(), _old.GetID()) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner from db '%s'", _old.GetID())) + return + } + + if nu.Id != old.Id { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner ID")) + return + } + if nu.Type != old.Type { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner type")) + return + } + if nu.AuthorityId != old.AuthorityId { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner authorityID")) + return + } + if !nu.CreatedAt.AsTime().Equal(old.CreatedAt.AsTime()) { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner createdAt")) + return + } + if !nu.DeletedAt.AsTime().Equal(old.DeletedAt.AsTime()) { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner deletedAt")) + return + } + + // TODO: Validate inputs + if err := authority.ValidateClaims(nu.Claims); err != nil { + api.WriteError(w, err) + return + } + + if err := h.auth.UpdateProvisioner(r.Context(), nu); err != nil { + api.WriteError(w, err) + return + } + api.ProtoJSONStatus(w, nu, http.StatusOK) +} diff --git a/authority/admin/collection.go b/authority/admin/collection.go deleted file mode 100644 index e9d41113..00000000 --- a/authority/admin/collection.go +++ /dev/null @@ -1,180 +0,0 @@ -package admin - -import ( - "crypto/sha1" - "encoding/binary" - "encoding/hex" - "fmt" - "sort" - "strings" - "sync" - - "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/linkedca" -) - -// DefaultAdminLimit is the default limit for listing provisioners. -const DefaultAdminLimit = 20 - -// DefaultAdminMax is the maximum limit for listing provisioners. -const DefaultAdminMax = 100 - -type uidAdmin struct { - admin *linkedca.Admin - uid string -} - -type adminSlice []uidAdmin - -func (p adminSlice) Len() int { return len(p) } -func (p adminSlice) Less(i, j int) bool { return p[i].uid < p[j].uid } -func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// Collection is a memory map of admins. -type Collection struct { - byID *sync.Map - bySubProv *sync.Map - byProv *sync.Map - sorted adminSlice - provisioners *provisioner.Collection - superCount int - superCountByProvisioner map[string]int -} - -// NewCollection initializes a collection of provisioners. The given list of -// audiences are the audiences used by the JWT provisioner. -func NewCollection(provisioners *provisioner.Collection) *Collection { - return &Collection{ - byID: new(sync.Map), - byProv: new(sync.Map), - bySubProv: new(sync.Map), - superCountByProvisioner: map[string]int{}, - provisioners: provisioners, - } -} - -// LoadByID a admin by the ID. -func (c *Collection) LoadByID(id string) (*linkedca.Admin, bool) { - return loadAdmin(c.byID, id) -} - -func subProvNameHash(sub, provName string) string { - subHash := sha1.Sum([]byte(sub)) - provNameHash := sha1.Sum([]byte(provName)) - _res := sha1.Sum(append(subHash[:], provNameHash[:]...)) - return string(_res[:]) -} - -// LoadBySubProv a admin by the subject and provisioner name. -func (c *Collection) LoadBySubProv(sub, provName string) (*linkedca.Admin, bool) { - return loadAdmin(c.bySubProv, subProvNameHash(sub, provName)) -} - -// LoadByProvisioner a admin by the subject and provisioner name. -func (c *Collection) LoadByProvisioner(provName string) ([]*linkedca.Admin, bool) { - a, ok := c.byProv.Load(provName) - if !ok { - return nil, false - } - admins, ok := a.([]*linkedca.Admin) - if !ok { - return nil, false - } - return admins, true -} - -// Store adds an admin to the collection and enforces the uniqueness of -// admin IDs and amdin subject <-> provisioner name combos. -func (c *Collection) Store(adm *linkedca.Admin) error { - p, ok := c.provisioners.Load(adm.ProvisionerId) - if !ok { - return fmt.Errorf("provisioner %s not found", adm.ProvisionerId) - } - // Store admin always in byID. ID must be unique. - if _, loaded := c.byID.LoadOrStore(adm.Id, adm); loaded { - return errors.New("cannot add multiple admins with the same id") - } - - provName := p.GetName() - // Store admin always in bySubProv. Subject <-> ProvisionerName must be unique. - if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded { - c.byID.Delete(adm.Id) - return errors.New("cannot add multiple admins with the same subject and provisioner") - } - - if admins, ok := c.LoadByProvisioner(provName); ok { - c.byProv.Store(provName, append(admins, adm)) - c.superCountByProvisioner[provName]++ - } else { - c.byProv.Store(provName, []*linkedca.Admin{adm}) - c.superCountByProvisioner[provName] = 1 - } - c.superCount++ - - // Store sorted admins. - // Use the first 4 bytes (32bit) of the sum to insert the order - // Using big endian format to get the strings sorted: - // 0x00000000, 0x00000001, 0x00000002, ... - bi := make([]byte, 4) - _sum := sha1.Sum([]byte(adm.Id)) - sum := _sum[:] - binary.BigEndian.PutUint32(bi, uint32(c.sorted.Len())) - sum[0], sum[1], sum[2], sum[3] = bi[0], bi[1], bi[2], bi[3] - c.sorted = append(c.sorted, uidAdmin{ - admin: adm, - uid: hex.EncodeToString(sum), - }) - sort.Sort(c.sorted) - - return nil -} - -// SuperCount returns the total number of admins. -func (c *Collection) SuperCount() int { - return c.superCount -} - -// SuperCountByProvisioner returns the total number of admins. -func (c *Collection) SuperCountByProvisioner(provName string) int { - if cnt, ok := c.superCountByProvisioner[provName]; ok { - return cnt - } - return 0 -} - -// Find implements pagination on a list of sorted provisioners. -func (c *Collection) Find(cursor string, limit int) ([]*linkedca.Admin, string) { - switch { - case limit <= 0: - limit = DefaultAdminLimit - case limit > DefaultAdminMax: - limit = DefaultAdminMax - } - - n := c.sorted.Len() - cursor = fmt.Sprintf("%040s", cursor) - i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor }) - - slice := []*linkedca.Admin{} - for ; i < n && len(slice) < limit; i++ { - slice = append(slice, c.sorted[i].admin) - } - - if i < n { - return slice, strings.TrimLeft(c.sorted[i].uid, "0") - } - return slice, "" -} - -func loadAdmin(m *sync.Map, key string) (*linkedca.Admin, bool) { - a, ok := m.Load(key) - if !ok { - return nil, false - } - adm, ok := a.(*linkedca.Admin) - if !ok { - return nil, false - } - return adm, true -} diff --git a/authority/mgmt/db.go b/authority/admin/db.go similarity index 72% rename from authority/mgmt/db.go rename to authority/admin/db.go index 64ed39a2..15fe6686 100644 --- a/authority/mgmt/db.go +++ b/authority/admin/db.go @@ -1,16 +1,59 @@ -package mgmt +package admin import ( "context" + "encoding/json" + "fmt" "github.com/pkg/errors" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/linkedca" +) + +const ( + // DefaultAuthorityID is the default AuthorityID. This will be the ID + // of the first Authority created, as well as the default AuthorityID + // if one is not specified in the configuration. + DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) // ErrNotFound is an error that should be used by the authority.DB interface to // indicate that an entity does not exist. var ErrNotFound = errors.New("not found") +// UnmarshalProvisionerDetails unmarshals details type to the specific provisioner details. +func UnmarshalProvisionerDetails(typ linkedca.Provisioner_Type, data []byte) (*linkedca.ProvisionerDetails, error) { + var v linkedca.ProvisionerDetails + switch typ { + case linkedca.Provisioner_JWK: + v.Data = new(linkedca.ProvisionerDetails_JWK) + case linkedca.Provisioner_OIDC: + v.Data = new(linkedca.ProvisionerDetails_OIDC) + case linkedca.Provisioner_GCP: + v.Data = new(linkedca.ProvisionerDetails_GCP) + case linkedca.Provisioner_AWS: + v.Data = new(linkedca.ProvisionerDetails_AWS) + case linkedca.Provisioner_AZURE: + v.Data = new(linkedca.ProvisionerDetails_Azure) + case linkedca.Provisioner_ACME: + v.Data = new(linkedca.ProvisionerDetails_ACME) + case linkedca.Provisioner_X5C: + v.Data = new(linkedca.ProvisionerDetails_X5C) + case linkedca.Provisioner_K8SSA: + v.Data = new(linkedca.ProvisionerDetails_K8SSA) + case linkedca.Provisioner_SSHPOP: + v.Data = new(linkedca.ProvisionerDetails_SSHPOP) + case linkedca.Provisioner_SCEP: + v.Data = new(linkedca.ProvisionerDetails_SCEP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal(data, v.Data); err != nil { + return nil, err + } + return &linkedca.ProvisionerDetails{Data: v.Data}, nil +} + // DB is the DB interface expected by the step-ca ACME API. type DB interface { CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error diff --git a/authority/mgmt/db/nosql/admin.go b/authority/admin/db/nosql/admin.go similarity index 68% rename from authority/mgmt/db/nosql/admin.go rename to authority/admin/db/nosql/admin.go index 8fd0fea5..6bb6bdd1 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/admin/db/nosql/admin.go @@ -6,9 +6,10 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/nosql" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" ) // dbAdmin is the database representation of the Admin type. @@ -22,60 +23,66 @@ type dbAdmin struct { DeletedAt time.Time `json:"deletedAt"` } -func (dbp *dbAdmin) clone() *dbAdmin { - u := *dbp +func (dba *dbAdmin) convert() *linkedca.Admin { + return &linkedca.Admin{ + Id: dba.ID, + AuthorityId: dba.AuthorityID, + ProvisionerId: dba.ProvisionerID, + Subject: dba.Subject, + Type: dba.Type, + CreatedAt: timestamppb.New(dba.CreatedAt), + DeletedAt: timestamppb.New(dba.DeletedAt), + } +} + +func (dba *dbAdmin) clone() *dbAdmin { + u := *dba return &u } func (db *DB) getDBAdminBytes(ctx context.Context, id string) ([]byte, error) { - data, err := db.db.Get(authorityAdminsTable, []byte(id)) + data, err := db.db.Get(adminsTable, []byte(id)) if nosql.IsErrNotFound(err) { - return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id) + return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id) } else if err != nil { return nil, errors.Wrapf(err, "error loading admin %s", id) } return data, nil } -func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { - data, err := db.getDBAdminBytes(ctx, id) - if err != nil { - return nil, err +func (db *DB) unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { + var dba = new(dbAdmin) + if err := json.Unmarshal(data, dba); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) } - dba, err := unmarshalDBAdmin(data, id) - if err != nil { - return nil, err + if !dba.DeletedAt.IsZero() { + return nil, admin.NewError(admin.ErrorDeletedType, "admin %s is deleted", id) } if dba.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + return nil, admin.NewError(admin.ErrorAuthorityMismatchType, "admin %s is not owned by authority %s", dba.ID, db.authorityID) } return dba, nil } -func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { - var dba = new(dbAdmin) - if err := json.Unmarshal(data, dba); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) +func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { + data, err := db.getDBAdminBytes(ctx, id) + if err != nil { + return nil, err } - if !dba.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", id) + dba, err := db.unmarshalDBAdmin(data, id) + if err != nil { + return nil, err } return dba, nil } -func unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { - dba, err := unmarshalDBAdmin(data, id) +func (db *DB) unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { + dba, err := db.unmarshalDBAdmin(data, id) if err != nil { return nil, err } - return &linkedca.Admin{ - Id: dba.ID, - AuthorityId: dba.AuthorityID, - ProvisionerId: dba.ProvisionerID, - Subject: dba.Subject, - Type: dba.Type, - }, nil + return dba.convert(), nil } // GetAdmin retrieves and unmarshals a admin from the database. @@ -84,14 +91,10 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) if err != nil { return nil, err } - adm, err := unmarshalAdmin(data, id) + adm, err := db.unmarshalAdmin(data, id) if err != nil { return nil, err } - if adm.AuthorityId != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "admin %s is not owned by authority %s", adm.Id, db.authorityID) - } return adm, nil } @@ -100,15 +103,24 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) // from the database. // TODO should we be paginating? func (db *DB) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { - dbEntries, err := db.db.List(authorityAdminsTable) + dbEntries, err := db.db.List(adminsTable) if err != nil { return nil, errors.Wrap(err, "error loading admins") } var admins = []*linkedca.Admin{} for _, entry := range dbEntries { - adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) + adm, err := db.unmarshalAdmin(entry.Value, string(entry.Key)) if err != nil { - return nil, err + switch k := err.(type) { + case *admin.Error: + if k.IsType(admin.ErrorDeletedType) || k.IsType(admin.ErrorAuthorityMismatchType) { + continue + } else { + return nil, err + } + default: + return nil, err + } } if adm.AuthorityId != db.authorityID { continue @@ -123,7 +135,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { var err error adm.Id, err = randID() if err != nil { - return mgmt.WrapErrorISE(err, "error generating random id for admin") + return admin.WrapErrorISE(err, "error generating random id for admin") } adm.AuthorityId = db.authorityID @@ -136,7 +148,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { CreatedAt: clock.Now(), } - return db.save(ctx, dba.ID, dba, nil, "admin", authorityAdminsTable) + return db.save(ctx, dba.ID, dba, nil, "admin", adminsTable) } // UpdateAdmin saves an updated admin to the database. @@ -149,7 +161,7 @@ func (db *DB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { nu := old.clone() nu.Type = adm.Type - return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) + return db.save(ctx, old.ID, nu, old, "admin", adminsTable) } // DeleteAdmin saves an updated admin to the database. @@ -162,5 +174,5 @@ func (db *DB) DeleteAdmin(ctx context.Context, id string) error { nu := old.clone() nu.DeletedAt = clock.Now() - return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) + return db.save(ctx, old.ID, nu, old, "admin", adminsTable) } diff --git a/authority/admin/db/nosql/admin_test.go b/authority/admin/db/nosql/admin_test.go new file mode 100644 index 00000000..092d72db --- /dev/null +++ b/authority/admin/db/nosql/admin_test.go @@ -0,0 +1,1121 @@ +package nosql + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/assert" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/db" + "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" + nosqldb "github.com/smallstep/nosql/database" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestDB_getDBAdminBytes(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "ok": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db} + if b, err := db.getDBAdminBytes(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) { + assert.Equals(t, string(b), "foo") + } + } + }) + } +} + +func TestDB_getDBAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted": func(t *testing.T) test { + now := clock.Now() + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + DeletedAt: now, + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "ok": func(t *testing.T) test { + now := clock.Now() + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dba, err := db.getDBAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalDBAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + in []byte + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dba := &dbAdmin{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "fail/authority-mismatch-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: "foo", + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", adminID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + Subject: "max@smallstep.com", + ProvisionerID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if dba, err := db.unmarshalDBAdmin(tc.in, adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + in []byte + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dba := &dbAdmin{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + Subject: "max@smallstep.com", + ProvisionerID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if adm, err := db.unmarshalAdmin(tc.in, adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) + } + } + }) + } +} + +func TestDB_GetAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + DeletedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "fail/authorityID-mismatch": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: "foo", + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", dba.ID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if adm, err := db.GetAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) + } + } + }) + } +} + +func TestDB_DeleteAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, dba.Type) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + assert.True(t, _dba.DeletedAt.Before(time.Now())) + assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, dba.Type) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + assert.True(t, _dba.DeletedAt.Before(time.Now())) + assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.DeleteAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_UpdateAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + adm *linkedca.Admin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + adm: &linkedca.Admin{Id: adminID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + adm: &linkedca.Admin{Id: adminID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + + upd := dba.convert() + upd.Type = linkedca.Admin_ADMIN + + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + adm: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + + upd := dba.convert() + upd.Type = linkedca.Admin_ADMIN + + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + adm: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.UpdateAdmin(context.Background(), tc.adm); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_CreateAdmin(t *testing.T) { + type test struct { + db nosql.DB + err error + adminErr *admin.Error + adm *linkedca.Admin + } + var tests = map[string]func(t *testing.T) test{ + "fail/save-error": func(t *testing.T) test { + adm := &linkedca.Admin{ + AuthorityId: admin.DefaultAuthorityID, + ProvisionerId: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_ADMIN, + } + + return test{ + adm: adm, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, old, nil) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key)) + assert.Equals(t, _dba.AuthorityID, adm.AuthorityId) + assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId) + assert.Equals(t, _dba.Subject, adm.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + + assert.True(t, _dba.CreatedAt.Before(time.Now())) + assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + adm := &linkedca.Admin{ + AuthorityId: admin.DefaultAuthorityID, + ProvisionerId: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_ADMIN, + } + + return test{ + adm: adm, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, old, nil) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key)) + assert.Equals(t, _dba.AuthorityID, adm.AuthorityId) + assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId) + assert.Equals(t, _dba.Subject, adm.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + + assert.True(t, _dba.CreatedAt.Before(time.Now())) + assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.CreateAdmin(context.Background(), tc.adm); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_GetAdmins(t *testing.T) { + now := clock.Now() + fooAdmin := &dbAdmin{ + ID: "foo", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "foo@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + } + foob, err := json.Marshal(fooAdmin) + assert.FatalError(t, err) + + barAdmin := &dbAdmin{ + ID: "bar", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "bar@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + DeletedAt: now, + } + barb, err := json.Marshal(barAdmin) + assert.FatalError(t, err) + + bazAdmin := &dbAdmin{ + ID: "baz", + AuthorityID: "bazzer", + ProvisionerID: "provID", + Subject: "baz@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + } + bazb, err := json.Marshal(bazAdmin) + assert.FatalError(t, err) + + zapAdmin := &dbAdmin{ + ID: "zap", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "zap@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + } + zapb, err := json.Marshal(zapAdmin) + assert.FatalError(t, err) + type test struct { + db nosql.DB + err error + adminErr *admin.Error + verify func(*testing.T, []*linkedca.Admin) + } + var tests = map[string]func(t *testing.T) test{ + "fail/db.List-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admins: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("foo"), Value: foob}, + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("zap"), Value: []byte("zap")}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + err: errors.New("error unmarshaling admin zap into dbAdmin"), + } + }, + "ok/none": func(t *testing.T) test { + ret := []*database.Entry{} + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 0) + }, + } + }, + "ok/only-invalid": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("baz"), Value: bazb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 0) + }, + } + }, + "ok": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("foo"), Value: foob}, + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("baz"), Value: bazb}, + {Bucket: adminsTable, Key: []byte("zap"), Value: zapb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 2) + + assert.Equals(t, admins[0].Id, fooAdmin.ID) + assert.Equals(t, admins[0].AuthorityId, fooAdmin.AuthorityID) + assert.Equals(t, admins[0].ProvisionerId, fooAdmin.ProvisionerID) + assert.Equals(t, admins[0].Subject, fooAdmin.Subject) + assert.Equals(t, admins[0].Type, fooAdmin.Type) + assert.Equals(t, admins[0].CreatedAt, timestamppb.New(fooAdmin.CreatedAt)) + assert.Equals(t, admins[0].DeletedAt, timestamppb.New(fooAdmin.DeletedAt)) + + assert.Equals(t, admins[1].Id, zapAdmin.ID) + assert.Equals(t, admins[1].AuthorityId, zapAdmin.AuthorityID) + assert.Equals(t, admins[1].ProvisionerId, zapAdmin.ProvisionerID) + assert.Equals(t, admins[1].Subject, zapAdmin.Subject) + assert.Equals(t, admins[1].Type, zapAdmin.Type) + assert.Equals(t, admins[1].CreatedAt, timestamppb.New(zapAdmin.CreatedAt)) + assert.Equals(t, admins[1].DeletedAt, timestamppb.New(zapAdmin.DeletedAt)) + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if admins, err := db.GetAdmins(context.Background()); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, admins) + } + } + }) + } +} diff --git a/authority/mgmt/db/nosql/nosql.go b/authority/admin/db/nosql/nosql.go similarity index 81% rename from authority/mgmt/db/nosql/nosql.go rename to authority/admin/db/nosql/nosql.go index 47f79bde..2ce4297c 100644 --- a/authority/mgmt/db/nosql/nosql.go +++ b/authority/admin/db/nosql/nosql.go @@ -11,10 +11,8 @@ import ( ) var ( - authorityAdminsTable = []byte("authority_admins") - authorityConfigsTable = []byte("authority_configs") - authorityProvisionersTable = []byte("authority_provisioners") - authorityProvisionersNameIDIndexTable = []byte("authority_provisioners_name_id_index") + adminsTable = []byte("admins") + provisionersTable = []byte("provisioners") ) // DB is a struct that implements the AcmeDB interface. @@ -25,7 +23,7 @@ type DB struct { // New configures and returns a new Authority DB backend implemented using a nosql DB. func New(db nosqlDB.DB, authorityID string) (*DB, error) { - tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable, authorityProvisionersNameIDIndexTable} + tables := [][]byte{adminsTable, provisionersTable} for _, b := range tables { if err := db.CreateTable(b); err != nil { return nil, errors.Wrapf(err, "error creating table %s", @@ -71,8 +69,6 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface } } -var idLen = 32 - func randID() (val string, err error) { val, err = randutil.UUIDv4() if err != nil { diff --git a/authority/admin/db/nosql/provisioner.go b/authority/admin/db/nosql/provisioner.go new file mode 100644 index 00000000..71d9c8d6 --- /dev/null +++ b/authority/admin/db/nosql/provisioner.go @@ -0,0 +1,211 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/nosql" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" +) + +// dbProvisioner is the database representation of a Provisioner type. +type dbProvisioner struct { + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type linkedca.Provisioner_Type `json:"type"` + Name string `json:"name"` + Claims *linkedca.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template *linkedca.Template `json:"x509Template"` + SSHTemplate *linkedca.Template `json:"sshTemplate"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` +} + +func (dbp *dbProvisioner) clone() *dbProvisioner { + u := *dbp + return &u +} + +func (dbp *dbProvisioner) convert2linkedca() (*linkedca.Provisioner, error) { + details, err := admin.UnmarshalProvisionerDetails(dbp.Type, dbp.Details) + if err != nil { + return nil, err + } + + return &linkedca.Provisioner{ + Id: dbp.ID, + AuthorityId: dbp.AuthorityID, + Type: dbp.Type, + Name: dbp.Name, + Claims: dbp.Claims, + Details: details, + X509Template: dbp.X509Template, + SshTemplate: dbp.SSHTemplate, + CreatedAt: timestamppb.New(dbp.CreatedAt), + DeletedAt: timestamppb.New(dbp.DeletedAt), + }, nil +} + +func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { + data, err := db.db.Get(provisionersTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading provisioner %s", id) + } + return data, nil +} + +func (db *DB) unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) { + var dbp = new(dbProvisioner) + if err := json.Unmarshal(data, dbp); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", id) + } + if !dbp.DeletedAt.IsZero() { + return nil, admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", id) + } + if dbp.AuthorityID != db.authorityID { + return nil, admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", id, db.authorityID) + } + return dbp, nil +} + +func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + dbp, err := db.unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + return dbp, nil +} + +func (db *DB) unmarshalProvisioner(data []byte, id string) (*linkedca.Provisioner, error) { + dbp, err := db.unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + + return dbp.convert2linkedca() +} + +// GetProvisioner retrieves and unmarshals a provisioner from the database. +func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + + prov, err := db.unmarshalProvisioner(data, id) + if err != nil { + return nil, err + } + return prov, nil +} + +// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners +// from the database. +func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { + dbEntries, err := db.db.List(provisionersTable) + if err != nil { + return nil, errors.Wrap(err, "error loading provisioners") + } + var provs []*linkedca.Provisioner + for _, entry := range dbEntries { + prov, err := db.unmarshalProvisioner(entry.Value, string(entry.Key)) + if err != nil { + switch k := err.(type) { + case *admin.Error: + if k.IsType(admin.ErrorDeletedType) || k.IsType(admin.ErrorAuthorityMismatchType) { + continue + } else { + return nil, err + } + default: + return nil, err + } + } + if prov.AuthorityId != db.authorityID { + continue + } + provs = append(provs, prov) + } + return provs, nil +} + +// CreateProvisioner stores a new provisioner to the database. +func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + var err error + prov.Id, err = randID() + if err != nil { + return admin.WrapErrorISE(err, "error generating random id for provisioner") + } + + details, err := json.Marshal(prov.Details.GetData()) + if err != nil { + return admin.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) + } + + dbp := &dbProvisioner{ + ID: prov.Id, + AuthorityID: db.authorityID, + Type: prov.Type, + Name: prov.Name, + Claims: prov.Claims, + Details: details, + X509Template: prov.X509Template, + SSHTemplate: prov.SshTemplate, + CreatedAt: clock.Now(), + } + + if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", provisionersTable); err != nil { + return admin.WrapErrorISE(err, "error creating provisioner %s", prov.Name) + } + + return nil +} + +// UpdateProvisioner saves an updated provisioner to the database. +func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + old, err := db.getDBProvisioner(ctx, prov.Id) + if err != nil { + return err + } + + nu := old.clone() + + if old.Type != prov.Type { + return admin.NewError(admin.ErrorBadRequestType, "cannot update provisioner type") + } + nu.Name = prov.Name + nu.Claims = prov.Claims + nu.Details, err = json.Marshal(prov.Details.GetData()) + if err != nil { + return admin.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name) + } + nu.X509Template = prov.X509Template + nu.SSHTemplate = prov.SshTemplate + + return db.save(ctx, prov.Id, nu, old, "provisioner", provisionersTable) +} + +// DeleteProvisioner saves an updated admin to the database. +func (db *DB) DeleteProvisioner(ctx context.Context, id string) error { + old, err := db.getDBProvisioner(ctx, id) + if err != nil { + return err + } + + nu := old.clone() + nu.DeletedAt = clock.Now() + + return db.save(ctx, old.ID, nu, old, "provisioner", provisionersTable) +} diff --git a/authority/admin/db/nosql/provisioner_test.go b/authority/admin/db/nosql/provisioner_test.go new file mode 100644 index 00000000..95811f26 --- /dev/null +++ b/authority/admin/db/nosql/provisioner_test.go @@ -0,0 +1,1221 @@ +package nosql + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/assert" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/db" + "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" + nosqldb "github.com/smallstep/nosql/database" + "go.step.sm/linkedca" +) + +func TestDB_getDBProvisionerBytes(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "ok": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db} + if b, err := db.getDBProvisionerBytes(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, string(b), "foo") + } + } + }) + } +} + +func TestDB_getDBProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted": func(t *testing.T) test { + now := clock.Now() + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: now, + DeletedAt: now, + } + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "ok": func(t *testing.T) test { + now := clock.Now() + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: now, + } + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dbp, err := db.getDBProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalDBProvisioner(t *testing.T) { + provID := "provID" + type test struct { + in []byte + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + DeletedAt: clock.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", provID), + } + }, + "fail/authority-mismatch-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: "foo", + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", provID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if dbp, err := db.unmarshalDBProvisioner(tc.in, provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.Details, tc.dbp.Details) + assert.Equals(t, dbp.Claims, tc.dbp.Claims) + assert.Equals(t, dbp.X509Template, tc.dbp.X509Template) + assert.Equals(t, dbp.SSHTemplate, tc.dbp.SSHTemplate) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) + } + } + }) + } +} + +func defaultDBP(t *testing.T) *dbProvisioner { + details := &linkedca.ProvisionerDetails_ACME{ + ACME: &linkedca.ACMEProvisioner{ + ForceCn: true, + }, + } + detailBytes, err := json.Marshal(details) + assert.FatalError(t, err) + + return &dbProvisioner{ + ID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_ACME, + Name: "provName", + Details: detailBytes, + Claims: &linkedca.Claims{ + DisableRenewal: true, + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + }, + Ssh: &linkedca.SSHClaims{ + Enabled: true, + UserDurations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + HostDurations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + }, + }, + X509Template: &linkedca.Template{ + Template: []byte("foo"), + Data: []byte("bar"), + }, + SSHTemplate: &linkedca.Template{ + Template: []byte("baz"), + Data: []byte("zap"), + }, + CreatedAt: clock.Now(), + } +} + +func TestDB_unmarshalProvisioner(t *testing.T) { + provID := "provID" + type test struct { + in []byte + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if prov, err := db.unmarshalProvisioner(tc.in, provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) + } + } + }) + } +} + +func TestDB_GetProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.DeletedAt = clock.Now() + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "fail/authorityID-mismatch": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.AuthorityID = "foo" + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", dbp.ID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if prov, err := db.GetProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) + } + } + }) + } +} + +func TestDB_DeleteProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.Equals(t, _dbp.ID, provID) + assert.Equals(t, _dbp.AuthorityID, dbp.AuthorityID) + assert.Equals(t, _dbp.Type, dbp.Type) + assert.Equals(t, _dbp.Name, dbp.Name) + assert.Equals(t, _dbp.Claims, dbp.Claims) + assert.Equals(t, _dbp.X509Template, dbp.X509Template) + assert.Equals(t, _dbp.SSHTemplate, dbp.SSHTemplate) + assert.Equals(t, _dbp.CreatedAt, dbp.CreatedAt) + assert.Equals(t, _dbp.Details, dbp.Details) + + assert.True(t, _dbp.DeletedAt.Before(time.Now())) + assert.True(t, _dbp.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.Equals(t, _dbp.ID, provID) + assert.Equals(t, _dbp.AuthorityID, dbp.AuthorityID) + assert.Equals(t, _dbp.Type, dbp.Type) + assert.Equals(t, _dbp.Name, dbp.Name) + assert.Equals(t, _dbp.Claims, dbp.Claims) + assert.Equals(t, _dbp.X509Template, dbp.X509Template) + assert.Equals(t, _dbp.SSHTemplate, dbp.SSHTemplate) + assert.Equals(t, _dbp.CreatedAt, dbp.CreatedAt) + assert.Equals(t, _dbp.Details, dbp.Details) + + assert.True(t, _dbp.DeletedAt.Before(time.Now())) + assert.True(t, _dbp.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.DeleteProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_GetProvisioners(t *testing.T) { + fooProv := defaultDBP(t) + fooProv.Name = "foo" + foob, err := json.Marshal(fooProv) + assert.FatalError(t, err) + + barProv := defaultDBP(t) + barProv.Name = "bar" + barProv.DeletedAt = clock.Now() + barb, err := json.Marshal(barProv) + assert.FatalError(t, err) + + bazProv := defaultDBP(t) + bazProv.Name = "baz" + bazProv.AuthorityID = "baz" + bazb, err := json.Marshal(bazProv) + assert.FatalError(t, err) + + zapProv := defaultDBP(t) + zapProv.Name = "zap" + zapb, err := json.Marshal(zapProv) + assert.FatalError(t, err) + + type test struct { + db nosql.DB + err error + adminErr *admin.Error + verify func(*testing.T, []*linkedca.Provisioner) + } + var tests = map[string]func(t *testing.T) test{ + "fail/db.List-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioners"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("foo"), Value: foob}, + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("zap"), Value: []byte("zap")}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + err: errors.New("error unmarshaling provisioner zap into dbProvisioner"), + } + }, + "ok/none": func(t *testing.T) test { + ret := []*database.Entry{} + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 0) + }, + } + }, + "ok/only-invalid": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("baz"), Value: bazb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 0) + }, + } + }, + "ok": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("foo"), Value: foob}, + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("baz"), Value: bazb}, + {Bucket: provisionersTable, Key: []byte("zap"), Value: zapb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 2) + + assert.Equals(t, provs[0].Id, fooProv.ID) + assert.Equals(t, provs[0].AuthorityId, fooProv.AuthorityID) + assert.Equals(t, provs[0].Type, fooProv.Type) + assert.Equals(t, provs[0].Name, fooProv.Name) + assert.Equals(t, provs[0].Claims, fooProv.Claims) + assert.Equals(t, provs[0].X509Template, fooProv.X509Template) + assert.Equals(t, provs[0].SshTemplate, fooProv.SSHTemplate) + + retDetailsBytes, err := json.Marshal(provs[0].Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, fooProv.Details) + + assert.Equals(t, provs[1].Id, zapProv.ID) + assert.Equals(t, provs[1].AuthorityId, zapProv.AuthorityID) + assert.Equals(t, provs[1].Type, zapProv.Type) + assert.Equals(t, provs[1].Name, zapProv.Name) + assert.Equals(t, provs[1].Claims, zapProv.Claims) + assert.Equals(t, provs[1].X509Template, zapProv.X509Template) + assert.Equals(t, provs[1].SshTemplate, zapProv.SSHTemplate) + + retDetailsBytes, err = json.Marshal(provs[1].Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, zapProv.Details) + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if provs, err := db.GetProvisioners(context.Background()); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, provs) + } + } + }) + } +} + +func TestDB_CreateProvisioner(t *testing.T) { + type test struct { + db nosql.DB + err error + adminErr *admin.Error + prov *linkedca.Provisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, old, nil) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + adminErr: admin.NewErrorISE("error creating provisioner provName: error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, old, nil) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.CreateProvisioner(context.Background(), tc.prov); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_UpdateProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + prov *linkedca.Provisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/update-deleted": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.DeletedAt = clock.Now() + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", provID), + } + }, + "fail/update-type-error": func(t *testing.T) test { + dbp := defaultDBP(t) + + upd, err := dbp.convert2linkedca() + assert.FatalError(t, err) + upd.Type = linkedca.Provisioner_JWK + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + }, + adminErr: admin.NewError(admin.ErrorBadRequestType, "cannot update provisioner type"), + } + }, + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + prov.Name = "new-name" + prov.Claims = &linkedca.Claims{ + DisableRenewal: true, + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Min: "10m", + Max: "8h", + Default: "4h", + }, + }, + Ssh: &linkedca.SSHClaims{ + Enabled: true, + UserDurations: &linkedca.Durations{ + Min: "7m", + Max: "11h", + Default: "5h", + }, + HostDurations: &linkedca.Durations{ + Min: "4m", + Max: "24h", + Default: "24h", + }, + }, + } + prov.X509Template = &linkedca.Template{ + Template: []byte("x"), + Data: []byte("y"), + } + prov.SshTemplate = &linkedca.Template{ + Template: []byte("z"), + Data: []byte("w"), + } + prov.Details = &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_ACME{ + ACME: &linkedca.ACMEProvisioner{ + ForceCn: false, + }, + }, + } + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.UpdateProvisioner(context.Background(), tc.prov); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} diff --git a/authority/mgmt/errors.go b/authority/admin/errors.go similarity index 88% rename from authority/mgmt/errors.go rename to authority/admin/errors.go index 63c0652e..81ae6d6f 100644 --- a/authority/mgmt/errors.go +++ b/authority/admin/errors.go @@ -1,4 +1,4 @@ -package mgmt +package admin import ( "encoding/json" @@ -25,6 +25,10 @@ const ( ErrorDeletedType // ErrorBadRequestType bad request. ErrorBadRequestType + // ErrorNotImplementedType not implemented. + ErrorNotImplementedType + // ErrorUnauthorizedType internal server error. + ErrorUnauthorizedType // ErrorServerInternalType internal server error. ErrorServerInternalType ) @@ -41,6 +45,10 @@ func (ap ProblemType) String() string { return "deleted" case ErrorBadRequestType: return "badRequest" + case ErrorNotImplementedType: + return "notImplemented" + case ErrorUnauthorizedType: + return "unauthorized" case ErrorServerInternalType: return "internalServerError" default: @@ -75,12 +83,22 @@ var ( ErrorDeletedType: { typ: ErrorDeletedType.String(), details: "resource is deleted", - status: 403, + status: http.StatusUnauthorized, + }, + ErrorNotImplementedType: { + typ: ErrorNotImplementedType.String(), + details: "not implemented", + status: http.StatusNotImplemented, }, ErrorBadRequestType: { typ: ErrorBadRequestType.String(), details: "bad request", - status: 400, + status: http.StatusBadRequest, + }, + ErrorUnauthorizedType: { + typ: ErrorUnauthorizedType.String(), + details: "unauthorized", + status: http.StatusUnauthorized, }, ErrorServerInternalType: errorServerInternalMetadata, } @@ -199,7 +217,6 @@ func WriteError(w http.ResponseWriter, err *Error) { } } - fmt.Printf("err = %+v\n", err) if err := json.NewEncoder(w).Encode(err); err != nil { log.Println(err) } diff --git a/authority/administrator/collection.go b/authority/administrator/collection.go new file mode 100644 index 00000000..47d3a35d --- /dev/null +++ b/authority/administrator/collection.go @@ -0,0 +1,243 @@ +package administrator + +import ( + "sort" + "sync" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/linkedca" +) + +// DefaultAdminLimit is the default limit for listing provisioners. +const DefaultAdminLimit = 20 + +// DefaultAdminMax is the maximum limit for listing provisioners. +const DefaultAdminMax = 100 + +type adminSlice []*linkedca.Admin + +func (p adminSlice) Len() int { return len(p) } +func (p adminSlice) Less(i, j int) bool { return p[i].Id < p[j].Id } +func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +// Collection is a memory map of admins. +type Collection struct { + byID *sync.Map + bySubProv *sync.Map + byProv *sync.Map + sorted adminSlice + provisioners *provisioner.Collection + superCount int + superCountByProvisioner map[string]int +} + +// NewCollection initializes a collection of provisioners. The given list of +// audiences are the audiences used by the JWT provisioner. +func NewCollection(provisioners *provisioner.Collection) *Collection { + return &Collection{ + byID: new(sync.Map), + byProv: new(sync.Map), + bySubProv: new(sync.Map), + superCountByProvisioner: map[string]int{}, + provisioners: provisioners, + } +} + +// LoadByID a admin by the ID. +func (c *Collection) LoadByID(id string) (*linkedca.Admin, bool) { + return loadAdmin(c.byID, id) +} + +type subProv struct { + subject string + provisioner string +} + +func newSubProv(subject, provisioner string) subProv { + return subProv{subject, provisioner} +} + +// LoadBySubProv a admin by the subject and provisioner name. +func (c *Collection) LoadBySubProv(sub, provName string) (*linkedca.Admin, bool) { + return loadAdmin(c.bySubProv, newSubProv(sub, provName)) +} + +// LoadByProvisioner a admin by the subject and provisioner name. +func (c *Collection) LoadByProvisioner(provName string) ([]*linkedca.Admin, bool) { + val, ok := c.byProv.Load(provName) + if !ok { + return nil, false + } + admins, ok := val.([]*linkedca.Admin) + if !ok { + return nil, false + } + return admins, true +} + +// Store adds an admin to the collection and enforces the uniqueness of +// admin IDs and amdin subject <-> provisioner name combos. +func (c *Collection) Store(adm *linkedca.Admin, prov provisioner.Interface) error { + // Input validation. + if adm.ProvisionerId != prov.GetID() { + return admin.NewErrorISE("admin.provisionerId does not match provisioner argument") + } + + // Store admin always in byID. ID must be unique. + if _, loaded := c.byID.LoadOrStore(adm.Id, adm); loaded { + return errors.New("cannot add multiple admins with the same id") + } + + provName := prov.GetName() + // Store admin always in bySubProv. Subject <-> ProvisionerName must be unique. + if _, loaded := c.bySubProv.LoadOrStore(newSubProv(adm.Subject, provName), adm); loaded { + c.byID.Delete(adm.Id) + return errors.New("cannot add multiple admins with the same subject and provisioner") + } + + var isSuper = (adm.Type == linkedca.Admin_SUPER_ADMIN) + if admins, ok := c.LoadByProvisioner(provName); ok { + c.byProv.Store(provName, append(admins, adm)) + if isSuper { + c.superCountByProvisioner[provName]++ + } + } else { + c.byProv.Store(provName, []*linkedca.Admin{adm}) + if isSuper { + c.superCountByProvisioner[provName] = 1 + } + } + if isSuper { + c.superCount++ + } + + c.sorted = append(c.sorted, adm) + sort.Sort(c.sorted) + + return nil +} + +// Remove deletes an admin from all associated collections and lists. +func (c *Collection) Remove(id string) error { + adm, ok := c.LoadByID(id) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id) + } + if adm.Type == linkedca.Admin_SUPER_ADMIN && c.SuperCount() == 1 { + return admin.NewError(admin.ErrorBadRequestType, "cannot remove the last super admin") + } + prov, ok := c.provisioners.Load(adm.ProvisionerId) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, + "provisioner %s for admin %s not found", adm.ProvisionerId, id) + } + provName := prov.GetName() + adminsByProv, ok := c.LoadByProvisioner(provName) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, + "admins not found for provisioner %s", provName) + } + + // Find index in sorted list. + sortedIndex := sort.Search(c.sorted.Len(), func(i int) bool { return c.sorted[i].Id >= adm.Id }) + if c.sorted[sortedIndex].Id != adm.Id { + return admin.NewError(admin.ErrorNotFoundType, + "admin %s not found in sorted list", adm.Id) + } + + var found bool + for i, a := range adminsByProv { + if a.Id == adm.Id { + // Remove admin from list. https://stackoverflow.com/questions/37334119/how-to-delete-an-element-from-a-slice-in-golang + // Order does not matter. + adminsByProv[i] = adminsByProv[len(adminsByProv)-1] + c.byProv.Store(provName, adminsByProv[:len(adminsByProv)-1]) + found = true + } + } + if !found { + return admin.NewError(admin.ErrorNotFoundType, + "admin %s not found in adminsByProvisioner list", adm.Id) + } + + // Remove index in sorted list + copy(c.sorted[sortedIndex:], c.sorted[sortedIndex+1:]) // Shift a[i+1:] left one index. + c.sorted[len(c.sorted)-1] = nil // Erase last element (write zero value). + c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. + + c.byID.Delete(adm.Id) + c.bySubProv.Delete(newSubProv(adm.Subject, provName)) + + if adm.Type == linkedca.Admin_SUPER_ADMIN { + c.superCount-- + c.superCountByProvisioner[provName]-- + } + return nil +} + +// Update updates the given admin in all related lists and collections. +func (c *Collection) Update(id string, nu *linkedca.Admin) (*linkedca.Admin, error) { + adm, ok := c.LoadByID(id) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", adm.Id) + } + if adm.Type == nu.Type { + return nil, admin.NewError(admin.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type) + } + if adm.Type == linkedca.Admin_SUPER_ADMIN && c.SuperCount() == 1 { + return nil, admin.NewError(admin.ErrorBadRequestType, "cannot change role of last super admin") + } + + adm.Type = nu.Type + return adm, nil +} + +// SuperCount returns the total number of admins. +func (c *Collection) SuperCount() int { + return c.superCount +} + +// SuperCountByProvisioner returns the total number of admins. +func (c *Collection) SuperCountByProvisioner(provName string) int { + if cnt, ok := c.superCountByProvisioner[provName]; ok { + return cnt + } + return 0 +} + +// Find implements pagination on a list of sorted admins. +func (c *Collection) Find(cursor string, limit int) ([]*linkedca.Admin, string) { + switch { + case limit <= 0: + limit = DefaultAdminLimit + case limit > DefaultAdminMax: + limit = DefaultAdminMax + } + + n := c.sorted.Len() + i := sort.Search(n, func(i int) bool { return c.sorted[i].Id >= cursor }) + + slice := []*linkedca.Admin{} + for ; i < n && len(slice) < limit; i++ { + slice = append(slice, c.sorted[i]) + } + + if i < n { + return slice, c.sorted[i].Id + } + return slice, "" +} + +func loadAdmin(m *sync.Map, key interface{}) (*linkedca.Admin, bool) { + val, ok := m.Load(key) + if !ok { + return nil, false + } + adm, ok := val.(*linkedca.Admin) + if !ok { + return nil, false + } + return adm, true +} diff --git a/authority/admins.go b/authority/admins.go new file mode 100644 index 00000000..dcaf9b49 --- /dev/null +++ b/authority/admins.go @@ -0,0 +1,97 @@ +package authority + +import ( + "context" + + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/linkedca" +) + +// LoadAdminByID returns an *linkedca.Admin with the given ID. +func (a *Authority) LoadAdminByID(id string) (*linkedca.Admin, bool) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + return a.admins.LoadByID(id) +} + +// LoadAdminBySubProv returns an *linkedca.Admin with the given ID. +func (a *Authority) LoadAdminBySubProv(subject, provisioner string) (*linkedca.Admin, bool) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + return a.admins.LoadBySubProv(subject, provisioner) +} + +// GetAdmins returns a map listing each provisioner and the JWK Key Set +// with their public keys. +func (a *Authority) GetAdmins(cursor string, limit int) ([]*linkedca.Admin, string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + admins, nextCursor := a.admins.Find(cursor, limit) + return admins, nextCursor, nil +} + +// StoreAdmin stores an *linkedca.Admin to the authority. +func (a *Authority) StoreAdmin(ctx context.Context, adm *linkedca.Admin, prov provisioner.Interface) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + if adm.ProvisionerId != prov.GetID() { + return admin.NewErrorISE("admin.provisionerId does not match provisioner argument") + } + + if _, ok := a.admins.LoadBySubProv(adm.Subject, prov.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "admin with subject %s and provisioner %s already exists", adm.Subject, prov.GetName()) + } + // Store to database -- this will set the ID. + if err := a.adminDB.CreateAdmin(ctx, adm); err != nil { + return admin.WrapErrorISE(err, "error creating admin") + } + if err := a.admins.Store(adm, prov); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed admin store") + } + return admin.WrapErrorISE(err, "error storing admin in authority cache") + } + return nil +} + +// UpdateAdmin stores an *linkedca.Admin to the authority. +func (a *Authority) UpdateAdmin(ctx context.Context, id string, nu *linkedca.Admin) (*linkedca.Admin, error) { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + adm, err := a.admins.Update(id, nu) + if err != nil { + return nil, admin.WrapErrorISE(err, "error updating cached admin %s", id) + } + if err := a.adminDB.UpdateAdmin(ctx, adm); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return nil, admin.WrapErrorISE(err, "error reloading admin resources on failed admin update") + } + return nil, admin.WrapErrorISE(err, "error updating admin %s", id) + } + return adm, nil +} + +// RemoveAdmin removes an *linkedca.Admin from the authority. +func (a *Authority) RemoveAdmin(ctx context.Context, id string) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + return a.removeAdmin(ctx, id) +} + +// removeAdmin helper that assumes lock. +func (a *Authority) removeAdmin(ctx context.Context, id string) error { + if err := a.admins.Remove(id); err != nil { + return admin.WrapErrorISE(err, "error removing admin %s from authority cache", id) + } + if err := a.adminDB.DeleteAdmin(ctx, id); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed admin remove") + } + return admin.WrapErrorISE(err, "error deleting admin %s", id) + } + return nil +} diff --git a/authority/authority.go b/authority/authority.go index b5924061..eec0c3e0 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -11,14 +11,14 @@ import ( "time" "github.com/smallstep/certificates/cas" - "github.com/smallstep/certificates/linkedca" "github.com/smallstep/certificates/scep" + "go.step.sm/linkedca" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" + adminDBNosql "github.com/smallstep/certificates/authority/admin/db/nosql" + "github.com/smallstep/certificates/authority/administrator" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" - authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql" "github.com/smallstep/certificates/authority/provisioner" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" @@ -34,16 +34,17 @@ import ( // Authority implements the Certificate Authority internal interface. type Authority struct { config *config.Config - adminDB mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection - admins *admin.Collection + admins *administrator.Collection db db.AuthDB + adminDB admin.DB templates *templates.Templates // X509 CA x509CAService cas.CertificateAuthorityService rootX509Certs []*x509.Certificate + rootX509CertPool *x509.CertPool federatedX509Certs []*x509.Certificate certificates *sync.Map @@ -67,6 +68,8 @@ type Authority struct { sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error) sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) getIdentityFunc provisioner.GetIdentityFunc + + adminMutex sync.RWMutex } // New creates and initiates a new Authority type. @@ -134,62 +137,62 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } -// ReloadAuthConfig reloads dynamic fields of the AuthConfig. -func (a *Authority) ReloadAuthConfig(ctx context.Context) error { - provs, err := a.adminDB.GetProvisioners(ctx) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") - } - a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(ctx) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") +// reloadAdminResources reloads admins and provisioners from the DB. +func (a *Authority) reloadAdminResources(ctx context.Context) error { + var ( + provList provisioner.List + adminList []*linkedca.Admin + ) + if a.config.AuthorityConfig.EnableAdmin { + provs, err := a.adminDB.GetProvisioners(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error getting provisioners to initialize authority") + } + provList, err = provisionerListToCertificates(provs) + if err != nil { + return admin.WrapErrorISE(err, "error converting provisioner list to certificates") + } + adminList, err = a.adminDB.GetAdmins(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error getting admins to initialize authority") + } + } else { + provList = a.config.AuthorityConfig.Provisioners + adminList = a.config.AuthorityConfig.Admins } - // Merge global and configuration claims - claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) + provisionerConfig, err := a.generateProvisionerConfig(ctx) if err != nil { - return err - } - // TODO: should we also be combining the ssh federated roots here? - // If we rotate ssh roots keys, sshpop provisioner will lose ability to - // validate old SSH certificates, unless they are added as federated certs. - sshKeys, err := a.GetSSHRoots(ctx) - if err != nil { - return err - } - // Initialize provisioners - audiences := a.config.GetAudiences() - a.provisioners = provisioner.NewCollection(audiences) - config := provisioner.Config{ - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, - SSHKeys: &provisioner.SSHKeys{ - UserKeys: sshKeys.UserKeys, - HostKeys: sshKeys.HostKeys, - }, - GetIdentityFunc: a.getIdentityFunc, + return admin.WrapErrorISE(err, "error generating provisioner config") } - // Store all the provisioners - for _, p := range a.config.AuthorityConfig.Provisioners { - if err := p.Init(config); err != nil { + + // Create provisioner collection. + provClxn := provisioner.NewCollection(provisionerConfig.Audiences) + for _, p := range provList { + if err := p.Init(*provisionerConfig); err != nil { return err } - if err := a.provisioners.Store(p); err != nil { + if err := provClxn.Store(p); err != nil { return err } } - // Store all the admins - a.admins = admin.NewCollection(a.provisioners) - for _, adm := range a.config.AuthorityConfig.Admins { - if err := a.admins.Store(adm); err != nil { + // Create admin collection. + adminClxn := administrator.NewCollection(provClxn) + for _, adm := range adminList { + p, ok := provClxn.Load(adm.ProvisionerId) + if !ok { + return admin.NewErrorISE("provisioner %s not found when loading admin %s", + adm.ProvisionerId, adm.Id) + } + if err := adminClxn.Store(adm, p); err != nil { return err } } + + a.config.AuthorityConfig.Provisioners = provList + a.provisioners = provClxn + a.config.AuthorityConfig.Admins = adminList + a.admins = adminClxn return nil } @@ -210,56 +213,6 @@ func (a *Authority) init() error { } } - if len(a.config.AuthorityConfig.Provisioners) == 0 { - // Initialize step-ca Admin Database if it's not already initialized using - // WithAdminDB. - if a.adminDB == nil { - // Check if AuthConfig already exists - a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - } - - provs, err := a.adminDB.GetProvisioners(context.Background()) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - if len(provs) == 0 { - // Create First Provisioner - prov, err := mgmt.CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) - if err != nil { - return mgmt.WrapErrorISE(err, "error creating first provisioner") - } - certProv, err := provisionerToCertificates(prov) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner to certificates type") - } - a.config.AuthorityConfig.Provisioners = []provisioner.Interface{certProv} - - // Create First Admin - adm := &linkedca.Admin{ - ProvisionerId: prov.Id, - Subject: "step", - Type: linkedca.Admin_SUPER_ADMIN, - } - if err := a.adminDB.CreateAdmin(context.Background(), adm); err != nil { - // TODO should we try to clean up? - return mgmt.WrapErrorISE(err, "error creating first admin") - } - a.config.AuthorityConfig.Admins = []*linkedca.Admin{adm} - } else { - a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates type") - } - a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(context.Background()) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - } - } - // Initialize key manager if it has not been set in the options. if a.keyManager == nil { var options kmsapi.Options @@ -329,6 +282,11 @@ func (a *Authority) init() error { a.certificates.Store(hex.EncodeToString(sum[:]), crt) } + a.rootX509CertPool = x509.NewCertPool() + for _, cert := range a.rootX509Certs { + a.rootX509CertPool.AddCert(cert) + } + // Read federated certificates and store them in the certificates map. if len(a.federatedX509Certs) == 0 { a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots)) @@ -430,32 +388,6 @@ func (a *Authority) init() error { tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } - // Merge global and configuration claims - claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) - if err != nil { - return err - } - // TODO: should we also be combining the ssh federated roots here? - // If we rotate ssh roots keys, sshpop provisioner will lose ability to - // validate old SSH certificates, unless they are added as federated certs. - sshKeys, err := a.GetSSHRoots(context.Background()) - if err != nil { - return err - } - // Initialize provisioners - audiences := a.config.GetAudiences() - a.provisioners = provisioner.NewCollection(audiences) - config := provisioner.Config{ - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, - SSHKeys: &provisioner.SSHKeys{ - UserKeys: sshKeys.UserKeys, - HostKeys: sshKeys.HostKeys, - }, - GetIdentityFunc: a.getIdentityFunc, - } - // Check if a KMS with decryption capability is required and available if a.requiresDecrypter() { if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { @@ -499,23 +431,44 @@ func (a *Authority) init() error { // TODO: mimick the x509CAService GetCertificateAuthority here too? } - // Store all the provisioners - for _, p := range a.config.AuthorityConfig.Provisioners { - if err := p.Init(config); err != nil { - return err + if a.config.AuthorityConfig.EnableAdmin { + // Initialize step-ca Admin Database if it's not already initialized using + // WithAdminDB. + if a.adminDB == nil { + // Check if AuthConfig already exists + a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) + if err != nil { + return err + } } - if err := a.provisioners.Store(p); err != nil { - return err + + provs, err := a.adminDB.GetProvisioners(context.Background()) + if err != nil { + return admin.WrapErrorISE(err, "error loading provisioners to initialize authority") } - } - // Store all the admins - a.admins = admin.NewCollection(a.provisioners) - for _, adm := range a.config.AuthorityConfig.Admins { - if err := a.admins.Store(adm); err != nil { - return err + if len(provs) == 0 { + // Create First Provisioner + prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) + if err != nil { + return admin.WrapErrorISE(err, "error creating first provisioner") + } + + // Create first admin + if err := a.adminDB.CreateAdmin(context.Background(), &linkedca.Admin{ + ProvisionerId: prov.Id, + Subject: "step", + Type: linkedca.Admin_SUPER_ADMIN, + }); err != nil { + return admin.WrapErrorISE(err, "error creating first admin") + } } } + // Load Provisioners and Admins + if err := a.reloadAdminResources(context.Background()); err != nil { + return err + } + // Configure templates, currently only ssh templates are supported. if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { a.templates = a.config.Templates @@ -543,21 +496,11 @@ func (a *Authority) GetDatabase() db.AuthDB { return a.db } -// GetAdminDatabase returns the mgmt database, if one exists. -func (a *Authority) GetAdminDatabase() mgmt.DB { +// GetAdminDatabase returns the admin database, if one exists. +func (a *Authority) GetAdminDatabase() admin.DB { return a.adminDB } -// GetAdminCollection returns the admin collection. -func (a *Authority) GetAdminCollection() *admin.Collection { - return a.admins -} - -// GetProvisionerCollection returns the admin collection. -func (a *Authority) GetProvisionerCollection() *provisioner.Collection { - return a.provisioners -} - // Shutdown safely shuts down any clients, databases, etc. held by the Authority. func (a *Authority) Shutdown() error { if err := a.keyManager.Close(); err != nil { diff --git a/authority/authority_test.go b/authority/authority_test.go index 618e7939..7604ec6b 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -454,8 +454,6 @@ func TestAuthority_GetSCEPService(t *testing.T) { // getIdentityFunc: tt.fields.getIdentityFunc, // } a, err := New(tt.fields.config) - fmt.Println(err) - fmt.Println(a) if (err != nil) != tt.wantErr { t.Errorf("Authority.New(), error = %v, wantErr %v", err, tt.wantErr) return diff --git a/authority/authorize.go b/authority/authorize.go index f84bd6f5..8d1f878a 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -7,10 +7,13 @@ import ( "encoding/hex" "net/http" "strings" + "time" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" + "go.step.sm/linkedca" "golang.org/x/crypto/ssh" ) @@ -73,25 +76,121 @@ func (a *Authority) authorizeToken(ctx context.Context, token string) (provision // Store the token to protect against reuse unless it's skipped. // If we cannot get a token id from the provisioner, just hash the token. if !SkipTokenReuseFromContext(ctx) { - if reuseKey, err := p.GetTokenID(token); err == nil { - if reuseKey == "" { - sum := sha256.Sum256([]byte(token)) - reuseKey = strings.ToLower(hex.EncodeToString(sum[:])) - } - ok, err := a.db.UseToken(reuseKey, token) - if err != nil { - return nil, errs.Wrap(http.StatusInternalServerError, err, - "authority.authorizeToken: failed when attempting to store token") - } - if !ok { - return nil, errs.Unauthorized("authority.authorizeToken: token already used") - } + if err = a.UseToken(token, p); err != nil { + return nil, err } } return p, nil } +// AuthorizeAdminToken authorize an Admin token. +func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedca.Admin, error) { + jwt, err := jose.ParseSigned(token) + if err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error parsing x5c token") + } + + verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{ + Roots: a.rootX509CertPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }) + if err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, + "adminHandler.authorizeToken; error verifying x5c certificate chain in token") + } + leaf := verifiedChains[0][0] + + if leaf.KeyUsage&x509.KeyUsageDigitalSignature == 0 { + return nil, admin.NewError(admin.ErrorUnauthorizedType, "adminHandler.authorizeToken; certificate used to sign x5c token cannot be used for digital signature") + } + + // Using the leaf certificates key to validate the claims accomplishes two + // things: + // 1. Asserts that the private key used to sign the token corresponds + // to the public certificate in the `x5c` header of the token. + // 2. Asserts that the claims are valid - have not been tampered with. + var claims jose.Claims + if err = jwt.Claims(leaf.PublicKey, &claims); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error parsing x5c claims") + } + + prov, err := a.LoadProvisionerByCertificate(leaf) + if err != nil { + return nil, err + } + + // Check that the token has not been used. + if err = a.UseToken(token, prov); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error with reuse token") + } + + // According to "rfc7519 JSON Web Token" acceptable skew should be no + // more than a few minutes. + if err = claims.ValidateWithLeeway(jose.Expected{ + Issuer: prov.GetName(), + Time: time.Now().UTC(), + }, time.Minute); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "x5c.authorizeToken; invalid x5c claims") + } + + // validate audience: path matches the current path + if r.URL.Path != claims.Audience[0] { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "x5c.authorizeToken; x5c token has invalid audience "+ + "claim (aud); expected %s, but got %s", r.URL.Path, claims.Audience) + } + + if claims.Subject == "" { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "x5c.authorizeToken; x5c token subject cannot be empty") + } + + var ( + ok bool + adm *linkedca.Admin + ) + adminFound := false + adminSANs := append([]string{leaf.Subject.CommonName}, leaf.DNSNames...) + adminSANs = append(adminSANs, leaf.EmailAddresses...) + for _, san := range adminSANs { + if adm, ok = a.LoadAdminBySubProv(san, claims.Issuer); ok { + adminFound = true + break + } + } + if !adminFound { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "adminHandler.authorizeToken; unable to load admin with subject(s) %s and provisioner '%s'", + adminSANs, claims.Issuer) + } + + if strings.HasPrefix(r.URL.Path, "/admin/admins") && (r.Method != "GET") && adm.Type != linkedca.Admin_SUPER_ADMIN { + return nil, admin.NewError(admin.ErrorUnauthorizedType, "must have super admin access to make this request") + } + + return adm, nil +} + +// UseToken stores the token to protect against reuse. +func (a *Authority) UseToken(token string, prov provisioner.Interface) error { + if reuseKey, err := prov.GetTokenID(token); err == nil { + if reuseKey == "" { + sum := sha256.Sum256([]byte(token)) + reuseKey = strings.ToLower(hex.EncodeToString(sum[:])) + } + ok, err := a.db.UseToken(reuseKey, token) + if err != nil { + return errs.Wrap(http.StatusInternalServerError, err, + "authority.authorizeToken: failed when attempting to store token") + } + if !ok { + return errs.Unauthorized("authority.authorizeToken: token already used") + } + } + return nil +} + // Authorize grabs the method from the context and authorizes the request by // validating the one-time-token. func (a *Authority) Authorize(ctx context.Context, token string) ([]provisioner.SignOption, error) { diff --git a/authority/authorize_test.go b/authority/authorize_test.go index f20e2976..f308ec28 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -822,7 +822,7 @@ func TestAuthority_authorizeRenew(t *testing.T) { return &authorizeTest{ auth: a, cert: renewDisabledCrt, - err: errors.New("authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner renew_disabled:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'renew_disabled'"), code: http.StatusUnauthorized, } }, diff --git a/authority/config.go b/authority/config.go index 79bdc7ff..744ca5e7 100644 --- a/authority/config.go +++ b/authority/config.go @@ -5,17 +5,26 @@ import "github.com/smallstep/certificates/authority/config" // Config is an alias to support older APIs. type Config = config.Config +// LoadConfiguration is an alias to support older APIs. +var LoadConfiguration = config.LoadConfiguration + // AuthConfig is an alias to support older APIs. type AuthConfig = config.AuthConfig +// TLS + // ASN1DN is an alias to support older APIs. type ASN1DN = config.ASN1DN -// TLS +// DefaultTLSOptions is an alias to support older APIs. +var DefaultTLSOptions = config.DefaultTLSOptions // TLSOptions is an alias to support older APIs. type TLSOptions = config.TLSOptions +// CipherSuites is an alias to support older APIs. +type CipherSuites = config.CipherSuites + // SSH // SSHConfig is an alias to support older APIs. diff --git a/authority/config/config.go b/authority/config/config.go index 55041142..9ad1ff5f 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -12,8 +12,8 @@ import ( cas "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" kms "github.com/smallstep/certificates/kms/apiv1" - "github.com/smallstep/certificates/linkedca" "github.com/smallstep/certificates/templates" + "go.step.sm/linkedca" ) const ( @@ -21,18 +21,6 @@ const ( ) var ( - // DefaultTLSOptions represents the default TLS version as well as the cipher - // suites used in the TLS certificates. - DefaultTLSOptions = TLSOptions{ - CipherSuites: CipherSuites{ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - }, - MinVersion: 1.2, - MaxVersion: 1.2, - Renegotiation: false, - } // DefaultBackdate length of time to backdate certificates to avoid // clock skew validation issues. DefaultBackdate = time.Minute @@ -41,7 +29,7 @@ var ( // DefaultEnableSSHCA enable SSH CA features per provisioner or globally // for all provisioners. DefaultEnableSSHCA = false - // GlobalProvisionerClaims default claims for the Authority. Can be overriden + // GlobalProvisionerClaims default claims for the Authority. Can be overridden // by provisioner specific claims. GlobalProvisionerClaims = provisioner.Claims{ MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs @@ -65,6 +53,7 @@ type Config struct { IntermediateCert string `json:"crt"` IntermediateKey string `json:"key"` Address string `json:"address"` + InsecureAddress string `json:"insecureAddress"` DNSNames []string `json:"dnsNames"` KMS *kms.Options `json:"kms,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"` @@ -80,13 +69,13 @@ type Config struct { // ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer // x509 Certificate blocks. type ASN1DN struct { - Country string `json:"country,omitempty" step:"country"` - Organization string `json:"organization,omitempty" step:"organization"` - OrganizationalUnit string `json:"organizationalUnit,omitempty" step:"organizationalUnit"` - Locality string `json:"locality,omitempty" step:"locality"` - Province string `json:"province,omitempty" step:"province"` - StreetAddress string `json:"streetAddress,omitempty" step:"streetAddress"` - CommonName string `json:"commonName,omitempty" step:"commonName"` + Country string `json:"country,omitempty"` + Organization string `json:"organization,omitempty"` + OrganizationalUnit string `json:"organizationalUnit,omitempty"` + Locality string `json:"locality,omitempty"` + Province string `json:"province,omitempty"` + StreetAddress string `json:"streetAddress,omitempty"` + CommonName string `json:"commonName,omitempty"` } // AuthConfig represents the configuration options for the authority. An @@ -101,6 +90,7 @@ type AuthConfig struct { Claims *provisioner.Claims `json:"claims,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` Backdate *provisioner.Duration `json:"backdate,omitempty"` + EnableAdmin bool `json:"enableAdmin,omitempty"` } // init initializes the required fields in the AuthConfig if they are not diff --git a/authority/config/config_test.go b/authority/config/config_test.go index 735ac33e..a5b60513 100644 --- a/authority/config/config_test.go +++ b/authority/config/config_test.go @@ -8,12 +8,14 @@ import ( "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" + + _ "github.com/smallstep/certificates/cas" ) func TestConfigValidate(t *testing.T) { - maxjwk, err := jose.ReadKey("testdata/secrets/max_pub.jwk") + maxjwk, err := jose.ReadKey("../testdata/secrets/max_pub.jwk") assert.FatalError(t, err) - clijwk, err := jose.ReadKey("testdata/secrets/step_cli_key_pub.jwk") + clijwk, err := jose.ReadKey("../testdata/secrets/step_cli_key_pub.jwk") assert.FatalError(t, err) ac := &AuthConfig{ Provisioners: provisioner.List{ @@ -39,9 +41,9 @@ func TestConfigValidate(t *testing.T) { "empty-address": func(t *testing.T) ConfigValidateTest { return ConfigValidateTest{ config: &Config{ - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -53,9 +55,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -67,8 +69,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -80,8 +82,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -93,8 +95,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -106,9 +108,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", Password: "pass", AuthorityConfig: ac, }, @@ -119,9 +121,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -133,9 +135,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -148,9 +150,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -177,9 +179,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -207,6 +209,8 @@ func TestConfigValidate(t *testing.T) { } } else { if assert.Nil(t, tc.err) { + fmt.Printf("tc.tls = %+v\n", tc.tls) + fmt.Printf("*tc.config.TLS = %+v\n", *tc.config.TLS) assert.Equals(t, *tc.config.TLS, tc.tls) } } @@ -224,9 +228,9 @@ func TestAuthConfigValidate(t *testing.T) { CommonName: "test", } - maxjwk, err := jose.ReadKey("testdata/secrets/max_pub.jwk") + maxjwk, err := jose.ReadKey("../testdata/secrets/max_pub.jwk") assert.FatalError(t, err) - clijwk, err := jose.ReadKey("testdata/secrets/step_cli_key_pub.jwk") + clijwk, err := jose.ReadKey("../testdata/secrets/step_cli_key_pub.jwk") assert.FatalError(t, err) p := provisioner.List{ &provisioner.JWK{ diff --git a/authority/config/ssh_test.go b/authority/config/ssh_test.go new file mode 100644 index 00000000..2c4c8eac --- /dev/null +++ b/authority/config/ssh_test.go @@ -0,0 +1,73 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/smallstep/assert" + "go.step.sm/crypto/jose" + "golang.org/x/crypto/ssh" +) + +func TestSSHPublicKey_Validate(t *testing.T) { + key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) + assert.FatalError(t, err) + + type fields struct { + Type string + Federated bool + Key jose.JSONWebKey + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {"user", fields{"user", true, key.Public()}, false}, + {"host", fields{"host", false, key.Public()}, false}, + {"empty", fields{"", true, key.Public()}, true}, + {"badType", fields{"bad", false, key.Public()}, true}, + {"badKey", fields{"user", false, *key}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &SSHPublicKey{ + Type: tt.fields.Type, + Federated: tt.fields.Federated, + Key: tt.fields.Key, + } + if err := k.Validate(); (err != nil) != tt.wantErr { + t.Errorf("SSHPublicKey.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestSSHPublicKey_PublicKey(t *testing.T) { + key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) + assert.FatalError(t, err) + pub, err := ssh.NewPublicKey(key.Public().Key) + assert.FatalError(t, err) + + type fields struct { + publicKey ssh.PublicKey + } + tests := []struct { + name string + fields fields + want ssh.PublicKey + }{ + {"ok", fields{pub}, pub}, + {"nil", fields{nil}, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &SSHPublicKey{ + publicKey: tt.fields.publicKey, + } + if got := k.PublicKey(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SSHPublicKey.PublicKey() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/authority/config/tls_options.go b/authority/config/tls_options.go index 5b0575d6..996b5834 100644 --- a/authority/config/tls_options.go +++ b/authority/config/tls_options.go @@ -34,6 +34,18 @@ var ( "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", } + // DefaultTLSOptions represents the default TLS version as well as the cipher + // suites used in the TLS certificates. + DefaultTLSOptions = TLSOptions{ + CipherSuites: CipherSuites{ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + }, + MinVersion: 1.2, + MaxVersion: 1.2, + Renegotiation: false, + } ) // TLSVersion represents a TLS version number. diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go deleted file mode 100644 index ad8b35ad..00000000 --- a/authority/mgmt/api/handler.go +++ /dev/null @@ -1,47 +0,0 @@ -package api - -import ( - "time" - - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority" - "github.com/smallstep/certificates/authority/mgmt" -) - -// Clock that returns time in UTC rounded to seconds. -type Clock struct{} - -// Now returns the UTC time rounded to seconds. -func (c *Clock) Now() time.Time { - return time.Now().UTC().Truncate(time.Second) -} - -var clock Clock - -// Handler is the ACME API request handler. -type Handler struct { - db mgmt.DB - auth *authority.Authority -} - -// NewHandler returns a new Authority Config Handler. -func NewHandler(auth *authority.Authority) api.RouterHandler { - return &Handler{auth.GetAdminDatabase(), auth} -} - -// Route traffic and implement the Router interface. -func (h *Handler) Route(r api.Router) { - // Provisioners - r.MethodFunc("GET", "/provisioners/{name}", h.GetProvisioner) - r.MethodFunc("GET", "/provisioners", h.GetProvisioners) - r.MethodFunc("POST", "/provisioners", h.CreateProvisioner) - r.MethodFunc("PUT", "/provisioners/{name}", h.UpdateProvisioner) - r.MethodFunc("DELETE", "/provisioners/{name}", h.DeleteProvisioner) - - // Admins - r.MethodFunc("GET", "/admins/{id}", h.GetAdmin) - r.MethodFunc("GET", "/admins", h.GetAdmins) - r.MethodFunc("POST", "/admins", h.CreateAdmin) - r.MethodFunc("PATCH", "/admins/{id}", h.UpdateAdmin) - r.MethodFunc("DELETE", "/admins/{id}", h.DeleteAdmin) -} diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go deleted file mode 100644 index 7964d36b..00000000 --- a/authority/mgmt/api/provisioner.go +++ /dev/null @@ -1,199 +0,0 @@ -package api - -import ( - "fmt" - "net/http" - - "github.com/go-chi/chi" - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" -) - -// CreateProvisionerRequest represents the body for a CreateProvisioner request. -type CreateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` -} - -// Validate validates a new-provisioner request body. -func (cpr *CreateProvisionerRequest) Validate(c *provisioner.Collection) error { - if _, ok := c.LoadByName(cpr.Name); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", cpr.Name) - } - return nil -} - -// GetProvisionersResponse is the type for GET /admin/provisioners responses. -type GetProvisionersResponse struct { - Provisioners provisioner.List `json:"provisioners"` - NextCursor string `json:"nextCursor"` -} - -// UpdateProvisionerRequest represents the body for a UpdateProvisioner request. -type UpdateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` -} - -// Validate validates a update-provisioner request body. -func (upr *UpdateProvisionerRequest) Validate(c *provisioner.Collection) error { - if _, ok := c.LoadByName(upr.Name); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", upr.Name) - } - return nil -} - -// GetProvisioner returns the requested provisioner, or an error. -func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - name := chi.URLParam(r, "name") - - p, ok := h.auth.GetProvisionerCollection().LoadByName(name) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) - return - } - - prov, err := h.db.GetProvisioner(ctx, p.GetID()) - if err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, prov) -} - -// GetProvisioners returns all provisioners associated with the authority. -func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { - cursor, limit, err := api.ParseCursor(r) - if err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, - "error parsing cursor / limt query params")) - return - } - - p, next, err := h.auth.GetProvisioners(cursor, limit) - if err != nil { - api.WriteError(w, errs.InternalServerErr(err)) - return - } - api.JSON(w, &GetProvisionersResponse{ - Provisioners: p, - NextCursor: next, - }) -} - -// CreateProvisioner creates a new prov. -func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var prov = new(linkedca.Provisioner) - if err := api.ReadJSON(r.Body, prov); err != nil { - api.WriteError(w, err) - return - } - - // TODO: validate - - // TODO: fix this - prov.Claims = mgmt.NewDefaultClaims() - - if err := h.db.CreateProvisioner(ctx, prov); err != nil { - api.WriteError(w, err) - return - } - api.JSONStatus(w, prov, http.StatusCreated) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } -} - -// DeleteProvisioner deletes a provisioner. -func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { - name := chi.URLParam(r, "name") - - p, ok := h.auth.GetProvisionerCollection().LoadByName(name) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) - return - } - - c := h.auth.GetAdminCollection() - if c.SuperCount() == c.SuperCountByProvisioner(name) { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, - "cannot remove provisioner %s because no super admins will remain", name)) - return - } - - ctx := r.Context() - if err := h.db.DeleteProvisioner(ctx, p.GetID()); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting provisioner %s", name)) - return - } - - // Delete all admins associated with the provisioner. - admins, ok := c.LoadByProvisioner(name) - if ok { - for _, adm := range admins { - if err := h.db.DeleteAdmin(ctx, adm.Id); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, name)) - return - } - } - } - - api.JSON(w, &DeleteResponse{Status: "ok"}) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } -} - -// UpdateProvisioner updates an existing prov. -func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { - /* - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateProvisionerRequest - if err := ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, err) - return - } - if err := body.Validate(); err != nil { - api.WriteError(w, err) - return - } - if prov, err := h.db.GetProvisioner(ctx, id); err != nil { - api.WriteError(w, err) - return - } - - prov.Claims = body.Claims - prov.Details = body.Provisioner - prov.X509Template = body.X509Template - prov.SSHTemplate = body.SSHTemplate - prov.Status = body.Status - - if err := h.db.UpdateProvisioner(ctx, prov); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, prov) - */ -} diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go deleted file mode 100644 index 3c322415..00000000 --- a/authority/mgmt/config.go +++ /dev/null @@ -1,48 +0,0 @@ -package mgmt - -const ( - // DefaultAuthorityID is the default AuthorityID. This will be the ID - // of the first Authority created, as well as the default AuthorityID - // if one is not specified in the configuration. - DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" -) - -/* -func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*AuthConfig, error) { - ac := NewDefaultAuthConfig() - - for _, o := range options { - if err := o(ac); err != nil { - return nil, err - } - } - - if err := db.CreateAuthConfig(ctx, ac); err != nil { - return nil, errors.Wrap(err, "error creating authConfig") - } - - // Generate default JWK provisioner. - - provOpts := []ProvisionerOption{WithPassword("pass")} - prov, err := CreateProvisioner(ctx, db, "JWK", "changeme", provOpts...) - if err != nil { - // TODO should we try to clean up? - return nil, WrapErrorISE(err, "error creating first provisioner") - } - - adm := &Admin{ - ProvisionerID: prov.ID, - Subject: "Change Me", - Type: AdminTypeSuper, - } - if err := db.CreateAdmin(ctx, adm); err != nil { - // TODO should we try to clean up? - return nil, WrapErrorISE(err, "error creating first admin") - } - - ac.Provisioners = []*Provisioner{prov} - ac.Admins = []*Admin{adm} - - return ac, nil -} -*/ diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go deleted file mode 100644 index fa5eaf2a..00000000 --- a/authority/mgmt/db/nosql/provisioner.go +++ /dev/null @@ -1,219 +0,0 @@ -package nosql - -import ( - "context" - "encoding/json" - "time" - - "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" - "github.com/smallstep/nosql" -) - -// dbProvisioner is the database representation of a Provisioner type. -type dbProvisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Type linkedca.Provisioner_Type `json:"type"` - // Name is the key - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template []byte `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate []byte `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` -} - -type provisionerNameID struct { - Name string `json:"name"` - ID string `json:"id"` -} - -func (dbp *dbProvisioner) clone() *dbProvisioner { - u := *dbp - return &u -} - -func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { - data, err := db.db.Get(authorityProvisionersTable, []byte(id)) - if nosql.IsErrNotFound(err) { - return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", id) - } else if err != nil { - return nil, errors.Wrapf(err, "error loading provisioner %s", id) - } - return data, nil -} - -func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) { - data, err := db.getDBProvisionerBytes(ctx, id) - if err != nil { - return nil, err - } - dbp, err := unmarshalDBProvisioner(data, id) - if err != nil { - return nil, err - } - if !dbp.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", id) - } - if dbp.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", dbp.ID, db.authorityID) - } - return dbp, nil -} - -// GetProvisioner retrieves and unmarshals a provisioner from the database. -func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { - data, err := db.getDBProvisionerBytes(ctx, id) - if err != nil { - return nil, err - } - - prov, err := unmarshalProvisioner(data, id) - if err != nil { - return nil, err - } - if prov.AuthorityId != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", prov.Id, db.authorityID) - } - return prov, nil -} - -func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) { - var dbp = new(dbProvisioner) - if err := json.Unmarshal(data, dbp); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", name) - } - if !dbp.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name) - } - return dbp, nil -} - -func unmarshalProvisioner(data []byte, name string) (*linkedca.Provisioner, error) { - dbp, err := unmarshalDBProvisioner(data, name) - if err != nil { - return nil, err - } - - details, err := linkedca.UnmarshalProvisionerDetails(dbp.Type, dbp.Details) - if err != nil { - return nil, err - } - - prov := &linkedca.Provisioner{ - Id: dbp.ID, - AuthorityId: dbp.AuthorityID, - Type: dbp.Type, - Name: dbp.Name, - Claims: dbp.Claims, - Details: details, - X509Template: dbp.X509Template, - X509TemplateData: dbp.X509TemplateData, - SshTemplate: dbp.SSHTemplate, - SshTemplateData: dbp.SSHTemplateData, - } - return prov, nil -} - -// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners -// from the database. -func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { - dbEntries, err := db.db.List(authorityProvisionersTable) - if err != nil { - return nil, mgmt.WrapErrorISE(err, "error loading provisioners") - } - var provs []*linkedca.Provisioner - for _, entry := range dbEntries { - prov, err := unmarshalProvisioner(entry.Value, string(entry.Key)) - if err != nil { - return nil, err - } - if prov.AuthorityId != db.authorityID { - continue - } - provs = append(provs, prov) - } - return provs, nil -} - -// CreateProvisioner stores a new provisioner to the database. -func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { - var err error - prov.Id, err = randID() - if err != nil { - return errors.Wrap(err, "error generating random id for provisioner") - } - - details, err := json.Marshal(prov.Details.GetData()) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) - } - - dbp := &dbProvisioner{ - ID: prov.Id, - AuthorityID: db.authorityID, - Type: prov.Type, - Name: prov.Name, - Claims: prov.Claims, - Details: details, - X509Template: prov.X509Template, - X509TemplateData: prov.X509TemplateData, - SSHTemplate: prov.SshTemplate, - SSHTemplateData: prov.SshTemplateData, - CreatedAt: clock.Now(), - } - - if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", authorityProvisionersTable); err != nil { - return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name) - } - - return nil -} - -// UpdateProvisioner saves an updated provisioner to the database. -func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { - old, err := db.getDBProvisioner(ctx, prov.Id) - if err != nil { - return err - } - - nu := old.clone() - - nu.Type = prov.Type - nu.Name = prov.Name - nu.Claims = prov.Claims - nu.Details, err = json.Marshal(prov.Details) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name) - } - nu.X509Template = prov.X509Template - nu.X509TemplateData = prov.X509TemplateData - nu.SSHTemplate = prov.SshTemplate - nu.SSHTemplateData = prov.SshTemplateData - - if err := db.save(ctx, prov.Id, nu, old, "provisioner", authorityProvisionersTable); err != nil { - return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name) - } - - return nil -} - -// DeleteProvisioner saves an updated admin to the database. -func (db *DB) DeleteProvisioner(ctx context.Context, id string) error { - old, err := db.getDBProvisioner(ctx, id) - if err != nil { - return err - } - - nu := old.clone() - nu.DeletedAt = clock.Now() - - return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) -} diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go deleted file mode 100644 index 2177e116..00000000 --- a/authority/mgmt/provisioner.go +++ /dev/null @@ -1,116 +0,0 @@ -package mgmt - -import ( - "context" - - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/linkedca" - "go.step.sm/crypto/jose" -) - -/* -type unmarshalProvisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Type string `json:"type"` - Name string `json:"name"` - Claims *Claims `json:"claims"` - Details json.RawMessage `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - Status status.Type `json:"status"` -} - -type typ struct { - Type linkedca.Provisioner_Type `json:"type"` -} - -// UnmarshalJSON implements the Unmarshal interface. -func (p *Provisioner) UnmarshalJSON(b []byte) error { - var ( - err error - up = new(unmarshalProvisioner) - ) - if err = json.Unmarshal(b, up); err != nil { - return WrapErrorISE(err, "error unmarshaling provisioner to intermediate type") - } - p.Details, err = UnmarshalProvisionerDetails(up.Details) - if err = json.Unmarshal(b, up); err != nil { - return WrapErrorISE(err, "error unmarshaling provisioner details") - } - - p.ID = up.ID - p.AuthorityID = up.AuthorityID - p.Type = up.Type - p.Name = up.Name - p.Claims = up.Claims - p.X509Template = up.X509Template - p.X509TemplateData = up.X509TemplateData - p.SSHTemplate = up.SSHTemplate - p.SSHTemplateData = up.SSHTemplateData - p.Status = up.Status - - return nil -} -*/ - -func NewDefaultClaims() *linkedca.Claims { - return &linkedca.Claims{ - X509: &linkedca.X509Claims{ - Durations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinTLSDur.String(), - Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), - Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), - }, - }, - Ssh: &linkedca.SSHClaims{ - UserDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), - }, - HostDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), - }, - }, - DisableRenewal: config.DefaultDisableRenewal, - } -} - -func CreateFirstProvisioner(ctx context.Context, db DB, password string) (*linkedca.Provisioner, error) { - jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) - if err != nil { - return nil, WrapErrorISE(err, "error generating JWK key pair") - } - - jwkPubBytes, err := jwk.MarshalJSON() - if err != nil { - return nil, WrapErrorISE(err, "error marshaling JWK") - } - jwePrivStr, err := jwe.CompactSerialize() - if err != nil { - return nil, WrapErrorISE(err, "error serializing JWE") - } - - p := &linkedca.Provisioner{ - Name: "Admin JWK", - Type: linkedca.Provisioner_JWK, - Claims: NewDefaultClaims(), - Details: &linkedca.ProvisionerDetails{ - Data: &linkedca.ProvisionerDetails_JWK{ - JWK: &linkedca.JWKProvisioner{ - PublicKey: jwkPubBytes, - EncryptedPrivateKey: []byte(jwePrivStr), - }, - }, - }, - } - if err := db.CreateProvisioner(ctx, p); err != nil { - return nil, WrapErrorISE(err, "error creating provisioner") - } - return p, nil -} diff --git a/authority/options.go b/authority/options.go index cc2f64af..4e9fbdbc 100644 --- a/authority/options.go +++ b/authority/options.go @@ -7,8 +7,8 @@ import ( "encoding/pem" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" @@ -189,7 +189,7 @@ func WithX509FederatedBundle(pemCerts []byte) Option { } // WithAdminDB is an option to set the database backing the admin APIs. -func WithAdminDB(db mgmt.DB) Option { +func WithAdminDB(db admin.DB) Option { return func(a *Authority) error { a.adminDB = db return nil diff --git a/authority/provisioner/acme.go b/authority/provisioner/acme.go index 4109d217..d81b0231 100644 --- a/authority/provisioner/acme.go +++ b/authority/provisioner/acme.go @@ -105,7 +105,7 @@ func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // certificate was configured to allow renewals. func (p *ACME) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("acme.AuthorizeRenew; renew is disabled for acme provisioner %s", p.GetID()) + return errs.Unauthorized("acme.AuthorizeRenew; renew is disabled for acme provisioner '%s'", p.GetName()) } return nil } diff --git a/authority/provisioner/acme_test.go b/authority/provisioner/acme_test.go index 7b669d8d..bd173f87 100644 --- a/authority/provisioner/acme_test.go +++ b/authority/provisioner/acme_test.go @@ -61,7 +61,7 @@ func TestACME_Init(t *testing.T) { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &ACME{Name: "foo", Type: "bar", Claims: &Claims{DefaultTLSDur: &Duration{0}}}, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { @@ -110,7 +110,7 @@ func TestACME_AuthorizeRenew(t *testing.T) { p: p, cert: &x509.Certificate{}, code: http.StatusUnauthorized, - err: errors.Errorf("acme.AuthorizeRenew; renew is disabled for acme provisioner %s", p.GetID()), + err: errors.Errorf("acme.AuthorizeRenew; renew is disabled for acme provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 8a443554..c1c77ce5 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -296,7 +296,7 @@ func (p *AWS) GetTokenID(token string) (string, error) { } // Use provisioner + instance-id as the identifier. - unique := fmt.Sprintf("%s.%s", p.GetID(), payload.document.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), payload.document.InstanceID) sum := sha256.Sum256([]byte(unique)) return strings.ToLower(hex.EncodeToString(sum[:])), nil } @@ -344,7 +344,7 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { return "", err } - audience, err := generateSignAudience(caURL, p.GetID()) + audience, err := generateSignAudience(caURL, p.GetIDForToken()) if err != nil { return "", err } @@ -352,7 +352,7 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { // Create unique ID for Trust On First Use (TOFU). Only the first instance // per provisioner is allowed as we don't have a way to trust the given // sans. - unique := fmt.Sprintf("%s.%s", p.GetID(), idoc.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), idoc.InstanceID) sum := sha256.Sum256([]byte(unique)) // Create a JWT from the identity document @@ -407,7 +407,7 @@ func (p *AWS) Init(config Config) (err error) { if p.config, err = newAWSConfig(p.IIDRoots); err != nil { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) // validate IMDS versions if len(p.IMDSVersions) == 0 { @@ -484,7 +484,7 @@ func (p *AWS) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // certificate was configured to allow renewals. func (p *AWS) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("aws.AuthorizeRenew; renew is disabled for aws provisioner %s", p.GetID()) + return errs.Unauthorized("aws.AuthorizeRenew; renew is disabled for aws provisioner '%s'", p.GetName()) } return nil } @@ -697,7 +697,7 @@ func (p *AWS) authorizeToken(token string) (*awsPayload, error) { // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("aws.AuthorizeSSHSign; ssh ca is disabled for aws provisioner %s", p.GetID()) + return nil, errs.Unauthorized("aws.AuthorizeSSHSign; ssh ca is disabled for aws provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index b077d735..230f246f 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -334,7 +334,7 @@ func (p *Azure) AuthorizeSign(ctx context.Context, token string) ([]SignOption, // certificate was configured to allow renewals. func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("azure.AuthorizeRenew; renew is disabled for azure provisioner %s", p.GetID()) + return errs.Unauthorized("azure.AuthorizeRenew; renew is disabled for azure provisioner '%s'", p.GetName()) } return nil } @@ -342,7 +342,7 @@ func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) erro // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner %s", p.GetID()) + return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner '%s'", p.GetName()) } _, name, _, err := p.authorizeToken(token) diff --git a/authority/provisioner/claims.go b/authority/provisioner/claims.go index 6c87792c..629a313c 100644 --- a/authority/provisioner/claims.go +++ b/authority/provisioner/claims.go @@ -7,27 +7,6 @@ import ( "golang.org/x/crypto/ssh" ) -type _Claims struct { - *X509Claims `json:"x509Claims"` - *SSHClaims `json:"sshClaims"` - DisableRenewal *bool `json:"disableRenewal"` -} - -type X509Claims struct { - Durations *Durations `json:"durations"` -} - -type SSHClaims struct { - UserDuration *Durations `json:"userDurations"` - HostDuration *Durations `json:"hostDuration"` -} - -type Durations struct { - Min string `json:"min"` - Max string `json:"max"` - Default string `json:"default"` -} - // Claims so that individual provisioners can override global claims. type Claims struct { // TLS CA properties @@ -92,6 +71,9 @@ func (c *Claimer) DefaultTLSCertDuration() time.Duration { // minimum from the authority configuration will be used. func (c *Claimer) MinTLSCertDuration() time.Duration { if c.claims == nil || c.claims.MinTLSDur == nil { + if c.claims != nil && c.claims.DefaultTLSDur != nil && c.claims.DefaultTLSDur.Duration < c.global.MinTLSDur.Duration { + return c.claims.DefaultTLSDur.Duration + } return c.global.MinTLSDur.Duration } return c.claims.MinTLSDur.Duration @@ -102,6 +84,9 @@ func (c *Claimer) MinTLSCertDuration() time.Duration { // maximum from the authority configuration will be used. func (c *Claimer) MaxTLSCertDuration() time.Duration { if c.claims == nil || c.claims.MaxTLSDur == nil { + if c.claims != nil && c.claims.DefaultTLSDur != nil && c.claims.DefaultTLSDur.Duration > c.global.MaxTLSDur.Duration { + return c.claims.DefaultTLSDur.Duration + } return c.global.MaxTLSDur.Duration } return c.claims.MaxTLSDur.Duration @@ -147,6 +132,9 @@ func (c *Claimer) DefaultUserSSHCertDuration() time.Duration { // global minimum from the authority configuration will be used. func (c *Claimer) MinUserSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MinUserSSHDur == nil { + if c.claims != nil && c.claims.DefaultUserSSHDur != nil && c.claims.DefaultUserSSHDur.Duration < c.global.MinUserSSHDur.Duration { + return c.claims.DefaultUserSSHDur.Duration + } return c.global.MinUserSSHDur.Duration } return c.claims.MinUserSSHDur.Duration @@ -157,6 +145,9 @@ func (c *Claimer) MinUserSSHCertDuration() time.Duration { // global maximum from the authority configuration will be used. func (c *Claimer) MaxUserSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MaxUserSSHDur == nil { + if c.claims != nil && c.claims.DefaultUserSSHDur != nil && c.claims.DefaultUserSSHDur.Duration > c.global.MaxUserSSHDur.Duration { + return c.claims.DefaultUserSSHDur.Duration + } return c.global.MaxUserSSHDur.Duration } return c.claims.MaxUserSSHDur.Duration @@ -177,6 +168,9 @@ func (c *Claimer) DefaultHostSSHCertDuration() time.Duration { // global minimum from the authority configuration will be used. func (c *Claimer) MinHostSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MinHostSSHDur == nil { + if c.claims != nil && c.claims.DefaultHostSSHDur != nil && c.claims.DefaultHostSSHDur.Duration < c.global.MinHostSSHDur.Duration { + return c.claims.DefaultHostSSHDur.Duration + } return c.global.MinHostSSHDur.Duration } return c.claims.MinHostSSHDur.Duration @@ -187,6 +181,9 @@ func (c *Claimer) MinHostSSHCertDuration() time.Duration { // global maximum from the authority configuration will be used. func (c *Claimer) MaxHostSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MaxHostSSHDur == nil { + if c.claims != nil && c.claims.DefaultHostSSHDur != nil && c.claims.DefaultHostSSHDur.Duration > c.global.MaxHostSSHDur.Duration { + return c.claims.DefaultHostSSHDur.Duration + } return c.global.MaxHostSSHDur.Duration } return c.claims.MaxHostSSHDur.Duration diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index eecacf69..3ba98a23 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -12,7 +12,7 @@ import ( "strings" "sync" - "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "go.step.sm/crypto/jose" ) @@ -148,24 +148,7 @@ func (c *Collection) LoadByCertificate(cert *x509.Certificate) (Interface, bool) if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil { return nil, false } - switch Type(provisioner.Type) { - case TypeJWK: - return c.Load(string(provisioner.Name) + ":" + string(provisioner.CredentialID)) - case TypeAWS: - return c.Load("aws/" + string(provisioner.Name)) - case TypeGCP: - return c.Load("gcp/" + string(provisioner.Name)) - case TypeACME: - return c.Load("acme/" + string(provisioner.Name)) - case TypeSCEP: - return c.Load("scep/" + string(provisioner.Name)) - case TypeX5C: - return c.Load("x5c/" + string(provisioner.Name)) - case TypeK8sSA: - return c.Load(K8sSAID) - default: - return c.Load(string(provisioner.CredentialID)) - } + return c.LoadByName(string(provisioner.Name)) } } @@ -190,18 +173,21 @@ func (c *Collection) LoadEncryptedKey(keyID string) (string, bool) { func (c *Collection) Store(p Interface) error { // Store provisioner always in byID. ID must be unique. if _, loaded := c.byID.LoadOrStore(p.GetID(), p); loaded { - return errors.New("cannot add multiple provisioners with the same id") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same id") } // Store provisioner always by name. if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded { c.byID.Delete(p.GetID()) - return errors.New("cannot add multiple provisioners with the same name") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same name") } // Store provisioner always by ID presented in token. if _, loaded := c.byTokenID.LoadOrStore(p.GetIDForToken(), p); loaded { c.byID.Delete(p.GetID()) c.byName.Delete(p.GetName()) - return errors.New("cannot add multiple provisioners with the same token identifier") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same token identifier") } // Store provisioner in byKey if EncryptedKey is defined. @@ -225,6 +211,65 @@ func (c *Collection) Store(p Interface) error { return nil } +// Remove deletes an provisioner from all associated collections and lists. +func (c *Collection) Remove(id string) error { + prov, ok := c.Load(id) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) + } + + var found bool + for i, elem := range c.sorted { + if elem.provisioner.GetID() == id { + // Remove index in sorted list + copy(c.sorted[i:], c.sorted[i+1:]) // Shift a[i+1:] left one index. + c.sorted[len(c.sorted)-1] = uidProvisioner{} // Erase last element (write zero value). + c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. + found = true + break + } + } + if !found { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found in sorted list", prov.GetName()) + } + + c.byID.Delete(id) + c.byName.Delete(prov.GetName()) + c.byTokenID.Delete(prov.GetIDForToken()) + if kid, _, ok := prov.GetEncryptedKey(); ok { + c.byKey.Delete(kid) + } + + return nil +} + +// Update updates the given provisioner in all related lists and collections. +func (c *Collection) Update(nu Interface) error { + old, ok := c.Load(nu.GetID()) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", nu.GetID()) + } + + if old.GetName() != nu.GetName() { + if _, ok := c.LoadByName(nu.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with name %s already exists", nu.GetName()) + } + } + if old.GetIDForToken() != nu.GetIDForToken() { + if _, ok := c.LoadByTokenID(nu.GetIDForToken()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with Token ID %s already exists", nu.GetIDForToken()) + } + } + + if err := c.Remove(old.GetID()); err != nil { + return err + } + + return c.Store(nu) +} + // Find implements pagination on a list of sorted provisioners. func (c *Collection) Find(cursor string, limit int) (List, string) { switch { diff --git a/authority/provisioner/collection_test.go b/authority/provisioner/collection_test.go index a0a79e92..348b797c 100644 --- a/authority/provisioner/collection_test.go +++ b/authority/provisioner/collection_test.go @@ -132,6 +132,7 @@ func TestCollection_LoadByToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := &Collection{ byID: tt.fields.byID, + byTokenID: tt.fields.byID, audiences: tt.fields.audiences, } got, got1 := c.LoadByToken(tt.args.token, tt.args.claims) @@ -153,10 +154,10 @@ func TestCollection_LoadByCertificate(t *testing.T) { p3, err := generateACME() assert.FatalError(t, err) - byID := new(sync.Map) - byID.Store(p1.GetID(), p1) - byID.Store(p2.GetID(), p2) - byID.Store(p3.GetID(), p3) + byName := new(sync.Map) + byName.Store(p1.GetName(), p1) + byName.Store(p2.GetName(), p2) + byName.Store(p3.GetName(), p3) ok1Ext, err := createProvisionerExtension(1, p1.Name, p1.Key.KeyID) assert.FatalError(t, err) @@ -186,7 +187,7 @@ func TestCollection_LoadByCertificate(t *testing.T) { } type fields struct { - byID *sync.Map + byName *sync.Map audiences Audiences } type args struct { @@ -199,17 +200,17 @@ func TestCollection_LoadByCertificate(t *testing.T) { want Interface want1 bool }{ - {"ok1", fields{byID, testAudiences}, args{ok1Cert}, p1, true}, - {"ok2", fields{byID, testAudiences}, args{ok2Cert}, p2, true}, - {"ok3", fields{byID, testAudiences}, args{ok3Cert}, p3, true}, - {"noExtension", fields{byID, testAudiences}, args{&x509.Certificate{}}, &noop{}, true}, - {"notFound", fields{byID, testAudiences}, args{notFoundCert}, nil, false}, - {"badCert", fields{byID, testAudiences}, args{badCert}, nil, false}, + {"ok1", fields{byName, testAudiences}, args{ok1Cert}, p1, true}, + {"ok2", fields{byName, testAudiences}, args{ok2Cert}, p2, true}, + {"ok3", fields{byName, testAudiences}, args{ok3Cert}, p3, true}, + {"noExtension", fields{byName, testAudiences}, args{&x509.Certificate{}}, &noop{}, true}, + {"notFound", fields{byName, testAudiences}, args{notFoundCert}, nil, false}, + {"badCert", fields{byName, testAudiences}, args{badCert}, nil, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Collection{ - byID: tt.fields.byID, + byName: tt.fields.byName, audiences: tt.fields.audiences, } got, got1 := c.LoadByCertificate(tt.args.cert) diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 6d19d052..1b599fb3 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -134,7 +134,7 @@ func (p *GCP) GetTokenID(token string) (string, error) { // Create unique ID for Trust On First Use (TOFU). Only the first instance // per provisioner is allowed as we don't have a way to trust the given // sans. - unique := fmt.Sprintf("%s.%s", p.GetID(), claims.Google.ComputeEngine.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), claims.Google.ComputeEngine.InstanceID) sum := sha256.Sum256([]byte(unique)) return strings.ToLower(hex.EncodeToString(sum[:])), nil } @@ -168,7 +168,7 @@ func (p *GCP) GetIdentityURL(audience string) string { // GetIdentityToken does an HTTP request to the identity url. func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) { - audience, err := generateSignAudience(caURL, p.GetID()) + audience, err := generateSignAudience(caURL, p.GetIDForToken()) if err != nil { return "", err } @@ -216,7 +216,7 @@ func (p *GCP) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) return nil } @@ -277,7 +277,7 @@ func (p *GCP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // AuthorizeRenew returns an error if the renewal is disabled. func (p *GCP) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("gcp.AuthorizeRenew; renew is disabled for gcp provisioner %s", p.GetID()) + return errs.Unauthorized("gcp.AuthorizeRenew; renew is disabled for gcp provisioner '%s'", p.GetName()) } return nil } @@ -382,7 +382,7 @@ func (p *GCP) authorizeToken(token string) (*gcpPayload, error) { // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner %s", p.GetID()) + return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index d57ff4c1..56768fb7 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -194,7 +194,7 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // certificate was configured to allow renewals. func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("jwk.AuthorizeRenew; renew is disabled for jwk provisioner %s", p.GetID()) + return errs.Unauthorized("jwk.AuthorizeRenew; renew is disabled for jwk provisioner '%s'", p.GetName()) } return nil } @@ -202,7 +202,7 @@ func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("jwk.AuthorizeSSHSign; sshCA is disabled for jwk provisioner %s", p.GetID()) + return nil, errs.Unauthorized("jwk.AuthorizeSSHSign; sshCA is disabled for jwk provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) if err != nil { diff --git a/authority/provisioner/jwk_test.go b/authority/provisioner/jwk_test.go index 9198ff69..deae8f7a 100644 --- a/authority/provisioner/jwk_test.go +++ b/authority/provisioner/jwk_test.go @@ -77,7 +77,7 @@ func TestJWK_Init(t *testing.T) { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &JWK{Name: "foo", Type: "bar", Key: &jose.JSONWebKey{}, audiences: testAudiences, Claims: &Claims{DefaultTLSDur: &Duration{0}}}, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { diff --git a/authority/provisioner/k8sSA.go b/authority/provisioner/k8sSA.go index 876131e1..d260f5ec 100644 --- a/authority/provisioner/k8sSA.go +++ b/authority/provisioner/k8sSA.go @@ -111,12 +111,12 @@ func (p *K8sSA) Init(config Config) (err error) { } key, err := pemutil.ParseKey(pem.EncodeToMemory(block)) if err != nil { - return errors.Wrapf(err, "error parsing public key in provisioner %s", p.GetID()) + return errors.Wrapf(err, "error parsing public key in provisioner '%s'", p.GetName()) } switch q := key.(type) { case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: default: - return errors.Errorf("Unexpected public key type %T in provisioner %s", q, p.GetID()) + return errors.Errorf("Unexpected public key type %T in provisioner '%s'", q, p.GetName()) } p.pubKeys = append(p.pubKeys, key) } @@ -250,7 +250,7 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption, // AuthorizeRenew returns an error if the renewal is disabled. func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner %s", p.GetID()) + return errs.Unauthorized("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner '%s'", p.GetName()) } return nil } @@ -258,7 +258,7 @@ func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) erro // AuthorizeSSHSign validates an request for an SSH certificate. func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner %s", p.GetID()) + return nil, errs.Unauthorized("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) if err != nil { diff --git a/authority/provisioner/k8sSA_test.go b/authority/provisioner/k8sSA_test.go index 03ae7eff..176cdfd3 100644 --- a/authority/provisioner/k8sSA_test.go +++ b/authority/provisioner/k8sSA_test.go @@ -198,7 +198,7 @@ func TestK8sSA_AuthorizeRenew(t *testing.T) { p: p, cert: &x509.Certificate{}, code: http.StatusUnauthorized, - err: errors.Errorf("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner %s", p.GetID()), + err: errors.Errorf("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { @@ -319,7 +319,7 @@ func TestK8sSA_AuthorizeSSHSign(t *testing.T) { p: p, token: "foo", code: http.StatusUnauthorized, - err: errors.Errorf("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner %s", p.GetID()), + err: errors.Errorf("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner '%s'", p.GetName()), } }, "fail/invalid-token": func(t *testing.T) test { diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index df3c1f9e..b6bca872 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -377,7 +377,7 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // certificate was configured to allow renewals. func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if o.claimer.IsDisableRenewal() { - return errs.Unauthorized("oidc.AuthorizeRenew; renew is disabled for oidc provisioner %s", o.GetID()) + return errs.Unauthorized("oidc.AuthorizeRenew; renew is disabled for oidc provisioner '%s'", o.GetName()) } return nil } @@ -385,7 +385,7 @@ func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !o.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("oidc.AuthorizeSSHSign; sshCA is disabled for oidc provisioner %s", o.GetID()) + return nil, errs.Unauthorized("oidc.AuthorizeSSHSign; sshCA is disabled for oidc provisioner '%s'", o.GetName()) } claims, err := o.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 7673ecc2..d4517627 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -11,6 +11,7 @@ import ( // SCEP provisioning flow type SCEP struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` @@ -26,8 +27,17 @@ type SCEP struct { secretChallengePassword string } -// GetID returns the provisioner unique identifier. -func (s SCEP) GetID() string { +// GetID returns the provisioner unique identifier. The name and credential id +// should uniquely identify any JWK provisioner. +func (s *SCEP) GetID() string { + if s.ID != "" { + return s.ID + } + return s.GetIDForToken() +} + +// GetIDForToken returns the provisioner unique identifier. +func (s SCEP) GetIDForToken() string { return "scep/" + s.Name } diff --git a/authority/provisioner/sshpop.go b/authority/provisioner/sshpop.go index dd9c7d1f..8bc76edf 100644 --- a/authority/provisioner/sshpop.go +++ b/authority/provisioner/sshpop.go @@ -101,7 +101,7 @@ func (p *SSHPOP) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) p.db = config.DB p.sshPubKeys = config.SSHKeys return nil diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index 9e47aaed..a05f39c7 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -116,7 +116,7 @@ func (p *X5C) Init(config Config) error { // Verify that at least one root was found. if len(p.rootPool.Subjects()) == 0 { - return errors.Errorf("no x509 certificates found in roots attribute for provisioner %s", p.GetName()) + return errors.Errorf("no x509 certificates found in roots attribute for provisioner '%s'", p.GetName()) } // Update claims with global ones @@ -125,7 +125,7 @@ func (p *X5C) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) return nil } @@ -139,7 +139,8 @@ func (p *X5C) authorizeToken(token string, audiences []string) (*x5cPayload, err } verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{ - Roots: p.rootPool, + Roots: p.rootPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }) if err != nil { return nil, errs.Wrap(http.StatusUnauthorized, err, @@ -234,7 +235,7 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // AuthorizeRenew returns an error if the renewal is disabled. func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("x5c.AuthorizeRenew; renew is disabled for x5c provisioner %s", p.GetID()) + return errs.Unauthorized("x5c.AuthorizeRenew; renew is disabled for x5c provisioner '%s'", p.GetName()) } return nil } @@ -242,7 +243,7 @@ func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner %s", p.GetID()) + return nil, errs.Unauthorized("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) diff --git a/authority/provisioner/x5c_test.go b/authority/provisioner/x5c_test.go index 5d288de5..2959f8c6 100644 --- a/authority/provisioner/x5c_test.go +++ b/authority/provisioner/x5c_test.go @@ -70,7 +70,7 @@ func TestX5C_Init(t *testing.T) { "fail/no-valid-root-certs": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &X5C{Name: "foo", Type: "bar", Roots: []byte("foo"), audiences: testAudiences}, - err: errors.Errorf("no x509 certificates found in roots attribute for provisioner foo"), + err: errors.Errorf("no x509 certificates found in roots attribute for provisioner 'foo'"), } }, "fail/invalid-duration": func(t *testing.T) ProvisionerValidateTest { @@ -79,7 +79,7 @@ func TestX5C_Init(t *testing.T) { p.Claims = &Claims{DefaultTLSDur: &Duration{0}} return ProvisionerValidateTest{ p: p, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { @@ -568,7 +568,7 @@ func TestX5C_AuthorizeRenew(t *testing.T) { return test{ p: p, code: http.StatusUnauthorized, - err: errors.Errorf("x5c.AuthorizeRenew; renew is disabled for x5c provisioner %s", p.GetID()), + err: errors.Errorf("x5c.AuthorizeRenew; renew is disabled for x5c provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { @@ -624,7 +624,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) { p: p, token: "foo", code: http.StatusUnauthorized, - err: errors.Errorf("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner %s", p.GetID()), + err: errors.Errorf("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner '%s'", p.GetName()), } }, "fail/invalid-token": func(t *testing.T) test { diff --git a/authority/provisioners.go b/authority/provisioners.go index 48f67dc8..d2581e76 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -1,19 +1,24 @@ package authority import ( + "context" "crypto/x509" "encoding/json" "fmt" - "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" "go.step.sm/crypto/jose" + "go.step.sm/linkedca" + "gopkg.in/square/go-jose.v2/jwt" ) // GetEncryptedKey returns the JWE key corresponding to the given kid argument. func (a *Authority) GetEncryptedKey(kid string) (string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() key, ok := a.provisioners.LoadEncryptedKey(kid) if !ok { return "", errs.NotFound("encrypted key with kid %s was not found", kid) @@ -24,6 +29,8 @@ func (a *Authority) GetEncryptedKey(kid string) (string, error) { // GetProvisioners returns a map listing each provisioner and the JWK Key Set // with their public keys. func (a *Authority) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() provisioners, nextCursor := a.provisioners.Find(cursor, limit) return provisioners, nextCursor, nil } @@ -31,39 +38,320 @@ func (a *Authority) GetProvisioners(cursor string, limit int) (provisioner.List, // LoadProvisionerByCertificate returns an interface to the provisioner that // provisioned the certificate. func (a *Authority) LoadProvisionerByCertificate(crt *x509.Certificate) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() p, ok := a.provisioners.LoadByCertificate(crt) if !ok { - return nil, errs.NotFound("provisioner not found") + return nil, admin.NewError(admin.ErrorNotFoundType, "unable to load provisioner from certificate") + } + return p, nil +} + +// LoadProvisionerByToken returns an interface to the provisioner that +// provisioned the token. +func (a *Authority) LoadProvisionerByToken(token *jwt.JSONWebToken, claims *jwt.Claims) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + p, ok := a.provisioners.LoadByToken(token, claims) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "unable to load provisioner from token") } return p, nil } // LoadProvisionerByID returns an interface to the provisioner with the given ID. func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() p, ok := a.provisioners.Load(id) if !ok { - return nil, errs.NotFound("provisioner not found") + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) } return p, nil } -func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { - return &provisioner.Options{ - X509: &provisioner.X509Options{ - Template: string(p.X509Template), - TemplateData: p.X509TemplateData, +// LoadProvisionerByName returns an interface to the provisioner with the given Name. +func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + p, ok := a.provisioners.LoadByName(name) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", name) + } + return p, nil +} + +func (a *Authority) generateProvisionerConfig(ctx context.Context) (*provisioner.Config, error) { + // Merge global and configuration claims + claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) + if err != nil { + return nil, err + } + // TODO: should we also be combining the ssh federated roots here? + // If we rotate ssh roots keys, sshpop provisioner will lose ability to + // validate old SSH certificates, unless they are added as federated certs. + sshKeys, err := a.GetSSHRoots(ctx) + if err != nil { + return nil, err + } + return &provisioner.Config{ + Claims: claimer.Claims(), + Audiences: a.config.GetAudiences(), + DB: a.db, + SSHKeys: &provisioner.SSHKeys{ + UserKeys: sshKeys.UserKeys, + HostKeys: sshKeys.HostKeys, }, - SSH: &provisioner.SSHOptions{ - Template: string(p.SshTemplate), - TemplateData: p.SshTemplateData, + GetIdentityFunc: a.getIdentityFunc, + }, nil + +} + +// StoreProvisioner stores an provisioner.Interface to the authority. +func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + certProv, err := ProvisionerToCertificates(prov) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + if _, ok := a.provisioners.LoadByName(prov.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with name %s already exists", prov.GetName()) + } + if _, ok := a.provisioners.LoadByTokenID(certProv.GetIDForToken()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with token ID %s already exists", certProv.GetIDForToken()) + } + + // Store to database -- this will set the ID. + if err := a.adminDB.CreateProvisioner(ctx, prov); err != nil { + return admin.WrapErrorISE(err, "error creating admin") + } + + // We need a new conversion that has the newly set ID. + certProv, err = ProvisionerToCertificates(prov) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + provisionerConfig, err := a.generateProvisionerConfig(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error generating provisioner config") + } + + if err := certProv.Init(*provisionerConfig); err != nil { + return admin.WrapErrorISE(err, "error initializing provisioner %s", prov.Name) + } + + if err := a.provisioners.Store(certProv); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner store") + } + return admin.WrapErrorISE(err, "error storing provisioner in authority cache") + } + return nil +} + +// UpdateProvisioner stores an provisioner.Interface to the authority. +func (a *Authority) UpdateProvisioner(ctx context.Context, nu *linkedca.Provisioner) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + certProv, err := ProvisionerToCertificates(nu) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + provisionerConfig, err := a.generateProvisionerConfig(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error generating provisioner config") + } + + if err := certProv.Init(*provisionerConfig); err != nil { + return admin.WrapErrorISE(err, "error initializing provisioner %s", nu.Name) + } + + if err := a.provisioners.Update(certProv); err != nil { + return admin.WrapErrorISE(err, "error updating provisioner '%s' in authority cache", nu.Name) + } + if err := a.adminDB.UpdateProvisioner(ctx, nu); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner update") + } + return admin.WrapErrorISE(err, "error updating provisioner '%s'", nu.Name) + } + return nil +} + +// RemoveProvisioner removes an provisioner.Interface from the authority. +func (a *Authority) RemoveProvisioner(ctx context.Context, id string) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + p, ok := a.provisioners.Load(id) + if !ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner %s not found", id) + } + + provName, provID := p.GetName(), p.GetID() + // Validate + // - Check that there will be SUPER_ADMINs that remain after we + // remove this provisioner. + if a.admins.SuperCount() == a.admins.SuperCountByProvisioner(provName) { + return admin.NewError(admin.ErrorBadRequestType, + "cannot remove provisioner %s because no super admins will remain", provName) + } + + // Delete all admins associated with the provisioner. + admins, ok := a.admins.LoadByProvisioner(provName) + if ok { + for _, adm := range admins { + if err := a.removeAdmin(ctx, adm.Id); err != nil { + return admin.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, provName) + } + } + } + + // Remove provisioner from authority caches. + if err := a.provisioners.Remove(provID); err != nil { + return admin.WrapErrorISE(err, "error removing admin from authority cache") + } + // Remove provisioner from database. + if err := a.adminDB.DeleteProvisioner(ctx, provID); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner remove") + } + return admin.WrapErrorISE(err, "error deleting provisioner %s", provName) + } + return nil +} + +func CreateFirstProvisioner(ctx context.Context, db admin.DB, password string) (*linkedca.Provisioner, error) { + jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) + if err != nil { + return nil, admin.WrapErrorISE(err, "error generating JWK key pair") + } + + jwkPubBytes, err := jwk.MarshalJSON() + if err != nil { + return nil, admin.WrapErrorISE(err, "error marshaling JWK") + } + jwePrivStr, err := jwe.CompactSerialize() + if err != nil { + return nil, admin.WrapErrorISE(err, "error serializing JWE") + } + + p := &linkedca.Provisioner{ + Name: "Admin JWK", + Type: linkedca.Provisioner_JWK, + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_JWK{ + JWK: &linkedca.JWKProvisioner{ + PublicKey: jwkPubBytes, + EncryptedPrivateKey: []byte(jwePrivStr), + }, + }, + }, + Claims: &linkedca.Claims{ + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Default: "5m", + }, + }, }, } + if err := db.CreateProvisioner(ctx, p); err != nil { + return nil, admin.WrapErrorISE(err, "error creating provisioner") + } + return p, nil +} + +func ValidateClaims(c *linkedca.Claims) error { + if c == nil { + return nil + } + if c.X509 != nil { + if c.X509.Durations != nil { + if err := ValidateDurations(c.X509.Durations); err != nil { + return err + } + } + } + if c.Ssh != nil { + if c.Ssh.UserDurations != nil { + if err := ValidateDurations(c.Ssh.UserDurations); err != nil { + return err + } + } + if c.Ssh.HostDurations != nil { + if err := ValidateDurations(c.Ssh.HostDurations); err != nil { + return err + } + } + } + return nil +} + +func ValidateDurations(d *linkedca.Durations) error { + var ( + err error + min, max, def *provisioner.Duration + ) + + if d.Min != "" { + min, err = provisioner.NewDuration(d.Min) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' is invalid", d.Min) + } + if min.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' cannot be less than 0", d.Min) + } + } + if d.Max != "" { + max, err = provisioner.NewDuration(d.Max) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' is invalid", d.Max) + } + if max.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' cannot be less than 0", d.Max) + } + } + if d.Default != "" { + def, err = provisioner.NewDuration(d.Default) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "default duration '%s' is invalid", d.Default) + } + if def.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "default duration '%s' cannot be less than 0", d.Default) + } + } + if d.Min != "" && d.Max != "" && min.Value() > max.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "min duration '%s' cannot be greater than max duration '%s'", d.Min, d.Max) + } + if d.Min != "" && d.Default != "" && min.Value() > def.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "min duration '%s' cannot be greater than default duration '%s'", d.Min, d.Default) + } + if d.Default != "" && d.Max != "" && min.Value() > def.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "default duration '%s' cannot be greater than max duration '%s'", d.Default, d.Max) + } + return nil } func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, error) { var nu provisioner.List for _, p := range l { - certProv, err := provisionerToCertificates(p) + certProv, err := ProvisionerToCertificates(p) if err != nil { return nil, err } @@ -72,9 +360,87 @@ func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, return nu, nil } -// provisionerToCertificates converts the landlord provisioner type to the open source -// provisioner type. -func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { +func optionsToCertificates(p *linkedca.Provisioner) *provisioner.Options { + ops := &provisioner.Options{ + X509: &provisioner.X509Options{}, + SSH: &provisioner.SSHOptions{}, + } + if p.X509Template != nil { + ops.X509.Template = string(p.X509Template.Template) + ops.X509.TemplateData = p.X509Template.Data + } + if p.SshTemplate != nil { + ops.SSH.Template = string(p.SshTemplate.Template) + ops.SSH.TemplateData = p.SshTemplate.Data + } + return ops +} + +func durationsToCertificates(d *linkedca.Durations) (min, max, def *provisioner.Duration, err error) { + if len(d.Min) > 0 { + min, err = provisioner.NewDuration(d.Min) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing minimum duration '%s'", d.Min) + } + } + if len(d.Max) > 0 { + max, err = provisioner.NewDuration(d.Max) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing maximum duration '%s'", d.Max) + } + } + if len(d.Default) > 0 { + def, err = provisioner.NewDuration(d.Default) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing default duration '%s'", d.Default) + } + } + return +} + +// claimsToCertificates converts the linkedca provisioner claims type to the +// certifictes claims type. +func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { + if c == nil { + return nil, nil + } + + pc := &provisioner.Claims{ + DisableRenewal: &c.DisableRenewal, + } + + var err error + + if xc := c.X509; xc != nil { + if d := xc.Durations; d != nil { + pc.MinTLSDur, pc.MaxTLSDur, pc.DefaultTLSDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + } + if sc := c.Ssh; sc != nil { + pc.EnableSSHCA = &sc.Enabled + if d := sc.UserDurations; d != nil { + pc.MinUserSSHDur, pc.MaxUserSSHDur, pc.DefaultUserSSHDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + if d := sc.HostDurations; d != nil { + pc.MinHostSSHDur, pc.MaxHostSSHDur, pc.DefaultHostSSHDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + } + + return pc, nil +} + +// ProvisionerToCertificates converts the linkedca provisioner type to the certificates provisioner +// interface. +func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { claims, err := claimsToCertificates(p.Claims) if err != nil { return nil, err @@ -85,9 +451,10 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, return nil, fmt.Errorf("provisioner does not have any details") } + options := optionsToCertificates(p) + switch d := details.(type) { case *linkedca.ProvisionerDetails_JWK: - fmt.Printf("d = %+v\n", d) jwk := new(jose.JSONWebKey) if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { return nil, err @@ -99,153 +466,136 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Key: jwk, EncryptedKey: string(d.JWK.EncryptedPrivateKey), Claims: claims, - Options: provisionerGetOptions(p), + Options: options, + }, nil + case *linkedca.ProvisionerDetails_X5C: + var roots []byte + for i, root := range d.X5C.GetRoots() { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, root...) + } + return &provisioner.X5C{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_K8SSA: + var publicKeys []byte + for i, k := range d.K8SSA.GetPublicKeys() { + if i > 0 { + publicKeys = append(publicKeys, '\n') + } + publicKeys = append(publicKeys, k...) + } + return &provisioner.K8sSA{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + PubKeys: publicKeys, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_SSHPOP: + return &provisioner.SSHPOP{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Claims: claims, + }, nil + case *linkedca.ProvisionerDetails_ACME: + cfg := d.ACME + return &provisioner.ACME{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + ForceCN: cfg.ForceCn, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_OIDC: + cfg := d.OIDC + return &provisioner.OIDC{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ClientID: cfg.ClientId, + ClientSecret: cfg.ClientSecret, + ConfigurationEndpoint: cfg.ConfigurationEndpoint, + Admins: cfg.Admins, + Domains: cfg.Domains, + Groups: cfg.Groups, + ListenAddress: cfg.ListenAddress, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_AWS: + cfg := d.AWS + instanceAge, err := parseInstanceAge(cfg.InstanceAge) + if err != nil { + return nil, err + } + return &provisioner.AWS{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Accounts: cfg.Accounts, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: instanceAge, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_GCP: + cfg := d.GCP + instanceAge, err := parseInstanceAge(cfg.InstanceAge) + if err != nil { + return nil, err + } + return &provisioner.GCP{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + ServiceAccounts: cfg.ServiceAccounts, + ProjectIDs: cfg.ProjectIds, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: instanceAge, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_Azure: + cfg := d.Azure + return &provisioner.Azure{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ResourceGroups: cfg.ResourceGroups, + Audience: cfg.Audience, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + Claims: claims, + Options: options, }, nil - /* - case *ProvisionerDetails_OIDC: - cfg := d.OIDC - return &provisioner.OIDC{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ClientID: cfg.ClientId, - ClientSecret: cfg.ClientSecret, - ConfigurationEndpoint: cfg.ConfigurationEndpoint, - Admins: cfg.Admins, - Domains: cfg.Domains, - Groups: cfg.Groups, - ListenAddress: cfg.ListenAddress, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_GCP: - cfg := d.GCP - return &provisioner.GCP{ - Type: p.Type.String(), - Name: p.Name, - ServiceAccounts: cfg.ServiceAccounts, - ProjectIDs: cfg.ProjectIds, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_AWS: - cfg := d.AWS - return &provisioner.AWS{ - Type: p.Type.String(), - Name: p.Name, - Accounts: cfg.Accounts, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_Azure: - cfg := d.Azure - return &provisioner.Azure{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ResourceGroups: cfg.ResourceGroups, - Audience: cfg.Audience, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_X5C: - var roots []byte - for i, k := range d.X5C.GetRoots() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - roots = append(roots, '\n') - } - roots = append(roots, b...) - } - } - return &provisioner.X5C{ - Type: p.Type.String(), - Name: p.Name, - Roots: roots, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_K8SSA: - var publicKeys []byte - for i, k := range d.K8SSA.GetPublicKeys() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - publicKeys = append(publicKeys, '\n') - } - publicKeys = append(publicKeys, k.Key.Public...) - } - } - return &provisioner.K8sSA{ - Type: p.Type.String(), - Name: p.Name, - PubKeys: publicKeys, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_SSHPOP: - return &provisioner.SSHPOP{ - Type: p.Type.String(), - Name: p.Name, - Claims: claims, - }, nil - case *ProvisionerDetails_ACME: - cfg := d.ACME - return &provisioner.ACME{ - Type: p.Type.String(), - Name: p.Name, - ForceCN: cfg.ForceCn, - Claims: claims, - Options: options, - }, nil - */ default: return nil, fmt.Errorf("provisioner %s not implemented", p.Type) } } -// claimsToCertificates converts the landlord provisioner claims type to the open source -// (step-ca) claims type. -func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { - var durs = map[string]struct { - durStr string - dur *provisioner.Duration - }{ - "minTLSDur": {durStr: c.X509.Durations.Min}, - "maxTLSDur": {durStr: c.X509.Durations.Max}, - "defaultTLSDur": {durStr: c.X509.Durations.Default}, - "minSSHUserDur": {durStr: c.Ssh.UserDurations.Min}, - "maxSSHUserDur": {durStr: c.Ssh.UserDurations.Max}, - "defaultSSHUserDur": {durStr: c.Ssh.UserDurations.Default}, - "minSSHHostDur": {durStr: c.Ssh.HostDurations.Min}, - "maxSSHHostDur": {durStr: c.Ssh.HostDurations.Max}, - "defaultSSHHostDur": {durStr: c.Ssh.HostDurations.Default}, - } - var err error - for k, v := range durs { - v.dur, err = provisioner.NewDuration(v.durStr) +func parseInstanceAge(age string) (provisioner.Duration, error) { + var instanceAge provisioner.Duration + if age != "" { + iap, err := provisioner.NewDuration(age) if err != nil { - return nil, mgmt.WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) - } - } - return &provisioner.Claims{ - MinTLSDur: durs["minTLSDur"].dur, - MaxTLSDur: durs["maxTLSDur"].dur, - DefaultTLSDur: durs["defaultTLSDur"].dur, - DisableRenewal: &c.DisableRenewal, - MinUserSSHDur: durs["minSSHUserDur"].dur, - MaxUserSSHDur: durs["maxSSHUserDur"].dur, - DefaultUserSSHDur: durs["defaultSSHUserDur"].dur, - MinHostSSHDur: durs["minSSHHostDur"].dur, - MaxHostSSHDur: durs["maxSSHHostDur"].dur, - DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, - EnableSSHCA: &c.Ssh.Enabled, - }, nil + return instanceAge, err + } + instanceAge = *iap + } + return instanceAge, nil } diff --git a/authority/provisioners_test.go b/authority/provisioners_test.go index 94b2d715..3975031b 100644 --- a/authority/provisioners_test.go +++ b/authority/provisioners_test.go @@ -56,7 +56,7 @@ func TestGetEncryptedKey(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - val, ok := tc.a.provisioners.Load("max:" + tc.kid) + val, ok := tc.a.provisioners.Load("mike:" + tc.kid) assert.Fatal(t, ok) p, ok := val.(*provisioner.JWK) assert.Fatal(t, ok) diff --git a/authority/ssh_test.go b/authority/ssh_test.go index 1662260c..8ca26af0 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -590,69 +590,6 @@ func TestSSHConfig_Validate(t *testing.T) { } } -func TestSSHPublicKey_Validate(t *testing.T) { - key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) - assert.FatalError(t, err) - - type fields struct { - Type string - Federated bool - Key jose.JSONWebKey - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - {"user", fields{"user", true, key.Public()}, false}, - {"host", fields{"host", false, key.Public()}, false}, - {"empty", fields{"", true, key.Public()}, true}, - {"badType", fields{"bad", false, key.Public()}, true}, - {"badKey", fields{"user", false, *key}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - k := &SSHPublicKey{ - Type: tt.fields.Type, - Federated: tt.fields.Federated, - Key: tt.fields.Key, - } - if err := k.Validate(); (err != nil) != tt.wantErr { - t.Errorf("SSHPublicKey.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestSSHPublicKey_PublicKey(t *testing.T) { - key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) - assert.FatalError(t, err) - pub, err := ssh.NewPublicKey(key.Public().Key) - assert.FatalError(t, err) - - type fields struct { - publicKey ssh.PublicKey - } - tests := []struct { - name string - fields fields - want ssh.PublicKey - }{ - {"ok", fields{pub}, pub}, - {"nil", fields{nil}, nil}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - k := &SSHPublicKey{ - publicKey: tt.fields.publicKey, - } - if got := k.PublicKey(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("SSHPublicKey.PublicKey() = %v, want %v", got, tt.want) - } - }) - } -} - func TestAuthority_GetSSHBastion(t *testing.T) { bastion := &Bastion{ Hostname: "bastion.local", diff --git a/authority/tls.go b/authority/tls.go index 1c8b3b8c..4c3420df 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -360,10 +360,9 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } // This method will also validate the audiences for JWK provisioners. - var ok bool - p, ok = a.provisioners.LoadByToken(token, &claims.Claims) - if !ok { - return errs.InternalServer("authority.Revoke; provisioner not found", opts...) + p, err = a.LoadProvisionerByToken(token, &claims.Claims) + if err != nil { + return err } rci.ProvisionerID = p.GetID() rci.TokenID, err = p.GetTokenID(revokeOpts.OTT) diff --git a/authority/tls_test.go b/authority/tls_test.go index 4c936f0c..cdd4c59a 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -656,7 +656,7 @@ func TestAuthority_Renew(t *testing.T) { "fail/unauthorized": func() (*renewTest, error) { return &renewTest{ cert: certNoRenew, - err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner dev:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'dev'"), code: http.StatusUnauthorized, }, nil }, @@ -856,7 +856,7 @@ func TestAuthority_Rekey(t *testing.T) { "fail/unauthorized": func() (*renewTest, error) { return &renewTest{ cert: certNoRenew, - err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner dev:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'dev'"), code: http.StatusUnauthorized, }, nil }, diff --git a/ca/adminClient.go b/ca/adminClient.go index ad708146..da2ff566 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -2,29 +2,42 @@ package ca import ( "bytes" + "crypto/x509" "encoding/json" "io" "net/http" "net/url" "path" "strconv" + "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" + "github.com/smallstep/certificates/authority/admin" + adminAPI "github.com/smallstep/certificates/authority/admin/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/cli-utils/token" + "go.step.sm/cli-utils/token/provision" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/randutil" + "go.step.sm/linkedca" + "google.golang.org/protobuf/encoding/protojson" ) var adminURLPrefix = "admin" // AdminClient implements an HTTP client for the CA server. type AdminClient struct { - client *uaClient - endpoint *url.URL - retryFunc RetryFunc - opts []ClientOption + client *uaClient + endpoint *url.URL + retryFunc RetryFunc + opts []ClientOption + x5cJWK *jose.JSONWebKey + x5cCertFile string + x5cCertStrs []string + x5cCert *x509.Certificate + x5cIssuer string + x5cSubject string } // NewAdminClient creates a new AdminClient with the given endpoint and options. @@ -44,13 +57,45 @@ func NewAdminClient(endpoint string, opts ...ClientOption) (*AdminClient, error) } return &AdminClient{ - client: newClient(tr), - endpoint: u, - retryFunc: o.retryFunc, - opts: opts, + client: newClient(tr), + endpoint: u, + retryFunc: o.retryFunc, + opts: opts, + x5cJWK: o.x5cJWK, + x5cCertFile: o.x5cCertFile, + x5cCertStrs: o.x5cCertStrs, + x5cCert: o.x5cCert, + x5cIssuer: o.x5cIssuer, + x5cSubject: o.x5cSubject, }, nil } +func (c *AdminClient) generateAdminToken(path string) (string, error) { + // A random jwt id will be used to identify duplicated tokens + jwtID, err := randutil.Hex(64) // 256 bits + if err != nil { + return "", err + } + + now := time.Now() + tokOptions := []token.Options{ + token.WithJWTID(jwtID), + token.WithKid(c.x5cJWK.KeyID), + token.WithIssuer(c.x5cIssuer), + token.WithAudience(path), + token.WithValidity(now, now.Add(token.DefaultValidity)), + token.WithX5CCerts(c.x5cCertStrs), + } + + tok, err := provision.New(c.x5cSubject, tokOptions...) + if err != nil { + return "", err + } + + return tok.SignedString(c.x5cJWK.Algorithm, c.x5cJWK.Key) + +} + func (c *AdminClient) retryOnError(r *http.Response) bool { if c.retryFunc != nil { if c.retryFunc(r.StatusCode) { @@ -138,7 +183,7 @@ func WithAdminLimit(limit int) AdminOption { } // GetAdminsPaginate returns a page from the the GET /admin/admins request to the CA. -func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { +func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*adminAPI.GetAdminsResponse, error) { var retried bool o := new(adminOptions) if err := o.apply(opts); err != nil { @@ -148,8 +193,17 @@ func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdmins Path: "/admin/admins", RawQuery: o.rawQuery(), }) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create GET %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -160,7 +214,7 @@ retry: } return nil, readAdminError(resp.Body) } - var body = new(mgmtAPI.GetAdminsResponse) + var body = new(adminAPI.GetAdminsResponse) if err := readJSON(resp.Body, body); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -184,19 +238,27 @@ func (c *AdminClient) GetAdmins(opts ...AdminOption) ([]*linkedca.Admin, error) } cursor = resp.NextCursor } - return admins, nil } // CreateAdmin performs the POST /admin/admins request to the CA. -func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*linkedca.Admin, error) { +func (c *AdminClient) CreateAdmin(createAdminRequest *adminAPI.CreateAdminRequest) (*linkedca.Admin, error) { var retried bool - body, err := json.Marshal(req) + body, err := json.Marshal(createAdminRequest) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admins"}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create GET %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client POST %s failed", u) } @@ -218,10 +280,15 @@ retry: func (c *AdminClient) RemoveAdmin(id string) error { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -238,17 +305,22 @@ retry: } // UpdateAdmin performs the PUT /admin/admins/{id} request to the CA. -func (c *AdminClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*linkedca.Admin, error) { +func (c *AdminClient) UpdateAdmin(id string, uar *adminAPI.UpdateAdminRequest) (*linkedca.Admin, error) { var retried bool body, err := json.Marshal(uar) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -268,12 +340,35 @@ retry: return adm, nil } -// GetProvisionerByName performs the GET /admin/provisioners/{name} request to the CA. -func (c *AdminClient) GetProvisionerByName(name string) (*linkedca.Provisioner, error) { +// GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. +func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) + o := new(provisionerOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + var u *url.URL + if len(o.id) > 0 { + u = c.endpoint.ResolveReference(&url.URL{ + Path: "/admin/provisioners/id", + RawQuery: o.rawQuery(), + }) + } else if len(o.name) > 0 { + u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) + } else { + return nil, errors.New("must set either name or id in method options") + } + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -285,14 +380,14 @@ retry: return nil, readAdminError(resp.Body) } var prov = new(linkedca.Provisioner) - if err := readJSON(resp.Body, prov); err != nil { + if err := readProtoJSON(resp.Body, prov); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return prov, nil } // GetProvisionersPaginate performs the GET /admin/provisioners request to the CA. -func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtAPI.GetProvisionersResponse, error) { +func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*adminAPI.GetProvisionersResponse, error) { var retried bool o := new(provisionerOptions) if err := o.apply(opts); err != nil { @@ -302,8 +397,17 @@ func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtA Path: "/admin/provisioners", RawQuery: o.rawQuery(), }) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -314,7 +418,7 @@ retry: } return nil, readAdminError(resp.Body) } - var body = new(mgmtAPI.GetProvisionersResponse) + var body = new(adminAPI.GetProvisionersResponse) if err := readJSON(resp.Body, body); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -338,17 +442,39 @@ func (c *AdminClient) GetProvisioners(opts ...AdminOption) (provisioner.List, er } cursor = resp.NextCursor } - return provs, nil } // RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. -func (c *AdminClient) RemoveProvisioner(name string) error { - var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) +func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error { + var ( + u *url.URL + retried bool + ) + + o := new(provisionerOptions) + if err := o.apply(opts); err != nil { + return err + } + + if len(o.id) > 0 { + u = c.endpoint.ResolveReference(&url.URL{ + Path: path.Join(adminURLPrefix, "provisioners/id"), + RawQuery: o.rawQuery(), + }) + } else if len(o.name) > 0 { + u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) + } else { + return errors.New("must set either name or id in method options") + } + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -367,13 +493,22 @@ retry: // CreateProvisioner performs the POST /admin/provisioners request to the CA. func (c *AdminClient) CreateProvisioner(prov *linkedca.Provisioner) (*linkedca.Provisioner, error) { var retried bool - body, err := json.Marshal(prov) + body, err := protojson.Marshal(prov) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners")}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create POST %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client POST %s failed", u) } @@ -385,48 +520,49 @@ retry: return nil, readAdminError(resp.Body) } var nuProv = new(linkedca.Provisioner) - if err := readJSON(resp.Body, nuProv); err != nil { + if err := readProtoJSON(resp.Body, nuProv); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return nuProv, nil } -// UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. -func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*linkedca.Provisioner, error) { +// UpdateProvisioner performs the PUT /admin/provisioners/{name} request to the CA. +func (c *AdminClient) UpdateProvisioner(name string, prov *linkedca.Provisioner) error { var retried bool - body, err := json.Marshal(upr) + body, err := protojson.Marshal(prov) if err != nil { - return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + return errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", id)}) req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) if err != nil { - return nil, errors.Wrapf(err, "create PUT %s request failed", u) + return errors.Wrapf(err, "create PUT %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { - return nil, errors.Wrapf(err, "client PUT %s failed", u) + return errors.Wrapf(err, "client PUT %s failed", u) } if resp.StatusCode >= 400 { if !retried && c.retryOnError(resp) { retried = true goto retry } - return nil, readAdminError(resp.Body) - } - var prov = new(linkedca.Provisioner) - if err := readJSON(resp.Body, prov); err != nil { - return nil, errors.Wrapf(err, "error reading %s", u) + return readAdminError(resp.Body) } - return prov, nil + return nil } func readAdminError(r io.ReadCloser) error { defer r.Close() - mgmtErr := new(mgmt.Error) - if err := json.NewDecoder(r).Decode(mgmtErr); err != nil { + adminErr := new(admin.Error) + if err := json.NewDecoder(r).Decode(adminErr); err != nil { return err } - return errors.New(mgmtErr.Message) + return errors.New(adminErr.Message) } diff --git a/ca/ca.go b/ca/ca.go index 1882e8a6..1ff265bf 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -17,9 +17,8 @@ import ( acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority" + adminAPI "github.com/smallstep/certificates/authority/admin/api" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" - mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" @@ -79,11 +78,12 @@ func WithDatabase(db db.AuthDB) Option { // CA is the type used to build the complete certificate authority. It builds // the HTTP server, set ups the middlewares and the HTTP handlers. type CA struct { - auth *authority.Authority - config *config.Config - srv *server.Server - opts *options - renewer *TLSRenewer + auth *authority.Authority + config *config.Config + srv *server.Server + insecureSrv *server.Server + opts *options + renewer *TLSRenewer } // New creates and initializes the CA with the given configuration and options. @@ -130,6 +130,9 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { mux := chi.NewRouter() handler := http.Handler(mux) + insecureMux := chi.NewRouter() + insecureHandler := http.Handler(insecureMux) + // Add regular CA api endpoints in / and /1.0 routerHandler := api.New(auth) routerHandler.Route(mux) @@ -154,7 +157,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { if config.DB == nil { acmeDB = nil } else { - acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB), mgmt.DefaultAuthorityID) + acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB)) if err != nil { return nil, errors.Wrap(err, "error configuring ACME DB interface") } @@ -176,12 +179,14 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) // Admin API Router - adminDB := auth.GetAdminDatabase() - if adminDB != nil { - mgmtHandler := mgmtAPI.NewHandler(auth) - mux.Route("/admin", func(r chi.Router) { - mgmtHandler.Route(r) - }) + if config.AuthorityConfig.EnableAdmin { + adminDB := auth.GetAdminDatabase() + if adminDB != nil { + adminHandler := adminAPI.NewHandler(auth) + mux.Route("/admin", func(r chi.Router) { + adminHandler.Route(r) + }) + } } if ca.shouldServeSCEPEndpoints() { diff --git a/ca/client.go b/ca/client.go index 3a3350ac..8997fbd5 100644 --- a/ca/client.go +++ b/ca/client.go @@ -10,8 +10,10 @@ import ( "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/hex" "encoding/json" + "encoding/pem" "io" "io/ioutil" "net/http" @@ -28,10 +30,13 @@ import ( "github.com/smallstep/certificates/ca/identity" "github.com/smallstep/certificates/errs" "go.step.sm/cli-utils/config" + "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" "go.step.sm/crypto/x509util" "golang.org/x/net/http2" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "gopkg.in/square/go-jose.v2/jwt" ) @@ -108,6 +113,12 @@ type clientOptions struct { certificate tls.Certificate getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error) retryFunc RetryFunc + x5cJWK *jose.JSONWebKey + x5cCertFile string + x5cCertStrs []string + x5cCert *x509.Certificate + x5cIssuer string + x5cSubject string } func (o *clientOptions) apply(opts []ClientOption) (err error) { @@ -266,9 +277,66 @@ func WithCABundle(bundle []byte) ClientOption { // WithCertificate will set the given certificate as the TLS client certificate // in the client. -func WithCertificate(crt tls.Certificate) ClientOption { +func WithCertificate(cert tls.Certificate) ClientOption { return func(o *clientOptions) error { - o.certificate = crt + o.certificate = cert + return nil + } +} + +var ( + stepOIDRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64} + stepOIDProvisioner = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 1)...) +) + +type stepProvisionerASN1 struct { + Type int + Name []byte + CredentialID []byte + KeyValuePairs []string `asn1:"optional,omitempty"` +} + +// WithAdminX5C will set the given file as the X5C certificate for use +// by the client. +func WithAdminX5C(certs []*x509.Certificate, key interface{}, passwordFile string) ClientOption { + return func(o *clientOptions) error { + // Get private key from given key file + var ( + err error + opts []jose.Option + ) + if len(passwordFile) != 0 { + opts = append(opts, jose.WithPasswordFile(passwordFile)) + } + blk, err := pemutil.Serialize(key) + if err != nil { + return errors.Wrap(err, "error serializing private key") + } + o.x5cJWK, err = jose.ParseKey(pem.EncodeToMemory(blk), opts...) + if err != nil { + return err + } + o.x5cCertStrs, err = jose.ValidateX5C(certs, o.x5cJWK.Key) + if err != nil { + return errors.Wrap(err, "error validating x5c certificate chain and key for use in x5c header") + } + + o.x5cCert = certs[0] + o.x5cSubject = o.x5cCert.Subject.CommonName + + for _, e := range o.x5cCert.Extensions { + if e.Id.Equal(stepOIDProvisioner) { + var provisioner stepProvisionerASN1 + if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil { + return errors.Wrap(err, "error unmarshaling provisioner OID from certificate") + } + o.x5cIssuer = string(provisioner.Name) + } + } + if len(o.x5cIssuer) == 0 { + return errors.New("provisioner extension not found in certificate") + } + return nil } } @@ -372,6 +440,8 @@ type ProvisionerOption func(o *provisionerOptions) error type provisionerOptions struct { cursor string limit int + id string + name string } func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) { @@ -391,6 +461,12 @@ func (o *provisionerOptions) rawQuery() string { if o.limit > 0 { v.Set("limit", strconv.Itoa(o.limit)) } + if len(o.id) > 0 { + v.Set("id", o.id) + } + if len(o.name) > 0 { + v.Set("name", o.name) + } return v.Encode() } @@ -410,6 +486,22 @@ func WithProvisionerLimit(limit int) ProvisionerOption { } } +// WithProvisionerID will request the given provisioner. +func WithProvisionerID(id string) ProvisionerOption { + return func(o *provisionerOptions) error { + o.id = id + return nil + } +} + +// WithProvisionerName will request the given provisioner. +func WithProvisionerName(name string) ProvisionerOption { + return func(o *provisionerOptions) error { + o.name = name + return nil + } +} + // Client implements an HTTP client for the CA server. type Client struct { client *uaClient @@ -1211,6 +1303,15 @@ func readJSON(r io.ReadCloser, v interface{}) error { return json.NewDecoder(r).Decode(v) } +func readProtoJSON(r io.ReadCloser, m proto.Message) error { + defer r.Close() + data, err := ioutil.ReadAll(r) + if err != nil { + return err + } + return protojson.Unmarshal(data, m) +} + func readError(r io.ReadCloser) error { defer r.Close() apiErr := new(errs.Error) diff --git a/ca/testdata/ca.json b/ca/testdata/ca.json index b094c02e..0a5149d9 100644 --- a/ca/testdata/ca.json +++ b/ca/testdata/ca.json @@ -34,7 +34,7 @@ "y": "ZhYcFQBqtErdC_pA7sOXrO7AboCEPIKP9Ik4CHJqANk" } }, { - "name": "max", + "name": "mike", "type": "jwk", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlZsWnl0dUxrWTR5enlqZXJybnN0aGcifQ.QP15wQYjZ12BLgl-XTq2Vb12G3OHAfic.X35QqAaXwnlmeCUU._2qIUp0TI8yDI7c2e9upIRdrnmB5OvtLfrYN-Su2NLBpaoYtr9O55Wo0Iryc0W2pYqnVDPvgPPes4P4nQAnzw5WhFYc1Xf1ZEetfdNhwi1x2FNwPbACBAgxm5AW40O5AAlbLcWushYASfeMBZocTGXuSGUzwFqoWD-5EDJ80TWQ7cAj3ttHrJ_3QV9hi4O9KJUCiXngN-Yz2zXrhBL4NOH2fmRbaf5c0rF8xUJIIW-TcyYJeX_Fbx1IzzKKPd9USUwkDhxD4tLa51I345xVqjuwG1PEn6nF8JKqLRVUKEKFin-ShXrfE61KceyAvm4YhWKrbJWIm3bH5Hxaphy4.TexIrIhsRxJStpE3EJ925Q", "key": { @@ -76,7 +76,7 @@ "minTLSCertDuration": "1s" } }, { - "name": "mariano", + "name": "maxey", "type": "jwk", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6Ik5SLTk5ZkVMSm1CLW1FZGllUlFFc3cifQ.Fr314BEUGTda4ICJl2uxFdjpEUGGqJEV.gBbu_DZE1ONDu14r.X-7MKMyokZIF1HTCVqqL0tTWgaC1ZGZBLLltd11ZUhQTswo_8kvgiTv3cFShj7ATF0tAY8HStyJmzLO8mKPVOPDXSwjdNsPriZclI6JWGi9iOu8pEiN9pZM6-itxan1JMcDUNg2U-P1BmKppHRbDKsOTivymfRyeUk51dBIlS54p5xNK1HFLc1YtWC1Rc_ngYVqOgqlhIrCHArAEBe3jrfUaH2ym-8fkVdwVqtxmte3XXK9g8FchsygRNnOKtRcr0TyzTUV-7bPi8_t02Zi-EHLFaSawVXWV_Qk1GeLYJR22Rp74beo-b5-lCNVp10btO0xdGySUWmCJ4v4_QZw.c8unwWycwtfdJMM_0b0fuA", "key": { diff --git a/cas/stepcas/x5c_issuer.go b/cas/stepcas/x5c_issuer.go index da4aa27e..636d22f9 100644 --- a/cas/stepcas/x5c_issuer.go +++ b/cas/stepcas/x5c_issuer.go @@ -143,7 +143,11 @@ func newX5CSigner(certFile, keyFile, password string) (jose.Signer, error) { if err != nil { return nil, err } - certs, err := jose.ValidateX5C(certFile, signer) + certs, err := pemutil.ReadCertificateBundle(certFile) + if err != nil { + return nil, errors.Wrap(err, "error reading x5c certificate chain") + } + certStrs, err := jose.ValidateX5C(certs, signer) if err != nil { return nil, errors.Wrap(err, "error validating x5c certificate chain and key") } @@ -151,7 +155,7 @@ func newX5CSigner(certFile, keyFile, password string) (jose.Signer, error) { so := new(jose.SignerOptions) so.WithType("JWT") so.WithHeader("kid", kid) - so.WithHeader("x5c", certs) + so.WithHeader("x5c", certStrs) return newJoseSigner(signer, so) } diff --git a/go.mod b/go.mod index 8a2fe4a1..15ea5fa0 100644 --- a/go.mod +++ b/go.mod @@ -23,18 +23,22 @@ require ( github.com/smallstep/nosql v0.3.6 github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 - go.step.sm/cli-utils v0.2.0 - go.step.sm/crypto v0.8.3 + go.step.sm/cli-utils v0.4.1 + go.step.sm/crypto v0.9.0 + go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 // indirect golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 google.golang.org/api v0.33.0 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 - google.golang.org/grpc v1.32.0 - google.golang.org/protobuf v1.25.0 + google.golang.org/grpc v1.38.0 + google.golang.org/protobuf v1.26.0 gopkg.in/square/go-jose.v2 v2.5.1 ) // replace github.com/smallstep/nosql => ../nosql -// replace go.step.sm/crypto => ../crypto + +//replace go.step.sm/crypto => ../crypto + +//replace go.step.sm/cli-utils => ../cli-utils replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index 80b154e1..34aea910 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -56,41 +57,60 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/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= @@ -98,12 +118,17 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -111,13 +136,16 @@ github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMu github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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= @@ -129,6 +157,7 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -136,22 +165,32 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -180,9 +219,11 @@ github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -214,6 +255,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= @@ -230,6 +273,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -253,43 +299,70 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= @@ -298,33 +371,35 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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= -<<<<<<< HEAD -======= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= ->>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -332,11 +407,14 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= 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 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -350,33 +428,28 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -<<<<<<< HEAD -<<<<<<< HEAD -======= -github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 h1:QACkVsQ7Qx4PuPDFL2OFD5u4OnYT0TkReWk9IVqaGHA= -github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23/go.mod h1:a82oNZyGV8jj9Do7dh8EkA90+esBls0CZHR6B85Qda8= ->>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) -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/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY= -github.com/micromdm/scep v1.0.0/go.mod h1:CID2SixSr5FvoauZdAFUSpQkn5MAuSy9oyURMGOJbag= -======= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -384,70 +457,101 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/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 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 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 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.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 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 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 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -457,10 +561,13 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -472,9 +579,13 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= 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= @@ -495,7 +606,9 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= @@ -507,6 +620,7 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -515,6 +629,7 @@ 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -526,14 +641,10 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec 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= -<<<<<<< HEAD -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -======= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -543,23 +654,32 @@ go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= +go.step.sm/cli-utils v0.4.0 h1:dni6gR/6/LOqfbzm/yUdgz5O12tkxX17SxA9+pRMidI= +go.step.sm/cli-utils v0.4.0/go.mod h1:1zFgatDqEJ1Y4MNStdWa0b1NPc1fvSHbDJC+wZ6iQlE= +go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= +go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= -<<<<<<< HEAD go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -======= -go.step.sm/crypto v0.8.0 h1:S4qBPyy3hR7KWLybSkHB0H14pwFfYkom4RZ96JzmXig= -go.step.sm/crypto v0.8.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= +go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7 h1:hAfzUm80XWGtFnxyVgeT/gc/3XnlVNnHD5HrLbk4Fc0= +go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 h1:ncJqviWswJT19IdnfOYQGKG1zL7IDy4lAJz1PuM3fgw= +go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25/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 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -569,8 +689,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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= @@ -642,9 +762,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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= @@ -854,6 +973,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -865,20 +986,30 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -900,5 +1031,7 @@ rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/linkedca/admin.pb.go b/linkedca/admin.pb.go deleted file mode 100644 index 02e287c1..00000000 --- a/linkedca/admin.pb.go +++ /dev/null @@ -1,240 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.8 -// source: linkedca/admin.proto - -package linkedca - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Admin_Type int32 - -const ( - Admin_UNKNOWN Admin_Type = 0 - Admin_ADMIN Admin_Type = 1 - Admin_SUPER_ADMIN Admin_Type = 2 -) - -// Enum value maps for Admin_Type. -var ( - Admin_Type_name = map[int32]string{ - 0: "UNKNOWN", - 1: "ADMIN", - 2: "SUPER_ADMIN", - } - Admin_Type_value = map[string]int32{ - "UNKNOWN": 0, - "ADMIN": 1, - "SUPER_ADMIN": 2, - } -) - -func (x Admin_Type) Enum() *Admin_Type { - p := new(Admin_Type) - *p = x - return p -} - -func (x Admin_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Admin_Type) Descriptor() protoreflect.EnumDescriptor { - return file_linkedca_admin_proto_enumTypes[0].Descriptor() -} - -func (Admin_Type) Type() protoreflect.EnumType { - return &file_linkedca_admin_proto_enumTypes[0] -} - -func (x Admin_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Admin_Type.Descriptor instead. -func (Admin_Type) EnumDescriptor() ([]byte, []int) { - return file_linkedca_admin_proto_rawDescGZIP(), []int{0, 0} -} - -type Admin struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` - ProvisionerId string `protobuf:"bytes,4,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` - Type Admin_Type `protobuf:"varint,5,opt,name=type,proto3,enum=linkedca.Admin_Type" json:"type,omitempty"` -} - -func (x *Admin) Reset() { - *x = Admin{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_admin_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Admin) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Admin) ProtoMessage() {} - -func (x *Admin) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_admin_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Admin.ProtoReflect.Descriptor instead. -func (*Admin) Descriptor() ([]byte, []int) { - return file_linkedca_admin_proto_rawDescGZIP(), []int{0} -} - -func (x *Admin) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Admin) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *Admin) GetSubject() string { - if x != nil { - return x.Subject - } - return "" -} - -func (x *Admin) GetProvisionerId() string { - if x != nil { - return x.ProvisionerId - } - return "" -} - -func (x *Admin) GetType() Admin_Type { - if x != nil { - return x.Type - } - return Admin_UNKNOWN -} - -var File_linkedca_admin_proto protoreflect.FileDescriptor - -var file_linkedca_admin_proto_rawDesc = []byte{ - 0x0a, 0x14, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, - 0x22, 0xd6, 0x01, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, - 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x55, 0x50, 0x45, - 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, - 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_linkedca_admin_proto_rawDescOnce sync.Once - file_linkedca_admin_proto_rawDescData = file_linkedca_admin_proto_rawDesc -) - -func file_linkedca_admin_proto_rawDescGZIP() []byte { - file_linkedca_admin_proto_rawDescOnce.Do(func() { - file_linkedca_admin_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_admin_proto_rawDescData) - }) - return file_linkedca_admin_proto_rawDescData -} - -var file_linkedca_admin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_linkedca_admin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_linkedca_admin_proto_goTypes = []interface{}{ - (Admin_Type)(0), // 0: linkedca.Admin.Type - (*Admin)(nil), // 1: linkedca.Admin -} -var file_linkedca_admin_proto_depIdxs = []int32{ - 0, // 0: linkedca.Admin.type:type_name -> linkedca.Admin.Type - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_linkedca_admin_proto_init() } -func file_linkedca_admin_proto_init() { - if File_linkedca_admin_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_linkedca_admin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Admin); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_linkedca_admin_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_linkedca_admin_proto_goTypes, - DependencyIndexes: file_linkedca_admin_proto_depIdxs, - EnumInfos: file_linkedca_admin_proto_enumTypes, - MessageInfos: file_linkedca_admin_proto_msgTypes, - }.Build() - File_linkedca_admin_proto = out.File - file_linkedca_admin_proto_rawDesc = nil - file_linkedca_admin_proto_goTypes = nil - file_linkedca_admin_proto_depIdxs = nil -} diff --git a/linkedca/admin.proto b/linkedca/admin.proto deleted file mode 100644 index 29c76139..00000000 --- a/linkedca/admin.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package linkedca; - -option go_package = "github.com/smallstep/certificates/linkedca"; - -message Admin { - enum Type { - UNKNOWN = 0; - ADMIN = 1; - SUPER_ADMIN = 2; - } - string id = 1; - string authority_id = 2; - string subject = 3; - string provisioner_id = 4; - Type type = 5; -} diff --git a/linkedca/doc.go b/linkedca/doc.go deleted file mode 100644 index ceddecb0..00000000 --- a/linkedca/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -package linkedca - -//go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative linkedca/provisioners.proto linkedca/admin.proto diff --git a/linkedca/provisioners.go b/linkedca/provisioners.go deleted file mode 100644 index ea46f342..00000000 --- a/linkedca/provisioners.go +++ /dev/null @@ -1,38 +0,0 @@ -package linkedca - -import ( - "encoding/json" - "fmt" -) - -// UnmarshalProvisionerDetails unmarshals details type to the specific provisioner details. -func UnmarshalProvisionerDetails(typ Provisioner_Type, data []byte) (*ProvisionerDetails, error) { - var v isProvisionerDetails_Data - switch typ { - case Provisioner_JWK: - v = new(ProvisionerDetails_JWK) - case Provisioner_OIDC: - v = new(ProvisionerDetails_OIDC) - case Provisioner_GCP: - v = new(ProvisionerDetails_GCP) - case Provisioner_AWS: - v = new(ProvisionerDetails_AWS) - case Provisioner_AZURE: - v = new(ProvisionerDetails_Azure) - case Provisioner_ACME: - v = new(ProvisionerDetails_ACME) - case Provisioner_X5C: - v = new(ProvisionerDetails_X5C) - case Provisioner_K8SSA: - v = new(ProvisionerDetails_K8SSA) - case Provisioner_SSHPOP: - v = new(ProvisionerDetails_SSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", typ) - } - - if err := json.Unmarshal(data, v); err != nil { - return nil, err - } - return &ProvisionerDetails{Data: v}, nil -} diff --git a/linkedca/provisioners.pb.go b/linkedca/provisioners.pb.go deleted file mode 100644 index 4b8ffab0..00000000 --- a/linkedca/provisioners.pb.go +++ /dev/null @@ -1,1833 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.8 -// source: linkedca/provisioners.proto - -package linkedca - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Provisioner_Type int32 - -const ( - Provisioner_NOOP Provisioner_Type = 0 - Provisioner_JWK Provisioner_Type = 1 - Provisioner_OIDC Provisioner_Type = 2 - Provisioner_GCP Provisioner_Type = 3 - Provisioner_AWS Provisioner_Type = 4 - Provisioner_AZURE Provisioner_Type = 5 - Provisioner_ACME Provisioner_Type = 6 - Provisioner_X5C Provisioner_Type = 7 - Provisioner_K8SSA Provisioner_Type = 8 - Provisioner_SSHPOP Provisioner_Type = 9 - Provisioner_SCEP Provisioner_Type = 10 -) - -// Enum value maps for Provisioner_Type. -var ( - Provisioner_Type_name = map[int32]string{ - 0: "NOOP", - 1: "JWK", - 2: "OIDC", - 3: "GCP", - 4: "AWS", - 5: "AZURE", - 6: "ACME", - 7: "X5C", - 8: "K8SSA", - 9: "SSHPOP", - 10: "SCEP", - } - Provisioner_Type_value = map[string]int32{ - "NOOP": 0, - "JWK": 1, - "OIDC": 2, - "GCP": 3, - "AWS": 4, - "AZURE": 5, - "ACME": 6, - "X5C": 7, - "K8SSA": 8, - "SSHPOP": 9, - "SCEP": 10, - } -) - -func (x Provisioner_Type) Enum() *Provisioner_Type { - p := new(Provisioner_Type) - *p = x - return p -} - -func (x Provisioner_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Provisioner_Type) Descriptor() protoreflect.EnumDescriptor { - return file_linkedca_provisioners_proto_enumTypes[0].Descriptor() -} - -func (Provisioner_Type) Type() protoreflect.EnumType { - return &file_linkedca_provisioners_proto_enumTypes[0] -} - -func (x Provisioner_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Provisioner_Type.Descriptor instead. -func (Provisioner_Type) EnumDescriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0, 0} -} - -type Provisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Type Provisioner_Type `protobuf:"varint,3,opt,name=type,proto3,enum=linkedca.Provisioner_Type" json:"type,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - Details *ProvisionerDetails `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` - Claims *Claims `protobuf:"bytes,6,opt,name=claims,proto3" json:"claims,omitempty"` - X509Template []byte `protobuf:"bytes,7,opt,name=x509_template,json=x509Template,proto3" json:"x509_template,omitempty"` - X509TemplateData []byte `protobuf:"bytes,8,opt,name=x509_template_data,json=x509TemplateData,proto3" json:"x509_template_data,omitempty"` - SshTemplate []byte `protobuf:"bytes,9,opt,name=ssh_template,json=sshTemplate,proto3" json:"ssh_template,omitempty"` - SshTemplateData []byte `protobuf:"bytes,10,opt,name=ssh_template_data,json=sshTemplateData,proto3" json:"ssh_template_data,omitempty"` -} - -func (x *Provisioner) Reset() { - *x = Provisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Provisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Provisioner) ProtoMessage() {} - -func (x *Provisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Provisioner.ProtoReflect.Descriptor instead. -func (*Provisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0} -} - -func (x *Provisioner) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Provisioner) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *Provisioner) GetType() Provisioner_Type { - if x != nil { - return x.Type - } - return Provisioner_NOOP -} - -func (x *Provisioner) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Provisioner) GetDetails() *ProvisionerDetails { - if x != nil { - return x.Details - } - return nil -} - -func (x *Provisioner) GetClaims() *Claims { - if x != nil { - return x.Claims - } - return nil -} - -func (x *Provisioner) GetX509Template() []byte { - if x != nil { - return x.X509Template - } - return nil -} - -func (x *Provisioner) GetX509TemplateData() []byte { - if x != nil { - return x.X509TemplateData - } - return nil -} - -func (x *Provisioner) GetSshTemplate() []byte { - if x != nil { - return x.SshTemplate - } - return nil -} - -func (x *Provisioner) GetSshTemplateData() []byte { - if x != nil { - return x.SshTemplateData - } - return nil -} - -type ProvisionerDetails struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Data: - // *ProvisionerDetails_JWK - // *ProvisionerDetails_OIDC - // *ProvisionerDetails_GCP - // *ProvisionerDetails_AWS - // *ProvisionerDetails_Azure - // *ProvisionerDetails_ACME - // *ProvisionerDetails_X5C - // *ProvisionerDetails_K8SSA - // *ProvisionerDetails_SSHPOP - // *ProvisionerDetails_SCEP - Data isProvisionerDetails_Data `protobuf_oneof:"data"` -} - -func (x *ProvisionerDetails) Reset() { - *x = ProvisionerDetails{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProvisionerDetails) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProvisionerDetails) ProtoMessage() {} - -func (x *ProvisionerDetails) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProvisionerDetails.ProtoReflect.Descriptor instead. -func (*ProvisionerDetails) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{1} -} - -func (m *ProvisionerDetails) GetData() isProvisionerDetails_Data { - if m != nil { - return m.Data - } - return nil -} - -func (x *ProvisionerDetails) GetJWK() *JWKProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_JWK); ok { - return x.JWK - } - return nil -} - -func (x *ProvisionerDetails) GetOIDC() *OIDCProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_OIDC); ok { - return x.OIDC - } - return nil -} - -func (x *ProvisionerDetails) GetGCP() *GCPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_GCP); ok { - return x.GCP - } - return nil -} - -func (x *ProvisionerDetails) GetAWS() *AWSProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_AWS); ok { - return x.AWS - } - return nil -} - -func (x *ProvisionerDetails) GetAzure() *AzureProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_Azure); ok { - return x.Azure - } - return nil -} - -func (x *ProvisionerDetails) GetACME() *ACMEProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_ACME); ok { - return x.ACME - } - return nil -} - -func (x *ProvisionerDetails) GetX5C() *X5CProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_X5C); ok { - return x.X5C - } - return nil -} - -func (x *ProvisionerDetails) GetK8SSA() *K8SSAProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_K8SSA); ok { - return x.K8SSA - } - return nil -} - -func (x *ProvisionerDetails) GetSSHPOP() *SSHPOPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_SSHPOP); ok { - return x.SSHPOP - } - return nil -} - -func (x *ProvisionerDetails) GetSCEP() *SCEPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_SCEP); ok { - return x.SCEP - } - return nil -} - -type isProvisionerDetails_Data interface { - isProvisionerDetails_Data() -} - -type ProvisionerDetails_JWK struct { - JWK *JWKProvisioner `protobuf:"bytes,20,opt,name=JWK,proto3,oneof"` -} - -type ProvisionerDetails_OIDC struct { - OIDC *OIDCProvisioner `protobuf:"bytes,21,opt,name=OIDC,proto3,oneof"` -} - -type ProvisionerDetails_GCP struct { - GCP *GCPProvisioner `protobuf:"bytes,22,opt,name=GCP,proto3,oneof"` -} - -type ProvisionerDetails_AWS struct { - AWS *AWSProvisioner `protobuf:"bytes,23,opt,name=AWS,proto3,oneof"` -} - -type ProvisionerDetails_Azure struct { - Azure *AzureProvisioner `protobuf:"bytes,24,opt,name=Azure,proto3,oneof"` -} - -type ProvisionerDetails_ACME struct { - ACME *ACMEProvisioner `protobuf:"bytes,25,opt,name=ACME,proto3,oneof"` -} - -type ProvisionerDetails_X5C struct { - X5C *X5CProvisioner `protobuf:"bytes,26,opt,name=X5C,proto3,oneof"` -} - -type ProvisionerDetails_K8SSA struct { - K8SSA *K8SSAProvisioner `protobuf:"bytes,27,opt,name=K8sSA,proto3,oneof"` -} - -type ProvisionerDetails_SSHPOP struct { - SSHPOP *SSHPOPProvisioner `protobuf:"bytes,28,opt,name=SSHPOP,proto3,oneof"` -} - -type ProvisionerDetails_SCEP struct { - SCEP *SCEPProvisioner `protobuf:"bytes,29,opt,name=SCEP,proto3,oneof"` -} - -func (*ProvisionerDetails_JWK) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_OIDC) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_GCP) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_AWS) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_Azure) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_ACME) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_X5C) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_K8SSA) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_SSHPOP) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_SCEP) isProvisionerDetails_Data() {} - -type ProvisionerList struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Provisioners []*Provisioner `protobuf:"bytes,1,rep,name=provisioners,proto3" json:"provisioners,omitempty"` -} - -func (x *ProvisionerList) Reset() { - *x = ProvisionerList{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProvisionerList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProvisionerList) ProtoMessage() {} - -func (x *ProvisionerList) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProvisionerList.ProtoReflect.Descriptor instead. -func (*ProvisionerList) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{2} -} - -func (x *ProvisionerList) GetProvisioners() []*Provisioner { - if x != nil { - return x.Provisioners - } - return nil -} - -type Claims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - X509 *X509Claims `protobuf:"bytes,1,opt,name=x509,proto3" json:"x509,omitempty"` - Ssh *SSHClaims `protobuf:"bytes,2,opt,name=ssh,proto3" json:"ssh,omitempty"` - DisableRenewal bool `protobuf:"varint,3,opt,name=disable_renewal,json=disableRenewal,proto3" json:"disable_renewal,omitempty"` -} - -func (x *Claims) Reset() { - *x = Claims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Claims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Claims) ProtoMessage() {} - -func (x *Claims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Claims.ProtoReflect.Descriptor instead. -func (*Claims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{3} -} - -func (x *Claims) GetX509() *X509Claims { - if x != nil { - return x.X509 - } - return nil -} - -func (x *Claims) GetSsh() *SSHClaims { - if x != nil { - return x.Ssh - } - return nil -} - -func (x *Claims) GetDisableRenewal() bool { - if x != nil { - return x.DisableRenewal - } - return false -} - -type X509Claims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Durations *Durations `protobuf:"bytes,2,opt,name=durations,proto3" json:"durations,omitempty"` -} - -func (x *X509Claims) Reset() { - *x = X509Claims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *X509Claims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*X509Claims) ProtoMessage() {} - -func (x *X509Claims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use X509Claims.ProtoReflect.Descriptor instead. -func (*X509Claims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{4} -} - -func (x *X509Claims) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *X509Claims) GetDurations() *Durations { - if x != nil { - return x.Durations - } - return nil -} - -type SSHClaims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - UserDurations *Durations `protobuf:"bytes,2,opt,name=user_durations,json=userDurations,proto3" json:"user_durations,omitempty"` - HostDurations *Durations `protobuf:"bytes,3,opt,name=host_durations,json=hostDurations,proto3" json:"host_durations,omitempty"` -} - -func (x *SSHClaims) Reset() { - *x = SSHClaims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHClaims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHClaims) ProtoMessage() {} - -func (x *SSHClaims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHClaims.ProtoReflect.Descriptor instead. -func (*SSHClaims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{5} -} - -func (x *SSHClaims) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *SSHClaims) GetUserDurations() *Durations { - if x != nil { - return x.UserDurations - } - return nil -} - -func (x *SSHClaims) GetHostDurations() *Durations { - if x != nil { - return x.HostDurations - } - return nil -} - -type Durations struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` - Min string `protobuf:"bytes,2,opt,name=min,proto3" json:"min,omitempty"` - Max string `protobuf:"bytes,3,opt,name=max,proto3" json:"max,omitempty"` -} - -func (x *Durations) Reset() { - *x = Durations{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Durations) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Durations) ProtoMessage() {} - -func (x *Durations) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Durations.ProtoReflect.Descriptor instead. -func (*Durations) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{6} -} - -func (x *Durations) GetDefault() string { - if x != nil { - return x.Default - } - return "" -} - -func (x *Durations) GetMin() string { - if x != nil { - return x.Min - } - return "" -} - -func (x *Durations) GetMax() string { - if x != nil { - return x.Max - } - return "" -} - -type JWKProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - EncryptedPrivateKey []byte `protobuf:"bytes,2,opt,name=encrypted_private_key,json=encryptedPrivateKey,proto3" json:"encrypted_private_key,omitempty"` -} - -func (x *JWKProvisioner) Reset() { - *x = JWKProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JWKProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JWKProvisioner) ProtoMessage() {} - -func (x *JWKProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JWKProvisioner.ProtoReflect.Descriptor instead. -func (*JWKProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{7} -} - -func (x *JWKProvisioner) GetPublicKey() []byte { - if x != nil { - return x.PublicKey - } - return nil -} - -func (x *JWKProvisioner) GetEncryptedPrivateKey() []byte { - if x != nil { - return x.EncryptedPrivateKey - } - return nil -} - -type OIDCProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` - ClientSecret string `protobuf:"bytes,2,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"` - ConfigurationEndpoint string `protobuf:"bytes,3,opt,name=configuration_endpoint,json=configurationEndpoint,proto3" json:"configuration_endpoint,omitempty"` - Admins []string `protobuf:"bytes,4,rep,name=admins,proto3" json:"admins,omitempty"` - Domains []string `protobuf:"bytes,5,rep,name=domains,proto3" json:"domains,omitempty"` - Groups []string `protobuf:"bytes,6,rep,name=groups,proto3" json:"groups,omitempty"` - ListenAddress string `protobuf:"bytes,7,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` - TenantId string `protobuf:"bytes,8,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` -} - -func (x *OIDCProvisioner) Reset() { - *x = OIDCProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OIDCProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OIDCProvisioner) ProtoMessage() {} - -func (x *OIDCProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OIDCProvisioner.ProtoReflect.Descriptor instead. -func (*OIDCProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{8} -} - -func (x *OIDCProvisioner) GetClientId() string { - if x != nil { - return x.ClientId - } - return "" -} - -func (x *OIDCProvisioner) GetClientSecret() string { - if x != nil { - return x.ClientSecret - } - return "" -} - -func (x *OIDCProvisioner) GetConfigurationEndpoint() string { - if x != nil { - return x.ConfigurationEndpoint - } - return "" -} - -func (x *OIDCProvisioner) GetAdmins() []string { - if x != nil { - return x.Admins - } - return nil -} - -func (x *OIDCProvisioner) GetDomains() []string { - if x != nil { - return x.Domains - } - return nil -} - -func (x *OIDCProvisioner) GetGroups() []string { - if x != nil { - return x.Groups - } - return nil -} - -func (x *OIDCProvisioner) GetListenAddress() string { - if x != nil { - return x.ListenAddress - } - return "" -} - -func (x *OIDCProvisioner) GetTenantId() string { - if x != nil { - return x.TenantId - } - return "" -} - -type GCPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ServiceAccounts []string `protobuf:"bytes,1,rep,name=service_accounts,json=serviceAccounts,proto3" json:"service_accounts,omitempty"` - ProjectIds []string `protobuf:"bytes,2,rep,name=project_ids,json=projectIds,proto3" json:"project_ids,omitempty"` - DisableCustomSans bool `protobuf:"varint,3,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,4,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` - InstanceAge string `protobuf:"bytes,5,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` -} - -func (x *GCPProvisioner) Reset() { - *x = GCPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCPProvisioner) ProtoMessage() {} - -func (x *GCPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCPProvisioner.ProtoReflect.Descriptor instead. -func (*GCPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{9} -} - -func (x *GCPProvisioner) GetServiceAccounts() []string { - if x != nil { - return x.ServiceAccounts - } - return nil -} - -func (x *GCPProvisioner) GetProjectIds() []string { - if x != nil { - return x.ProjectIds - } - return nil -} - -func (x *GCPProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *GCPProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -func (x *GCPProvisioner) GetInstanceAge() string { - if x != nil { - return x.InstanceAge - } - return "" -} - -type AWSProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Accounts []string `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` - DisableCustomSans bool `protobuf:"varint,2,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,3,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` - InstanceAge string `protobuf:"bytes,4,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` -} - -func (x *AWSProvisioner) Reset() { - *x = AWSProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AWSProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AWSProvisioner) ProtoMessage() {} - -func (x *AWSProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AWSProvisioner.ProtoReflect.Descriptor instead. -func (*AWSProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{10} -} - -func (x *AWSProvisioner) GetAccounts() []string { - if x != nil { - return x.Accounts - } - return nil -} - -func (x *AWSProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *AWSProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -func (x *AWSProvisioner) GetInstanceAge() string { - if x != nil { - return x.InstanceAge - } - return "" -} - -type AzureProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` - ResourceGroups []string `protobuf:"bytes,2,rep,name=resource_groups,json=resourceGroups,proto3" json:"resource_groups,omitempty"` - Audience string `protobuf:"bytes,3,opt,name=audience,proto3" json:"audience,omitempty"` - DisableCustomSans bool `protobuf:"varint,4,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,5,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` -} - -func (x *AzureProvisioner) Reset() { - *x = AzureProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AzureProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AzureProvisioner) ProtoMessage() {} - -func (x *AzureProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AzureProvisioner.ProtoReflect.Descriptor instead. -func (*AzureProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{11} -} - -func (x *AzureProvisioner) GetTenantId() string { - if x != nil { - return x.TenantId - } - return "" -} - -func (x *AzureProvisioner) GetResourceGroups() []string { - if x != nil { - return x.ResourceGroups - } - return nil -} - -func (x *AzureProvisioner) GetAudience() string { - if x != nil { - return x.Audience - } - return "" -} - -func (x *AzureProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *AzureProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -type ACMEProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` -} - -func (x *ACMEProvisioner) Reset() { - *x = ACMEProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ACMEProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ACMEProvisioner) ProtoMessage() {} - -func (x *ACMEProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ACMEProvisioner.ProtoReflect.Descriptor instead. -func (*ACMEProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{12} -} - -func (x *ACMEProvisioner) GetForceCn() bool { - if x != nil { - return x.ForceCn - } - return false -} - -type X5CProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Roots [][]byte `protobuf:"bytes,1,rep,name=roots,proto3" json:"roots,omitempty"` -} - -func (x *X5CProvisioner) Reset() { - *x = X5CProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *X5CProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*X5CProvisioner) ProtoMessage() {} - -func (x *X5CProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use X5CProvisioner.ProtoReflect.Descriptor instead. -func (*X5CProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{13} -} - -func (x *X5CProvisioner) GetRoots() [][]byte { - if x != nil { - return x.Roots - } - return nil -} - -type K8SSAProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"` -} - -func (x *K8SSAProvisioner) Reset() { - *x = K8SSAProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *K8SSAProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*K8SSAProvisioner) ProtoMessage() {} - -func (x *K8SSAProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use K8SSAProvisioner.ProtoReflect.Descriptor instead. -func (*K8SSAProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{14} -} - -func (x *K8SSAProvisioner) GetPublicKeys() [][]byte { - if x != nil { - return x.PublicKeys - } - return nil -} - -type SSHPOPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SSHPOPProvisioner) Reset() { - *x = SSHPOPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHPOPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHPOPProvisioner) ProtoMessage() {} - -func (x *SSHPOPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHPOPProvisioner.ProtoReflect.Descriptor instead. -func (*SSHPOPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{15} -} - -type SCEPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` - Challenge string `protobuf:"bytes,2,opt,name=challenge,proto3" json:"challenge,omitempty"` - Capabilities []string `protobuf:"bytes,3,rep,name=capabilities,proto3" json:"capabilities,omitempty"` - MinimumPublicKeyLength int32 `protobuf:"varint,4,opt,name=minimum_public_key_length,json=minimumPublicKeyLength,proto3" json:"minimum_public_key_length,omitempty"` -} - -func (x *SCEPProvisioner) Reset() { - *x = SCEPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SCEPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SCEPProvisioner) ProtoMessage() {} - -func (x *SCEPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SCEPProvisioner.ProtoReflect.Descriptor instead. -func (*SCEPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{16} -} - -func (x *SCEPProvisioner) GetForceCn() bool { - if x != nil { - return x.ForceCn - } - return false -} - -func (x *SCEPProvisioner) GetChallenge() string { - if x != nil { - return x.Challenge - } - return "" -} - -func (x *SCEPProvisioner) GetCapabilities() []string { - if x != nil { - return x.Capabilities - } - return nil -} - -func (x *SCEPProvisioner) GetMinimumPublicKeyLength() int32 { - if x != nil { - return x.MinimumPublicKeyLength - } - return 0 -} - -var File_linkedca_provisioners_proto protoreflect.FileDescriptor - -var file_linkedca_provisioners_proto_rawDesc = []byte{ - 0x0a, 0x1b, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x22, 0xfe, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, - 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, - 0x61, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x10, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x22, 0x74, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, - 0x4f, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, - 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, - 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, - 0x52, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, - 0x0a, 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, - 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x12, 0x08, - 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, 0x10, 0x0a, 0x22, 0x86, 0x04, 0x0a, 0x12, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x2c, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x4a, 0x57, 0x4b, 0x12, 0x2f, 0x0a, - 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2c, - 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x47, 0x43, 0x50, 0x12, 0x2c, 0x0a, 0x03, - 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x41, 0x57, 0x53, 0x12, 0x32, 0x0a, 0x05, 0x41, 0x7a, - 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x2f, - 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, - 0x2c, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x58, 0x35, 0x43, 0x12, 0x32, 0x0a, - 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, - 0x41, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, - 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x12, 0x2f, 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, - 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, - 0x61, 0x2e, 0x53, 0x43, 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x04, 0x53, 0x43, 0x45, 0x50, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x22, 0x4c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x22, - 0x82, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x78, 0x35, - 0x30, 0x39, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x04, - 0x78, 0x35, 0x30, 0x39, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, - 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6e, - 0x65, 0x77, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0a, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, - 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x09, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x9d, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x63, 0x0a, 0x0e, 0x4a, 0x57, - 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x65, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x65, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, - 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, - 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xeb, 0x01, 0x0a, 0x0e, 0x47, - 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, - 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, - 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, - 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0e, 0x41, 0x57, 0x53, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, - 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, - 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, - 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, - 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, - 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x0f, 0x41, 0x43, 0x4d, - 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, 0x58, 0x35, 0x43, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6f, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x22, - 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x53, 0x43, - 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, - 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, - 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, - 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x69, - 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x4c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_linkedca_provisioners_proto_rawDescOnce sync.Once - file_linkedca_provisioners_proto_rawDescData = file_linkedca_provisioners_proto_rawDesc -) - -func file_linkedca_provisioners_proto_rawDescGZIP() []byte { - file_linkedca_provisioners_proto_rawDescOnce.Do(func() { - file_linkedca_provisioners_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_provisioners_proto_rawDescData) - }) - return file_linkedca_provisioners_proto_rawDescData -} - -var file_linkedca_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_linkedca_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 17) -var file_linkedca_provisioners_proto_goTypes = []interface{}{ - (Provisioner_Type)(0), // 0: linkedca.Provisioner.Type - (*Provisioner)(nil), // 1: linkedca.Provisioner - (*ProvisionerDetails)(nil), // 2: linkedca.ProvisionerDetails - (*ProvisionerList)(nil), // 3: linkedca.ProvisionerList - (*Claims)(nil), // 4: linkedca.Claims - (*X509Claims)(nil), // 5: linkedca.X509Claims - (*SSHClaims)(nil), // 6: linkedca.SSHClaims - (*Durations)(nil), // 7: linkedca.Durations - (*JWKProvisioner)(nil), // 8: linkedca.JWKProvisioner - (*OIDCProvisioner)(nil), // 9: linkedca.OIDCProvisioner - (*GCPProvisioner)(nil), // 10: linkedca.GCPProvisioner - (*AWSProvisioner)(nil), // 11: linkedca.AWSProvisioner - (*AzureProvisioner)(nil), // 12: linkedca.AzureProvisioner - (*ACMEProvisioner)(nil), // 13: linkedca.ACMEProvisioner - (*X5CProvisioner)(nil), // 14: linkedca.X5CProvisioner - (*K8SSAProvisioner)(nil), // 15: linkedca.K8sSAProvisioner - (*SSHPOPProvisioner)(nil), // 16: linkedca.SSHPOPProvisioner - (*SCEPProvisioner)(nil), // 17: linkedca.SCEPProvisioner -} -var file_linkedca_provisioners_proto_depIdxs = []int32{ - 0, // 0: linkedca.Provisioner.type:type_name -> linkedca.Provisioner.Type - 2, // 1: linkedca.Provisioner.details:type_name -> linkedca.ProvisionerDetails - 4, // 2: linkedca.Provisioner.claims:type_name -> linkedca.Claims - 8, // 3: linkedca.ProvisionerDetails.JWK:type_name -> linkedca.JWKProvisioner - 9, // 4: linkedca.ProvisionerDetails.OIDC:type_name -> linkedca.OIDCProvisioner - 10, // 5: linkedca.ProvisionerDetails.GCP:type_name -> linkedca.GCPProvisioner - 11, // 6: linkedca.ProvisionerDetails.AWS:type_name -> linkedca.AWSProvisioner - 12, // 7: linkedca.ProvisionerDetails.Azure:type_name -> linkedca.AzureProvisioner - 13, // 8: linkedca.ProvisionerDetails.ACME:type_name -> linkedca.ACMEProvisioner - 14, // 9: linkedca.ProvisionerDetails.X5C:type_name -> linkedca.X5CProvisioner - 15, // 10: linkedca.ProvisionerDetails.K8sSA:type_name -> linkedca.K8sSAProvisioner - 16, // 11: linkedca.ProvisionerDetails.SSHPOP:type_name -> linkedca.SSHPOPProvisioner - 17, // 12: linkedca.ProvisionerDetails.SCEP:type_name -> linkedca.SCEPProvisioner - 1, // 13: linkedca.ProvisionerList.provisioners:type_name -> linkedca.Provisioner - 5, // 14: linkedca.Claims.x509:type_name -> linkedca.X509Claims - 6, // 15: linkedca.Claims.ssh:type_name -> linkedca.SSHClaims - 7, // 16: linkedca.X509Claims.durations:type_name -> linkedca.Durations - 7, // 17: linkedca.SSHClaims.user_durations:type_name -> linkedca.Durations - 7, // 18: linkedca.SSHClaims.host_durations:type_name -> linkedca.Durations - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name -} - -func init() { file_linkedca_provisioners_proto_init() } -func file_linkedca_provisioners_proto_init() { - if File_linkedca_provisioners_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_linkedca_provisioners_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Provisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProvisionerDetails); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProvisionerList); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Claims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*X509Claims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHClaims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Durations); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JWKProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OIDCProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AWSProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AzureProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ACMEProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*X5CProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*K8SSAProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHPOPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SCEPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_linkedca_provisioners_proto_msgTypes[1].OneofWrappers = []interface{}{ - (*ProvisionerDetails_JWK)(nil), - (*ProvisionerDetails_OIDC)(nil), - (*ProvisionerDetails_GCP)(nil), - (*ProvisionerDetails_AWS)(nil), - (*ProvisionerDetails_Azure)(nil), - (*ProvisionerDetails_ACME)(nil), - (*ProvisionerDetails_X5C)(nil), - (*ProvisionerDetails_K8SSA)(nil), - (*ProvisionerDetails_SSHPOP)(nil), - (*ProvisionerDetails_SCEP)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_linkedca_provisioners_proto_rawDesc, - NumEnums: 1, - NumMessages: 17, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_linkedca_provisioners_proto_goTypes, - DependencyIndexes: file_linkedca_provisioners_proto_depIdxs, - EnumInfos: file_linkedca_provisioners_proto_enumTypes, - MessageInfos: file_linkedca_provisioners_proto_msgTypes, - }.Build() - File_linkedca_provisioners_proto = out.File - file_linkedca_provisioners_proto_rawDesc = nil - file_linkedca_provisioners_proto_goTypes = nil - file_linkedca_provisioners_proto_depIdxs = nil -} diff --git a/linkedca/provisioners.proto b/linkedca/provisioners.proto deleted file mode 100644 index ae047391..00000000 --- a/linkedca/provisioners.proto +++ /dev/null @@ -1,133 +0,0 @@ -syntax = "proto3"; - -package linkedca; - -option go_package = "github.com/smallstep/certificates/linkedca"; - -message Provisioner { - enum Type { - NOOP = 0; - JWK = 1; - OIDC = 2; - GCP = 3; - AWS = 4; - AZURE = 5; - ACME = 6; - X5C = 7; - K8SSA = 8; - SSHPOP = 9; - SCEP = 10; - } - string id = 1; - string authority_id = 2; - Type type = 3; - string name = 4; - ProvisionerDetails details = 5; - Claims claims = 6; - bytes x509_template = 7; - bytes x509_template_data = 8; - bytes ssh_template = 9; - bytes ssh_template_data = 10; -} - -message ProvisionerDetails { - oneof data { - JWKProvisioner JWK = 20; - OIDCProvisioner OIDC = 21; - GCPProvisioner GCP = 22; - AWSProvisioner AWS = 23; - AzureProvisioner Azure = 24; - ACMEProvisioner ACME = 25; - X5CProvisioner X5C = 26; - K8sSAProvisioner K8sSA = 27; - SSHPOPProvisioner SSHPOP = 28; - SCEPProvisioner SCEP = 29; - } -} - -message ProvisionerList { - repeated Provisioner provisioners = 1; -} - -message Claims { - X509Claims x509 = 1; - SSHClaims ssh = 2; - bool disable_renewal = 3; -} - -message X509Claims { - bool enabled = 1; - Durations durations = 2; -} - -message SSHClaims { - bool enabled = 1; - Durations user_durations = 2; - Durations host_durations = 3; -} - -message Durations { - string default = 1; - string min = 2; - string max = 3; -} - -message JWKProvisioner { - bytes public_key = 1; - bytes encrypted_private_key = 2; -} - -message OIDCProvisioner { - string client_id = 1; - string client_secret = 2; - string configuration_endpoint = 3; - repeated string admins = 4; - repeated string domains = 5; - repeated string groups = 6; - string listen_address = 7; - string tenant_id = 8; -} - -message GCPProvisioner { - repeated string service_accounts = 1; - repeated string project_ids = 2; - bool disable_custom_sans = 3; - bool disable_trust_on_first_use = 4; - string instance_age = 5; -} - -message AWSProvisioner { - repeated string accounts = 1; - bool disable_custom_sans = 2; - bool disable_trust_on_first_use = 3; - string instance_age = 4; -} - -message AzureProvisioner { - string tenant_id = 1; - repeated string resource_groups = 2; - string audience = 3; - bool disable_custom_sans = 4; - bool disable_trust_on_first_use = 5; -} - -message ACMEProvisioner { - bool force_cn = 1; -} - -message X5CProvisioner { - repeated bytes roots = 1; -} - -message K8sSAProvisioner { - repeated bytes public_keys = 1; -} - -message SSHPOPProvisioner {} - -message SCEPProvisioner { - bool force_cn = 1; - string challenge = 2; - repeated string capabilities = 3; - int32 minimum_public_key_length = 4; -} \ No newline at end of file diff --git a/scep/api/api.go b/scep/api/api.go index e64eef83..7036ffcb 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -67,8 +67,8 @@ func New(scepAuth scep.Interface) api.RouterHandler { // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { getLink := h.Auth.GetLinkExplicit - r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) - r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) + r.MethodFunc(http.MethodGet, getLink("{name}", false, nil), h.lookupProvisioner(h.Get)) + r.MethodFunc(http.MethodPost, getLink("{name}", false, nil), h.lookupProvisioner(h.Post)) } // Get handles all SCEP GET requests @@ -185,14 +185,14 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - name := chi.URLParam(r, "provisionerID") - provisionerID, err := url.PathUnescape(name) + nameEscaped := chi.URLParam(r, "name") + name, err := url.PathUnescape(nameEscaped) if err != nil { api.WriteError(w, errors.Errorf("error url unescaping provisioner id '%s'", name)) return } - p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) + p, err := h.Auth.LoadProvisionerByName(name) if err != nil { api.WriteError(w, err) return diff --git a/scep/authority.go b/scep/authority.go index 47d1d41c..5c863b63 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -20,7 +20,7 @@ import ( // Interface is the SCEP authority interface. type Interface interface { - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetCACertificates() ([]*x509.Certificate, error) @@ -56,7 +56,7 @@ type AuthorityOptions struct { // SignAuthority is the interface for a signing authority type SignAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) } // New returns a new Authority that implements the SCEP interface. @@ -92,10 +92,10 @@ var ( } ) -// LoadProvisionerByID calls out to the SignAuthority interface to load a -// provisioner by ID. -func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { - return a.signAuth.LoadProvisionerByID(id) +// LoadProvisionerByName calls out to the SignAuthority interface to load a +// provisioner by name. +func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + return a.signAuth.LoadProvisionerByName(name) } // GetLinkExplicit returns the requested link from the directory. From 5679c9933df5077736fa1d242cef7490f0157a28 Mon Sep 17 00:00:00 2001 From: max furman Date: Sat, 3 Jul 2021 12:08:30 -0700 Subject: [PATCH 101/291] Fixes from PR review --- authority/admin/api/middleware.go | 12 ------------ authority/administrator/collection.go | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/authority/admin/api/middleware.go b/authority/admin/api/middleware.go index 137bd6f7..0261047a 100644 --- a/authority/admin/api/middleware.go +++ b/authority/admin/api/middleware.go @@ -52,15 +52,3 @@ const ( // adminContextKey account key adminContextKey = ContextKey("admin") ) - -/* -// adminFromContext searches the context for the token. Returns the -// token or an error. -func adminFromContext(ctx context.Context) (*linkedca.Admin, error) { - val, ok := ctx.Value(adminContextKey).(*linkedca.Admin) - if !ok || val == nil { - return nil, admin.NewErrorISE("admin not in context") - } - return val, nil -} -*/ diff --git a/authority/administrator/collection.go b/authority/administrator/collection.go index 47d3a35d..ff04a41f 100644 --- a/authority/administrator/collection.go +++ b/authority/administrator/collection.go @@ -184,7 +184,7 @@ func (c *Collection) Update(id string, nu *linkedca.Admin) (*linkedca.Admin, err return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", adm.Id) } if adm.Type == nu.Type { - return nil, admin.NewError(admin.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type) + return adm, nil } if adm.Type == linkedca.Admin_SUPER_ADMIN && c.SuperCount() == 1 { return nil, admin.NewError(admin.ErrorBadRequestType, "cannot change role of last super admin") From bc1434138732839236411096912752e133e7f27d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 6 Jul 2021 16:35:00 +0200 Subject: [PATCH 102/291] Fix bootstrap command. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d3c0573f..235517b2 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ ci: testcgo build bootstra%: # Using a released version of golangci-lint to take into account custom replacements in their go.mod - $Q curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.39.0 + $Q curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.39.0 .PHONY: bootstra% From 1df21b9b6a5f86df54cba905110212b583b5a1b7 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 6 Jul 2021 17:14:13 -0700 Subject: [PATCH 103/291] Addressing comments in PR review - added a bit of validation to admin create and update - using protojson where possible in admin api - fixing a few instances of admin -> acme in errors --- authority/admin/api/admin.go | 27 ++++++++++++++++++++++++--- authority/admin/api/middleware.go | 2 +- authority/admin/api/provisioner.go | 2 +- authority/admin/db/nosql/nosql.go | 4 ++-- authority/admin/errors.go | 12 ++++++------ authority/authority.go | 6 ++++++ ca/adminClient.go | 6 +++--- 7 files changed, 43 insertions(+), 16 deletions(-) diff --git a/authority/admin/api/admin.go b/authority/admin/api/admin.go index d92af5b2..bf79ebcf 100644 --- a/authority/admin/api/admin.go +++ b/authority/admin/api/admin.go @@ -18,6 +18,17 @@ type CreateAdminRequest struct { // Validate validates a new-admin request body. func (car *CreateAdminRequest) Validate() error { + if car.Subject == "" { + return admin.NewError(admin.ErrorBadRequestType, "subject cannot be empty") + } + if car.Provisioner == "" { + return admin.NewError(admin.ErrorBadRequestType, "provisioner cannot be empty") + } + switch car.Type { + case linkedca.Admin_SUPER_ADMIN, linkedca.Admin_ADMIN: + default: + return admin.NewError(admin.ErrorBadRequestType, "invalid value for admin type") + } return nil } @@ -34,6 +45,11 @@ type UpdateAdminRequest struct { // Validate validates a new-admin request body. func (uar *UpdateAdminRequest) Validate() error { + switch uar.Type { + case linkedca.Admin_SUPER_ADMIN, linkedca.Admin_ADMIN: + default: + return admin.NewError(admin.ErrorBadRequestType, "invalid value for admin type") + } return nil } @@ -52,7 +68,7 @@ func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { "admin %s not found", id)) return } - api.JSON(w, adm) + api.ProtoJSON(w, adm) } // GetAdmins returns a segment of admins associated with the authority. @@ -104,7 +120,7 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { return } - api.JSON(w, adm) + api.ProtoJSONStatus(w, adm, http.StatusCreated) } // DeleteAdmin deletes admin. @@ -127,6 +143,11 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { return } + if err := body.Validate(); err != nil { + api.WriteError(w, err) + return + } + id := chi.URLParam(r, "id") adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type}) @@ -135,5 +156,5 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { return } - api.JSON(w, adm) + api.ProtoJSON(w, adm) } diff --git a/authority/admin/api/middleware.go b/authority/admin/api/middleware.go index 0261047a..90289f85 100644 --- a/authority/admin/api/middleware.go +++ b/authority/admin/api/middleware.go @@ -14,7 +14,7 @@ type nextHTTP = func(http.ResponseWriter, *http.Request) // is enabled before servicing requests. func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - if h.db == nil { + if !h.auth.IsAdminAPIEnabled() { api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType, "administration API not enabled")) return diff --git a/authority/admin/api/provisioner.go b/authority/admin/api/provisioner.go index e63a4417..fd1a02d5 100644 --- a/authority/admin/api/provisioner.go +++ b/authority/admin/api/provisioner.go @@ -171,5 +171,5 @@ func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { api.WriteError(w, err) return } - api.ProtoJSONStatus(w, nu, http.StatusOK) + api.ProtoJSON(w, nu) } diff --git a/authority/admin/db/nosql/nosql.go b/authority/admin/db/nosql/nosql.go index 2ce4297c..18599b02 100644 --- a/authority/admin/db/nosql/nosql.go +++ b/authority/admin/db/nosql/nosql.go @@ -15,7 +15,7 @@ var ( provisionersTable = []byte("provisioners") ) -// DB is a struct that implements the AcmeDB interface. +// DB is a struct that implements the AdminDB interface. type DB struct { db nosqlDB.DB authorityID string @@ -54,7 +54,7 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface } else { oldB, err = json.Marshal(old) if err != nil { - return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old) + return errors.Wrapf(err, "error marshaling admin type: %s, value: %v", typ, old) } } diff --git a/authority/admin/errors.go b/authority/admin/errors.go index 81ae6d6f..607093b0 100644 --- a/authority/admin/errors.go +++ b/authority/admin/errors.go @@ -12,7 +12,7 @@ import ( "github.com/smallstep/certificates/logging" ) -// ProblemType is the type of the ACME problem. +// ProblemType is the type of the Admin problem. type ProblemType int const ( @@ -33,7 +33,7 @@ const ( ErrorServerInternalType ) -// String returns the string representation of the acme problem type, +// String returns the string representation of the admin problem type, // fulfilling the Stringer interface. func (ap ProblemType) String() string { switch ap { @@ -73,17 +73,17 @@ var ( ErrorNotFoundType: { typ: ErrorNotFoundType.String(), details: "resource not found", - status: 400, + status: http.StatusNotFound, }, ErrorAuthorityMismatchType: { typ: ErrorAuthorityMismatchType.String(), details: "resource not owned by authority", - status: 401, + status: http.StatusUnauthorized, }, ErrorDeletedType: { typ: ErrorDeletedType.String(), details: "resource is deleted", - status: http.StatusUnauthorized, + status: http.StatusNotFound, }, ErrorNotImplementedType: { typ: ErrorNotImplementedType.String(), @@ -104,7 +104,7 @@ var ( } ) -// Error represents an ACME +// Error represents an Admin type Error struct { Type string `json:"type"` Detail string `json:"detail"` diff --git a/authority/authority.go b/authority/authority.go index dbe803ca..0f171fa7 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -508,6 +508,12 @@ func (a *Authority) GetAdminDatabase() admin.DB { return a.adminDB } +// IsAdminAPIEnabled returns a boolean indicating whether the Admin API has +// been enabled. +func (a *Authority) IsAdminAPIEnabled() bool { + return a.config.AuthorityConfig.EnableAdmin +} + // Shutdown safely shuts down any clients, databases, etc. held by the Authority. func (a *Authority) Shutdown() error { if err := a.keyManager.Close(); err != nil { diff --git a/ca/adminClient.go b/ca/adminClient.go index da2ff566..2f3d4b5d 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -132,7 +132,7 @@ retry: return nil, readAdminError(resp.Body) } var adm = new(linkedca.Admin) - if err := readJSON(resp.Body, adm); err != nil { + if err := readProtoJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return adm, nil @@ -270,7 +270,7 @@ retry: return nil, readAdminError(resp.Body) } var adm = new(linkedca.Admin) - if err := readJSON(resp.Body, adm); err != nil { + if err := readProtoJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return adm, nil @@ -334,7 +334,7 @@ retry: return nil, readAdminError(resp.Body) } var adm = new(linkedca.Admin) - if err := readJSON(resp.Body, adm); err != nil { + if err := readProtoJSON(resp.Body, adm); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return adm, nil From f7e09af9dfbaee897dd78e48b61c48d74b06f917 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 12 Jul 2021 15:28:13 +0200 Subject: [PATCH 104/291] Implement the login command. The login commands creates a new certificate for the linked ca. This certificate will be used to sync data with the linkedca endpoint. --- commands/login.go | 219 ++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 +-- go.sum | 210 +++++--------------------------------------- 3 files changed, 247 insertions(+), 193 deletions(-) create mode 100644 commands/login.go diff --git a/commands/login.go b/commands/login.go new file mode 100644 index 00000000..0206d073 --- /dev/null +++ b/commands/login.go @@ -0,0 +1,219 @@ +package commands + +import ( + "context" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "time" + + "github.com/pkg/errors" + "github.com/urfave/cli" + "go.step.sm/cli-utils/command" + "go.step.sm/cli-utils/config" + "go.step.sm/cli-utils/errs" + "go.step.sm/cli-utils/ui" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/keyutil" + "go.step.sm/crypto/pemutil" + "go.step.sm/crypto/x509util" + "go.step.sm/linkedca" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +const loginEndpoint = "linkedca.smallstep.com:443" +const uuidPattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + +type linkedCAClaims struct { + jose.Claims + SANs []string `json:"sans"` +} + +func init() { + command.Register(cli.Command{ + Name: "login", + Usage: "create the certificates to authorize your Linked CA instance", + UsageText: `**step-ca login** **--token*= + [**--linkedca**=] [**--root**=]`, + Action: loginAction, + Description: `**step-ca login** ... + +## POSITIONAL ARGUMENTS + + +: The authority uuid provided by the web app.`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "token", + Usage: "The one-time used to authenticate with the Linked CA in order to create the initial credentials", + }, + cli.StringFlag{ + Name: "linkedca", + Usage: "The linkedca to connect to.", + Value: loginEndpoint, + }, + cli.StringFlag{ + Name: "root", + Usage: "The root certificate used to authenticate with the linkedca endpoint.", + }, + }, + }) +} + +func loginAction(ctx *cli.Context) error { + if err := errs.NumberOfArguments(ctx, 1); err != nil { + return err + } + + args := ctx.Args() + authority := args[0] + token := ctx.String("token") + endpoint := ctx.String("linkedca") + rx := regexp.MustCompile(uuidPattern) + switch { + case !rx.MatchString(authority): + return errors.Errorf("positional argument %s is not a valid uuid", authority) + case token == "": + return errs.RequiredFlag(ctx, "token") + case endpoint == "": + return errs.RequiredFlag(ctx, "linkedca") + } + + var claims linkedCAClaims + tok, err := jose.ParseSigned(token) + if err != nil { + return errors.Wrap(err, "error parsing token") + } + if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { + return errors.Wrap(err, "error parsing payload") + } + + signer, err := keyutil.GenerateDefaultSigner() + if err != nil { + return err + } + + csr, err := x509util.CreateCertificateRequest(claims.Subject, claims.SANs, signer) + if err != nil { + return err + } + block, err := pemutil.Serialize(csr) + if err != nil { + return err + } + + var options []grpc.DialOption + if root := ctx.String("root"); root != "" { + b, err := ioutil.ReadFile(root) + if err != nil { + return errors.Wrap(err, "error reading file") + } + + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(b) { + return errors.Errorf("error reading %s: no certificates were found", root) + } + + options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: pool, + }))) + } else { + options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + } + + gctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + conn, err := grpc.DialContext(gctx, endpoint, options...) + if err != nil { + return errors.Wrapf(err, "error connecting %s", endpoint) + } + + client := linkedca.NewMajordomoClient(conn) + gctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + resp, err := client.Login(gctx, &linkedca.LoginRequest{ + AuthorityId: authority, + Token: token, + PemCertificateRequest: string(pem.EncodeToMemory(block)), + }) + if err != nil { + return errors.Wrap(err, "error doing login") + } + + certData, rootData, err := parseLoginResponse(resp) + if err != nil { + return err + } + block, err = pemutil.Serialize(signer, pemutil.WithPKCS8(true)) + if err != nil { + return err + } + keyData := pem.EncodeToMemory(block) + + base := filepath.Join(config.StepPath(), "linkedca") + if err := os.MkdirAll(base, 0700); err != nil { + return errors.Wrap(err, "error creating linkedca directory") + } + rootFile := filepath.Join(base, "root_ca.crt") + certFile := filepath.Join(base, "linkedca.crt") + keyFile := filepath.Join(base, "linkedca.key") + + if err := ioutil.WriteFile(rootFile, []byte(rootData), 0600); err != nil { + return errors.Wrap(err, "error writing file") + } + if err := ioutil.WriteFile(certFile, []byte(certData), 0600); err != nil { + return errors.Wrap(err, "error writing file") + } + if err := ioutil.WriteFile(keyFile, []byte(keyData), 0600); err != nil { + return errors.Wrap(err, "error writing file") + } + + ui.PrintSelected("Certificate", certFile) + ui.PrintSelected("Key", keyFile) + ui.PrintSelected("Root", rootFile) + return nil +} + +func parseLoginResponse(resp *linkedca.LoginResponse) ([]byte, []byte, error) { + var block *pem.Block + var bundle []*x509.Certificate + b := []byte(resp.PemCertificateChain) + for len(b) > 0 { + block, b = pem.Decode(b) + if block == nil { + break + } + if block.Type != "CERTIFICATE" { + return nil, nil, errors.New("error decoding login response: pemCertificateChain is not a certificate bundle") + } + crt, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, nil, errors.Wrap(err, "error parsing login response") + } + bundle = append(bundle, crt) + } + if len(bundle) == 0 { + return nil, nil, errors.New("error decoding login response: pemCertificateChain should not be empty") + } + + last := len(bundle) - 1 + + certBytes := []byte(resp.PemCertificate) + for i := 0; i < last; i++ { + certBytes = append(certBytes, pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: bundle[i].Raw, + })...) + } + + return certBytes, pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: bundle[last].Raw, + }), nil +} diff --git a/go.mod b/go.mod index 58228557..591c35ce 100644 --- a/go.mod +++ b/go.mod @@ -28,13 +28,14 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 + go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 + golang.org/x/net v0.0.0-20210614182718-04defd469f4e + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect google.golang.org/api v0.47.0 - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c - google.golang.org/grpc v1.38.0 - google.golang.org/protobuf v1.26.0 + google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f + google.golang.org/grpc v1.39.0 + google.golang.org/protobuf v1.27.1 gopkg.in/square/go-jose.v2 v2.5.1 ) diff --git a/go.sum b/go.sum index e2e71580..2688662a 100644 --- a/go.sum +++ b/go.sum @@ -45,7 +45,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -55,77 +54,57 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= -github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= 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/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= -github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= 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/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= 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 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/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= @@ -133,32 +112,24 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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= @@ -170,7 +141,6 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -178,13 +148,9 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -192,32 +158,24 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +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 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.4.0 h1:KeVK+Emj3c3S4eRztFuzbFYb2BAgf2jmwDwyXEri7Lo= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 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-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 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= @@ -227,17 +185,13 @@ github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8H github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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= @@ -267,7 +221,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= @@ -289,13 +242,10 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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= @@ -320,101 +270,67 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 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 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -422,16 +338,12 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= 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 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -446,132 +358,90 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/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 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.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 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 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 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= +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= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -579,13 +449,10 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -597,13 +464,9 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= 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= @@ -613,17 +476,13 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -636,18 +495,14 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc 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 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= 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= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -657,7 +512,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 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= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -669,31 +523,19 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= -go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= -go.step.sm/cli-utils v0.4.0 h1:dni6gR/6/LOqfbzm/yUdgz5O12tkxX17SxA9+pRMidI= -go.step.sm/cli-utils v0.4.0/go.mod h1:1zFgatDqEJ1Y4MNStdWa0b1NPc1fvSHbDJC+wZ6iQlE= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= -go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= -go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= -go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7 h1:hAfzUm80XWGtFnxyVgeT/gc/3XnlVNnHD5HrLbk4Fc0= -go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= -go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 h1:ncJqviWswJT19IdnfOYQGKG1zL7IDy4lAJz1PuM3fgw= -go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a h1:bu8HRqaJeZpXyAdULY3lptl1U0TrwAfm0WMwxWtG0JY= +go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a/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 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -786,10 +628,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/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= @@ -870,11 +711,9 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= @@ -943,6 +782,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f 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.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= @@ -1005,6 +845,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1022,10 +863,9 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d h1:KzwjikDymrEmYYbdyfievTwjEeGlu+OM6oiKBkF3Jfg= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f h1:khwpF3oSk7GIab/7DDMDyE8cPQEO6FAfOcWHIRAhO20= +google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1044,6 +884,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= @@ -1051,8 +892,9 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1065,33 +907,27 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= @@ -1107,7 +943,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 49c1427d1576edd1cd62db6bb6688f7749706d46 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 12 Jul 2021 15:31:05 +0200 Subject: [PATCH 105/291] Use authorityId instead of authorityID. In json or javascript world authorityId, userId, ... are more common than authorityID, ... --- authority/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/config/config.go b/authority/config/config.go index 9ad1ff5f..fabd3f91 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -83,7 +83,7 @@ type ASN1DN struct { // cas.Options. type AuthConfig struct { *cas.Options - AuthorityID string `json:"authorityID,omitempty"` + AuthorityID string `json:"authorityId,omitempty"` Provisioners provisioner.List `json:"provisioners"` Admins []*linkedca.Admin `json:"-"` Template *ASN1DN `json:"template,omitempty"` From dd9850ce4c0a5843714cc2320ba84cbd8a49fa22 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 12 Jul 2021 18:11:00 +0200 Subject: [PATCH 106/291] Add working implementation of the linkedca. Replaces the authority adminDB with a new impmentation that users the linkedca client to retrieve the data. Note that this implementation still hardcodes the endpoint to localhost. --- authority/authority.go | 15 +++- authority/linkedca.go | 171 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 authority/linkedca.go diff --git a/authority/authority.go b/authority/authority.go index 0f171fa7..48b7a566 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -442,10 +442,17 @@ func (a *Authority) init() error { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. if a.adminDB == nil { - // Check if AuthConfig already exists - a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) - if err != nil { - return err + if a.config.AuthorityConfig.AuthorityID == "" { + // Check if AuthConfig already exists + a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) + if err != nil { + return err + } + } else { + a.adminDB, err = createLinkedCAClient(a.config.AuthorityConfig.AuthorityID, "localhost:6040") + if err != nil { + return err + } } } diff --git a/authority/linkedca.go b/authority/linkedca.go new file mode 100644 index 00000000..0d9a748f --- /dev/null +++ b/authority/linkedca.go @@ -0,0 +1,171 @@ +package authority + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "path/filepath" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/errs" + "go.step.sm/cli-utils/config" + "go.step.sm/linkedca" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type linkedCaClient struct { + client linkedca.MajordomoClient + authorityID string +} + +func createLinkedCAClient(authorityID, endpoint string) (*linkedCaClient, error) { + base := filepath.Join(config.StepPath(), "linkedca") + rootFile := filepath.Join(base, "root_ca.crt") + certFile := filepath.Join(base, "linkedca.crt") + keyFile := filepath.Join(base, "linkedca.key") + + b, err := ioutil.ReadFile(rootFile) + if err != nil { + return nil, errors.Wrap(err, "error reading linkedca root") + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(b) { + return nil, errors.Errorf("error reading %s: no certificates were found", rootFile) + } + + conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: pool, + GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return nil, errors.Wrap(err, "error reading linkedca certificate") + } + return &cert, nil + }, + }))) + if err != nil { + return nil, errors.Wrapf(err, "error connecting %s", endpoint) + } + + return &linkedCaClient{ + client: linkedca.NewMajordomoClient(conn), + authorityID: authorityID, + }, nil +} + +func (c *linkedCaClient) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + resp, err := c.client.CreateProvisioner(ctx, &linkedca.CreateProvisionerRequest{ + Type: prov.Type, + Name: prov.Name, + Details: prov.Details, + Claims: prov.Claims, + X509Template: prov.X509Template, + SshTemplate: prov.SshTemplate, + }) + if err != nil { + return errors.Wrap(err, "error creating provisioner") + } + prov.Id = resp.Id + prov.AuthorityId = resp.AuthorityId + return nil +} + +func (c *linkedCaClient) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting provisioners") + } + for _, p := range resp.Provisioners { + if p.Id == id { + return p, nil + } + } + return nil, errs.NotFound("provisioner not found") +} + +func (c *linkedCaClient) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting provisioners") + } + return resp.Provisioners, nil +} + +func (c *linkedCaClient) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + _, err := c.client.UpdateProvisioner(ctx, &linkedca.UpdateProvisionerRequest{ + Id: prov.Id, + Name: prov.Name, + Details: prov.Details, + Claims: prov.Claims, + X509Template: prov.X509Template, + SshTemplate: prov.SshTemplate, + }) + return errors.Wrap(err, "error updating provisioner") +} + +func (c *linkedCaClient) DeleteProvisioner(ctx context.Context, id string) error { + _, err := c.client.DeleteProvisioner(ctx, &linkedca.DeleteProvisionerRequest{ + Id: id, + }) + return errors.Wrap(err, "error deleting provisioner") +} + +func (c *linkedCaClient) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { + resp, err := c.client.CreateAdmin(ctx, &linkedca.CreateAdminRequest{ + Subject: adm.Subject, + ProvisionerId: adm.ProvisionerId, + Type: adm.Type, + }) + if err != nil { + return errors.Wrap(err, "error creating admin") + } + adm.Id = resp.Id + adm.AuthorityId = resp.AuthorityId + return nil +} + +func (c *linkedCaClient) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting admins") + } + for _, a := range resp.Admins { + if a.Id == id { + return a, nil + } + } + return nil, errs.NotFound("admin not found") +} + +func (c *linkedCaClient) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting admins") + } + return resp.Admins, nil +} + +func (c *linkedCaClient) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { + _, err := c.client.UpdateAdmin(ctx, &linkedca.UpdateAdminRequest{ + Id: adm.Id, + Type: adm.Type, + }) + return errors.Wrap(err, "error updating admin") +} + +func (c *linkedCaClient) DeleteAdmin(ctx context.Context, id string) error { + _, err := c.client.DeleteAdmin(ctx, &linkedca.DeleteAdminRequest{ + Id: id, + }) + return errors.Wrap(err, "error deleting admin") +} From edb01bc9f27233d46f214b68a2c713b777459d05 Mon Sep 17 00:00:00 2001 From: Hilko Bengen Date: Wed, 14 Jul 2021 11:25:56 +0200 Subject: [PATCH 107/291] Log certificate's serial number as stringified decimal number Using a JSON string fixes a common issue with JSON parsers that deserialize all numbers to a 64-bit IEEE-754 floats. (Certificate serial numbers are usually 128 bit values.) This change is consistent with existing log entries for revocation requests. See also: #630, #631 --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 21950ccd..f0817ff6 100644 --- a/api/api.go +++ b/api/api.go @@ -400,7 +400,7 @@ func logOtt(w http.ResponseWriter, token string) { func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if rl, ok := w.(logging.ResponseLogger); ok { m := map[string]interface{}{ - "serial": cert.SerialNumber, + "serial": cert.SerialNumber.String(), "subject": cert.Subject.CommonName, "issuer": cert.Issuer.CommonName, "valid-from": cert.NotBefore.Format(time.RFC3339), From 9210a6740b2a564924981208ef4c340648fd1260 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 15 Jul 2021 23:13:08 +0200 Subject: [PATCH 108/291] Fix logging provisioner name as string --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 21950ccd..b542f473 100644 --- a/api/api.go +++ b/api/api.go @@ -418,7 +418,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if len(val.CredentialID) > 0 { m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) } else { - m["provisioner"] = val.Name + m["provisioner"] = string(val.Name) } break } From b71ff09a082975fc2f9671de7d84ec445262db39 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 16 Jul 2021 10:50:22 -0700 Subject: [PATCH 109/291] UI updates for certificates new issue page --- .github/ISSUE_TEMPLATE/enhancement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md index 28eec406..3a6ffc94 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -1,5 +1,5 @@ --- -name: Certificates Enhancement +name: Enhancement about: Suggest an enhancement to step certificates title: '' labels: enhancement, needs triage From a3af991261f9734a333e2ad64ce80e861df45485 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 16 Jul 2021 12:15:03 -0700 Subject: [PATCH 110/291] Update pull request labeler action --- .github/workflows/labeler.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 72b01a92..bf5b4bc0 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,14 +1,12 @@ -name: labeler +name: Pull Request Labeler on: - pull_request: - branches: - - master + pull_request_target jobs: label: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v3 + - uses: actions/labeler@v3.0.2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/labeler.yml + From bd51b1f85b3aba74963a2a414fc7da2d13cc0bcc Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 16 Jul 2021 15:09:38 -0700 Subject: [PATCH 111/291] Updates for new issue page --- .github/ISSUE_TEMPLATE/config.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..4a273c46 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +blank_issues_enabled: true +contact_links: + - name: Ask on Discord + url: https://discord.gg/7xgjhVAg6g + about: You can ask for help here! + - name: Want to contribute to step certificates? + url: https://github.com/smallstep/certificates/blob/master/docs/CONTRIBUTING.md + about: Be sure to read contributing guidelines! + From 3e5b90b6fac1e33a0bf6db24ec9a2af87dec234f Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Mon, 19 Jul 2021 08:34:22 -0500 Subject: [PATCH 112/291] systemd cert renewer can now use 'step certificate needs-renewal' --- systemd/cert-renewer@.service | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/systemd/cert-renewer@.service b/systemd/cert-renewer@.service index f38951b5..7c4c4e3c 100644 --- a/systemd/cert-renewer@.service +++ b/systemd/cert-renewer@.service @@ -15,11 +15,7 @@ Environment=STEPPATH=/etc/step-ca \ ; ExecStartPre checks if the certificate is ready for renewal, ; based on the exit status of the command. ; (In systemd 243 and above, you can use ExecCondition= here.) -ExecStartPre=/usr/bin/env bash -c \ - 'step certificate inspect $CERT_LOCATION --format json --roots "$STEPPATH/certs/root_ca.crt" | \ - jq -e "(((.validity.start | fromdate) + \ - ((.validity.end | fromdate) - (.validity.start | fromdate)) * 0.66) \ - - now) <= 0" > /dev/null' +ExecStartPre=/usr/bin/step certificate needs-renewal $CERT_LOCATION --roots $STEPPATH/certs/root_ca.crt ; ExecStart renews the certificate, if ExecStartPre was successful. ExecStart=/usr/bin/step ca renew --force $CERT_LOCATION $KEY_LOCATION From 0dd6564b1e16627b160e491f9e4b4395221e2d90 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Mon, 19 Jul 2021 13:05:01 -0500 Subject: [PATCH 113/291] README link fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f0649175..544dd5b5 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,10 @@ You can issue certificates in exchange for: - ID tokens from Okta, GSuite, Azure AD, Auth0. - ID tokens from an OAuth OIDC service that you host, like [Keycloak](https://www.keycloak.org/) or [Dex](https://github.com/dexidp/dex) - [Cloud instance identity documents](https://smallstep.com/blog/embarrassingly-easy-certificates-on-aws-azure-gcp/), for VMs on AWS, GCP, and Azure -- [Single-use, short-lived JWK tokens]() issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc. +- [Single-use, short-lived JWK tokens](https://smallstep.com/docs/step-ca/provisioners#jwk) issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc. - A trusted X.509 certificate (X5C provisioner) - Expiring SSH host certificates needing rotation (the SSHPOP provisioner) -- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/configuration#jwk) +- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/provisioners) ### 🏔 Your own private ACME server From 8fb5340dc9aa779dab96cb3d5b8672a572759ca6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 19 Jul 2021 19:28:06 -0700 Subject: [PATCH 114/291] Use a token at start time to configure linkedca. Instead of using `step-ca login` we will use a new token provided as a flag to configure and start linkedca. Certificates will be kept in memory and refreshed automatically. --- authority/authority.go | 30 +++-- authority/linkedca.go | 266 +++++++++++++++++++++++++++++++++++++---- authority/options.go | 9 ++ ca/ca.go | 13 ++ commands/app.go | 8 +- commands/login.go | 151 +++++++++++++++-------- go.mod | 3 +- 7 files changed, 397 insertions(+), 83 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 48b7a566..80242e8b 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "encoding/hex" "log" + "strings" "sync" "time" @@ -33,13 +34,14 @@ import ( // Authority implements the Certificate Authority internal interface. type Authority struct { - config *config.Config - keyManager kms.KeyManager - provisioners *provisioner.Collection - admins *administrator.Collection - db db.AuthDB - adminDB admin.DB - templates *templates.Templates + config *config.Config + keyManager kms.KeyManager + provisioners *provisioner.Collection + admins *administrator.Collection + db db.AuthDB + adminDB admin.DB + templates *templates.Templates + linkedCAToken string // X509 CA x509CAService cas.CertificateAuthorityService @@ -442,17 +444,24 @@ func (a *Authority) init() error { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. if a.adminDB == nil { - if a.config.AuthorityConfig.AuthorityID == "" { + if a.linkedCAToken == "" { // Check if AuthConfig already exists a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) if err != nil { return err } } else { - a.adminDB, err = createLinkedCAClient(a.config.AuthorityConfig.AuthorityID, "localhost:6040") + // Use the linkedca client as the admindb. + client, err := newLinkedCAClient(a.linkedCAToken) if err != nil { return err } + // If authorityId is configured make sure it matches the one in the token + if id := a.config.AuthorityConfig.AuthorityID; id != "" && !strings.EqualFold(id, client.authorityID) { + return errors.New("error initializing linkedca: token authority and configured authority do not match") + } + client.Run() + a.adminDB = client } } @@ -534,6 +543,9 @@ func (a *Authority) CloseForReload() { if err := a.keyManager.Close(); err != nil { log.Printf("error closing the key manager: %v", err) } + if client, ok := a.adminDB.(*linkedCaClient); ok { + client.Stop() + } } // requiresDecrypter returns whether the Authority diff --git a/authority/linkedca.go b/authority/linkedca.go index 0d9a748f..4e67f246 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -2,59 +2,126 @@ package authority import ( "context" + "crypto" + "crypto/sha256" "crypto/tls" "crypto/x509" - "io/ioutil" - "path/filepath" + "encoding/hex" + "encoding/pem" + "fmt" + "net/url" + "regexp" + "strings" + "time" "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "go.step.sm/cli-utils/config" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/keyutil" + "go.step.sm/crypto/tlsutil" + "go.step.sm/crypto/x509util" "go.step.sm/linkedca" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) +const uuidPattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + type linkedCaClient struct { + renewer *tlsutil.Renewer client linkedca.MajordomoClient authorityID string } -func createLinkedCAClient(authorityID, endpoint string) (*linkedCaClient, error) { - base := filepath.Join(config.StepPath(), "linkedca") - rootFile := filepath.Join(base, "root_ca.crt") - certFile := filepath.Join(base, "linkedca.crt") - keyFile := filepath.Join(base, "linkedca.key") +type linkedCAClaims struct { + jose.Claims + SANs []string `json:"sans"` + SHA string `json:"sha"` +} + +func newLinkedCAClient(token string) (*linkedCaClient, error) { + tok, err := jose.ParseSigned(token) + if err != nil { + return nil, errors.Wrap(err, "error parsing token") + } - b, err := ioutil.ReadFile(rootFile) + var claims linkedCAClaims + if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { + return nil, errors.Wrap(err, "error parsing token") + } + // Validate claims + if len(claims.Audience) != 1 { + return nil, errors.New("error parsing token: invalid aud claim") + } + if claims.SHA == "" { + return nil, errors.New("error parsing token: invalid sha claim") + } + // Get linkedCA endpoint from audience. + u, err := url.Parse(claims.Audience[0]) + if err != nil { + return nil, errors.New("error parsing token: invalid aud claim") + } + // Get authority from SANs + authority, err := getAuthority(claims.SANs) if err != nil { - return nil, errors.Wrap(err, "error reading linkedca root") + return nil, err } + + // Create csr to login with + signer, err := keyutil.GenerateDefaultSigner() + if err != nil { + return nil, err + } + csr, err := x509util.CreateCertificateRequest(claims.Subject, claims.SANs, signer) + if err != nil { + return nil, err + } + + // Get and verify root certificate + root, err := getRootCertificate(u.Host, claims.SHA) + if err != nil { + return nil, err + } + pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(b) { - return nil, errors.Errorf("error reading %s: no certificates were found", rootFile) + pool.AddCert(root) + + // Login with majordomo and get certificates + cert, tlsConfig, err := login(authority, token, csr, signer, u.Host, pool) + if err != nil { + return nil, err } - conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - RootCAs: pool, - GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, errors.Wrap(err, "error reading linkedca certificate") - } - return &cert, nil - }, - }))) + // Start TLS renewer and set the GetClientCertificate callback to it. + renewer, err := tlsutil.NewRenewer(cert, tlsConfig, func() (*tls.Certificate, *tls.Config, error) { + return login(authority, token, csr, signer, u.Host, pool) + }) if err != nil { - return nil, errors.Wrapf(err, "error connecting %s", endpoint) + return nil, err + } + tlsConfig.GetClientCertificate = renewer.GetClientCertificate + + // Start mTLS client + conn, err := grpc.Dial(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) + if err != nil { + return nil, errors.Wrapf(err, "error connecting %s", u.Host) } return &linkedCaClient{ + renewer: renewer, client: linkedca.NewMajordomoClient(conn), - authorityID: authorityID, + authorityID: authority, }, nil } +func (c *linkedCaClient) Run() { + c.renewer.Run() +} + +func (c *linkedCaClient) Stop() { + c.renewer.Stop() +} + func (c *linkedCaClient) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { resp, err := c.client.CreateProvisioner(ctx, &linkedca.CreateProvisionerRequest{ Type: prov.Type, @@ -169,3 +236,154 @@ func (c *linkedCaClient) DeleteAdmin(ctx context.Context, id string) error { }) return errors.Wrap(err, "error deleting admin") } + +func getAuthority(sans []string) (string, error) { + for _, s := range sans { + if strings.HasPrefix(s, "urn:smallstep:authority:") { + if regexp.MustCompile(uuidPattern).MatchString(s[24:]) { + return s[24:], nil + } + } + } + return "", fmt.Errorf("error parsing token: invalid sans claim") +} + +// getRootCertificate creates an insecure majordomo client and returns the +// verified root certificate. +func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + InsecureSkipVerify: true, + }))) + if err != nil { + return nil, errors.Wrapf(err, "error connecting %s", endpoint) + } + + ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + client := linkedca.NewMajordomoClient(conn) + resp, err := client.GetRootCertificate(ctx, &linkedca.GetRootCertificateRequest{ + Fingerprint: fingerprint, + }) + if err != nil { + return nil, fmt.Errorf("error getting root certificate: %w", err) + } + + var block *pem.Block + b := []byte(resp.PemCertificate) + for len(b) > 0 { + block, b = pem.Decode(b) + if block == nil { + break + } + if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + continue + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("error parsing certificate: %w", err) + } + + // verify the sha256 + sum := sha256.Sum256(cert.Raw) + if !strings.EqualFold(fingerprint, hex.EncodeToString(sum[:])) { + return nil, fmt.Errorf("error verifying certificate: SHA256 fingerprint does not match") + } + + return cert, nil + } + + return nil, fmt.Errorf("error getting root certificate: certificate not found") +} + +// login creates a new majordomo client with just the root ca pool and returns +// the signed certificate and tls configuration. +func login(authority, token string, csr *x509.CertificateRequest, signer crypto.PrivateKey, endpoint string, rootCAs *x509.CertPool) (*tls.Certificate, *tls.Config, error) { + // Connect to majordomo + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: rootCAs, + }))) + if err != nil { + return nil, nil, errors.Wrapf(err, "error connecting %s", endpoint) + } + + // Login to get the signed certificate + ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + client := linkedca.NewMajordomoClient(conn) + resp, err := client.Login(ctx, &linkedca.LoginRequest{ + AuthorityId: authority, + Token: token, + PemCertificateRequest: string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: csr.Raw, + })), + }) + if err != nil { + return nil, nil, errors.Wrapf(err, "error logging in %s", endpoint) + } + + // Parse login response + var block *pem.Block + var bundle []*x509.Certificate + rest := []byte(resp.PemCertificateChain) + for { + block, rest = pem.Decode(rest) + if block == nil { + break + } + if block.Type != "CERTIFICATE" { + return nil, nil, errors.New("error decoding login response: pemCertificateChain is not a certificate bundle") + } + crt, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, nil, errors.Wrap(err, "error parsing login response") + } + bundle = append(bundle, crt) + } + if len(bundle) == 0 { + return nil, nil, errors.New("error decoding login response: pemCertificateChain should not be empty") + } + + // Build tls.Certificate with PemCertificate and intermediates in the + // PemCertificateChain + cert := &tls.Certificate{ + PrivateKey: signer, + } + rest = []byte(resp.PemCertificate) + for { + block, rest = pem.Decode(rest) + if block == nil { + break + } + if block.Type == "CERTIFICATE" { + leaf, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, nil, errors.Wrap(err, "error parsing pemCertificate") + } + cert.Certificate = append(cert.Certificate, block.Bytes) + cert.Leaf = leaf + } + } + + // Add intermediates to the tls.Certificate + last := len(bundle) - 1 + for i := 0; i < last; i++ { + cert.Certificate = append(cert.Certificate, bundle[i].Raw) + } + + // Add root to the pool if it's not there yet + rootCAs.AddCert(bundle[last]) + + return cert, &tls.Config{ + RootCAs: rootCAs, + }, nil +} diff --git a/authority/options.go b/authority/options.go index 4e9fbdbc..6baeb2bc 100644 --- a/authority/options.go +++ b/authority/options.go @@ -196,6 +196,15 @@ func WithAdminDB(db admin.DB) Option { } } +// WithLinkedCAToken is an option to set the authentication token used to enable +// linked ca. +func WithLinkedCAToken(token string) Option { + return func(a *Authority) error { + a.linkedCAToken = token + return nil + } +} + func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) { var block *pem.Block var certs []*x509.Certificate diff --git a/ca/ca.go b/ca/ca.go index 4551286b..51d15bec 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -30,6 +30,7 @@ import ( type options struct { configFile string + linkedCAToken string password []byte issuerPassword []byte database db.AuthDB @@ -75,6 +76,13 @@ func WithDatabase(db db.AuthDB) Option { } } +// WithLinkedCAToken sets the token used to authenticate with the linkedca. +func WithLinkedCAToken(token string) Option { + return func(o *options) { + o.linkedCAToken = token + } +} + // CA is the type used to build the complete certificate authority. It builds // the HTTP server, set ups the middlewares and the HTTP handlers. type CA struct { @@ -111,6 +119,10 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { } var opts []authority.Option + if ca.opts.linkedCAToken != "" { + opts = append(opts, authority.WithLinkedCAToken(ca.opts.linkedCAToken)) + } + if ca.opts.database != nil { opts = append(opts, authority.WithDatabase(ca.opts.database)) } @@ -326,6 +338,7 @@ func (ca *CA) Reload() error { newCA, err := New(config, WithPassword(ca.opts.password), WithIssuerPassword(ca.opts.issuerPassword), + WithLinkedCAToken(ca.opts.linkedCAToken), WithConfigFile(ca.opts.configFile), WithDatabase(ca.auth.GetDatabase()), ) diff --git a/commands/app.go b/commands/app.go index 8833726c..3b874ae8 100644 --- a/commands/app.go +++ b/commands/app.go @@ -38,6 +38,10 @@ certificate issuer private key used in the RA mode.`, Name: "resolver", Usage: "address of a DNS resolver to be used instead of the default.", }, + cli.StringFlag{ + Name: "token", + Usage: "token used to enable the linked ca.", + }, }, } @@ -46,6 +50,7 @@ func appAction(ctx *cli.Context) error { passFile := ctx.String("password-file") issuerPassFile := ctx.String("issuer-password-file") resolver := ctx.String("resolver") + token := ctx.String("token") // If zero cmd line args show help, if >1 cmd line args show error. if ctx.NArg() == 0 { @@ -88,7 +93,8 @@ func appAction(ctx *cli.Context) error { srv, err := ca.New(config, ca.WithConfigFile(configFile), ca.WithPassword(password), - ca.WithIssuerPassword(issuerPassword)) + ca.WithIssuerPassword(issuerPassword), + ca.WithLinkedCAToken(token)) if err != nil { fatal(err) } diff --git a/commands/login.go b/commands/login.go index 0206d073..8c0049ee 100644 --- a/commands/login.go +++ b/commands/login.go @@ -2,13 +2,18 @@ package commands import ( "context" + "crypto/sha256" "crypto/tls" "crypto/x509" + "encoding/hex" "encoding/pem" + "fmt" "io/ioutil" + "net/url" "os" "path/filepath" "regexp" + "strings" "time" "github.com/pkg/errors" @@ -32,13 +37,14 @@ const uuidPattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4 type linkedCAClaims struct { jose.Claims SANs []string `json:"sans"` + SHA string `json:"sha"` } func init() { command.Register(cli.Command{ Name: "login", Usage: "create the certificates to authorize your Linked CA instance", - UsageText: `**step-ca login** **--token*= + UsageText: `**step-ca login** **--token*= [**--linkedca**=] [**--root**=]`, Action: loginAction, Description: `**step-ca login** ... @@ -50,16 +56,7 @@ func init() { Flags: []cli.Flag{ cli.StringFlag{ Name: "token", - Usage: "The one-time used to authenticate with the Linked CA in order to create the initial credentials", - }, - cli.StringFlag{ - Name: "linkedca", - Usage: "The linkedca to connect to.", - Value: loginEndpoint, - }, - cli.StringFlag{ - Name: "root", - Usage: "The root certificate used to authenticate with the linkedca endpoint.", + Usage: "The used to authenticate with the Linked CA in order to create the initial credentials", }, }, }) @@ -70,18 +67,9 @@ func loginAction(ctx *cli.Context) error { return err } - args := ctx.Args() - authority := args[0] token := ctx.String("token") - endpoint := ctx.String("linkedca") - rx := regexp.MustCompile(uuidPattern) - switch { - case !rx.MatchString(authority): - return errors.Errorf("positional argument %s is not a valid uuid", authority) - case token == "": + if token == "" { return errs.RequiredFlag(ctx, "token") - case endpoint == "": - return errs.RequiredFlag(ctx, "linkedca") } var claims linkedCAClaims @@ -90,50 +78,58 @@ func loginAction(ctx *cli.Context) error { return errors.Wrap(err, "error parsing token") } if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { - return errors.Wrap(err, "error parsing payload") + return errors.Wrap(err, "error parsing token") } - - signer, err := keyutil.GenerateDefaultSigner() + if len(claims.Audience) != 0 { + return errors.Wrap(err, "error parsing token: invalid aud claim") + } + u, err := url.Parse(claims.Audience[0]) if err != nil { - return err + return errors.Wrap(err, "error parsing token: invalid aud claim") } - - csr, err := x509util.CreateCertificateRequest(claims.Subject, claims.SANs, signer) + if claims.SHA == "" { + return errors.Wrap(err, "error parsing token: invalid sha claim") + } + authority, err := getAuthority(claims.SANs) if err != nil { return err } - block, err := pemutil.Serialize(csr) + + // Get and verify root certificate + root, err := getRootCertificate(u.Host, claims.SHA) if err != nil { return err } - var options []grpc.DialOption - if root := ctx.String("root"); root != "" { - b, err := ioutil.ReadFile(root) - if err != nil { - return errors.Wrap(err, "error reading file") - } + pool := x509.NewCertPool() + pool.AddCert(root) - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(b) { - return errors.Errorf("error reading %s: no certificates were found", root) - } + gctx, cancel := context.WithCancel(context.Background()) + defer cancel() - options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - RootCAs: pool, - }))) - } else { - options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + conn, err := grpc.DialContext(gctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: pool, + }))) + if err != nil { + return errors.Wrapf(err, "error connecting %s", u.Host) } - gctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() + // Create csr + signer, err := keyutil.GenerateDefaultSigner() + if err != nil { + return err + } - conn, err := grpc.DialContext(gctx, endpoint, options...) + csr, err := x509util.CreateCertificateRequest(claims.Subject, claims.SANs, signer) + if err != nil { + return err + } + block, err := pemutil.Serialize(csr) if err != nil { - return errors.Wrapf(err, "error connecting %s", endpoint) + return err } + // Perform login and get signed certificate client := linkedca.NewMajordomoClient(conn) gctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) defer cancel() @@ -180,6 +176,67 @@ func loginAction(ctx *cli.Context) error { return nil } +func getAuthority(sans []string) (string, error) { + for _, s := range sans { + if strings.HasPrefix(s, "urn:smallstep:authority:") { + if regexp.MustCompile(uuidPattern).MatchString(s[24:]) { + return s[24:], nil + } + } + } + return "", fmt.Errorf("error parsing token: invalid sans claim") +} + +func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + InsecureSkipVerify: true, + }))) + if err != nil { + return nil, errors.Wrapf(err, "error connecting %s", endpoint) + } + + ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + client := linkedca.NewMajordomoClient(conn) + resp, err := client.GetRootCertificate(ctx, &linkedca.GetRootCertificateRequest{ + Fingerprint: fingerprint, + }) + if err != nil { + return nil, fmt.Errorf("error getting root certificate: %w", err) + } + + var block *pem.Block + b := []byte(resp.PemCertificate) + for len(b) > 0 { + block, b = pem.Decode(b) + if block == nil { + break + } + if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + continue + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("error parsing certificate: %w", err) + } + + // verify the sha256 + sum := sha256.Sum256(cert.Raw) + if !strings.EqualFold(fingerprint, hex.EncodeToString(sum[:])) { + return nil, fmt.Errorf("error verifying certificate: SHA256 fingerprint does not match") + } + + return cert, nil + } + + return nil, fmt.Errorf("error getting root certificate: certificate not found") +} + func parseLoginResponse(resp *linkedca.LoginResponse) ([]byte, []byte, error) { var block *pem.Block var bundle []*x509.Certificate diff --git a/go.mod b/go.mod index 591c35ce..86e0db73 100644 --- a/go.mod +++ b/go.mod @@ -40,9 +40,8 @@ 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 replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 From f8c137af4ff0530400683ea24922e67d56cd07c3 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 20 Jul 2021 10:32:18 -0500 Subject: [PATCH 115/291] Update provisioners.md --- docs/provisioners.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/provisioners.md b/docs/provisioners.md index 7ee9af50..18010f88 100644 --- a/docs/provisioners.md +++ b/docs/provisioners.md @@ -1,7 +1,7 @@ # Provisioners > Note: The canonical documentation for `step-ca` provisioners now lives at -> https://smallstep.com/docs/step-ca/configuration#provisioners. Documentation +> https://smallstep.com/docs/step-ca/provisioners. Documentation > found on this page may be out of date. Provisioners are people or code that are registered with the CA and authorized From 7c0faab73e1ae372c3612dca55cb6ffad94b0223 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 20 Jul 2021 12:57:34 -0700 Subject: [PATCH 116/291] Remove now unused step-ca login. --- commands/login.go | 276 ---------------------------------------------- 1 file changed, 276 deletions(-) delete mode 100644 commands/login.go diff --git a/commands/login.go b/commands/login.go deleted file mode 100644 index 8c0049ee..00000000 --- a/commands/login.go +++ /dev/null @@ -1,276 +0,0 @@ -package commands - -import ( - "context" - "crypto/sha256" - "crypto/tls" - "crypto/x509" - "encoding/hex" - "encoding/pem" - "fmt" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "regexp" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/urfave/cli" - "go.step.sm/cli-utils/command" - "go.step.sm/cli-utils/config" - "go.step.sm/cli-utils/errs" - "go.step.sm/cli-utils/ui" - "go.step.sm/crypto/jose" - "go.step.sm/crypto/keyutil" - "go.step.sm/crypto/pemutil" - "go.step.sm/crypto/x509util" - "go.step.sm/linkedca" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -const loginEndpoint = "linkedca.smallstep.com:443" -const uuidPattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" - -type linkedCAClaims struct { - jose.Claims - SANs []string `json:"sans"` - SHA string `json:"sha"` -} - -func init() { - command.Register(cli.Command{ - Name: "login", - Usage: "create the certificates to authorize your Linked CA instance", - UsageText: `**step-ca login** **--token*= - [**--linkedca**=] [**--root**=]`, - Action: loginAction, - Description: `**step-ca login** ... - -## POSITIONAL ARGUMENTS - - -: The authority uuid provided by the web app.`, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "token", - Usage: "The used to authenticate with the Linked CA in order to create the initial credentials", - }, - }, - }) -} - -func loginAction(ctx *cli.Context) error { - if err := errs.NumberOfArguments(ctx, 1); err != nil { - return err - } - - token := ctx.String("token") - if token == "" { - return errs.RequiredFlag(ctx, "token") - } - - var claims linkedCAClaims - tok, err := jose.ParseSigned(token) - if err != nil { - return errors.Wrap(err, "error parsing token") - } - if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { - return errors.Wrap(err, "error parsing token") - } - if len(claims.Audience) != 0 { - return errors.Wrap(err, "error parsing token: invalid aud claim") - } - u, err := url.Parse(claims.Audience[0]) - if err != nil { - return errors.Wrap(err, "error parsing token: invalid aud claim") - } - if claims.SHA == "" { - return errors.Wrap(err, "error parsing token: invalid sha claim") - } - authority, err := getAuthority(claims.SANs) - if err != nil { - return err - } - - // Get and verify root certificate - root, err := getRootCertificate(u.Host, claims.SHA) - if err != nil { - return err - } - - pool := x509.NewCertPool() - pool.AddCert(root) - - gctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - conn, err := grpc.DialContext(gctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - RootCAs: pool, - }))) - if err != nil { - return errors.Wrapf(err, "error connecting %s", u.Host) - } - - // Create csr - signer, err := keyutil.GenerateDefaultSigner() - if err != nil { - return err - } - - csr, err := x509util.CreateCertificateRequest(claims.Subject, claims.SANs, signer) - if err != nil { - return err - } - block, err := pemutil.Serialize(csr) - if err != nil { - return err - } - - // Perform login and get signed certificate - client := linkedca.NewMajordomoClient(conn) - gctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - resp, err := client.Login(gctx, &linkedca.LoginRequest{ - AuthorityId: authority, - Token: token, - PemCertificateRequest: string(pem.EncodeToMemory(block)), - }) - if err != nil { - return errors.Wrap(err, "error doing login") - } - - certData, rootData, err := parseLoginResponse(resp) - if err != nil { - return err - } - block, err = pemutil.Serialize(signer, pemutil.WithPKCS8(true)) - if err != nil { - return err - } - keyData := pem.EncodeToMemory(block) - - base := filepath.Join(config.StepPath(), "linkedca") - if err := os.MkdirAll(base, 0700); err != nil { - return errors.Wrap(err, "error creating linkedca directory") - } - rootFile := filepath.Join(base, "root_ca.crt") - certFile := filepath.Join(base, "linkedca.crt") - keyFile := filepath.Join(base, "linkedca.key") - - if err := ioutil.WriteFile(rootFile, []byte(rootData), 0600); err != nil { - return errors.Wrap(err, "error writing file") - } - if err := ioutil.WriteFile(certFile, []byte(certData), 0600); err != nil { - return errors.Wrap(err, "error writing file") - } - if err := ioutil.WriteFile(keyFile, []byte(keyData), 0600); err != nil { - return errors.Wrap(err, "error writing file") - } - - ui.PrintSelected("Certificate", certFile) - ui.PrintSelected("Key", keyFile) - ui.PrintSelected("Root", rootFile) - return nil -} - -func getAuthority(sans []string) (string, error) { - for _, s := range sans { - if strings.HasPrefix(s, "urn:smallstep:authority:") { - if regexp.MustCompile(uuidPattern).MatchString(s[24:]) { - return s[24:], nil - } - } - } - return "", fmt.Errorf("error parsing token: invalid sans claim") -} - -func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - InsecureSkipVerify: true, - }))) - if err != nil { - return nil, errors.Wrapf(err, "error connecting %s", endpoint) - } - - ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - client := linkedca.NewMajordomoClient(conn) - resp, err := client.GetRootCertificate(ctx, &linkedca.GetRootCertificateRequest{ - Fingerprint: fingerprint, - }) - if err != nil { - return nil, fmt.Errorf("error getting root certificate: %w", err) - } - - var block *pem.Block - b := []byte(resp.PemCertificate) - for len(b) > 0 { - block, b = pem.Decode(b) - if block == nil { - break - } - if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { - continue - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, fmt.Errorf("error parsing certificate: %w", err) - } - - // verify the sha256 - sum := sha256.Sum256(cert.Raw) - if !strings.EqualFold(fingerprint, hex.EncodeToString(sum[:])) { - return nil, fmt.Errorf("error verifying certificate: SHA256 fingerprint does not match") - } - - return cert, nil - } - - return nil, fmt.Errorf("error getting root certificate: certificate not found") -} - -func parseLoginResponse(resp *linkedca.LoginResponse) ([]byte, []byte, error) { - var block *pem.Block - var bundle []*x509.Certificate - b := []byte(resp.PemCertificateChain) - for len(b) > 0 { - block, b = pem.Decode(b) - if block == nil { - break - } - if block.Type != "CERTIFICATE" { - return nil, nil, errors.New("error decoding login response: pemCertificateChain is not a certificate bundle") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, "error parsing login response") - } - bundle = append(bundle, crt) - } - if len(bundle) == 0 { - return nil, nil, errors.New("error decoding login response: pemCertificateChain should not be empty") - } - - last := len(bundle) - 1 - - certBytes := []byte(resp.PemCertificate) - for i := 0; i < last; i++ { - certBytes = append(certBytes, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: bundle[i].Raw, - })...) - } - - return certBytes, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: bundle[last].Raw, - }), nil -} From a72eab915b920290b184ef1e13b47649d493cb3e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 20 Jul 2021 12:59:59 -0700 Subject: [PATCH 117/291] Use linkedca v0.1.0 --- go.mod | 6 +++--- go.sum | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 86e0db73..8ebffc43 100644 --- a/go.mod +++ b/go.mod @@ -28,12 +28,12 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a + go.step.sm/linkedca v0.1.0 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/net v0.0.0-20210614182718-04defd469f4e + golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect google.golang.org/api v0.47.0 - google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f + google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 google.golang.org/grpc v1.39.0 google.golang.org/protobuf v1.27.1 gopkg.in/square/go-jose.v2 v2.5.1 diff --git a/go.sum b/go.sum index 2688662a..d3435218 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,6 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a h1:bu8HRqaJeZpXyAdULY3lptl1U0TrwAfm0WMwxWtG0JY= -go.step.sm/linkedca v0.0.0-20210712083753-ce3a4a62479a/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= @@ -629,8 +627,8 @@ 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-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds= +golang.org/x/net v0.0.0-20210716203947-853a461950ff/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= @@ -782,7 +780,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f 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.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= @@ -864,8 +862,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f h1:khwpF3oSk7GIab/7DDMDyE8cPQEO6FAfOcWHIRAhO20= -google.golang.org/genproto v0.0.0-20210708141623-e76da96a951f/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 h1:7yQQsvnwjfEahbNNEKcBHv3mR+HnB1ctGY/z1JXzx8M= +google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= From 17eef81c916b080db8555bd22c0cbfcdb6f2874d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 20 Jul 2021 14:55:07 -0700 Subject: [PATCH 118/291] Remove linkerd replace. --- go.mod | 6 +++--- go.sum | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8ebffc43..c3fdb002 100644 --- a/go.mod +++ b/go.mod @@ -40,8 +40,8 @@ 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 +// replace go.step.sm/crypto => ../crypto +// replace go.step.sm/cli-utils => ../cli-utils +// replace go.step.sm/linkedca => ../linkedca replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index d3435218..140557ba 100644 --- a/go.sum +++ b/go.sum @@ -528,6 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/linkedca v0.1.0 h1:2kVdQZi37pIijm1thSYVOQ/mKoREPYNXulUZj/G0azM= +go.step.sm/linkedca v0.1.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 71f8019243e201fe48c9c0b782fdee3cc9abd32e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 20 Jul 2021 18:16:24 -0700 Subject: [PATCH 119/291] Store x509 and ssh certificates on linkedca if enabled. --- authority/linkedca.go | 43 +++++++++++++++++++++++++++++++++++++++++++ authority/ssh.go | 18 ++++++++++++++---- authority/tls.go | 20 ++++++++++++++++---- go.mod | 2 +- 4 files changed, 74 insertions(+), 9 deletions(-) diff --git a/authority/linkedca.go b/authority/linkedca.go index 4e67f246..117f19ef 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -6,6 +6,7 @@ import ( "crypto/sha256" "crypto/tls" "crypto/x509" + "encoding/base64" "encoding/hex" "encoding/pem" "fmt" @@ -21,6 +22,7 @@ import ( "go.step.sm/crypto/tlsutil" "go.step.sm/crypto/x509util" "go.step.sm/linkedca" + "golang.org/x/crypto/ssh" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -237,6 +239,47 @@ func (c *linkedCaClient) DeleteAdmin(ctx context.Context, id string) error { return errors.Wrap(err, "error deleting admin") } +func (c *linkedCaClient) StoreCertificateChain(fullchain ...*x509.Certificate) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + _, err := c.client.PostCertificate(ctx, &linkedca.CertificateRequest{ + PemCertificate: serializeCertificateChain(fullchain[0]), + PemCertificateChain: serializeCertificateChain(fullchain[1:]...), + }) + return errors.Wrap(err, "error posting certificate") +} + +func (c *linkedCaClient) StoreRenewedCertificate(parent *x509.Certificate, fullchain ...*x509.Certificate) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + _, err := c.client.PostCertificate(ctx, &linkedca.CertificateRequest{ + PemCertificate: serializeCertificateChain(fullchain[0]), + PemCertificateChain: serializeCertificateChain(fullchain[1:]...), + PemParentCertificate: serializeCertificateChain(parent), + }) + return errors.Wrap(err, "error posting certificate") +} + +func (c *linkedCaClient) StoreSSHCertificate(crt *ssh.Certificate) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + _, err := c.client.PostSSHCertificate(ctx, &linkedca.SSHCertificateRequest{ + Certificate: base64.StdEncoding.EncodeToString(crt.Marshal()), + }) + return errors.Wrap(err, "error posting ssh certificate") +} + +func serializeCertificateChain(fullchain ...*x509.Certificate) string { + var chain string + for _, crt := range fullchain { + chain += string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: crt.Raw, + })) + } + return chain +} + func getAuthority(sans []string) (string, error) { for _, s := range sans { if strings.HasPrefix(s, "urn:smallstep:authority:") { diff --git a/authority/ssh.go b/authority/ssh.go index 335b6702..3b03fd7e 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -239,7 +239,7 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi } } - if err = a.db.StoreSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { + if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error storing certificate in db") } @@ -294,7 +294,7 @@ func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ss return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate") } - if err = a.db.StoreSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { + if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { return nil, errs.Wrap(http.StatusInternalServerError, err, "renewSSH: error storing certificate in db") } @@ -369,13 +369,23 @@ func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub } } - if err = a.db.StoreSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { + if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { return nil, errs.Wrap(http.StatusInternalServerError, err, "rekeySSH; error storing certificate in db") } return cert, nil } +func (a *Authority) storeSSHCertificate(cert *ssh.Certificate) error { + type sshCertificateStorer interface { + StoreSSHCertificate(crt *ssh.Certificate) error + } + if s, ok := a.adminDB.(sshCertificateStorer); ok { + return s.StoreSSHCertificate(cert) + } + return a.db.StoreSSHCertificate(cert) +} + // IsValidForAddUser checks if a user provisioner certificate can be issued to // the given certificate. func IsValidForAddUser(cert *ssh.Certificate) error { @@ -451,7 +461,7 @@ func (a *Authority) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, subje } cert.Signature = sig - if err = a.db.StoreSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { + if err = a.storeSSHCertificate(cert); err != nil && err != db.ErrNotImplemented { return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSHAddUser: error storing certificate in db") } diff --git a/authority/tls.go b/authority/tls.go index 4c3420df..b2dc4c92 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -280,9 +280,15 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5 // `StoreCertificate(...*x509.Certificate) error` instead of just // `StoreCertificate(*x509.Certificate) error`. func (a *Authority) storeCertificate(fullchain []*x509.Certificate) error { - if s, ok := a.db.(interface { + type certificateChainStorer interface { StoreCertificateChain(...*x509.Certificate) error - }); ok { + } + // Store certificate in linkedca + if s, ok := a.adminDB.(certificateChainStorer); ok { + return s.StoreCertificateChain(fullchain...) + } + // Store certificate in local db + if s, ok := a.db.(certificateChainStorer); ok { return s.StoreCertificateChain(fullchain...) } return a.db.StoreCertificate(fullchain[0]) @@ -293,9 +299,15 @@ func (a *Authority) storeCertificate(fullchain []*x509.Certificate) error { // // TODO: at some point we should implement this in the standard implementation. func (a *Authority) storeRenewedCertificate(oldCert *x509.Certificate, fullchain []*x509.Certificate) error { - if s, ok := a.db.(interface { + type renewedCertificateChainStorer interface { StoreRenewedCertificate(*x509.Certificate, ...*x509.Certificate) error - }); ok { + } + // Store certificate in linkedca + if s, ok := a.adminDB.(renewedCertificateChainStorer); ok { + return s.StoreRenewedCertificate(oldCert, fullchain...) + } + // Store certificate in local db + if s, ok := a.db.(renewedCertificateChainStorer); ok { return s.StoreRenewedCertificate(oldCert, fullchain...) } return a.db.StoreCertificate(fullchain[0]) diff --git a/go.mod b/go.mod index c3fdb002..6957cc83 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,6 @@ 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 +replace go.step.sm/linkedca => ../linkedca replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 From 22ef324534cee45f3b231b32347155ca818377d0 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 21 Jul 2021 11:49:10 -0500 Subject: [PATCH 120/291] Fix needs-renewal condition and switch to using ExecCondition --- systemd/cert-renewer@.service | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/systemd/cert-renewer@.service b/systemd/cert-renewer@.service index 7c4c4e3c..5b56f5fc 100644 --- a/systemd/cert-renewer@.service +++ b/systemd/cert-renewer@.service @@ -12,10 +12,10 @@ Environment=STEPPATH=/etc/step-ca \ CERT_LOCATION=/etc/step/certs/%i.crt \ KEY_LOCATION=/etc/step/certs/%i.key -; ExecStartPre checks if the certificate is ready for renewal, +; ExecCondition checks if the certificate is ready for renewal, ; based on the exit status of the command. -; (In systemd 243 and above, you can use ExecCondition= here.) -ExecStartPre=/usr/bin/step certificate needs-renewal $CERT_LOCATION --roots $STEPPATH/certs/root_ca.crt +; (In systemd 242 or below, you can use ExecStartPre= here.) +ExecCondition=/usr/bin/step certificate needs-renewal $CERT_LOCATION ; ExecStart renews the certificate, if ExecStartPre was successful. ExecStart=/usr/bin/step ca renew --force $CERT_LOCATION $KEY_LOCATION From f7542a5bd9037c570e0ba4ad87b26cd47670f85e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 21 Jul 2021 15:22:57 -0700 Subject: [PATCH 121/291] Move check of ssh revocation from provisioner to the authority. --- authority/authorize.go | 14 ++++ authority/provisioner/sshpop.go | 13 +--- authority/provisioner/sshpop_test.go | 109 --------------------------- authority/ssh.go | 10 ++- authority/ssh_test.go | 58 ++++++++++++++ 5 files changed, 83 insertions(+), 121 deletions(-) diff --git a/authority/authorize.go b/authority/authorize.go index 8d1f878a..91a07353 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/hex" "net/http" + "strconv" "strings" "time" @@ -291,6 +292,19 @@ func (a *Authority) authorizeRenew(cert *x509.Certificate) error { return nil } +// authorizeSSHCertificate returns an error if the given certificate is revoked. +func (a *Authority) authorizeSSHCertificate(ctx context.Context, cert *ssh.Certificate) error { + serial := strconv.FormatUint(cert.Serial, 10) + isRevoked, err := a.db.IsSSHRevoked(serial) + if err != nil { + return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeSSHCertificate", errs.WithKeyVal("serialNumber", serial)) + } + if isRevoked { + return errs.Unauthorized("authority.authorizeSSHCertificate: certificate has been revoked", errs.WithKeyVal("serialNumber", serial)) + } + return nil +} + // authorizeSSHSign loads the provisioner from the token, checks that it has not // been used again and calls the provisioner AuthorizeSSHSign method. Returns a // list of methods to apply to the signing flow. diff --git a/authority/provisioner/sshpop.go b/authority/provisioner/sshpop.go index 8bc76edf..99974ff1 100644 --- a/authority/provisioner/sshpop.go +++ b/authority/provisioner/sshpop.go @@ -8,7 +8,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" "golang.org/x/crypto/ssh" @@ -30,7 +29,6 @@ type SSHPOP struct { Type string `json:"type"` Name string `json:"name"` Claims *Claims `json:"claims,omitempty"` - db db.AuthDB claimer *Claimer audiences Audiences sshPubKeys *SSHKeys @@ -102,7 +100,6 @@ func (p *SSHPOP) Init(config Config) error { } p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) - p.db = config.DB p.sshPubKeys = config.SSHKeys return nil } @@ -110,6 +107,8 @@ func (p *SSHPOP) Init(config Config) error { // authorizeToken performs common jwt authorization actions and returns the // claims for case specific downstream parsing. // e.g. a Sign request will auth/validate different fields than a Revoke request. +// +// Checking for certificate revocation has been moved to the authority package. func (p *SSHPOP) authorizeToken(token string, audiences []string) (*sshPOPPayload, error) { sshCert, jwt, err := ExtractSSHPOPCert(token) if err != nil { @@ -117,14 +116,6 @@ func (p *SSHPOP) authorizeToken(token string, audiences []string) (*sshPOPPayloa "sshpop.authorizeToken; error extracting sshpop header from token") } - // Check for revocation. - if isRevoked, err := p.db.IsSSHRevoked(strconv.FormatUint(sshCert.Serial, 10)); err != nil { - return nil, errs.Wrap(http.StatusInternalServerError, err, - "sshpop.authorizeToken; error checking checking sshpop cert revocation") - } else if isRevoked { - return nil, errs.Unauthorized("sshpop.authorizeToken; sshpop certificate is revoked") - } - // Check validity period of the certificate. n := time.Now() if sshCert.ValidAfter != 0 && time.Unix(int64(sshCert.ValidAfter), 0).After(n) { diff --git a/authority/provisioner/sshpop_test.go b/authority/provisioner/sshpop_test.go index 5d51b90e..79d82e00 100644 --- a/authority/provisioner/sshpop_test.go +++ b/authority/provisioner/sshpop_test.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" "github.com/smallstep/assert" - "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" "go.step.sm/crypto/pemutil" @@ -83,52 +82,9 @@ func TestSSHPOP_authorizeToken(t *testing.T) { err: errors.New("sshpop.authorizeToken; error extracting sshpop header from token: extractSSHPOPCert; error parsing token: "), } }, - "fail/error-revoked-db-check": func(t *testing.T) test { - p, err := generateSSHPOP() - assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, errors.New("force") - }, - } - cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) - assert.FatalError(t, err) - tok, err := generateSSHPOPToken(p, cert, jwk) - assert.FatalError(t, err) - return test{ - p: p, - token: tok, - code: http.StatusInternalServerError, - err: errors.New("sshpop.authorizeToken; error checking checking sshpop cert revocation: force"), - } - }, - "fail/cert-already-revoked": func(t *testing.T) test { - p, err := generateSSHPOP() - assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return true, nil - }, - } - cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) - assert.FatalError(t, err) - tok, err := generateSSHPOPToken(p, cert, jwk) - assert.FatalError(t, err) - return test{ - p: p, - token: tok, - code: http.StatusUnauthorized, - err: errors.New("sshpop.authorizeToken; sshpop certificate is revoked"), - } - }, "fail/cert-not-yet-valid": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{ CertType: ssh.UserCert, ValidAfter: uint64(time.Now().Add(time.Minute).Unix()), @@ -146,11 +102,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/cert-past-validity": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{ CertType: ssh.UserCert, ValidBefore: uint64(time.Now().Add(-time.Minute).Unix()), @@ -168,11 +119,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/no-signer-found": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.HostCert}, sshSigner) assert.FatalError(t, err) tok, err := generateSSHPOPToken(p, cert, jwk) @@ -187,11 +133,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/error-parsing-claims-bad-sig": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, _, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) otherJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -208,11 +149,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/invalid-claims-issuer": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateToken("foo", "bar", testAudiences.Sign[0], "", @@ -228,11 +164,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/invalid-audience": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateToken("foo", p.GetName(), "invalid-aud", "", @@ -248,11 +179,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "fail/empty-subject": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateToken("", p.GetName(), testAudiences.Sign[0], "", @@ -268,11 +194,6 @@ func TestSSHPOP_authorizeToken(t *testing.T) { "ok": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateSSHPOPToken(p, cert, jwk) @@ -330,11 +251,6 @@ func TestSSHPOP_AuthorizeSSHRevoke(t *testing.T) { "fail/subject-not-equal-serial": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateToken("foo", p.GetName(), testAudiences.SSHRevoke[0], "", @@ -350,11 +266,6 @@ func TestSSHPOP_AuthorizeSSHRevoke(t *testing.T) { "ok": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{Serial: 123455, CertType: ssh.UserCert}, sshSigner) assert.FatalError(t, err) tok, err := generateToken("123455", p.GetName(), testAudiences.SSHRevoke[0], "", @@ -419,11 +330,6 @@ func TestSSHPOP_AuthorizeSSHRenew(t *testing.T) { "fail/not-host-cert": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshUserSigner) assert.FatalError(t, err) tok, err := generateToken("foo", p.GetName(), testAudiences.SSHRenew[0], "", @@ -439,11 +345,6 @@ func TestSSHPOP_AuthorizeSSHRenew(t *testing.T) { "ok": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{Serial: 123455, CertType: ssh.HostCert}, sshHostSigner) assert.FatalError(t, err) tok, err := generateToken("123455", p.GetName(), testAudiences.SSHRenew[0], "", @@ -511,11 +412,6 @@ func TestSSHPOP_AuthorizeSSHRekey(t *testing.T) { "fail/not-host-cert": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.UserCert}, sshUserSigner) assert.FatalError(t, err) tok, err := generateToken("foo", p.GetName(), testAudiences.SSHRekey[0], "", @@ -531,11 +427,6 @@ func TestSSHPOP_AuthorizeSSHRekey(t *testing.T) { "ok": func(t *testing.T) test { p, err := generateSSHPOP() assert.FatalError(t, err) - p.db = &db.MockAuthDB{ - MIsSSHRevoked: func(sn string) (bool, error) { - return false, nil - }, - } cert, jwk, err := createSSHCert(&ssh.Certificate{Serial: 123455, CertType: ssh.HostCert}, sshHostSigner) assert.FatalError(t, err) tok, err := generateToken("123455", p.GetName(), testAudiences.SSHRekey[0], "", diff --git a/authority/ssh.go b/authority/ssh.go index 3b03fd7e..1c873279 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -249,7 +249,11 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi // RenewSSH creates a signed SSH certificate using the old SSH certificate as a template. func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, error) { if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 { - return nil, errs.BadRequest("rewnewSSH: cannot renew certificate without validity period") + return nil, errs.BadRequest("renewSSH: cannot renew certificate without validity period") + } + + if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil { + return nil, err } backdate := a.config.AuthorityConfig.Backdate.Duration @@ -319,6 +323,10 @@ func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub return nil, errs.BadRequest("rekeySSH; cannot rekey certificate without validity period") } + if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil { + return nil, err + } + backdate := a.config.AuthorityConfig.Backdate.Duration duration := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second now := time.Now() diff --git a/authority/ssh_test.go b/authority/ssh_test.go index 8ca26af0..e468ecf0 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -750,6 +750,11 @@ func TestAuthority_RekeySSH(t *testing.T) { now := time.Now().UTC() a := testAuthority(t) + a.db = &db.MockAuthDB{ + MIsSSHRevoked: func(sn string) (bool, error) { + return false, nil + }, + } type test struct { auth *Authority @@ -763,6 +768,56 @@ func TestAuthority_RekeySSH(t *testing.T) { code int } tests := map[string]func(t *testing.T) *test{ + "fail/is-revoked": func(t *testing.T) *test { + auth := testAuthority(t) + auth.db = &db.MockAuthDB{ + MIsSSHRevoked: func(sn string) (bool, error) { + return true, nil + }, + } + return &test{ + auth: auth, + userSigner: signer, + hostSigner: signer, + cert: &ssh.Certificate{ + Serial: 1234567890, + ValidAfter: uint64(now.Unix()), + ValidBefore: uint64(now.Add(time.Hour).Unix()), + CertType: ssh.UserCert, + ValidPrincipals: []string{"foo", "bar"}, + KeyId: "foo", + }, + key: pub, + signOpts: []provisioner.SignOption{}, + err: errors.New("authority.authorizeSSHCertificate: certificate has been revoked"), + code: http.StatusUnauthorized, + } + }, + "fail/is-revoked-error": func(t *testing.T) *test { + auth := testAuthority(t) + auth.db = &db.MockAuthDB{ + MIsSSHRevoked: func(sn string) (bool, error) { + return false, errors.New("an error") + }, + } + return &test{ + auth: auth, + userSigner: signer, + hostSigner: signer, + cert: &ssh.Certificate{ + Serial: 1234567890, + ValidAfter: uint64(now.Unix()), + ValidBefore: uint64(now.Add(time.Hour).Unix()), + CertType: ssh.UserCert, + ValidPrincipals: []string{"foo", "bar"}, + KeyId: "foo", + }, + key: pub, + signOpts: []provisioner.SignOption{}, + err: errors.New("authority.authorizeSSHCertificate: an error"), + code: http.StatusInternalServerError, + } + }, "fail/opts-type": func(t *testing.T) *test { return &test{ userSigner: signer, @@ -831,6 +886,9 @@ func TestAuthority_RekeySSH(t *testing.T) { "fail/db-store": func(t *testing.T) *test { return &test{ auth: testAuthority(t, WithDatabase(&db.MockAuthDB{ + MIsSSHRevoked: func(sn string) (bool, error) { + return false, nil + }, MStoreSSHCertificate: func(cert *ssh.Certificate) error { return errors.New("force") }, From 4ad82a2f769c96a55b8dc1cbf886dbb0142e4803 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 23 Jul 2021 16:10:13 -0700 Subject: [PATCH 122/291] Check linkedca for revocation. --- authority/authorize.go | 22 ++++++++++++++++++++-- authority/linkedca.go | 24 ++++++++++++++++++++++++ go.mod | 2 +- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/authority/authorize.go b/authority/authorize.go index 91a07353..8555db9b 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -271,10 +271,19 @@ func (a *Authority) authorizeRevoke(ctx context.Context, token string) error { // // TODO(mariano): should we authorize by default? func (a *Authority) authorizeRenew(cert *x509.Certificate) error { + var err error + var isRevoked bool var opts = []interface{}{errs.WithKeyVal("serialNumber", cert.SerialNumber.String())} // Check the passive revocation table. - isRevoked, err := a.db.IsRevoked(cert.SerialNumber.String()) + serial := cert.SerialNumber.String() + if lca, ok := a.adminDB.(interface { + IsRevoked(string) (bool, error) + }); ok { + isRevoked, err = lca.IsRevoked(serial) + } else { + isRevoked, err = a.db.IsRevoked(serial) + } if err != nil { return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...) } @@ -294,8 +303,17 @@ func (a *Authority) authorizeRenew(cert *x509.Certificate) error { // authorizeSSHCertificate returns an error if the given certificate is revoked. func (a *Authority) authorizeSSHCertificate(ctx context.Context, cert *ssh.Certificate) error { + var err error + var isRevoked bool + serial := strconv.FormatUint(cert.Serial, 10) - isRevoked, err := a.db.IsSSHRevoked(serial) + if lca, ok := a.adminDB.(interface { + IsSSHRevoked(string) (bool, error) + }); ok { + isRevoked, err = lca.IsSSHRevoked(serial) + } else { + isRevoked, err = a.db.IsSSHRevoked(serial) + } if err != nil { return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeSSHCertificate", errs.WithKeyVal("serialNumber", serial)) } diff --git a/authority/linkedca.go b/authority/linkedca.go index 117f19ef..79427c5c 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -269,6 +269,30 @@ func (c *linkedCaClient) StoreSSHCertificate(crt *ssh.Certificate) error { return errors.Wrap(err, "error posting ssh certificate") } +func (c *linkedCaClient) IsRevoked(serial string) (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + resp, err := c.client.GetCertificateStatus(ctx, &linkedca.GetCertificateStatusRequest{ + Serial: serial, + }) + if err != nil { + return false, errors.Wrap(err, "error getting certificate status") + } + return resp.Status != linkedca.RevocationStatus_ACTIVE, nil +} + +func (c *linkedCaClient) IsSSHRevoked(serial string) (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + resp, err := c.client.GetSSHCertificateStatus(ctx, &linkedca.GetSSHCertificateStatusRequest{ + Serial: serial, + }) + if err != nil { + return false, errors.Wrap(err, "error getting certificate status") + } + return resp.Status != linkedca.RevocationStatus_ACTIVE, nil +} + func serializeCertificateChain(fullchain ...*x509.Certificate) string { var chain string for _, crt := range fullchain { diff --git a/go.mod b/go.mod index 6957cc83..98e7dbdb 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.1.0 + go.step.sm/linkedca v0.3.0 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect From 3a00b6b39656fb8bba385cdada98b35f56a8f0ed Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 26 Jul 2021 14:31:42 -0700 Subject: [PATCH 123/291] Properly marshal a certificate when we send it to linkedca. --- authority/linkedca.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/authority/linkedca.go b/authority/linkedca.go index 79427c5c..75bc6e1a 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -6,7 +6,6 @@ import ( "crypto/sha256" "crypto/tls" "crypto/x509" - "encoding/base64" "encoding/hex" "encoding/pem" "fmt" @@ -264,7 +263,7 @@ func (c *linkedCaClient) StoreSSHCertificate(crt *ssh.Certificate) error { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() _, err := c.client.PostSSHCertificate(ctx, &linkedca.SSHCertificateRequest{ - Certificate: base64.StdEncoding.EncodeToString(crt.Marshal()), + Certificate: string(ssh.MarshalAuthorizedKey(crt)), }) return errors.Wrap(err, "error posting ssh certificate") } From d0c1530f894ce9f5189de950b81c054fc64cf2a6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 26 Jul 2021 14:48:01 -0700 Subject: [PATCH 124/291] Remove replace of linkedca package. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 98e7dbdb..2c7a0687 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,6 @@ 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 +// replace go.step.sm/linkedca => ../linkedca replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index 140557ba..9310efc5 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.1.0 h1:2kVdQZi37pIijm1thSYVOQ/mKoREPYNXulUZj/G0azM= -go.step.sm/linkedca v0.1.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.3.0 h1:6jyghg/ErVTJ/J23DrbWhGyWbWX2b4aJkPQEGL4xZ40= +go.step.sm/linkedca v0.3.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 dc1ec18b52287d09e68082c1ec7bc5b469b55916 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 26 Jul 2021 19:01:56 -0700 Subject: [PATCH 125/291] Create a way to export ca configurations. --- authority/export.go | 39 +++++ authority/provisioners.go | 331 ++++++++++++++++++++++++++++++++++++++ commands/export.go | 74 +++++++++ go.mod | 2 +- 4 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 authority/export.go create mode 100644 commands/export.go diff --git a/authority/export.go b/authority/export.go new file mode 100644 index 00000000..e8f7298e --- /dev/null +++ b/authority/export.go @@ -0,0 +1,39 @@ +package authority + +import "go.step.sm/linkedca" + +func (a *Authority) Export() (*linkedca.Configuration, error) { + var admins []*linkedca.Admin + var provisioners []*linkedca.Provisioner + + for { + list, cursor := a.admins.Find("", 100) + admins = append(admins, list...) + if cursor == "" { + break + } + } + + for { + list, cursor := a.provisioners.Find("", 100) + for _, p := range list { + lp, err := ProvisionerToLinkedca(p) + if err != nil { + return nil, err + } + provisioners = append(provisioners, lp) + } + if cursor == "" { + break + } + } + + // Global claims for all provisioners. + claims := claimsToLinkedca(a.config.AuthorityConfig.Claims) + + return &linkedca.Configuration{ + Admins: admins, + Provisioners: provisioners, + Claims: claims, + }, nil +} diff --git a/authority/provisioners.go b/authority/provisioners.go index d2581e76..3e2d1276 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -4,12 +4,16 @@ import ( "context" "crypto/x509" "encoding/json" + "encoding/pem" "fmt" + "io/ioutil" + "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" + step "go.step.sm/cli-utils/config" "go.step.sm/crypto/jose" "go.step.sm/linkedca" "gopkg.in/square/go-jose.v2/jwt" @@ -398,6 +402,13 @@ func durationsToCertificates(d *linkedca.Durations) (min, max, def *provisioner. return } +func durationsToLinkedca(d *provisioner.Duration) string { + if d == nil { + return "" + } + return d.Duration.String() +} + // claimsToCertificates converts the linkedca provisioner claims type to the // certifictes claims type. func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { @@ -438,6 +449,109 @@ func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { return pc, nil } +func claimsToLinkedca(c *provisioner.Claims) *linkedca.Claims { + if c == nil { + return nil + } + + disableRenewal := config.DefaultDisableRenewal + if c.DisableRenewal != nil { + disableRenewal = *c.DisableRenewal + } + + lc := &linkedca.Claims{ + DisableRenewal: disableRenewal, + } + + if c.DefaultTLSDur != nil || c.MinTLSDur != nil || c.MaxTLSDur != nil { + lc.X509 = &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Default: durationsToLinkedca(c.DefaultTLSDur), + Min: durationsToLinkedca(c.MinTLSDur), + Max: durationsToLinkedca(c.MaxTLSDur), + }, + } + } + + if c.EnableSSHCA != nil && *c.EnableSSHCA { + lc.Ssh = &linkedca.SSHClaims{ + Enabled: true, + } + if c.DefaultUserSSHDur != nil || c.MinUserSSHDur != nil || c.MaxUserSSHDur != nil { + lc.Ssh.UserDurations = &linkedca.Durations{ + Default: durationsToLinkedca(c.DefaultUserSSHDur), + Min: durationsToLinkedca(c.MinUserSSHDur), + Max: durationsToLinkedca(c.MaxUserSSHDur), + } + } + if c.DefaultHostSSHDur != nil || c.MinHostSSHDur != nil || c.MaxHostSSHDur != nil { + lc.Ssh.HostDurations = &linkedca.Durations{ + Default: durationsToLinkedca(c.DefaultHostSSHDur), + Min: durationsToLinkedca(c.MinHostSSHDur), + Max: durationsToLinkedca(c.MaxHostSSHDur), + } + } + } + + return lc +} + +func provisionerOptionsToLinkedca(p *provisioner.Options) (*linkedca.Template, *linkedca.Template, error) { + var err error + var x509Template, sshTemplate *linkedca.Template + + if p == nil { + return nil, nil, nil + } + + if p.X509 != nil && p.X509.HasTemplate() { + x509Template = &linkedca.Template{ + Template: nil, + Data: nil, + } + + if p.X509.Template != "" { + x509Template.Template = []byte(p.SSH.Template) + } else if p.X509.TemplateFile != "" { + filename := step.StepAbs(p.X509.TemplateFile) + if x509Template.Template, err = ioutil.ReadFile(filename); err != nil { + return nil, nil, errors.Wrap(err, "error reading x509 template") + } + } + } + + if p.SSH != nil && p.SSH.HasTemplate() { + sshTemplate = &linkedca.Template{ + Template: nil, + Data: nil, + } + + if p.SSH.Template != "" { + sshTemplate.Template = []byte(p.SSH.Template) + } else if p.SSH.TemplateFile != "" { + filename := step.StepAbs(p.SSH.TemplateFile) + if sshTemplate.Template, err = ioutil.ReadFile(filename); err != nil { + return nil, nil, errors.Wrap(err, "error reading ssh template") + } + } + } + + return x509Template, sshTemplate, nil +} + +func provisionerPEMToLinkedca(b []byte) [][]byte { + var roots [][]byte + var block *pem.Block + for { + if block, b = pem.Decode(b); block == nil { + break + } + roots = append(roots, pem.EncodeToMemory(block)) + } + return roots +} + // ProvisionerToCertificates converts the linkedca provisioner type to the certificates provisioner // interface. func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { @@ -588,6 +702,223 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, } } +// ProvisionerToLinkedca converts a provisioner.Interface to a +// linkedca.Provisioner type. +func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, error) { + switch p := p.(type) { + case *provisioner.JWK: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + publicKey, err := json.Marshal(p.Key) + if err != nil { + return nil, errors.Wrap(err, "error marshaling key") + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_JWK, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_JWK{ + JWK: &linkedca.JWKProvisioner{ + PublicKey: publicKey, + EncryptedPrivateKey: []byte(p.EncryptedKey), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.OIDC: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_OIDC, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_OIDC{ + OIDC: &linkedca.OIDCProvisioner{ + ClientId: p.ClientID, + ClientSecret: p.ClientSecret, + ConfigurationEndpoint: p.ConfigurationEndpoint, + Admins: p.Admins, + Domains: p.Domains, + Groups: p.Groups, + ListenAddress: p.ListenAddress, + TenantId: p.TenantID, + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.GCP: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_GCP, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_GCP{ + GCP: &linkedca.GCPProvisioner{ + ServiceAccounts: p.ServiceAccounts, + ProjectIds: p.ProjectIDs, + DisableCustomSans: p.DisableCustomSANs, + DisableTrustOnFirstUse: p.DisableTrustOnFirstUse, + InstanceAge: p.InstanceAge.String(), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.AWS: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_AWS, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_AWS{ + AWS: &linkedca.AWSProvisioner{ + Accounts: p.Accounts, + DisableCustomSans: p.DisableCustomSANs, + DisableTrustOnFirstUse: p.DisableTrustOnFirstUse, + InstanceAge: p.InstanceAge.String(), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.Azure: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_AZURE, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_Azure{ + Azure: &linkedca.AzureProvisioner{ + TenantId: p.TenantID, + ResourceGroups: p.ResourceGroups, + Audience: p.Audience, + DisableCustomSans: p.DisableCustomSANs, + DisableTrustOnFirstUse: p.DisableTrustOnFirstUse, + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.ACME: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_ACME, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_ACME{ + ACME: &linkedca.ACMEProvisioner{ + ForceCn: p.ForceCN, + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.X5C: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_X5C, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_X5C{ + X5C: &linkedca.X5CProvisioner{ + Roots: provisionerPEMToLinkedca(p.Roots), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.K8sSA: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_K8SSA, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_K8SSA{ + K8SSA: &linkedca.K8SSAProvisioner{ + PublicKeys: provisionerPEMToLinkedca(p.PubKeys), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + case *provisioner.SSHPOP: + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_SSHPOP, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_SSHPOP{ + SSHPOP: &linkedca.SSHPOPProvisioner{}, + }, + }, + Claims: claimsToLinkedca(p.Claims), + }, nil + case *provisioner.SCEP: + x509Template, sshTemplate, err := provisionerOptionsToLinkedca(p.Options) + if err != nil { + return nil, err + } + return &linkedca.Provisioner{ + Type: linkedca.Provisioner_SCEP, + Name: p.GetName(), + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_SCEP{ + SCEP: &linkedca.SCEPProvisioner{ + ForceCn: p.ForceCN, + Challenge: p.GetChallengePassword(), + Capabilities: p.Capabilities, + MinimumPublicKeyLength: int32(p.MinimumPublicKeyLength), + }, + }, + }, + Claims: claimsToLinkedca(p.Claims), + X509Template: x509Template, + SshTemplate: sshTemplate, + }, nil + default: + return nil, fmt.Errorf("provisioner %s not implemented", p.GetType()) + } +} + func parseInstanceAge(age string) (provisioner.Duration, error) { var instanceAge provisioner.Duration if age != "" { diff --git a/commands/export.go b/commands/export.go new file mode 100644 index 00000000..bb11fea7 --- /dev/null +++ b/commands/export.go @@ -0,0 +1,74 @@ +package commands + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" + "github.com/urfave/cli" + "google.golang.org/protobuf/encoding/protojson" + + "go.step.sm/cli-utils/command" + "go.step.sm/cli-utils/errs" +) + +func init() { + command.Register(cli.Command{ + Name: "export", + Usage: "export the current configuration of step-ca", + UsageText: "**step-ca export** ", + Action: exportAction, + Description: `**step-ca export** exports the current configuration of step-ca. + +## POSITIONAL ARGUMENTS + + +: The ca.json that contains the step-ca configuration. + +## EXAMPLES + +Export the current configuration: +''' +$ step-ca export $(step path)/config/ca.json +'''`, + }) +} + +func exportAction(ctx *cli.Context) error { + if err := errs.NumberOfArguments(ctx, 1); err != nil { + return err + } + + configFile := ctx.Args().Get(0) + + config, err := config.LoadConfiguration(configFile) + if err != nil { + return err + } + + auth, err := authority.New(config) + if err != nil { + return err + } + + export, err := auth.Export() + if err != nil { + return err + } + + b, err := protojson.Marshal(export) + if err != nil { + return errors.Wrap(err, "error marshaling export") + } + + var buf bytes.Buffer + if err := json.Indent(&buf, b, "", "\t"); err != nil { + return errors.Wrap(err, "error indenting export") + } + + fmt.Println(buf.String()) + return nil +} diff --git a/go.mod b/go.mod index 2c7a0687..98e7dbdb 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,6 @@ 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 +replace go.step.sm/linkedca => ../linkedca replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 From 8f4c833845bf59610f0f805574c8ca3232b8c942 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 27 Jul 2021 12:01:50 -0700 Subject: [PATCH 126/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 544dd5b5..ca08cbe5 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ You can use it to: Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [safe, sane defaults](https://smallstep.com/docs/step-ca/certificate-authority-server-production#sane-cryptographic-defaults). -**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions).** +**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/stepdiscord).** [Website](https://smallstep.com/certificates) | [Documentation](https://smallstep.com/docs) | From 53d08e1f5c97261f424dec68184536d67fed4ea9 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 27 Jul 2021 12:03:52 -0700 Subject: [PATCH 127/291] Remove microbadger.com (the website is gone) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ca08cbe5..79e567ab 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [ [Contributor's Guide](./docs/CONTRIBUTING.md) [![GitHub release](https://img.shields.io/github/release/smallstep/certificates.svg)](https://github.com/smallstep/certificates/releases/latest) -[![CA Image](https://images.microbadger.com/badges/image/smallstep/step-ca.svg)](https://microbadger.com/images/smallstep/step-ca) [![Go Report Card](https://goreportcard.com/badge/github.com/smallstep/certificates)](https://goreportcard.com/report/github.com/smallstep/certificates) [![Build Status](https://travis-ci.com/smallstep/certificates.svg?branch=master)](https://travis-ci.com/smallstep/certificates) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) From 887423ee6e520466c2c2b75abb53f62c84de1755 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Jul 2021 18:29:10 -0700 Subject: [PATCH 128/291] Update TLS cipher suites. --- authority/config/tls_options.go | 53 ++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/authority/config/tls_options.go b/authority/config/tls_options.go index 996b5834..ed61cfc9 100644 --- a/authority/config/tls_options.go +++ b/authority/config/tls_options.go @@ -119,27 +119,38 @@ func (c CipherSuites) Value() []uint16 { // cipherSuites has the list of supported cipher suites. var cipherSuites = map[string]uint16{ - "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, - "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + // TLS 1.0 - 1.2 cipher suites. + "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, + "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, + "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + + // TLS 1.3 cipher sutes. + "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, + "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, + "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, + + // Legacy names. + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, } // TLSOptions represents the TLS options that can be specified on *tls.Config From c7f8516142bed7ce577f7a439629f31321fcb684 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Jul 2021 18:29:29 -0700 Subject: [PATCH 129/291] Add to export all the information in the ca.json --- authority/export.go | 235 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 222 insertions(+), 13 deletions(-) diff --git a/authority/export.go b/authority/export.go index e8f7298e..f78f7fbb 100644 --- a/authority/export.go +++ b/authority/export.go @@ -1,19 +1,118 @@ package authority -import "go.step.sm/linkedca" +import ( + "encoding/json" + "io/ioutil" + "net/url" + "strings" -func (a *Authority) Export() (*linkedca.Configuration, error) { - var admins []*linkedca.Admin - var provisioners []*linkedca.Provisioner + "github.com/pkg/errors" + step "go.step.sm/cli-utils/config" + "go.step.sm/linkedca/config" + "google.golang.org/protobuf/types/known/structpb" +) +func (a *Authority) Export() (c *config.Configuration, err error) { + // Recover from panics + defer func() { + if r := recover(); r != nil { + err = r.(error) + } + }() + + c = &config.Configuration{ + Root: mustReadFilesOrUris(a.config.Root), + FederatedRoots: mustReadFilesOrUris(a.config.FederatedRoots), + Intermediate: mustReadFileOrUri(a.config.IntermediateCert), + IntermediateKey: mustReadFileOrUri(a.config.IntermediateKey), + Address: a.config.Address, + InsecureAddress: a.config.InsecureAddress, + DnsNames: a.config.DNSNames, + Db: mustMarshalToStruct(a.config.DB), + Logger: mustMarshalToStruct(a.config.Logger), + Monitoring: mustMarshalToStruct(a.config.Monitoring), + Authority: &config.Authority{}, + Password: mustPassword(a.config.Password), + } + + // SSH + if v := a.config.SSH; v != nil { + c.Ssh = &config.SSH{ + HostKey: mustReadFileOrUri(v.HostKey), + UserKey: mustReadFileOrUri(v.UserKey), + AddUserPrincipal: v.AddUserPrincipal, + AddUserCommand: v.AddUserCommand, + } + for _, k := range v.Keys { + typ, ok := config.SSHPublicKey_Type_value[strings.ToUpper(k.Type)] + if !ok { + return nil, errors.Errorf("unsupported ssh key type %s", k.Type) + } + c.Ssh.Keys = append(c.Ssh.Keys, &config.SSHPublicKey{ + Type: config.SSHPublicKey_Type(typ), + Federated: k.Federated, + Key: mustMarshalToStruct(k), + }) + } + } + + // KMS + if v := a.config.KMS; v != nil { + var typ int32 + var ok bool + if v.Type == "" { + typ = int32(config.KMS_SOFTKMS) + } else { + typ, ok = config.KMS_Type_value[strings.ToUpper(v.Type)] + if !ok { + return nil, errors.Errorf("unsupported kms type %s", v.Type) + } + } + c.Kms = &config.KMS{ + Type: config.KMS_Type(typ), + CredentialsFile: v.CredentialsFile, + Uri: v.URI, + Pin: v.Pin, + ManagementKey: v.ManagementKey, + Region: v.Region, + Profile: v.Profile, + } + } + + // Authority + c.Authority.Id = a.config.AuthorityConfig.AuthorityID + + // cas options + if v := a.config.AuthorityConfig.Options; v != nil { + c.Authority.Type = 0 + c.Authority.CertificateAuthority = v.CertificateAuthority + c.Authority.CertificateAuthorityFingerprint = v.CertificateAuthorityFingerprint + c.Authority.CredentialsFile = v.CredentialsFile + if iss := v.CertificateIssuer; iss != nil { + typ, ok := config.CertificateIssuer_Type_value[strings.ToUpper(iss.Type)] + if !ok { + return nil, errors.Errorf("unknown certificate issuer type %s", iss.Type) + } + c.Authority.CertificateIssuer = &config.CertificateIssuer{ + Type: config.CertificateIssuer_Type(typ), + Provisioner: iss.Provisioner, + Certificate: mustReadFileOrUri(iss.Certificate), + Key: mustReadFileOrUri(iss.Key), + Password: mustPassword(iss.Password), + } + } + } + + // admins for { list, cursor := a.admins.Find("", 100) - admins = append(admins, list...) + c.Authority.Admins = append(c.Authority.Admins, list...) if cursor == "" { break } } + // provisioners for { list, cursor := a.provisioners.Find("", 100) for _, p := range list { @@ -21,19 +120,129 @@ func (a *Authority) Export() (*linkedca.Configuration, error) { if err != nil { return nil, err } - provisioners = append(provisioners, lp) + c.Authority.Provisioners = append(c.Authority.Provisioners, lp) } if cursor == "" { break } } + c.Authority.Claims = claimsToLinkedca(a.config.AuthorityConfig.Claims) - // Global claims for all provisioners. - claims := claimsToLinkedca(a.config.AuthorityConfig.Claims) + // TLS + if v := a.config.TLS; v != nil { + c.Tls = &config.TLS{ + MinVersion: v.MinVersion.String(), + MaxVersion: v.MaxVersion.String(), + Renegotiation: v.Renegotiation, + } + for _, cs := range v.CipherSuites.Value() { + c.Tls.CipherSuites = append(c.Tls.CipherSuites, config.TLS_CiperSuite(cs)) + } + } - return &linkedca.Configuration{ - Admins: admins, - Provisioners: provisioners, - Claims: claims, - }, nil + // Templates + if v := a.config.Templates; v != nil { + c.Templates = &config.Templates{ + Ssh: &config.SSHTemplate{}, + Data: mustMarshalToStruct(v.Data), + } + // Remove automatically loaded vars + if c.Templates.Data != nil && c.Templates.Data.Fields != nil { + delete(c.Templates.Data.Fields, "Step") + } + for _, t := range v.SSH.Host { + typ, ok := config.Template_Type_value[strings.ToUpper(string(t.Type))] + if !ok { + return nil, errors.Errorf("unsupported template type %s", t.Type) + } + content := t.Content + if len(content) == 0 { + content = mustReadFileOrUri(t.TemplatePath) + } + c.Templates.Ssh.Hosts = append(c.Templates.Ssh.Hosts, &config.Template{ + Type: config.Template_Type(typ), + Name: t.Name, + Template: t.TemplatePath, + Path: t.Path, + Comment: t.Comment, + Requires: t.RequiredData, + Content: content, + }) + } + for _, t := range v.SSH.User { + typ, ok := config.Template_Type_value[strings.ToUpper(string(t.Type))] + if !ok { + return nil, errors.Errorf("unsupported template type %s", t.Type) + } + content := t.Content + if len(content) == 0 { + content = mustReadFileOrUri(t.TemplatePath) + } + c.Templates.Ssh.Users = append(c.Templates.Ssh.Users, &config.Template{ + Type: config.Template_Type(typ), + Name: t.Name, + Template: t.TemplatePath, + Path: t.Path, + Comment: t.Comment, + Requires: t.RequiredData, + Content: content, + }) + } + } + + return c, nil +} + +func mustPassword(s string) []byte { + if s == "" { + return nil + } + return []byte(s) +} + +func mustMarshalToStruct(v interface{}) *structpb.Struct { + b, err := json.Marshal(v) + if err != nil { + panic(errors.Wrapf(err, "error marshaling %T", v)) + } + var r *structpb.Struct + if err := json.Unmarshal(b, &r); err != nil { + panic(errors.Wrapf(err, "error unmarshaling %T", v)) + } + return r +} + +func mustReadFileOrUri(fn string) []byte { + if fn == "" { + return nil + } + + ok, err := isFilename(fn) + if err != nil { + panic(err) + } + if ok { + b, err := ioutil.ReadFile(step.StepAbs(fn)) + if err != nil { + panic(errors.Wrapf(err, "error reading %s", fn)) + } + return b + } + return []byte(fn) +} + +func mustReadFilesOrUris(fns []string) [][]byte { + var result [][]byte + for _, fn := range fns { + result = append(result, mustReadFileOrUri(fn)) + } + return result +} + +func isFilename(fn string) (bool, error) { + u, err := url.Parse(fn) + if err != nil { + return false, errors.Wrapf(err, "error parsing %s", fn) + } + return u.Scheme == "" || u.Scheme == "file", nil } From 0730a165fd5d0a79c3881613235cae6c423381bf Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Jul 2021 19:19:58 -0700 Subject: [PATCH 130/291] Add collection of files and authority template. --- authority/config/config.go | 1 + authority/export.go | 85 +++++++++++++++++++++++--------------- authority/tls.go | 8 +++- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/authority/config/config.go b/authority/config/config.go index fabd3f91..4d7592ac 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -75,6 +75,7 @@ type ASN1DN struct { Locality string `json:"locality,omitempty"` Province string `json:"province,omitempty"` StreetAddress string `json:"streetAddress,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` CommonName string `json:"commonName,omitempty"` } diff --git a/authority/export.go b/authority/export.go index f78f7fbb..316ede0e 100644 --- a/authority/export.go +++ b/authority/export.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io/ioutil" "net/url" + "path/filepath" "strings" "github.com/pkg/errors" @@ -20,26 +21,34 @@ func (a *Authority) Export() (c *config.Configuration, err error) { } }() + files := make(map[string][]byte) c = &config.Configuration{ - Root: mustReadFilesOrUris(a.config.Root), - FederatedRoots: mustReadFilesOrUris(a.config.FederatedRoots), - Intermediate: mustReadFileOrUri(a.config.IntermediateCert), - IntermediateKey: mustReadFileOrUri(a.config.IntermediateKey), + Version: "1.0", + Root: mustReadFilesOrUris(a.config.Root, files), + FederatedRoots: mustReadFilesOrUris(a.config.FederatedRoots, files), + Intermediate: mustReadFileOrUri(a.config.IntermediateCert, files), + IntermediateKey: mustReadFileOrUri(a.config.IntermediateKey, files), Address: a.config.Address, InsecureAddress: a.config.InsecureAddress, DnsNames: a.config.DNSNames, Db: mustMarshalToStruct(a.config.DB), Logger: mustMarshalToStruct(a.config.Logger), Monitoring: mustMarshalToStruct(a.config.Monitoring), - Authority: &config.Authority{}, - Password: mustPassword(a.config.Password), + Authority: &config.Authority{ + Id: a.config.AuthorityConfig.AuthorityID, + EnableAdmin: a.config.AuthorityConfig.EnableAdmin, + DisableIssuedAtCheck: a.config.AuthorityConfig.DisableIssuedAtCheck, + Backdate: a.config.AuthorityConfig.Backdate.String(), + }, + Password: mustPassword(a.config.Password), + Files: files, } // SSH if v := a.config.SSH; v != nil { c.Ssh = &config.SSH{ - HostKey: mustReadFileOrUri(v.HostKey), - UserKey: mustReadFileOrUri(v.UserKey), + HostKey: mustReadFileOrUri(v.HostKey, files), + UserKey: mustReadFileOrUri(v.UserKey, files), AddUserPrincipal: v.AddUserPrincipal, AddUserCommand: v.AddUserCommand, } @@ -80,8 +89,6 @@ func (a *Authority) Export() (c *config.Configuration, err error) { } // Authority - c.Authority.Id = a.config.AuthorityConfig.AuthorityID - // cas options if v := a.config.AuthorityConfig.Options; v != nil { c.Authority.Type = 0 @@ -96,13 +103,12 @@ func (a *Authority) Export() (c *config.Configuration, err error) { c.Authority.CertificateIssuer = &config.CertificateIssuer{ Type: config.CertificateIssuer_Type(typ), Provisioner: iss.Provisioner, - Certificate: mustReadFileOrUri(iss.Certificate), - Key: mustReadFileOrUri(iss.Key), + Certificate: mustReadFileOrUri(iss.Certificate, files), + Key: mustReadFileOrUri(iss.Key, files), Password: mustPassword(iss.Password), } } } - // admins for { list, cursor := a.admins.Find("", 100) @@ -111,7 +117,6 @@ func (a *Authority) Export() (c *config.Configuration, err error) { break } } - // provisioners for { list, cursor := a.provisioners.Find("", 100) @@ -126,7 +131,21 @@ func (a *Authority) Export() (c *config.Configuration, err error) { break } } + // global claims c.Authority.Claims = claimsToLinkedca(a.config.AuthorityConfig.Claims) + // Distiguised names template + if v := a.config.AuthorityConfig.Template; v != nil { + c.Authority.Template = &config.DistinguishedName{ + Country: v.Country, + Organization: v.Organization, + OrganizationalUnit: v.OrganizationalUnit, + Locality: v.Locality, + Province: v.Province, + StreetAddress: v.StreetAddress, + SerialNumber: v.SerialNumber, + CommonName: v.CommonName, + } + } // TLS if v := a.config.TLS; v != nil { @@ -155,18 +174,14 @@ func (a *Authority) Export() (c *config.Configuration, err error) { if !ok { return nil, errors.Errorf("unsupported template type %s", t.Type) } - content := t.Content - if len(content) == 0 { - content = mustReadFileOrUri(t.TemplatePath) - } c.Templates.Ssh.Hosts = append(c.Templates.Ssh.Hosts, &config.Template{ Type: config.Template_Type(typ), Name: t.Name, - Template: t.TemplatePath, + Template: mustReadFileOrUri(t.TemplatePath, files), Path: t.Path, Comment: t.Comment, Requires: t.RequiredData, - Content: content, + Content: t.Content, }) } for _, t := range v.SSH.User { @@ -174,18 +189,14 @@ func (a *Authority) Export() (c *config.Configuration, err error) { if !ok { return nil, errors.Errorf("unsupported template type %s", t.Type) } - content := t.Content - if len(content) == 0 { - content = mustReadFileOrUri(t.TemplatePath) - } c.Templates.Ssh.Users = append(c.Templates.Ssh.Users, &config.Template{ Type: config.Template_Type(typ), Name: t.Name, - Template: t.TemplatePath, + Template: mustReadFileOrUri(t.TemplatePath, files), Path: t.Path, Comment: t.Comment, Requires: t.RequiredData, - Content: content, + Content: t.Content, }) } } @@ -212,11 +223,18 @@ func mustMarshalToStruct(v interface{}) *structpb.Struct { return r } -func mustReadFileOrUri(fn string) []byte { +func mustReadFileOrUri(fn string, m map[string][]byte) string { if fn == "" { - return nil + return "" } + stepPath := filepath.ToSlash(step.StepPath()) + if !strings.HasSuffix(stepPath, "/") { + stepPath += "/" + } + + fn = strings.TrimPrefix(filepath.ToSlash(fn), stepPath) + ok, err := isFilename(fn) if err != nil { panic(err) @@ -226,15 +244,16 @@ func mustReadFileOrUri(fn string) []byte { if err != nil { panic(errors.Wrapf(err, "error reading %s", fn)) } - return b + m[fn] = b + return fn } - return []byte(fn) + return fn } -func mustReadFilesOrUris(fns []string) [][]byte { - var result [][]byte +func mustReadFilesOrUris(fns []string, m map[string][]byte) []string { + var result []string for _, fn := range fns { - result = append(result, mustReadFileOrUri(fn)) + result = append(result, mustReadFileOrUri(fn, m)) } return result } diff --git a/authority/tls.go b/authority/tls.go index b2dc4c92..32d6f3c6 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -36,7 +36,6 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { if def == nil { return errors.New("default ASN1DN template cannot be nil") } - if len(crt.Subject.Country) == 0 && def.Country != "" { crt.Subject.Country = append(crt.Subject.Country, def.Country) } @@ -55,7 +54,12 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { if len(crt.Subject.StreetAddress) == 0 && def.StreetAddress != "" { crt.Subject.StreetAddress = append(crt.Subject.StreetAddress, def.StreetAddress) } - + if len(crt.Subject.SerialNumber) == 0 && def.SerialNumber != "" { + crt.Subject.SerialNumber = def.SerialNumber + } + if len(crt.Subject.CommonName) == 0 && def.CommonName != "" { + crt.Subject.CommonName = def.CommonName + } return nil } } From 07f7316851117a8aa4245e92c5385781a8554f78 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 27 Jul 2021 19:22:29 -0700 Subject: [PATCH 131/291] Add bastion to export. --- authority/export.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/authority/export.go b/authority/export.go index 316ede0e..4c5059ea 100644 --- a/authority/export.go +++ b/authority/export.go @@ -63,6 +63,15 @@ func (a *Authority) Export() (c *config.Configuration, err error) { Key: mustMarshalToStruct(k), }) } + if b := v.Bastion; b != nil { + c.Ssh.Bastion = &config.Bastion{ + Hostname: b.Hostname, + User: b.User, + Port: b.Port, + Command: b.Command, + Flags: b.Flags, + } + } } // KMS From 97af829805588967243297c3e31f13dcc1b52259 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 28 Jul 2021 13:55:35 -0700 Subject: [PATCH 132/291] RA install script --- scripts/README.md | 4 + scripts/install-step-ra.sh | 253 +++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 scripts/README.md create mode 100644 scripts/install-step-ra.sh diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..80d3cdba --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,4 @@ +# Scripts folder + +Please note that `install-step-ra.sh` is referenced on the `files.smallstep.com` S3 website bucket as a redirect to `raw.githubusercontent.com`. If you move it, please update the S3 redirect. + diff --git a/scripts/install-step-ra.sh b/scripts/install-step-ra.sh new file mode 100644 index 00000000..a24f6ea4 --- /dev/null +++ b/scripts/install-step-ra.sh @@ -0,0 +1,253 @@ +#!/bin/bash +set -e + +echo "This script will install and start a step-ca server running in Registration Authority (RA) mode." +echo "" +echo "You will need an upstream CA (URL and fingerprint)" +echo "Don't have a CA? Sign up for a hosted CA at smallstep.com — or run your own." +echo "" + +# Fail if this script is not run as root. +if ! [ $(id -u) = 0 ]; then + echo "This script must be run as root" + exit 1 +fi + +# Architecture detection +arch=$(uname -m) +case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; +esac + +if [ "$arch" = "armv5" ]; then + echo "This script doesn't work on armv5 machines" + exit 1 +fi + +if ! hash jq &> /dev/null; then + echo "This script requires the jq commmand; please install it." + exit 1 +fi + +if ! hash curl &> /dev/null; then + echo "This script requires the curl commmand; please install it." + exit 1 +fi + +if ! hash tar &> /dev/null; then + echo "This script requires the tar commmand; please install it." + exit 1 +fi + +while [ $# -gt 0 ]; do + case "$1" in + --ca-url) + CA_URL="$2" + shift + shift + ;; + --fingerprint) + CA_FINGERPRINT="$2" + shift + shift + ;; + --provisioner-name) + CA_PROVISIONER_NAME="$2" + shift + shift + ;; + --provisioner-password-file) + CA_PROVISIONER_JWK_PASSWORD_FILE="$2" + shift + shift + ;; + --dns-names) + RA_DNS_NAMES="$2" + shift + shift + ;; + --listen-address) + RA_ADDRESS="$2" + shift + shift + ;; + *) + shift + ;; + esac +done + +# Install step +if ! hash step &> /dev/null; then + echo "Installing 'step' in /usr/bin..." + STEP_VERSION=$(curl -s https://api.github.com/repos/smallstep/cli/releases/latest | jq -r '.tag_name') + + curl -sLO https://github.com/smallstep/cli/releases/download/$STEP_VERSION/step_linux_${STEP_VERSION:1}_$arch.tar.gz + tar xvzf step_linux_${STEP_VERSION:1}_$arch.tar.gz + install -m 0755 -t /usr/bin step_${STEP_VERSION:1}/bin/step + + rm step_linux_${STEP_VERSION:1}_$arch.tar.gz + rm -rf step_${STEP_VERSION:1} +fi + +# Prompt for required parameters +if [ -z "$CA_URL" ]; then + CA_URL="" + while [[ $CA_URL = "" ]]; do + read -p "Issuing CA URL: " CA_URL < /dev/tty + done +fi + +if [ -z "$CA_FINGERPRINT" ]; then + CA_FINGERPRINT="" + while [[ $CA_FINGERPRINT = "" ]]; do + read -p "Issuing CA Fingerprint: " CA_FINGERPRINT < /dev/tty + done +fi + +echo "Bootstrapping with the CA..." +export STEPPATH=$(mktemp -d) +export STEP_CONSOLE=true + +step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT + +if [ -z "$CA_PROVISIONER_NAME" ]; then + declare -a provisioners + readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name') + provisioners+=("Create provisioner") + printf '%s\n' "${provisioners[@]}" + + printf "%b" "\nSelect a JWK provisioner:\n" >&2 + select provisioner in "${provisioners[@]}"; do + if [ "$provisioner" == "Create provisioner" ]; then + echo "Creating a JWK provisioner on the upstream CA..." + echo "" + read -p "Label your provisioner (e.g. example-ra): " CA_PROVISIONER_NAME < /dev/tty + step beta ca provisioner add $CA_PROVISIONER_NAME --type JWK --create + break + elif [ -n "$provisioner" ]; then + echo "Using existing provisioner $provisioner." + CA_PROVISIONER_NAME=$provisioner + break + else + echo "Invalid selection!" + fi + done +fi + +if [ -z "$RA_DNS_NAMES" ]; then + RA_DNS_NAMES="" + while [[ $RA_DNS_NAMES = "" ]]; do + echo "What DNS names or IP addresses will your RA use?" + read -p "(e.g. acme.example.com[,1.1.1.1,etc.]): " RA_DNS_NAMES < /dev/tty + done +fi + +if [ -z "$RA_ADDRESS" ]; then + RA_ADDRESS="" + while [[ $RA_ADDRESS = "" ]] ; do + echo "What address should your RA listen on?" + read -p "(e.g. :443 or 10.2.1.201:4430): " RA_ADDRESS < /dev/tty + done +fi + +if [ -z "$CA_PROVISIONER_JWK_PASSWORD_FILE" ]; then + read -s -p "Enter the CA Provisioner Password: " CA_PROVISIONER_JWK_PASSWORD < /dev/tty + printf "%b" "\n" +fi + +echo "Installing 'step-ca' in /usr/bin..." +CA_VERSION=$(curl -s https://api.github.com/repos/smallstep/certificates/releases/latest | jq -r '.tag_name') + +curl -sLO https://github.com/smallstep/certificates/releases/download/$CA_VERSION/step-ca_linux_${CA_VERSION:1}_$arch.tar.gz +tar -xf step-ca_linux_${CA_VERSION:1}_$arch.tar.gz +install -m 0755 -t /usr/bin step-ca_${CA_VERSION:1}/bin/step-ca +setcap CAP_NET_BIND_SERVICE=+eip $(which step-ca) +rm step-ca_linux_${CA_VERSION:1}_$arch.tar.gz +rm -rf step-ca_${CA_VERSION:1} + +echo "Creating 'step' user..." +export STEPPATH=/etc/step-ca + +useradd --system --home $(step path) --shell /bin/false step + +echo "Creating RA configuration..." +mkdir -p $(step path)/db +mkdir -p $(step path)/config + +cat < $(step path)/config/ca.json +{ + "address": "$RA_ADDRESS", + "dnsNames": ["$RA_DNS_NAMES"], + "db": { + "type": "badgerV2", + "dataSource": "/etc/step-ca/db" + }, + "logger": {"format": "text"}, + "authority": { + "type": "stepcas", + "certificateAuthority": "$CA_URL", + "certificateAuthorityFingerprint": "$CA_FINGERPRINT", + "certificateIssuer": { + "type" : "jwk", + "provisioner": "$CA_PROVISIONER_NAME" + }, + "provisioners": [{ + "type": "ACME", + "name": "acme" + }] + }, + "tls": { + "cipherSuites": [ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + ], + "minVersion": 1.2, + "maxVersion": 1.3, + "renegotiation": false + } +} +EOF + +if ! [ -z "$CA_PROVISIONER_JWK_PASSWORD" ]; then + echo "Saving provisoiner password to $(step path)/password.txt..." + echo $CA_PROVISIONER_JWK_PASSWORD > $(step path)/password.txt +else + echo "Copying provisioner password file to $(step path)/password.txt..." + cp $CA_PROVISIONER_JWK_PASSWORD_FILE $(step path)/password.txt +fi +chmod 440 $(step path)/password.txt + +# Add a service to systemd for the RA. +echo "Creating systemd service step-ca.service..." +curl -sL https://raw.githubusercontent.com/smallstep/certificates/master/systemd/step-ca.service \ + -o /etc/systemd/system/step-ca.service + +echo "Creating RA mode override /etc/systemd/system/step-ca.service.d/local.conf..." +mkdir /etc/systemd/system/step-ca.service.d +cat < /etc/systemd/system/step-ca.service.d/local.conf +[Service] +; The empty ExecStart= clears the inherited ExecStart= value +ExecStart= +ExecStart=/usr/bin/step-ca config/ca.json --issuer-password-file password.txt +EOF + +echo "Starting step-ca.service..." +systemctl daemon-reload + +chown -R step:step $(step path) + +systemctl enable --now step-ca + +echo "Adding STEPPATH export to /root/.bash_profile..." +echo "export STEPPATH=$STEPPATH" >> /root/.bash_profile + +echo "Finished. Check the journal with journalctl -fu step-ca.service" + From 4f27f4b0020a0715be830e6f557fa04fa850b0cb Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 28 Jul 2021 13:56:05 -0700 Subject: [PATCH 133/291] Change default ciphersuites to newer names. --- authority/config/config.go | 3 ++- authority/config/tls_options.go | 19 ++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/authority/config/config.go b/authority/config/config.go index 4d7592ac..7c6de130 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -189,9 +189,10 @@ func (c *Config) Validate() error { switch { case c.Address == "": return errors.New("address cannot be empty") - case len(c.DNSNames) == 0: return errors.New("dnsNames cannot be empty") + case c.AuthorityConfig == nil: + return errors.New("authority cannot be nil") } // Options holds the RA/CAS configuration. diff --git a/authority/config/tls_options.go b/authority/config/tls_options.go index ed61cfc9..0db202e5 100644 --- a/authority/config/tls_options.go +++ b/authority/config/tls_options.go @@ -15,8 +15,9 @@ var ( // DefaultTLSRenegotiation default TLS connection renegotiation policy. DefaultTLSRenegotiation = false // Never regnegotiate. // DefaultTLSCipherSuites specifies default step ciphersuite(s). + // These are TLS 1.0 - 1.2 cipher suites. DefaultTLSCipherSuites = CipherSuites{ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", } // ApprovedTLSCipherSuites smallstep approved ciphersuites. @@ -26,25 +27,21 @@ var ( "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", } // DefaultTLSOptions represents the default TLS version as well as the cipher // suites used in the TLS certificates. DefaultTLSOptions = TLSOptions{ - CipherSuites: CipherSuites{ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - }, - MinVersion: 1.2, - MaxVersion: 1.2, - Renegotiation: false, + CipherSuites: DefaultTLSCipherSuites, + MinVersion: DefaultTLSMinVersion, + MaxVersion: DefaultTLSMaxVersion, + Renegotiation: DefaultTLSRenegotiation, } ) From ac363d7824c1ea4c71edbd26de6d7b53bdb5a822 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 28 Jul 2021 15:21:48 -0700 Subject: [PATCH 134/291] Add --password-file and --issuer-password-file flags to export. --- commands/export.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/commands/export.go b/commands/export.go index bb11fea7..0080df7d 100644 --- a/commands/export.go +++ b/commands/export.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/json" "fmt" + "io/ioutil" + "unicode" "github.com/pkg/errors" "github.com/smallstep/certificates/authority" @@ -23,6 +25,7 @@ func init() { Action: exportAction, Description: `**step-ca export** exports the current configuration of step-ca. +Note that neither the PKI password nor ## POSITIONAL ARGUMENTS @@ -34,6 +37,18 @@ Export the current configuration: ''' $ step-ca export $(step path)/config/ca.json '''`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "password-file", + Usage: `path to the containing the password to decrypt the +intermediate private key.`, + }, + cli.StringFlag{ + Name: "issuer-password-file", + Usage: `path to the containing the password to decrypt the + certificate issuer private key used in the RA mode.`, + }, + }, }) } @@ -43,11 +58,33 @@ func exportAction(ctx *cli.Context) error { } configFile := ctx.Args().Get(0) + passwordFile := ctx.String("password-file") + issuerPasswordFile := ctx.String("issuer-password-file") config, err := config.LoadConfiguration(configFile) if err != nil { return err } + if err := config.Validate(); err != nil { + return err + } + + if passwordFile != "" { + b, err := ioutil.ReadFile(passwordFile) + if err != nil { + return errors.Wrapf(err, "error reading %s", passwordFile) + } + config.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) + } + if issuerPasswordFile != "" { + b, err := ioutil.ReadFile(issuerPasswordFile) + if err != nil { + return errors.Wrapf(err, "error reading %s", issuerPasswordFile) + } + if config.AuthorityConfig.CertificateIssuer != nil { + config.AuthorityConfig.CertificateIssuer.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) + } + } auth, err := authority.New(config) if err != nil { From e62d7988b836e660500718bf78e362054d159c37 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 28 Jul 2021 15:22:21 -0700 Subject: [PATCH 135/291] Do not store password on exports. --- authority/export.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/authority/export.go b/authority/export.go index 4c5059ea..b8679ac6 100644 --- a/authority/export.go +++ b/authority/export.go @@ -13,6 +13,11 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) +// Export creates a linkedca configuration form the current ca.json and loaded +// authorities. +// +// Note that export will not export neither the pki password nor the certificate +// issuer password. func (a *Authority) Export() (c *config.Configuration, err error) { // Recover from panics defer func() { @@ -22,6 +27,8 @@ func (a *Authority) Export() (c *config.Configuration, err error) { }() files := make(map[string][]byte) + + // The exported configuration should not include the password in it. c = &config.Configuration{ Version: "1.0", Root: mustReadFilesOrUris(a.config.Root, files), @@ -40,8 +47,7 @@ func (a *Authority) Export() (c *config.Configuration, err error) { DisableIssuedAtCheck: a.config.AuthorityConfig.DisableIssuedAtCheck, Backdate: a.config.AuthorityConfig.Backdate.String(), }, - Password: mustPassword(a.config.Password), - Files: files, + Files: files, } // SSH @@ -109,12 +115,12 @@ func (a *Authority) Export() (c *config.Configuration, err error) { if !ok { return nil, errors.Errorf("unknown certificate issuer type %s", iss.Type) } + // The exporte certificate issuer should not include the password. c.Authority.CertificateIssuer = &config.CertificateIssuer{ Type: config.CertificateIssuer_Type(typ), Provisioner: iss.Provisioner, Certificate: mustReadFileOrUri(iss.Certificate, files), Key: mustReadFileOrUri(iss.Key, files), - Password: mustPassword(iss.Password), } } } From 2620c38aee77526692c8c3934d7a3e3acb8a5861 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 28 Jul 2021 18:05:57 -0700 Subject: [PATCH 136/291] Add is converting provisioners to linkedca. The ids are required to be able to link admins with provisioners. --- authority/provisioners.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/authority/provisioners.go b/authority/provisioners.go index 3e2d1276..ab069501 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -716,6 +716,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, errors.Wrap(err, "error marshaling key") } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_JWK, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -736,6 +737,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_OIDC, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -762,6 +764,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_GCP, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -785,6 +788,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_AWS, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -807,6 +811,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_AZURE, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -830,6 +835,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_ACME, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -849,6 +855,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_X5C, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -868,6 +875,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_K8SSA, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -883,6 +891,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro }, nil case *provisioner.SSHPOP: return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_SSHPOP, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ @@ -898,6 +907,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro return nil, err } return &linkedca.Provisioner{ + Id: p.ID, Type: linkedca.Provisioner_SCEP, Name: p.GetName(), Details: &linkedca.ProvisionerDetails{ From 5344f42f21f271f260fc159827cff5fcc2363aa7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 11:33:02 -0700 Subject: [PATCH 137/291] Allow to use the environment variable STEP_CA_TOKEN For helm charts we want to store the tokens in a secret and load it from an environment variable. --- commands/app.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commands/app.go b/commands/app.go index 3b874ae8..481c4867 100644 --- a/commands/app.go +++ b/commands/app.go @@ -39,8 +39,9 @@ certificate issuer private key used in the RA mode.`, Usage: "address of a DNS resolver to be used instead of the default.", }, cli.StringFlag{ - Name: "token", - Usage: "token used to enable the linked ca.", + Name: "token", + Usage: "token used to enable the linked ca.", + EnvVar: "STEP_CA_TOKEN", }, }, } From 26122a2cbfa7530712de7713c04ab0477ce04991 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 11:48:37 -0700 Subject: [PATCH 138/291] Enable admin automatically if a token is provided. --- authority/authority.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/authority.go b/authority/authority.go index 80242e8b..ae41f129 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -440,7 +440,7 @@ func (a *Authority) init() error { // TODO: mimick the x509CAService GetCertificateAuthority here too? } - if a.config.AuthorityConfig.EnableAdmin { + if a.config.AuthorityConfig.EnableAdmin || a.linkedCAToken != "" { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. if a.adminDB == nil { From 91a369f61857748f96f28a712ac8563a8282ce38 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 12:13:39 -0700 Subject: [PATCH 139/291] Automatically enable admin properly on linked cas. --- authority/authority.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/authority/authority.go b/authority/authority.go index ae41f129..28af693c 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -207,6 +207,11 @@ func (a *Authority) init() error { var err error + // Automatically enable admin for all linked cas. + if a.linkedCAToken != "" { + a.config.AuthorityConfig.EnableAdmin = true + } + // Initialize step-ca Database if it's not already initialized with WithDB. // If a.config.DB is nil then a simple, barebones in memory DB will be used. if a.db == nil { @@ -440,7 +445,7 @@ func (a *Authority) init() error { // TODO: mimick the x509CAService GetCertificateAuthority here too? } - if a.config.AuthorityConfig.EnableAdmin || a.linkedCAToken != "" { + if a.config.AuthorityConfig.EnableAdmin { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. if a.adminDB == nil { From b0e0f2b89d8d9c14f582b6d40ad0620c943b0902 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 14:45:59 -0700 Subject: [PATCH 140/291] Use linkedca GetAdmin and GetProvisioner. --- authority/linkedca.go | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/authority/linkedca.go b/authority/linkedca.go index 75bc6e1a..e0678aca 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -15,7 +15,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/tlsutil" @@ -141,18 +140,13 @@ func (c *linkedCaClient) CreateProvisioner(ctx context.Context, prov *linkedca.P } func (c *linkedCaClient) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { - resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ - AuthorityId: c.authorityID, + resp, err := c.client.GetProvisioner(ctx, &linkedca.GetProvisionerRequest{ + Id: id, }) if err != nil { return nil, errors.Wrap(err, "error getting provisioners") } - for _, p := range resp.Provisioners { - if p.Id == id { - return p, nil - } - } - return nil, errs.NotFound("provisioner not found") + return resp, nil } func (c *linkedCaClient) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { @@ -199,18 +193,13 @@ func (c *linkedCaClient) CreateAdmin(ctx context.Context, adm *linkedca.Admin) e } func (c *linkedCaClient) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) { - resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ - AuthorityId: c.authorityID, + resp, err := c.client.GetAdmin(ctx, &linkedca.GetAdminRequest{ + Id: id, }) if err != nil { return nil, errors.Wrap(err, "error getting admins") } - for _, a := range resp.Admins { - if a.Id == id { - return a, nil - } - } - return nil, errs.NotFound("admin not found") + return resp, nil } func (c *linkedCaClient) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { From 384be6e2053e9826011faa4ae05f1d9cdbc518aa Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 15:34:39 -0700 Subject: [PATCH 141/291] Do not show provisioners if they are not required. For deployment types like linked ca, the list of provisioners in the ca.json are not required, so we should tag the json as omitempty. --- authority/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/config/config.go b/authority/config/config.go index 7c6de130..68886d77 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -85,7 +85,7 @@ type ASN1DN struct { type AuthConfig struct { *cas.Options AuthorityID string `json:"authorityId,omitempty"` - Provisioners provisioner.List `json:"provisioners"` + Provisioners provisioner.List `json:"provisioners,omitempty"` Admins []*linkedca.Admin `json:"-"` Template *ASN1DN `json:"template,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` From 721459210ee273397770ec7976975bb36c88a0cf Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 16:07:30 -0700 Subject: [PATCH 142/291] Make pki initialization more flexible. --- pki/pki.go | 307 +++++++++++++++++++++++++++++++++++++---------- pki/templates.go | 2 +- 2 files changed, 243 insertions(+), 66 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index e4e7bad3..cfe9c49c 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -19,12 +19,16 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/admin" + admindb "github.com/smallstep/certificates/authority/admin/db/nosql" authconfig "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/ca" "github.com/smallstep/certificates/cas" "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" + "github.com/smallstep/nosql" "go.step.sm/cli-utils/config" "go.step.sm/cli-utils/errs" "go.step.sm/cli-utils/fileutil" @@ -32,9 +36,26 @@ import ( "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" + "go.step.sm/linkedca" "golang.org/x/crypto/ssh" ) +// DeploymentType defines witch type of deployment a user is initializing +type DeploymentType int + +const ( + // StandaloneDeployment is a deployment where all the components like keys, + // provisioners, admins, certificates and others are managed by the user. + StandaloneDeployment DeploymentType = iota + // LinkedDeployment is a deployment where the keys are managed by the user, + // but provisioners, admins and the record of certificates are managed in + // the cloud. + LinkedDeployment + // HostedDeployment is a deployment where all the components are managed in + // the cloud by smallstep.com/certificate-manager. + HostedDeployment +) + const ( // ConfigPath is the directory name under the step path where the configuration // files will be stored. @@ -134,9 +155,88 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) { return resp.Key, nil } +type options struct { + address string + caURL string + dnsNames []string + provisioner string + enableACME bool + enableSSH bool + enableAdmin bool + noDB bool + deploymentType DeploymentType +} + +// PKIOption is the type of a configuration option on the pki constructor. +type PKIOption func(o *options) + +// WithAddress sets the listen address of step-ca. +func WithAddress(s string) PKIOption { + return func(o *options) { + o.address = s + } +} + +// WithCaUrl sets the default ca-url of step-ca. +func WithCaUrl(s string) PKIOption { + return func(o *options) { + o.caURL = s + } +} + +// WithDNSNames sets the SANs of step-ca. +func WithDNSNames(s []string) PKIOption { + return func(o *options) { + o.dnsNames = s + } +} + +// WithProvisioner defines the name of the default provisioner. +func WithProvisioner(s string) PKIOption { + return func(o *options) { + o.provisioner = s + } +} + +// WithACME enables acme provisioner in step-ca. +func WithACME() PKIOption { + return func(o *options) { + o.enableACME = true + } +} + +// WithSSH enables ssh in step-ca. +func WithSSH() PKIOption { + return func(o *options) { + o.enableSSH = true + } +} + +// WithAdmin enables the admin api in step-ca. +func WithAdmin() PKIOption { + return func(o *options) { + o.enableAdmin = true + } +} + +// WithNoDB disables the db in step-ca. +func WithNoDB() PKIOption { + return func(o *options) { + o.noDB = true + } +} + +// WithDeploymentType defines the deployment type of step-ca. +func WithDeploymentType(dt DeploymentType) PKIOption { + return func(o *options) { + o.deploymentType = dt + } +} + // PKI represents the Public Key Infrastructure used by a certificate authority. type PKI struct { casOptions apiv1.Options + caService apiv1.CertificateAuthorityService caCreator apiv1.CertificateAuthorityCreator root, rootKey, rootFingerprint string intermediate, intermediateKey string @@ -145,20 +245,25 @@ type PKI struct { config, defaults string ottPublicKey *jose.JSONWebKey ottPrivateKey *jose.JSONWebEncryption - provisioner string - address string - dnsNames []string - caURL string - enableSSH bool + options *options } // New creates a new PKI configuration. -func New(opts apiv1.Options) (*PKI, error) { - caCreator, err := cas.NewCreator(context.Background(), opts) +func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) { + caService, err := cas.New(context.Background(), o) if err != nil { return nil, err } + var caCreator apiv1.CertificateAuthorityCreator + if o.IsCreator { + creator, ok := caService.(apiv1.CertificateAuthorityCreator) + if !ok { + return nil, errors.Errorf("cas type '%s' does not implements CertificateAuthorityCreator", o.Type) + } + caCreator = creator + } + public := GetPublicPath() private := GetSecretsPath() config := GetConfigPath() @@ -180,12 +285,19 @@ func New(opts apiv1.Options) (*PKI, error) { } p := &PKI{ - casOptions: opts, - caCreator: caCreator, - provisioner: "step-cli", - address: "127.0.0.1:9000", - dnsNames: []string{"127.0.0.1"}, + casOptions: o, + caCreator: caCreator, + caService: caService, + options: &options{ + provisioner: "step-cli", + address: "127.0.0.1:9000", + dnsNames: []string{"127.0.0.1"}, + }, + } + for _, fn := range opts { + fn(p.options) } + if p.root, err = getPath(public, "root_ca.crt"); err != nil { return nil, err } @@ -233,23 +345,31 @@ func (p *PKI) GetRootFingerprint() string { } // SetProvisioner sets the provisioner name of the OTT keys. +// +// Deprecated: this method is deprecated in favor of WithProvisioner. func (p *PKI) SetProvisioner(s string) { - p.provisioner = s + p.options.provisioner = s } // SetAddress sets the listening address of the CA. +// +// Deprecated: this method is deprecated in favor of WithAddress. func (p *PKI) SetAddress(s string) { - p.address = s + p.options.address = s } // SetDNSNames sets the dns names of the CA. +// +// Deprecated: this method is deprecated in favor of WithDNSNames. func (p *PKI) SetDNSNames(s []string) { - p.dnsNames = s + p.options.dnsNames = s } // SetCAURL sets the ca-url to use in the defaults.json. +// +// Deprecated: this method is deprecated in favor of WithCaUrl. func (p *PKI) SetCAURL(s string) { - p.caURL = s + p.options.caURL = s } // GenerateKeyPairs generates the key pairs used by the certificate authority. @@ -379,7 +499,7 @@ func (p *PKI) CreateCertificateAuthorityResponse(cert *x509.Certificate, key cry // GetCertificateAuthority attempts to load the certificate authority from the // RA. func (p *PKI) GetCertificateAuthority() error { - srv, ok := p.caCreator.(apiv1.CertificateAuthorityGetter) + srv, ok := p.caService.(apiv1.CertificateAuthorityGetter) if !ok { return nil } @@ -427,7 +547,7 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { return err } } - p.enableSSH = true + p.options.enableSSH = true return nil } @@ -440,7 +560,8 @@ func (p *PKI) askFeedback() { ui.Println(" phone home. But your feedback is extremely valuable. Any information you") ui.Println(" can provide regarding how you’re using `step` helps. Please send us a") ui.Println(" sentence or two, good or bad: \033[1mfeedback@smallstep.com\033[0m or join") - ui.Println(" \033[1mhttps://github.com/smallstep/certificates/discussions\033[0m.") + ui.Println(" \033[1mhttps://github.com/smallstep/certificates/discussions\033[0m and our Discord") + ui.Println(" \033[1mhttps://bit.ly/step-discord\033[0m.") } // TellPKI outputs the locations of public and private keys generated @@ -465,7 +586,7 @@ func (p *PKI) tellPKI() { } else { ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad) } - if p.enableSSH { + if p.options.enableSSH { ui.PrintSelected("SSH user root certificate", p.sshUserPubKey) ui.PrintSelected("SSH user root private key", p.sshUserKey) ui.PrintSelected("SSH host root certificate", p.sshHostPubKey) @@ -485,6 +606,8 @@ type Option func(c *authconfig.Config) error // WithDefaultDB is a configuration modifier that adds a default DB stanza to // the authority config. +// +// Deprecated: this method is deprecated because this is the default behavior. func WithDefaultDB() Option { return func(c *authconfig.Config) error { c.DB = &db.Config{ @@ -497,6 +620,8 @@ func WithDefaultDB() Option { // WithoutDB is a configuration modifier that adds a default DB stanza to // the authority config. +// +// De[recated: this method is deprecated in favor or WithNoDB. func WithoutDB() Option { return func(c *authconfig.Config) error { c.DB = nil @@ -506,18 +631,6 @@ func WithoutDB() Option { // GenerateConfig returns the step certificates configuration. func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { - key, err := p.ottPrivateKey.CompactSerialize() - if err != nil { - return nil, errors.Wrap(err, "error serializing private key") - } - - prov := &provisioner.JWK{ - Name: p.provisioner, - Type: "JWK", - Key: p.ottPublicKey, - EncryptedKey: key, - } - var authorityOptions *apiv1.Options if !p.casOptions.Is(apiv1.SoftCAS) { authorityOptions = &p.casOptions @@ -528,8 +641,8 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { FederatedRoots: []string{}, IntermediateCert: p.intermediate, IntermediateKey: p.intermediateKey, - Address: p.address, - DNSNames: p.dnsNames, + Address: p.options.address, + DNSNames: p.options.dnsNames, Logger: []byte(`{"format": "text"}`), DB: &db.Config{ Type: "badger", @@ -538,44 +651,109 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { AuthorityConfig: &authconfig.AuthConfig{ Options: authorityOptions, DisableIssuedAtCheck: false, - Provisioners: provisioner.List{prov}, - }, - TLS: &authconfig.TLSOptions{ - MinVersion: authconfig.DefaultTLSMinVersion, - MaxVersion: authconfig.DefaultTLSMaxVersion, - Renegotiation: authconfig.DefaultTLSRenegotiation, - CipherSuites: authconfig.DefaultTLSCipherSuites, + EnableAdmin: false, }, + TLS: &authconfig.DefaultTLSOptions, Templates: p.getTemplates(), } - if p.enableSSH { - enableSSHCA := true - config.SSH = &authconfig.SSHConfig{ - HostKey: p.sshHostKey, - UserKey: p.sshUserKey, + + // On standalone deployments add the provisioners to either the ca.json or + // the database. + var provisioners []provisioner.Interface + if p.options.deploymentType == StandaloneDeployment { + key, err := p.ottPrivateKey.CompactSerialize() + if err != nil { + return nil, errors.Wrap(err, "error serializing private key") + } + + prov := &provisioner.JWK{ + Name: p.options.provisioner, + Type: "JWK", + Key: p.ottPublicKey, + EncryptedKey: key, } - // Enable SSH authorization for default JWK provisioner - prov.Claims = &provisioner.Claims{ - EnableSSHCA: &enableSSHCA, + provisioners = append(provisioners, prov) + + // Add default ACME provisioner if enabled + if p.options.enableACME { + provisioners = append(provisioners, &provisioner.ACME{ + Type: "ACME", + Name: "acme", + }) } - // Add default SSHPOP provisioner - sshpop := &provisioner.SSHPOP{ - Type: "SSHPOP", - Name: "sshpop", - Claims: &provisioner.Claims{ + + if p.options.enableSSH { + enableSSHCA := true + config.SSH = &authconfig.SSHConfig{ + HostKey: p.sshHostKey, + UserKey: p.sshUserKey, + } + // Enable SSH authorization for default JWK provisioner + prov.Claims = &provisioner.Claims{ EnableSSHCA: &enableSSHCA, - }, + } + + // Add default SSHPOP provisioner + provisioners = append(provisioners, &provisioner.SSHPOP{ + Type: "SSHPOP", + Name: "sshpop", + Claims: &provisioner.Claims{ + EnableSSHCA: &enableSSHCA, + }, + }) } - config.AuthorityConfig.Provisioners = append(config.AuthorityConfig.Provisioners, sshpop) } // Apply configuration modifiers for _, o := range opt { - if err = o(config); err != nil { + if err := o(config); err != nil { return nil, err } } + // Set authority.enableAdmin to true + if p.options.enableAdmin { + config.AuthorityConfig.EnableAdmin = true + } + + if p.options.deploymentType == StandaloneDeployment { + if !config.AuthorityConfig.EnableAdmin { + config.AuthorityConfig.Provisioners = provisioners + } else { + db, err := db.New(config.DB) + if err != nil { + return nil, err + } + adminDB, err := admindb.New(db.(nosql.DB), admin.DefaultAuthorityID) + if err != nil { + return nil, err + } + // Add all the provisioners to the db. + var adminID string + for i, p := range provisioners { + prov, err := authority.ProvisionerToLinkedca(p) + if err != nil { + return nil, err + } + if err := adminDB.CreateProvisioner(context.Background(), prov); err != nil { + return nil, err + } + if i == 0 { + adminID = prov.Id + } + } + // Add the first provisioner as an admin. + if err := adminDB.CreateAdmin(context.Background(), &linkedca.Admin{ + AuthorityId: admin.DefaultAuthorityID, + Subject: "step", + Type: linkedca.Admin_SUPER_ADMIN, + ProvisionerId: adminID, + }); err != nil { + return nil, err + } + } + } + return config, nil } @@ -599,17 +777,16 @@ func (p *PKI) Save(opt ...Option) error { } // Generate the CA URL. - if p.caURL == "" { - p.caURL = p.dnsNames[0] - var port string - _, port, err = net.SplitHostPort(p.address) + if p.options.caURL == "" { + p.options.caURL = p.options.dnsNames[0] + _, port, err := net.SplitHostPort(p.options.address) if err != nil { - return errors.Wrapf(err, "error parsing %s", p.address) + return errors.Wrapf(err, "error parsing %s", p.options.address) } if port == "443" { - p.caURL = fmt.Sprintf("https://%s", p.caURL) + p.options.caURL = fmt.Sprintf("https://%s", p.options.caURL) } else { - p.caURL = fmt.Sprintf("https://%s:%s", p.caURL, port) + p.options.caURL = fmt.Sprintf("https://%s:%s", p.options.caURL, port) } } @@ -617,7 +794,7 @@ func (p *PKI) Save(opt ...Option) error { defaults := &caDefaults{ Root: p.root, CAConfig: p.config, - CAUrl: p.caURL, + CAUrl: p.options.caURL, Fingerprint: p.rootFingerprint, } b, err = json.MarshalIndent(defaults, "", "\t") diff --git a/pki/templates.go b/pki/templates.go index 4c5309bb..3506a96d 100644 --- a/pki/templates.go +++ b/pki/templates.go @@ -13,7 +13,7 @@ import ( // getTemplates returns all the templates enabled func (p *PKI) getTemplates() *templates.Templates { - if !p.enableSSH { + if !p.options.enableSSH { return nil } return &templates.Templates{ From de292fbed60731360f00dbd6d173f4a83903ded1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 2 Aug 2021 16:08:54 -0700 Subject: [PATCH 143/291] Use branch version of linkedca. --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 98e7dbdb..12933ece 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.3.0 + go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect @@ -42,6 +42,6 @@ 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 +// replace go.step.sm/linkedca => ../linkedca replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index 9310efc5..a0996433 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.3.0 h1:6jyghg/ErVTJ/J23DrbWhGyWbWX2b4aJkPQEGL4xZ40= -go.step.sm/linkedca v0.3.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d h1:d5cE1Bgyqw4pW3M7cPD+DndyOgKf41WJIzO+Dnx3q+4= +go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d/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 de719eb6f0228b8ebef847f96c6b60c842080f3f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 4 Aug 2021 16:16:35 -0700 Subject: [PATCH 144/291] Add an option to avoid password prompts on step cas When we are using `step ca init` to create a stepcas RA we don't have access to the password for verify the provisioner. --- cas/apiv1/options.go | 11 +++++++++-- cas/stepcas/stepcas.go | 11 +++++++---- cas/stepcas/stepcas_test.go | 13 +++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index 61cac9a2..badad7fc 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -38,10 +38,17 @@ type Options struct { CertificateChain []*x509.Certificate `json:"-"` Signer crypto.Signer `json:"-"` - // IsCreator is set to true when we're creating a certificate authority. Is - // used to skip some validations when initializing a CertificateAuthority. + // IsCreator is set to true when we're creating a certificate authority. It + // is used to skip some validations when initializing a + // CertificateAuthority. This option is used on SoftCAS and CloudCAS. IsCreator bool `json:"-"` + // IsCAGetter is set to true when we're just using the + // CertificateAuthorityGetter interface to retrieve the root certificate. It + // is used to skip some validations when initializing a + // CertificateAuthority. This option is used on StepCAS. + IsCAGetter bool `json:"-"` + // KeyManager is the KMS used to generate keys in SoftCAS. KeyManager kms.KeyManager `json:"-"` diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index 49a99963..a124b4ae 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -47,10 +47,13 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) { return nil, err } - // Create configured issuer - iss, err := newStepIssuer(caURL, client, opts.CertificateIssuer) - if err != nil { - return nil, err + var iss stepIssuer + // Create configured issuer unless we only want to use GetCertificateAuthority. + // This avoid the request for the password if not provided. + if !opts.IsCAGetter { + if iss, err = newStepIssuer(caURL, client, opts.CertificateIssuer); err != nil { + return nil, err + } } return &StepCAS{ diff --git a/cas/stepcas/stepcas_test.go b/cas/stepcas/stepcas_test.go index fb8259f5..f430a1dd 100644 --- a/cas/stepcas/stepcas_test.go +++ b/cas/stepcas/stepcas_test.go @@ -411,6 +411,19 @@ func TestNew(t *testing.T) { client: client, fingerprint: testRootFingerprint, }, false}, + {"ok ca getter", args{context.TODO(), apiv1.Options{ + IsCAGetter: true, + CertificateAuthority: caURL.String(), + CertificateAuthorityFingerprint: testRootFingerprint, + CertificateIssuer: &apiv1.CertificateIssuer{ + Type: "jwk", + Provisioner: "ra@doe.org", + }, + }}, &StepCAS{ + iss: nil, + client: client, + fingerprint: testRootFingerprint, + }, false}, {"fail authority", args{context.TODO(), apiv1.Options{ CertificateAuthority: "", CertificateAuthorityFingerprint: testRootFingerprint, From 798b90c35947d6cd72e2eb6c2cb0ef746e90f528 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 4 Aug 2021 20:15:04 -0700 Subject: [PATCH 145/291] Move linkedca configuration to the main package. --- authority/export.go | 70 +++++++++++++++++++++++++-------------------- go.mod | 2 +- go.sum | 4 +-- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/authority/export.go b/authority/export.go index b8679ac6..97efac03 100644 --- a/authority/export.go +++ b/authority/export.go @@ -8,8 +8,9 @@ import ( "strings" "github.com/pkg/errors" - step "go.step.sm/cli-utils/config" - "go.step.sm/linkedca/config" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/cli-utils/config" + "go.step.sm/linkedca" "google.golang.org/protobuf/types/known/structpb" ) @@ -18,7 +19,7 @@ import ( // // Note that export will not export neither the pki password nor the certificate // issuer password. -func (a *Authority) Export() (c *config.Configuration, err error) { +func (a *Authority) Export() (c *linkedca.Configuration, err error) { // Recover from panics defer func() { if r := recover(); r != nil { @@ -29,7 +30,7 @@ func (a *Authority) Export() (c *config.Configuration, err error) { files := make(map[string][]byte) // The exported configuration should not include the password in it. - c = &config.Configuration{ + c = &linkedca.Configuration{ Version: "1.0", Root: mustReadFilesOrUris(a.config.Root, files), FederatedRoots: mustReadFilesOrUris(a.config.FederatedRoots, files), @@ -41,36 +42,36 @@ func (a *Authority) Export() (c *config.Configuration, err error) { Db: mustMarshalToStruct(a.config.DB), Logger: mustMarshalToStruct(a.config.Logger), Monitoring: mustMarshalToStruct(a.config.Monitoring), - Authority: &config.Authority{ + Authority: &linkedca.Authority{ Id: a.config.AuthorityConfig.AuthorityID, EnableAdmin: a.config.AuthorityConfig.EnableAdmin, DisableIssuedAtCheck: a.config.AuthorityConfig.DisableIssuedAtCheck, - Backdate: a.config.AuthorityConfig.Backdate.String(), + Backdate: mustDuration(a.config.AuthorityConfig.Backdate), }, Files: files, } // SSH if v := a.config.SSH; v != nil { - c.Ssh = &config.SSH{ + c.Ssh = &linkedca.SSH{ HostKey: mustReadFileOrUri(v.HostKey, files), UserKey: mustReadFileOrUri(v.UserKey, files), AddUserPrincipal: v.AddUserPrincipal, AddUserCommand: v.AddUserCommand, } for _, k := range v.Keys { - typ, ok := config.SSHPublicKey_Type_value[strings.ToUpper(k.Type)] + typ, ok := linkedca.SSHPublicKey_Type_value[strings.ToUpper(k.Type)] if !ok { return nil, errors.Errorf("unsupported ssh key type %s", k.Type) } - c.Ssh.Keys = append(c.Ssh.Keys, &config.SSHPublicKey{ - Type: config.SSHPublicKey_Type(typ), + c.Ssh.Keys = append(c.Ssh.Keys, &linkedca.SSHPublicKey{ + Type: linkedca.SSHPublicKey_Type(typ), Federated: k.Federated, Key: mustMarshalToStruct(k), }) } if b := v.Bastion; b != nil { - c.Ssh.Bastion = &config.Bastion{ + c.Ssh.Bastion = &linkedca.Bastion{ Hostname: b.Hostname, User: b.User, Port: b.Port, @@ -85,15 +86,15 @@ func (a *Authority) Export() (c *config.Configuration, err error) { var typ int32 var ok bool if v.Type == "" { - typ = int32(config.KMS_SOFTKMS) + typ = int32(linkedca.KMS_SOFTKMS) } else { - typ, ok = config.KMS_Type_value[strings.ToUpper(v.Type)] + typ, ok = linkedca.KMS_Type_value[strings.ToUpper(v.Type)] if !ok { return nil, errors.Errorf("unsupported kms type %s", v.Type) } } - c.Kms = &config.KMS{ - Type: config.KMS_Type(typ), + c.Kms = &linkedca.KMS{ + Type: linkedca.KMS_Type(typ), CredentialsFile: v.CredentialsFile, Uri: v.URI, Pin: v.Pin, @@ -111,13 +112,13 @@ func (a *Authority) Export() (c *config.Configuration, err error) { c.Authority.CertificateAuthorityFingerprint = v.CertificateAuthorityFingerprint c.Authority.CredentialsFile = v.CredentialsFile if iss := v.CertificateIssuer; iss != nil { - typ, ok := config.CertificateIssuer_Type_value[strings.ToUpper(iss.Type)] + typ, ok := linkedca.CertificateIssuer_Type_value[strings.ToUpper(iss.Type)] if !ok { return nil, errors.Errorf("unknown certificate issuer type %s", iss.Type) } // The exporte certificate issuer should not include the password. - c.Authority.CertificateIssuer = &config.CertificateIssuer{ - Type: config.CertificateIssuer_Type(typ), + c.Authority.CertificateIssuer = &linkedca.CertificateIssuer{ + Type: linkedca.CertificateIssuer_Type(typ), Provisioner: iss.Provisioner, Certificate: mustReadFileOrUri(iss.Certificate, files), Key: mustReadFileOrUri(iss.Key, files), @@ -150,7 +151,7 @@ func (a *Authority) Export() (c *config.Configuration, err error) { c.Authority.Claims = claimsToLinkedca(a.config.AuthorityConfig.Claims) // Distiguised names template if v := a.config.AuthorityConfig.Template; v != nil { - c.Authority.Template = &config.DistinguishedName{ + c.Authority.Template = &linkedca.DistinguishedName{ Country: v.Country, Organization: v.Organization, OrganizationalUnit: v.OrganizationalUnit, @@ -164,20 +165,20 @@ func (a *Authority) Export() (c *config.Configuration, err error) { // TLS if v := a.config.TLS; v != nil { - c.Tls = &config.TLS{ + c.Tls = &linkedca.TLS{ MinVersion: v.MinVersion.String(), MaxVersion: v.MaxVersion.String(), Renegotiation: v.Renegotiation, } for _, cs := range v.CipherSuites.Value() { - c.Tls.CipherSuites = append(c.Tls.CipherSuites, config.TLS_CiperSuite(cs)) + c.Tls.CipherSuites = append(c.Tls.CipherSuites, linkedca.TLS_CiperSuite(cs)) } } // Templates if v := a.config.Templates; v != nil { - c.Templates = &config.Templates{ - Ssh: &config.SSHTemplate{}, + c.Templates = &linkedca.ConfigTemplates{ + Ssh: &linkedca.SSHConfigTemplate{}, Data: mustMarshalToStruct(v.Data), } // Remove automatically loaded vars @@ -185,12 +186,12 @@ func (a *Authority) Export() (c *config.Configuration, err error) { delete(c.Templates.Data.Fields, "Step") } for _, t := range v.SSH.Host { - typ, ok := config.Template_Type_value[strings.ToUpper(string(t.Type))] + typ, ok := linkedca.ConfigTemplate_Type_value[strings.ToUpper(string(t.Type))] if !ok { return nil, errors.Errorf("unsupported template type %s", t.Type) } - c.Templates.Ssh.Hosts = append(c.Templates.Ssh.Hosts, &config.Template{ - Type: config.Template_Type(typ), + c.Templates.Ssh.Hosts = append(c.Templates.Ssh.Hosts, &linkedca.ConfigTemplate{ + Type: linkedca.ConfigTemplate_Type(typ), Name: t.Name, Template: mustReadFileOrUri(t.TemplatePath, files), Path: t.Path, @@ -200,12 +201,12 @@ func (a *Authority) Export() (c *config.Configuration, err error) { }) } for _, t := range v.SSH.User { - typ, ok := config.Template_Type_value[strings.ToUpper(string(t.Type))] + typ, ok := linkedca.ConfigTemplate_Type_value[strings.ToUpper(string(t.Type))] if !ok { return nil, errors.Errorf("unsupported template type %s", t.Type) } - c.Templates.Ssh.Users = append(c.Templates.Ssh.Users, &config.Template{ - Type: config.Template_Type(typ), + c.Templates.Ssh.Users = append(c.Templates.Ssh.Users, &linkedca.ConfigTemplate{ + Type: linkedca.ConfigTemplate_Type(typ), Name: t.Name, Template: mustReadFileOrUri(t.TemplatePath, files), Path: t.Path, @@ -226,6 +227,13 @@ func mustPassword(s string) []byte { return []byte(s) } +func mustDuration(d *provisioner.Duration) string { + if d == nil || d.Duration == 0 { + return "" + } + return d.String() +} + func mustMarshalToStruct(v interface{}) *structpb.Struct { b, err := json.Marshal(v) if err != nil { @@ -243,7 +251,7 @@ func mustReadFileOrUri(fn string, m map[string][]byte) string { return "" } - stepPath := filepath.ToSlash(step.StepPath()) + stepPath := filepath.ToSlash(config.StepPath()) if !strings.HasSuffix(stepPath, "/") { stepPath += "/" } @@ -255,7 +263,7 @@ func mustReadFileOrUri(fn string, m map[string][]byte) string { panic(err) } if ok { - b, err := ioutil.ReadFile(step.StepAbs(fn)) + b, err := ioutil.ReadFile(config.StepAbs(fn)) if err != nil { panic(errors.Wrapf(err, "error reading %s", fn)) } diff --git a/go.mod b/go.mod index 12933ece..1149ac33 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d + go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect diff --git a/go.sum b/go.sum index a0996433..555f5b64 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d h1:d5cE1Bgyqw4pW3M7cPD+DndyOgKf41WJIzO+Dnx3q+4= -go.step.sm/linkedca v0.4.1-0.20210802195257-6104dc57167d/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d h1:bMcTynjdYq1Xmoi0G3NPCfV/aP1/vVQ/p7W3oYhoVXU= +go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d/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 50f7a0d0c017da03a60a41cbc72b6e2ddb252e41 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 4 Aug 2021 20:15:26 -0700 Subject: [PATCH 146/291] Work in progress implementation of PKI with helm support --- pki/helm.go | 150 ++++++++++++++++++++++++ pki/pki.go | 327 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 354 insertions(+), 123 deletions(-) create mode 100644 pki/helm.go diff --git a/pki/helm.go b/pki/helm.go new file mode 100644 index 00000000..7e4f1b2d --- /dev/null +++ b/pki/helm.go @@ -0,0 +1,150 @@ +package pki + +import ( + "io" + "text/template" + + "github.com/Masterminds/sprig/v3" + "github.com/pkg/errors" + authconfig "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/linkedca" +) + +type helmVariables struct { + linkedca.Configuration + Defaults linkedca.Defaults + Password string + SSH struct { + Enabled bool + } + TLS authconfig.TLSOptions + Provisioners []provisioner.Interface +} + +func (p *PKI) WriteHelmTemplate(w io.Writer) error { + tmpl, err := template.New("helm").Funcs(sprig.TxtFuncMap()).Parse(helmTemplate) + if err != nil { + return errors.Wrap(err, "error writing helm template") + } + + // Delete ssh section if it is not enabled + if !p.options.enableSSH { + p.Ssh = nil + } + + if err := tmpl.Execute(w, helmVariables{ + Configuration: p.Configuration, + Defaults: p.Defaults, + Password: "asdf", + TLS: authconfig.DefaultTLSOptions, + Provisioners: []provisioner.Interface{ + &provisioner.JWK{ + Name: p.options.provisioner, + Type: "JWK", + Key: p.ottPublicKey, + EncryptedKey: "", + }, + }, + }); err != nil { + return errors.Wrap(err, "error executing helm template") + } + return nil +} + +const helmTemplate = `# Helm template +inject: + enabled: true + # Config contains the configuration files ca.json and defaults.json + config: + files: + ca.json: + root: {{ first .Root }} + federateRoots: [] + crt: {{ .Intermediate }} + key: {{ .IntermediateKey }} + {{- if .SSH.Enabled }} + ssh: + hostKey: {{ .Ssh.HostKey }} + userKey: {{ .Ssh.UserKey }} + {{- end }} + address: {{ .Address }} + dnsNames: + {{- range .DnsNames }} + - {{ . }} + {{- end }} + logger: + format: json + db: + type: badger + dataSource: /home/step/db + authority: + provisioners: + {{- range .Provisioners }} + - {{ . | toJson }} + {{- end }} + tls: + cipherSuites: + {{- range .TLS.CipherSuites }} + - {{ . }} + {{- end }} + minVersion: {{ .TLS.MinVersion }} + maxVersion: {{ .TLS.MaxVersion }} + renegotiation: {{ .TLS.Renegotiation }} + + defaults.json: + ca-url: {{ .Defaults.CaUrl }} + ca-config: {{ .Defaults.CaConfig }} + fingerprint: {{ .Defaults.Fingerprint }} + root: {{ .Defaults.Root }} + + # Certificates contains the root and intermediate certificate and + # optionally the SSH host and user public keys + certificates: + # intermediate_ca contains the text of the intermediate CA Certificate + intermediate_ca: | + {{- index .Files .Intermediate | toString | nindent 6 }} + + # root_ca contains the text of the root CA Certificate + root_ca: | + {{- first .Root | index .Files | toString | nindent 6 }} + + {{- if .Ssh }} + # ssh_host_ca contains the text of the public ssh key for the SSH root CA + ssh_host_ca: {{ index .Files .Ssh.HostPublicKey | toString }} + + # ssh_user_ca contains the text of the public ssh key for the SSH root CA + ssh_user_ca: {{ index .Files .Ssh.UserPublicKey | toString }} + {{- end }} + + # Secrets contains the root and intermediate keys and optionally the SSH + # private keys + secrets: + # ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key + # This value must be base64 encoded. + ca_password: {{ .Password | b64enc }} + provisioner_password: {{ .Password | b64enc}} + + x509: + # intermediate_ca_key contains the contents of your encrypted intermediate CA key + intermediate_ca_key: | + {{- index .Files .IntermediateKey | toString | nindent 8 }} + + # root_ca_key contains the contents of your encrypted root CA key + # Note that this value can be omitted without impacting the functionality of step-certificates + # If supplied, this should be encrypted using a unique password that is not used for encrypting + # the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key. + root_ca_key: | + {{- first .RootKey | index .Files | toString | nindent 8 }} + + {{- if .Ssh }} + ssh: + # ssh_host_ca_key contains the contents of your encrypted SSH Host CA key + host_ca_key: | + {{- index .Files .Ssh.HostKey | toString | nindent 8 }} + + # ssh_user_ca_key contains the contents of your encrypted SSH User CA key + user_ca_key: | + {{- index .Files .Ssh.UserKey | toString | nindent 8 }} + {{- end }} +` diff --git a/pki/pki.go b/pki/pki.go index cfe9c49c..72350cd6 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -156,96 +156,108 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) { } type options struct { - address string - caURL string - dnsNames []string + // address string + // caURL string + // dnsNames []string provisioner string enableACME bool enableSSH bool enableAdmin bool noDB bool + isHelm bool deploymentType DeploymentType } // PKIOption is the type of a configuration option on the pki constructor. -type PKIOption func(o *options) +type PKIOption func(p *PKI) // WithAddress sets the listen address of step-ca. func WithAddress(s string) PKIOption { - return func(o *options) { - o.address = s + return func(p *PKI) { + p.Address = s } } // WithCaUrl sets the default ca-url of step-ca. func WithCaUrl(s string) PKIOption { - return func(o *options) { - o.caURL = s + return func(p *PKI) { + p.Defaults.CaUrl = s } } // WithDNSNames sets the SANs of step-ca. func WithDNSNames(s []string) PKIOption { - return func(o *options) { - o.dnsNames = s + return func(p *PKI) { + p.DnsNames = s } } // WithProvisioner defines the name of the default provisioner. func WithProvisioner(s string) PKIOption { - return func(o *options) { - o.provisioner = s + return func(p *PKI) { + p.options.provisioner = s } } // WithACME enables acme provisioner in step-ca. func WithACME() PKIOption { - return func(o *options) { - o.enableACME = true + return func(p *PKI) { + p.options.enableACME = true } } // WithSSH enables ssh in step-ca. func WithSSH() PKIOption { - return func(o *options) { - o.enableSSH = true + return func(p *PKI) { + p.options.enableSSH = true } } // WithAdmin enables the admin api in step-ca. func WithAdmin() PKIOption { - return func(o *options) { - o.enableAdmin = true + return func(p *PKI) { + p.options.enableAdmin = true } } // WithNoDB disables the db in step-ca. func WithNoDB() PKIOption { - return func(o *options) { - o.noDB = true + return func(p *PKI) { + p.options.noDB = true + } +} + +// WithHelm configures the pki to create a helm values.yaml. +func WithHelm() PKIOption { + return func(p *PKI) { + p.options.isHelm = true } } // WithDeploymentType defines the deployment type of step-ca. func WithDeploymentType(dt DeploymentType) PKIOption { - return func(o *options) { - o.deploymentType = dt + return func(p *PKI) { + p.options.deploymentType = dt } } // PKI represents the Public Key Infrastructure used by a certificate authority. type PKI struct { - casOptions apiv1.Options - caService apiv1.CertificateAuthorityService - caCreator apiv1.CertificateAuthorityCreator - root, rootKey, rootFingerprint string - intermediate, intermediateKey string - sshHostPubKey, sshHostKey string - sshUserPubKey, sshUserKey string - config, defaults string - ottPublicKey *jose.JSONWebKey - ottPrivateKey *jose.JSONWebEncryption - options *options + linkedca.Configuration + Defaults linkedca.Defaults + casOptions apiv1.Options + caService apiv1.CertificateAuthorityService + caCreator apiv1.CertificateAuthorityCreator + // root, rootKey, rootFingerprint string + // intermediate, intermediateKey string + // sshHostPubKey, sshHostKey string + // sshUserPubKey, sshUserKey string + config string + defaults string + // rootFingerprint string + ottPublicKey *jose.JSONWebKey + ottPrivateKey *jose.JSONWebEncryption + options *options } // New creates a new PKI configuration. @@ -264,20 +276,6 @@ func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) { caCreator = creator } - public := GetPublicPath() - private := GetSecretsPath() - config := GetConfigPath() - - // Create directories - dirs := []string{public, private, config, GetTemplatesPath()} - for _, name := range dirs { - if _, err := os.Stat(name); os.IsNotExist(err) { - if err = os.MkdirAll(name, 0700); err != nil { - return nil, errs.FileError(err, name) - } - } - } - // get absolute path for dir/name getPath := func(dir string, name string) (string, error) { s, err := filepath.Abs(filepath.Join(dir, name)) @@ -285,51 +283,95 @@ func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) { } p := &PKI{ + Configuration: linkedca.Configuration{ + Address: "127.0.0.1:9000", + DnsNames: []string{"127.0.0.1"}, + Ssh: &linkedca.SSH{}, + Files: make(map[string][]byte), + }, casOptions: o, caCreator: caCreator, caService: caService, options: &options{ provisioner: "step-cli", - address: "127.0.0.1:9000", - dnsNames: []string{"127.0.0.1"}, }, } for _, fn := range opts { - fn(p.options) + fn(p) + } + + // Use /home/step as the step path in helm configurations. + // Use the current step path when creating pki in files. + var public, private, config string + if p.options.isHelm { + public = "/home/step/certs" + private = "/home/step/secrets" + config = "/home/step/config" + } else { + public = GetPublicPath() + private = GetSecretsPath() + config = GetConfigPath() + // Create directories + dirs := []string{public, private, config, GetTemplatesPath()} + for _, name := range dirs { + if _, err := os.Stat(name); os.IsNotExist(err) { + if err = os.MkdirAll(name, 0700); err != nil { + return nil, errs.FileError(err, name) + } + } + } + } + + if p.Defaults.CaUrl == "" { + p.Defaults.CaUrl = p.DnsNames[0] + _, port, err := net.SplitHostPort(p.Address) + if err != nil { + return nil, errors.Wrapf(err, "error parsing %s", p.Address) + } + if port == "443" { + p.Defaults.CaUrl = fmt.Sprintf("https://%s", p.Defaults.CaUrl) + } else { + p.Defaults.CaUrl = fmt.Sprintf("https://%s:%s", p.Defaults.CaUrl, port) + } } - if p.root, err = getPath(public, "root_ca.crt"); err != nil { + root, err := getPath(public, "root_ca.crt") + if err != nil { return nil, err } - if p.rootKey, err = getPath(private, "root_ca_key"); err != nil { + rootKey, err := getPath(private, "root_ca_key") + if err != nil { return nil, err } - if p.intermediate, err = getPath(public, "intermediate_ca.crt"); err != nil { + p.Root = []string{root} + p.RootKey = []string{rootKey} + p.Defaults.Root = root + + if p.Intermediate, err = getPath(public, "intermediate_ca.crt"); err != nil { return nil, err } - if p.intermediateKey, err = getPath(private, "intermediate_ca_key"); err != nil { + if p.IntermediateKey, err = getPath(private, "intermediate_ca_key"); err != nil { return nil, err } - if p.sshHostPubKey, err = getPath(public, "ssh_host_ca_key.pub"); err != nil { + if p.Ssh.HostPublicKey, err = getPath(public, "ssh_host_ca_key.pub"); err != nil { return nil, err } - if p.sshUserPubKey, err = getPath(public, "ssh_user_ca_key.pub"); err != nil { + if p.Ssh.UserPublicKey, err = getPath(public, "ssh_user_ca_key.pub"); err != nil { return nil, err } - if p.sshHostKey, err = getPath(private, "ssh_host_ca_key"); err != nil { + if p.Ssh.HostKey, err = getPath(private, "ssh_host_ca_key"); err != nil { return nil, err } - if p.sshUserKey, err = getPath(private, "ssh_user_ca_key"); err != nil { + if p.Ssh.UserKey, err = getPath(private, "ssh_user_ca_key"); err != nil { return nil, err } - if len(config) > 0 { - if p.config, err = getPath(config, "ca.json"); err != nil { - return nil, err - } - if p.defaults, err = getPath(config, "defaults.json"); err != nil { - return nil, err - } + if p.defaults, err = getPath(config, "defaults.json"); err != nil { + return nil, err + } + if p.config, err = getPath(config, "ca.json"); err != nil { + return nil, err } + p.Defaults.CaConfig = p.config return p, nil } @@ -341,7 +383,7 @@ func (p *PKI) GetCAConfigPath() string { // GetRootFingerprint returns the root fingerprint. func (p *PKI) GetRootFingerprint() string { - return p.rootFingerprint + return p.Defaults.Fingerprint } // SetProvisioner sets the provisioner name of the OTT keys. @@ -355,21 +397,21 @@ func (p *PKI) SetProvisioner(s string) { // // Deprecated: this method is deprecated in favor of WithAddress. func (p *PKI) SetAddress(s string) { - p.options.address = s + p.Address = s } // SetDNSNames sets the dns names of the CA. // // Deprecated: this method is deprecated in favor of WithDNSNames. func (p *PKI) SetDNSNames(s []string) { - p.options.dnsNames = s + p.DnsNames = s } // SetCAURL sets the ca-url to use in the defaults.json. // // Deprecated: this method is deprecated in favor of WithCaUrl. func (p *PKI) SetCAURL(s string) { - p.options.caURL = s + p.Defaults.CaUrl = s } // GenerateKeyPairs generates the key pairs used by the certificate authority. @@ -408,11 +450,19 @@ func (p *PKI) GenerateRootCertificate(name, org, resource string, pass []byte) ( return nil, err } - // PrivateKey will only be set if we have access to it (SoftCAS). - if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { + sum := sha256.Sum256(resp.Certificate.Raw) + p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:])) + p.Files[p.Root[0]] = encodeCertificate(resp.Certificate) + p.Files[p.RootKey[0]], err = encodePrivateKey(resp.PrivateKey, pass) + if err != nil { return nil, err } + // PrivateKey will only be set if we have access to it (SoftCAS). + // if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { + // return nil, err + // } + return resp, nil } @@ -442,12 +492,24 @@ func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent } p.casOptions.CertificateAuthority = resp.Name - return p.WriteIntermediateCertificate(resp.Certificate, resp.PrivateKey, pass) + p.Files[p.Intermediate] = encodeCertificate(resp.Certificate) + p.Files[p.IntermediateKey], err = encodePrivateKey(resp.PrivateKey, pass) + if err != nil { + return err + } + + return nil + // return p.WriteIntermediateCertificate(resp.Certificate, resp.PrivateKey, pass) } // WriteRootCertificate writes to disk the given certificate and key. func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error { - if err := fileutil.WriteFile(p.root, pem.EncodeToMemory(&pem.Block{ + fmt.Println(p.options.isHelm) + if p.options.isHelm { + return nil + } + + if err := fileutil.WriteFile(p.Root[0], pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: rootCrt.Raw, }), 0600); err != nil { @@ -455,28 +517,32 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{ } if rootKey != nil { - _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600)) + _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.RootKey[0], 0600)) if err != nil { return err } } sum := sha256.Sum256(rootCrt.Raw) - p.rootFingerprint = strings.ToLower(hex.EncodeToString(sum[:])) + p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:])) return nil } // WriteIntermediateCertificate writes to disk the given certificate and key. func (p *PKI) WriteIntermediateCertificate(crt *x509.Certificate, key interface{}, pass []byte) error { - if err := fileutil.WriteFile(p.intermediate, pem.EncodeToMemory(&pem.Block{ + if p.options.isHelm { + return nil + } + + if err := fileutil.WriteFile(p.Intermediate, pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: crt.Raw, }), 0600); err != nil { return err } if key != nil { - _, err := pemutil.Serialize(key, pemutil.WithPassword(pass), pemutil.ToFile(p.intermediateKey, 0600)) + _, err := pemutil.Serialize(key, pemutil.WithPassword(pass), pemutil.ToFile(p.IntermediateKey, 0600)) if err != nil { return err } @@ -516,8 +582,8 @@ func (p *PKI) GetCertificateAuthority() error { } // Issuer is in the RA - p.intermediate = "" - p.intermediateKey = "" + p.Intermediate = "" + p.IntermediateKey = "" return nil } @@ -525,8 +591,8 @@ func (p *PKI) GetCertificateAuthority() error { // GenerateSSHSigningKeys generates and encrypts a private key used for signing // SSH user certificates and a private key used for signing host certificates. func (p *PKI) GenerateSSHSigningKeys(password []byte) error { - var pubNames = []string{p.sshHostPubKey, p.sshUserPubKey} - var privNames = []string{p.sshHostKey, p.sshUserKey} + var pubNames = []string{p.Ssh.HostPublicKey, p.Ssh.UserPublicKey} + var privNames = []string{p.Ssh.HostKey, p.Ssh.UserKey} for i := 0; i < 2; i++ { pub, priv, err := keyutil.GenerateDefaultKeyPair() if err != nil { @@ -539,13 +605,19 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return errors.Wrapf(err, "error converting public key") } - _, err = pemutil.Serialize(priv, pemutil.WithFilename(privNames[i]), pemutil.WithPassword(password)) + p.Files[pubNames[i]] = ssh.MarshalAuthorizedKey(sshKey) + p.Files[privNames[i]], err = encodePrivateKey(priv, password) if err != nil { return err } - if err = fileutil.WriteFile(pubNames[i], ssh.MarshalAuthorizedKey(sshKey), 0600); err != nil { - return err - } + + // _, err = pemutil.Serialize(priv, pemutil.WithFilename(privNames[i]), pemutil.WithPassword(password)) + // if err != nil { + // return err + // } + // if err = fileutil.WriteFile(pubNames[i], ssh.MarshalAuthorizedKey(sshKey), 0600); err != nil { + // return err + // } } p.options.enableSSH = true return nil @@ -575,22 +647,22 @@ func (p *PKI) TellPKI() { func (p *PKI) tellPKI() { ui.Println() if p.casOptions.Is(apiv1.SoftCAS) { - ui.PrintSelected("Root certificate", p.root) - ui.PrintSelected("Root private key", p.rootKey) - ui.PrintSelected("Root fingerprint", p.rootFingerprint) - ui.PrintSelected("Intermediate certificate", p.intermediate) - ui.PrintSelected("Intermediate private key", p.intermediateKey) - } else if p.rootFingerprint != "" { - ui.PrintSelected("Root certificate", p.root) - ui.PrintSelected("Root fingerprint", p.rootFingerprint) + ui.PrintSelected("Root certificate", p.Root[0]) + ui.PrintSelected("Root private key", p.RootKey[0]) + ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint) + ui.PrintSelected("Intermediate certificate", p.Intermediate) + ui.PrintSelected("Intermediate private key", p.IntermediateKey) + } else if p.Defaults.Fingerprint != "" { + ui.PrintSelected("Root certificate", p.Root[0]) + ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint) } else { ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad) } if p.options.enableSSH { - ui.PrintSelected("SSH user root certificate", p.sshUserPubKey) - ui.PrintSelected("SSH user root private key", p.sshUserKey) - ui.PrintSelected("SSH host root certificate", p.sshHostPubKey) - ui.PrintSelected("SSH host root private key", p.sshHostKey) + ui.PrintSelected("SSH user public key", p.Ssh.UserPublicKey) + ui.PrintSelected("SSH user private key", p.Ssh.UserKey) + ui.PrintSelected("SSH host public key", p.Ssh.HostPublicKey) + ui.PrintSelected("SSH host private key", p.Ssh.HostKey) } } @@ -637,12 +709,12 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { } config := &authconfig.Config{ - Root: []string{p.root}, - FederatedRoots: []string{}, - IntermediateCert: p.intermediate, - IntermediateKey: p.intermediateKey, - Address: p.options.address, - DNSNames: p.options.dnsNames, + Root: p.Root, + FederatedRoots: p.FederatedRoots, + IntermediateCert: p.Intermediate, + IntermediateKey: p.IntermediateKey, + Address: p.Address, + DNSNames: p.DnsNames, Logger: []byte(`{"format": "text"}`), DB: &db.Config{ Type: "badger", @@ -685,8 +757,8 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { if p.options.enableSSH { enableSSHCA := true config.SSH = &authconfig.SSHConfig{ - HostKey: p.sshHostKey, - UserKey: p.sshUserKey, + HostKey: p.Ssh.HostKey, + UserKey: p.Ssh.UserKey, } // Enable SSH authorization for default JWK provisioner prov.Claims = &provisioner.Claims{ @@ -776,26 +848,12 @@ func (p *PKI) Save(opt ...Option) error { return errs.FileError(err, p.config) } - // Generate the CA URL. - if p.options.caURL == "" { - p.options.caURL = p.options.dnsNames[0] - _, port, err := net.SplitHostPort(p.options.address) - if err != nil { - return errors.Wrapf(err, "error parsing %s", p.options.address) - } - if port == "443" { - p.options.caURL = fmt.Sprintf("https://%s", p.options.caURL) - } else { - p.options.caURL = fmt.Sprintf("https://%s:%s", p.options.caURL, port) - } - } - // Generate and write defaults.json defaults := &caDefaults{ - Root: p.root, - CAConfig: p.config, - CAUrl: p.options.caURL, - Fingerprint: p.rootFingerprint, + Root: p.Defaults.Root, + CAConfig: p.Defaults.CaConfig, + CAUrl: p.Defaults.CaUrl, + Fingerprint: p.Defaults.Fingerprint, } b, err = json.MarshalIndent(defaults, "", "\t") if err != nil { @@ -830,3 +888,26 @@ func (p *PKI) Save(opt ...Option) error { return nil } + +func encodeCertificate(c *x509.Certificate) []byte { + return pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: c.Raw, + }) +} + +func encodePublicKey(key crypto.PublicKey) ([]byte, error) { + block, err := pemutil.Serialize(key) + if err != nil { + return nil, err + } + return pem.EncodeToMemory(block), nil +} + +func encodePrivateKey(key crypto.PrivateKey, pass []byte) ([]byte, error) { + block, err := pemutil.Serialize(key, pemutil.WithPassword(pass)) + if err != nil { + return nil, err + } + return pem.EncodeToMemory(block), nil +} From ad4dbd6764ef807f952b7490a988a3c3f4376e19 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 12:58:54 -0700 Subject: [PATCH 147/291] Write all files on save. --- pki/pki.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pki/pki.go b/pki/pki.go index 72350cd6..87e4842e 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -832,6 +832,13 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { // Save stores the pki on a json file that will be used as the certificate // authority configuration. func (p *PKI) Save(opt ...Option) error { + // Write pre-generated files. + for fn, b := range p.Files { + if err := fileutil.WriteFile(fn, b, 0600); err != nil { + return err + } + } + p.tellPKI() // Generate and write ca.json From 79cf059447ff6b62b23a5bde019dd54a86235578 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 15:57:13 -0700 Subject: [PATCH 148/291] Remove deprecated methods and write all pki files at once. --- pki/pki.go | 260 +++++++++++++++++++---------------------------------- 1 file changed, 90 insertions(+), 170 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 87e4842e..db168cf8 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -156,10 +156,8 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) { } type options struct { - // address string - // caURL string - // dnsNames []string provisioner string + pkiOnly bool enableACME bool enableSSH bool enableAdmin bool @@ -199,6 +197,13 @@ func WithProvisioner(s string) PKIOption { } } +// WithPKIOnly will only generate the PKI without the step-ca config files. +func WithPKIOnly() PKIOption { + return func(p *PKI) { + p.options.pkiOnly = true + } +} + // WithACME enables acme provisioner in step-ca. func WithACME() PKIOption { return func(p *PKI) { @@ -244,17 +249,12 @@ func WithDeploymentType(dt DeploymentType) PKIOption { // PKI represents the Public Key Infrastructure used by a certificate authority. type PKI struct { linkedca.Configuration - Defaults linkedca.Defaults - casOptions apiv1.Options - caService apiv1.CertificateAuthorityService - caCreator apiv1.CertificateAuthorityCreator - // root, rootKey, rootFingerprint string - // intermediate, intermediateKey string - // sshHostPubKey, sshHostKey string - // sshUserPubKey, sshUserKey string - config string - defaults string - // rootFingerprint string + Defaults linkedca.Defaults + casOptions apiv1.Options + caService apiv1.CertificateAuthorityService + caCreator apiv1.CertificateAuthorityCreator + config string + defaults string ottPublicKey *jose.JSONWebKey ottPrivateKey *jose.JSONWebEncryption options *options @@ -386,34 +386,6 @@ func (p *PKI) GetRootFingerprint() string { return p.Defaults.Fingerprint } -// SetProvisioner sets the provisioner name of the OTT keys. -// -// Deprecated: this method is deprecated in favor of WithProvisioner. -func (p *PKI) SetProvisioner(s string) { - p.options.provisioner = s -} - -// SetAddress sets the listening address of the CA. -// -// Deprecated: this method is deprecated in favor of WithAddress. -func (p *PKI) SetAddress(s string) { - p.Address = s -} - -// SetDNSNames sets the dns names of the CA. -// -// Deprecated: this method is deprecated in favor of WithDNSNames. -func (p *PKI) SetDNSNames(s []string) { - p.DnsNames = s -} - -// SetCAURL sets the ca-url to use in the defaults.json. -// -// Deprecated: this method is deprecated in favor of WithCaUrl. -func (p *PKI) SetCAURL(s string) { - p.Defaults.CaUrl = s -} - // GenerateKeyPairs generates the key pairs used by the certificate authority. func (p *PKI) GenerateKeyPairs(pass []byte) error { var err error @@ -450,22 +422,29 @@ func (p *PKI) GenerateRootCertificate(name, org, resource string, pass []byte) ( return nil, err } - sum := sha256.Sum256(resp.Certificate.Raw) - p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:])) - p.Files[p.Root[0]] = encodeCertificate(resp.Certificate) - p.Files[p.RootKey[0]], err = encodePrivateKey(resp.PrivateKey, pass) - if err != nil { + // PrivateKey will only be set if we have access to it (SoftCAS). + if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { return nil, err } - // PrivateKey will only be set if we have access to it (SoftCAS). - // if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { - // return nil, err - // } - return resp, nil } +// WriteRootCertificate writes to the buffer the given certificate and key if given. +func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error { + p.Files[p.Root[0]] = encodeCertificate(rootCrt) + if rootKey != nil { + var err error + p.Files[p.RootKey[0]], err = encodePrivateKey(rootKey, pass) + if err != nil { + return err + } + } + sum := sha256.Sum256(rootCrt.Raw) + p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:])) + return nil +} + // GenerateIntermediateCertificate generates an intermediate certificate with // the given name and using the default key type. func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent *apiv1.CreateCertificateAuthorityResponse, pass []byte) error { @@ -494,60 +473,7 @@ func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent p.casOptions.CertificateAuthority = resp.Name p.Files[p.Intermediate] = encodeCertificate(resp.Certificate) p.Files[p.IntermediateKey], err = encodePrivateKey(resp.PrivateKey, pass) - if err != nil { - return err - } - - return nil - // return p.WriteIntermediateCertificate(resp.Certificate, resp.PrivateKey, pass) -} - -// WriteRootCertificate writes to disk the given certificate and key. -func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error { - fmt.Println(p.options.isHelm) - if p.options.isHelm { - return nil - } - - if err := fileutil.WriteFile(p.Root[0], pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: rootCrt.Raw, - }), 0600); err != nil { - return err - } - - if rootKey != nil { - _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.RootKey[0], 0600)) - if err != nil { - return err - } - } - - sum := sha256.Sum256(rootCrt.Raw) - p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:])) - - return nil -} - -// WriteIntermediateCertificate writes to disk the given certificate and key. -func (p *PKI) WriteIntermediateCertificate(crt *x509.Certificate, key interface{}, pass []byte) error { - if p.options.isHelm { - return nil - } - - if err := fileutil.WriteFile(p.Intermediate, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: crt.Raw, - }), 0600); err != nil { - return err - } - if key != nil { - _, err := pemutil.Serialize(key, pemutil.WithPassword(pass), pemutil.ToFile(p.IntermediateKey, 0600)) - if err != nil { - return err - } - } - return nil + return err } // CreateCertificateAuthorityResponse returns a @@ -610,19 +536,21 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return err } - - // _, err = pemutil.Serialize(priv, pemutil.WithFilename(privNames[i]), pemutil.WithPassword(password)) - // if err != nil { - // return err - // } - // if err = fileutil.WriteFile(pubNames[i], ssh.MarshalAuthorizedKey(sshKey), 0600); err != nil { - // return err - // } } p.options.enableSSH = true return nil } +// WriteFiles writes on disk the previously generated files. +func (p *PKI) WriteFiles() error { + for fn, b := range p.Files { + if err := fileutil.WriteFile(fn, b, 0600); err != nil { + return err + } + } + return nil +} + func (p *PKI) askFeedback() { ui.Println() ui.Printf("\033[1mFEEDBACK\033[0m %s %s\n", @@ -636,14 +564,6 @@ func (p *PKI) askFeedback() { ui.Println(" \033[1mhttps://bit.ly/step-discord\033[0m.") } -// TellPKI outputs the locations of public and private keys generated -// generated for a new PKI. Generally this will consist of a root certificate -// and key and an intermediate certificate and key. -func (p *PKI) TellPKI() { - p.tellPKI() - p.askFeedback() -} - func (p *PKI) tellPKI() { ui.Println() if p.casOptions.Is(apiv1.SoftCAS) { @@ -832,67 +752,67 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { // Save stores the pki on a json file that will be used as the certificate // authority configuration. func (p *PKI) Save(opt ...Option) error { - // Write pre-generated files. - for fn, b := range p.Files { - if err := fileutil.WriteFile(fn, b, 0600); err != nil { - return err - } + // Write generated files + if err := p.WriteFiles(); err != nil { + return err } + // Display only the p.tellPKI() // Generate and write ca.json - config, err := p.GenerateConfig(opt...) - if err != nil { - return err - } + if !p.options.pkiOnly { + config, err := p.GenerateConfig(opt...) + if err != nil { + return err + } - b, err := json.MarshalIndent(config, "", "\t") - if err != nil { - return errors.Wrapf(err, "error marshaling %s", p.config) - } - if err = fileutil.WriteFile(p.config, b, 0644); err != nil { - return errs.FileError(err, p.config) - } + b, err := json.MarshalIndent(config, "", "\t") + if err != nil { + return errors.Wrapf(err, "error marshaling %s", p.config) + } + if err = fileutil.WriteFile(p.config, b, 0644); err != nil { + return errs.FileError(err, p.config) + } - // Generate and write defaults.json - defaults := &caDefaults{ - Root: p.Defaults.Root, - CAConfig: p.Defaults.CaConfig, - CAUrl: p.Defaults.CaUrl, - Fingerprint: p.Defaults.Fingerprint, - } - b, err = json.MarshalIndent(defaults, "", "\t") - if err != nil { - return errors.Wrapf(err, "error marshaling %s", p.defaults) - } - if err = fileutil.WriteFile(p.defaults, b, 0644); err != nil { - return errs.FileError(err, p.defaults) - } + // Generate and write defaults.json + defaults := &caDefaults{ + Root: p.Defaults.Root, + CAConfig: p.Defaults.CaConfig, + CAUrl: p.Defaults.CaUrl, + Fingerprint: p.Defaults.Fingerprint, + } + b, err = json.MarshalIndent(defaults, "", "\t") + if err != nil { + return errors.Wrapf(err, "error marshaling %s", p.defaults) + } + if err = fileutil.WriteFile(p.defaults, b, 0644); err != nil { + return errs.FileError(err, p.defaults) + } - // Generate and write templates - if err := generateTemplates(config.Templates); err != nil { - return err - } + // Generate and write templates + if err := generateTemplates(config.Templates); err != nil { + return err + } - if config.DB != nil { - ui.PrintSelected("Database folder", config.DB.DataSource) - } - if config.Templates != nil { - ui.PrintSelected("Templates folder", GetTemplatesPath()) - } + if config.DB != nil { + ui.PrintSelected("Database folder", config.DB.DataSource) + } + if config.Templates != nil { + ui.PrintSelected("Templates folder", GetTemplatesPath()) + } - ui.PrintSelected("Default configuration", p.defaults) - ui.PrintSelected("Certificate Authority configuration", p.config) - ui.Println() - if p.casOptions.Is(apiv1.SoftCAS) { - ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") - } else { - ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.") + ui.PrintSelected("Default configuration", p.defaults) + ui.PrintSelected("Certificate Authority configuration", p.config) + ui.Println() + if p.casOptions.Is(apiv1.SoftCAS) { + ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") + } else { + ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.") + } } p.askFeedback() - return nil } From f643af70951044a11b6e312459dced2b6bbc2bce Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 15:57:48 -0700 Subject: [PATCH 149/291] Update onboarding flow with new pki package. --- commands/onboard.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/commands/onboard.go b/commands/onboard.go index 251a4b47..4b804560 100644 --- a/commands/onboard.go +++ b/commands/onboard.go @@ -163,17 +163,21 @@ func onboardAction(ctx *cli.Context) error { } func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) { + var opts = []pki.PKIOption{ + pki.WithAddress(config.Address), + pki.WithDNSNames([]string{config.DNS}), + pki.WithProvisioner("admin"), + } + p, err := pki.New(apiv1.Options{ Type: apiv1.SoftCAS, IsCreator: true, - }) + }, opts...) if err != nil { return nil, "", err } - p.SetAddress(config.Address) - p.SetDNSNames([]string{config.DNS}) - + // Generate pki ui.Println("Generating root certificate...") root, err := p.GenerateRootCertificate(config.Name, config.Name, config.Name, config.password) if err != nil { @@ -186,8 +190,12 @@ func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) return nil, "", err } + // Write files to disk + if err = p.WriteFiles(); err != nil { + return nil, "", err + } + // Generate provisioner - p.SetProvisioner("admin") ui.Println("Generating admin provisioner...") if err = p.GenerateKeyPairs(config.password); err != nil { return nil, "", err From 81004ce1f9319f934a6e4fa7e4843c4e7a1561ef Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 17:36:18 -0700 Subject: [PATCH 150/291] Remove deprecated functions. --- pki/pki.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index db168cf8..912865cf 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -596,31 +596,6 @@ type caDefaults struct { // Option is the type for modifiers over the auth config object. type Option func(c *authconfig.Config) error -// WithDefaultDB is a configuration modifier that adds a default DB stanza to -// the authority config. -// -// Deprecated: this method is deprecated because this is the default behavior. -func WithDefaultDB() Option { - return func(c *authconfig.Config) error { - c.DB = &db.Config{ - Type: "badger", - DataSource: GetDBPath(), - } - return nil - } -} - -// WithoutDB is a configuration modifier that adds a default DB stanza to -// the authority config. -// -// De[recated: this method is deprecated in favor or WithNoDB. -func WithoutDB() Option { - return func(c *authconfig.Config) error { - c.DB = nil - return nil - } -} - // GenerateConfig returns the step certificates configuration. func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { var authorityOptions *apiv1.Options From 3f07eb597a731872465820a2c6a4381d08d89b68 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 18:45:50 -0700 Subject: [PATCH 151/291] Implement revocation using linkedca. --- authority/linkedca.go | 46 +++++++++++++++++++++++++++++++++++++++++++ authority/tls.go | 25 +++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/authority/linkedca.go b/authority/linkedca.go index e0678aca..9c816e1e 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -15,6 +15,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/smallstep/certificates/db" "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/tlsutil" @@ -257,6 +258,34 @@ func (c *linkedCaClient) StoreSSHCertificate(crt *ssh.Certificate) error { return errors.Wrap(err, "error posting ssh certificate") } +func (c *linkedCaClient) Revoke(crt *x509.Certificate, rci *db.RevokedCertificateInfo) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + _, err := c.client.RevokeCertificate(ctx, &linkedca.RevokeCertificateRequest{ + Serial: rci.Serial, + PemCertificate: serializeCertificate(crt), + Reason: rci.Reason, + ReasonCode: linkedca.RevocationReasonCode(rci.ReasonCode), + Passive: true, + }) + + return errors.Wrap(err, "error revoking certificate") +} + +func (c *linkedCaClient) RevokeSSH(ssh *ssh.Certificate, rci *db.RevokedCertificateInfo) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + _, err := c.client.RevokeSSHCertificate(ctx, &linkedca.RevokeSSHCertificateRequest{ + Serial: rci.Serial, + Certificate: serializeSSHCertificate(ssh), + Reason: rci.Reason, + ReasonCode: linkedca.RevocationReasonCode(rci.ReasonCode), + Passive: true, + }) + + return errors.Wrap(err, "error revoking ssh certificate") +} + func (c *linkedCaClient) IsRevoked(serial string) (bool, error) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() @@ -281,6 +310,16 @@ func (c *linkedCaClient) IsSSHRevoked(serial string) (bool, error) { return resp.Status != linkedca.RevocationStatus_ACTIVE, nil } +func serializeCertificate(crt *x509.Certificate) string { + if crt == nil { + return "" + } + return string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: crt.Raw, + })) +} + func serializeCertificateChain(fullchain ...*x509.Certificate) string { var chain string for _, crt := range fullchain { @@ -292,6 +331,13 @@ func serializeCertificateChain(fullchain ...*x509.Certificate) string { return chain } +func serializeSSHCertificate(crt *ssh.Certificate) string { + if crt == nil { + return "" + } + return string(ssh.MarshalAuthorizedKey(crt)) +} + func getAuthority(sans []string) (string, error) { for _, s := range sans { if strings.HasPrefix(s, "urn:smallstep:authority:") { diff --git a/authority/tls.go b/authority/tls.go index 32d6f3c6..90c70fc3 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -21,6 +21,7 @@ import ( "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" "go.step.sm/crypto/x509util" + "golang.org/x/crypto/ssh" ) // GetTLSOptions returns the tls options configured. @@ -397,7 +398,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } if provisioner.MethodFromContext(ctx) == provisioner.SSHRevokeMethod { - err = a.db.RevokeSSH(rci) + err = a.revokeSSH(nil, rci) } else { // Revoke an X.509 certificate using CAS. If the certificate is not // provided we will try to read it from the db. If the read fails we @@ -424,7 +425,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } // Save as revoked in the Db. - err = a.db.Revoke(rci) + err = a.revoke(revokedCert, rci) } switch err { case nil: @@ -439,6 +440,26 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } } +func (a *Authority) revoke(crt *x509.Certificate, rci *db.RevokedCertificateInfo) error { + if lca, ok := a.adminDB.(interface { + Revoke(*x509.Certificate, *db.RevokedCertificateInfo) error + }); ok { + println(true) + return lca.Revoke(crt, rci) + } + println(false) + return a.db.Revoke(rci) +} + +func (a *Authority) revokeSSH(crt *ssh.Certificate, rci *db.RevokedCertificateInfo) error { + if lca, ok := a.adminDB.(interface { + RevokeSSH(*ssh.Certificate, *db.RevokedCertificateInfo) error + }); ok { + return lca.RevokeSSH(crt, rci) + } + return a.db.Revoke(rci) +} + // GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server. func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { fatal := func(err error) (*tls.Certificate, error) { From d72fa953acb1da02f4462c0f983ea4472801f3fa Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 5 Aug 2021 18:50:18 -0700 Subject: [PATCH 152/291] Remove debug statements. --- authority/tls.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/authority/tls.go b/authority/tls.go index 90c70fc3..bba0243a 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -444,10 +444,8 @@ func (a *Authority) revoke(crt *x509.Certificate, rci *db.RevokedCertificateInfo if lca, ok := a.adminDB.(interface { Revoke(*x509.Certificate, *db.RevokedCertificateInfo) error }); ok { - println(true) return lca.Revoke(crt, rci) } - println(false) return a.db.Revoke(rci) } From 16d3afb92aa1c1785c9f170161bfefd354b94b40 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 6 Aug 2021 12:37:20 -0700 Subject: [PATCH 153/291] Remove unused method. --- authority/export.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/authority/export.go b/authority/export.go index 97efac03..c7a4724b 100644 --- a/authority/export.go +++ b/authority/export.go @@ -220,13 +220,6 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { return c, nil } -func mustPassword(s string) []byte { - if s == "" { - return nil - } - return []byte(s) -} - func mustDuration(d *provisioner.Duration) string { if d == nil || d.Duration == 0 { return "" From 9d51c2ccebf74e63d186fce8e40cd8191f50f6b0 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 6 Aug 2021 14:29:54 -0700 Subject: [PATCH 154/291] Fix linter errors in the name of export methods. --- authority/export.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/authority/export.go b/authority/export.go index c7a4724b..290c6e7b 100644 --- a/authority/export.go +++ b/authority/export.go @@ -32,10 +32,10 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { // The exported configuration should not include the password in it. c = &linkedca.Configuration{ Version: "1.0", - Root: mustReadFilesOrUris(a.config.Root, files), - FederatedRoots: mustReadFilesOrUris(a.config.FederatedRoots, files), - Intermediate: mustReadFileOrUri(a.config.IntermediateCert, files), - IntermediateKey: mustReadFileOrUri(a.config.IntermediateKey, files), + Root: mustReadFilesOrURIs(a.config.Root, files), + FederatedRoots: mustReadFilesOrURIs(a.config.FederatedRoots, files), + Intermediate: mustReadFileOrURI(a.config.IntermediateCert, files), + IntermediateKey: mustReadFileOrURI(a.config.IntermediateKey, files), Address: a.config.Address, InsecureAddress: a.config.InsecureAddress, DnsNames: a.config.DNSNames, @@ -54,8 +54,8 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { // SSH if v := a.config.SSH; v != nil { c.Ssh = &linkedca.SSH{ - HostKey: mustReadFileOrUri(v.HostKey, files), - UserKey: mustReadFileOrUri(v.UserKey, files), + HostKey: mustReadFileOrURI(v.HostKey, files), + UserKey: mustReadFileOrURI(v.UserKey, files), AddUserPrincipal: v.AddUserPrincipal, AddUserCommand: v.AddUserCommand, } @@ -120,8 +120,8 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { c.Authority.CertificateIssuer = &linkedca.CertificateIssuer{ Type: linkedca.CertificateIssuer_Type(typ), Provisioner: iss.Provisioner, - Certificate: mustReadFileOrUri(iss.Certificate, files), - Key: mustReadFileOrUri(iss.Key, files), + Certificate: mustReadFileOrURI(iss.Certificate, files), + Key: mustReadFileOrURI(iss.Key, files), } } } @@ -193,7 +193,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { c.Templates.Ssh.Hosts = append(c.Templates.Ssh.Hosts, &linkedca.ConfigTemplate{ Type: linkedca.ConfigTemplate_Type(typ), Name: t.Name, - Template: mustReadFileOrUri(t.TemplatePath, files), + Template: mustReadFileOrURI(t.TemplatePath, files), Path: t.Path, Comment: t.Comment, Requires: t.RequiredData, @@ -208,7 +208,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { c.Templates.Ssh.Users = append(c.Templates.Ssh.Users, &linkedca.ConfigTemplate{ Type: linkedca.ConfigTemplate_Type(typ), Name: t.Name, - Template: mustReadFileOrUri(t.TemplatePath, files), + Template: mustReadFileOrURI(t.TemplatePath, files), Path: t.Path, Comment: t.Comment, Requires: t.RequiredData, @@ -239,7 +239,7 @@ func mustMarshalToStruct(v interface{}) *structpb.Struct { return r } -func mustReadFileOrUri(fn string, m map[string][]byte) string { +func mustReadFileOrURI(fn string, m map[string][]byte) string { if fn == "" { return "" } @@ -266,10 +266,10 @@ func mustReadFileOrUri(fn string, m map[string][]byte) string { return fn } -func mustReadFilesOrUris(fns []string, m map[string][]byte) []string { +func mustReadFilesOrURIs(fns []string, m map[string][]byte) []string { var result []string for _, fn := range fns { - result = append(result, mustReadFileOrUri(fn, m)) + result = append(result, mustReadFileOrURI(fn, m)) } return result } From 640f523150e8f5931f59d23ad4a3664142ec5141 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 6 Aug 2021 14:31:49 -0700 Subject: [PATCH 155/291] Remove unused function. --- pki/pki.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 912865cf..5474a707 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -798,14 +798,6 @@ func encodeCertificate(c *x509.Certificate) []byte { }) } -func encodePublicKey(key crypto.PublicKey) ([]byte, error) { - block, err := pemutil.Serialize(key) - if err != nil { - return nil, err - } - return pem.EncodeToMemory(block), nil -} - func encodePrivateKey(key crypto.PrivateKey, pass []byte) ([]byte, error) { block, err := pemutil.Serialize(key, pemutil.WithPassword(pass)) if err != nil { From 536536c92da8e9a8c40e96b4df3aa0d73a10e41f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 6 Aug 2021 14:55:17 -0700 Subject: [PATCH 156/291] Wrap json errors. --- authority/provisioners.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authority/provisioners.go b/authority/provisioners.go index ab069501..beb4f9ed 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -562,7 +562,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, details := p.Details.GetData() if details == nil { - return nil, fmt.Errorf("provisioner does not have any details") + return nil, errors.New("provisioner does not have any details") } options := optionsToCertificates(p) @@ -571,7 +571,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, case *linkedca.ProvisionerDetails_JWK: jwk := new(jose.JSONWebKey) if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { - return nil, err + return nil, errors.Wrap(err, "error unmarshaling public key") } return &provisioner.JWK{ ID: p.Id, From 47a30f15248d0c864fef2c7112716a031a1305db Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 6 Aug 2021 14:58:03 -0700 Subject: [PATCH 157/291] Add JWK provisioner to generic config. Fix linter errors. --- commands/onboard.go | 2 +- pki/helm.go | 30 +++++++++++--------- pki/pki.go | 69 ++++++++++++++++++++++++++++++--------------- 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/commands/onboard.go b/commands/onboard.go index 4b804560..eb8285aa 100644 --- a/commands/onboard.go +++ b/commands/onboard.go @@ -163,7 +163,7 @@ func onboardAction(ctx *cli.Context) error { } func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) { - var opts = []pki.PKIOption{ + var opts = []pki.Option{ pki.WithAddress(config.Address), pki.WithDNSNames([]string{config.DNS}), pki.WithProvisioner("admin"), diff --git a/pki/helm.go b/pki/helm.go index 7e4f1b2d..2a8ac513 100644 --- a/pki/helm.go +++ b/pki/helm.go @@ -6,14 +6,15 @@ import ( "github.com/Masterminds/sprig/v3" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority" authconfig "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/linkedca" ) type helmVariables struct { - linkedca.Configuration - Defaults linkedca.Defaults + *linkedca.Configuration + Defaults *linkedca.Defaults Password string SSH struct { Enabled bool @@ -33,19 +34,22 @@ func (p *PKI) WriteHelmTemplate(w io.Writer) error { p.Ssh = nil } + // Convert provisioner to ca.json + provisioners := make([]provisioner.Interface, len(p.Authority.Provisioners)) + for i, p := range p.Authority.Provisioners { + pp, err := authority.ProvisionerToCertificates(p) + if err != nil { + return err + } + provisioners[i] = pp + } + if err := tmpl.Execute(w, helmVariables{ - Configuration: p.Configuration, - Defaults: p.Defaults, - Password: "asdf", + Configuration: &p.Configuration, + Defaults: &p.Defaults, + Password: "", TLS: authconfig.DefaultTLSOptions, - Provisioners: []provisioner.Interface{ - &provisioner.JWK{ - Name: p.options.provisioner, - Type: "JWK", - Key: p.ottPublicKey, - EncryptedKey: "", - }, - }, + Provisioners: provisioners, }); err != nil { return errors.Wrap(err, "error executing helm template") } diff --git a/pki/pki.go b/pki/pki.go index 5474a707..626e42d3 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -166,81 +166,81 @@ type options struct { deploymentType DeploymentType } -// PKIOption is the type of a configuration option on the pki constructor. -type PKIOption func(p *PKI) +// Option is the type of a configuration option on the pki constructor. +type Option func(p *PKI) // WithAddress sets the listen address of step-ca. -func WithAddress(s string) PKIOption { +func WithAddress(s string) Option { return func(p *PKI) { p.Address = s } } -// WithCaUrl sets the default ca-url of step-ca. -func WithCaUrl(s string) PKIOption { +// WithCaURL sets the default ca-url of step-ca. +func WithCaURL(s string) Option { return func(p *PKI) { p.Defaults.CaUrl = s } } // WithDNSNames sets the SANs of step-ca. -func WithDNSNames(s []string) PKIOption { +func WithDNSNames(s []string) Option { return func(p *PKI) { p.DnsNames = s } } // WithProvisioner defines the name of the default provisioner. -func WithProvisioner(s string) PKIOption { +func WithProvisioner(s string) Option { return func(p *PKI) { p.options.provisioner = s } } // WithPKIOnly will only generate the PKI without the step-ca config files. -func WithPKIOnly() PKIOption { +func WithPKIOnly() Option { return func(p *PKI) { p.options.pkiOnly = true } } // WithACME enables acme provisioner in step-ca. -func WithACME() PKIOption { +func WithACME() Option { return func(p *PKI) { p.options.enableACME = true } } // WithSSH enables ssh in step-ca. -func WithSSH() PKIOption { +func WithSSH() Option { return func(p *PKI) { p.options.enableSSH = true } } // WithAdmin enables the admin api in step-ca. -func WithAdmin() PKIOption { +func WithAdmin() Option { return func(p *PKI) { p.options.enableAdmin = true } } // WithNoDB disables the db in step-ca. -func WithNoDB() PKIOption { +func WithNoDB() Option { return func(p *PKI) { p.options.noDB = true } } // WithHelm configures the pki to create a helm values.yaml. -func WithHelm() PKIOption { +func WithHelm() Option { return func(p *PKI) { p.options.isHelm = true } } // WithDeploymentType defines the deployment type of step-ca. -func WithDeploymentType(dt DeploymentType) PKIOption { +func WithDeploymentType(dt DeploymentType) Option { return func(p *PKI) { p.options.deploymentType = dt } @@ -261,7 +261,7 @@ type PKI struct { } // New creates a new PKI configuration. -func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) { +func New(o apiv1.Options, opts ...Option) (*PKI, error) { caService, err := cas.New(context.Background(), o) if err != nil { return nil, err @@ -284,10 +284,11 @@ func New(o apiv1.Options, opts ...PKIOption) (*PKI, error) { p := &PKI{ Configuration: linkedca.Configuration{ - Address: "127.0.0.1:9000", - DnsNames: []string{"127.0.0.1"}, - Ssh: &linkedca.SSH{}, - Files: make(map[string][]byte), + Address: "127.0.0.1:9000", + DnsNames: []string{"127.0.0.1"}, + Ssh: &linkedca.SSH{}, + Authority: &linkedca.Authority{}, + Files: make(map[string][]byte), }, casOptions: o, caCreator: caCreator, @@ -395,6 +396,28 @@ func (p *PKI) GenerateKeyPairs(pass []byte) error { return err } + // Add JWK provisioner to the configuration. + publicKey, err := json.Marshal(p.ottPublicKey) + if err != nil { + return errors.Wrap(err, "error marshaling public key") + } + encryptedKey, err := p.ottPrivateKey.CompactSerialize() + if err != nil { + return errors.Wrap(err, "error serializing private key") + } + p.Authority.Provisioners = append(p.Authority.Provisioners, &linkedca.Provisioner{ + Type: linkedca.Provisioner_JWK, + Name: p.options.provisioner, + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_JWK{ + JWK: &linkedca.JWKProvisioner{ + PublicKey: publicKey, + EncryptedPrivateKey: []byte(encryptedKey), + }, + }, + }, + }) + return nil } @@ -593,11 +616,11 @@ type caDefaults struct { Root string `json:"root"` } -// Option is the type for modifiers over the auth config object. -type Option func(c *authconfig.Config) error +// ConfigOption is the type for modifiers over the auth config object. +type ConfigOption func(c *authconfig.Config) error // GenerateConfig returns the step certificates configuration. -func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { +func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { var authorityOptions *apiv1.Options if !p.casOptions.Is(apiv1.SoftCAS) { authorityOptions = &p.casOptions @@ -726,7 +749,7 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authconfig.Config, error) { // Save stores the pki on a json file that will be used as the certificate // authority configuration. -func (p *PKI) Save(opt ...Option) error { +func (p *PKI) Save(opt ...ConfigOption) error { // Write generated files if err := p.WriteFiles(); err != nil { return err From 56bb3eb6e1b5c03d5298922652c82c99bd50986b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 10 Aug 2021 14:54:31 -0700 Subject: [PATCH 158/291] Add next steps for linked ca. --- pki/pki.go | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 626e42d3..6ada040d 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -10,11 +10,9 @@ import ( "encoding/json" "encoding/pem" "fmt" - "html" "net" "os" "path/filepath" - "strconv" "strings" "time" @@ -576,15 +574,22 @@ func (p *PKI) WriteFiles() error { func (p *PKI) askFeedback() { ui.Println() - ui.Printf("\033[1mFEEDBACK\033[0m %s %s\n", - html.UnescapeString("&#"+strconv.Itoa(128525)+";"), - html.UnescapeString("&#"+strconv.Itoa(127867)+";")) - ui.Println(" The \033[1mstep\033[0m utility is not instrumented for usage statistics. It does not") - ui.Println(" phone home. But your feedback is extremely valuable. Any information you") - ui.Println(" can provide regarding how you’re using `step` helps. Please send us a") - ui.Println(" sentence or two, good or bad: \033[1mfeedback@smallstep.com\033[0m or join") - ui.Println(" \033[1mhttps://github.com/smallstep/certificates/discussions\033[0m and our Discord") - ui.Println(" \033[1mhttps://bit.ly/step-discord\033[0m.") + ui.Println("\033[1mFEEDBACK\033[0m 😍 🍻") + ui.Println(" The \033[1mstep\033[0m utility is not instrumented for usage statistics. It does not phone") + ui.Println(" home. But your feedback is extremely valuable. Any information you can provide") + ui.Println(" regarding how you’re using `step` helps. Please send us a sentence or two,") + ui.Println(" good or bad at \033[1mfeedback@smallstep.com\033[0m or join GitHub Discussions") + ui.Println(" \033[1mhttps://github.com/smallstep/certificates/discussions\033[0m and our Discord ") + ui.Println(" \033[1mhttps://bit.ly/step-discord\033[0m.") + + if p.options.deploymentType == LinkedDeployment { + ui.Println() + ui.Println("\033[1mNEXT STEPS\033[0m") + ui.Println(" 1. Log in or create a Certificate Manager account at \033[1mhttps://u.step.sm/linked\033[0m") + ui.Println(" 2. Add a new authority with \"linked\" type") + ui.Println(" 3. Follow instructions in browser to start `step-ca` using the `--token` flag") + ui.Println() + } } func (p *PKI) tellPKI() { @@ -802,11 +807,13 @@ func (p *PKI) Save(opt ...ConfigOption) error { ui.PrintSelected("Default configuration", p.defaults) ui.PrintSelected("Certificate Authority configuration", p.config) - ui.Println() - if p.casOptions.Is(apiv1.SoftCAS) { - ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") - } else { - ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.") + if p.options.deploymentType != LinkedDeployment { + ui.Println() + if p.casOptions.Is(apiv1.SoftCAS) { + ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") + } else { + ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.") + } } } From 072ba4227caaf1cf3489cc6cec85478791724681 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 10 Aug 2021 17:07:15 -0700 Subject: [PATCH 159/291] Add deployment type to config. This field is ignored except for the start of the ca. If the type is linked and the token is not passed, it will fail with an error. --- authority/config/config.go | 1 + commands/app.go | 14 ++++++++++++++ pki/pki.go | 22 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/authority/config/config.go b/authority/config/config.go index 68886d77..2cf6bfac 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -85,6 +85,7 @@ type ASN1DN struct { type AuthConfig struct { *cas.Options AuthorityID string `json:"authorityId,omitempty"` + DeploymentType string `json:"deploymentType,omitempty"` Provisioners provisioner.List `json:"provisioners,omitempty"` Admins []*linkedca.Admin `json:"-"` Template *ASN1DN `json:"template,omitempty"` diff --git a/commands/app.go b/commands/app.go index 481c4867..faa25e31 100644 --- a/commands/app.go +++ b/commands/app.go @@ -8,11 +8,13 @@ import ( "net" "net/http" "os" + "strings" "unicode" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/ca" + "github.com/smallstep/certificates/pki" "github.com/urfave/cli" "go.step.sm/cli-utils/errs" ) @@ -67,6 +69,18 @@ func appAction(ctx *cli.Context) error { fatal(err) } + if config.AuthorityConfig != nil { + if token == "" && strings.EqualFold(config.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) { + return errors.New(`'step-ca' requires the '--token' flag for linked deploy type. + +To get a linked authority token: + 1. Log in or create a Certificate Manager account at ` + "\033[1mhttps://u.step.sm/linked\033[0m" + ` + 2. Add a new authority with "linked" type + 3. Follow instructions in browser to start 'step-ca' using the '--token' flag +`) + } + } + var password []byte if passFile != "" { if password, err = ioutil.ReadFile(passFile); err != nil { diff --git a/pki/pki.go b/pki/pki.go index 6ada040d..3f566bad 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -54,6 +54,20 @@ const ( HostedDeployment ) +// String returns the string version of the deployment type. +func (d DeploymentType) String() string { + switch d { + case StandaloneDeployment: + return "standalone" + case LinkedDeployment: + return "linked" + case HostedDeployment: + return "hosted" + default: + return "unknown" + } +} + const ( // ConfigPath is the directory name under the step path where the configuration // files will be stored. @@ -580,7 +594,7 @@ func (p *PKI) askFeedback() { ui.Println(" regarding how you’re using `step` helps. Please send us a sentence or two,") ui.Println(" good or bad at \033[1mfeedback@smallstep.com\033[0m or join GitHub Discussions") ui.Println(" \033[1mhttps://github.com/smallstep/certificates/discussions\033[0m and our Discord ") - ui.Println(" \033[1mhttps://bit.ly/step-discord\033[0m.") + ui.Println(" \033[1mhttps://u.step.sm/discord\033[0m.") if p.options.deploymentType == LinkedDeployment { ui.Println() @@ -652,6 +666,12 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { Templates: p.getTemplates(), } + // Add linked as a deployment type to detect it on start and provide a + // message if the token is not given. + if p.options.deploymentType == LinkedDeployment { + config.AuthorityConfig.DeploymentType = LinkedDeployment.String() + } + // On standalone deployments add the provisioners to either the ca.json or // the database. var provisioners []provisioner.Interface From 28e882c9b30ff3e7b18670ed50da05e4e4d1e71c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 10 Aug 2021 17:14:17 -0700 Subject: [PATCH 160/291] Add deployment type to export. --- authority/export.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/authority/export.go b/authority/export.go index 290c6e7b..4eeb2813 100644 --- a/authority/export.go +++ b/authority/export.go @@ -47,6 +47,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { EnableAdmin: a.config.AuthorityConfig.EnableAdmin, DisableIssuedAtCheck: a.config.AuthorityConfig.DisableIssuedAtCheck, Backdate: mustDuration(a.config.AuthorityConfig.Backdate), + DeploymentType: a.config.AuthorityConfig.DeploymentType, }, Files: files, } diff --git a/go.mod b/go.mod index 1149ac33..b26b1f29 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d + go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect diff --git a/go.sum b/go.sum index 555f5b64..28f22b04 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d h1:bMcTynjdYq1Xmoi0G3NPCfV/aP1/vVQ/p7W3oYhoVXU= -go.step.sm/linkedca v0.4.1-0.20210805031331-a377303edb9d/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4 h1:9xNAR/hIsmw5K/B7oe27U3NoISS4KJux+c6Ij0YpwoY= +go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4/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 492ff4b6323cf3bd2765298fa5c0fdfafe28b08b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 10 Aug 2021 17:30:33 -0700 Subject: [PATCH 161/291] Ask for the first provisioner password if none is provided. --- authority/authority.go | 9 ++++----- authority/provisioners.go | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 28af693c..1b060ef8 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -11,24 +11,23 @@ import ( "sync" "time" - "github.com/smallstep/certificates/cas" - "github.com/smallstep/certificates/scep" - "go.step.sm/linkedca" - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" adminDBNosql "github.com/smallstep/certificates/authority/admin/db/nosql" "github.com/smallstep/certificates/authority/administrator" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/kms" kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/sshagentkms" + "github.com/smallstep/certificates/scep" "github.com/smallstep/certificates/templates" "github.com/smallstep/nosql" "go.step.sm/crypto/pemutil" + "go.step.sm/linkedca" "golang.org/x/crypto/ssh" ) @@ -474,7 +473,7 @@ func (a *Authority) init() error { if err != nil { return admin.WrapErrorISE(err, "error loading provisioners to initialize authority") } - if len(provs) == 0 { + if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") { // Create First Provisioner prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) if err != nil { diff --git a/authority/provisioners.go b/authority/provisioners.go index beb4f9ed..7e02126f 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -14,6 +14,7 @@ import ( "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" step "go.step.sm/cli-utils/config" + "go.step.sm/cli-utils/ui" "go.step.sm/crypto/jose" "go.step.sm/linkedca" "gopkg.in/square/go-jose.v2/jwt" @@ -238,6 +239,14 @@ func (a *Authority) RemoveProvisioner(ctx context.Context, id string) error { } func CreateFirstProvisioner(ctx context.Context, db admin.DB, password string) (*linkedca.Provisioner, error) { + if password == "" { + pass, err := ui.PromptPasswordGenerate("Please enter the password to encrypt your first provisioner, leave empty and we'll generate one") + if err != nil { + return nil, err + } + password = string(pass) + } + jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) if err != nil { return nil, admin.WrapErrorISE(err, "error generating JWK key pair") From 66f6c73655da2a4cb89325fca577576fd8fc090a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 11:19:29 -0700 Subject: [PATCH 162/291] Update badger driver to use v2 by default. --- pki/pki.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pki/pki.go b/pki/pki.go index 3f566bad..333497db 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -654,7 +654,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { DNSNames: p.DnsNames, Logger: []byte(`{"format": "text"}`), DB: &db.Config{ - Type: "badger", + Type: "badgerv2", DataSource: GetDBPath(), }, AuthorityConfig: &authconfig.AuthConfig{ From 9e5762fe063b663e9e852d3a4a15f9cae7adb4b9 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 11:50:54 -0700 Subject: [PATCH 163/291] Allow the reuse of azure token if DisableTrustOnFirstUse is true Azure caches tokens for 24h and we cannot issue a new certificate for the same instance in that period of time. The meaning of this parameter is to allow the signing of multiple certificate in one instance. This is possible in GCP, because we get a new token, and is possible in AWS because we can generate a new one. On Azure there was no other way to do it unless you wait for 24h. Fixes #656 --- authority/authorize.go | 3 +++ authority/provisioner/azure.go | 5 +++-- authority/provisioner/azure_test.go | 2 +- authority/provisioner/provisioner.go | 12 ++++++++++++ authority/tls.go | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/authority/authorize.go b/authority/authorize.go index 8d1f878a..69ad2a90 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -173,6 +173,9 @@ func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedc } // UseToken stores the token to protect against reuse. +// +// This method currently ignores any error coming from the GetTokenID, but it +// should specifically ignore the error provisioner.ErrAllowTokenReuse. func (a *Authority) UseToken(token string, prov provisioner.Interface) error { if reuseKey, err := prov.GetTokenID(token); err == nil { if reuseKey == "" { diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index 230f246f..fee50658 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -131,9 +131,10 @@ func (p *Azure) GetTokenID(token string) (string, error) { return "", errors.Wrap(err, "error verifying claims") } - // If TOFU is disabled create return the token kid + // If TOFU is disabled then allow token re-use. Azure caches the token for + // 24h and without allowing the re-use we cannot use it twice. if p.DisableTrustOnFirstUse { - return claims.ID, nil + return "", ErrAllowTokenReuse } sum := sha256.Sum256([]byte(claims.XMSMirID)) diff --git a/authority/provisioner/azure_test.go b/authority/provisioner/azure_test.go index f21a5676..8033d345 100644 --- a/authority/provisioner/azure_test.go +++ b/authority/provisioner/azure_test.go @@ -72,7 +72,7 @@ func TestAzure_GetTokenID(t *testing.T) { wantErr bool }{ {"ok", p1, args{t1}, w1, false}, - {"ok no TOFU", p2, args{t2}, "the-jti", false}, + {"ok no TOFU", p2, args{t2}, "", true}, {"fail token", p1, args{"bad-token"}, "", true}, {"fail claims", p1, args{"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ey.fooo"}, "", true}, } diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 83cc6946..75fabed5 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -4,6 +4,7 @@ import ( "context" "crypto/x509" "encoding/json" + stderrors "errors" "net/url" "regexp" "strings" @@ -32,6 +33,17 @@ type Interface interface { AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certificate, []SignOption, error) } +// ErrAllowTokenReuse is an error that is returned by provisioners that allows +// the reuse of tokens. +// +// This is for example returned by the Azure provisioner when +// DisableTrustOnFirstUse is set to true. For AWS and GCP DisableTrustOnFirst +// use means that we allow the re-use of a token coming from a specific +// instance, but in these providers we can always get a new token, but because +// Azure caches the token for up to 24h we should add a mechanism to allow the +// re-use. +var ErrAllowTokenReuse = stderrors.New("allow token reuse") + // Audiences stores all supported audiences by request type. type Audiences struct { Sign []string diff --git a/authority/tls.go b/authority/tls.go index 4c3420df..a3dd95d3 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -366,7 +366,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } rci.ProvisionerID = p.GetID() rci.TokenID, err = p.GetTokenID(revokeOpts.OTT) - if err != nil { + if err != nil && !errors.Is(err, provisioner.ErrAllowTokenReuse) { return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke; could not get ID for token") } From d4ae267addc45ad7b4d9009b218f52f0933a2c89 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 14:59:26 -0700 Subject: [PATCH 164/291] Fix ErrAllowTokenReuse comment. --- authority/provisioner/provisioner.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 75fabed5..652cb888 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -36,12 +36,12 @@ type Interface interface { // ErrAllowTokenReuse is an error that is returned by provisioners that allows // the reuse of tokens. // -// This is for example returned by the Azure provisioner when -// DisableTrustOnFirstUse is set to true. For AWS and GCP DisableTrustOnFirst -// use means that we allow the re-use of a token coming from a specific -// instance, but in these providers we can always get a new token, but because -// Azure caches the token for up to 24h we should add a mechanism to allow the -// re-use. +// This is, for example, returned by the Azure provisioner when +// DisableTrustOnFirstUse is set to true. Azure caches tokens for up to 24hr and +// has no mechanism for getting a different token - this can be an issue when +// rebooting a VM. In contrast, AWS and GCP have facilities for requesting a new +// token. Therefore, for the Azure provisioner we are enabling token reuse, with +// the understanding that we are not following security best practices var ErrAllowTokenReuse = stderrors.New("allow token reuse") // Audiences stores all supported audiences by request type. From 456ffd88060988dbb989496003fa1258eec4577d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 15:33:34 -0700 Subject: [PATCH 165/291] Use linkedca v0.5.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b26b1f29..dcc6721e 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 - go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4 + go.step.sm/linkedca v0.5.0 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210716203947-853a461950ff golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect diff --git a/go.sum b/go.sum index 28f22b04..f50932aa 100644 --- a/go.sum +++ b/go.sum @@ -528,8 +528,8 @@ go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4 h1:9xNAR/hIsmw5K/B7oe27U3NoISS4KJux+c6Ij0YpwoY= -go.step.sm/linkedca v0.4.1-0.20210811000902-b3e9cfa09de4/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.5.0 h1:oZVRSpElM7lAL1XN2YkjdHwI/oIZ+1ULOnuqYPM6xjY= +go.step.sm/linkedca v0.5.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 da2802504b9e1378da0afc8c0484b71dba0e9e4d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 15:33:45 -0700 Subject: [PATCH 166/291] Use Default min version if not specified. --- authority/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/config/config.go b/authority/config/config.go index 2cf6bfac..75c32994 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -225,7 +225,7 @@ func (c *Config) Validate() error { c.TLS.MaxVersion = DefaultTLSOptions.MaxVersion } if c.TLS.MinVersion == 0 { - c.TLS.MinVersion = c.TLS.MaxVersion + c.TLS.MinVersion = DefaultTLSOptions.MinVersion } if c.TLS.MinVersion > c.TLS.MaxVersion { return errors.New("tls minVersion cannot exceed tls maxVersion") From e3ef4a7da973cdbb24eb1a044b009e98a63ae495 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 11 Aug 2021 15:42:22 -0700 Subject: [PATCH 167/291] Update test with default tls options. --- ca/testdata/ca.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ca/testdata/ca.json b/ca/testdata/ca.json index 0a5149d9..d40325e8 100644 --- a/ca/testdata/ca.json +++ b/ca/testdata/ca.json @@ -9,12 +9,11 @@ "logger": {"format": "text"}, "tls": { "minVersion": 1.2, - "maxVersion": 1.2, + "maxVersion": 1.3, "renegotiation": false, "cipherSuites": [ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" ] }, "authority": { From b1f59586ab0335ee43cf495c078acb23edbdb7bf Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 13 Aug 2021 11:59:12 -0700 Subject: [PATCH 168/291] Update message to align with UI. --- commands/app.go | 2 +- pki/pki.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/app.go b/commands/app.go index faa25e31..aa7b43d4 100644 --- a/commands/app.go +++ b/commands/app.go @@ -75,7 +75,7 @@ func appAction(ctx *cli.Context) error { To get a linked authority token: 1. Log in or create a Certificate Manager account at ` + "\033[1mhttps://u.step.sm/linked\033[0m" + ` - 2. Add a new authority with "linked" type + 2. Add a new authority and select "Link a step-ca instance" 3. Follow instructions in browser to start 'step-ca' using the '--token' flag `) } diff --git a/pki/pki.go b/pki/pki.go index 333497db..1d81d82a 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -600,7 +600,7 @@ func (p *PKI) askFeedback() { ui.Println() ui.Println("\033[1mNEXT STEPS\033[0m") ui.Println(" 1. Log in or create a Certificate Manager account at \033[1mhttps://u.step.sm/linked\033[0m") - ui.Println(" 2. Add a new authority with \"linked\" type") + ui.Println(" 2. Add a new authority and select \"Link a step-ca instance\"") ui.Println(" 3. Follow instructions in browser to start `step-ca` using the `--token` flag") ui.Println() } From a864f0134db9acbf64d5155f7ab3a0dbea58dd5a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 16 Aug 2021 14:47:38 -0700 Subject: [PATCH 169/291] Fix key version when SHA512WithRSA is used. There was a typo creating RSA keys with SHA256 digests instead of SHA512 --- kms/cloudkms/cloudkms.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index cfbf8235..f4c656d3 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -46,8 +46,8 @@ var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]interface{}{ 4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256, }, apiv1.SHA512WithRSA: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{ - 0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256, - 4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256, + 0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512, + 4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512, }, apiv1.SHA256WithRSAPSS: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{ 0: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256, From abd78e2d2acb5c84c4026300d88f1451c077697e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 Aug 2021 13:25:55 -0700 Subject: [PATCH 170/291] Make kms uri compatible with Go 1.17. Go 1.17 introduces a change in the net/url package disallowing the use of semicolon (;) in URL queries. We used url.ParseQuery to decode the opaque string that is semicolon separated. This change replaces the semicolon with ampersands before decoding it. --- kms/uri/uri.go | 4 +++- kms/uri/uri_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/kms/uri/uri.go b/kms/uri/uri.go index 94009c47..44271e74 100644 --- a/kms/uri/uri.go +++ b/kms/uri/uri.go @@ -59,7 +59,9 @@ func Parse(rawuri string) (*URI, error) { if u.Scheme == "" { return nil, errors.Errorf("error parsing %s: scheme is missing", rawuri) } - v, err := url.ParseQuery(u.Opaque) + // Starting with Go 1.17 url.ParseQuery returns an error using semicolon as + // separator. + v, err := url.ParseQuery(strings.ReplaceAll(u.Opaque, ";", "&")) if err != nil { return nil, errors.Wrapf(err, "error parsing %s", rawuri) } diff --git a/kms/uri/uri_test.go b/kms/uri/uri_test.go index aa420db4..c2e0a9fe 100644 --- a/kms/uri/uri_test.go +++ b/kms/uri/uri_test.go @@ -274,3 +274,28 @@ func TestURI_Pin(t *testing.T) { }) } } + +func TestURI_String(t *testing.T) { + mustParse := func(s string) *URI { + u, err := Parse(s) + if err != nil { + t.Fatal(err) + } + return u + } + tests := []struct { + name string + uri *URI + want string + }{ + {"ok new", New("yubikey", url.Values{"slot-id": []string{"9a"}, "foo": []string{"bar"}}), "yubikey:foo=bar;slot-id=9a"}, + {"ok parse", mustParse("yubikey:slot-id=9a;foo=bar?bar=zar"), "yubikey:slot-id=9a;foo=bar?bar=zar"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.uri.String(); got != tt.want { + t.Errorf("URI.String() = %v, want %v", got, tt.want) + } + }) + } +} From ae58a0ee4e5291a4d55c0a3ac6400030ba6dec51 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 Aug 2021 16:31:53 -0700 Subject: [PATCH 171/291] Make tests compatible with Go 1.17. With Go 1.17 tls.Dial will fail if the client and server configured protocols do not overlap. See https://golang.org/doc/go1.17#ALPN --- acme/challenge.go | 6 ++++++ acme/challenge_test.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/acme/challenge.go b/acme/challenge.go index 1d5f0ec9..559eeb13 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -129,6 +129,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { + // With Go 1.17+ tls.Dial fails if there's no overlap between configured + // client and server protocols. See https://golang.org/doc/go1.17#ALPN + if err.Error() == "remote error: tls: no application protocol" { + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, + "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge")) + } return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, "error doing TLS dial for %s", hostPort)) } diff --git a/acme/challenge_test.go b/acme/challenge_test.go index bb9a2507..97c5e4cd 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -1395,7 +1395,7 @@ func TestTLSALPN01Validate(t *testing.T) { assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) - err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: tls: DialWithDialer timed out", ch.Value) + err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443:", ch.Value) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) From dc5205cc7276b8e803ffc8eb6f08cdb88b6d8cb6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 Aug 2021 17:06:25 -0700 Subject: [PATCH 172/291] Extract the tls error code and fail accordingly. --- acme/challenge.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 559eeb13..70c52578 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -10,11 +10,13 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "errors" "fmt" "io/ioutil" "net" "net/http" "net/url" + "reflect" "strings" "time" @@ -114,6 +116,17 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb return nil } +func tlsAlert(err error) uint8 { + var opErr *net.OpError + if errors.As(err, &opErr) { + v := reflect.ValueOf(opErr.Err) + if v.Kind() == reflect.Uint8 { + return uint8(v.Uint()) + } + } + return 0 +} + func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { config := &tls.Config{ NextProtos: []string{"acme-tls/1"}, @@ -130,8 +143,10 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { // With Go 1.17+ tls.Dial fails if there's no overlap between configured - // client and server protocols. See https://golang.org/doc/go1.17#ALPN - if err.Error() == "remote error: tls: no application protocol" { + // client and server protocols. When this happens the connection is + // closed with the error no_application_protocol(120) as required by + // RFC7301. See https://golang.org/doc/go1.17#ALPN + if tlsAlert(err) == 120 { return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge")) } From 8d523797718f61e96ecd521fbd8d840eb877ec27 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 17 Aug 2021 17:17:28 -0700 Subject: [PATCH 173/291] New Dockerfile with entrypoint script for easy CA init --- docker/Dockerfile.step-ca | 3 +++ docker/entrypoint.sh | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 docker/entrypoint.sh diff --git a/docker/Dockerfile.step-ca b/docker/Dockerfile.step-ca index 4a1908d6..9363b6ae 100644 --- a/docker/Dockerfile.step-ca +++ b/docker/Dockerfile.step-ca @@ -24,4 +24,7 @@ VOLUME ["/home/step"] STOPSIGNAL SIGTERM HEALTHCHECK CMD step ca health 2>/dev/null | grep "^ok" >/dev/null +COPY docker/entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] CMD exec /usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 00000000..f3e51705 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -eo pipefail + +# Paraphrased from: +# https://github.com/influxdata/influxdata-docker/blob/0d341f18067c4652dfa8df7dcb24d69bf707363d/influxdb/2.0/entrypoint.sh +# (a repo with no LICENSE.md) + +export STEPPATH=$(step path) + +# List of env vars required for step ca init +declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS DOCKER_STEPCA_INIT_EMAIL DOCKER_STEPCA_INIT_PASSWORD) + +# optional: +# DOCKER_STEPCA_INIT_SSH (boolean default false) + +# Ensure all env vars required to run step ca init are set. +function init_if_possible () { + local missing_vars=0 + for var in "${REQUIRED_INIT_VARS[@]}"; do + if [ -z "${!var}" ]; then + missing_vars=1 + fi + done + if [ ${missing_vars} = 1 ]; then + >&2 echo "there is no ca.json config file; please run step ca init, or provide config parameters via DOCKER_STEPCA_INIT_ vars" + else + step_ca_init "${@}" + fi +} + +# Initialize a CA if not already initialized +function step_ca_init () { + echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + local -a setup_args=( + --name "${DOCKER_STEPCA_INIT_NAME}" + --dns "${DOCKER_STEPCA_INIT_DNS}" + --provisioner "${DOCKER_STEPCA_INIT_EMAIL}" + --password-file "${STEPPATH}/password" + --address ":9000" + ) + if [ -n "${DOCKER_STEPCA_INIT_SSH}" ]; then + setup_args=("${setup_args[@]}" --ssh) + fi + step ca init "${setup_args[@]}" + mv $STEPPATH/password $PWDPATH +} + +if [ ! -f "${STEPPATH}/config/ca.json" ]; then + init_if_possible +fi + +exec "${@}" \ No newline at end of file From b88b2f9808bdc637fc41f688a5cd760fc2652e86 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 18 Aug 2021 08:46:37 -0700 Subject: [PATCH 174/291] Just adding a comment to the step-ra install script --- scripts/install-step-ra.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/install-step-ra.sh b/scripts/install-step-ra.sh index a24f6ea4..1920b17d 100644 --- a/scripts/install-step-ra.sh +++ b/scripts/install-step-ra.sh @@ -1,6 +1,18 @@ #!/bin/bash set -e +# TODO: +# - Parse params using argbash (argbash.io). Here's a template that I have tested but have not implemented yet: +# +# ARG_OPTIONAL_SINGLE([ca-url], , [the URL of the upstream (issuing) step-ca server]) +# ARG_OPTIONAL_SINGLE([fingerprint], , [the SHA256 fingerprint of the upstream peer step-ca server]) +# ARG_OPTIONAL_SINGLE([provisioner-name], , [the name of a JWK provisioner on the upstream CA that this RA will use]) +# ARG_OPTIONAL_SINGLE([provisioner-password-file], , [the name a file containing the upstream JWK provisioner password]) +# ARG_OPTIONAL_REPEATED([dns-name], , [DNS name of this RA that will appear on its TLS certificate; you may pass this flag multiple times]) +# ARG_OPTIONAL_SINGLE([listen-address], , [the address (and port #) this RA will listen on, eg. :443 or 127.0.0.1:4443]) +# ARG_HELP([This script will install and configure a Registration Authority that connects to an upstream CA running step-ca.]) +# ARGBASH_GO + echo "This script will install and start a step-ca server running in Registration Authority (RA) mode." echo "" echo "You will need an upstream CA (URL and fingerprint)" From 7ab26c830365bb717fc2e7a41f9bd21f9c86d5c4 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 18 Aug 2021 11:09:26 -0700 Subject: [PATCH 175/291] Auto-generate password by default --- docker/entrypoint.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index f3e51705..583e2e97 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -8,10 +8,11 @@ set -eo pipefail export STEPPATH=$(step path) # List of env vars required for step ca init -declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS DOCKER_STEPCA_INIT_EMAIL DOCKER_STEPCA_INIT_PASSWORD) +declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS DOCKER_STEPCA_INIT_EMAIL) # optional: -# DOCKER_STEPCA_INIT_SSH (boolean default false) +# DOCKER_STEPCA_INIT_PASSWORD (initial CA password) +# DOCKER_STEPCA_INIT_SSH (boolean: given a non-empty value, create an SSH CA) # Ensure all env vars required to run step ca init are set. function init_if_possible () { @@ -28,9 +29,19 @@ function init_if_possible () { fi } +function generate_password () { + set +o pipefail + < /dev/urandom tr -dc A-Za-z0-9 | head -c40 + set -o pipefail +} + # Initialize a CA if not already initialized function step_ca_init () { - echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then + echo -n "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + else + generate_password > "${STEPPATH}/password" + fi local -a setup_args=( --name "${DOCKER_STEPCA_INIT_NAME}" --dns "${DOCKER_STEPCA_INIT_DNS}" From bc63829111dbcb6ada9a776c71c84deaa5187754 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 18 Aug 2021 11:11:05 -0700 Subject: [PATCH 176/291] Auto-generate password by default --- docker/entrypoint.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 583e2e97..a6d29768 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -37,11 +37,6 @@ function generate_password () { # Initialize a CA if not already initialized function step_ca_init () { - if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then - echo -n "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" - else - generate_password > "${STEPPATH}/password" - fi local -a setup_args=( --name "${DOCKER_STEPCA_INIT_NAME}" --dns "${DOCKER_STEPCA_INIT_DNS}" @@ -49,6 +44,11 @@ function step_ca_init () { --password-file "${STEPPATH}/password" --address ":9000" ) + if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then + echo -n "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + else + generate_password > "${STEPPATH}/password" + fi if [ -n "${DOCKER_STEPCA_INIT_SSH}" ]; then setup_args=("${setup_args[@]}" --ssh) fi From f53f78974eda5022ae16a6a8b0775e8e1326ae0f Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 18 Aug 2021 11:38:31 -0700 Subject: [PATCH 177/291] Badger bump to fix issue with caddy build --- go.mod | 5 +- go.sum | 190 ++------------------------------------------------------- 2 files changed, 8 insertions(+), 187 deletions(-) diff --git a/go.mod b/go.mod index 58228557..f17b29bc 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Masterminds/sprig/v3 v3.1.0 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 + github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/go-kit/kit v0.10.0 // indirect github.com/go-piv/piv-go v1.7.0 @@ -22,7 +23,7 @@ require ( github.com/rs/xid v1.2.1 github.com/sirupsen/logrus v1.4.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 - github.com/smallstep/nosql v0.3.6 + github.com/smallstep/nosql v0.3.7 github.com/stretchr/testify v1.7.0 // indirect github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 @@ -38,7 +39,7 @@ require ( gopkg.in/square/go-jose.v2 v2.5.1 ) -// replace github.com/smallstep/nosql => ../nosql +//replace github.com/smallstep/nosql => ../nosql //replace go.step.sm/crypto => ../crypto diff --git a/go.sum b/go.sum index e2e71580..c0d45435 100644 --- a/go.sum +++ b/go.sum @@ -45,7 +45,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -55,77 +54,56 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= -github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= 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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= 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/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= 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 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/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= @@ -133,44 +111,35 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658 h1:/WBjuutuivOA02gpDtrvrWKw01ugkyt3QnimB7enbtI= -github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h1:2uGEvGm+JSDLd5UAaKIFSbXDcYyeH0fWJP4N2HMMYMI= +github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= +github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -178,13 +147,9 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -192,32 +157,23 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.4.0 h1:KeVK+Emj3c3S4eRztFuzbFYb2BAgf2jmwDwyXEri7Lo= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 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-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 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= @@ -227,17 +183,13 @@ github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8H github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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= @@ -267,7 +219,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= @@ -289,13 +240,10 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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= @@ -320,101 +268,66 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 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 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -422,16 +335,12 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= 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 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -446,132 +355,89 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/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 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.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 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 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 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -579,13 +445,10 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -595,15 +458,11 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd 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= -github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= -github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smallstep/nosql v0.3.7 h1:P5C1cCj89a/MbD+4k8k585fzoaZNISmRA06v3q6u5lU= +github.com/smallstep/nosql v0.3.7/go.mod h1:mC+MOhUY1uV5S5vGmAwp1FSPfDB7iImiYn5nJCjzAdA= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= 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= @@ -613,17 +472,13 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -636,18 +491,14 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc 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 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= 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= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -657,7 +508,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 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= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -669,31 +519,18 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= -go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= -go.step.sm/cli-utils v0.4.0 h1:dni6gR/6/LOqfbzm/yUdgz5O12tkxX17SxA9+pRMidI= -go.step.sm/cli-utils v0.4.0/go.mod h1:1zFgatDqEJ1Y4MNStdWa0b1NPc1fvSHbDJC+wZ6iQlE= go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= -go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= -go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= -go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7 h1:hAfzUm80XWGtFnxyVgeT/gc/3XnlVNnHD5HrLbk4Fc0= -go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 h1:ncJqviWswJT19IdnfOYQGKG1zL7IDy4lAJz1PuM3fgw= go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25/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 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -788,8 +625,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= @@ -872,9 +707,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= @@ -1024,8 +856,6 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d h1:KzwjikDymrEmYYbdyfievTwjEeGlu+OM6oiKBkF3Jfg= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1067,27 +897,19 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1107,7 +929,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 4e8e4c638ecc6ee2437a43a5fc7e3b5b3821deb4 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 18 Aug 2021 12:50:14 -0700 Subject: [PATCH 178/291] Add newline to password file for readabiliy --- docker/entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index a6d29768..eb764bd4 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -32,6 +32,7 @@ function init_if_possible () { function generate_password () { set +o pipefail < /dev/urandom tr -dc A-Za-z0-9 | head -c40 + echo set -o pipefail } @@ -45,7 +46,7 @@ function step_ca_init () { --address ":9000" ) if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then - echo -n "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" else generate_password > "${STEPPATH}/password" fi From f738cb43c34e79c6254738aeb18a3579fb35c000 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 18 Aug 2021 13:37:58 -0700 Subject: [PATCH 179/291] Make the default provisioner name optional; change DNS names variable name --- docker/entrypoint.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index eb764bd4..1f48c028 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -8,11 +8,7 @@ set -eo pipefail export STEPPATH=$(step path) # List of env vars required for step ca init -declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS DOCKER_STEPCA_INIT_EMAIL) - -# optional: -# DOCKER_STEPCA_INIT_PASSWORD (initial CA password) -# DOCKER_STEPCA_INIT_SSH (boolean: given a non-empty value, create an SSH CA) +declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS_NAMES) # Ensure all env vars required to run step ca init are set. function init_if_possible () { @@ -40,8 +36,8 @@ function generate_password () { function step_ca_init () { local -a setup_args=( --name "${DOCKER_STEPCA_INIT_NAME}" - --dns "${DOCKER_STEPCA_INIT_DNS}" - --provisioner "${DOCKER_STEPCA_INIT_EMAIL}" + --dns "${DOCKER_STEPCA_INIT_DNS_NAMES}" + --provisioner "${DOCKER_STEPCA_INIT_PROVISIONER_NAME:-admin}" --password-file "${STEPPATH}/password" --address ":9000" ) @@ -61,4 +57,4 @@ if [ ! -f "${STEPPATH}/config/ca.json" ]; then init_if_possible fi -exec "${@}" \ No newline at end of file +exec "${@}" From 31d3bf1cfc6785d0504682a2195e9e2ccc1bc70a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 18 Aug 2021 18:50:31 -0700 Subject: [PATCH 180/291] Update discord link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79e567ab..eb441c7b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ You can use it to: Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [safe, sane defaults](https://smallstep.com/docs/step-ca/certificate-authority-server-production#sane-cryptographic-defaults). -**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/stepdiscord).** +**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/step-discord).** [Website](https://smallstep.com/certificates) | [Documentation](https://smallstep.com/docs) | From a3028bbc0e60967c994e6b5000c96c778b60cc24 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 18 Aug 2021 23:44:24 -0700 Subject: [PATCH 181/291] Add test for updateAddOrderIDs --- acme/db/nosql/order.go | 4 +--- acme/db/nosql/order_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/acme/db/nosql/order.go b/acme/db/nosql/order.go index ba3934af..0c6bf795 100644 --- a/acme/db/nosql/order.go +++ b/acme/db/nosql/order.go @@ -124,10 +124,8 @@ func (db *DB) updateAddOrderIDs(ctx context.Context, accID string, addOids ...st ordersByAccountMux.Lock() defer ordersByAccountMux.Unlock() + var oldOids []string b, err := db.db.Get(ordersByAccountIDTable, []byte(accID)) - var ( - oldOids []string - ) if err != nil { if !nosql.IsErrNotFound(err) { return nil, errors.Wrapf(err, "error loading orderIDs for account %s", accID) diff --git a/acme/db/nosql/order_test.go b/acme/db/nosql/order_test.go index 7248700f..8882fd82 100644 --- a/acme/db/nosql/order_test.go +++ b/acme/db/nosql/order_test.go @@ -12,6 +12,7 @@ import ( "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/db" "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" nosqldb "github.com/smallstep/nosql/database" ) @@ -710,6 +711,34 @@ func TestDB_updateAddOrderIDs(t *testing.T) { err: errors.Errorf("error saving orderIDs index for account %s", accID), } }, + "ok/no-old": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + switch string(bucket) { + case string(ordersByAccountIDTable): + return nil, database.ErrNotFound + default: + assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket))) + return nil, errors.New("force") + } + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + switch string(bucket) { + case string(ordersByAccountIDTable): + assert.Equals(t, key, []byte(accID)) + assert.Equals(t, old, nil) + assert.Equals(t, nu, nil) + return nil, true, nil + default: + assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket))) + return nil, false, errors.New("force") + } + }, + }, + res: []string{}, + } + }, "ok/all-old-not-pending": func(t *testing.T) test { oldOids := []string{"foo", "bar"} bOldOids, err := json.Marshal(oldOids) From 568fce201a964f338add0af86e74da472605d2eb Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:15:36 -0700 Subject: [PATCH 182/291] Enforce identity cert to match ssh cert on renewals. --- api/sshRekey.go | 7 ++++++- api/sshRenew.go | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/api/sshRekey.go b/api/sshRekey.go index 285422f9..3d8e7c47 100644 --- a/api/sshRekey.go +++ b/api/sshRekey.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "time" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" @@ -72,7 +73,11 @@ func (h *caHandler) SSHRekey(w http.ResponseWriter, r *http.Request) { return } - identity, err := h.renewIdentityCertificate(r) + // Match identity cert with the SSH cert + notBefore := time.Unix(int64(oldCert.ValidAfter), 0) + notAfter := time.Unix(int64(oldCert.ValidBefore), 0) + + identity, err := h.renewIdentityCertificate(r, notBefore, notAfter) if err != nil { WriteError(w, errs.ForbiddenErr(err)) return diff --git a/api/sshRenew.go b/api/sshRenew.go index 048c83a3..4a36673a 100644 --- a/api/sshRenew.go +++ b/api/sshRenew.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "time" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" @@ -62,7 +63,11 @@ func (h *caHandler) SSHRenew(w http.ResponseWriter, r *http.Request) { return } - identity, err := h.renewIdentityCertificate(r) + // Match identity cert with the SSH cert + notBefore := time.Unix(int64(oldCert.ValidAfter), 0) + notAfter := time.Unix(int64(oldCert.ValidBefore), 0) + + identity, err := h.renewIdentityCertificate(r, notBefore, notAfter) if err != nil { WriteError(w, errs.ForbiddenErr(err)) return @@ -74,13 +79,24 @@ func (h *caHandler) SSHRenew(w http.ResponseWriter, r *http.Request) { }, http.StatusCreated) } -// renewIdentityCertificate request the client TLS certificate if present. -func (h *caHandler) renewIdentityCertificate(r *http.Request) ([]Certificate, error) { +// renewIdentityCertificate request the client TLS certificate if present. If notBefore and notAfter are passed the +func (h *caHandler) renewIdentityCertificate(r *http.Request, notBefore, notAfter time.Time) ([]Certificate, error) { if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 { return nil, nil } - certChain, err := h.Authority.Renew(r.TLS.PeerCertificates[0]) + cert := r.TLS.PeerCertificates[0] + + // Enforce the cert to match another certificate, for example an ssh + // certificate. + if !notBefore.IsZero() { + cert.NotBefore = notBefore + } + if !notAfter.IsZero() { + cert.NotAfter = notAfter + } + + certChain, err := h.Authority.Renew(cert) if err != nil { return nil, err } From 61b8bfda1a32999a7d081f3fca2808e05471eddc Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:18:54 -0700 Subject: [PATCH 183/291] Fix comment typos. --- authority/export.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authority/export.go b/authority/export.go index 4eeb2813..8a5a257f 100644 --- a/authority/export.go +++ b/authority/export.go @@ -117,7 +117,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { if !ok { return nil, errors.Errorf("unknown certificate issuer type %s", iss.Type) } - // The exporte certificate issuer should not include the password. + // The exported certificate issuer should not include the password. c.Authority.CertificateIssuer = &linkedca.CertificateIssuer{ Type: linkedca.CertificateIssuer_Type(typ), Provisioner: iss.Provisioner, @@ -150,7 +150,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) { } // global claims c.Authority.Claims = claimsToLinkedca(a.config.AuthorityConfig.Claims) - // Distiguised names template + // Distinguished names template if v := a.config.AuthorityConfig.Template; v != nil { c.Authority.Template = &linkedca.DistinguishedName{ Country: v.Country, From e12f6fcc84cbb90df8858e89e45421f43201f3ae Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:24:13 -0700 Subject: [PATCH 184/291] Complete phrase in step-ca export help. --- commands/export.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commands/export.go b/commands/export.go index 0080df7d..4a18a0dd 100644 --- a/commands/export.go +++ b/commands/export.go @@ -25,7 +25,9 @@ func init() { Action: exportAction, Description: `**step-ca export** exports the current configuration of step-ca. -Note that neither the PKI password nor +Note that neither the PKI password nor the certificate issuer password will be +included in the export file. + ## POSITIONAL ARGUMENTS From 3d141896e2b1ebce4ea6867e0985edb9f758593a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:26:32 -0700 Subject: [PATCH 185/291] Remove extra space. --- commands/export.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/export.go b/commands/export.go index 4a18a0dd..be6d88e5 100644 --- a/commands/export.go +++ b/commands/export.go @@ -48,7 +48,7 @@ intermediate private key.`, cli.StringFlag{ Name: "issuer-password-file", Usage: `path to the containing the password to decrypt the - certificate issuer private key used in the RA mode.`, +certificate issuer private key used in the RA mode.`, }, }, }) From ff25f4974f7a9d0df3859ce11205e4f3972c3a80 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:29:18 -0700 Subject: [PATCH 186/291] Fix comment. --- pki/pki.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pki/pki.go b/pki/pki.go index 1d81d82a..7ba56ad3 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -780,7 +780,7 @@ func (p *PKI) Save(opt ...ConfigOption) error { return err } - // Display only the + // Display the files written p.tellPKI() // Generate and write ca.json From 516b74f43a3e4bb670825adff9d53053465f1759 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 23 Aug 2021 15:33:16 -0700 Subject: [PATCH 187/291] Add comment about unused code. --- pki/pki.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pki/pki.go b/pki/pki.go index 7ba56ad3..1b6c83e7 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -735,6 +735,13 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { if !config.AuthorityConfig.EnableAdmin { config.AuthorityConfig.Provisioners = provisioners } else { + // At this moment this code path is never used because `step ca + // init` will always set enableAdmin to false for a standalone + // deployment. Once we move `step beta` commands out of the beta we + // should probably default to this route. + // + // Note that we might want to be able to define the database as a + // flag in `step ca init` so we can write to the proper place. db, err := db.New(config.DB) if err != nil { return nil, err From 21b2057ecdb28050403c921e629013fb5f9b75b5 Mon Sep 17 00:00:00 2001 From: Alan Christopher Thomas Date: Wed, 25 Aug 2021 09:54:22 -0700 Subject: [PATCH 188/291] Add Certificate Manager notice for the pragmatic folks --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index eb441c7b..64458929 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,13 @@ You can use it to: Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [safe, sane defaults](https://smallstep.com/docs/step-ca/certificate-authority-server-production#sane-cryptographic-defaults). +--- + +**Don't want to run your own CA?** +To get up and running quickly, or as an alternative to running your own `step-ca` server, consider creating a [free hosted smallstep Certificate Manager authority](https://info.smallstep.com/certificate-manager-early-access-mvp/). + +--- + **Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/step-discord).** [Website](https://smallstep.com/certificates) | From cc9bc9c84bc32f7c08c8b1e2b23fe5cdfc526b50 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 25 Aug 2021 10:24:18 -0700 Subject: [PATCH 189/291] Bump Badger --- go.mod | 1 + go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index f17b29bc..2067bb20 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Masterminds/sprig/v3 v3.1.0 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/go-kit/kit v0.10.0 // indirect diff --git a/go.sum b/go.sum index c0d45435..f73da328 100644 --- a/go.sum +++ b/go.sum @@ -43,7 +43,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOv github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= @@ -134,8 +133,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= @@ -325,6 +325,8 @@ github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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= From 2317bf183b9fdc78f7d61bd554224eec693a2eca Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 25 Aug 2021 10:32:12 -0700 Subject: [PATCH 190/291] Nosql and badger bump --- go.mod | 3 +-- go.sum | 9 ++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 2067bb20..9340565c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/Masterminds/sprig/v3 v3.1.0 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 - github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/go-kit/kit v0.10.0 // indirect @@ -24,7 +23,7 @@ require ( github.com/rs/xid v1.2.1 github.com/sirupsen/logrus v1.4.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 - github.com/smallstep/nosql v0.3.7 + github.com/smallstep/nosql v0.3.8 github.com/stretchr/testify v1.7.0 // indirect github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 diff --git a/go.sum b/go.sum index f73da328..c82c70c8 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIo github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -133,7 +131,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= @@ -224,8 +221,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -460,8 +455,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd 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= -github.com/smallstep/nosql v0.3.7 h1:P5C1cCj89a/MbD+4k8k585fzoaZNISmRA06v3q6u5lU= -github.com/smallstep/nosql v0.3.7/go.mod h1:mC+MOhUY1uV5S5vGmAwp1FSPfDB7iImiYn5nJCjzAdA= +github.com/smallstep/nosql v0.3.8 h1:1/EWUbbEdz9ai0g9Fd09VekVjtxp+5+gIHpV2PdwW3o= +github.com/smallstep/nosql v0.3.8/go.mod h1:X2qkYpNcW3yjLUvhEHfgGfClpKbFPapewvx7zo4TOFs= 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= From 2c5080aae0352984e7bf57112a5ed9f9cd3b2a3e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 25 Aug 2021 15:57:47 -0700 Subject: [PATCH 191/291] go mod tidy --- go.sum | 3 --- 1 file changed, 3 deletions(-) diff --git a/go.sum b/go.sum index 60e9ab6b..cb6766a6 100644 --- a/go.sum +++ b/go.sum @@ -42,9 +42,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIo github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= From 833d28cb6a59bb935f781e2811c840b348eaf19b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 25 Aug 2021 16:15:12 -0700 Subject: [PATCH 192/291] Clone the certificate in case we need to look at it later. --- api/sshRenew.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/sshRenew.go b/api/sshRenew.go index 4a36673a..cb6ec5fd 100644 --- a/api/sshRenew.go +++ b/api/sshRenew.go @@ -1,6 +1,7 @@ package api import ( + "crypto/x509" "net/http" "time" @@ -85,7 +86,11 @@ func (h *caHandler) renewIdentityCertificate(r *http.Request, notBefore, notAfte return nil, nil } - cert := r.TLS.PeerCertificates[0] + // Clone the certificate as we can modify it. + cert, err := x509.ParseCertificate(r.TLS.PeerCertificates[0].Raw) + if err != nil { + return nil, errors.Wrap(err, "error parsing client certificate") + } // Enforce the cert to match another certificate, for example an ssh // certificate. From 8cb62b6d67019af7981c5c840f68fa7b5711d1f6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 26 Aug 2021 10:20:16 -0700 Subject: [PATCH 193/291] Fix ssh in helm chart values. --- pki/helm.go | 11 +++++------ pki/pki.go | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pki/helm.go b/pki/helm.go index 2a8ac513..570fb04d 100644 --- a/pki/helm.go +++ b/pki/helm.go @@ -14,11 +14,9 @@ import ( type helmVariables struct { *linkedca.Configuration - Defaults *linkedca.Defaults - Password string - SSH struct { - Enabled bool - } + Defaults *linkedca.Defaults + Password string + EnableSSH bool TLS authconfig.TLSOptions Provisioners []provisioner.Interface } @@ -48,6 +46,7 @@ func (p *PKI) WriteHelmTemplate(w io.Writer) error { Configuration: &p.Configuration, Defaults: &p.Defaults, Password: "", + EnableSSH: p.options.enableSSH, TLS: authconfig.DefaultTLSOptions, Provisioners: provisioners, }); err != nil { @@ -67,7 +66,7 @@ inject: federateRoots: [] crt: {{ .Intermediate }} key: {{ .IntermediateKey }} - {{- if .SSH.Enabled }} + {{- if .EnableSSH }} ssh: hostKey: {{ .Ssh.HostKey }} userKey: {{ .Ssh.UserKey }} diff --git a/pki/pki.go b/pki/pki.go index 1b6c83e7..fd625199 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -408,6 +408,15 @@ func (p *PKI) GenerateKeyPairs(pass []byte) error { return err } + var claims *linkedca.Claims + if p.options.enableSSH { + claims = &linkedca.Claims{ + Ssh: &linkedca.SSHClaims{ + Enabled: true, + }, + } + } + // Add JWK provisioner to the configuration. publicKey, err := json.Marshal(p.ottPublicKey) if err != nil { @@ -418,8 +427,9 @@ func (p *PKI) GenerateKeyPairs(pass []byte) error { return errors.Wrap(err, "error serializing private key") } p.Authority.Provisioners = append(p.Authority.Provisioners, &linkedca.Provisioner{ - Type: linkedca.Provisioner_JWK, - Name: p.options.provisioner, + Type: linkedca.Provisioner_JWK, + Name: p.options.provisioner, + Claims: claims, Details: &linkedca.ProvisionerDetails{ Data: &linkedca.ProvisionerDetails_JWK{ JWK: &linkedca.JWKProvisioner{ From 352acf8faa718d52b6ba2e44a246ecea9e140ae8 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 26 Aug 2021 11:29:13 -0700 Subject: [PATCH 194/291] Upgrade golang.org/x/crypto --- go.mod | 6 ++---- go.sum | 16 ++++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index f6228a9b..89cae8e2 100644 --- a/go.mod +++ b/go.mod @@ -24,15 +24,13 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/nosql v0.3.8 - github.com/stretchr/testify v1.7.0 // indirect github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.0 go.step.sm/linkedca v0.5.0 - golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/net v0.0.0-20210716203947-853a461950ff - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/net v0.0.0-20210825183410-e898025ed96a google.golang.org/api v0.47.0 google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 google.golang.org/grpc v1.39.0 diff --git a/go.sum b/go.sum index cb6766a6..d06e3ad7 100644 --- a/go.sum +++ b/go.sum @@ -488,9 +488,8 @@ 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= @@ -546,8 +545,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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= @@ -627,8 +626,8 @@ 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-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds= -golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/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= @@ -710,8 +709,9 @@ 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-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= From 40e77f6e9a480299496c03abe8a5fe7429608239 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 26 Aug 2021 17:55:42 -0700 Subject: [PATCH 195/291] Initialize required variables on GetIdentityToken Fixes smallstep/cli#465 --- authority/provisioner/aws.go | 5 +++++ authority/provisioner/aws_test.go | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index c1c77ce5..cdd06f00 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -515,6 +515,11 @@ func (p *AWS) readURL(url string) ([]byte, error) { var resp *http.Response var err error + // Initialize IMDS versions when this is called from the cli. + if len(p.IMDSVersions) == 0 { + p.IMDSVersions = []string{"v2", "v1"} + } + for _, v := range p.IMDSVersions { switch v { case "v1": diff --git a/authority/provisioner/aws_test.go b/authority/provisioner/aws_test.go index dadf1f17..aff0aecb 100644 --- a/authority/provisioner/aws_test.go +++ b/authority/provisioner/aws_test.go @@ -141,6 +141,12 @@ func TestAWS_GetIdentityToken(t *testing.T) { p7.config.signatureURL = p1.config.signatureURL p7.config.tokenURL = p1.config.tokenURL + p8, err := generateAWS() + assert.FatalError(t, err) + p8.IMDSVersions = nil + p8.Accounts = p1.Accounts + p8.config = p1.config + caURL := "https://ca.smallstep.com" u, err := url.Parse(caURL) assert.FatalError(t, err) @@ -156,6 +162,7 @@ func TestAWS_GetIdentityToken(t *testing.T) { wantErr bool }{ {"ok", p1, args{"foo.local", caURL}, false}, + {"ok no imds", p8, args{"foo.local", caURL}, false}, {"fail ca url", p1, args{"foo.local", "://ca.smallstep.com"}, true}, {"fail identityURL", p2, args{"foo.local", caURL}, true}, {"fail signatureURL", p3, args{"foo.local", caURL}, true}, From 9e7a3cd897dfe4518b4afd861cf4f3a42d11bf7d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 26 Aug 2021 18:12:37 -0700 Subject: [PATCH 196/291] Update go.step.sm/crypto --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 89cae8e2..d786a523 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.step.sm/cli-utils v0.4.1 - go.step.sm/crypto v0.9.0 + go.step.sm/crypto v0.9.2 go.step.sm/linkedca v0.5.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210825183410-e898025ed96a diff --git a/go.sum b/go.sum index d06e3ad7..cc687ddd 100644 --- a/go.sum +++ b/go.sum @@ -523,8 +523,9 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= -go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/crypto v0.9.2 h1:UvQHE4brjAOdgcK2ob6zupL1iRzDd8+QiEvPOeQrm4E= +go.step.sm/crypto v0.9.2/go.mod h1:F5OJyPDWntNa1SbuWPxuHJc9bLzu84NzYrrdzDuBugk= go.step.sm/linkedca v0.5.0 h1:oZVRSpElM7lAL1XN2YkjdHwI/oIZ+1ULOnuqYPM6xjY= go.step.sm/linkedca v0.5.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= From 6ec8a1f114695cb045127fd0a9724a561debe833 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 30 Aug 2021 12:57:39 -0700 Subject: [PATCH 197/291] Bump golangci-lint to latest --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 235517b2..3435f132 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ ci: testcgo build bootstra%: # Using a released version of golangci-lint to take into account custom replacements in their go.mod - $Q curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.39.0 + $Q curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.42.0 .PHONY: bootstra% From 097a918da7397665d7b64a50c9fe234313065659 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 30 Aug 2021 16:36:18 -0700 Subject: [PATCH 198/291] Fix tests when we create re-use a token with a new authority. --- authority/authority_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/authority/authority_test.go b/authority/authority_test.go index 7604ec6b..1e18a24f 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -11,6 +11,7 @@ import ( "net" "reflect" "testing" + "time" "github.com/pkg/errors" "github.com/smallstep/assert" @@ -82,6 +83,10 @@ func testAuthority(t *testing.T, opts ...Option) *Authority { } a, err := New(c, opts...) assert.FatalError(t, err) + // Avoid errors when test tokens are created before the test authority. This + // happens in some tests where we re-create the same authority to test + // special cases without re-creating the token. + a.startTime = a.startTime.Add(-1 * time.Minute) return a } From f919535475f7821249fad7ec61ca6fd8c0be6dae Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 30 Aug 2021 16:37:29 -0700 Subject: [PATCH 199/291] Add an extra way to distinguish Azure and Azure OIDC tokens. We used to distinguish these tokens using the azp claim, but this claim does not appear on new azure oidc tokens, at least on some configurations. This change will try to load by audience (client id) if the token contains an email, required for OIDC. --- authority/provisioner/collection.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index 3ba98a23..caf46ca9 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -37,8 +37,9 @@ func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // provisioner. type loadByTokenPayload struct { jose.Claims - AuthorizedParty string `json:"azp"` // OIDC client id - TenantID string `json:"tid"` // Microsoft Azure tenant id + Email string `json:"email"` // OIDC email + AuthorizedParty string `json:"azp"` // OIDC client id + TenantID string `json:"tid"` // Microsoft Azure tenant id } // Collection is a memory map of provisioners. @@ -129,12 +130,20 @@ func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims) return p, ok } } - // Try with tid (Azure) + // Try with tid (Azure, Azure OIDC) if payload.TenantID != "" { + // Try to load an OIDC provisioner first. + if payload.Email != "" { + if p, ok := c.LoadByTokenID(payload.Audience[0]); ok { + return p, ok + } + } + // Try to load an Azure provisioner. if p, ok := c.LoadByTokenID(payload.TenantID); ok { return p, ok } } + // Fallback to aud return c.LoadByTokenID(payload.Audience[0]) } From d6203eb2512de64f9394e08b9b65797c0249a069 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 30 Aug 2021 16:52:40 -0700 Subject: [PATCH 200/291] [action] use cosign to sign over goreleaser artifacts --- .github/workflows/release.yml | 12 +++++++++++- .goreleaser.yml | 5 +++++ cosign.pub | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 cosign.pub diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 819a470e..a15f893d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,14 +100,24 @@ jobs: uses: actions/setup-go@v2 with: go-version: 1.16 + - + name: Install cosign + uses: sigstore/cosign-installer@main + with: + cosign-release: 'v1.1.0' + - + name: Write cosign key to disk + id: write_key + run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@56f5b77f7fa4a8fe068bf22b732ec036cc9bc13f # v2.4.1 + uses: goreleaser/goreleaser-action@5a54d7e660bda43b405e8463261b3d25631ffe86 # v2.7.0 with: version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.PAT }} + COSIGN_PWD: ${{ secrets.COSIGN_PWD }} release_deb: name: Build & Upload Debian Package To Github diff --git a/.goreleaser.yml b/.goreleaser.yml index 7a7e20d3..1acf405a 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -111,6 +111,11 @@ source: name_template: '{{ .ProjectName }}_{{ .Version }}' checksum: name_template: 'checksums.txt' +signs: +- cmd: cosign + stdin: '{{ .Env.COSIGN_PWD }}' + args: ["sign-blob", "-key=/tmp/cosign.key", "-output=${signature}", "${artifact}"] + artifacts: all snapshot: name_template: "{{ .Tag }}-next" release: diff --git a/cosign.pub b/cosign.pub new file mode 100644 index 00000000..9a0b42be --- /dev/null +++ b/cosign.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEs+6THbAiXx4bja5ARQFNZmPwZjlD +GRvt5H+9ZFDhrcFPR1E7eB2rt1B/DhobANdHGKjvEBZEf0v4X/7S+SHrIw== +-----END PUBLIC KEY----- From 96762226cd8e327ad35fa53adea6843ec2359adc Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 30 Aug 2021 20:54:46 -0700 Subject: [PATCH 201/291] changelog update for 0.17.2 --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2b3e25..bd9ba01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased - 0.0.1] - DATE +## [Unreleased - 0.17.3] - DATE ### Added ### Changed ### Deprecated ### Removed ### Fixed ### Security + +## [0.17.2] - 08.30.2021 +### Added +- Additional way to distinguish Azure IID and Azure OIDC tokens. +### Security +- Sign over all goreleaser github artifacts using cosign From 12153c96aeae54928f4f340bd217f57d051c8468 Mon Sep 17 00:00:00 2001 From: Kevin Chen <49530888+devadvocado@users.noreply.github.com> Date: Tue, 31 Aug 2021 10:18:13 -0700 Subject: [PATCH 202/291] update changelog --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9ba01e..5909638f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,3 +17,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Additional way to distinguish Azure IID and Azure OIDC tokens. ### Security - Sign over all goreleaser github artifacts using cosign + +## [0.17.1] - 2021-08-26 + +## [0.17.0] - 2021-08-25 +### Added +- Add support for Linked CAs using protocol buffers and gRPC +- `step-ca init` adds support for + - configuring a StepCAS RA + - configuring a Linked CA + - congifuring a `step-ca` using Helm +### Changed +- Update badger driver to use v2 by default +- Update TLS cipher suites to include 1.3 +### Security +- Fix key version when SHA512WithRSA is used. There was a typo creating RSA keys with SHA256 digests instead of SHA512. + From e73612da079f11f85c4f3ba2d274e0aafef652fc Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 1 Sep 2021 12:43:59 -0700 Subject: [PATCH 203/291] [action] sign and push sigs for multi-arch docker containers w/ cosign --- .github/workflows/release.yml | 19 ++++++++++++++++--- make/docker.mk | 2 ++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a15f893d..87a3228b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,13 +158,25 @@ jobs: runs-on: ubuntu-20.04 needs: test steps: - - name: Checkout + - + name: Checkout uses: actions/checkout@v2 - - name: Setup Go + - + name: Setup Go uses: actions/setup-go@v2 with: go-version: '1.16' - - name: Build + - + name: Install cosign + uses: sigstore/cosign-installer@main + with: + cosign-release: 'v1.1.0' + - + name: Write cosign key to disk + id: write_key + run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key" + - + name: Build id: build run: | PATH=$PATH:/usr/local/go/bin:/home/admin/go/bin @@ -172,3 +184,4 @@ jobs: env: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + COSIGN_PWD: ${{ secrets.COSIGN_PWD }} diff --git a/make/docker.mk b/make/docker.mk index 8ed25219..edb82423 100644 --- a/make/docker.mk +++ b/make/docker.mk @@ -54,6 +54,8 @@ define DOCKER_BUILDX # $(1) -- Image Tag # $(2) -- Push (empty is no push | --push will push to dockerhub) docker buildx build . --progress plain -t $(DOCKER_IMAGE_NAME):$(1) -f docker/Dockerfile.step-ca --platform="$(DOCKER_PLATFORMS)" $(2) + echo -n "$(COSIGN_PWD)" | cosign sign -key /tmp/cosign.key -r $(DOCKER_IMAGE_NAME):$(1) + endef # For non-master builds don't build the docker containers. From 19726aa61f39ea7809dee6360fca98e6d45c08d0 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 1 Sep 2021 13:21:59 -0700 Subject: [PATCH 204/291] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5909638f..272e2716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed ### Fixed ### Security +- Use cosign to sign and upload signatures for multi-arch Docker container. ## [0.17.2] - 08.30.2021 ### Added From e4739171b4cf66dba0dc80bed67025758f9a6266 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 11:28:16 -0700 Subject: [PATCH 205/291] [action] Build deb during goreleaser action, add to checksum and ... - add go 1.17 to test matrix - build with go 1.17 --- .github/workflows/release.yml | 25 +++++++++++++++++++------ .github/workflows/test.yml | 2 +- .goreleaser.yml | 12 ++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87a3228b..08dd88ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - go: [ '1.15', '1.16' ] + go: [ '1.15', '1.16', '1.17' ] outputs: is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }} steps: @@ -99,10 +99,23 @@ jobs: name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 + - + name: APT Install + id: aptInstall + run: sudo apt-get -y install build-essential debhelper fakeroot + - + name: Build Debian package + id: make_debian + run: | + PATH=$PATH:/usr/local/go/bin:/home/admin/go/bin + make debian + # need to restore the git state otherwise goreleaser fails due to dirty state + git restore debian/changelog + git clean -fd - name: Install cosign - uses: sigstore/cosign-installer@main + uses: sigstore/cosign-installer@v1.1.0 with: cosign-release: 'v1.1.0' - @@ -133,7 +146,7 @@ jobs: name: Set up Go uses: actions/setup-go@v2 with: - go-version: '1.16' + go-version: '1.17' - name: APT Install id: aptInstall @@ -165,10 +178,10 @@ jobs: name: Setup Go uses: actions/setup-go@v2 with: - go-version: '1.16' + go-version: '1.17' - name: Install cosign - uses: sigstore/cosign-installer@main + uses: sigstore/cosign-installer@v1.1.0 with: cosign-release: 'v1.1.0' - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c73cfbd..96655664 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - go: [ '1.15', '1.16' ] + go: [ '1.15', '1.16', '1.17' ] steps: - name: Checkout diff --git a/.goreleaser.yml b/.goreleaser.yml index 1acf405a..8dbbac29 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,10 +1,12 @@ # This is an example .goreleaser.yml file with some sane defaults. # Make sure to check the documentation at http://goreleaser.com project_name: step-ca + before: hooks: # You may remove this if you don't use go modules. - go mod download + builds: - id: step-ca @@ -93,6 +95,7 @@ builds: binary: bin/step-awskms-init ldflags: - -w -X main.Version={{.Version}} -X main.BuildTime={{.Date}} + archives: - # Can be used to change the archive formats for specific GOOSs. @@ -106,18 +109,25 @@ archives: files: - README.md - LICENSE + source: enabled: true name_template: '{{ .ProjectName }}_{{ .Version }}' + checksum: name_template: 'checksums.txt' + extra_files: + - glob: ./.releases/* + signs: - cmd: cosign stdin: '{{ .Env.COSIGN_PWD }}' args: ["sign-blob", "-key=/tmp/cosign.key", "-output=${signature}", "${artifact}"] artifacts: all + snapshot: name_template: "{{ .Tag }}-next" + release: # Repo in which the release will be created. # Default is extracted from the origin remote URL or empty if its private hosted. @@ -154,6 +164,8 @@ release: # The filename on the release will be the last part of the path (base). If # another file with the same name exists, the latest one found will be used. # Defaults to empty. + extra_files: + - glob: ./.releases/* #extra_files: # - glob: ./path/to/file.txt # - glob: ./glob/**/to/**/file/**/* From 8bec473f8ebfea7935db239d9ec18b3ed319f34b Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 11:30:35 -0700 Subject: [PATCH 206/291] fix gofmt linting errors --- kms/pkcs11/pkcs11_no_cgo.go | 1 + kms/yubikey/yubikey_no_cgo.go | 1 + 2 files changed, 2 insertions(+) diff --git a/kms/pkcs11/pkcs11_no_cgo.go b/kms/pkcs11/pkcs11_no_cgo.go index 87c9a36b..6fa51dff 100644 --- a/kms/pkcs11/pkcs11_no_cgo.go +++ b/kms/pkcs11/pkcs11_no_cgo.go @@ -1,3 +1,4 @@ +//go:build !cgo // +build !cgo package pkcs11 diff --git a/kms/yubikey/yubikey_no_cgo.go b/kms/yubikey/yubikey_no_cgo.go index 6ed7c630..24a76174 100644 --- a/kms/yubikey/yubikey_no_cgo.go +++ b/kms/yubikey/yubikey_no_cgo.go @@ -1,3 +1,4 @@ +//go:build !cgo // +build !cgo package yubikey From 8ba9013f5d21c2a38a0011451fefc3bc5bc353aa Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 11:35:51 -0700 Subject: [PATCH 207/291] gofmt linting errors --- kms/pkcs11/benchmark_test.go | 1 + kms/pkcs11/opensc_test.go | 1 + kms/pkcs11/other_test.go | 1 + kms/pkcs11/pkcs11.go | 1 + kms/pkcs11/pkcs11_test.go | 1 + kms/pkcs11/setup_test.go | 1 + kms/pkcs11/softhsm2_test.go | 1 + kms/pkcs11/yubihsm2_test.go | 1 + kms/yubikey/yubikey.go | 1 + 9 files changed, 9 insertions(+) diff --git a/kms/pkcs11/benchmark_test.go b/kms/pkcs11/benchmark_test.go index 30e21117..c567872f 100644 --- a/kms/pkcs11/benchmark_test.go +++ b/kms/pkcs11/benchmark_test.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo package pkcs11 diff --git a/kms/pkcs11/opensc_test.go b/kms/pkcs11/opensc_test.go index f3b61932..b365e614 100644 --- a/kms/pkcs11/opensc_test.go +++ b/kms/pkcs11/opensc_test.go @@ -1,3 +1,4 @@ +//go:build opensc // +build opensc package pkcs11 diff --git a/kms/pkcs11/other_test.go b/kms/pkcs11/other_test.go index 835587f7..680d3860 100644 --- a/kms/pkcs11/other_test.go +++ b/kms/pkcs11/other_test.go @@ -1,3 +1,4 @@ +//go:build cgo && !softhsm2 && !yubihsm2 && !opensc // +build cgo,!softhsm2,!yubihsm2,!opensc package pkcs11 diff --git a/kms/pkcs11/pkcs11.go b/kms/pkcs11/pkcs11.go index 47c298a5..07d40c05 100644 --- a/kms/pkcs11/pkcs11.go +++ b/kms/pkcs11/pkcs11.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo package pkcs11 diff --git a/kms/pkcs11/pkcs11_test.go b/kms/pkcs11/pkcs11_test.go index 77277366..6df9b92a 100644 --- a/kms/pkcs11/pkcs11_test.go +++ b/kms/pkcs11/pkcs11_test.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo package pkcs11 diff --git a/kms/pkcs11/setup_test.go b/kms/pkcs11/setup_test.go index c9ff9311..52dc5207 100644 --- a/kms/pkcs11/setup_test.go +++ b/kms/pkcs11/setup_test.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo package pkcs11 diff --git a/kms/pkcs11/softhsm2_test.go b/kms/pkcs11/softhsm2_test.go index 37aa667d..ed2ff208 100644 --- a/kms/pkcs11/softhsm2_test.go +++ b/kms/pkcs11/softhsm2_test.go @@ -1,3 +1,4 @@ +//go:build cgo && softhsm2 // +build cgo,softhsm2 package pkcs11 diff --git a/kms/pkcs11/yubihsm2_test.go b/kms/pkcs11/yubihsm2_test.go index 6d02a420..281aff54 100644 --- a/kms/pkcs11/yubihsm2_test.go +++ b/kms/pkcs11/yubihsm2_test.go @@ -1,3 +1,4 @@ +//go:build cgo && yubihsm2 // +build cgo,yubihsm2 package pkcs11 diff --git a/kms/yubikey/yubikey.go b/kms/yubikey/yubikey.go index 2dde244a..b1d5f7e3 100644 --- a/kms/yubikey/yubikey.go +++ b/kms/yubikey/yubikey.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo package yubikey From 23d3232d7589f015f762bced6c10e70aca9cdbdc Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 11:39:49 -0700 Subject: [PATCH 208/291] Changelog updates --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 272e2716..a99bbd1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased - 0.17.3] - DATE ### Added +- go 1.17 to github action test matrix ### Changed +- Using go 1.17 for binaries ### Deprecated ### Removed ### Fixed ### Security - Use cosign to sign and upload signatures for multi-arch Docker container. +- Add debian checksum ## [0.17.2] - 08.30.2021 ### Added From 9df5cc40b6b86e952c1e285767b495d22db8385c Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 12:16:40 -0700 Subject: [PATCH 209/291] [action] remove duplicate debian step --- .github/workflows/release.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08dd88ce..e2e0176c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -132,40 +132,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.PAT }} COSIGN_PWD: ${{ secrets.COSIGN_PWD }} - release_deb: - name: Build & Upload Debian Package To Github - runs-on: ubuntu-20.04 - needs: create_release - steps: - - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: '1.17' - - - name: APT Install - id: aptInstall - run: sudo apt-get -y install build-essential debhelper fakeroot - - - name: Build Debian package - id: build - run: | - PATH=$PATH:/usr/local/go/bin:/home/admin/go/bin - make debian - - - name: Upload Debian Package - id: upload_deb - run: | - tag_name="${GITHUB_REF##*/}" - hub release edit $(find ./.releases -type f -printf "-a %p ") -m "" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build_upload_docker: name: Build & Upload Docker Images runs-on: ubuntu-20.04 From 9641354675a2cc14ea00233d5d29d017c95ac768 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 12:37:45 -0700 Subject: [PATCH 210/291] [action] tmate debugger --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2e0176c..449bb072 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -122,6 +122,9 @@ jobs: name: Write cosign key to disk id: write_key run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key" + - + name: Setup tmate session + uses: mxschmitt/action-tmate@v3 - name: Run GoReleaser uses: goreleaser/goreleaser-action@5a54d7e660bda43b405e8463261b3d25631ffe86 # v2.7.0 From 9ed84d71f605c10d29bf1bc099155ebbbace097c Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 7 Sep 2021 13:07:25 -0700 Subject: [PATCH 211/291] [action] Add .releases to .gitignore --- .github/workflows/release.yml | 3 --- .gitignore | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 449bb072..e2e0176c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -122,9 +122,6 @@ jobs: name: Write cosign key to disk id: write_key run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key" - - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - name: Run GoReleaser uses: goreleaser/goreleaser-action@5a54d7e660bda43b405e8463261b3d25631ffe86 # v2.7.0 diff --git a/.gitignore b/.gitignore index 7cba0d08..d87786b0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,8 @@ # Others *.swp -.travis-releases +.releases coverage.txt -vendor output +vendor .idea From 913bd0f24ad984f9571b83fba0815b93707149ec Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Wed, 8 Sep 2021 06:45:32 +0200 Subject: [PATCH 212/291] CHANGELOG: use ISO-8601 dates --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a99bbd1a..e15dca6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Use cosign to sign and upload signatures for multi-arch Docker container. - Add debian checksum -## [0.17.2] - 08.30.2021 +## [0.17.2] - 2021-08-30 ### Added - Additional way to distinguish Azure IID and Azure OIDC tokens. ### Security From 7a94b0c1570e9b0ad60bb8eadfb3d80e75b5b3e7 Mon Sep 17 00:00:00 2001 From: Fearghal O Floinn Date: Wed, 8 Sep 2021 12:24:49 +0100 Subject: [PATCH 213/291] Converts group and subgroup to lowercase for comparison. Fixes #679 --- authority/provisioner/sign_ssh_options.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/authority/provisioner/sign_ssh_options.go b/authority/provisioner/sign_ssh_options.go index a872513e..4a0cf526 100644 --- a/authority/provisioner/sign_ssh_options.go +++ b/authority/provisioner/sign_ssh_options.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/json" "math/big" + "strings" "time" "github.com/pkg/errors" @@ -453,12 +454,20 @@ func containsAllMembers(group, subgroup []string) bool { if lsg > lg || (lg > 0 && lsg == 0) { return false } + groupLower := []string{} + subgroupLower := []string{} + for _, s := range group { + groupLower = append(groupLower, strings.ToLower(s)) + } + for _, s := range subgroup { + subgroupLower = append(subgroupLower, strings.ToLower(s)) + } visit := make(map[string]struct{}, lg) for i := 0; i < lg; i++ { - visit[group[i]] = struct{}{} + visit[groupLower[i]] = struct{}{} } for i := 0; i < lsg; i++ { - if _, ok := visit[subgroup[i]]; !ok { + if _, ok := visit[subgroupLower[i]]; !ok { return false } } From 141c51917156f648cfe79e937ca9a7eb0724cd65 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 8 Sep 2021 16:00:33 -0700 Subject: [PATCH 214/291] Simplify check of principals in a case insensitive way Fixes #679 --- authority/provisioner/sign_ssh_options.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/authority/provisioner/sign_ssh_options.go b/authority/provisioner/sign_ssh_options.go index 4a0cf526..158470d1 100644 --- a/authority/provisioner/sign_ssh_options.go +++ b/authority/provisioner/sign_ssh_options.go @@ -454,20 +454,12 @@ func containsAllMembers(group, subgroup []string) bool { if lsg > lg || (lg > 0 && lsg == 0) { return false } - groupLower := []string{} - subgroupLower := []string{} - for _, s := range group { - groupLower = append(groupLower, strings.ToLower(s)) - } - for _, s := range subgroup { - subgroupLower = append(subgroupLower, strings.ToLower(s)) - } visit := make(map[string]struct{}, lg) for i := 0; i < lg; i++ { - visit[groupLower[i]] = struct{}{} + visit[strings.ToLower(group[i])] = struct{}{} } for i := 0; i < lsg; i++ { - if _, ok := visit[subgroupLower[i]]; !ok { + if _, ok := visit[strings.ToLower(subgroup[i])]; !ok { return false } } From 6d644880bd2012744e517b9fb0f354f45ae58c5a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 8 Sep 2021 15:33:34 -0700 Subject: [PATCH 215/291] Allow to kms signers to define the SignatureAlgorithm CloudKMS keys signs data using an specific signature algorithm, in RSA keys, this can be PKCS#1 RSA or RSA-PSS, if the later is used, x509.CreateCertificate will fail unless the template SignatureCertificate is properly set. On contrast, AWSKMS RSA keys, are just RSA keys, and can sign with PKCS#1 or RSA-PSS schemes, so right now the way to enforce one or the other is to used templates. --- cas/apiv1/services.go | 7 ++++ cas/softcas/softcas.go | 21 ++++++++-- cas/softcas/softcas_test.go | 49 +++++++++++++++++++++++ kms/cloudkms/cloudkms.go | 14 +++++++ kms/cloudkms/signer.go | 11 +++++- kms/cloudkms/signer_test.go | 77 +++++++++++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 5 deletions(-) diff --git a/cas/apiv1/services.go b/cas/apiv1/services.go index d4dd3c8c..cf9a5470 100644 --- a/cas/apiv1/services.go +++ b/cas/apiv1/services.go @@ -1,6 +1,7 @@ package apiv1 import ( + "crypto/x509" "net/http" "strings" ) @@ -26,6 +27,12 @@ type CertificateAuthorityCreator interface { CreateCertificateAuthority(req *CreateCertificateAuthorityRequest) (*CreateCertificateAuthorityResponse, error) } +// SignatureAlgorithmGetter is an optional implementation in a crypto.Signer +// that returns the SignatureAlgorithm to use. +type SignatureAlgorithmGetter interface { + SignatureAlgorithm() x509.SignatureAlgorithm +} + // Type represents the CAS type used. type Type string diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index 21760490..f3b2d051 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -68,7 +68,7 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 } req.Template.Issuer = c.CertificateChain[0].Subject - cert, err := x509util.CreateCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) + cert, err := createCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) if err != nil { return nil, err } @@ -93,7 +93,7 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R req.Template.NotAfter = t.Add(req.Lifetime) req.Template.Issuer = c.CertificateChain[0].Subject - cert, err := x509util.CreateCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) + cert, err := createCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) if err != nil { return nil, err } @@ -150,12 +150,12 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori var cert *x509.Certificate switch req.Type { case apiv1.RootCA: - cert, err = x509util.CreateCertificate(req.Template, req.Template, signer.Public(), signer) + cert, err = createCertificate(req.Template, req.Template, signer.Public(), signer) if err != nil { return nil, err } case apiv1.IntermediateCA: - cert, err = x509util.CreateCertificate(req.Template, req.Parent.Certificate, signer.Public(), req.Parent.Signer) + cert, err = createCertificate(req.Template, req.Parent.Certificate, signer.Public(), req.Parent.Signer) if err != nil { return nil, err } @@ -210,3 +210,16 @@ func (c *SoftCAS) createSigner(req *kmsapi.CreateSignerRequest) (crypto.Signer, } return c.KeyManager.CreateSigner(req) } + +// createCertificate sets the SignatureAlgorithm of the template if necessary +// and calls x509util.CreateCertificate. +func createCertificate(template, parent *x509.Certificate, pub crypto.PublicKey, signer crypto.Signer) (*x509.Certificate, error) { + // Signers can specify the signature algorithm. This is specially important + // when x509.CreateCertificates attempts to validate a RSAPSS signature. + if template.SignatureAlgorithm == 0 { + if sa, ok := signer.(apiv1.SignatureAlgorithmGetter); ok { + template.SignatureAlgorithm = sa.SignatureAlgorithm() + } + } + return x509util.CreateCertificate(template, parent, pub, signer) +} diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index 092a0337..c8e1a8e9 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -75,6 +75,15 @@ var ( testSignedIntermediateTemplate = mustSign(testIntermediateTemplate, testSignedRootTemplate, testNow, testNow.Add(24*time.Hour)) ) +type signatureAlgorithmSigner struct { + crypto.Signer + algorithm x509.SignatureAlgorithm +} + +func (s *signatureAlgorithmSigner) SignatureAlgorithm() x509.SignatureAlgorithm { + return s.algorithm +} + type mockKeyManager struct { signer crypto.Signer errGetPublicKey error @@ -247,6 +256,13 @@ func TestSoftCAS_CreateCertificate(t *testing.T) { tmplNoSerial := *testTemplate tmplNoSerial.SerialNumber = nil + saTemplate := *testSignedTemplate + saTemplate.SignatureAlgorithm = 0 + saSigner := &signatureAlgorithmSigner{ + Signer: testSigner, + algorithm: x509.PureEd25519, + } + type fields struct { Issuer *x509.Certificate Signer crypto.Signer @@ -267,6 +283,12 @@ func TestSoftCAS_CreateCertificate(t *testing.T) { Certificate: testSignedTemplate, CertificateChain: []*x509.Certificate{testIssuer}, }, false}, + {"ok signature algorithm", fields{testIssuer, saSigner}, args{&apiv1.CreateCertificateRequest{ + Template: &saTemplate, Lifetime: 24 * time.Hour, + }}, &apiv1.CreateCertificateResponse{ + Certificate: testSignedTemplate, + CertificateChain: []*x509.Certificate{testIssuer}, + }, false}, {"ok with notBefore", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{ Template: &tmplNotBefore, Lifetime: 24 * time.Hour, }}, &apiv1.CreateCertificateResponse{ @@ -316,6 +338,11 @@ func TestSoftCAS_RenewCertificate(t *testing.T) { tmplNoSerial := *testTemplate tmplNoSerial.SerialNumber = nil + saSigner := &signatureAlgorithmSigner{ + Signer: testSigner, + algorithm: x509.PureEd25519, + } + type fields struct { Issuer *x509.Certificate Signer crypto.Signer @@ -336,6 +363,12 @@ func TestSoftCAS_RenewCertificate(t *testing.T) { Certificate: testSignedTemplate, CertificateChain: []*x509.Certificate{testIssuer}, }, false}, + {"ok signature algorithm", fields{testIssuer, saSigner}, args{&apiv1.RenewCertificateRequest{ + Template: testTemplate, Lifetime: 24 * time.Hour, + }}, &apiv1.RenewCertificateResponse{ + Certificate: testSignedTemplate, + CertificateChain: []*x509.Certificate{testIssuer}, + }, false}, {"fail template", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{Lifetime: 24 * time.Hour}}, nil, true}, {"fail lifetime", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{Template: testTemplate}}, nil, true}, {"fail CreateCertificate", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{ @@ -425,6 +458,11 @@ func Test_now(t *testing.T) { func TestSoftCAS_CreateCertificateAuthority(t *testing.T) { mockNow(t) + saSigner := &signatureAlgorithmSigner{ + Signer: testSigner, + algorithm: x509.PureEd25519, + } + type fields struct { Issuer *x509.Certificate Signer crypto.Signer @@ -467,6 +505,17 @@ func TestSoftCAS_CreateCertificateAuthority(t *testing.T) { PrivateKey: testSigner, Signer: testSigner, }, false}, + {"ok signature algorithm", fields{nil, nil, &mockKeyManager{signer: saSigner}}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: testRootTemplate, + Lifetime: 24 * time.Hour, + }}, &apiv1.CreateCertificateAuthorityResponse{ + Name: "Test Root CA", + Certificate: testSignedRootTemplate, + PublicKey: testSignedRootTemplate.PublicKey, + PrivateKey: saSigner, + Signer: saSigner, + }, false}, {"fail template", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Lifetime: 24 * time.Hour, diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index f4c656d3..65d06048 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -3,6 +3,7 @@ package cloudkms import ( "context" "crypto" + "crypto/x509" "log" "strings" "time" @@ -63,6 +64,19 @@ var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]interface{}{ apiv1.ECDSAWithSHA384: kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384, } +var cryptoKeyVersionMapping = map[kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm]x509.SignatureAlgorithm{ + kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256: x509.ECDSAWithSHA256, + kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384: x509.ECDSAWithSHA384, + kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_2048_SHA256: x509.SHA256WithRSA, + kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256: x509.SHA256WithRSA, + kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256: x509.SHA256WithRSA, + kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512: x509.SHA512WithRSA, + kmspb.CryptoKeyVersion_RSA_SIGN_PSS_2048_SHA256: x509.SHA256WithRSAPSS, + kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256: x509.SHA256WithRSAPSS, + kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA256: x509.SHA256WithRSAPSS, + kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512: x509.SHA512WithRSAPSS, +} + // KeyManagementClient defines the methods on KeyManagementClient that this // package will use. This interface will be used for unit testing. type KeyManagementClient interface { diff --git a/kms/cloudkms/signer.go b/kms/cloudkms/signer.go index 686aca25..5a5443cf 100644 --- a/kms/cloudkms/signer.go +++ b/kms/cloudkms/signer.go @@ -2,6 +2,7 @@ package cloudkms import ( "crypto" + "crypto/x509" "io" "github.com/pkg/errors" @@ -13,6 +14,7 @@ import ( type Signer struct { client KeyManagementClient signingKey string + algorithm x509.SignatureAlgorithm publicKey crypto.PublicKey } @@ -40,7 +42,7 @@ func (s *Signer) preloadKey(signingKey string) error { if err != nil { return errors.Wrap(err, "cloudKMS GetPublicKey failed") } - + s.algorithm = cryptoKeyVersionMapping[response.Algorithm] s.publicKey, err = pemutil.ParseKey([]byte(response.Pem)) return err } @@ -84,3 +86,10 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] return response.Signature, nil } + +// SignatureAlgorithm returns the algorithm that must be specified in a +// certificate to sign. This is specially important to distinguish RSA and +// RSAPSS schemas. +func (s *Signer) SignatureAlgorithm() x509.SignatureAlgorithm { + return s.algorithm +} diff --git a/kms/cloudkms/signer_test.go b/kms/cloudkms/signer_test.go index fa730fe3..a8f964f1 100644 --- a/kms/cloudkms/signer_test.go +++ b/kms/cloudkms/signer_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto" "crypto/rand" + "crypto/x509" "fmt" "io" "io/ioutil" @@ -156,3 +157,79 @@ func Test_signer_Sign(t *testing.T) { }) } } + +func TestSigner_SignatureAlgorithm(t *testing.T) { + pemBytes, err := ioutil.ReadFile("testdata/pub.pem") + if err != nil { + t.Fatal(err) + } + + client := &MockClient{ + getPublicKey: func(_ context.Context, req *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) { + var algorithm kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm + switch req.Name { + case "ECDSA-SHA256": + algorithm = kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256 + case "ECDSA-SHA384": + algorithm = kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384 + case "SHA256-RSA-2048": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_2048_SHA256 + case "SHA256-RSA-3072": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256 + case "SHA256-RSA-4096": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256 + case "SHA512-RSA-4096": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512 + case "SHA256-RSAPSS-2048": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PSS_2048_SHA256 + case "SHA256-RSAPSS-3072": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256 + case "SHA256-RSAPSS-4096": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA256 + case "SHA512-RSAPSS-4096": + algorithm = kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512 + } + return &kmspb.PublicKey{ + Pem: string(pemBytes), + Algorithm: algorithm, + }, nil + }, + } + + if err != nil { + t.Fatal(err) + } + + type fields struct { + client KeyManagementClient + signingKey string + } + tests := []struct { + name string + fields fields + want x509.SignatureAlgorithm + }{ + {"ECDSA-SHA256", fields{client, "ECDSA-SHA256"}, x509.ECDSAWithSHA256}, + {"ECDSA-SHA384", fields{client, "ECDSA-SHA384"}, x509.ECDSAWithSHA384}, + {"SHA256-RSA-2048", fields{client, "SHA256-RSA-2048"}, x509.SHA256WithRSA}, + {"SHA256-RSA-3072", fields{client, "SHA256-RSA-3072"}, x509.SHA256WithRSA}, + {"SHA256-RSA-4096", fields{client, "SHA256-RSA-4096"}, x509.SHA256WithRSA}, + {"SHA512-RSA-4096", fields{client, "SHA512-RSA-4096"}, x509.SHA512WithRSA}, + {"SHA256-RSAPSS-2048", fields{client, "SHA256-RSAPSS-2048"}, x509.SHA256WithRSAPSS}, + {"SHA256-RSAPSS-3072", fields{client, "SHA256-RSAPSS-3072"}, x509.SHA256WithRSAPSS}, + {"SHA256-RSAPSS-4096", fields{client, "SHA256-RSAPSS-4096"}, x509.SHA256WithRSAPSS}, + {"SHA512-RSAPSS-4096", fields{client, "SHA512-RSAPSS-4096"}, x509.SHA512WithRSAPSS}, + {"unknown", fields{client, "UNKNOWN"}, x509.UnknownSignatureAlgorithm}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + signer, err := NewSigner(tt.fields.client, tt.fields.signingKey) + if err != nil { + t.Errorf("NewSigner() error = %v", err) + } + if got := signer.SignatureAlgorithm(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Signer.SignatureAlgorithm() = %v, want %v", got, tt.want) + } + }) + } +} From 6e0d515a55854f419f596b2a6f979bfa7b3a2615 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 8 Sep 2021 17:46:55 -0700 Subject: [PATCH 216/291] Add entry to changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e15dca6e..c963b44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased - 0.17.3] - DATE ### Added - go 1.17 to github action test matrix +- Support for CloudKMS RSA-PSS signers without using templates. ### Changed - Using go 1.17 for binaries ### Deprecated From e4e799ca8548771d0c3463d4d7dff12d039ccb1f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 9 Sep 2021 12:45:29 -0700 Subject: [PATCH 217/291] Fix typos in comment. --- cas/softcas/softcas.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index f3b2d051..23dac91b 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -214,8 +214,8 @@ func (c *SoftCAS) createSigner(req *kmsapi.CreateSignerRequest) (crypto.Signer, // createCertificate sets the SignatureAlgorithm of the template if necessary // and calls x509util.CreateCertificate. func createCertificate(template, parent *x509.Certificate, pub crypto.PublicKey, signer crypto.Signer) (*x509.Certificate, error) { - // Signers can specify the signature algorithm. This is specially important - // when x509.CreateCertificates attempts to validate a RSAPSS signature. + // Signers can specify the signature algorithm. This is especially important + // when x509.CreateCertificate attempts to validate a RSAPSS signature. if template.SignatureAlgorithm == 0 { if sa, ok := signer.(apiv1.SignatureAlgorithmGetter); ok { template.SignatureAlgorithm = sa.SignatureAlgorithm() From 8a99f7e4586f838afb669d3065924208541304b4 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 9 Sep 2021 16:35:09 -0700 Subject: [PATCH 218/291] [action] add header and footer to github release page --- .goreleaser.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index 8dbbac29..1bef6ce5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -156,6 +156,35 @@ release: # Default is `{{.Tag}}` #name_template: "{{.ProjectName}}-v{{.Version}} {{.Env.USER}}" + # Header template for the release body. + # Defaults to empty. + header: | + Welcome to this new release! + + ## Signatures and Checksums + + `step-ca` uses [sigstore/cosign](https://github.com/sigstore/cosign) for signing and verifying release artifacts. + Here is an example of how to use `cosign` to verify a release artifact: + + ``` + cosign verify-blob \ + -key https://raw.githubusercontent.com/smallstep/cli/master/cosign.pub \ + -signature ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz.sig + ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz + ``` + + We use the `checksums.txt` file to store checksums for every artifact in the release. + + # Footer template for the release body. + # Defaults to empty. + footer: | + ## Thanks! + + Those were the changes on {{ .Tag }}! + + Come join us on [Discord](https://discord.gg/X2RKGwEbV9) to ask questions, chat about PKI, + or get a sneak peak at the freshest PKI memes. + # You can disable this pipe in order to not upload any artifacts. # Defaults to false. #disable: true From 2cce795d8f98ede22ddca836d91bf35ddc5228b8 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 9 Sep 2021 16:53:47 -0700 Subject: [PATCH 219/291] [action] reference correct pub key in cosign example release header --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 1bef6ce5..78c892f0 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -168,7 +168,7 @@ release: ``` cosign verify-blob \ - -key https://raw.githubusercontent.com/smallstep/cli/master/cosign.pub \ + -key https://raw.githubusercontent.com/smallstep/certificates/master/cosign.pub \ -signature ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz.sig ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz ``` From 494da3d668b3fc79dc6972e9a37c34650ba24eb6 Mon Sep 17 00:00:00 2001 From: max furman Date: Sat, 11 Sep 2021 13:05:17 -0700 Subject: [PATCH 220/291] [action] goreleaser header packages --- .github/workflows/release.yml | 15 +++++++++++++++ .goreleaser.yml | 34 ++++++++++++++++++++++++++-------- Makefile | 2 +- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2e0176c..6da2aa27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,8 +62,15 @@ jobs: needs: test runs-on: ubuntu-20.04 outputs: + debversion: ${{ steps.extract-tag.outputs.DEB_VERSION }} is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }} steps: + - + name: Extract Tag Names + id: extract-tag + run: | + DEB_VERSION=$(echo ${GITHUB_REF#refs/tags/v} | sed 's/-/./') + echo "::set-output name=DEB_VERSION::${DEB_VERSION}" - name: Is Pre-release id: is_prerelease @@ -122,6 +129,12 @@ jobs: name: Write cosign key to disk id: write_key run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key" + - + name: Get Release Date + id: release_date + run: | + RELEASE_DATE=$(date +"%y-%m-%d") + echo "::set-output name=RELEASE_DATE::${RELEASE_DATE}" - name: Run GoReleaser uses: goreleaser/goreleaser-action@5a54d7e660bda43b405e8463261b3d25631ffe86 # v2.7.0 @@ -131,6 +144,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PAT }} COSIGN_PWD: ${{ secrets.COSIGN_PWD }} + DEB_VERSION: ${{ needs.create_release.outputs.debversion }} + RELEASE_DATE: ${{ steps.release_date.outputs.RELEASE_DATE }} build_upload_docker: name: Build & Upload Docker Images diff --git a/.goreleaser.yml b/.goreleaser.yml index 78c892f0..b61bde61 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -154,26 +154,45 @@ release: # You can change the name of the release. # Default is `{{.Tag}}` - #name_template: "{{.ProjectName}}-v{{.Version}} {{.Env.USER}}" + name_template: "Step CA {{ .Tag }} ({{ .Env.RELEASE_DATE }})" # Header template for the release body. # Defaults to empty. header: | - Welcome to this new release! + ## Official Release Artifacts + + #### Linux + + - 📦 [step-ca_linux_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_linux_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_{{ .Env.DEB_VERSION }}_amd64.deb](https://dl.step.sm/cli/{{ .Tag }}/step-ca_{{ .Env.DEB_VERSION }}_amd64.deb) + + #### OSX Darwin + + - 📦 [step-ca_darwin_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_darwin_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_darwin_{{ .Version }}_arm64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_darwin_{{ .Version }}_arm64.tar.gz) + + #### Windows + + - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/cli/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) + + For more builds across platforms and architectures see the `Assets` section below. + + Don't see the artifact you need? Open an issue [here](https://github.com/smallstep/certificates/issues/new/choose). ## Signatures and Checksums `step-ca` uses [sigstore/cosign](https://github.com/sigstore/cosign) for signing and verifying release artifacts. - Here is an example of how to use `cosign` to verify a release artifact: + + Below is an example using `cosign` to verify a release artifact: ``` cosign verify-blob \ -key https://raw.githubusercontent.com/smallstep/certificates/master/cosign.pub \ - -signature ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz.sig - ~/Downloads/step-ca_darwin_0.17.2_amd64.tar.gz + -signature ~/Downloads/step-ca_darwin_{{ .Version }_amd64.tar.gz.sig + ~/Downloads/step-ca_darwin_{{ .Version }_amd64.tar.gz ``` - We use the `checksums.txt` file to store checksums for every artifact in the release. + The `checksums.txt` file (in the `Assets` section below) contains a checksum for every artifact in the release. # Footer template for the release body. # Defaults to empty. @@ -182,8 +201,7 @@ release: Those were the changes on {{ .Tag }}! - Come join us on [Discord](https://discord.gg/X2RKGwEbV9) to ask questions, chat about PKI, - or get a sneak peak at the freshest PKI memes. + Come join us on [Discord](https://discord.gg/X2RKGwEbV9) to ask questions, chat about PKI, or get a sneak peak at the freshest PKI memes. # You can disable this pipe in order to not upload any artifacts. # Defaults to false. diff --git a/Makefile b/Makefile index 3435f132..108efa1d 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,7 @@ PUSHTYPE := branch endif VERSION := $(shell echo $(VERSION) | sed 's/^v//') -DEB_VERSION := $(shell echo $(VERSION) | sed 's/-/~/g') +DEB_VERSION := $(shell echo $(VERSION) | sed 's/-/./g') ifdef V $(info TRAVIS_TAG is $(TRAVIS_TAG)) From fcf322023a7f3e7a55d241d245a7cafa539fdfb9 Mon Sep 17 00:00:00 2001 From: max furman Date: Sat, 11 Sep 2021 14:42:02 -0700 Subject: [PATCH 221/291] [action] goreleaser github release footer fix missing close braces --- .goreleaser.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index b61bde61..5d4e4ab0 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -188,8 +188,8 @@ release: ``` cosign verify-blob \ -key https://raw.githubusercontent.com/smallstep/certificates/master/cosign.pub \ - -signature ~/Downloads/step-ca_darwin_{{ .Version }_amd64.tar.gz.sig - ~/Downloads/step-ca_darwin_{{ .Version }_amd64.tar.gz + -signature ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig + ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz ``` The `checksums.txt` file (in the `Assets` section below) contains a checksum for every artifact in the release. From 6d644ddb2a8cad1691384613890bf45066ca3114 Mon Sep 17 00:00:00 2001 From: max furman Date: Sun, 12 Sep 2021 21:32:22 -0700 Subject: [PATCH 222/291] [action] goreleaser pkg link cli -> certificates --- .goreleaser.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 5d4e4ab0..05f98941 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -163,17 +163,17 @@ release: #### Linux - - 📦 [step-ca_linux_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_linux_{{ .Version }}_amd64.tar.gz) - - 📦 [step-ca_{{ .Env.DEB_VERSION }}_amd64.deb](https://dl.step.sm/cli/{{ .Tag }}/step-ca_{{ .Env.DEB_VERSION }}_amd64.deb) + - 📦 [step-ca_linux_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_linux_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_{{ .Env.DEB_VERSION }}_amd64.deb](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_{{ .Env.DEB_VERSION }}_amd64.deb) #### OSX Darwin - - 📦 [step-ca_darwin_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_darwin_{{ .Version }}_amd64.tar.gz) - - 📦 [step-ca_darwin_{{ .Version }}_arm64.tar.gz](https://dl.step.sm/cli/{{ .Tag }}/step-ca_darwin_{{ .Version }}_arm64.tar.gz) + - 📦 [step-ca_darwin_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_darwin_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_darwin_{{ .Version }}_arm64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_darwin_{{ .Version }}_arm64.tar.gz) #### Windows - - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/cli/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) + - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) For more builds across platforms and architectures see the `Assets` section below. From e3acea97043365b7146d21eadf5c4380c07efad4 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 15 Sep 2021 10:30:04 -0700 Subject: [PATCH 223/291] Add release page link to install docs --- .goreleaser.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 05f98941..9e95e928 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -175,7 +175,8 @@ release: - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) - For more builds across platforms and architectures see the `Assets` section below. + For more builds across platforms and architectures, see the `Assets` section below. + And for packaged versions (Docker, k8s, Homebrew), see our [installation docs](https://smallstep.com/docs/step-ca/installation). Don't see the artifact you need? Open an issue [here](https://github.com/smallstep/certificates/issues/new/choose). From 611859eec45ac9b77f797eed49826926addb9371 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 16 Sep 2021 08:24:28 +0200 Subject: [PATCH 224/291] Update go.mozilla.org/pkcs7 This includes the fix as described in https://github.com/mozilla-services/pkcs7/pull/59, which was the reason a fork of the library was used. --- go.mod | 3 +-- go.sum | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d786a523..7fe587b6 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/nosql v0.3.8 github.com/urfave/cli v1.22.4 - go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 + go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.9.2 go.step.sm/linkedca v0.5.0 @@ -43,4 +43,3 @@ require ( // replace go.step.sm/cli-utils => ../cli-utils // replace go.step.sm/linkedca => ../linkedca -replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index cc687ddd..47875803 100644 --- a/go.sum +++ b/go.sum @@ -510,6 +510,9 @@ 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= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= From 73d0a11a2075517de9ea14dd7ed7322dcf7c8b80 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 16 Sep 2021 08:29:25 +0200 Subject: [PATCH 225/291] Update github.com/micromdm/scep/v2 --- go.mod | 3 +-- go.sum | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 7fe587b6..524c98e7 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( 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.0.0 + 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 @@ -42,4 +42,3 @@ require ( // 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 47875803..8bd0b7ad 100644 --- a/go.sum +++ b/go.sum @@ -360,6 +360,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= +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/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= @@ -511,6 +513,7 @@ go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= From 8df9f629b173182eb9c73768c87b377cb4f56d53 Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 16 Sep 2021 00:14:06 -0700 Subject: [PATCH 226/291] go mod tidy --- go.sum | 5 ----- 1 file changed, 5 deletions(-) diff --git a/go.sum b/go.sum index 8bd0b7ad..f216c1d3 100644 --- a/go.sum +++ b/go.sum @@ -358,8 +358,6 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= -github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= 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= @@ -394,8 +392,6 @@ github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HD github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= -github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -512,7 +508,6 @@ 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= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= From 6729c79253a1721ef84a1180bf19215c6498c91b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 16 Sep 2021 11:55:41 -0700 Subject: [PATCH 227/291] Add support for setting individual password for ssh and tls keys This change add the following flags: * --ssh-host-password-file * --ssh-user-password-file Fixes #693 --- authority/authority.go | 36 +++++++++++++++++++++++++------ authority/options.go | 36 +++++++++++++++++++++++++++++++ ca/ca.go | 49 +++++++++++++++++++++++++++--------------- commands/app.go | 32 +++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 23 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 1b060ef8..16968d9d 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -43,6 +43,8 @@ type Authority struct { linkedCAToken string // X509 CA + password []byte + issuerPassword []byte x509CAService cas.CertificateAuthorityService rootX509Certs []*x509.Certificate rootX509CertPool *x509.CertPool @@ -53,6 +55,8 @@ type Authority struct { scepService *scep.Service // SSH CA + sshHostPassword []byte + sshUserPassword []byte sshCAUserCertSignKey ssh.Signer sshCAHostCertSignKey ssh.Signer sshCAUserCerts []ssh.PublicKey @@ -206,6 +210,21 @@ func (a *Authority) init() error { var err error + // Set password if they are not set. + var configPassword []byte + if a.config.Password != "" { + configPassword = []byte(a.config.Password) + } + if configPassword != nil && a.password == nil { + a.password = configPassword + } + if a.sshHostPassword == nil { + a.sshHostPassword = a.password + } + if a.sshUserPassword == nil { + a.sshUserPassword = a.password + } + // Automatically enable admin for all linked cas. if a.linkedCAToken != "" { a.config.AuthorityConfig.EnableAdmin = true @@ -238,6 +257,11 @@ func (a *Authority) init() error { options = *a.config.AuthorityConfig.Options } + // Set the issuer password if passed in the flags. + if options.CertificateIssuer != nil && a.issuerPassword != nil { + options.CertificateIssuer.Password = string(a.issuerPassword) + } + // Read intermediate and create X509 signer for default CAS. if options.Is(casapi.SoftCAS) { options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) @@ -246,7 +270,7 @@ func (a *Authority) init() error { } options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), + Password: []byte(a.password), }) if err != nil { return err @@ -315,7 +339,7 @@ func (a *Authority) init() error { if a.config.SSH.HostKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.SSH.HostKey, - Password: []byte(a.config.Password), + Password: []byte(a.sshHostPassword), }) if err != nil { return err @@ -341,7 +365,7 @@ func (a *Authority) init() error { if a.config.SSH.UserKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.SSH.UserKey, - Password: []byte(a.config.Password), + Password: []byte(a.sshUserPassword), }) if err != nil { return err @@ -420,7 +444,7 @@ func (a *Authority) init() error { } options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), + Password: []byte(a.password), }) if err != nil { return err @@ -429,7 +453,7 @@ func (a *Authority) init() error { if km, ok := a.keyManager.(kmsapi.Decrypter); ok { options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ DecryptionKey: a.config.IntermediateKey, - Password: []byte(a.config.Password), + Password: []byte(a.password), }) if err != nil { return err @@ -475,7 +499,7 @@ func (a *Authority) init() error { } if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") { // Create First Provisioner - prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) + prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, string(a.password)) if err != nil { return admin.WrapErrorISE(err, "error creating first provisioner") } diff --git a/authority/options.go b/authority/options.go index 6baeb2bc..5c8a6e66 100644 --- a/authority/options.go +++ b/authority/options.go @@ -38,6 +38,42 @@ func WithConfigFile(filename string) Option { } } +// WithPassword set the password to decrypt the intermediate key as well as the +// ssh host and user keys if they are not overridden by other options. +func WithPassword(password []byte) Option { + return func(a *Authority) (err error) { + a.password = password + return + } +} + +// WithSSHHostPassword set the password to decrypt the key used to sign SSH host +// certificates. +func WithSSHHostPassword(password []byte) Option { + return func(a *Authority) (err error) { + a.sshHostPassword = password + return + } +} + +// WithSSHUserPassword set the password to decrypt the key used to sign SSH user +// certificates. +func WithSSHUserPassword(password []byte) Option { + return func(a *Authority) (err error) { + a.sshUserPassword = password + return + } +} + +// WithIssuerPassword set the password to decrypt the certificate issuer private +// key used in RA mode. +func WithIssuerPassword(password []byte) Option { + return func(a *Authority) (err error) { + a.issuerPassword = password + return + } +} + // WithDatabase sets an already initialized authority database to a new // authority. This option is intended to be use on graceful reloads. func WithDatabase(db db.AuthDB) Option { diff --git a/ca/ca.go b/ca/ca.go index 51d15bec..00a5970a 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -29,11 +29,13 @@ import ( ) type options struct { - configFile string - linkedCAToken string - password []byte - issuerPassword []byte - database db.AuthDB + configFile string + linkedCAToken string + password []byte + issuerPassword []byte + sshHostPassword []byte + sshUserPassword []byte + database db.AuthDB } func (o *options) apply(opts []Option) { @@ -61,6 +63,22 @@ func WithPassword(password []byte) Option { } } +// WithSSHHostPassword sets the given password to decrypt the key used to sign +// ssh host certificates. +func WithSSHHostPassword(password []byte) Option { + return func(o *options) { + o.sshHostPassword = password + } +} + +// WithSSHUserPassword sets the given password to decrypt the key used to sign +// ssh user certificates. +func WithSSHUserPassword(password []byte) Option { + return func(o *options) { + o.sshUserPassword = password + } +} + // WithIssuerPassword sets the given password as the configured certificate // issuer password in the CA options. func WithIssuerPassword(password []byte) Option { @@ -106,19 +124,14 @@ func New(config *config.Config, opts ...Option) (*CA, error) { // Init initializes the CA with the given configuration. func (ca *CA) Init(config *config.Config) (*CA, error) { - // Intermediate Password. - if len(ca.opts.password) > 0 { - ca.config.Password = string(ca.opts.password) - } - - // Certificate issuer password for RA mode. - if len(ca.opts.issuerPassword) > 0 { - if ca.config.AuthorityConfig != nil && ca.config.AuthorityConfig.CertificateIssuer != nil { - ca.config.AuthorityConfig.CertificateIssuer.Password = string(ca.opts.issuerPassword) - } + // Set password, it's ok to set nil password, the ca will prompt for them if + // they are required. + opts := []authority.Option{ + authority.WithPassword(ca.opts.password), + authority.WithSSHHostPassword(ca.opts.sshHostPassword), + authority.WithSSHUserPassword(ca.opts.sshUserPassword), + authority.WithIssuerPassword(ca.opts.issuerPassword), } - - var opts []authority.Option if ca.opts.linkedCAToken != "" { opts = append(opts, authority.WithLinkedCAToken(ca.opts.linkedCAToken)) } @@ -337,6 +350,8 @@ func (ca *CA) Reload() error { newCA, err := New(config, WithPassword(ca.opts.password), + WithSSHHostPassword(ca.opts.sshHostPassword), + WithSSHUserPassword(ca.opts.sshUserPassword), WithIssuerPassword(ca.opts.issuerPassword), WithLinkedCAToken(ca.opts.linkedCAToken), WithConfigFile(ca.opts.configFile), diff --git a/commands/app.go b/commands/app.go index aa7b43d4..20e71506 100644 --- a/commands/app.go +++ b/commands/app.go @@ -30,6 +30,18 @@ var AppCommand = cli.Command{ Name: "password-file", Usage: `path to the containing the password to decrypt the intermediate private key.`, + }, + cli.StringFlag{ + Name: "ssh-host-password-file", + Usage: `path to the containing the password to decrypt the +private key used to sign SSH host certificates. If the flag is not passed it +will default to --password-file.`, + }, + cli.StringFlag{ + Name: "ssh-user-password-file", + Usage: `path to the containing the password to decrypt the +private key used to sign SSH user certificates. If the flag is not passed it +will default to --password-file.`, }, cli.StringFlag{ Name: "issuer-password-file", @@ -51,6 +63,8 @@ certificate issuer private key used in the RA mode.`, // AppAction is the action used when the top command runs. func appAction(ctx *cli.Context) error { passFile := ctx.String("password-file") + sshHostPassFile := ctx.String("ssh-host-password-file") + sshUserPassFile := ctx.String("ssh-user-password-file") issuerPassFile := ctx.String("issuer-password-file") resolver := ctx.String("resolver") token := ctx.String("token") @@ -89,6 +103,22 @@ To get a linked authority token: password = bytes.TrimRightFunc(password, unicode.IsSpace) } + var sshHostPassword []byte + if sshHostPassFile != "" { + if sshHostPassword, err = ioutil.ReadFile(sshHostPassFile); err != nil { + fatal(errors.Wrapf(err, "error reading %s", sshHostPassFile)) + } + sshHostPassword = bytes.TrimRightFunc(sshHostPassword, unicode.IsSpace) + } + + var sshUserPassword []byte + if sshUserPassFile != "" { + if sshUserPassword, err = ioutil.ReadFile(sshUserPassFile); err != nil { + fatal(errors.Wrapf(err, "error reading %s", sshUserPassFile)) + } + sshUserPassword = bytes.TrimRightFunc(sshUserPassword, unicode.IsSpace) + } + var issuerPassword []byte if issuerPassFile != "" { if issuerPassword, err = ioutil.ReadFile(issuerPassFile); err != nil { @@ -108,6 +138,8 @@ To get a linked authority token: srv, err := ca.New(config, ca.WithConfigFile(configFile), ca.WithPassword(password), + ca.WithSSHHostPassword(sshHostPassword), + ca.WithSSHUserPassword(sshUserPassword), ca.WithIssuerPassword(issuerPassword), ca.WithLinkedCAToken(token)) if err != nil { From cfe08ad6fe85e1477708ac81eeb4a42a2f005fd7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 16 Sep 2021 12:05:23 -0700 Subject: [PATCH 228/291] Add flags to usage. --- cmd/step-ca/main.go | 4 +++- commands/app.go | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index 4396e028..aaf37df2 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -107,7 +107,9 @@ func main() { app.HelpName = "step-ca" app.Version = config.Version() app.Usage = "an online certificate authority for secure automated certificate management" - app.UsageText = `**step-ca** [**--password-file**=] [**--issuer-password-file**=] [**--resolver**=] [**--help**] [**--version**]` + app.UsageText = `**step-ca** [**--password-file**=] +[**--ssh-host-password-file**=] [**--ssh-user-password-file**=] +[**--issuer-password-file**=] [**--resolver**=] [**--help**] [**--version**]` app.Description = `**step-ca** runs the Step Online Certificate Authority (Step CA) using the given configuration. See the README.md for more detailed configuration documentation. diff --git a/commands/app.go b/commands/app.go index 20e71506..3aaee0f5 100644 --- a/commands/app.go +++ b/commands/app.go @@ -23,8 +23,9 @@ import ( var AppCommand = cli.Command{ Name: "start", Action: appAction, - UsageText: `**step-ca** -[**--password-file**=] [**--issuer-password-file**=] [**--resolver**=]`, + UsageText: `**step-ca** [**--password-file**=] +[**--ssh-host-password-file**=] [**--ssh-user-password-file**=] +[**--issuer-password-file**=] [**--resolver**=]`, Flags: []cli.Flag{ cli.StringFlag{ Name: "password-file", From 4fde7b52501034ac0314c317d109d44884fe2267 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 17 Sep 2021 12:49:16 -0700 Subject: [PATCH 229/291] Use badgerv2 the default in helm too. Use also port 443 for the ca-url, as we usually access through the service, this can be overridden by --with-ca-url flag in the cli. --- pki/helm.go | 2 +- pki/pki.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pki/helm.go b/pki/helm.go index 570fb04d..817c1bf4 100644 --- a/pki/helm.go +++ b/pki/helm.go @@ -79,7 +79,7 @@ inject: logger: format: json db: - type: badger + type: badgerv2 dataSource: /home/step/db authority: provisioners: diff --git a/pki/pki.go b/pki/pki.go index fd625199..12e71e47 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -341,7 +341,9 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { if err != nil { return nil, errors.Wrapf(err, "error parsing %s", p.Address) } - if port == "443" { + // On k8s we usually access through a service, and this is configured on + // port 443 by default. + if port == "443" || p.options.isHelm { p.Defaults.CaUrl = fmt.Sprintf("https://%s", p.Defaults.CaUrl) } else { p.Defaults.CaUrl = fmt.Sprintf("https://%s:%s", p.Defaults.CaUrl, port) From 04784be03e61dd126abd465211cebb8a59c76587 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 21 Sep 2021 17:23:29 -0700 Subject: [PATCH 230/291] Update cert-renewer@.service Wrap command line env variables in braces so they are treated as a single argument (rather than split on whitespace) --- systemd/cert-renewer@.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/cert-renewer@.service b/systemd/cert-renewer@.service index 5b56f5fc..a9962c2e 100644 --- a/systemd/cert-renewer@.service +++ b/systemd/cert-renewer@.service @@ -15,10 +15,10 @@ Environment=STEPPATH=/etc/step-ca \ ; ExecCondition checks if the certificate is ready for renewal, ; based on the exit status of the command. ; (In systemd 242 or below, you can use ExecStartPre= here.) -ExecCondition=/usr/bin/step certificate needs-renewal $CERT_LOCATION +ExecCondition=/usr/bin/step certificate needs-renewal ${CERT_LOCATION} ; ExecStart renews the certificate, if ExecStartPre was successful. -ExecStart=/usr/bin/step ca renew --force $CERT_LOCATION $KEY_LOCATION +ExecStart=/usr/bin/step ca renew --force ${CERT_LOCATION} ${KEY_LOCATION} ; Try to reload or restart the systemd service that relies on this cert-renewer ; If the relying service doesn't exist, forge ahead. From 2d5bfd34857eaebd682a91016a4106b03bb332cc Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 22 Sep 2021 11:56:52 -0700 Subject: [PATCH 231/291] fix comment --- authority/admin/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authority/admin/db.go b/authority/admin/db.go index 15fe6686..8a6339d9 100644 --- a/authority/admin/db.go +++ b/authority/admin/db.go @@ -54,7 +54,7 @@ func UnmarshalProvisionerDetails(typ linkedca.Provisioner_Type, data []byte) (*l return &linkedca.ProvisionerDetails{Data: v.Data}, nil } -// DB is the DB interface expected by the step-ca ACME API. +// DB is the DB interface expected by the step-ca Admin API. type DB interface { CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) From ad82d8a250be6c86990be361fa377f4e72f54df7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 22 Sep 2021 15:15:19 -0700 Subject: [PATCH 232/291] Upgrade go.step.sm/crypto as long with go-jose.v2 There was a typo in the OKP template causing bad fingerprints for Ed25519 keys. See https://github.com/square/go-jose/commit/a10ff54e00bc6e833bf549e04ae976f0fe8ea2fd Fixes #705 --- CHANGELOG.md | 1 + go.mod | 12 ++++++------ go.sum | 54 ++++++++++++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c963b44f..bb4dc430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Deprecated ### Removed ### Fixed +- Upgrade go-jose.v2 to fix a bug in the JWK fingerprint of Ed25519 keys. ### Security - Use cosign to sign and upload signatures for multi-arch Docker container. - Add debian checksum diff --git a/go.mod b/go.mod index 524c98e7..04af53fc 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( cloud.google.com/go v0.83.0 - github.com/Masterminds/sprig/v3 v3.1.0 + github.com/Masterminds/sprig/v3 v3.2.2 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect @@ -12,7 +12,7 @@ require ( github.com/go-kit/kit v0.10.0 // indirect github.com/go-piv/piv-go v1.7.0 github.com/golang/mock v1.5.0 - github.com/google/uuid v1.1.2 + 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 @@ -27,15 +27,15 @@ require ( github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.4.1 - go.step.sm/crypto v0.9.2 + go.step.sm/crypto v0.11.0 go.step.sm/linkedca v0.5.0 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/net v0.0.0-20210825183410-e898025ed96a + golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 + golang.org/x/net v0.0.0-20210913180222-943fd674d43e google.golang.org/api v0.47.0 google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492 google.golang.org/grpc v1.39.0 google.golang.org/protobuf v1.27.1 - gopkg.in/square/go-jose.v2 v2.5.1 + gopkg.in/square/go-jose.v2 v2.6.0 ) // replace github.com/smallstep/nosql => ../nosql diff --git a/go.sum b/go.sum index f216c1d3..a6ff0f08 100644 --- a/go.sum +++ b/go.sum @@ -43,12 +43,15 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOv github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -261,8 +264,9 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -300,13 +304,16 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -364,8 +371,9 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N 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= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -373,8 +381,9 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -450,6 +459,8 @@ github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0 github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 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= @@ -469,8 +480,9 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -525,8 +537,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.9.2 h1:UvQHE4brjAOdgcK2ob6zupL1iRzDd8+QiEvPOeQrm4E= -go.step.sm/crypto v0.9.2/go.mod h1:F5OJyPDWntNa1SbuWPxuHJc9bLzu84NzYrrdzDuBugk= +go.step.sm/crypto v0.11.0 h1:VDpeVgEmqme/FK2w5QINxkOQ1FWOm/Wi2TwQXiacKr8= +go.step.sm/crypto v0.11.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= go.step.sm/linkedca v0.5.0 h1:oZVRSpElM7lAL1XN2YkjdHwI/oIZ+1ULOnuqYPM6xjY= go.step.sm/linkedca v0.5.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -547,8 +559,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +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/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= @@ -628,8 +640,8 @@ 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-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/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/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,8 +723,9 @@ 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-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw= +golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -722,8 +735,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 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/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= @@ -920,16 +934,18 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 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.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 2ae6b42cfe05989bff5acac4608ac11f458b2ae1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 22 Sep 2021 16:39:23 -0700 Subject: [PATCH 233/291] Add missing feature to the changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb4dc430..9aba9913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. +- Add flags to support different passwords for the intermediate and SSH keys. ### Changed - Using go 1.17 for binaries ### Deprecated From 7f00cc7aad7856df69c16a874487d0c6e5ad78b1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 22 Sep 2021 17:41:12 -0700 Subject: [PATCH 234/291] Clarify changelog feature. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aba9913..a6e67397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. -- Add flags to support different passwords for the intermediate and SSH keys. +- Add flags to support individual passwords for the intermediate and SSH keys. ### Changed - Using go 1.17 for binaries ### Deprecated From a50654b46895d9aefdf26ebeac105dcef4f41c24 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 23 Sep 2021 15:49:28 -0700 Subject: [PATCH 235/291] Check for admins in both emails and groups. --- authority/provisioner/oidc.go | 71 ++++++++++++++---------------- authority/provisioner/oidc_test.go | 36 +++++++++++++++ 2 files changed, 68 insertions(+), 39 deletions(-) diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index b6bca872..3786f54b 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -49,6 +49,29 @@ type openIDPayload struct { Groups []string `json:"groups"` } +func (o *openIDPayload) IsAdmin(admins []string) bool { + if o.Email != "" { + email := sanitizeEmail(o.Email) + for _, e := range admins { + if email == sanitizeEmail(e) { + return true + } + } + } + + // The groups and emails can be in the same array for now, but consider + // making a specialized option later. + for _, name := range o.Groups { + for _, admin := range admins { + if name == admin { + return true + } + } + } + + return false +} + // OIDC represents an OAuth 2.0 OpenID Connect provider. // // ClientSecret is mandatory, but it can be an empty string. @@ -73,35 +96,6 @@ type OIDC struct { getIdentityFunc GetIdentityFunc } -// IsAdmin returns true if the given email is in the Admins allowlist, false -// otherwise. -func (o *OIDC) IsAdmin(email string) bool { - if email != "" { - email = sanitizeEmail(email) - for _, e := range o.Admins { - if email == sanitizeEmail(e) { - return true - } - } - } - return false -} - -// IsAdminGroup returns true if the one group in the given list is in the Admins -// allowlist, false otherwise. -func (o *OIDC) IsAdminGroup(groups []string) bool { - for _, g := range groups { - // The groups and emails can be in the same array for now, but consider - // making a specialized option later. - for _, gadmin := range o.Admins { - if g == gadmin { - return true - } - } - } - return false -} - func sanitizeEmail(email string) string { if i := strings.LastIndex(email, "@"); i >= 0 { email = email[:i] + strings.ToLower(email[i:]) @@ -234,7 +228,7 @@ func (o *OIDC) ValidatePayload(p openIDPayload) error { } // Validate domains (case-insensitive) - if p.Email != "" && len(o.Domains) > 0 && !o.IsAdmin(p.Email) { + if p.Email != "" && len(o.Domains) > 0 && !p.IsAdmin(o.Admins) { email := sanitizeEmail(p.Email) var found bool for _, d := range o.Domains { @@ -313,9 +307,10 @@ func (o *OIDC) AuthorizeRevoke(ctx context.Context, token string) error { } // Only admins can revoke certificates. - if o.IsAdmin(claims.Email) { + if claims.IsAdmin(o.Admins) { return nil } + return errs.Unauthorized("oidc.AuthorizeRevoke; cannot revoke with non-admin oidc token") } @@ -351,7 +346,7 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // Use the default template unless no-templates are configured and email is // an admin, in that case we will use the CR template. defaultTemplate := x509util.DefaultLeafTemplate - if !o.Options.GetX509Options().HasTemplate() && o.IsAdmin(claims.Email) { + if !o.Options.GetX509Options().HasTemplate() && claims.IsAdmin(o.Admins) { defaultTemplate = x509util.DefaultAdminLeafTemplate } @@ -420,10 +415,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption // Use the default template unless no-templates are configured and email is // an admin, in that case we will use the parameters in the request. - isAdmin := o.IsAdmin(claims.Email) - if !isAdmin && len(claims.Groups) > 0 { - isAdmin = o.IsAdminGroup(claims.Groups) - } + isAdmin := claims.IsAdmin(o.Admins) defaultTemplate := sshutil.DefaultTemplate if isAdmin && !o.Options.GetSSHOptions().HasTemplate() { defaultTemplate = sshutil.DefaultAdminTemplate @@ -471,10 +463,11 @@ func (o *OIDC) AuthorizeSSHRevoke(ctx context.Context, token string) error { } // Only admins can revoke certificates. - if !o.IsAdmin(claims.Email) { - return errs.Unauthorized("oidc.AuthorizeSSHRevoke; cannot revoke with non-admin oidc token") + if claims.IsAdmin(o.Admins) { + return nil } - return nil + + return errs.Unauthorized("oidc.AuthorizeSSHRevoke; cannot revoke with non-admin oidc token") } func getAndDecode(uri string, v interface{}) error { diff --git a/authority/provisioner/oidc_test.go b/authority/provisioner/oidc_test.go index 48f879a8..532bd2e0 100644 --- a/authority/provisioner/oidc_test.go +++ b/authority/provisioner/oidc_test.go @@ -698,3 +698,39 @@ func Test_sanitizeEmail(t *testing.T) { }) } } + +func Test_openIDPayload_IsAdmin(t *testing.T) { + type fields struct { + Email string + Groups []string + } + type args struct { + admins []string + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + {"ok email", fields{"admin@smallstep.com", nil}, args{[]string{"admin@smallstep.com"}}, true}, + {"ok email multiple", fields{"admin@smallstep.com", []string{"admin", "eng"}}, args{[]string{"eng@smallstep.com", "admin@smallstep.com"}}, true}, + {"ok email sanitized", fields{"admin@Smallstep.com", nil}, args{[]string{"admin@smallStep.com"}}, true}, + {"ok group", fields{"", []string{"admin"}}, args{[]string{"admin"}}, true}, + {"ok group multiple", fields{"admin@smallstep.com", []string{"engineering", "admin"}}, args{[]string{"admin"}}, true}, + {"fail missing", fields{"eng@smallstep.com", []string{"admin"}}, args{[]string{"admin@smallstep.com"}}, false}, + {"fail email letter case", fields{"Admin@smallstep.com", []string{}}, args{[]string{"admin@smallstep.com"}}, false}, + {"fail group letter case", fields{"", []string{"Admin"}}, args{[]string{"admin"}}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &openIDPayload{ + Email: tt.fields.Email, + Groups: tt.fields.Groups, + } + if got := o.IsAdmin(tt.args.admins); got != tt.want { + t.Errorf("openIDPayload.IsAdmin() = %v, want %v", got, tt.want) + } + }) + } +} From 9eb757797ec4e514a80199d666dd66e9e1f29ff2 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 24 Sep 2021 13:50:10 -0700 Subject: [PATCH 236/291] Add line to changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e67397..0ec765d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. - Add flags to support individual passwords for the intermediate and SSH keys. +- Support for group admins in OIDC provisioners for X509 certificates. ### Changed - Using go 1.17 for binaries ### Deprecated From 963eaf8882cee1b3d881df7f969b220a8b6b4a83 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 24 Sep 2021 13:50:47 -0700 Subject: [PATCH 237/291] Fix line in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec765d3..60b36b9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. - Add flags to support individual passwords for the intermediate and SSH keys. -- Support for group admins in OIDC provisioners for X509 certificates. +- Global support for group admins in the OIDC provisioner. ### Changed - Using go 1.17 for binaries ### Deprecated From ba17869deb19111c4374a6a0c49dc36cff42a7f5 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 24 Sep 2021 14:24:28 -0700 Subject: [PATCH 238/291] changelog update for 0.17.3 --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b36b9b..b0d4bda1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased - 0.17.3] - DATE +## [Unreleased - 0.17.4] - DATE +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + +## [0.17.3] - 2021-09-24 ### Added - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. @@ -12,8 +20,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Global support for group admins in the OIDC provisioner. ### Changed - Using go 1.17 for binaries -### Deprecated -### Removed ### Fixed - Upgrade go-jose.v2 to fix a bug in the JWK fingerprint of Ed25519 keys. ### Security From 6aaa7853b28f6e225cecae93e82e6d73a3f9a085 Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 27 Sep 2021 16:24:01 -0700 Subject: [PATCH 239/291] [action] update release URLs in header --- .goreleaser.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 9e95e928..97b9c24c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -163,17 +163,17 @@ release: #### Linux - - 📦 [step-ca_linux_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_linux_{{ .Version }}_amd64.tar.gz) - - 📦 [step-ca_{{ .Env.DEB_VERSION }}_amd64.deb](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_{{ .Env.DEB_VERSION }}_amd64.deb) + - 📦 [step-ca_linux_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_linux_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_{{ .Env.DEB_VERSION }}_amd64.deb](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_{{ .Env.DEB_VERSION }}_amd64.deb) #### OSX Darwin - - 📦 [step-ca_darwin_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_darwin_{{ .Version }}_amd64.tar.gz) - - 📦 [step-ca_darwin_{{ .Version }}_arm64.tar.gz](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_darwin_{{ .Version }}_arm64.tar.gz) + - 📦 [step-ca_darwin_{{ .Version }}_amd64.tar.gz](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_darwin_{{ .Version }}_amd64.tar.gz) + - 📦 [step-ca_darwin_{{ .Version }}_arm64.tar.gz](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_darwin_{{ .Version }}_arm64.tar.gz) #### Windows - - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/certificates/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) + - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) For more builds across platforms and architectures, see the `Assets` section below. And for packaged versions (Docker, k8s, Homebrew), see our [installation docs](https://smallstep.com/docs/step-ca/installation). From aedd7fcc050e164b773589c1ceb51e3a8629d154 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 28 Sep 2021 15:07:09 -0700 Subject: [PATCH 240/291] Be able to start a SSH host or SSH user only CA In previous versions if the host or user CA is not configured, the start of step-ca was crashing. This allows to configure a user or host only ssh ca. --- authority/authority.go | 25 +++++++++++----------- authority/ssh_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 16968d9d..e26ce591 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -361,6 +361,8 @@ func (a *Authority) init() error { // Append public key to list of host certs a.sshCAHostCerts = append(a.sshCAHostCerts, a.sshCAHostCertSignKey.PublicKey()) a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) + // Configure template variables. + tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() } if a.config.SSH.UserKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ @@ -387,35 +389,32 @@ func (a *Authority) init() error { // Append public key to list of user certs a.sshCAUserCerts = append(a.sshCAUserCerts, a.sshCAUserCertSignKey.PublicKey()) a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, a.sshCAUserCertSignKey.PublicKey()) + // Configure template variables. + tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() } - // Append other public keys + // Append other public keys and add them to the template variables. for _, key := range a.config.SSH.Keys { + publicKey := key.PublicKey() switch key.Type { case provisioner.SSHHostCert: if key.Federated { - a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, key.PublicKey()) + a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, publicKey) + tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, publicKey) } else { - a.sshCAHostCerts = append(a.sshCAHostCerts, key.PublicKey()) + a.sshCAHostCerts = append(a.sshCAHostCerts, publicKey) } case provisioner.SSHUserCert: if key.Federated { - a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, key.PublicKey()) + a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, publicKey) + tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, publicKey) } else { - a.sshCAUserCerts = append(a.sshCAUserCerts, key.PublicKey()) + a.sshCAUserCerts = append(a.sshCAUserCerts, publicKey) } default: return errors.Errorf("unsupported type %s", key.Type) } } - - // Configure template variables. - tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() - tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() - // On the templates we skip the first one because there's a distinction - // between the main key and federated keys. - tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) - tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } // Check if a KMS with decryption capability is required and available diff --git a/authority/ssh_test.go b/authority/ssh_test.go index e468ecf0..41df8576 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -87,6 +87,52 @@ func (m sshTestOptionsModifier) Modify(cert *ssh.Certificate, opts provisioner.S return fmt.Errorf(string(m)) } +func TestAuthority_initHostOnly(t *testing.T) { + auth := testAuthority(t, func(a *Authority) error { + a.config.SSH.UserKey = "" + return nil + }) + + // Check keys + keys, err := auth.GetSSHRoots(context.Background()) + assert.NoError(t, err) + assert.Len(t, 1, keys.HostKeys) + assert.Len(t, 0, keys.UserKeys) + + // Check templates, user templates should work fine. + _, err = auth.GetSSHConfig(context.Background(), "user", nil) + assert.NoError(t, err) + + _, err = auth.GetSSHConfig(context.Background(), "host", map[string]string{ + "Certificate": "ssh_host_ecdsa_key-cert.pub", + "Key": "ssh_host_ecdsa_key", + }) + assert.Error(t, err) +} + +func TestAuthority_initUserOnly(t *testing.T) { + auth := testAuthority(t, func(a *Authority) error { + a.config.SSH.HostKey = "" + return nil + }) + + // Check keys + keys, err := auth.GetSSHRoots(context.Background()) + assert.NoError(t, err) + assert.Len(t, 0, keys.HostKeys) + assert.Len(t, 1, keys.UserKeys) + + // Check templates, host templates should work fine. + _, err = auth.GetSSHConfig(context.Background(), "host", map[string]string{ + "Certificate": "ssh_host_ecdsa_key-cert.pub", + "Key": "ssh_host_ecdsa_key", + }) + assert.NoError(t, err) + + _, err = auth.GetSSHConfig(context.Background(), "user", nil) + assert.Error(t, err) +} + func TestAuthority_SignSSH(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.FatalError(t, err) @@ -153,6 +199,8 @@ func TestAuthority_SignSSH(t *testing.T) { }{ {"ok-user", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userTemplate, userOptions}}, want{CertType: ssh.UserCert}, false}, {"ok-host", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{hostTemplate, hostOptions}}, want{CertType: ssh.HostCert}, false}, + {"ok-user-only", fields{signer, nil}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{userTemplate, userOptions}}, want{CertType: ssh.UserCert}, false}, + {"ok-host-only", fields{nil, signer}, args{pub, provisioner.SignSSHOptions{}, []provisioner.SignOption{hostTemplate, hostOptions}}, want{CertType: ssh.HostCert}, false}, {"ok-opts-type-user", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{CertType: "user"}, []provisioner.SignOption{userTemplate}}, want{CertType: ssh.UserCert}, false}, {"ok-opts-type-host", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{CertType: "host"}, []provisioner.SignOption{hostTemplate}}, want{CertType: ssh.HostCert}, false}, {"ok-opts-principals", fields{signer, signer}, args{pub, provisioner.SignSSHOptions{CertType: "user", Principals: []string{"user"}}, []provisioner.SignOption{userTemplateWithUser}}, want{CertType: ssh.UserCert, Principals: []string{"user"}}, false}, From 42e263584893c89e18f6fc4c0ff0bd4448647203 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 28 Sep 2021 15:59:48 -0700 Subject: [PATCH 241/291] Add entry in changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b36b9b..cce6f095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Support for CloudKMS RSA-PSS signers without using templates. - Add flags to support individual passwords for the intermediate and SSH keys. - Global support for group admins in the OIDC provisioner. +- Support host-only or user-only SSH CA. ### Changed - Using go 1.17 for binaries ### Deprecated From afe1980d139a8f0a2b6212c69fae8fa4d94666c4 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 28 Sep 2021 16:15:23 -0700 Subject: [PATCH 242/291] changelog update for 0.17.4 --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba6998a8..a902ee2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased - 0.17.4] - DATE +## [Unreleased - 0.17.5] - DATE ### Added ### Changed ### Deprecated @@ -12,13 +12,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed ### Security +## [0.17.4] - 2021-09-28 +### Added +### Changed +### Deprecated +### Removed +### Fixed +- Support host-only or user-only SSH CA. +### Security + ## [0.17.3] - 2021-09-24 ### Added - go 1.17 to github action test matrix - Support for CloudKMS RSA-PSS signers without using templates. - Add flags to support individual passwords for the intermediate and SSH keys. - Global support for group admins in the OIDC provisioner. -- Support host-only or user-only SSH CA. ### Changed - Using go 1.17 for binaries ### Fixed From 9fb6df3abb4178e5b95b02daaee1e223c9425a37 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 28 Sep 2021 18:50:45 -0700 Subject: [PATCH 243/291] Fix ssh template variables when CA is injected using options. --- authority/authority.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index e26ce591..3f97ceab 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -361,8 +361,6 @@ func (a *Authority) init() error { // Append public key to list of host certs a.sshCAHostCerts = append(a.sshCAHostCerts, a.sshCAHostCertSignKey.PublicKey()) a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) - // Configure template variables. - tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() } if a.config.SSH.UserKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ @@ -389,8 +387,6 @@ func (a *Authority) init() error { // Append public key to list of user certs a.sshCAUserCerts = append(a.sshCAUserCerts, a.sshCAUserCertSignKey.PublicKey()) a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, a.sshCAUserCertSignKey.PublicKey()) - // Configure template variables. - tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() } // Append other public keys and add them to the template variables. @@ -400,14 +396,12 @@ func (a *Authority) init() error { case provisioner.SSHHostCert: if key.Federated { a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, publicKey) - tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, publicKey) } else { a.sshCAHostCerts = append(a.sshCAHostCerts, publicKey) } case provisioner.SSHUserCert: if key.Federated { a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, publicKey) - tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, publicKey) } else { a.sshCAUserCerts = append(a.sshCAUserCerts, publicKey) } @@ -417,6 +411,25 @@ func (a *Authority) init() error { } } + // Configure template variables. On the template variables HostFederatedKeys + // and UserFederatedKeys we will skip the actual CA that will be available + // in HostKey and UserKey. + // + // We cannot do it in the previous blocks because this configuration can be + // injected using options. + if a.sshCAHostCertSignKey != nil { + tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() + tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) + } else { + tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts...) + } + if a.sshCAUserCertSignKey != nil { + tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() + tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) + } else { + tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts...) + } + // Check if a KMS with decryption capability is required and available if a.requiresDecrypter() { if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { From 392a18465f319cc963509e97d423e0885521d110 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 5 Oct 2021 17:06:17 -0700 Subject: [PATCH 244/291] Add initial implementation of Azure Key Vault KMS. Fixes #462 --- go.mod | 7 +- go.sum | 33 ++++++ kms/azurekms/key_vault.go | 242 ++++++++++++++++++++++++++++++++++++++ kms/azurekms/signer.go | 151 ++++++++++++++++++++++++ kms/azurekms/utils.go | 57 +++++++++ 5 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 kms/azurekms/key_vault.go create mode 100644 kms/azurekms/signer.go create mode 100644 kms/azurekms/utils.go diff --git a/go.mod b/go.mod index 04af53fc..ddf51740 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,14 @@ module github.com/smallstep/certificates -go 1.14 +go 1.15 require ( cloud.google.com/go v0.83.0 + github.com/Azure/azure-sdk-for-go v58.0.0+incompatible + github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 + github.com/Azure/go-autorest/autorest/date v0.3.0 + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 github.com/ThalesIgnite/crypto11 v1.2.4 github.com/aws/aws-sdk-go v1.30.29 diff --git a/go.sum b/go.sum index a6ff0f08..5de2ab46 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,31 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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= +github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.17 h1:2zCdHwNgRH+St1J+ZMf66xI8aLr/5KMy+wWLH97zwYM= +github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.11 h1:L4/pmq7poLdsy41Bj1FayKvBhayuWRYkx9HU5i4Ybl0= +github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -146,6 +171,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -163,6 +191,8 @@ 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/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= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -375,6 +405,7 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -559,6 +590,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go new file mode 100644 index 00000000..1b133c2e --- /dev/null +++ b/kms/azurekms/key_vault.go @@ -0,0 +1,242 @@ +package azurekms + +import ( + "context" + "crypto" + "net/url" + "time" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/Azure/go-autorest/autorest/azure/auth" + "github.com/Azure/go-autorest/autorest/date" + "github.com/pkg/errors" + "github.com/smallstep/certificates/kms/apiv1" + "github.com/smallstep/certificates/kms/uri" +) + +func init() { + apiv1.Register(apiv1.CloudKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + return New(ctx, opts) + }) +} + +// Scheme is the scheme used for Azure Key Vault uris. +const Scheme = "azurekms" + +var ( + valueTrue = true + value2048 int32 = 2048 + value3072 int32 = 3072 + value4096 int32 = 4096 +) + +var now = func() time.Time { + return time.Now().UTC() +} + +type keyType struct { + Kty keyvault.JSONWebKeyType + Curve keyvault.JSONWebKeyCurveName + KeySize int +} + +func (k keyType) KeyType(pl apiv1.ProtectionLevel) keyvault.JSONWebKeyType { + switch k.Kty { + case keyvault.EC: + if pl == apiv1.HSM { + return keyvault.ECHSM + } + return k.Kty + case keyvault.RSA: + if pl == apiv1.HSM { + return keyvault.RSAHSM + } + return k.Kty + default: + return "" + } +} + +var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]keyType{ + apiv1.UnspecifiedSignAlgorithm: { + Kty: keyvault.EC, + Curve: keyvault.P256, + }, + apiv1.SHA256WithRSA: { + Kty: keyvault.RSA, + }, + apiv1.SHA384WithRSA: { + Kty: keyvault.RSA, + }, + apiv1.SHA512WithRSA: { + Kty: keyvault.RSA, + }, + apiv1.SHA256WithRSAPSS: { + Kty: keyvault.RSA, + }, + apiv1.SHA384WithRSAPSS: { + Kty: keyvault.RSA, + }, + apiv1.SHA512WithRSAPSS: { + Kty: keyvault.RSA, + }, + apiv1.ECDSAWithSHA256: { + Kty: keyvault.EC, + Curve: keyvault.P256, + }, + apiv1.ECDSAWithSHA384: { + Kty: keyvault.EC, + Curve: keyvault.P384, + }, + apiv1.ECDSAWithSHA512: { + Kty: keyvault.EC, + Curve: keyvault.P521, + }, +} + +// vaultResource is that the client will use as audience. +const vaultResource = "https://vault.azure.net" + +// KeyVaultClient is the interface implemented by keyvault.BaseClient. It it +// will be used for testing purposes. +type KeyVaultClient interface { + GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (keyvault.KeyBundle, error) + CreateKey(ctx context.Context, vaultBaseURL string, keyName string, parameters keyvault.KeyCreateParameters) (keyvault.KeyBundle, error) + Sign(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string, parameters keyvault.KeySignParameters) (keyvault.KeyOperationResult, error) +} + +// KeyVault implements a KMS using Azure Key Vault. +// +// TODO(mariano): The implementation is using /services/keyvault/v7.1/keyvault +// package, at some point Azure might create a keyvault client with all the +// functionality in /sdk/keyvault, we should migrate to that once available. +type KeyVault struct { + baseClient KeyVaultClient +} + +// New initializes a new KMS implemented using Azure Key Vault. +func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { + // Attempt to authorize with the following methods: + // 1. Environment variables. + // - Client credentials + // - Client certificate + // - Username and password + // - MSI + // 2. Using Azure CLI 2.0 on local development. + authorizer, err := auth.NewAuthorizerFromEnvironmentWithResource(vaultResource) + if err != nil { + authorizer, err = auth.NewAuthorizerFromCLIWithResource(vaultResource) + if err != nil { + return nil, errors.Wrap(err, "error getting authorizer for key vault") + } + } + + baseClient := keyvault.New() + baseClient.Authorizer = authorizer + + return &KeyVault{ + baseClient: &baseClient, + }, nil +} + +// GetPublicKey loads a public key from Azure Key Vault by its resource name. +func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) { + switch { + case req.Name == "": + return nil, errors.New("getPublicKeyRequest 'name' cannot be empty") + } + + vault, name, version, err := parseKeyName(req.Name) + if err != nil { + return nil, err + } + + ctx, cancel := defaultContext() + defer cancel() + + resp, err := k.baseClient.GetKey(ctx, vaultBaseURL(vault), name, version) + if err != nil { + return nil, errors.Wrap(err, "keyVault GetKey failed") + } + + return convertKey(resp.Key) +} + +// CreateKey creates a asymmetric key in Azure Key Vault. +func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) { + vault, name, _, err := parseKeyName(req.Name) + if err != nil { + return nil, err + } + + kt, ok := signatureAlgorithmMapping[req.SignatureAlgorithm] + if !ok { + return nil, errors.Errorf("keyVault does not support signature algorithm '%s'", req.SignatureAlgorithm) + } + var keySize *int32 + if kt.Kty == keyvault.RSA || kt.Kty == keyvault.RSAHSM { + switch req.Bits { + case 2048: + keySize = &value2048 + case 0, 3072: + keySize = &value3072 + case 4096: + keySize = &value4096 + default: + return nil, errors.Errorf("keyVault does not support key size %d", req.Bits) + } + } + + created := date.UnixTime(now()) + + ctx, cancel := defaultContext() + defer cancel() + + resp, err := k.baseClient.CreateKey(ctx, vaultBaseURL(vault), name, keyvault.KeyCreateParameters{ + Kty: kt.KeyType(req.ProtectionLevel), + KeySize: keySize, + Curve: kt.Curve, + KeyOps: &[]keyvault.JSONWebKeyOperation{ + keyvault.Sign, keyvault.Verify, + }, + KeyAttributes: &keyvault.KeyAttributes{ + Enabled: &valueTrue, + Created: &created, + NotBefore: &created, + }, + }) + if err != nil { + return nil, errors.Wrap(err, "keyVault CreateKey failed") + } + + keyURI := uri.New("azurekms", url.Values{ + "vault": []string{vault}, + "id": []string{name}, + }).String() + + publicKey, err := convertKey(resp.Key) + if err != nil { + return nil, err + } + + return &apiv1.CreateKeyResponse{ + Name: keyURI, + PublicKey: publicKey, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: keyURI, + }, + }, nil +} + +// CreateSigner returns a crypto.Signer from a previously created asymmetric key. +func (k *KeyVault) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) { + if req.SigningKey == "" { + return nil, errors.New("createSignerRequest 'signingKey' cannot be empty") + } + return NewSigner(k.baseClient, req.SigningKey) +} + +// Close closes the client connection to the Azure Key Vault. This is a noop. +func (k *KeyVault) Close() error { + return nil +} diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go new file mode 100644 index 00000000..217c6258 --- /dev/null +++ b/kms/azurekms/signer.go @@ -0,0 +1,151 @@ +package azurekms + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "encoding/base64" + "io" + "math/big" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/pkg/errors" + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/cryptobyte/asn1" +) + +// Signer implements a crypto.Signer using the AWS KMS. +type Signer struct { + client KeyVaultClient + vaultBaseURL string + name string + version string + publicKey crypto.PublicKey +} + +// NewSigner creates a new signer using a key in the AWS KMS. +func NewSigner(client KeyVaultClient, signingKey string) (*Signer, error) { + vault, name, version, err := parseKeyName(signingKey) + if err != nil { + return nil, err + } + + // Make sure that the key exists. + signer := &Signer{ + client: client, + vaultBaseURL: vaultBaseURL(vault), + name: name, + version: version, + } + if err := signer.preloadKey(); err != nil { + return nil, err + } + + return signer, nil +} + +func (s *Signer) preloadKey() error { + ctx, cancel := defaultContext() + defer cancel() + + resp, err := s.client.GetKey(ctx, s.vaultBaseURL, s.name, s.version) + if err != nil { + return errors.Wrap(err, "keyVault GetKey failed") + } + + s.publicKey, err = convertKey(resp.Key) + return err +} + +// Public returns the public key of this signer or an error. +func (s *Signer) Public() crypto.PublicKey { + return s.publicKey +} + +// Sign signs digest with the private key stored in the AWS KMS. +func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + alg, err := getSigningAlgorithm(s.Public(), opts) + if err != nil { + return nil, err + } + + ctx, cancel := defaultContext() + defer cancel() + + b64 := base64.RawURLEncoding.EncodeToString(digest) + + resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{ + Algorithm: alg, + Value: &b64, + }) + if err != nil { + return nil, errors.Wrap(err, "keyVault Sign failed") + } + + sig, err := base64.RawURLEncoding.DecodeString(*resp.Result) + if err != nil { + return nil, errors.Wrap(err, "error decoding keyVault Sign result") + } + + var octetSize int + switch alg { + case keyvault.ES256: + octetSize = 32 // 256-bit, concat(R,S) = 64 bytes + case keyvault.ES384: + octetSize = 48 // 384-bit, concat(R,S) = 96 bytes + case keyvault.ES512: + octetSize = 66 // 528-bit, concat(R,S) = 132 bytes + default: + return sig, nil + } + + // Convert to ans1 + if len(sig) != octetSize*2 { + return nil, errors.Errorf("keyVault Sign failed: unexpected signature length") + } + var b cryptobyte.Builder + b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1BigInt(new(big.Int).SetBytes(sig[:octetSize])) // R + b.AddASN1BigInt(new(big.Int).SetBytes(sig[octetSize:])) // S + }) + return b.Bytes() +} + +func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) { + switch key.(type) { + case *rsa.PublicKey: + _, isPSS := opts.(*rsa.PSSOptions) + switch h := opts.HashFunc(); h { + case crypto.SHA256: + if isPSS { + return keyvault.PS256, nil + } + return keyvault.RS256, nil + case crypto.SHA384: + if isPSS { + return keyvault.PS384, nil + } + return keyvault.RS384, nil + case crypto.SHA512: + if isPSS { + return keyvault.PS512, nil + } + return keyvault.RS512, nil + default: + return "", errors.Errorf("unsupported hash function %v", h) + } + case *ecdsa.PublicKey: + switch h := opts.HashFunc(); h { + case crypto.SHA256: + return keyvault.ES256, nil + case crypto.SHA384: + return keyvault.ES384, nil + case crypto.SHA512: + return keyvault.ES512, nil + default: + return "", errors.Errorf("unsupported hash function %v", h) + } + default: + return "", errors.Errorf("unsupported key type %T", key) + } +} diff --git a/kms/azurekms/utils.go b/kms/azurekms/utils.go new file mode 100644 index 00000000..2d99f6d3 --- /dev/null +++ b/kms/azurekms/utils.go @@ -0,0 +1,57 @@ +package azurekms + +import ( + "context" + "crypto" + "encoding/json" + "time" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/pkg/errors" + "github.com/smallstep/certificates/kms/uri" + "go.step.sm/crypto/jose" +) + +// defaultContext returns the default context used in requests to azure. +func defaultContext() (context.Context, context.CancelFunc) { + return context.WithTimeout(context.Background(), 15*time.Second) +} + +// parseKeyName returns the key vault, name and version for urls like +// azurekms:vault=key-vault;id=key-name?version=key-version. If version is not +// passed the latest version will be used. +func parseKeyName(rawURI string) (vault, name, version string, err error) { + var u *uri.URI + + u, err = uri.ParseWithScheme("azurekms", rawURI) + if err != nil { + return + } + + if vault = u.Get("vault"); vault == "" { + err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI) + return + } + if name = u.Get("id"); name == "" { + err = errors.Errorf("key uri %s is not valid: id is missing", rawURI) + return + } + version = u.Get("version") + return +} + +func vaultBaseURL(vault string) string { + return "https://" + vault + ".vault.azure.net/" +} + +func convertKey(key *keyvault.JSONWebKey) (crypto.PublicKey, error) { + b, err := json.Marshal(key) + if err != nil { + return nil, errors.Wrap(err, "error marshalling key") + } + var jwk jose.JSONWebKey + if err := jwk.UnmarshalJSON(b); err != nil { + return nil, errors.Wrap(err, "error unmarshalling key") + } + return jwk.Key, nil +} From d02cb1c8691f8b18929caa0bd679e381e24974a3 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 5 Oct 2021 17:09:40 -0700 Subject: [PATCH 245/291] Enable azurekms. --- cmd/step-ca/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index aaf37df2..cc2fffc9 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -26,6 +26,7 @@ import ( // Enabled kms interfaces. _ "github.com/smallstep/certificates/kms/awskms" + _ "github.com/smallstep/certificates/kms/azurekms" _ "github.com/smallstep/certificates/kms/cloudkms" _ "github.com/smallstep/certificates/kms/softkms" _ "github.com/smallstep/certificates/kms/sshagentkms" From 97d08a1b61ec56e5aed80ad65985bc0defabdde8 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 5 Oct 2021 17:11:23 -0700 Subject: [PATCH 246/291] Fix typos. --- kms/azurekms/utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/azurekms/utils.go b/kms/azurekms/utils.go index 2d99f6d3..46263726 100644 --- a/kms/azurekms/utils.go +++ b/kms/azurekms/utils.go @@ -47,11 +47,11 @@ func vaultBaseURL(vault string) string { func convertKey(key *keyvault.JSONWebKey) (crypto.PublicKey, error) { b, err := json.Marshal(key) if err != nil { - return nil, errors.Wrap(err, "error marshalling key") + return nil, errors.Wrap(err, "error marshaling key") } var jwk jose.JSONWebKey if err := jwk.UnmarshalJSON(b); err != nil { - return nil, errors.Wrap(err, "error unmarshalling key") + return nil, errors.Wrap(err, "error unmarshaling key") } return jwk.Key, nil } From 63891003259ca1b3a85f7419187671e305f25d11 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 5 Oct 2021 20:35:52 -0700 Subject: [PATCH 247/291] Add unit tests for azurekms. --- go.mod | 2 +- go.sum | 3 +- .../internal/mock/key_vault_client.go | 80 +++ kms/azurekms/key_vault.go | 17 +- kms/azurekms/key_vault_test.go | 482 ++++++++++++++++++ kms/azurekms/signer.go | 15 +- kms/azurekms/signer_test.go | 329 ++++++++++++ 7 files changed, 920 insertions(+), 8 deletions(-) create mode 100644 kms/azurekms/internal/mock/key_vault_client.go create mode 100644 kms/azurekms/key_vault_test.go create mode 100644 kms/azurekms/signer_test.go diff --git a/go.mod b/go.mod index ddf51740..43590e4a 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/go-chi/chi v4.0.2+incompatible github.com/go-kit/kit v0.10.0 // indirect github.com/go-piv/piv-go v1.7.0 - github.com/golang/mock v1.5.0 + github.com/golang/mock v1.6.0 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 diff --git a/go.sum b/go.sum index 5de2ab46..cf33febd 100644 --- a/go.sum +++ b/go.sum @@ -236,8 +236,9 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/kms/azurekms/internal/mock/key_vault_client.go b/kms/azurekms/internal/mock/key_vault_client.go new file mode 100644 index 00000000..42bd55fd --- /dev/null +++ b/kms/azurekms/internal/mock/key_vault_client.go @@ -0,0 +1,80 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/smallstep/certificates/kms/azurekms (interfaces: KeyVaultClient) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + keyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// KeyVaultClient is a mock of KeyVaultClient interface +type KeyVaultClient struct { + ctrl *gomock.Controller + recorder *KeyVaultClientMockRecorder +} + +// KeyVaultClientMockRecorder is the mock recorder for KeyVaultClient +type KeyVaultClientMockRecorder struct { + mock *KeyVaultClient +} + +// NewKeyVaultClient creates a new mock instance +func NewKeyVaultClient(ctrl *gomock.Controller) *KeyVaultClient { + mock := &KeyVaultClient{ctrl: ctrl} + mock.recorder = &KeyVaultClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *KeyVaultClient) EXPECT() *KeyVaultClientMockRecorder { + return m.recorder +} + +// CreateKey mocks base method +func (m *KeyVaultClient) CreateKey(arg0 context.Context, arg1, arg2 string, arg3 keyvault.KeyCreateParameters) (keyvault.KeyBundle, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateKey", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(keyvault.KeyBundle) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateKey indicates an expected call of CreateKey +func (mr *KeyVaultClientMockRecorder) CreateKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKey", reflect.TypeOf((*KeyVaultClient)(nil).CreateKey), arg0, arg1, arg2, arg3) +} + +// GetKey mocks base method +func (m *KeyVaultClient) GetKey(arg0 context.Context, arg1, arg2, arg3 string) (keyvault.KeyBundle, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetKey", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(keyvault.KeyBundle) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetKey indicates an expected call of GetKey +func (mr *KeyVaultClientMockRecorder) GetKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKey", reflect.TypeOf((*KeyVaultClient)(nil).GetKey), arg0, arg1, arg2, arg3) +} + +// Sign mocks base method +func (m *KeyVaultClient) Sign(arg0 context.Context, arg1, arg2, arg3 string, arg4 keyvault.KeySignParameters) (keyvault.KeyOperationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sign", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(keyvault.KeyOperationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Sign indicates an expected call of Sign +func (mr *KeyVaultClientMockRecorder) Sign(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*KeyVaultClient)(nil).Sign), arg0, arg1, arg2, arg3, arg4) +} diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 1b133c2e..dde368da 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -114,8 +114,7 @@ type KeyVault struct { baseClient KeyVaultClient } -// New initializes a new KMS implemented using Azure Key Vault. -func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { +var createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { // Attempt to authorize with the following methods: // 1. Environment variables. // - Client credentials @@ -133,9 +132,17 @@ func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { baseClient := keyvault.New() baseClient.Authorizer = authorizer + return &baseClient, nil +} +// New initializes a new KMS implemented using Azure Key Vault. +func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { + baseClient, err := createClient(ctx, opts) + if err != nil { + return nil, err + } return &KeyVault{ - baseClient: &baseClient, + baseClient: baseClient, }, nil } @@ -164,6 +171,10 @@ func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe // CreateKey creates a asymmetric key in Azure Key Vault. func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) { + if req.Name == "" { + return nil, errors.New("createKeyRequest 'name' cannot be empty") + } + vault, name, _, err := parseKeyName(req.Name) if err != nil { return nil, err diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go new file mode 100644 index 00000000..b5b3597d --- /dev/null +++ b/kms/azurekms/key_vault_test.go @@ -0,0 +1,482 @@ +//go:generate mockgen -package mock -mock_names=KeyVaultClient=KeyVaultClient -destination internal/mock/key_vault_client.go github.com/smallstep/certificates/kms/azurekms KeyVaultClient +package azurekms + +import ( + "context" + "crypto" + "encoding/json" + "fmt" + "reflect" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/Azure/go-autorest/autorest/date" + "github.com/golang/mock/gomock" + "github.com/smallstep/certificates/kms/apiv1" + "github.com/smallstep/certificates/kms/azurekms/internal/mock" + "go.step.sm/crypto/keyutil" + "gopkg.in/square/go-jose.v2" +) + +var errTest = fmt.Errorf("test error") + +func mockNow(t *testing.T) time.Time { + old := now + t0 := time.Unix(1234567890, 123).UTC() + now = func() time.Time { + return t0 + } + t.Cleanup(func() { + now = old + }) + return t0 +} + +func mockClient(t *testing.T) *mock.KeyVaultClient { + t.Helper() + ctrl := gomock.NewController(t) + t.Cleanup(func() { + ctrl.Finish() + }) + return mock.NewKeyVaultClient(ctrl) +} + +func mockCreateClient(t *testing.T, ctrl *gomock.Controller) { + t.Helper() + old := createClient + + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return mock.NewKeyVaultClient(ctrl), nil + } + + t.Cleanup(func() { + createClient = old + }) +} + +func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey { + t.Helper() + b, err := json.Marshal(&jose.JSONWebKey{ + Key: pub, + }) + if err != nil { + t.Fatal(err) + } + key := new(keyvault.JSONWebKey) + if err := json.Unmarshal(b, key); err != nil { + t.Fatal(err) + } + return key +} + +func TestNew(t *testing.T) { + ctrl := gomock.NewController(t) + mockCreateClient(t, ctrl) + + type args struct { + ctx context.Context + opts apiv1.Options + } + tests := []struct { + name string + args args + want *KeyVault + wantErr bool + }{ + {"ok", args{context.Background(), apiv1.Options{}}, &KeyVault{ + baseClient: mock.NewKeyVaultClient(ctrl), + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := New(tt.args.ctx, tt.args.opts) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("New() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKeyVault_GetPublicKey(t *testing.T) { + key, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatal(err) + } + pub := key.Public() + jwk := createJWK(t, pub) + + client := mockClient(t) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest) + + type fields struct { + baseClient KeyVaultClient + } + type args struct { + req *apiv1.GetPublicKeyRequest + } + tests := []struct { + name string + fields fields + args args + want crypto.PublicKey + wantErr bool + }{ + {"ok", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + }}, pub, false}, + {"ok with version", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key?version=my-version", + }}, pub, false}, + {"fail GetKey", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "azurekms:vault=my-vault;id=not-found?version=my-version", + }}, nil, true}, + {"fail vault", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "azurekms:vault=;id=not-found?version=my-version", + }}, nil, true}, + {"fail id", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "azurekms:vault=;id=?version=my-version", + }}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &KeyVault{ + baseClient: tt.fields.baseClient, + } + got, err := k.GetPublicKey(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("KeyVault.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KeyVault.GetPublicKey() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKeyVault_CreateKey(t *testing.T) { + ecKey, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatal(err) + } + rsaKey, err := keyutil.GenerateSigner("RSA", "", 2048) + if err != nil { + t.Fatal(err) + } + ecPub := ecKey.Public() + rsaPub := rsaKey.Public() + ecJWK := createJWK(t, ecPub) + rsaJWK := createJWK(t, rsaPub) + + t0 := date.UnixTime(mockNow(t)) + client := mockClient(t) + + expects := []struct { + Name string + Kty keyvault.JSONWebKeyType + KeySize *int32 + Curve keyvault.JSONWebKeyCurveName + Key *keyvault.JSONWebKey + }{ + {"P-256", keyvault.EC, nil, keyvault.P256, ecJWK}, + {"P-256 HSM", keyvault.ECHSM, nil, keyvault.P256, ecJWK}, + {"P-256 Default", keyvault.EC, nil, keyvault.P256, ecJWK}, + {"P-384", keyvault.EC, nil, keyvault.P384, ecJWK}, + {"P-521", keyvault.EC, nil, keyvault.P521, ecJWK}, + {"RSA 0", keyvault.RSA, &value3072, "", rsaJWK}, + {"RSA 0 HSM", keyvault.RSAHSM, &value3072, "", rsaJWK}, + {"RSA 2048", keyvault.RSA, &value2048, "", rsaJWK}, + {"RSA 3072", keyvault.RSA, &value3072, "", rsaJWK}, + {"RSA 4096", keyvault.RSA, &value4096, "", rsaJWK}, + } + + for _, e := range expects { + client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", keyvault.KeyCreateParameters{ + Kty: e.Kty, + KeySize: e.KeySize, + Curve: e.Curve, + KeyOps: &[]keyvault.JSONWebKeyOperation{ + keyvault.Sign, keyvault.Verify, + }, + KeyAttributes: &keyvault.KeyAttributes{ + Enabled: &valueTrue, + Created: &t0, + NotBefore: &t0, + }, + }).Return(keyvault.KeyBundle{ + Key: e.Key, + }, nil) + } + client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{}, errTest) + client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{ + Key: nil, + }, nil) + + type fields struct { + baseClient KeyVaultClient + } + type args struct { + req *apiv1.CreateKeyRequest + } + tests := []struct { + name string + fields fields + args args + want *apiv1.CreateKeyResponse + wantErr bool + }{ + {"ok P-256", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + SignatureAlgorithm: apiv1.ECDSAWithSHA256, + ProtectionLevel: apiv1.Software, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok P-256 HSM", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + SignatureAlgorithm: apiv1.ECDSAWithSHA256, + ProtectionLevel: apiv1.HSM, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok P-256 Default", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok P-384", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + SignatureAlgorithm: apiv1.ECDSAWithSHA384, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok P-521", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + SignatureAlgorithm: apiv1.ECDSAWithSHA512, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok RSA 0", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + Bits: 0, + SignatureAlgorithm: apiv1.SHA256WithRSA, + ProtectionLevel: apiv1.Software, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok RSA 0 HSM", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + Bits: 0, + SignatureAlgorithm: apiv1.SHA256WithRSAPSS, + ProtectionLevel: apiv1.HSM, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok RSA 2048", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + Bits: 2048, + SignatureAlgorithm: apiv1.SHA384WithRSA, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok RSA 3072", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + Bits: 3072, + SignatureAlgorithm: apiv1.SHA512WithRSA, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"ok RSA 4096", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=my-key", + Bits: 4096, + SignatureAlgorithm: apiv1.SHA512WithRSAPSS, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:id=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:id=my-key;vault=my-vault", + }, + }, false}, + {"fail createKey", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=not-found", + SignatureAlgorithm: apiv1.ECDSAWithSHA256, + }}, nil, true}, + {"fail convertKey", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=not-found", + SignatureAlgorithm: apiv1.ECDSAWithSHA256, + }}, nil, true}, + {"fail name", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "", + }}, nil, true}, + {"fail vault", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=;id=not-found?version=my-version", + }}, nil, true}, + {"fail id", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=?version=my-version", + }}, nil, true}, + {"fail SignatureAlgorithm", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=not-found", + SignatureAlgorithm: apiv1.PureEd25519, + }}, nil, true}, + {"fail bit size", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;id=not-found", + SignatureAlgorithm: apiv1.SHA384WithRSAPSS, + Bits: 1024, + }}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &KeyVault{ + baseClient: tt.fields.baseClient, + } + got, err := k.CreateKey(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("KeyVault.CreateKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KeyVault.CreateKey() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKeyVault_CreateSigner(t *testing.T) { + key, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatal(err) + } + pub := key.Public() + jwk := createJWK(t, pub) + + client := mockClient(t) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest) + + type fields struct { + baseClient KeyVaultClient + } + type args struct { + req *apiv1.CreateSignerRequest + } + tests := []struct { + name string + fields fields + args args + want crypto.Signer + wantErr bool + }{ + {"ok", fields{client}, args{&apiv1.CreateSignerRequest{ + SigningKey: "azurekms:vault=my-vault;id=my-key", + }}, &Signer{ + client: client, + vaultBaseURL: "https://my-vault.vault.azure.net/", + name: "my-key", + version: "", + publicKey: pub, + }, false}, + {"ok with version", fields{client}, args{&apiv1.CreateSignerRequest{ + SigningKey: "azurekms:vault=my-vault;id=my-key;version=my-version", + }}, &Signer{ + client: client, + vaultBaseURL: "https://my-vault.vault.azure.net/", + name: "my-key", + version: "my-version", + publicKey: pub, + }, false}, + {"fail GetKey", fields{client}, args{&apiv1.CreateSignerRequest{ + SigningKey: "azurekms:vault=my-vault;id=not-found;version=my-version", + }}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &KeyVault{ + baseClient: tt.fields.baseClient, + } + got, err := k.CreateSigner(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("KeyVault.CreateSigner() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KeyVault.CreateSigner() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKeyVault_Close(t *testing.T) { + client := mockClient(t) + type fields struct { + baseClient KeyVaultClient + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {"ok", fields{client}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &KeyVault{ + baseClient: tt.fields.baseClient, + } + if err := k.Close(); (err != nil) != tt.wantErr { + t.Errorf("KeyVault.Close() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index 217c6258..cb844bdf 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -24,7 +24,7 @@ type Signer struct { } // NewSigner creates a new signer using a key in the AWS KMS. -func NewSigner(client KeyVaultClient, signingKey string) (*Signer, error) { +func NewSigner(client KeyVaultClient, signingKey string) (crypto.Signer, error) { vault, name, version, err := parseKeyName(signingKey) if err != nil { return nil, err @@ -114,8 +114,17 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) { switch key.(type) { case *rsa.PublicKey: - _, isPSS := opts.(*rsa.PSSOptions) - switch h := opts.HashFunc(); h { + hashFunc := opts.HashFunc() + pss, isPSS := opts.(*rsa.PSSOptions) + // Random salt lengths are not supported + if isPSS && + pss.SaltLength != rsa.PSSSaltLengthAuto && + pss.SaltLength != rsa.PSSSaltLengthEqualsHash && + pss.SaltLength != hashFunc.Size() { + return "", errors.Errorf("unsupported RSA-PSS salt length %d", pss.SaltLength) + } + + switch h := hashFunc; h { case crypto.SHA256: if isPSS { return keyvault.PS256, nil diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go new file mode 100644 index 00000000..798d69f9 --- /dev/null +++ b/kms/azurekms/signer_test.go @@ -0,0 +1,329 @@ +package azurekms + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "io" + "reflect" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/golang/mock/gomock" + "go.step.sm/crypto/keyutil" + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/cryptobyte/asn1" +) + +func TestNewSigner(t *testing.T) { + key, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatal(err) + } + pub := key.Public() + jwk := createJWK(t, pub) + + client := mockClient(t) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest) + + type args struct { + client KeyVaultClient + signingKey string + } + tests := []struct { + name string + args args + want crypto.Signer + wantErr bool + }{ + {"ok", args{client, "azurekms:vault=my-vault;id=my-key"}, &Signer{ + client: client, + vaultBaseURL: "https://my-vault.vault.azure.net/", + name: "my-key", + version: "", + publicKey: pub, + }, false}, + {"ok with version", args{client, "azurekms:id=my-key;vault=my-vault?version=my-version"}, &Signer{ + client: client, + vaultBaseURL: "https://my-vault.vault.azure.net/", + name: "my-key", + version: "my-version", + publicKey: pub, + }, false}, + {"fail GetKey", args{client, "azurekms:id=not-found;vault=my-vault?version=my-version"}, nil, true}, + {"fail vault", args{client, "azurekms:id=not-found;vault="}, nil, true}, + {"fail id", args{client, "azurekms:id=;vault=my-vault?version=my-version"}, nil, true}, + {"fail scheme", args{client, "kms:id=not-found;vault=my-vault?version=my-version"}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewSigner(tt.args.client, tt.args.signingKey) + if (err != nil) != tt.wantErr { + t.Errorf("NewSigner() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewSigner() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSigner_Public(t *testing.T) { + key, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatal(err) + } + pub := key.Public() + + type fields struct { + publicKey crypto.PublicKey + } + tests := []struct { + name string + fields fields + want crypto.PublicKey + }{ + {"ok", fields{pub}, pub}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Signer{ + publicKey: tt.fields.publicKey, + } + if got := s.Public(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Signer.Public() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSigner_Sign(t *testing.T) { + sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) { + key, err := keyutil.GenerateSigner(kty, crv, bits) + if err != nil { + t.Fatal(err) + } + h := opts.HashFunc().New() + h.Write([]byte("random-data")) + sum := h.Sum(nil) + + var sig, resultSig []byte + if priv, ok := key.(*ecdsa.PrivateKey); ok { + r, s, err := ecdsa.Sign(rand.Reader, priv, sum) + if err != nil { + t.Fatal(err) + } + curveBits := priv.Params().BitSize + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes++ + } + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + resultSig = append(rBytesPadded, sBytesPadded...) + + var b cryptobyte.Builder + b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1BigInt(r) + b.AddASN1BigInt(s) + }) + sig, err = b.Bytes() + if err != nil { + t.Fatal(err) + } + } else { + sig, err = key.Sign(rand.Reader, sum, opts) + if err != nil { + t.Fatal(err) + } + resultSig = sig + } + + return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig + } + + p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256) + p384, p384Digest, p386ResultSig, p384Sig := sign("EC", "P-384", 0, crypto.SHA384) + p521, p521Digest, p521ResultSig, p521Sig := sign("EC", "P-521", 0, crypto.SHA512) + rsaSHA256, rsaSHA256Digest, rsaSHA256ResultSig, rsaSHA256Sig := sign("RSA", "", 2048, crypto.SHA256) + rsaSHA384, rsaSHA384Digest, rsaSHA384ResultSig, rsaSHA384Sig := sign("RSA", "", 2048, crypto.SHA384) + rsaSHA512, rsaSHA512Digest, rsaSHA512ResultSig, rsaSHA512Sig := sign("RSA", "", 2048, crypto.SHA512) + rsaPSSSHA256, rsaPSSSHA256Digest, rsaPSSSHA256ResultSig, rsaPSSSHA256Sig := sign("RSA", "", 2048, &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA256, + }) + rsaPSSSHA384, rsaPSSSHA384Digest, rsaPSSSHA384ResultSig, rsaPSSSHA384Sig := sign("RSA", "", 2048, &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA512, + }) + rsaPSSSHA512, rsaPSSSHA512Digest, rsaPSSSHA512ResultSig, rsaPSSSHA512Sig := sign("RSA", "", 2048, &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA512, + }) + + ed25519Key, err := keyutil.GenerateSigner("OKP", "Ed25519", 0) + if err != nil { + t.Fatal(err) + } + + client := mockClient(t) + expects := []struct { + name string + keyVersion string + alg keyvault.JSONWebKeySignatureAlgorithm + digest []byte + result keyvault.KeyOperationResult + err error + }{ + {"P-256", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{ + Result: &p256ResultSig, + }, nil}, + {"P-384", "my-version", keyvault.ES384, p384Digest, keyvault.KeyOperationResult{ + Result: &p386ResultSig, + }, nil}, + {"P-521", "my-version", keyvault.ES512, p521Digest, keyvault.KeyOperationResult{ + Result: &p521ResultSig, + }, nil}, + {"RSA SHA256", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{ + Result: &rsaSHA256ResultSig, + }, nil}, + {"RSA SHA384", "", keyvault.RS384, rsaSHA384Digest, keyvault.KeyOperationResult{ + Result: &rsaSHA384ResultSig, + }, nil}, + {"RSA SHA512", "", keyvault.RS512, rsaSHA512Digest, keyvault.KeyOperationResult{ + Result: &rsaSHA512ResultSig, + }, nil}, + {"RSA-PSS SHA256", "", keyvault.PS256, rsaPSSSHA256Digest, keyvault.KeyOperationResult{ + Result: &rsaPSSSHA256ResultSig, + }, nil}, + {"RSA-PSS SHA384", "", keyvault.PS384, rsaPSSSHA384Digest, keyvault.KeyOperationResult{ + Result: &rsaPSSSHA384ResultSig, + }, nil}, + {"RSA-PSS SHA512", "", keyvault.PS512, rsaPSSSHA512Digest, keyvault.KeyOperationResult{ + Result: &rsaPSSSHA512ResultSig, + }, nil}, + // Errors + {"fail Sign", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{}, errTest}, + {"fail sign length", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{ + Result: &rsaSHA256ResultSig, + }, nil}, + } + for _, e := range expects { + value := base64.RawURLEncoding.EncodeToString(e.digest) + client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{ + Algorithm: e.alg, + Value: &value, + }).Return(e.result, e.err) + } + + type fields struct { + client KeyVaultClient + vaultBaseURL string + name string + version string + publicKey crypto.PublicKey + } + type args struct { + rand io.Reader + digest []byte + opts crypto.SignerOpts + } + tests := []struct { + name string + fields fields + args args + want []byte + wantErr bool + }{ + {"ok P-256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ + rand.Reader, p256Digest[:], crypto.SHA256, + }, p256Sig, false}, + {"ok P-384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p384}, args{ + rand.Reader, p384Digest[:], crypto.SHA384, + }, p384Sig, false}, + {"ok P-521", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p521}, args{ + rand.Reader, p521Digest[:], crypto.SHA512, + }, p521Sig, false}, + {"ok RSA SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ + rand.Reader, rsaSHA256Digest[:], crypto.SHA256, + }, rsaSHA256Sig, false}, + {"ok RSA SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA384}, args{ + rand.Reader, rsaSHA384Digest[:], crypto.SHA384, + }, rsaSHA384Sig, false}, + {"ok RSA SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA512}, args{ + rand.Reader, rsaSHA512Digest[:], crypto.SHA512, + }, rsaSHA512Sig, false}, + {"ok RSA-PSS SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{ + rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA256, + }, + }, rsaPSSSHA256Sig, false}, + {"ok RSA-PSS SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA384}, args{ + rand.Reader, rsaPSSSHA384Digest[:], &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA384, + }, + }, rsaPSSSHA384Sig, false}, + {"ok RSA-PSS SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA512}, args{ + rand.Reader, rsaPSSSHA512Digest[:], &rsa.PSSOptions{ + SaltLength: 64, + Hash: crypto.SHA512, + }, + }, rsaPSSSHA512Sig, false}, + {"fail Sign", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ + rand.Reader, rsaSHA256Digest[:], crypto.SHA256, + }, nil, true}, + {"fail sign length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ + rand.Reader, p256Digest[:], crypto.SHA256, + }, nil, true}, + {"fail RSA-PSS salt length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{ + rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{ + SaltLength: 64, + Hash: crypto.SHA256, + }, + }, nil, true}, + {"fail RSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ + rand.Reader, rsaSHA256Digest[:], crypto.SHA1, + }, nil, true}, + {"fail ECDSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ + rand.Reader, p256Digest[:], crypto.MD5, + }, nil, true}, + {"fail Ed25519", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", ed25519Key}, args{ + rand.Reader, []byte("message"), crypto.Hash(0), + }, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Signer{ + client: tt.fields.client, + vaultBaseURL: tt.fields.vaultBaseURL, + name: tt.fields.name, + version: tt.fields.version, + publicKey: tt.fields.publicKey, + } + got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts) + if (err != nil) != tt.wantErr { + t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Signer.Sign() = %v, want %v", got, tt.want) + } + }) + } +} From 56c3559e52f9135c43e22756e66ba46eb3b9290b Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 5 Oct 2021 20:41:55 -0700 Subject: [PATCH 248/291] Add some extra coverage. --- kms/azurekms/key_vault_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index b5b3597d..92048246 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -70,6 +70,13 @@ func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey { return key } +func Test_now(t *testing.T) { + t0 := now() + if loc := t0.Location(); loc != time.UTC { + t.Errorf("now() Location = %v, want %v", loc, time.UTC) + } +} + func TestNew(t *testing.T) { ctrl := gomock.NewController(t) mockCreateClient(t, ctrl) @@ -439,6 +446,9 @@ func TestKeyVault_CreateSigner(t *testing.T) { {"fail GetKey", fields{client}, args{&apiv1.CreateSignerRequest{ SigningKey: "azurekms:vault=my-vault;id=not-found;version=my-version", }}, nil, true}, + {"fail SigningKey", fields{client}, args{&apiv1.CreateSignerRequest{ + SigningKey: "", + }}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 656099c4f0c3f937894dd9222c0dc7cec44badac Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 6 Oct 2021 18:38:32 -0700 Subject: [PATCH 249/291] Add type for azurekms. --- kms/apiv1/options.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 7cc7f748..37c1fd4c 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -73,6 +73,8 @@ const ( YubiKey Type = "yubikey" // SSHAgentKMS is a KMS implementation using ssh-agent to access keys. SSHAgentKMS Type = "sshagentkms" + // AzureKMS is a KMS implementation using Azure Key Vault. + AzureKMS Type = "azurekms" ) // Options are the KMS options. They represent the kms object in the ca.json. @@ -118,8 +120,9 @@ func (o *Options) Validate() error { switch Type(strings.ToLower(o.Type)) { case DefaultKMS, SoftKMS: // Go crypto based kms. - case CloudKMS, AmazonKMS, SSHAgentKMS: // Cloud based kms. + case CloudKMS, AmazonKMS, AzureKMS: // Cloud based kms. case YubiKey, PKCS11: // Hardware based kms. + case SSHAgentKMS: // Others default: return errors.Errorf("unsupported kms type %s", o.Type) } From d2581489a38d18765611ef38bf0634bc949139e0 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 6 Oct 2021 18:39:12 -0700 Subject: [PATCH 250/291] Redefine uris and set proper type. URIs will now have the form: - azurekms:name=my-key;vault=my-vault - azurekms:name=my-key;vault=my-vault?version=my-version --- kms/azurekms/key_vault.go | 25 ++++++---- kms/azurekms/key_vault_test.go | 88 +++++++++++++++++----------------- kms/azurekms/signer_test.go | 12 ++--- kms/azurekms/utils.go | 42 ++++++++++++---- kms/azurekms/utils_test.go | 83 ++++++++++++++++++++++++++++++++ 5 files changed, 184 insertions(+), 66 deletions(-) create mode 100644 kms/azurekms/utils_test.go diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index dde368da..f7b00ec7 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -3,7 +3,7 @@ package azurekms import ( "context" "crypto" - "net/url" + "regexp" "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" @@ -11,11 +11,10 @@ import ( "github.com/Azure/go-autorest/autorest/date" "github.com/pkg/errors" "github.com/smallstep/certificates/kms/apiv1" - "github.com/smallstep/certificates/kms/uri" ) func init() { - apiv1.Register(apiv1.CloudKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + apiv1.Register(apiv1.AzureKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { return New(ctx, opts) }) } @@ -23,6 +22,10 @@ func init() { // Scheme is the scheme used for Azure Key Vault uris. const Scheme = "azurekms" +// keyIDRegexp is the regular experssion that Key Vault uses for on the kid. We +// can extract the vault, name and version of the key. +var keyIDRegexp = regexp.MustCompile("^https://([0-9a-zA-Z-]+).vault.azure.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$") + var ( valueTrue = true value2048 int32 = 2048 @@ -107,6 +110,16 @@ type KeyVaultClient interface { // KeyVault implements a KMS using Azure Key Vault. // +// The URI format used in Azure Key Vault is the following: +// +// - azurekms:name=key-name;vault=vault-name +// - azurekms:name=key-name;vault=vault-name?version=key-version +// +// The scheme is "azurekms"; "name" is the key name; "vault" is the key vault +// name where the key is located; "version" is an optional parameter that +// defines the version of they key, if version is not given, the latest one will +// be used. +// // TODO(mariano): The implementation is using /services/keyvault/v7.1/keyvault // package, at some point Azure might create a keyvault client with all the // functionality in /sdk/keyvault, we should migrate to that once available. @@ -220,16 +233,12 @@ func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo return nil, errors.Wrap(err, "keyVault CreateKey failed") } - keyURI := uri.New("azurekms", url.Values{ - "vault": []string{vault}, - "id": []string{name}, - }).String() - publicKey, err := convertKey(resp.Key) if err != nil { return nil, err } + keyURI := getKeyName(vault, name, resp) return &apiv1.CreateKeyResponse{ Name: keyURI, PublicKey: publicKey, diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index 92048246..1f09b2d5 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -140,19 +140,19 @@ func TestKeyVault_GetPublicKey(t *testing.T) { wantErr bool }{ {"ok", fields{client}, args{&apiv1.GetPublicKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", }}, pub, false}, {"ok with version", fields{client}, args{&apiv1.GetPublicKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key?version=my-version", + Name: "azurekms:vault=my-vault;name=my-key?version=my-version", }}, pub, false}, {"fail GetKey", fields{client}, args{&apiv1.GetPublicKeyRequest{ - Name: "azurekms:vault=my-vault;id=not-found?version=my-version", + Name: "azurekms:vault=my-vault;name=not-found?version=my-version", }}, nil, true}, {"fail vault", fields{client}, args{&apiv1.GetPublicKeyRequest{ - Name: "azurekms:vault=;id=not-found?version=my-version", + Name: "azurekms:vault=;name=not-found?version=my-version", }}, nil, true}, {"fail id", fields{client}, args{&apiv1.GetPublicKeyRequest{ - Name: "azurekms:vault=;id=?version=my-version", + Name: "azurekms:vault=;name=?version=my-version", }}, nil, true}, } for _, tt := range tests { @@ -244,136 +244,136 @@ func TestKeyVault_CreateKey(t *testing.T) { wantErr bool }{ {"ok P-256", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", SignatureAlgorithm: apiv1.ECDSAWithSHA256, ProtectionLevel: apiv1.Software, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: ecPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok P-256 HSM", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", SignatureAlgorithm: apiv1.ECDSAWithSHA256, ProtectionLevel: apiv1.HSM, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: ecPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok P-256 Default", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: ecPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok P-384", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", SignatureAlgorithm: apiv1.ECDSAWithSHA384, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: ecPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok P-521", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", SignatureAlgorithm: apiv1.ECDSAWithSHA512, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: ecPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok RSA 0", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", Bits: 0, SignatureAlgorithm: apiv1.SHA256WithRSA, ProtectionLevel: apiv1.Software, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: rsaPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok RSA 0 HSM", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", Bits: 0, SignatureAlgorithm: apiv1.SHA256WithRSAPSS, ProtectionLevel: apiv1.HSM, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: rsaPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok RSA 2048", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", Bits: 2048, SignatureAlgorithm: apiv1.SHA384WithRSA, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: rsaPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok RSA 3072", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", Bits: 3072, SignatureAlgorithm: apiv1.SHA512WithRSA, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: rsaPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"ok RSA 4096", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=my-key", + Name: "azurekms:vault=my-vault;name=my-key", Bits: 4096, SignatureAlgorithm: apiv1.SHA512WithRSAPSS, }}, &apiv1.CreateKeyResponse{ - Name: "azurekms:id=my-key;vault=my-vault", + Name: "azurekms:name=my-key;vault=my-vault", PublicKey: rsaPub, CreateSignerRequest: apiv1.CreateSignerRequest{ - SigningKey: "azurekms:id=my-key;vault=my-vault", + SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, {"fail createKey", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=not-found", + Name: "azurekms:vault=my-vault;name=not-found", SignatureAlgorithm: apiv1.ECDSAWithSHA256, }}, nil, true}, {"fail convertKey", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=not-found", + Name: "azurekms:vault=my-vault;name=not-found", SignatureAlgorithm: apiv1.ECDSAWithSHA256, }}, nil, true}, {"fail name", fields{client}, args{&apiv1.CreateKeyRequest{ Name: "", }}, nil, true}, {"fail vault", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=;id=not-found?version=my-version", + Name: "azurekms:vault=;name=not-found?version=my-version", }}, nil, true}, {"fail id", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=?version=my-version", + Name: "azurekms:vault=my-vault;name=?version=my-version", }}, nil, true}, {"fail SignatureAlgorithm", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=not-found", + Name: "azurekms:vault=my-vault;name=not-found", SignatureAlgorithm: apiv1.PureEd25519, }}, nil, true}, {"fail bit size", fields{client}, args{&apiv1.CreateKeyRequest{ - Name: "azurekms:vault=my-vault;id=not-found", + Name: "azurekms:vault=my-vault;name=not-found", SignatureAlgorithm: apiv1.SHA384WithRSAPSS, Bits: 1024, }}, nil, true}, @@ -426,7 +426,7 @@ func TestKeyVault_CreateSigner(t *testing.T) { wantErr bool }{ {"ok", fields{client}, args{&apiv1.CreateSignerRequest{ - SigningKey: "azurekms:vault=my-vault;id=my-key", + SigningKey: "azurekms:vault=my-vault;name=my-key", }}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", @@ -435,7 +435,7 @@ func TestKeyVault_CreateSigner(t *testing.T) { publicKey: pub, }, false}, {"ok with version", fields{client}, args{&apiv1.CreateSignerRequest{ - SigningKey: "azurekms:vault=my-vault;id=my-key;version=my-version", + SigningKey: "azurekms:vault=my-vault;name=my-key;version=my-version", }}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", @@ -444,7 +444,7 @@ func TestKeyVault_CreateSigner(t *testing.T) { publicKey: pub, }, false}, {"fail GetKey", fields{client}, args{&apiv1.CreateSignerRequest{ - SigningKey: "azurekms:vault=my-vault;id=not-found;version=my-version", + SigningKey: "azurekms:vault=my-vault;name=not-found;version=my-version", }}, nil, true}, {"fail SigningKey", fields{client}, args{&apiv1.CreateSignerRequest{ SigningKey: "", diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go index 798d69f9..389f65b3 100644 --- a/kms/azurekms/signer_test.go +++ b/kms/azurekms/signer_test.go @@ -44,24 +44,24 @@ func TestNewSigner(t *testing.T) { want crypto.Signer wantErr bool }{ - {"ok", args{client, "azurekms:vault=my-vault;id=my-key"}, &Signer{ + {"ok", args{client, "azurekms:vault=my-vault;name=my-key"}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", name: "my-key", version: "", publicKey: pub, }, false}, - {"ok with version", args{client, "azurekms:id=my-key;vault=my-vault?version=my-version"}, &Signer{ + {"ok with version", args{client, "azurekms:name=my-key;vault=my-vault?version=my-version"}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", name: "my-key", version: "my-version", publicKey: pub, }, false}, - {"fail GetKey", args{client, "azurekms:id=not-found;vault=my-vault?version=my-version"}, nil, true}, - {"fail vault", args{client, "azurekms:id=not-found;vault="}, nil, true}, - {"fail id", args{client, "azurekms:id=;vault=my-vault?version=my-version"}, nil, true}, - {"fail scheme", args{client, "kms:id=not-found;vault=my-vault?version=my-version"}, nil, true}, + {"fail GetKey", args{client, "azurekms:name=not-found;vault=my-vault?version=my-version"}, nil, true}, + {"fail vault", args{client, "azurekms:name=not-found;vault="}, nil, true}, + {"fail id", args{client, "azurekms:name=;vault=my-vault?version=my-version"}, nil, true}, + {"fail scheme", args{client, "kms:name=not-found;vault=my-vault?version=my-version"}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/kms/azurekms/utils.go b/kms/azurekms/utils.go index 46263726..6b6d1511 100644 --- a/kms/azurekms/utils.go +++ b/kms/azurekms/utils.go @@ -4,6 +4,7 @@ import ( "context" "crypto" "encoding/json" + "net/url" "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" @@ -17,9 +18,34 @@ func defaultContext() (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), 15*time.Second) } -// parseKeyName returns the key vault, name and version for urls like -// azurekms:vault=key-vault;id=key-name?version=key-version. If version is not -// passed the latest version will be used. +// getKeyName returns the uri of the key vault key. +func getKeyName(vault, name string, bundle keyvault.KeyBundle) string { + if bundle.Key != nil && bundle.Key.Kid != nil { + sm := keyIDRegexp.FindAllStringSubmatch(*bundle.Key.Kid, 1) + if len(sm) == 1 && len(sm[0]) == 4 { + m := sm[0] + u := uri.New(Scheme, url.Values{ + "vault": []string{m[1]}, + "name": []string{m[2]}, + }) + u.RawQuery = url.Values{"version": []string{m[3]}}.Encode() + return u.String() + } + } + // Fallback to URI without id. + return uri.New(Scheme, url.Values{ + "vault": []string{vault}, + "name": []string{name}, + }).String() +} + +// parseKeyName returns the key vault, name and version from URIs like: +// +// - azurekms:vault=key-vault;name=key-name +// - azurekms:vault=key-vault;name=key-name;id=key-id +// +// The key-id defines the version of the key, if it is not passed the latest +// version will be used. func parseKeyName(rawURI string) (vault, name, version string, err error) { var u *uri.URI @@ -27,13 +53,13 @@ func parseKeyName(rawURI string) (vault, name, version string, err error) { if err != nil { return } - - if vault = u.Get("vault"); vault == "" { - err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI) + if name = u.Get("name"); name == "" { + err = errors.Errorf("key uri %s is not valid: name is missing", rawURI) return } - if name = u.Get("id"); name == "" { - err = errors.Errorf("key uri %s is not valid: id is missing", rawURI) + if vault = u.Get("vault"); vault == "" { + err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI) + name = "" return } version = u.Get("version") diff --git a/kms/azurekms/utils_test.go b/kms/azurekms/utils_test.go new file mode 100644 index 00000000..53ec4057 --- /dev/null +++ b/kms/azurekms/utils_test.go @@ -0,0 +1,83 @@ +package azurekms + +import ( + "testing" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" +) + +func Test_getKeyName(t *testing.T) { + getBundle := func(kid string) keyvault.KeyBundle { + return keyvault.KeyBundle{ + Key: &keyvault.JSONWebKey{ + Kid: &kid, + }, + } + } + + type args struct { + vault string + name string + bundle keyvault.KeyBundle + } + tests := []struct { + name string + args args + want string + }{ + {"ok", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-key/my-version")}, "azurekms:name=my-key;vault=my-vault?version=my-version"}, + {"ok default", args{"my-vault", "my-key", getBundle("https://my-vault.foo.net/keys/my-key/my-version")}, "azurekms:name=my-key;vault=my-vault"}, + {"ok too short", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-version")}, "azurekms:name=my-key;vault=my-vault"}, + {"ok too long", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-key/my-version/sign")}, "azurekms:name=my-key;vault=my-vault"}, + {"ok nil key", args{"my-vault", "my-key", keyvault.KeyBundle{}}, "azurekms:name=my-key;vault=my-vault"}, + {"ok nil kid", args{"my-vault", "my-key", keyvault.KeyBundle{Key: &keyvault.JSONWebKey{}}}, "azurekms:name=my-key;vault=my-vault"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getKeyName(tt.args.vault, tt.args.name, tt.args.bundle); got != tt.want { + t.Errorf("getKeyName() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_parseKeyName(t *testing.T) { + type args struct { + rawURI string + } + tests := []struct { + name string + args args + wantVault string + wantName string + wantVersion string + wantErr bool + }{ + {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version"}, "my-vault", "my-key", "my-version", false}, + {"ok no version", args{"azurekms:name=my-key;vault=my-vault"}, "my-vault", "my-key", "", false}, + {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, "", "", "", true}, + {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, "", "", "", true}, + {"fail no name", args{"azurekms:vault=my-vault"}, "", "", "", true}, + {"fail empty name", args{"azurekms:name=;vault=my-vault"}, "", "", "", true}, + {"fail no vault", args{"azurekms:name=my-key"}, "", "", "", true}, + {"fail empty vault", args{"azurekms:name=my-key;vault="}, "", "", "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotVault, gotName, gotVersion, err := parseKeyName(tt.args.rawURI) + if (err != nil) != tt.wantErr { + t.Errorf("parseKeyName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotVault != tt.wantVault { + t.Errorf("parseKeyName() gotVault = %v, want %v", gotVault, tt.wantVault) + } + if gotName != tt.wantName { + t.Errorf("parseKeyName() gotName = %v, want %v", gotName, tt.wantName) + } + if gotVersion != tt.wantVersion { + t.Errorf("parseKeyName() gotVersion = %v, want %v", gotVersion, tt.wantVersion) + } + }) + } +} From 505b1f3678365261439a10ae6cf0d89df5418fc6 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 6 Oct 2021 18:41:31 -0700 Subject: [PATCH 251/291] Add new test case with a version in the opaque string. --- kms/azurekms/utils_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/kms/azurekms/utils_test.go b/kms/azurekms/utils_test.go index 53ec4057..915ee74d 100644 --- a/kms/azurekms/utils_test.go +++ b/kms/azurekms/utils_test.go @@ -54,6 +54,7 @@ func Test_parseKeyName(t *testing.T) { wantErr bool }{ {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version"}, "my-vault", "my-key", "my-version", false}, + {"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version"}, "my-vault", "my-key", "my-version", false}, {"ok no version", args{"azurekms:name=my-key;vault=my-vault"}, "my-vault", "my-key", "", false}, {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, "", "", "", true}, {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, "", "", "", true}, From 08c9902f29a5c1ae78fd26181ce1e5bedbbe5771 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 6 Oct 2021 18:42:01 -0700 Subject: [PATCH 252/291] Add new alias in the kms package. --- kms/kms.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kms/kms.go b/kms/kms.go index 3eddca93..e1b76f1a 100644 --- a/kms/kms.go +++ b/kms/kms.go @@ -18,6 +18,9 @@ type KeyManager = apiv1.KeyManager // store x509.Certificates. type CertificateManager = apiv1.CertificateManager +// Options are the KMS options. They represent the kms object in the ca.json. +type Options = apiv1.Options + // New initializes a new KMS from the given type. func New(ctx context.Context, opts apiv1.Options) (KeyManager, error) { if err := opts.Validate(); err != nil { From 48549bf31719d289498790f179ef9dddf31072c8 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 11:09:32 -0700 Subject: [PATCH 253/291] Initialize windows terminal on all binaries. --- cmd/step-awskms-init/main.go | 4 ++++ cmd/step-ca/main.go | 5 +++++ cmd/step-cloudkms-init/main.go | 4 ++++ cmd/step-pkcs11-init/main.go | 4 ++++ cmd/step-yubikey-init/main.go | 4 ++++ go.mod | 2 +- go.sum | 8 ++++---- 7 files changed, 26 insertions(+), 5 deletions(-) diff --git a/cmd/step-awskms-init/main.go b/cmd/step-awskms-init/main.go index 0d686239..7b9dc7a3 100644 --- a/cmd/step-awskms-init/main.go +++ b/cmd/step-awskms-init/main.go @@ -31,6 +31,10 @@ func main() { flag.Usage = usage flag.Parse() + // Initialize windows terminal + ui.Init() + defer ui.Reset() + c, err := awskms.New(context.Background(), apiv1.Options{ Type: string(apiv1.AmazonKMS), Region: region, diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index aaf37df2..d9d17fed 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -22,6 +22,7 @@ import ( "go.step.sm/cli-utils/command" "go.step.sm/cli-utils/command/version" "go.step.sm/cli-utils/config" + "go.step.sm/cli-utils/ui" "go.step.sm/cli-utils/usage" // Enabled kms interfaces. @@ -90,6 +91,10 @@ Please send us a sentence or two, good or bad: **feedback@smallstep.com** or htt ` func main() { + // Initialize windows terminal + ui.Init() + defer ui.Reset() + // Override global framework components cli.VersionPrinter = func(c *cli.Context) { version.Command(c) diff --git a/cmd/step-cloudkms-init/main.go b/cmd/step-cloudkms-init/main.go index 69573c5d..b924f1a1 100644 --- a/cmd/step-cloudkms-init/main.go +++ b/cmd/step-cloudkms-init/main.go @@ -62,6 +62,10 @@ func main() { os.Exit(1) } + // Initialize windows terminal + ui.Init() + defer ui.Reset() + c, err := cloudkms.New(context.Background(), apiv1.Options{ Type: string(apiv1.CloudKMS), CredentialsFile: credentialsFile, diff --git a/cmd/step-pkcs11-init/main.go b/cmd/step-pkcs11-init/main.go index 34f9f8f8..5d9ba3e1 100644 --- a/cmd/step-pkcs11-init/main.go +++ b/cmd/step-pkcs11-init/main.go @@ -129,6 +129,10 @@ func main() { fatal(err) } + // Initialize windows terminal + ui.Init() + defer ui.Reset() + if u.Get("pin-value") == "" && u.Get("pin-source") == "" && c.Pin == "" { pin, err := ui.PromptPassword("What is the PKCS#11 PIN?") if err != nil { diff --git a/cmd/step-yubikey-init/main.go b/cmd/step-yubikey-init/main.go index df7b9ea8..cc55614a 100644 --- a/cmd/step-yubikey-init/main.go +++ b/cmd/step-yubikey-init/main.go @@ -87,6 +87,10 @@ func main() { fatal(err) } + // Initialize windows terminal + ui.Init() + defer ui.Reset() + pin, err := ui.PromptPassword("What is the YubiKey PIN?") if err != nil { fatal(err) diff --git a/go.mod b/go.mod index 04af53fc..0aab9189 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/smallstep/nosql v0.3.8 github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 - go.step.sm/cli-utils v0.4.1 + go.step.sm/cli-utils v0.6.0 go.step.sm/crypto v0.11.0 go.step.sm/linkedca v0.5.0 golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 diff --git a/go.sum b/go.sum index a6ff0f08..0c042a04 100644 --- a/go.sum +++ b/go.sum @@ -365,6 +365,8 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 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= @@ -455,8 +457,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= -github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -534,8 +534,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= -go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= +go.step.sm/cli-utils v0.6.0 h1:sH4FxBcjmbxyilKXheSyJuKF/QjpojpiW90ERwUWOgQ= +go.step.sm/cli-utils v0.6.0/go.mod h1:jklBMavFl2PbmGlyxgax08ZnB0uWpadjuOlSKKXz+0U= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.11.0 h1:VDpeVgEmqme/FK2w5QINxkOQ1FWOm/Wi2TwQXiacKr8= go.step.sm/crypto v0.11.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= From 205148ad1fbb397d325d3aa984e7da8c94a59baf Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 12:43:24 -0700 Subject: [PATCH 254/291] Fix exit after defer. --- cmd/step-awskms-init/main.go | 5 ++++- cmd/step-ca/main.go | 10 ++++++++-- cmd/step-cloudkms-init/main.go | 5 ++++- cmd/step-pkcs11-init/main.go | 5 ++++- cmd/step-yubikey-init/main.go | 5 ++++- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cmd/step-awskms-init/main.go b/cmd/step-awskms-init/main.go index 7b9dc7a3..0678ef39 100644 --- a/cmd/step-awskms-init/main.go +++ b/cmd/step-awskms-init/main.go @@ -33,7 +33,6 @@ func main() { // Initialize windows terminal ui.Init() - defer ui.Reset() c, err := awskms.New(context.Background(), apiv1.Options{ Type: string(apiv1.AmazonKMS), @@ -54,10 +53,14 @@ func main() { fatal(err) } } + + // Reset windows terminal + ui.Reset() } func fatal(err error) { fmt.Fprintln(os.Stderr, err) + ui.Reset() os.Exit(1) } diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index d9d17fed..e0123678 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -53,6 +53,11 @@ func init() { rand.Seed(time.Now().UnixNano()) } +func exit(code int) { + ui.Reset() + os.Exit(code) +} + // appHelpTemplate contains the modified template for the main app var appHelpTemplate = `## NAME **{{.HelpName}}** -- {{.Usage}} @@ -93,7 +98,6 @@ Please send us a sentence or two, good or bad: **feedback@smallstep.com** or htt func main() { // Initialize windows terminal ui.Init() - defer ui.Reset() // Override global framework components cli.VersionPrinter = func(c *cli.Context) { @@ -169,8 +173,10 @@ $ step-ca $STEPPATH/config/ca.json --password-file ./password.txt } else { fmt.Fprintln(os.Stderr, err) } - os.Exit(1) + exit(1) } + + exit(0) } func flagValue(f cli.Flag) reflect.Value { diff --git a/cmd/step-cloudkms-init/main.go b/cmd/step-cloudkms-init/main.go index b924f1a1..14bf50f1 100644 --- a/cmd/step-cloudkms-init/main.go +++ b/cmd/step-cloudkms-init/main.go @@ -64,7 +64,6 @@ func main() { // Initialize windows terminal ui.Init() - defer ui.Reset() c, err := cloudkms.New(context.Background(), apiv1.Options{ Type: string(apiv1.CloudKMS), @@ -84,10 +83,14 @@ func main() { fatal(err) } } + + // Reset windows terminal + ui.Reset() } func fatal(err error) { fmt.Fprintln(os.Stderr, err) + ui.Reset() os.Exit(1) } diff --git a/cmd/step-pkcs11-init/main.go b/cmd/step-pkcs11-init/main.go index 5d9ba3e1..78c531c6 100644 --- a/cmd/step-pkcs11-init/main.go +++ b/cmd/step-pkcs11-init/main.go @@ -131,7 +131,6 @@ func main() { // Initialize windows terminal ui.Init() - defer ui.Reset() if u.Get("pin-value") == "" && u.Get("pin-source") == "" && c.Pin == "" { pin, err := ui.PromptPassword("What is the PKCS#11 PIN?") @@ -205,6 +204,9 @@ func main() { if err := createPKI(k, c); err != nil { fatalClose(err, k) } + + // Reset windows terminal + ui.Reset() } func fatal(err error) { @@ -213,6 +215,7 @@ func fatal(err error) { } else { fmt.Fprintln(os.Stderr, err) } + ui.Reset() os.Exit(1) } diff --git a/cmd/step-yubikey-init/main.go b/cmd/step-yubikey-init/main.go index cc55614a..163d0fcb 100644 --- a/cmd/step-yubikey-init/main.go +++ b/cmd/step-yubikey-init/main.go @@ -89,7 +89,6 @@ func main() { // Initialize windows terminal ui.Init() - defer ui.Reset() pin, err := ui.PromptPassword("What is the YubiKey PIN?") if err != nil { @@ -123,6 +122,9 @@ func main() { defer func() { _ = k.Close() }() + + // Reset windows terminal + ui.Reset() } func fatal(err error) { @@ -131,6 +133,7 @@ func fatal(err error) { } else { fmt.Fprintln(os.Stderr, err) } + ui.Reset() os.Exit(1) } From 52a18e0c2d0d28d12a1c8b7737e7a476e11b08ba Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 14:19:39 -0700 Subject: [PATCH 255/291] Add key name to CreateCertificateAuthority --- cas/apiv1/requests.go | 6 +++++- cas/softcas/softcas.go | 1 + cas/softcas/softcas_test.go | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cas/apiv1/requests.go b/cas/apiv1/requests.go index b47a9c13..bf745c17 100644 --- a/cas/apiv1/requests.go +++ b/cas/apiv1/requests.go @@ -108,6 +108,9 @@ type GetCertificateAuthorityResponse struct { RootCertificate *x509.Certificate } +// CreateKeyRequest is the request used to generate a new key using a KMS. +type CreateKeyRequest = apiv1.CreateKeyRequest + // CreateCertificateAuthorityRequest is the request used to generate a root or // intermediate certificate. type CreateCertificateAuthorityRequest struct { @@ -126,7 +129,7 @@ type CreateCertificateAuthorityRequest struct { // CreateKey defines the KMS CreateKeyRequest to use when creating a new // CertificateAuthority. If CreateKey is nil, a default algorithm will be // used. - CreateKey *apiv1.CreateKeyRequest + CreateKey *CreateKeyRequest } // CreateCertificateAuthorityResponse is the response for @@ -136,6 +139,7 @@ type CreateCertificateAuthorityResponse struct { Name string Certificate *x509.Certificate CertificateChain []*x509.Certificate + KeyName string PublicKey crypto.PublicKey PrivateKey crypto.PrivateKey Signer crypto.Signer diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index 23dac91b..e33a043a 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -174,6 +174,7 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori Name: cert.Subject.CommonName, Certificate: cert, CertificateChain: chain, + KeyName: key.Name, PublicKey: key.PublicKey, PrivateKey: key.PrivateKey, Signer: signer, diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index c8e1a8e9..b9b79250 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -106,6 +106,7 @@ func (m *mockKeyManager) CreateKey(req *kmsapi.CreateKeyRequest) (*kmsapi.Create signer = m.signer } return &kmsapi.CreateKeyResponse{ + Name: req.Name, PrivateKey: signer, PublicKey: signer.Public(), }, m.errCreateKey @@ -516,6 +517,22 @@ func TestSoftCAS_CreateCertificateAuthority(t *testing.T) { PrivateKey: saSigner, Signer: saSigner, }, false}, + {"ok createKey", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{ + Type: apiv1.RootCA, + Template: testRootTemplate, + Lifetime: 24 * time.Hour, + CreateKey: &kmsapi.CreateKeyRequest{ + Name: "root_ca.crt", + SignatureAlgorithm: kmsapi.ECDSAWithSHA256, + }, + }}, &apiv1.CreateCertificateAuthorityResponse{ + Name: "Test Root CA", + Certificate: testSignedRootTemplate, + PublicKey: testSignedRootTemplate.PublicKey, + KeyName: "root_ca.crt", + PrivateKey: testSigner, + Signer: testSigner, + }, false}, {"fail template", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{ Type: apiv1.RootCA, Lifetime: 24 * time.Hour, From 2026787ce4a8360d8b3e19a08edfd756e90d2d10 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 15:01:11 -0700 Subject: [PATCH 256/291] Add some extra coverage. --- kms/azurekms/key_vault.go | 5 +-- kms/azurekms/key_vault_test.go | 70 ++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index f7b00ec7..c5dc56bf 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -38,9 +38,8 @@ var now = func() time.Time { } type keyType struct { - Kty keyvault.JSONWebKeyType - Curve keyvault.JSONWebKeyCurveName - KeySize int + Kty keyvault.JSONWebKeyType + Curve keyvault.JSONWebKeyCurveName } func (k keyType) KeyType(pl apiv1.ProtectionLevel) keyvault.JSONWebKeyType { diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index 1f09b2d5..4f98d274 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -42,17 +42,19 @@ func mockClient(t *testing.T) *mock.KeyVaultClient { return mock.NewKeyVaultClient(ctrl) } -func mockCreateClient(t *testing.T, ctrl *gomock.Controller) { +func mockCreateClient(t *testing.T) *mock.KeyVaultClient { t.Helper() + ctrl := gomock.NewController(t) + client := mock.NewKeyVaultClient(ctrl) old := createClient - createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { - return mock.NewKeyVaultClient(ctrl), nil + return client, nil } - t.Cleanup(func() { createClient = old + ctrl.Finish() }) + return client } func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey { @@ -78,8 +80,11 @@ func Test_now(t *testing.T) { } func TestNew(t *testing.T) { - ctrl := gomock.NewController(t) - mockCreateClient(t, ctrl) + client := mockClient(t) + old := createClient + t.Cleanup(func() { + createClient = old + }) type args struct { ctx context.Context @@ -87,16 +92,27 @@ func TestNew(t *testing.T) { } tests := []struct { name string + setup func() args args want *KeyVault wantErr bool }{ - {"ok", args{context.Background(), apiv1.Options{}}, &KeyVault{ - baseClient: mock.NewKeyVaultClient(ctrl), + {"ok", func() { + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return client, nil + } + }, args{context.Background(), apiv1.Options{}}, &KeyVault{ + baseClient: client, }, false}, + {"fail", func() { + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return nil, errTest + } + }, args{context.Background(), apiv1.Options{}}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.setup() got, err := New(tt.args.ctx, tt.args.opts) if (err != nil) != tt.wantErr { t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) @@ -148,6 +164,9 @@ func TestKeyVault_GetPublicKey(t *testing.T) { {"fail GetKey", fields{client}, args{&apiv1.GetPublicKeyRequest{ Name: "azurekms:vault=my-vault;name=not-found?version=my-version", }}, nil, true}, + {"fail empty", fields{client}, args{&apiv1.GetPublicKeyRequest{ + Name: "", + }}, nil, true}, {"fail vault", fields{client}, args{&apiv1.GetPublicKeyRequest{ Name: "azurekms:vault=;name=not-found?version=my-version", }}, nil, true}, @@ -490,3 +509,38 @@ func TestKeyVault_Close(t *testing.T) { }) } } + +func Test_keyType_KeyType(t *testing.T) { + type fields struct { + Kty keyvault.JSONWebKeyType + Curve keyvault.JSONWebKeyCurveName + } + type args struct { + pl apiv1.ProtectionLevel + } + tests := []struct { + name string + fields fields + args args + want keyvault.JSONWebKeyType + }{ + {"ec", fields{keyvault.EC, keyvault.P256}, args{apiv1.UnspecifiedProtectionLevel}, keyvault.EC}, + {"ec software", fields{keyvault.EC, keyvault.P384}, args{apiv1.Software}, keyvault.EC}, + {"ec hsm", fields{keyvault.EC, keyvault.P521}, args{apiv1.HSM}, keyvault.ECHSM}, + {"rsa", fields{keyvault.RSA, keyvault.P256}, args{apiv1.UnspecifiedProtectionLevel}, keyvault.RSA}, + {"rsa software", fields{keyvault.RSA, ""}, args{apiv1.Software}, keyvault.RSA}, + {"rsa hsm", fields{keyvault.RSA, ""}, args{apiv1.HSM}, keyvault.RSAHSM}, + {"empty", fields{"FOO", ""}, args{apiv1.UnspecifiedProtectionLevel}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := keyType{ + Kty: tt.fields.Kty, + Curve: tt.fields.Curve, + } + if got := k.KeyType(tt.args.pl); !reflect.DeepEqual(got, tt.want) { + t.Errorf("keyType.KeyType() = %v, want %v", got, tt.want) + } + }) + } +} From 500b540406f35c0ac2e46c48c97ace9f0a05e299 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 15:35:21 -0700 Subject: [PATCH 257/291] Remove unused code. --- kms/azurekms/key_vault_test.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index 4f98d274..f9446b0f 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -42,21 +42,6 @@ func mockClient(t *testing.T) *mock.KeyVaultClient { return mock.NewKeyVaultClient(ctrl) } -func mockCreateClient(t *testing.T) *mock.KeyVaultClient { - t.Helper() - ctrl := gomock.NewController(t) - client := mock.NewKeyVaultClient(ctrl) - old := createClient - createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { - return client, nil - } - t.Cleanup(func() { - createClient = old - ctrl.Finish() - }) - return client -} - func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey { t.Helper() b, err := json.Marshal(&jose.JSONWebKey{ From f1ef3fb351ce0659c7540f5863397d9d3012e153 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 15:48:11 -0700 Subject: [PATCH 258/291] Add GetBool(s string) bool to URI type. --- kms/uri/uri.go | 10 ++++++++++ kms/uri/uri_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/kms/uri/uri.go b/kms/uri/uri.go index 44271e74..3a1b8981 100644 --- a/kms/uri/uri.go +++ b/kms/uri/uri.go @@ -95,6 +95,16 @@ func (u *URI) Get(key string) string { return v } +// GetBool returns true if a given key has the value "true". It will returns +// false otherwise. +func (u *URI) GetBool(key string) bool { + v := u.Values.Get(key) + if v == "" { + v = u.URL.Query().Get(key) + } + return strings.EqualFold(v, "true") +} + // GetEncoded returns the first value in the uri with the given key, it will // return empty nil if that field is not present or is empty. If the return // value is hex encoded it will decode it and return it. diff --git a/kms/uri/uri_test.go b/kms/uri/uri_test.go index c2e0a9fe..01fbad0f 100644 --- a/kms/uri/uri_test.go +++ b/kms/uri/uri_test.go @@ -212,6 +212,40 @@ func TestURI_Get(t *testing.T) { } } +func TestURI_GetBool(t *testing.T) { + mustParse := func(s string) *URI { + u, err := Parse(s) + if err != nil { + t.Fatal(err) + } + return u + } + type args struct { + key string + } + tests := []struct { + name string + uri *URI + args args + want bool + }{ + {"true", mustParse("azurekms:name=foo;vault=bar;hsm=true"), args{"hsm"}, true}, + {"TRUE", mustParse("azurekms:name=foo;vault=bar;hsm=TRUE"), args{"hsm"}, true}, + {"tRUe query", mustParse("azurekms:name=foo;vault=bar?hsm=tRUe"), args{"hsm"}, true}, + {"false", mustParse("azurekms:name=foo;vault=bar;hsm=false"), args{"hsm"}, false}, + {"false query", mustParse("azurekms:name=foo;vault=bar?hsm=false"), args{"hsm"}, false}, + {"empty", mustParse("azurekms:name=foo;vault=bar;hsm=?bar=true"), args{"hsm"}, false}, + {"missing", mustParse("azurekms:name=foo;vault=bar"), args{"hsm"}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.uri.GetBool(tt.args.key); got != tt.want { + t.Errorf("URI.GetBool() = %v, want %v", got, tt.want) + } + }) + } +} + func TestURI_GetEncoded(t *testing.T) { mustParse := func(s string) *URI { u, err := Parse(s) From abdb56065d646dc77f5b45f406a309dc83f3c22a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 16:18:36 -0700 Subject: [PATCH 259/291] Allow o specify an hsm using the uri. --- kms/azurekms/key_vault.go | 17 +++++++++++++---- kms/azurekms/key_vault_test.go | 23 +++++++++++++++++++++++ kms/azurekms/signer.go | 2 +- kms/azurekms/signer_test.go | 9 +++++++++ kms/azurekms/utils.go | 9 +++++++-- kms/azurekms/utils_test.go | 26 ++++++++++++++++---------- 6 files changed, 69 insertions(+), 17 deletions(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index c5dc56bf..4a927d4f 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -113,11 +113,13 @@ type KeyVaultClient interface { // // - azurekms:name=key-name;vault=vault-name // - azurekms:name=key-name;vault=vault-name?version=key-version +// - azurekms:name=key-name;vault=vault-name?hsm=true // // The scheme is "azurekms"; "name" is the key name; "vault" is the key vault // name where the key is located; "version" is an optional parameter that // defines the version of they key, if version is not given, the latest one will -// be used. +// be used; "hsm" defines if an HSM want to be used for this key, this is +// specially useful when this is used from `step`. // // TODO(mariano): The implementation is using /services/keyvault/v7.1/keyvault // package, at some point Azure might create a keyvault client with all the @@ -165,7 +167,7 @@ func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe return nil, errors.New("getPublicKeyRequest 'name' cannot be empty") } - vault, name, version, err := parseKeyName(req.Name) + vault, name, version, _, err := parseKeyName(req.Name) if err != nil { return nil, err } @@ -187,11 +189,18 @@ func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo return nil, errors.New("createKeyRequest 'name' cannot be empty") } - vault, name, _, err := parseKeyName(req.Name) + vault, name, _, hsm, err := parseKeyName(req.Name) if err != nil { return nil, err } + // Override protection level to HSM only if it's not specified, and is given + // in the uri. + protectionLevel := req.ProtectionLevel + if protectionLevel == apiv1.UnspecifiedProtectionLevel && hsm { + protectionLevel = apiv1.HSM + } + kt, ok := signatureAlgorithmMapping[req.SignatureAlgorithm] if !ok { return nil, errors.Errorf("keyVault does not support signature algorithm '%s'", req.SignatureAlgorithm) @@ -216,7 +225,7 @@ func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo defer cancel() resp, err := k.baseClient.CreateKey(ctx, vaultBaseURL(vault), name, keyvault.KeyCreateParameters{ - Kty: kt.KeyType(req.ProtectionLevel), + Kty: kt.KeyType(protectionLevel), KeySize: keySize, Curve: kt.Curve, KeyOps: &[]keyvault.JSONWebKeyOperation{ diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index f9446b0f..0f6d7e0e 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -202,11 +202,13 @@ func TestKeyVault_CreateKey(t *testing.T) { }{ {"P-256", keyvault.EC, nil, keyvault.P256, ecJWK}, {"P-256 HSM", keyvault.ECHSM, nil, keyvault.P256, ecJWK}, + {"P-256 HSM (uri)", keyvault.ECHSM, nil, keyvault.P256, ecJWK}, {"P-256 Default", keyvault.EC, nil, keyvault.P256, ecJWK}, {"P-384", keyvault.EC, nil, keyvault.P384, ecJWK}, {"P-521", keyvault.EC, nil, keyvault.P521, ecJWK}, {"RSA 0", keyvault.RSA, &value3072, "", rsaJWK}, {"RSA 0 HSM", keyvault.RSAHSM, &value3072, "", rsaJWK}, + {"RSA 0 HSM (uri)", keyvault.RSAHSM, &value3072, "", rsaJWK}, {"RSA 2048", keyvault.RSA, &value2048, "", rsaJWK}, {"RSA 3072", keyvault.RSA, &value3072, "", rsaJWK}, {"RSA 4096", keyvault.RSA, &value4096, "", rsaJWK}, @@ -269,6 +271,16 @@ func TestKeyVault_CreateKey(t *testing.T) { SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, + {"ok P-256 HSM (uri)", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;name=my-key?hsm=true", + SignatureAlgorithm: apiv1.ECDSAWithSHA256, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:name=my-key;vault=my-vault", + PublicKey: ecPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:name=my-key;vault=my-vault", + }, + }, false}, {"ok P-256 Default", fields{client}, args{&apiv1.CreateKeyRequest{ Name: "azurekms:vault=my-vault;name=my-key", }}, &apiv1.CreateKeyResponse{ @@ -322,6 +334,17 @@ func TestKeyVault_CreateKey(t *testing.T) { SigningKey: "azurekms:name=my-key;vault=my-vault", }, }, false}, + {"ok RSA 0 HSM (uri)", fields{client}, args{&apiv1.CreateKeyRequest{ + Name: "azurekms:vault=my-vault;name=my-key;hsm=true", + Bits: 0, + SignatureAlgorithm: apiv1.SHA256WithRSAPSS, + }}, &apiv1.CreateKeyResponse{ + Name: "azurekms:name=my-key;vault=my-vault", + PublicKey: rsaPub, + CreateSignerRequest: apiv1.CreateSignerRequest{ + SigningKey: "azurekms:name=my-key;vault=my-vault", + }, + }, false}, {"ok RSA 2048", fields{client}, args{&apiv1.CreateKeyRequest{ Name: "azurekms:vault=my-vault;name=my-key", Bits: 2048, diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index cb844bdf..cf0197fb 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -25,7 +25,7 @@ type Signer struct { // NewSigner creates a new signer using a key in the AWS KMS. func NewSigner(client KeyVaultClient, signingKey string) (crypto.Signer, error) { - vault, name, version, err := parseKeyName(signingKey) + vault, name, version, _, err := parseKeyName(signingKey) if err != nil { return nil, err } diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go index 389f65b3..90740b9f 100644 --- a/kms/azurekms/signer_test.go +++ b/kms/azurekms/signer_test.go @@ -221,6 +221,12 @@ func TestSigner_Sign(t *testing.T) { {"fail sign length", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{ Result: &rsaSHA256ResultSig, }, nil}, + {"fail base64", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{ + Result: func() *string { + v := "😎" + return &v + }(), + }, nil}, } for _, e := range expects { value := base64.RawURLEncoding.EncodeToString(e.digest) @@ -291,6 +297,9 @@ func TestSigner_Sign(t *testing.T) { {"fail sign length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ rand.Reader, p256Digest[:], crypto.SHA256, }, nil, true}, + {"fail base64", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ + rand.Reader, p256Digest[:], crypto.SHA256, + }, nil, true}, {"fail RSA-PSS salt length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{ rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{ SaltLength: 64, diff --git a/kms/azurekms/utils.go b/kms/azurekms/utils.go index 6b6d1511..52bed868 100644 --- a/kms/azurekms/utils.go +++ b/kms/azurekms/utils.go @@ -42,11 +42,15 @@ func getKeyName(vault, name string, bundle keyvault.KeyBundle) string { // parseKeyName returns the key vault, name and version from URIs like: // // - azurekms:vault=key-vault;name=key-name -// - azurekms:vault=key-vault;name=key-name;id=key-id +// - azurekms:vault=key-vault;name=key-name?version=key-id +// - azurekms:vault=key-vault;name=key-name?version=key-id&hsm=true // // The key-id defines the version of the key, if it is not passed the latest // version will be used. -func parseKeyName(rawURI string) (vault, name, version string, err error) { +// +// HSM can also be passed to define the protection level if this is not given in +// CreateQuery. +func parseKeyName(rawURI string) (vault, name, version string, hsm bool, err error) { var u *uri.URI u, err = uri.ParseWithScheme("azurekms", rawURI) @@ -63,6 +67,7 @@ func parseKeyName(rawURI string) (vault, name, version string, err error) { return } version = u.Get("version") + hsm = u.GetBool("hsm") return } diff --git a/kms/azurekms/utils_test.go b/kms/azurekms/utils_test.go index 915ee74d..03d3f6e2 100644 --- a/kms/azurekms/utils_test.go +++ b/kms/azurekms/utils_test.go @@ -51,21 +51,24 @@ func Test_parseKeyName(t *testing.T) { wantVault string wantName string wantVersion string + wantHsm bool wantErr bool }{ - {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version"}, "my-vault", "my-key", "my-version", false}, - {"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version"}, "my-vault", "my-key", "my-version", false}, - {"ok no version", args{"azurekms:name=my-key;vault=my-vault"}, "my-vault", "my-key", "", false}, - {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, "", "", "", true}, - {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, "", "", "", true}, - {"fail no name", args{"azurekms:vault=my-vault"}, "", "", "", true}, - {"fail empty name", args{"azurekms:name=;vault=my-vault"}, "", "", "", true}, - {"fail no vault", args{"azurekms:name=my-key"}, "", "", "", true}, - {"fail empty vault", args{"azurekms:name=my-key;vault="}, "", "", "", true}, + {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version"}, "my-vault", "my-key", "my-version", false, false}, + {"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version"}, "my-vault", "my-key", "my-version", false, false}, + {"ok no version", args{"azurekms:name=my-key;vault=my-vault"}, "my-vault", "my-key", "", false, false}, + {"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true"}, "my-vault", "my-key", "", true, false}, + {"ok hsm false", args{"azurekms:name=my-key;vault=my-vault?hsm=false"}, "my-vault", "my-key", "", false, false}, + {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, "", "", "", false, true}, + {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, "", "", "", false, true}, + {"fail no name", args{"azurekms:vault=my-vault"}, "", "", "", false, true}, + {"fail empty name", args{"azurekms:name=;vault=my-vault"}, "", "", "", false, true}, + {"fail no vault", args{"azurekms:name=my-key"}, "", "", "", false, true}, + {"fail empty vault", args{"azurekms:name=my-key;vault="}, "", "", "", false, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotVault, gotName, gotVersion, err := parseKeyName(tt.args.rawURI) + gotVault, gotName, gotVersion, gotHsm, err := parseKeyName(tt.args.rawURI) if (err != nil) != tt.wantErr { t.Errorf("parseKeyName() error = %v, wantErr %v", err, tt.wantErr) return @@ -79,6 +82,9 @@ func Test_parseKeyName(t *testing.T) { if gotVersion != tt.wantVersion { t.Errorf("parseKeyName() gotVersion = %v, want %v", gotVersion, tt.wantVersion) } + if gotHsm != tt.wantHsm { + t.Errorf("parseKeyName() gotHsm = %v, want %v", gotHsm, tt.wantHsm) + } }) } } From 2240ebbadc8d3e96fde6b433dc44fd2abc31ec61 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:19:55 -0700 Subject: [PATCH 260/291] Add NameValidator interface and implement it for azurekms. --- kms/apiv1/options.go | 6 ++++++ kms/azurekms/key_vault.go | 6 ++++++ kms/azurekms/key_vault_test.go | 27 +++++++++++++++++++++++++++ kms/azurekms/utils_test.go | 1 + 4 files changed, 40 insertions(+) diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 37c1fd4c..1faf3e7e 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -29,6 +29,12 @@ type CertificateManager interface { StoreCertificate(req *StoreCertificateRequest) error } +// ValidateName is an interface that KeyManager can implement to validate a +// given name or URI. +type NameValidator interface { + ValidateName(s string) error +} + // ErrNotImplemented is the type of error returned if an operation is not // implemented. type ErrNotImplemented struct { diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 4a927d4f..5bd31d30 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -268,3 +268,9 @@ func (k *KeyVault) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, func (k *KeyVault) Close() error { return nil } + +// ValidateName validates that the given string is a valid URI. +func (k *KeyVault) ValidateName(s string) error { + _, _, _, _, err := parseKeyName(s) + return err +} diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index 0f6d7e0e..1f26e1ef 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -552,3 +552,30 @@ func Test_keyType_KeyType(t *testing.T) { }) } } + +func TestKeyVault_ValidateName(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"ok", args{"azurekms:name=my-key;vault=my-vault"}, false}, + {"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true"}, false}, + {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, true}, + {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, true}, + {"fail no name", args{"azurekms:vault=my-vault"}, true}, + {"fail no vault", args{"azurekms:name=my-key"}, true}, + {"fail empty", args{""}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &KeyVault{} + if err := k.ValidateName(tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("KeyVault.ValidateName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/kms/azurekms/utils_test.go b/kms/azurekms/utils_test.go index 03d3f6e2..000a9d6b 100644 --- a/kms/azurekms/utils_test.go +++ b/kms/azurekms/utils_test.go @@ -65,6 +65,7 @@ func Test_parseKeyName(t *testing.T) { {"fail empty name", args{"azurekms:name=;vault=my-vault"}, "", "", "", false, true}, {"fail no vault", args{"azurekms:name=my-key"}, "", "", "", false, true}, {"fail empty vault", args{"azurekms:name=my-key;vault="}, "", "", "", false, true}, + {"fail empty", args{""}, "", "", "", false, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From d8720c3723bbab5f6ba00ca58fbb6fc1020cd8a2 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:21:40 -0700 Subject: [PATCH 261/291] Update linkedca package. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 43590e4a..f5930ce2 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.4.1 go.step.sm/crypto v0.11.0 - go.step.sm/linkedca v0.5.0 + 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 google.golang.org/api v0.47.0 diff --git a/go.sum b/go.sum index cf33febd..05b0f579 100644 --- a/go.sum +++ b/go.sum @@ -571,8 +571,8 @@ go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0W go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= go.step.sm/crypto v0.11.0 h1:VDpeVgEmqme/FK2w5QINxkOQ1FWOm/Wi2TwQXiacKr8= go.step.sm/crypto v0.11.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= -go.step.sm/linkedca v0.5.0 h1:oZVRSpElM7lAL1XN2YkjdHwI/oIZ+1ULOnuqYPM6xjY= -go.step.sm/linkedca v0.5.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +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= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= From 822a1e3bdb0d891dfd14d2c63b1f440ff340c84d Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:23:56 -0700 Subject: [PATCH 262/291] Add variable with the default implementation. --- kms/kms.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kms/kms.go b/kms/kms.go index e1b76f1a..92b544df 100644 --- a/kms/kms.go +++ b/kms/kms.go @@ -8,7 +8,7 @@ import ( "github.com/smallstep/certificates/kms/apiv1" // Enable default implementation - _ "github.com/smallstep/certificates/kms/softkms" + "github.com/smallstep/certificates/kms/softkms" ) // KeyManager is the interface implemented by all the KMS. @@ -21,6 +21,9 @@ type CertificateManager = apiv1.CertificateManager // Options are the KMS options. They represent the kms object in the ca.json. type Options = apiv1.Options +// Default is the implementation of the default KMS. +var Default = &softkms.SoftKMS{} + // New initializes a new KMS from the given type. func New(ctx context.Context, opts apiv1.Options) (KeyManager, error) { if err := opts.Validate(); err != nil { From ece67feffff32bfa6f4746e36cb38ebdb493f241 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:28:39 -0700 Subject: [PATCH 263/291] Add support for kms in pki package. Adding support to kms in the pki packages opens the door to use kms implementations in `step ca init` --- pki/pki.go | 174 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 33 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 12e71e47..41a644e1 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -26,13 +26,14 @@ import ( "github.com/smallstep/certificates/cas" "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" + "github.com/smallstep/certificates/kms" + kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/nosql" "go.step.sm/cli-utils/config" "go.step.sm/cli-utils/errs" "go.step.sm/cli-utils/fileutil" "go.step.sm/cli-utils/ui" "go.step.sm/crypto/jose" - "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" "go.step.sm/linkedca" "golang.org/x/crypto/ssh" @@ -168,14 +169,18 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) { } type options struct { - provisioner string - pkiOnly bool - enableACME bool - enableSSH bool - enableAdmin bool - noDB bool - isHelm bool - deploymentType DeploymentType + provisioner string + pkiOnly bool + enableACME bool + enableSSH bool + enableAdmin bool + noDB bool + isHelm bool + deploymentType DeploymentType + rootKeyURI string + intermediateKeyURI string + hostKeyURI string + userKeyURI string } // Option is the type of a configuration option on the pki constructor. @@ -258,6 +263,26 @@ func WithDeploymentType(dt DeploymentType) Option { } } +// WithKMS enabled the kms with the given name. +func WithKMS(name string) Option { + return func(p *PKI) { + typ := linkedca.KMS_Type_value[strings.ToUpper(name)] + p.Configuration.Kms = &linkedca.KMS{ + Type: linkedca.KMS_Type(typ), + } + } +} + +// WithKeyURIs defines the key uris for X.509 and SSH keys. +func WithKeyURIs(rootKey, intermediateKey, hostKey, userKey string) Option { + return func(p *PKI) { + p.options.rootKeyURI = rootKey + p.options.intermediateKeyURI = intermediateKey + p.options.hostKeyURI = hostKey + p.options.userKeyURI = userKey + } +} + // PKI represents the Public Key Infrastructure used by a certificate authority. type PKI struct { linkedca.Configuration @@ -265,6 +290,7 @@ type PKI struct { casOptions apiv1.Options caService apiv1.CertificateAuthorityService caCreator apiv1.CertificateAuthorityCreator + keyManager kmsapi.KeyManager config string defaults string ottPublicKey *jose.JSONWebKey @@ -303,8 +329,9 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { Files: make(map[string][]byte), }, casOptions: o, - caCreator: caCreator, caService: caService, + caCreator: caCreator, + keyManager: o.KeyManager, options: &options{ provisioner: "step-cli", }, @@ -313,6 +340,11 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { fn(p) } + // Use default key manager + if p.keyManager != nil { + p.keyManager = kms.Default + } + // Use /home/step as the step path in helm configurations. // Use the current step path when creating pki in files. var public, private, config string @@ -448,11 +480,18 @@ func (p *PKI) GenerateKeyPairs(pass []byte) error { // GenerateRootCertificate generates a root certificate with the given name // and using the default key type. func (p *PKI) GenerateRootCertificate(name, org, resource string, pass []byte) (*apiv1.CreateCertificateAuthorityResponse, error) { + if uri := p.options.rootKeyURI; uri != "" { + p.RootKey[0] = uri + } + resp, err := p.caCreator.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{ - Name: resource + "-Root-CA", - Type: apiv1.RootCA, - Lifetime: 10 * 365 * 24 * time.Hour, - CreateKey: nil, // use default + Name: resource + "-Root-CA", + Type: apiv1.RootCA, + Lifetime: 10 * 365 * 24 * time.Hour, + CreateKey: &apiv1.CreateKeyRequest{ + Name: p.RootKey[0], + SignatureAlgorithm: kmsapi.UnspecifiedSignAlgorithm, + }, Template: &x509.Certificate{ Subject: pkix.Name{ CommonName: name + " Root CA", @@ -469,6 +508,13 @@ func (p *PKI) GenerateRootCertificate(name, org, resource string, pass []byte) ( return nil, err } + // Replace key name with the one from the key manager if available. On + // softcas this will be the original filename, on any other kms will be the + // uri to the key. + if resp.KeyName != "" { + p.RootKey[0] = resp.KeyName + } + // PrivateKey will only be set if we have access to it (SoftCAS). if err := p.WriteRootCertificate(resp.Certificate, resp.PrivateKey, pass); err != nil { return nil, err @@ -495,11 +541,18 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{ // GenerateIntermediateCertificate generates an intermediate certificate with // the given name and using the default key type. func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent *apiv1.CreateCertificateAuthorityResponse, pass []byte) error { + if uri := p.options.intermediateKeyURI; uri != "" { + p.IntermediateKey = uri + } + resp, err := p.caCreator.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{ - Name: resource + "-Intermediate-CA", - Type: apiv1.IntermediateCA, - Lifetime: 10 * 365 * 24 * time.Hour, - CreateKey: nil, // use default + Name: resource + "-Intermediate-CA", + Type: apiv1.IntermediateCA, + Lifetime: 10 * 365 * 24 * time.Hour, + CreateKey: &apiv1.CreateKeyRequest{ + Name: p.IntermediateKey, + SignatureAlgorithm: kmsapi.UnspecifiedSignAlgorithm, + }, Template: &x509.Certificate{ Subject: pkix.Name{ CommonName: name + " Intermediate CA", @@ -519,7 +572,19 @@ func (p *PKI) GenerateIntermediateCertificate(name, org, resource string, parent p.casOptions.CertificateAuthority = resp.Name p.Files[p.Intermediate] = encodeCertificate(resp.Certificate) - p.Files[p.IntermediateKey], err = encodePrivateKey(resp.PrivateKey, pass) + + // Replace the key name with the one from the key manager. On softcas this + // will be the original filename, on any other kms will be the uri to the + // key. + if resp.KeyName != "" { + p.IntermediateKey = resp.KeyName + } + + // If a kms is used it will not have the private key + if resp.PrivateKey != nil { + p.Files[p.IntermediateKey], err = encodePrivateKey(resp.PrivateKey, pass) + } + return err } @@ -564,27 +629,63 @@ func (p *PKI) GetCertificateAuthority() error { // GenerateSSHSigningKeys generates and encrypts a private key used for signing // SSH user certificates and a private key used for signing host certificates. func (p *PKI) GenerateSSHSigningKeys(password []byte) error { - var pubNames = []string{p.Ssh.HostPublicKey, p.Ssh.UserPublicKey} - var privNames = []string{p.Ssh.HostKey, p.Ssh.UserKey} - for i := 0; i < 2; i++ { - pub, priv, err := keyutil.GenerateDefaultKeyPair() + // Enable SSH + p.options.enableSSH = true + + // Create SSH key used to sign host certificates. Using + // kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm. + name := p.Ssh.HostPublicKey + if uri := p.options.hostKeyURI; uri != "" { + name = uri + } + resp, err := p.keyManager.CreateKey(&kmsapi.CreateKeyRequest{ + Name: name, + SignatureAlgorithm: kmsapi.UnspecifiedSignAlgorithm, + }) + if err != nil { + return err + } + sshKey, err := ssh.NewPublicKey(resp.PublicKey) + if err != nil { + return errors.Wrapf(err, "error converting public key") + } + p.Files[resp.Name] = ssh.MarshalAuthorizedKey(sshKey) + + // On softkms we will have the private key + if resp.PrivateKey != nil { + p.Files[p.Ssh.HostKey], err = encodePrivateKey(resp.PrivateKey, password) if err != nil { return err } - if _, ok := priv.(crypto.Signer); !ok { - return errors.Errorf("key of type %T is not a crypto.Signer", priv) - } - sshKey, err := ssh.NewPublicKey(pub) - if err != nil { - return errors.Wrapf(err, "error converting public key") - } - p.Files[pubNames[i]] = ssh.MarshalAuthorizedKey(sshKey) - p.Files[privNames[i]], err = encodePrivateKey(priv, password) + } + + // Create SSH key used to sign user certificates. Using + // kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm. + name = p.Ssh.UserPublicKey + if uri := p.options.userKeyURI; uri != "" { + name = uri + } + resp, err = p.keyManager.CreateKey(&kmsapi.CreateKeyRequest{ + Name: name, + SignatureAlgorithm: kmsapi.UnspecifiedSignAlgorithm, + }) + if err != nil { + return err + } + sshKey, err = ssh.NewPublicKey(resp.PublicKey) + if err != nil { + return errors.Wrapf(err, "error converting public key") + } + p.Files[resp.Name] = ssh.MarshalAuthorizedKey(sshKey) + + // On softkms we will have the private key + if resp.PrivateKey != nil { + p.Files[p.Ssh.UserKey], err = encodePrivateKey(resp.PrivateKey, password) if err != nil { return err } } - p.options.enableSSH = true + return nil } @@ -684,6 +785,13 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { config.AuthorityConfig.DeploymentType = LinkedDeployment.String() } + // Enable KMS if necessary + if p.Kms != nil { + config.KMS = &kmsapi.Options{ + Type: strings.ToLower(p.Kms.Type.String()), + } + } + // On standalone deployments add the provisioners to either the ca.json or // the database. var provisioners []provisioner.Interface From c638c282d8c12ab20f51ec61f74fd0dbe0669468 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:30:28 -0700 Subject: [PATCH 264/291] Add omitempty to KMS options. --- kms/apiv1/options.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 1faf3e7e..79b07a60 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -89,18 +89,18 @@ type Options struct { Type string `json:"type"` // Path to the credentials file used in CloudKMS and AmazonKMS. - CredentialsFile string `json:"credentialsFile"` + CredentialsFile string `json:"credentialsFile,omitempty"` // URI is based on the PKCS #11 URI Scheme defined in // https://tools.ietf.org/html/rfc7512 and represents the configuration used // to connect to the KMS. // // Used by: pkcs11 - URI string `json:"uri"` + URI string `json:"uri,omitempty"` // Pin used to access the PKCS11 module. It can be defined in the URI using // the pin-value or pin-source properties. - Pin string `json:"pin"` + Pin string `json:"pin,omitempty"` // ManagementKey used in YubiKeys. Default management key is the hexadecimal // string 010203040506070801020304050607080102030405060708: @@ -109,13 +109,13 @@ type Options struct { // 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // } - ManagementKey string `json:"managementKey"` + ManagementKey string `json:"managementKey,omitempty"` // Region to use in AmazonKMS. - Region string `json:"region"` + Region string `json:"region,omitempty"` // Profile to use in AmazonKMS. - Profile string `json:"profile"` + Profile string `json:"profile,omitempty"` } // Validate checks the fields in Options. From f6e69bf826be1b2ebbfb25e5cb39344d29ce4d55 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:37:47 -0700 Subject: [PATCH 265/291] Fix typo. --- kms/azurekms/key_vault.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 5bd31d30..1fb8572c 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -22,7 +22,7 @@ func init() { // Scheme is the scheme used for Azure Key Vault uris. const Scheme = "azurekms" -// keyIDRegexp is the regular experssion that Key Vault uses for on the kid. We +// keyIDRegexp is the regular expression that Key Vault uses for on the kid. We // can extract the vault, name and version of the key. var keyIDRegexp = regexp.MustCompile("^https://([0-9a-zA-Z-]+).vault.azure.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$") From ca59ee43c57f0c7a297b4a2acf4521f463dc0b48 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 17:41:33 -0700 Subject: [PATCH 266/291] Update changelog. --- CHANGELOG.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a902ee2f..3a726dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased - 0.17.5] - DATE ### Added +- Support for Azure Key Vault as a KMS. +- Adapt `pki` package to support key managers. ### Changed ### Deprecated ### Removed @@ -13,13 +15,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Security ## [0.17.4] - 2021-09-28 -### Added -### Changed -### Deprecated -### Removed ### Fixed - Support host-only or user-only SSH CA. -### Security ## [0.17.3] - 2021-09-24 ### Added @@ -55,4 +52,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Update TLS cipher suites to include 1.3 ### Security - Fix key version when SHA512WithRSA is used. There was a typo creating RSA keys with SHA256 digests instead of SHA512. - From 933b40a02ad99c27717e602bf719056b61e39459 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 8 Oct 2021 14:59:57 -0400 Subject: [PATCH 267/291] Introduce gocritic linter and address warnings --- .golangci.yml | 20 ++- acme/api/account.go | 2 +- acme/api/account_test.go | 4 +- acme/api/handler_test.go | 16 +- acme/api/middleware.go | 4 +- acme/api/middleware_test.go | 56 +++---- acme/api/order_test.go | 18 +-- acme/challenge.go | 10 +- acme/challenge_test.go | 2 +- acme/db/nosql/account_test.go | 70 ++++---- acme/db/nosql/authz_test.go | 66 ++++---- acme/db/nosql/certificate_test.go | 22 ++- acme/db/nosql/challenge_test.go | 56 +++---- acme/db/nosql/nonce.go | 2 +- acme/db/nosql/nonce_test.go | 8 +- acme/db/nosql/nosql.go | 2 +- acme/db/nosql/nosql_test.go | 4 +- acme/db/nosql/order_test.go | 89 +++++------ acme/order.go | 1 + api/api.go | 29 ++-- api/api_test.go | 36 ++--- api/errors.go | 10 +- api/ssh.go | 10 +- api/sshRekey.go | 2 +- api/sshRenew.go | 2 +- api/sshRevoke.go | 2 +- api/ssh_test.go | 2 +- authority/admin/api/middleware.go | 2 +- authority/admin/db/nosql/admin_test.go | 131 +++++++-------- authority/admin/db/nosql/nosql.go | 2 +- authority/admin/db/nosql/provisioner_test.go | 151 ++++++++---------- authority/administrator/collection.go | 4 +- authority/admins.go | 4 +- authority/authority.go | 6 +- authority/authorize.go | 12 +- authority/authorize_test.go | 2 +- authority/config/types.go | 2 +- authority/linkedca.go | 4 +- authority/options.go | 12 +- authority/provisioner/aws.go | 22 +-- authority/provisioner/aws_test.go | 8 +- authority/provisioner/azure.go | 14 +- authority/provisioner/azure_test.go | 8 +- authority/provisioner/collection.go | 15 +- authority/provisioner/gcp.go | 22 +-- authority/provisioner/gcp_test.go | 8 +- authority/provisioner/keystore.go | 2 +- authority/provisioner/noop.go | 2 +- authority/provisioner/oidc.go | 4 +- authority/provisioner/oidc_test.go | 46 +++--- authority/provisioner/options.go | 2 +- authority/provisioner/provisioner.go | 2 +- .../provisioner/sign_ssh_options_test.go | 16 +- authority/provisioner/sshpop_test.go | 8 +- authority/provisioner/utils_test.go | 4 +- authority/ssh.go | 6 +- authority/tls.go | 18 +-- authority/tls_test.go | 1 + ca/acmeClient.go | 2 +- ca/acmeClient_test.go | 1 + ca/adminClient.go | 18 ++- ca/bootstrap.go | 2 +- ca/ca.go | 52 +++--- ca/ca_test.go | 10 +- ca/client.go | 32 ++-- ca/client_test.go | 2 +- ca/identity/client_test.go | 7 +- ca/tls.go | 8 +- cas/cloudcas/cloudcas.go | 4 +- cas/cloudcas/cloudcas_test.go | 11 +- cas/softcas/softcas.go | 4 +- cas/softcas/softcas_test.go | 2 +- cas/stepcas/stepcas.go | 4 +- cas/stepcas/x5c_issuer.go | 4 +- cas/stepcas/x5c_issuer_test.go | 2 +- cmd/step-awskms-init/main.go | 14 +- cmd/step-ca/main.go | 8 +- cmd/step-cloudkms-init/main.go | 14 +- cmd/step-pkcs11-init/main.go | 8 +- cmd/step-yubikey-init/main.go | 8 +- commands/app.go | 10 +- commands/export.go | 12 +- commands/onboard.go | 34 ++-- db/db_test.go | 8 +- kms/sshagentkms/sshagentkms_test.go | 2 + pki/pki.go | 55 +++---- scep/api/api.go | 4 +- templates/templates.go | 14 +- 88 files changed, 699 insertions(+), 742 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 92af7723..cf389517 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -36,22 +36,30 @@ linters-settings: - performance - style - experimental + - diagnostic disabled-checks: - - wrapperFunc - - dupImport # https://github.com/go-critic/go-critic/issues/845 + - commentFormatting + - commentedOutCode + - evalOrder + - hugeParam + - octalLiteral + - rangeValCopy + - tooManyResultsChecker + - unnamedResult linters: disable-all: true enable: + - deadcode + - gocritic - gofmt - - revive + - gosimple - govet - - misspell - ineffassign - - deadcode + - misspell + - revive - staticcheck - unused - - gosimple run: skip-dirs: diff --git a/acme/api/account.go b/acme/api/account.go index b733c679..259cb2a2 100644 --- a/acme/api/account.go +++ b/acme/api/account.go @@ -19,7 +19,7 @@ type NewAccountRequest struct { func validateContacts(cs []string) error { for _, c := range cs { - if len(c) == 0 { + if c == "" { return acme.NewError(acme.ErrorMalformedType, "contact cannot be empty string") } } diff --git a/acme/api/account_test.go b/acme/api/account_test.go index c4d7a812..a45751a0 100644 --- a/acme/api/account_test.go +++ b/acme/api/account_test.go @@ -178,7 +178,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { provName := url.PathEscape(prov.GetName()) baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"} - url := fmt.Sprintf("http://ca.smallstep.com/acme/%s/account/%s/orders", provName, accID) + u := fmt.Sprintf("http://ca.smallstep.com/acme/%s/account/%s/orders", provName, accID) oids := []string{"foo", "bar"} oidURLs := []string{ @@ -255,7 +255,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db, linker: NewLinker("dns", "acme")} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.GetOrdersByAccountID(w, req) diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index f354bbac..8112ad4c 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -148,7 +148,7 @@ func TestHandler_GetAuthorization(t *testing.T) { // Request with chi context chiCtx := chi.NewRouteContext() chiCtx.URLParams.Add("authzID", az.ID) - url := fmt.Sprintf("%s/acme/%s/authz/%s", + u := fmt.Sprintf("%s/acme/%s/authz/%s", baseURL.String(), provName, az.ID) type test struct { @@ -280,7 +280,7 @@ func TestHandler_GetAuthorization(t *testing.T) { expB, err := json.Marshal(az) assert.FatalError(t, err) assert.Equals(t, bytes.TrimSpace(body), expB) - assert.Equals(t, res.Header["Location"], []string{url}) + assert.Equals(t, res.Header["Location"], []string{u}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) @@ -314,7 +314,7 @@ func TestHandler_GetCertificate(t *testing.T) { // Request with chi context chiCtx := chi.NewRouteContext() chiCtx.URLParams.Add("certID", certID) - url := fmt.Sprintf("%s/acme/%s/certificate/%s", + u := fmt.Sprintf("%s/acme/%s/certificate/%s", baseURL.String(), provName, certID) type test struct { @@ -396,7 +396,7 @@ func TestHandler_GetCertificate(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.GetCertificate(w, req) @@ -434,7 +434,7 @@ func TestHandler_GetChallenge(t *testing.T) { baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"} - url := fmt.Sprintf("%s/acme/%s/challenge/%s/%s", + u := fmt.Sprintf("%s/acme/%s/challenge/%s/%s", baseURL.String(), provName, "authzID", "chID") type test struct { @@ -635,7 +635,7 @@ func TestHandler_GetChallenge(t *testing.T) { AuthorizationID: "authzID", Type: acme.HTTP01, AccountID: "accID", - URL: url, + URL: u, Error: acme.NewError(acme.ErrorConnectionType, "force"), }, vco: &acme.ValidateChallengeOptions{ @@ -652,7 +652,7 @@ func TestHandler_GetChallenge(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db, linker: NewLinker("dns", "acme"), validateChallengeOptions: tc.vco} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.GetChallenge(w, req) @@ -678,7 +678,7 @@ func TestHandler_GetChallenge(t *testing.T) { assert.FatalError(t, err) assert.Equals(t, bytes.TrimSpace(body), expB) assert.Equals(t, res.Header["Link"], []string{fmt.Sprintf("<%s/acme/%s/authz/%s>;rel=\"up\"", baseURL, provName, "authzID")}) - assert.Equals(t, res.Header["Location"], []string{url}) + assert.Equals(t, res.Header["Location"], []string{u}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) diff --git a/acme/api/middleware.go b/acme/api/middleware.go index b2244dd7..bc67dbc6 100644 --- a/acme/api/middleware.go +++ b/acme/api/middleware.go @@ -223,7 +223,7 @@ func (h *Handler) validateJWS(next nextHTTP) nextHTTP { api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive")) return } - if hdr.JSONWebKey == nil && len(hdr.KeyID) == 0 { + if hdr.JSONWebKey == nil && hdr.KeyID == "" { api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header")) return } @@ -367,7 +367,7 @@ func (h *Handler) verifyAndExtractJWSPayload(next nextHTTP) nextHTTP { api.WriteError(w, err) return } - if len(jwk.Algorithm) != 0 && jwk.Algorithm != jws.Signatures[0].Protected.Algorithm { + if jwk.Algorithm != "" && jwk.Algorithm != jws.Signatures[0].Protected.Algorithm { api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match")) return } diff --git a/acme/api/middleware_test.go b/acme/api/middleware_test.go index 40090e83..e8d22d53 100644 --- a/acme/api/middleware_test.go +++ b/acme/api/middleware_test.go @@ -108,7 +108,7 @@ func TestHandler_baseURLFromRequest(t *testing.T) { } func TestHandler_addNonce(t *testing.T) { - url := "https://ca.smallstep.com/acme/new-nonce" + u := "https://ca.smallstep.com/acme/new-nonce" type test struct { db acme.DB err *acme.Error @@ -141,7 +141,7 @@ func TestHandler_addNonce(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) w := httptest.NewRecorder() h.addNonce(testNext)(w, req) res := w.Result() @@ -230,7 +230,7 @@ func TestHandler_verifyContentType(t *testing.T) { prov := newProv() escProvName := url.PathEscape(prov.GetName()) baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"} - url := fmt.Sprintf("%s/acme/%s/certificate/abc123", baseURL.String(), escProvName) + u := fmt.Sprintf("%s/acme/%s/certificate/abc123", baseURL.String(), escProvName) type test struct { h Handler ctx context.Context @@ -245,7 +245,7 @@ func TestHandler_verifyContentType(t *testing.T) { h: Handler{ linker: NewLinker("dns", "acme"), }, - url: url, + url: u, ctx: context.Background(), contentType: "foo", statusCode: 500, @@ -257,7 +257,7 @@ func TestHandler_verifyContentType(t *testing.T) { h: Handler{ linker: NewLinker("dns", "acme"), }, - url: url, + url: u, ctx: context.WithValue(context.Background(), provisionerContextKey, prov), contentType: "foo", statusCode: 400, @@ -319,11 +319,11 @@ func TestHandler_verifyContentType(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - _url := url + _u := u if tc.url != "" { - _url = tc.url + _u = tc.url } - req := httptest.NewRequest("GET", _url, nil) + req := httptest.NewRequest("GET", _u, nil) req = req.WithContext(tc.ctx) req.Header.Add("Content-Type", tc.contentType) w := httptest.NewRecorder() @@ -353,7 +353,7 @@ func TestHandler_verifyContentType(t *testing.T) { } func TestHandler_isPostAsGet(t *testing.T) { - url := "https://ca.smallstep.com/acme/new-account" + u := "https://ca.smallstep.com/acme/new-account" type test struct { ctx context.Context err *acme.Error @@ -392,7 +392,7 @@ func TestHandler_isPostAsGet(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.isPostAsGet(testNext)(w, req) @@ -430,7 +430,7 @@ func (errReader) Close() error { } func TestHandler_parseJWS(t *testing.T) { - url := "https://ca.smallstep.com/acme/new-account" + u := "https://ca.smallstep.com/acme/new-account" type test struct { next nextHTTP body io.Reader @@ -483,7 +483,7 @@ func TestHandler_parseJWS(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{} - req := httptest.NewRequest("GET", url, tc.body) + req := httptest.NewRequest("GET", u, tc.body) w := httptest.NewRecorder() h.parseJWS(tc.next)(w, req) res := w.Result() @@ -528,7 +528,7 @@ func TestHandler_verifyAndExtractJWSPayload(t *testing.T) { assert.FatalError(t, err) parsedJWS, err := jose.ParseJWS(raw) assert.FatalError(t, err) - url := "https://ca.smallstep.com/acme/account/1234" + u := "https://ca.smallstep.com/acme/account/1234" type test struct { ctx context.Context next func(http.ResponseWriter, *http.Request) @@ -681,7 +681,7 @@ func TestHandler_verifyAndExtractJWSPayload(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.verifyAndExtractJWSPayload(tc.next)(w, req) @@ -713,7 +713,7 @@ func TestHandler_lookupJWK(t *testing.T) { prov := newProv() provName := url.PathEscape(prov.GetName()) baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"} - url := fmt.Sprintf("%s/acme/%s/account/1234", + u := fmt.Sprintf("%s/acme/%s/account/1234", baseURL, provName) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) assert.FatalError(t, err) @@ -883,7 +883,7 @@ func TestHandler_lookupJWK(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db, linker: tc.linker} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.lookupJWK(tc.next)(w, req) @@ -934,7 +934,7 @@ func TestHandler_extractJWK(t *testing.T) { assert.FatalError(t, err) parsedJWS, err := jose.ParseJWS(raw) assert.FatalError(t, err) - url := fmt.Sprintf("https://ca.smallstep.com/acme/%s/account/1234", + u := fmt.Sprintf("https://ca.smallstep.com/acme/%s/account/1234", provName) type test struct { db acme.DB @@ -1079,7 +1079,7 @@ func TestHandler_extractJWK(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.extractJWK(tc.next)(w, req) @@ -1108,7 +1108,7 @@ func TestHandler_extractJWK(t *testing.T) { } func TestHandler_validateJWS(t *testing.T) { - url := "https://ca.smallstep.com/acme/account/1234" + u := "https://ca.smallstep.com/acme/account/1234" type test struct { db acme.DB ctx context.Context @@ -1198,7 +1198,7 @@ func TestHandler_validateJWS(t *testing.T) { Algorithm: jose.RS256, JSONWebKey: &pub, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1226,7 +1226,7 @@ func TestHandler_validateJWS(t *testing.T) { Algorithm: jose.RS256, JSONWebKey: &pub, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1298,7 +1298,7 @@ func TestHandler_validateJWS(t *testing.T) { }, ctx: context.WithValue(context.Background(), jwsContextKey, jws), statusCode: 400, - err: acme.NewError(acme.ErrorMalformedType, "url header in JWS (foo) does not match request url (%s)", url), + err: acme.NewError(acme.ErrorMalformedType, "url header in JWS (foo) does not match request url (%s)", u), } }, "fail/both-jwk-kid": func(t *testing.T) test { @@ -1313,7 +1313,7 @@ func TestHandler_validateJWS(t *testing.T) { KeyID: "bar", JSONWebKey: &pub, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1337,7 +1337,7 @@ func TestHandler_validateJWS(t *testing.T) { Protected: jose.Header{ Algorithm: jose.ES256, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1362,7 +1362,7 @@ func TestHandler_validateJWS(t *testing.T) { Algorithm: jose.ES256, KeyID: "bar", ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1392,7 +1392,7 @@ func TestHandler_validateJWS(t *testing.T) { Algorithm: jose.ES256, JSONWebKey: &pub, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1422,7 +1422,7 @@ func TestHandler_validateJWS(t *testing.T) { Algorithm: jose.RS256, JSONWebKey: &pub, ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, + "url": u, }, }, }, @@ -1446,7 +1446,7 @@ func TestHandler_validateJWS(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.validateJWS(tc.next)(w, req) diff --git a/acme/api/order_test.go b/acme/api/order_test.go index afb23c3f..3c6d768f 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -264,7 +264,7 @@ func TestHandler_GetOrder(t *testing.T) { // Request with chi context chiCtx := chi.NewRouteContext() chiCtx.URLParams.Add("ordID", o.ID) - url := fmt.Sprintf("%s/acme/%s/order/%s", + u := fmt.Sprintf("%s/acme/%s/order/%s", baseURL.String(), escProvName, o.ID) type test struct { @@ -422,7 +422,7 @@ func TestHandler_GetOrder(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{linker: NewLinker("dns", "acme"), db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.GetOrder(w, req) @@ -448,7 +448,7 @@ func TestHandler_GetOrder(t *testing.T) { assert.FatalError(t, err) assert.Equals(t, bytes.TrimSpace(body), expB) - assert.Equals(t, res.Header["Location"], []string{url}) + assert.Equals(t, res.Header["Location"], []string{u}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) @@ -663,7 +663,7 @@ func TestHandler_NewOrder(t *testing.T) { prov := newProv() escProvName := url.PathEscape(prov.GetName()) baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"} - url := fmt.Sprintf("%s/acme/%s/order/ordID", + u := fmt.Sprintf("%s/acme/%s/order/ordID", baseURL.String(), escProvName) type test struct { @@ -1335,7 +1335,7 @@ func TestHandler_NewOrder(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{linker: NewLinker("dns", "acme"), db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.NewOrder(w, req) @@ -1363,7 +1363,7 @@ func TestHandler_NewOrder(t *testing.T) { tc.vr(t, ro) } - assert.Equals(t, res.Header["Location"], []string{url}) + assert.Equals(t, res.Header["Location"], []string{u}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) @@ -1406,7 +1406,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { // Request with chi context chiCtx := chi.NewRouteContext() chiCtx.URLParams.Add("ordID", o.ID) - url := fmt.Sprintf("%s/acme/%s/order/%s", + u := fmt.Sprintf("%s/acme/%s/order/%s", baseURL.String(), escProvName, o.ID) _csr, err := pemutil.Read("../../authority/testdata/certs/foo.csr") @@ -1625,7 +1625,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { tc := run(t) t.Run(name, func(t *testing.T) { h := &Handler{linker: NewLinker("dns", "acme"), db: tc.db} - req := httptest.NewRequest("GET", url, nil) + req := httptest.NewRequest("GET", u, nil) req = req.WithContext(tc.ctx) w := httptest.NewRecorder() h.FinalizeOrder(w, req) @@ -1654,7 +1654,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { assert.FatalError(t, json.Unmarshal(body, ro)) assert.Equals(t, bytes.TrimSpace(body), expB) - assert.Equals(t, res.Header["Location"], []string{url}) + assert.Equals(t, res.Header["Location"], []string{u}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) diff --git a/acme/challenge.go b/acme/challenge.go index 70c52578..b880708c 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -76,23 +76,23 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, } func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { - url := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} + u := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} - resp, err := vo.HTTPGet(url.String()) + resp, err := vo.HTTPGet(u.String()) if err != nil { return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, - "error doing http GET for url %s", url)) + "error doing http GET for url %s", u)) } defer resp.Body.Close() if resp.StatusCode >= 400 { return storeError(ctx, db, ch, false, NewError(ErrorConnectionType, - "error doing http GET for url %s with status code %d", url, resp.StatusCode)) + "error doing http GET for url %s with status code %d", u, resp.StatusCode)) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return WrapErrorISE(err, "error reading "+ - "response body for url %s", url) + "response body for url %s", u) } keyAuth := strings.TrimSpace(string(body)) diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 97c5e4cd..a522790f 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -1276,7 +1276,7 @@ func newTLSALPNValidationCert(keyAuthHash []byte, obsoleteOID, critical bool, na oid = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1} } - keyAuthHashEnc, _ := asn1.Marshal(keyAuthHash[:]) + keyAuthHashEnc, _ := asn1.Marshal(keyAuthHash) certTemplate.ExtraExtensions = []pkix.Extension{ { diff --git a/acme/db/nosql/account_test.go b/acme/db/nosql/account_test.go index 5ba99a73..a02e93dc 100644 --- a/acme/db/nosql/account_test.go +++ b/acme/db/nosql/account_test.go @@ -93,8 +93,8 @@ func TestDB_getDBAccount(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if dbacc, err := db.getDBAccount(context.Background(), accID); err != nil { + d := DB{db: tc.db} + if dbacc, err := d.getDBAccount(context.Background(), accID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -109,15 +109,13 @@ func TestDB_getDBAccount(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, dbacc.ID, tc.dbacc.ID) - assert.Equals(t, dbacc.Status, tc.dbacc.Status) - assert.Equals(t, dbacc.CreatedAt, tc.dbacc.CreatedAt) - assert.Equals(t, dbacc.DeactivatedAt, tc.dbacc.DeactivatedAt) - assert.Equals(t, dbacc.Contact, tc.dbacc.Contact) - assert.Equals(t, dbacc.Key.KeyID, tc.dbacc.Key.KeyID) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, dbacc.ID, tc.dbacc.ID) + assert.Equals(t, dbacc.Status, tc.dbacc.Status) + assert.Equals(t, dbacc.CreatedAt, tc.dbacc.CreatedAt) + assert.Equals(t, dbacc.DeactivatedAt, tc.dbacc.DeactivatedAt) + assert.Equals(t, dbacc.Contact, tc.dbacc.Contact) + assert.Equals(t, dbacc.Key.KeyID, tc.dbacc.Key.KeyID) } }) } @@ -174,8 +172,8 @@ func TestDB_getAccountIDByKeyID(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if retAccID, err := db.getAccountIDByKeyID(context.Background(), kid); err != nil { + d := DB{db: tc.db} + if retAccID, err := d.getAccountIDByKeyID(context.Background(), kid); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -190,10 +188,8 @@ func TestDB_getAccountIDByKeyID(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, retAccID, accID) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, retAccID, accID) } }) } @@ -250,8 +246,8 @@ func TestDB_GetAccount(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if acc, err := db.GetAccount(context.Background(), accID); err != nil { + d := DB{db: tc.db} + if acc, err := d.GetAccount(context.Background(), accID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -266,13 +262,11 @@ func TestDB_GetAccount(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, acc.ID, tc.dbacc.ID) - assert.Equals(t, acc.Status, tc.dbacc.Status) - assert.Equals(t, acc.Contact, tc.dbacc.Contact) - assert.Equals(t, acc.Key.KeyID, tc.dbacc.Key.KeyID) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, acc.ID, tc.dbacc.ID) + assert.Equals(t, acc.Status, tc.dbacc.Status) + assert.Equals(t, acc.Contact, tc.dbacc.Contact) + assert.Equals(t, acc.Key.KeyID, tc.dbacc.Key.KeyID) } }) } @@ -358,8 +352,8 @@ func TestDB_GetAccountByKeyID(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if acc, err := db.GetAccountByKeyID(context.Background(), kid); err != nil { + d := DB{db: tc.db} + if acc, err := d.GetAccountByKeyID(context.Background(), kid); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -374,13 +368,11 @@ func TestDB_GetAccountByKeyID(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, acc.ID, tc.dbacc.ID) - assert.Equals(t, acc.Status, tc.dbacc.Status) - assert.Equals(t, acc.Contact, tc.dbacc.Contact) - assert.Equals(t, acc.Key.KeyID, tc.dbacc.Key.KeyID) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, acc.ID, tc.dbacc.ID) + assert.Equals(t, acc.Status, tc.dbacc.Status) + assert.Equals(t, acc.Contact, tc.dbacc.Contact) + assert.Equals(t, acc.Key.KeyID, tc.dbacc.Key.KeyID) } }) } @@ -527,8 +519,8 @@ func TestDB_CreateAccount(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.CreateAccount(context.Background(), tc.acc); err != nil { + d := DB{db: tc.db} + if err := d.CreateAccount(context.Background(), tc.acc); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -688,8 +680,8 @@ func TestDB_UpdateAccount(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.UpdateAccount(context.Background(), tc.acc); err != nil { + d := DB{db: tc.db} + if err := d.UpdateAccount(context.Background(), tc.acc); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } diff --git a/acme/db/nosql/authz_test.go b/acme/db/nosql/authz_test.go index 0c2cec50..01c255dc 100644 --- a/acme/db/nosql/authz_test.go +++ b/acme/db/nosql/authz_test.go @@ -97,8 +97,8 @@ func TestDB_getDBAuthz(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if dbaz, err := db.getDBAuthz(context.Background(), azID); err != nil { + d := DB{db: tc.db} + if dbaz, err := d.getDBAuthz(context.Background(), azID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -113,18 +113,16 @@ func TestDB_getDBAuthz(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, dbaz.ID, tc.dbaz.ID) - assert.Equals(t, dbaz.AccountID, tc.dbaz.AccountID) - assert.Equals(t, dbaz.Identifier, tc.dbaz.Identifier) - assert.Equals(t, dbaz.Status, tc.dbaz.Status) - assert.Equals(t, dbaz.Token, tc.dbaz.Token) - assert.Equals(t, dbaz.CreatedAt, tc.dbaz.CreatedAt) - assert.Equals(t, dbaz.ExpiresAt, tc.dbaz.ExpiresAt) - assert.Equals(t, dbaz.Error.Error(), tc.dbaz.Error.Error()) - assert.Equals(t, dbaz.Wildcard, tc.dbaz.Wildcard) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, dbaz.ID, tc.dbaz.ID) + assert.Equals(t, dbaz.AccountID, tc.dbaz.AccountID) + assert.Equals(t, dbaz.Identifier, tc.dbaz.Identifier) + assert.Equals(t, dbaz.Status, tc.dbaz.Status) + assert.Equals(t, dbaz.Token, tc.dbaz.Token) + assert.Equals(t, dbaz.CreatedAt, tc.dbaz.CreatedAt) + assert.Equals(t, dbaz.ExpiresAt, tc.dbaz.ExpiresAt) + assert.Equals(t, dbaz.Error.Error(), tc.dbaz.Error.Error()) + assert.Equals(t, dbaz.Wildcard, tc.dbaz.Wildcard) } }) } @@ -293,8 +291,8 @@ func TestDB_GetAuthorization(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if az, err := db.GetAuthorization(context.Background(), azID); err != nil { + d := DB{db: tc.db} + if az, err := d.GetAuthorization(context.Background(), azID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -309,21 +307,19 @@ func TestDB_GetAuthorization(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, az.ID, tc.dbaz.ID) - assert.Equals(t, az.AccountID, tc.dbaz.AccountID) - assert.Equals(t, az.Identifier, tc.dbaz.Identifier) - assert.Equals(t, az.Status, tc.dbaz.Status) - assert.Equals(t, az.Token, tc.dbaz.Token) - assert.Equals(t, az.Wildcard, tc.dbaz.Wildcard) - assert.Equals(t, az.ExpiresAt, tc.dbaz.ExpiresAt) - assert.Equals(t, az.Challenges, []*acme.Challenge{ - {ID: "foo"}, - {ID: "bar"}, - }) - assert.Equals(t, az.Error.Error(), tc.dbaz.Error.Error()) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, az.ID, tc.dbaz.ID) + assert.Equals(t, az.AccountID, tc.dbaz.AccountID) + assert.Equals(t, az.Identifier, tc.dbaz.Identifier) + assert.Equals(t, az.Status, tc.dbaz.Status) + assert.Equals(t, az.Token, tc.dbaz.Token) + assert.Equals(t, az.Wildcard, tc.dbaz.Wildcard) + assert.Equals(t, az.ExpiresAt, tc.dbaz.ExpiresAt) + assert.Equals(t, az.Challenges, []*acme.Challenge{ + {ID: "foo"}, + {ID: "bar"}, + }) + assert.Equals(t, az.Error.Error(), tc.dbaz.Error.Error()) } }) } @@ -445,8 +441,8 @@ func TestDB_CreateAuthorization(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.CreateAuthorization(context.Background(), tc.az); err != nil { + d := DB{db: tc.db} + if err := d.CreateAuthorization(context.Background(), tc.az); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -594,8 +590,8 @@ func TestDB_UpdateAuthorization(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.UpdateAuthorization(context.Background(), tc.az); err != nil { + d := DB{db: tc.db} + if err := d.UpdateAuthorization(context.Background(), tc.az); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } diff --git a/acme/db/nosql/certificate_test.go b/acme/db/nosql/certificate_test.go index 4ec4589e..37a61352 100644 --- a/acme/db/nosql/certificate_test.go +++ b/acme/db/nosql/certificate_test.go @@ -98,8 +98,8 @@ func TestDB_CreateCertificate(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.CreateCertificate(context.Background(), tc.cert); err != nil { + d := DB{db: tc.db} + if err := d.CreateCertificate(context.Background(), tc.cert); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -228,8 +228,8 @@ func TestDB_GetCertificate(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - cert, err := db.GetCertificate(context.Background(), certID) + d := DB{db: tc.db} + cert, err := d.GetCertificate(context.Background(), certID) if err != nil { switch k := err.(type) { case *acme.Error: @@ -245,14 +245,12 @@ func TestDB_GetCertificate(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, cert.ID, certID) - assert.Equals(t, cert.AccountID, "accountID") - assert.Equals(t, cert.OrderID, "orderID") - assert.Equals(t, cert.Leaf, leaf) - assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, cert.ID, certID) + assert.Equals(t, cert.AccountID, "accountID") + assert.Equals(t, cert.OrderID, "orderID") + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) } }) } diff --git a/acme/db/nosql/challenge_test.go b/acme/db/nosql/challenge_test.go index b39395e8..4da5679b 100644 --- a/acme/db/nosql/challenge_test.go +++ b/acme/db/nosql/challenge_test.go @@ -92,8 +92,8 @@ func TestDB_getDBChallenge(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if ch, err := db.getDBChallenge(context.Background(), chID); err != nil { + d := DB{db: tc.db} + if ch, err := d.getDBChallenge(context.Background(), chID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -108,17 +108,15 @@ func TestDB_getDBChallenge(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, ch.ID, tc.dbc.ID) - assert.Equals(t, ch.AccountID, tc.dbc.AccountID) - assert.Equals(t, ch.Type, tc.dbc.Type) - assert.Equals(t, ch.Status, tc.dbc.Status) - assert.Equals(t, ch.Token, tc.dbc.Token) - assert.Equals(t, ch.Value, tc.dbc.Value) - assert.Equals(t, ch.ValidatedAt, tc.dbc.ValidatedAt) - assert.Equals(t, ch.Error.Error(), tc.dbc.Error.Error()) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, ch.ID, tc.dbc.ID) + assert.Equals(t, ch.AccountID, tc.dbc.AccountID) + assert.Equals(t, ch.Type, tc.dbc.Type) + assert.Equals(t, ch.Status, tc.dbc.Status) + assert.Equals(t, ch.Token, tc.dbc.Token) + assert.Equals(t, ch.Value, tc.dbc.Value) + assert.Equals(t, ch.ValidatedAt, tc.dbc.ValidatedAt) + assert.Equals(t, ch.Error.Error(), tc.dbc.Error.Error()) } }) } @@ -206,8 +204,8 @@ func TestDB_CreateChallenge(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.CreateChallenge(context.Background(), tc.ch); err != nil { + d := DB{db: tc.db} + if err := d.CreateChallenge(context.Background(), tc.ch); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -286,8 +284,8 @@ func TestDB_GetChallenge(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if ch, err := db.GetChallenge(context.Background(), chID, azID); err != nil { + d := DB{db: tc.db} + if ch, err := d.GetChallenge(context.Background(), chID, azID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -302,17 +300,15 @@ func TestDB_GetChallenge(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, ch.ID, tc.dbc.ID) - assert.Equals(t, ch.AccountID, tc.dbc.AccountID) - assert.Equals(t, ch.Type, tc.dbc.Type) - assert.Equals(t, ch.Status, tc.dbc.Status) - assert.Equals(t, ch.Token, tc.dbc.Token) - assert.Equals(t, ch.Value, tc.dbc.Value) - assert.Equals(t, ch.ValidatedAt, tc.dbc.ValidatedAt) - assert.Equals(t, ch.Error.Error(), tc.dbc.Error.Error()) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, ch.ID, tc.dbc.ID) + assert.Equals(t, ch.AccountID, tc.dbc.AccountID) + assert.Equals(t, ch.Type, tc.dbc.Type) + assert.Equals(t, ch.Status, tc.dbc.Status) + assert.Equals(t, ch.Token, tc.dbc.Token) + assert.Equals(t, ch.Value, tc.dbc.Value) + assert.Equals(t, ch.ValidatedAt, tc.dbc.ValidatedAt) + assert.Equals(t, ch.Error.Error(), tc.dbc.Error.Error()) } }) } @@ -442,8 +438,8 @@ func TestDB_UpdateChallenge(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.UpdateChallenge(context.Background(), tc.ch); err != nil { + d := DB{db: tc.db} + if err := d.UpdateChallenge(context.Background(), tc.ch); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } diff --git a/acme/db/nosql/nonce.go b/acme/db/nosql/nonce.go index 9badae87..e438c9ed 100644 --- a/acme/db/nosql/nonce.go +++ b/acme/db/nosql/nonce.go @@ -31,7 +31,7 @@ func (db *DB) CreateNonce(ctx context.Context) (acme.Nonce, error) { ID: id, CreatedAt: clock.Now(), } - if err = db.save(ctx, id, n, nil, "nonce", nonceTable); err != nil { + if err := db.save(ctx, id, n, nil, "nonce", nonceTable); err != nil { return "", err } return acme.Nonce(id), nil diff --git a/acme/db/nosql/nonce_test.go b/acme/db/nosql/nonce_test.go index 05d73d52..7dc5cc91 100644 --- a/acme/db/nosql/nonce_test.go +++ b/acme/db/nosql/nonce_test.go @@ -67,8 +67,8 @@ func TestDB_CreateNonce(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if n, err := db.CreateNonce(context.Background()); err != nil { + d := DB{db: tc.db} + if n, err := d.CreateNonce(context.Background()); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -144,8 +144,8 @@ func TestDB_DeleteNonce(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.DeleteNonce(context.Background(), acme.Nonce(nonceID)); err != nil { + d := DB{db: tc.db} + if err := d.DeleteNonce(context.Background(), acme.Nonce(nonceID)); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go index 052f5729..b1547373 100644 --- a/acme/db/nosql/nosql.go +++ b/acme/db/nosql/nosql.go @@ -41,7 +41,7 @@ func New(db nosqlDB.DB) (*DB, error) { // save writes the new data to the database, overwriting the old data if it // existed. -func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface{}, typ string, table []byte) error { +func (db *DB) save(ctx context.Context, id string, nu, old interface{}, typ string, table []byte) error { var ( err error newB []byte diff --git a/acme/db/nosql/nosql_test.go b/acme/db/nosql/nosql_test.go index 4396acc8..d9c0b484 100644 --- a/acme/db/nosql/nosql_test.go +++ b/acme/db/nosql/nosql_test.go @@ -126,8 +126,8 @@ func TestDB_save(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - db := &DB{db: tc.db} - if err := db.save(context.Background(), "id", tc.nu, tc.old, "challenge", challengeTable); err != nil { + d := &DB{db: tc.db} + if err := d.save(context.Background(), "id", tc.nu, tc.old, "challenge", challengeTable); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } diff --git a/acme/db/nosql/order_test.go b/acme/db/nosql/order_test.go index 8882fd82..e92eb684 100644 --- a/acme/db/nosql/order_test.go +++ b/acme/db/nosql/order_test.go @@ -13,7 +13,6 @@ import ( "github.com/smallstep/certificates/db" "github.com/smallstep/nosql" "github.com/smallstep/nosql/database" - nosqldb "github.com/smallstep/nosql/database" ) func TestDB_getDBOrder(t *testing.T) { @@ -32,7 +31,7 @@ func TestDB_getDBOrder(t *testing.T) { assert.Equals(t, bucket, orderTable) assert.Equals(t, string(key), orderID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, acmeErr: acme.NewError(acme.ErrorMalformedType, "order orderID not found"), @@ -101,8 +100,8 @@ func TestDB_getDBOrder(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if dbo, err := db.getDBOrder(context.Background(), orderID); err != nil { + d := DB{db: tc.db} + if dbo, err := d.getDBOrder(context.Background(), orderID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -117,20 +116,18 @@ func TestDB_getDBOrder(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, dbo.ID, tc.dbo.ID) - assert.Equals(t, dbo.ProvisionerID, tc.dbo.ProvisionerID) - assert.Equals(t, dbo.CertificateID, tc.dbo.CertificateID) - assert.Equals(t, dbo.Status, tc.dbo.Status) - assert.Equals(t, dbo.CreatedAt, tc.dbo.CreatedAt) - assert.Equals(t, dbo.ExpiresAt, tc.dbo.ExpiresAt) - assert.Equals(t, dbo.NotBefore, tc.dbo.NotBefore) - assert.Equals(t, dbo.NotAfter, tc.dbo.NotAfter) - assert.Equals(t, dbo.Identifiers, tc.dbo.Identifiers) - assert.Equals(t, dbo.AuthorizationIDs, tc.dbo.AuthorizationIDs) - assert.Equals(t, dbo.Error.Error(), tc.dbo.Error.Error()) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, dbo.ID, tc.dbo.ID) + assert.Equals(t, dbo.ProvisionerID, tc.dbo.ProvisionerID) + assert.Equals(t, dbo.CertificateID, tc.dbo.CertificateID) + assert.Equals(t, dbo.Status, tc.dbo.Status) + assert.Equals(t, dbo.CreatedAt, tc.dbo.CreatedAt) + assert.Equals(t, dbo.ExpiresAt, tc.dbo.ExpiresAt) + assert.Equals(t, dbo.NotBefore, tc.dbo.NotBefore) + assert.Equals(t, dbo.NotAfter, tc.dbo.NotAfter) + assert.Equals(t, dbo.Identifiers, tc.dbo.Identifiers) + assert.Equals(t, dbo.AuthorizationIDs, tc.dbo.AuthorizationIDs) + assert.Equals(t, dbo.Error.Error(), tc.dbo.Error.Error()) } }) } @@ -165,7 +162,7 @@ func TestDB_GetOrder(t *testing.T) { assert.Equals(t, bucket, orderTable) assert.Equals(t, string(key), orderID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, acmeErr: acme.NewError(acme.ErrorMalformedType, "order orderID not found"), @@ -207,8 +204,8 @@ func TestDB_GetOrder(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if o, err := db.GetOrder(context.Background(), orderID); err != nil { + d := DB{db: tc.db} + if o, err := d.GetOrder(context.Background(), orderID); err != nil { switch k := err.(type) { case *acme.Error: if assert.NotNil(t, tc.acmeErr) { @@ -223,20 +220,18 @@ func TestDB_GetOrder(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, o.ID, tc.dbo.ID) - assert.Equals(t, o.AccountID, tc.dbo.AccountID) - assert.Equals(t, o.ProvisionerID, tc.dbo.ProvisionerID) - assert.Equals(t, o.CertificateID, tc.dbo.CertificateID) - assert.Equals(t, o.Status, tc.dbo.Status) - assert.Equals(t, o.ExpiresAt, tc.dbo.ExpiresAt) - assert.Equals(t, o.NotBefore, tc.dbo.NotBefore) - assert.Equals(t, o.NotAfter, tc.dbo.NotAfter) - assert.Equals(t, o.Identifiers, tc.dbo.Identifiers) - assert.Equals(t, o.AuthorizationIDs, tc.dbo.AuthorizationIDs) - assert.Equals(t, o.Error.Error(), tc.dbo.Error.Error()) - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, o.ID, tc.dbo.ID) + assert.Equals(t, o.AccountID, tc.dbo.AccountID) + assert.Equals(t, o.ProvisionerID, tc.dbo.ProvisionerID) + assert.Equals(t, o.CertificateID, tc.dbo.CertificateID) + assert.Equals(t, o.Status, tc.dbo.Status) + assert.Equals(t, o.ExpiresAt, tc.dbo.ExpiresAt) + assert.Equals(t, o.NotBefore, tc.dbo.NotBefore) + assert.Equals(t, o.NotAfter, tc.dbo.NotAfter) + assert.Equals(t, o.Identifiers, tc.dbo.Identifiers) + assert.Equals(t, o.AuthorizationIDs, tc.dbo.AuthorizationIDs) + assert.Equals(t, o.Error.Error(), tc.dbo.Error.Error()) } }) } @@ -367,8 +362,8 @@ func TestDB_UpdateOrder(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.UpdateOrder(context.Background(), tc.o); err != nil { + d := DB{db: tc.db} + if err := d.UpdateOrder(context.Background(), tc.o); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -512,7 +507,7 @@ func TestDB_CreateOrder(t *testing.T) { MGet: func(bucket, key []byte) ([]byte, error) { assert.Equals(t, string(bucket), string(ordersByAccountIDTable)) assert.Equals(t, string(key), o.AccountID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { switch string(bucket) { @@ -558,8 +553,8 @@ func TestDB_CreateOrder(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if err := db.CreateOrder(context.Background(), tc.o); err != nil { + d := DB{db: tc.db} + if err := d.CreateOrder(context.Background(), tc.o); err != nil { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } @@ -681,7 +676,7 @@ func TestDB_updateAddOrderIDs(t *testing.T) { MGet: func(bucket, key []byte) ([]byte, error) { assert.Equals(t, bucket, ordersByAccountIDTable) assert.Equals(t, key, []byte(accID)) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { assert.Equals(t, bucket, ordersByAccountIDTable) @@ -996,15 +991,15 @@ func TestDB_updateAddOrderIDs(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} + d := DB{db: tc.db} var ( res []string err error ) if tc.addOids == nil { - res, err = db.updateAddOrderIDs(context.Background(), accID) + res, err = d.updateAddOrderIDs(context.Background(), accID) } else { - res, err = db.updateAddOrderIDs(context.Background(), accID, tc.addOids...) + res, err = d.updateAddOrderIDs(context.Background(), accID, tc.addOids...) } if err != nil { @@ -1022,10 +1017,8 @@ func TestDB_updateAddOrderIDs(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.True(t, reflect.DeepEqual(res, tc.res)) - } + } else if assert.Nil(t, tc.err) { + assert.True(t, reflect.DeepEqual(res, tc.res)) } }) } diff --git a/acme/order.go b/acme/order.go index fd8956f7..237c6979 100644 --- a/acme/order.go +++ b/acme/order.go @@ -289,6 +289,7 @@ func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.Certificate // name or in an extensionRequest attribute [RFC2985] requesting a // subjectAltName extension, or both. if csr.Subject.CommonName != "" { + // nolint:gocritic canonicalized.DNSNames = append(csr.DNSNames, csr.Subject.CommonName) } canonicalized.DNSNames = uniqueSortedLowerNames(csr.DNSNames) diff --git a/api/api.go b/api/api.go index 5be9ecc1..30ba03f9 100644 --- a/api/api.go +++ b/api/api.go @@ -240,9 +240,9 @@ type caHandler struct { } // New creates a new RouterHandler with the CA endpoints. -func New(authority Authority) RouterHandler { +func New(auth Authority) RouterHandler { return &caHandler{ - Authority: authority, + Authority: auth, } } @@ -295,7 +295,7 @@ func (h *caHandler) Health(w http.ResponseWriter, r *http.Request) { // certificate for the given SHA256. func (h *caHandler) Root(w http.ResponseWriter, r *http.Request) { sha := chi.URLParam(r, "sha") - sum := strings.ToLower(strings.Replace(sha, "-", "", -1)) + sum := strings.ToLower(strings.ReplaceAll(sha, "-", "")) // Load root certificate with the cert, err := h.Authority.Root(sum) if err != nil { @@ -409,19 +409,20 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { "certificate": base64.StdEncoding.EncodeToString(cert.Raw), } for _, ext := range cert.Extensions { - if ext.Id.Equal(oidStepProvisioner) { - val := &stepProvisioner{} - rest, err := asn1.Unmarshal(ext.Value, val) - if err != nil || len(rest) > 0 { - break - } - if len(val.CredentialID) > 0 { - m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) - } else { - m["provisioner"] = string(val.Name) - } + if !ext.Id.Equal(oidStepProvisioner) { + continue + } + val := &stepProvisioner{} + rest, err := asn1.Unmarshal(ext.Value, val) + if err != nil || len(rest) > 0 { break } + if len(val.CredentialID) > 0 { + m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) + } else { + m["provisioner"] = string(val.Name) + } + break } rl.WithFields(m) } diff --git a/api/api_test.go b/api/api_test.go index 33d2bae7..89596165 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -186,8 +186,8 @@ func TestCertificate_MarshalJSON(t *testing.T) { }{ {"nil", fields{Certificate: nil}, []byte("null"), false}, {"empty", fields{Certificate: &x509.Certificate{Raw: nil}}, []byte(`"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n"`), false}, - {"root", fields{Certificate: parseCertificate(rootPEM)}, []byte(`"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"`), false}, - {"cert", fields{Certificate: parseCertificate(certPEM)}, []byte(`"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n"`), false}, + {"root", fields{Certificate: parseCertificate(rootPEM)}, []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"`), false}, + {"cert", fields{Certificate: parseCertificate(certPEM)}, []byte(`"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n"`), false}, } for _, tt := range tests { @@ -219,11 +219,11 @@ func TestCertificate_UnmarshalJSON(t *testing.T) { {"invalid string", []byte(`"foobar"`), false, true}, {"invalid bytes 0", []byte{}, false, true}, {"invalid bytes 1", []byte{1}, false, true}, {"empty csr", []byte(`"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE----\n"`), false, true}, - {"invalid type", []byte(`"` + strings.Replace(csrPEM, "\n", `\n`, -1) + `"`), false, true}, + {"invalid type", []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"`), false, true}, {"empty string", []byte(`""`), false, false}, {"json null", []byte(`null`), false, false}, - {"valid root", []byte(`"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `"`), true, false}, - {"valid cert", []byte(`"` + strings.Replace(certPEM, "\n", `\n`, -1) + `"`), true, false}, + {"valid root", []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `"`), true, false}, + {"valid cert", []byte(`"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `"`), true, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -251,7 +251,7 @@ func TestCertificate_UnmarshalJSON_json(t *testing.T) { {"empty crt (null)", `{"crt":null}`, false, false}, {"empty crt (string)", `{"crt":""}`, false, false}, {"empty crt", `{"crt":"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE----\n"}`, false, true}, - {"valid crt", `{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `"}`, true, false}, + {"valid crt", `{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `"}`, true, false}, } type request struct { @@ -297,7 +297,7 @@ func TestCertificateRequest_MarshalJSON(t *testing.T) { }{ {"nil", fields{CertificateRequest: nil}, []byte("null"), false}, {"empty", fields{CertificateRequest: &x509.CertificateRequest{}}, []byte(`"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST-----\n"`), false}, - {"csr", fields{CertificateRequest: parseCertificateRequest(csrPEM)}, []byte(`"` + strings.Replace(csrPEM, "\n", `\n`, -1) + `\n"`), false}, + {"csr", fields{CertificateRequest: parseCertificateRequest(csrPEM)}, []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `\n"`), false}, } for _, tt := range tests { @@ -329,10 +329,10 @@ func TestCertificateRequest_UnmarshalJSON(t *testing.T) { {"invalid string", []byte(`"foobar"`), false, true}, {"invalid bytes 0", []byte{}, false, true}, {"invalid bytes 1", []byte{1}, false, true}, {"empty csr", []byte(`"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST----\n"`), false, true}, - {"invalid type", []byte(`"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `"`), false, true}, + {"invalid type", []byte(`"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `"`), false, true}, {"empty string", []byte(`""`), false, false}, {"json null", []byte(`null`), false, false}, - {"valid csr", []byte(`"` + strings.Replace(csrPEM, "\n", `\n`, -1) + `"`), true, false}, + {"valid csr", []byte(`"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"`), true, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -360,7 +360,7 @@ func TestCertificateRequest_UnmarshalJSON_json(t *testing.T) { {"empty csr (null)", `{"csr":null}`, false, false}, {"empty csr (string)", `{"csr":""}`, false, false}, {"empty csr", `{"csr":"-----BEGIN CERTIFICATE REQUEST-----\n-----END CERTIFICATE REQUEST----\n"}`, false, true}, - {"valid csr", `{"csr":"` + strings.Replace(csrPEM, "\n", `\n`, -1) + `"}`, true, false}, + {"valid csr", `{"csr":"` + strings.ReplaceAll(csrPEM, "\n", `\n`) + `"}`, true, false}, } type request struct { @@ -739,7 +739,7 @@ func (m *mockAuthority) CheckSSHHost(ctx context.Context, principal, token strin return m.ret1.(bool), m.err } -func (m *mockAuthority) GetSSHBastion(ctx context.Context, user string, hostname string) (*authority.Bastion, error) { +func (m *mockAuthority) GetSSHBastion(ctx context.Context, user, hostname string) (*authority.Bastion, error) { if m.getSSHBastion != nil { return m.getSSHBastion(ctx, user, hostname) } @@ -816,7 +816,7 @@ func Test_caHandler_Root(t *testing.T) { req := httptest.NewRequest("GET", "http://example.com/root/efc7d6b475a56fe587650bcdb999a4a308f815ba44db4bf0371ea68a786ccd36", nil) req = req.WithContext(context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)) - expected := []byte(`{"ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"}`) + expected := []byte(`{"ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"}`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -860,8 +860,8 @@ func Test_caHandler_Sign(t *testing.T) { t.Fatal(err) } - expected1 := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) - expected2 := []byte(`{"crt":"` + strings.Replace(stepCertPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(stepCertPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) + expected1 := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) + expected2 := []byte(`{"crt":"` + strings.ReplaceAll(stepCertPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(stepCertPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) tests := []struct { name string @@ -934,7 +934,7 @@ func Test_caHandler_Renew(t *testing.T) { {"renew error", cs, nil, nil, errs.Forbidden("an error"), http.StatusForbidden}, } - expected := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) + expected := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -995,7 +995,7 @@ func Test_caHandler_Rekey(t *testing.T) { {"json read error", "{", cs, nil, nil, nil, http.StatusBadRequest}, } - expected := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) + expected := []byte(`{"crt":"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","ca":"` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n","certChain":["` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n","` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1210,7 +1210,7 @@ func Test_caHandler_Roots(t *testing.T) { {"fail", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden}, } - expected := []byte(`{"crts":["` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) + expected := []byte(`{"crts":["` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1256,7 +1256,7 @@ func Test_caHandler_Federation(t *testing.T) { {"fail", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden}, } - expected := []byte(`{"crts":["` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`) + expected := []byte(`{"crts":["` + strings.ReplaceAll(rootPEM, "\n", `\n`) + `\n"]}`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/errors.go b/api/errors.go index db3bc3e2..bff46b55 100644 --- a/api/errors.go +++ b/api/errors.go @@ -50,12 +50,10 @@ func WriteError(w http.ResponseWriter, err error) { rl.WithFields(map[string]interface{}{ "stack-trace": fmt.Sprintf("%+v", e), }) - } else { - if e, ok := cause.(errs.StackTracer); ok { - rl.WithFields(map[string]interface{}{ - "stack-trace": fmt.Sprintf("%+v", e), - }) - } + } else if e, ok := cause.(errs.StackTracer); ok { + rl.WithFields(map[string]interface{}{ + "stack-trace": fmt.Sprintf("%+v", e), + }) } } } diff --git a/api/ssh.go b/api/ssh.go index 8c0c1aa3..7c7a5acd 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -52,7 +52,7 @@ func (s *SSHSignRequest) Validate() error { return errors.Errorf("unknown certType %s", s.CertType) case len(s.PublicKey) == 0: return errors.New("missing or empty publicKey") - case len(s.OTT) == 0: + case s.OTT == "": return errors.New("missing or empty ott") default: // Validate identity signature if provided @@ -408,18 +408,18 @@ func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) { return } - var config SSHConfigResponse + var cfg SSHConfigResponse switch body.Type { case provisioner.SSHUserCert: - config.UserTemplates = ts + cfg.UserTemplates = ts case provisioner.SSHHostCert: - config.HostTemplates = ts + cfg.HostTemplates = ts default: WriteError(w, errs.InternalServer("it should hot get here")) return } - JSON(w, config) + JSON(w, cfg) } // SSHCheckHost is the HTTP handler that returns if a hosts certificate exists or not. diff --git a/api/sshRekey.go b/api/sshRekey.go index 3d8e7c47..9d9e17cf 100644 --- a/api/sshRekey.go +++ b/api/sshRekey.go @@ -19,7 +19,7 @@ type SSHRekeyRequest struct { // Validate validates the SSHSignRekey. func (s *SSHRekeyRequest) Validate() error { switch { - case len(s.OTT) == 0: + case s.OTT == "": return errors.New("missing or empty ott") case len(s.PublicKey) == 0: return errors.New("missing or empty public key") diff --git a/api/sshRenew.go b/api/sshRenew.go index cb6ec5fd..d0633ecf 100644 --- a/api/sshRenew.go +++ b/api/sshRenew.go @@ -18,7 +18,7 @@ type SSHRenewRequest struct { // Validate validates the SSHSignRequest. func (s *SSHRenewRequest) Validate() error { switch { - case len(s.OTT) == 0: + case s.OTT == "": return errors.New("missing or empty ott") default: return nil diff --git a/api/sshRevoke.go b/api/sshRevoke.go index 5a1c858c..c6ebe99d 100644 --- a/api/sshRevoke.go +++ b/api/sshRevoke.go @@ -36,7 +36,7 @@ func (r *SSHRevokeRequest) Validate() (err error) { if !r.Passive { return errs.NotImplemented("non-passive revocation not implemented") } - if len(r.OTT) == 0 { + if r.OTT == "" { return errs.BadRequest("missing ott") } return diff --git a/api/ssh_test.go b/api/ssh_test.go index 1873a96d..a2e8748f 100644 --- a/api/ssh_test.go +++ b/api/ssh_test.go @@ -284,7 +284,7 @@ func Test_caHandler_SSHSign(t *testing.T) { identityCerts := []*x509.Certificate{ parseCertificate(certPEM), } - identityCertsPEM := []byte(`"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n"`) + identityCertsPEM := []byte(`"` + strings.ReplaceAll(certPEM, "\n", `\n`) + `\n"`) tests := []struct { name string diff --git a/authority/admin/api/middleware.go b/authority/admin/api/middleware.go index 90289f85..19025a9d 100644 --- a/authority/admin/api/middleware.go +++ b/authority/admin/api/middleware.go @@ -27,7 +27,7 @@ func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP { func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { tok := r.Header.Get("Authorization") - if len(tok) == 0 { + if tok == "" { api.WriteError(w, admin.NewError(admin.ErrorUnauthorizedType, "missing authorization header token")) return diff --git a/authority/admin/db/nosql/admin_test.go b/authority/admin/db/nosql/admin_test.go index 092d72db..4234d526 100644 --- a/authority/admin/db/nosql/admin_test.go +++ b/authority/admin/db/nosql/admin_test.go @@ -12,7 +12,6 @@ import ( "github.com/smallstep/certificates/db" "github.com/smallstep/nosql" "github.com/smallstep/nosql/database" - nosqldb "github.com/smallstep/nosql/database" "go.step.sm/linkedca" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -32,7 +31,7 @@ func TestDB_getDBAdminBytes(t *testing.T) { assert.Equals(t, bucket, adminsTable) assert.Equals(t, string(key), adminID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), @@ -67,8 +66,8 @@ func TestDB_getDBAdminBytes(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if b, err := db.getDBAdminBytes(context.Background(), adminID); err != nil { + d := DB{db: tc.db} + if b, err := d.getDBAdminBytes(context.Background(), adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -83,10 +82,8 @@ func TestDB_getDBAdminBytes(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) { - assert.Equals(t, string(b), "foo") - } + } else if assert.Nil(t, tc.err) { + assert.Equals(t, string(b), "foo") } }) } @@ -108,7 +105,7 @@ func TestDB_getDBAdmin(t *testing.T) { assert.Equals(t, bucket, adminsTable) assert.Equals(t, string(key), adminID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), @@ -193,8 +190,8 @@ func TestDB_getDBAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if dba, err := db.getDBAdmin(context.Background(), adminID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dba, err := d.getDBAdmin(context.Background(), adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -209,16 +206,14 @@ func TestDB_getDBAdmin(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, dba.ID, adminID) - assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) - assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) - assert.Equals(t, dba.Subject, tc.dba.Subject) - assert.Equals(t, dba.Type, tc.dba.Type) - assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) - assert.Fatal(t, dba.DeletedAt.IsZero()) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) } }) } @@ -283,8 +278,8 @@ func TestDB_unmarshalDBAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{authorityID: admin.DefaultAuthorityID} - if dba, err := db.unmarshalDBAdmin(tc.in, adminID); err != nil { + d := DB{authorityID: admin.DefaultAuthorityID} + if dba, err := d.unmarshalDBAdmin(tc.in, adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -299,16 +294,14 @@ func TestDB_unmarshalDBAdmin(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, dba.ID, adminID) - assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) - assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) - assert.Equals(t, dba.Subject, tc.dba.Subject) - assert.Equals(t, dba.Type, tc.dba.Type) - assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) - assert.Fatal(t, dba.DeletedAt.IsZero()) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) } }) } @@ -360,8 +353,8 @@ func TestDB_unmarshalAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{authorityID: admin.DefaultAuthorityID} - if adm, err := db.unmarshalAdmin(tc.in, adminID); err != nil { + d := DB{authorityID: admin.DefaultAuthorityID} + if adm, err := d.unmarshalAdmin(tc.in, adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -376,16 +369,14 @@ func TestDB_unmarshalAdmin(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, adm.Id, adminID) - assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) - assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) - assert.Equals(t, adm.Subject, tc.dba.Subject) - assert.Equals(t, adm.Type, tc.dba.Type) - assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) - assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) } }) } @@ -407,7 +398,7 @@ func TestDB_GetAdmin(t *testing.T) { assert.Equals(t, bucket, adminsTable) assert.Equals(t, string(key), adminID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), @@ -516,8 +507,8 @@ func TestDB_GetAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if adm, err := db.GetAdmin(context.Background(), adminID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if adm, err := d.GetAdmin(context.Background(), adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -532,16 +523,14 @@ func TestDB_GetAdmin(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, adm.Id, adminID) - assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) - assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) - assert.Equals(t, adm.Subject, tc.dba.Subject) - assert.Equals(t, adm.Type, tc.dba.Type) - assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) - assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) } }) } @@ -562,7 +551,7 @@ func TestDB_DeleteAdmin(t *testing.T) { assert.Equals(t, bucket, adminsTable) assert.Equals(t, string(key), adminID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), @@ -670,8 +659,8 @@ func TestDB_DeleteAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.DeleteAdmin(context.Background(), adminID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.DeleteAdmin(context.Background(), adminID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -708,7 +697,7 @@ func TestDB_UpdateAdmin(t *testing.T) { assert.Equals(t, bucket, adminsTable) assert.Equals(t, string(key), adminID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), @@ -821,8 +810,8 @@ func TestDB_UpdateAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.UpdateAdmin(context.Background(), tc.adm); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.UpdateAdmin(context.Background(), tc.adm); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -919,8 +908,8 @@ func TestDB_CreateAdmin(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.CreateAdmin(context.Background(), tc.adm); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.CreateAdmin(context.Background(), tc.adm); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -1095,8 +1084,8 @@ func TestDB_GetAdmins(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if admins, err := db.GetAdmins(context.Background()); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if admins, err := d.GetAdmins(context.Background()); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -1111,10 +1100,8 @@ func TestDB_GetAdmins(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - tc.verify(t, admins) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, admins) } }) } diff --git a/authority/admin/db/nosql/nosql.go b/authority/admin/db/nosql/nosql.go index 18599b02..22b049f5 100644 --- a/authority/admin/db/nosql/nosql.go +++ b/authority/admin/db/nosql/nosql.go @@ -35,7 +35,7 @@ func New(db nosqlDB.DB, authorityID string) (*DB, error) { // save writes the new data to the database, overwriting the old data if it // existed. -func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface{}, typ string, table []byte) error { +func (db *DB) save(ctx context.Context, id string, nu, old interface{}, typ string, table []byte) error { var ( err error newB []byte diff --git a/authority/admin/db/nosql/provisioner_test.go b/authority/admin/db/nosql/provisioner_test.go index 95811f26..e599ea04 100644 --- a/authority/admin/db/nosql/provisioner_test.go +++ b/authority/admin/db/nosql/provisioner_test.go @@ -12,7 +12,6 @@ import ( "github.com/smallstep/certificates/db" "github.com/smallstep/nosql" "github.com/smallstep/nosql/database" - nosqldb "github.com/smallstep/nosql/database" "go.step.sm/linkedca" ) @@ -31,7 +30,7 @@ func TestDB_getDBProvisionerBytes(t *testing.T) { assert.Equals(t, bucket, provisionersTable) assert.Equals(t, string(key), provID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), @@ -66,8 +65,8 @@ func TestDB_getDBProvisionerBytes(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db} - if b, err := db.getDBProvisionerBytes(context.Background(), provID); err != nil { + d := DB{db: tc.db} + if b, err := d.getDBProvisionerBytes(context.Background(), provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -82,10 +81,8 @@ func TestDB_getDBProvisionerBytes(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, string(b), "foo") - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, string(b), "foo") } }) } @@ -107,7 +104,7 @@ func TestDB_getDBProvisioner(t *testing.T) { assert.Equals(t, bucket, provisionersTable) assert.Equals(t, string(key), provID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), @@ -190,8 +187,8 @@ func TestDB_getDBProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if dbp, err := db.getDBProvisioner(context.Background(), provID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dbp, err := d.getDBProvisioner(context.Background(), provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -206,15 +203,13 @@ func TestDB_getDBProvisioner(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, dbp.ID, provID) - assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) - assert.Equals(t, dbp.Type, tc.dbp.Type) - assert.Equals(t, dbp.Name, tc.dbp.Name) - assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) - assert.Fatal(t, dbp.DeletedAt.IsZero()) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) } }) } @@ -278,8 +273,8 @@ func TestDB_unmarshalDBProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{authorityID: admin.DefaultAuthorityID} - if dbp, err := db.unmarshalDBProvisioner(tc.in, provID); err != nil { + d := DB{authorityID: admin.DefaultAuthorityID} + if dbp, err := d.unmarshalDBProvisioner(tc.in, provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -294,19 +289,17 @@ func TestDB_unmarshalDBProvisioner(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, dbp.ID, provID) - assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) - assert.Equals(t, dbp.Type, tc.dbp.Type) - assert.Equals(t, dbp.Name, tc.dbp.Name) - assert.Equals(t, dbp.Details, tc.dbp.Details) - assert.Equals(t, dbp.Claims, tc.dbp.Claims) - assert.Equals(t, dbp.X509Template, tc.dbp.X509Template) - assert.Equals(t, dbp.SSHTemplate, tc.dbp.SSHTemplate) - assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) - assert.Fatal(t, dbp.DeletedAt.IsZero()) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.Details, tc.dbp.Details) + assert.Equals(t, dbp.Claims, tc.dbp.Claims) + assert.Equals(t, dbp.X509Template, tc.dbp.X509Template) + assert.Equals(t, dbp.SSHTemplate, tc.dbp.SSHTemplate) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) } }) } @@ -402,8 +395,8 @@ func TestDB_unmarshalProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{authorityID: admin.DefaultAuthorityID} - if prov, err := db.unmarshalProvisioner(tc.in, provID); err != nil { + d := DB{authorityID: admin.DefaultAuthorityID} + if prov, err := d.unmarshalProvisioner(tc.in, provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -418,20 +411,18 @@ func TestDB_unmarshalProvisioner(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, prov.Id, provID) - assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) - assert.Equals(t, prov.Type, tc.dbp.Type) - assert.Equals(t, prov.Name, tc.dbp.Name) - assert.Equals(t, prov.Claims, tc.dbp.Claims) - assert.Equals(t, prov.X509Template, tc.dbp.X509Template) - assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) - - retDetailsBytes, err := json.Marshal(prov.Details.GetData()) - assert.FatalError(t, err) - assert.Equals(t, retDetailsBytes, tc.dbp.Details) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) } }) } @@ -453,7 +444,7 @@ func TestDB_GetProvisioner(t *testing.T) { assert.Equals(t, bucket, provisionersTable) assert.Equals(t, string(key), provID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), @@ -542,8 +533,8 @@ func TestDB_GetProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if prov, err := db.GetProvisioner(context.Background(), provID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if prov, err := d.GetProvisioner(context.Background(), provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -558,20 +549,18 @@ func TestDB_GetProvisioner(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - assert.Equals(t, prov.Id, provID) - assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) - assert.Equals(t, prov.Type, tc.dbp.Type) - assert.Equals(t, prov.Name, tc.dbp.Name) - assert.Equals(t, prov.Claims, tc.dbp.Claims) - assert.Equals(t, prov.X509Template, tc.dbp.X509Template) - assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) - - retDetailsBytes, err := json.Marshal(prov.Details.GetData()) - assert.FatalError(t, err) - assert.Equals(t, retDetailsBytes, tc.dbp.Details) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) } }) } @@ -592,7 +581,7 @@ func TestDB_DeleteProvisioner(t *testing.T) { assert.Equals(t, bucket, provisionersTable) assert.Equals(t, string(key), provID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), @@ -692,8 +681,8 @@ func TestDB_DeleteProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.DeleteProvisioner(context.Background(), provID); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.DeleteProvisioner(context.Background(), provID); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -853,8 +842,8 @@ func TestDB_GetProvisioners(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if provs, err := db.GetProvisioners(context.Background()); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if provs, err := d.GetProvisioners(context.Background()); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -869,10 +858,8 @@ func TestDB_GetProvisioners(t *testing.T) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } } - } else { - if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { - tc.verify(t, provs) - } + } else if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, provs) } }) } @@ -963,8 +950,8 @@ func TestDB_CreateProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.CreateProvisioner(context.Background(), tc.prov); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.CreateProvisioner(context.Background(), tc.prov); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { @@ -1001,7 +988,7 @@ func TestDB_UpdateProvisioner(t *testing.T) { assert.Equals(t, bucket, provisionersTable) assert.Equals(t, string(key), provID) - return nil, nosqldb.ErrNotFound + return nil, database.ErrNotFound }, }, adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), @@ -1199,8 +1186,8 @@ func TestDB_UpdateProvisioner(t *testing.T) { for name, run := range tests { tc := run(t) t.Run(name, func(t *testing.T) { - db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} - if err := db.UpdateProvisioner(context.Background(), tc.prov); err != nil { + d := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := d.UpdateProvisioner(context.Background(), tc.prov); err != nil { switch k := err.(type) { case *admin.Error: if assert.NotNil(t, tc.adminErr) { diff --git a/authority/administrator/collection.go b/authority/administrator/collection.go index ff04a41f..88d7bb2c 100644 --- a/authority/administrator/collection.go +++ b/authority/administrator/collection.go @@ -55,8 +55,8 @@ type subProv struct { provisioner string } -func newSubProv(subject, provisioner string) subProv { - return subProv{subject, provisioner} +func newSubProv(subject, prov string) subProv { + return subProv{subject, prov} } // LoadBySubProv a admin by the subject and provisioner name. diff --git a/authority/admins.go b/authority/admins.go index dcaf9b49..b975297a 100644 --- a/authority/admins.go +++ b/authority/admins.go @@ -16,10 +16,10 @@ func (a *Authority) LoadAdminByID(id string) (*linkedca.Admin, bool) { } // LoadAdminBySubProv returns an *linkedca.Admin with the given ID. -func (a *Authority) LoadAdminBySubProv(subject, provisioner string) (*linkedca.Admin, bool) { +func (a *Authority) LoadAdminBySubProv(subject, prov string) (*linkedca.Admin, bool) { a.adminMutex.RLock() defer a.adminMutex.RUnlock() - return a.admins.LoadBySubProv(subject, provisioner) + return a.admins.LoadBySubProv(subject, prov) } // GetAdmins returns a map listing each provisioner and the JWK Key Set diff --git a/authority/authority.go b/authority/authority.go index 3f97ceab..aa8698d7 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -78,14 +78,14 @@ type Authority struct { } // New creates and initiates a new Authority type. -func New(config *config.Config, opts ...Option) (*Authority, error) { - err := config.Validate() +func New(cfg *config.Config, opts ...Option) (*Authority, error) { + err := cfg.Validate() if err != nil { return nil, err } var a = &Authority{ - config: config, + config: cfg, certificates: new(sync.Map), } diff --git a/authority/authorize.go b/authority/authorize.go index 816699f7..a4e7e591 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -54,7 +54,7 @@ func (a *Authority) authorizeToken(ctx context.Context, token string) (provision // key in order to verify the claims and we need the issuer from the claims // before we can look up the provisioner. var claims Claims - if err = tok.UnsafeClaimsWithoutVerification(&claims); err != nil { + if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { return nil, errs.Wrap(http.StatusUnauthorized, err, "authority.authorizeToken") } @@ -77,7 +77,7 @@ func (a *Authority) authorizeToken(ctx context.Context, token string) (provision // Store the token to protect against reuse unless it's skipped. // If we cannot get a token id from the provisioner, just hash the token. if !SkipTokenReuseFromContext(ctx) { - if err = a.UseToken(token, p); err != nil { + if err := a.UseToken(token, p); err != nil { return nil, err } } @@ -112,7 +112,7 @@ func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedc // to the public certificate in the `x5c` header of the token. // 2. Asserts that the claims are valid - have not been tampered with. var claims jose.Claims - if err = jwt.Claims(leaf.PublicKey, &claims); err != nil { + if err := jwt.Claims(leaf.PublicKey, &claims); err != nil { return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error parsing x5c claims") } @@ -122,13 +122,13 @@ func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedc } // Check that the token has not been used. - if err = a.UseToken(token, prov); err != nil { + if err := a.UseToken(token, prov); err != nil { return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error with reuse token") } // According to "rfc7519 JSON Web Token" acceptable skew should be no // more than a few minutes. - if err = claims.ValidateWithLeeway(jose.Expected{ + if err := claims.ValidateWithLeeway(jose.Expected{ Issuer: prov.GetName(), Time: time.Now().UTC(), }, time.Minute); err != nil { @@ -262,7 +262,7 @@ func (a *Authority) authorizeRevoke(ctx context.Context, token string) error { if err != nil { return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRevoke") } - if err = p.AuthorizeRevoke(ctx, token); err != nil { + if err := p.AuthorizeRevoke(ctx, token); err != nil { return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRevoke") } return nil diff --git a/authority/authorize_test.go b/authority/authorize_test.go index f308ec28..6d524a25 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -917,7 +917,7 @@ func createSSHCert(cert *ssh.Certificate, signer ssh.Signer) (*ssh.Certificate, if err != nil { return nil, nil, err } - if err = cert.SignCert(rand.Reader, signer); err != nil { + if err := cert.SignCert(rand.Reader, signer); err != nil { return nil, nil, err } return cert, jwk, nil diff --git a/authority/config/types.go b/authority/config/types.go index 6d7b9389..5ca3b15f 100644 --- a/authority/config/types.go +++ b/authority/config/types.go @@ -25,7 +25,7 @@ func (s multiString) HasEmpties() bool { return true } for _, ss := range s { - if len(ss) == 0 { + if ss == "" { return true } } diff --git a/authority/linkedca.go b/authority/linkedca.go index 9c816e1e..b568dcbb 100644 --- a/authority/linkedca.go +++ b/authority/linkedca.go @@ -272,12 +272,12 @@ func (c *linkedCaClient) Revoke(crt *x509.Certificate, rci *db.RevokedCertificat return errors.Wrap(err, "error revoking certificate") } -func (c *linkedCaClient) RevokeSSH(ssh *ssh.Certificate, rci *db.RevokedCertificateInfo) error { +func (c *linkedCaClient) RevokeSSH(cert *ssh.Certificate, rci *db.RevokedCertificateInfo) error { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() _, err := c.client.RevokeSSHCertificate(ctx, &linkedca.RevokeSSHCertificateRequest{ Serial: rci.Serial, - Certificate: serializeSSHCertificate(ssh), + Certificate: serializeSSHCertificate(cert), Reason: rci.Reason, ReasonCode: linkedca.RevocationReasonCode(rci.ReasonCode), Passive: true, diff --git a/authority/options.go b/authority/options.go index 5c8a6e66..0f80cbbf 100644 --- a/authority/options.go +++ b/authority/options.go @@ -22,9 +22,9 @@ type Option func(*Authority) error // WithConfig replaces the current config with the given one. No validation is // performed in the given value. -func WithConfig(config *config.Config) Option { +func WithConfig(cfg *config.Config) Option { return func(a *Authority) error { - a.config = config + a.config = cfg return nil } } @@ -76,9 +76,9 @@ func WithIssuerPassword(password []byte) Option { // WithDatabase sets an already initialized authority database to a new // authority. This option is intended to be use on graceful reloads. -func WithDatabase(db db.AuthDB) Option { +func WithDatabase(d db.AuthDB) Option { return func(a *Authority) error { - a.db = db + a.db = d return nil } } @@ -225,9 +225,9 @@ func WithX509FederatedBundle(pemCerts []byte) Option { } // WithAdminDB is an option to set the database backing the admin APIs. -func WithAdminDB(db admin.DB) Option { +func WithAdminDB(d admin.DB) Option { return func(a *Authority) error { - a.adminDB = db + a.adminDB = d return nil } } diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index cdd06f00..cd129b7b 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -312,7 +312,7 @@ func (p *AWS) GetType() Type { } // GetEncryptedKey is not available in an AWS provisioner. -func (p *AWS) GetEncryptedKey() (kid string, key string, ok bool) { +func (p *AWS) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } @@ -449,13 +449,15 @@ func (p *AWS) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // There's no way to trust them other than TOFU. var so []SignOption if p.DisableCustomSANs { - dnsName := fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region) - so = append(so, dnsNamesValidator([]string{dnsName})) - so = append(so, ipAddressesValidator([]net.IP{ - net.ParseIP(doc.PrivateIP), - })) - so = append(so, emailAddressesValidator(nil)) - so = append(so, urisValidator(nil)) + dnsName := fmt.Sprintf("ip-%s.%s.compute.internal", strings.ReplaceAll(doc.PrivateIP, ".", "-"), doc.Region) + so = append(so, + dnsNamesValidator([]string{dnsName}), + ipAddressesValidator([]net.IP{ + net.ParseIP(doc.PrivateIP), + }), + emailAddressesValidator(nil), + urisValidator(nil), + ) // Template options data.SetSANs([]string{dnsName, doc.PrivateIP}) @@ -669,7 +671,7 @@ func (p *AWS) authorizeToken(token string) (*awsPayload, error) { if p.DisableCustomSANs { if payload.Subject != doc.InstanceID && payload.Subject != doc.PrivateIP && - payload.Subject != fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region) { + payload.Subject != fmt.Sprintf("ip-%s.%s.compute.internal", strings.ReplaceAll(doc.PrivateIP, ".", "-"), doc.Region) { return nil, errs.Unauthorized("aws.authorizeToken; invalid token - invalid subject claim (sub)") } } @@ -720,7 +722,7 @@ func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, // Validated principals. principals := []string{ doc.PrivateIP, - fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region), + fmt.Sprintf("ip-%s.%s.compute.internal", strings.ReplaceAll(doc.PrivateIP, ".", "-"), doc.Region), } // Only enforce known principals if disable custom sans is true. diff --git a/authority/provisioner/aws_test.go b/authority/provisioner/aws_test.go index aff0aecb..0d2786db 100644 --- a/authority/provisioner/aws_test.go +++ b/authority/provisioner/aws_test.go @@ -663,15 +663,15 @@ func TestAWS_AuthorizeSign(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := NewContextWithMethod(context.Background(), SignMethod) - got, err := tt.aws.AuthorizeSign(ctx, tt.args.token) - if (err != nil) != tt.wantErr { + switch got, err := tt.aws.AuthorizeSign(ctx, tt.args.token); { + case (err != nil) != tt.wantErr: t.Errorf("AWS.AuthorizeSign() error = %v, wantErr %v", err, tt.wantErr) return - } else if err != nil { + case err != nil: sc, ok := err.(errs.StatusCoder) assert.Fatal(t, ok, "error does not implement StatusCoder interface") assert.Equals(t, sc.StatusCode(), tt.code) - } else { + default: assert.Len(t, tt.wantLen, got) for _, o := range got { switch v := o.(type) { diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index fee50658..a90d1728 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -152,7 +152,7 @@ func (p *Azure) GetType() Type { } // GetEncryptedKey is not available in an Azure provisioner. -func (p *Azure) GetEncryptedKey() (kid string, key string, ok bool) { +func (p *Azure) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } @@ -303,11 +303,13 @@ func (p *Azure) AuthorizeSign(ctx context.Context, token string) ([]SignOption, var so []SignOption if p.DisableCustomSANs { // name will work only inside the virtual network - so = append(so, commonNameValidator(name)) - so = append(so, dnsNamesValidator([]string{name})) - so = append(so, ipAddressesValidator(nil)) - so = append(so, emailAddressesValidator(nil)) - so = append(so, urisValidator(nil)) + so = append(so, + commonNameValidator(name), + dnsNamesValidator([]string{name}), + ipAddressesValidator(nil), + emailAddressesValidator(nil), + urisValidator(nil), + ) // Enforce SANs in the template. data.SetSANs([]string{name}) diff --git a/authority/provisioner/azure_test.go b/authority/provisioner/azure_test.go index 8033d345..b7c321a6 100644 --- a/authority/provisioner/azure_test.go +++ b/authority/provisioner/azure_test.go @@ -446,15 +446,15 @@ func TestAzure_AuthorizeSign(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := NewContextWithMethod(context.Background(), SignMethod) - got, err := tt.azure.AuthorizeSign(ctx, tt.args.token) - if (err != nil) != tt.wantErr { + switch got, err := tt.azure.AuthorizeSign(ctx, tt.args.token); { + case (err != nil) != tt.wantErr: t.Errorf("Azure.AuthorizeSign() error = %v, wantErr %v", err, tt.wantErr) return - } else if err != nil { + case err != nil: sc, ok := err.(errs.StatusCoder) assert.Fatal(t, ok, "error does not implement StatusCoder interface") assert.Equals(t, sc.StatusCode(), tt.code) - } else { + default: assert.Len(t, tt.wantLen, got) for _, o := range got { switch v := o.(type) { diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index caf46ca9..1bec8689 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -229,14 +229,15 @@ func (c *Collection) Remove(id string) error { var found bool for i, elem := range c.sorted { - if elem.provisioner.GetID() == id { - // Remove index in sorted list - copy(c.sorted[i:], c.sorted[i+1:]) // Shift a[i+1:] left one index. - c.sorted[len(c.sorted)-1] = uidProvisioner{} // Erase last element (write zero value). - c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. - found = true - break + if elem.provisioner.GetID() != id { + continue } + // Remove index in sorted list + copy(c.sorted[i:], c.sorted[i+1:]) // Shift a[i+1:] left one index. + c.sorted[len(c.sorted)-1] = uidProvisioner{} // Erase last element (write zero value). + c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. + found = true + break } if !found { return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found in sorted list", prov.GetName()) diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 1b599fb3..98d776d1 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -150,7 +150,7 @@ func (p *GCP) GetType() Type { } // GetEncryptedKey is not available in a GCP provisioner. -func (p *GCP) GetEncryptedKey() (kid string, key string, ok bool) { +func (p *GCP) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } @@ -244,15 +244,17 @@ func (p *GCP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er if p.DisableCustomSANs { dnsName1 := fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID) dnsName2 := fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID) - so = append(so, commonNameSliceValidator([]string{ - ce.InstanceName, ce.InstanceID, dnsName1, dnsName2, - })) - so = append(so, dnsNamesValidator([]string{ - dnsName1, dnsName2, - })) - so = append(so, ipAddressesValidator(nil)) - so = append(so, emailAddressesValidator(nil)) - so = append(so, urisValidator(nil)) + so = append(so, + commonNameSliceValidator([]string{ + ce.InstanceName, ce.InstanceID, dnsName1, dnsName2, + }), + dnsNamesValidator([]string{ + dnsName1, dnsName2, + }), + ipAddressesValidator(nil), + emailAddressesValidator(nil), + urisValidator(nil), + ) // Template SANs data.SetSANs([]string{dnsName1, dnsName2}) diff --git a/authority/provisioner/gcp_test.go b/authority/provisioner/gcp_test.go index d6c4054c..5f6f9bc7 100644 --- a/authority/provisioner/gcp_test.go +++ b/authority/provisioner/gcp_test.go @@ -535,15 +535,15 @@ func TestGCP_AuthorizeSign(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := NewContextWithMethod(context.Background(), SignMethod) - got, err := tt.gcp.AuthorizeSign(ctx, tt.args.token) - if (err != nil) != tt.wantErr { + switch got, err := tt.gcp.AuthorizeSign(ctx, tt.args.token); { + case (err != nil) != tt.wantErr: t.Errorf("GCP.AuthorizeSign() error = %v, wantErr %v", err, tt.wantErr) return - } else if err != nil { + case err != nil: sc, ok := err.(errs.StatusCoder) assert.Fatal(t, ok, "error does not implement StatusCoder interface") assert.Equals(t, sc.StatusCode(), tt.code) - } else { + default: assert.Len(t, tt.wantLen, got) for _, o := range got { switch v := o.(type) { diff --git a/authority/provisioner/keystore.go b/authority/provisioner/keystore.go index f775e150..d1811fab 100644 --- a/authority/provisioner/keystore.go +++ b/authority/provisioner/keystore.go @@ -18,7 +18,7 @@ const ( defaultCacheJitter = 1 * time.Hour ) -var maxAgeRegex = regexp.MustCompile("max-age=([0-9]+)") +var maxAgeRegex = regexp.MustCompile(`max-age=(\d+)`) type keyStore struct { sync.RWMutex diff --git a/authority/provisioner/noop.go b/authority/provisioner/noop.go index 18a38331..1709fbca 100644 --- a/authority/provisioner/noop.go +++ b/authority/provisioner/noop.go @@ -29,7 +29,7 @@ func (p *noop) GetType() Type { return noopType } -func (p *noop) GetEncryptedKey() (kid string, key string, ok bool) { +func (p *noop) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index 3786f54b..ac1f2a25 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -148,7 +148,7 @@ func (o *OIDC) GetType() Type { } // GetEncryptedKey is not available in an OIDC provisioner. -func (o *OIDC) GetEncryptedKey() (kid string, key string, ok bool) { +func (o *OIDC) GetEncryptedKey() (kid, key string, ok bool) { return "", "", false } @@ -193,7 +193,7 @@ func (o *OIDC) Init(config Config) (err error) { } // Replace {tenantid} with the configured one if o.TenantID != "" { - o.configuration.Issuer = strings.Replace(o.configuration.Issuer, "{tenantid}", o.TenantID, -1) + o.configuration.Issuer = strings.ReplaceAll(o.configuration.Issuer, "{tenantid}", o.TenantID) } // Get JWK key set o.keyStore, err = newKeyStore(o.configuration.JWKSetURI) diff --git a/authority/provisioner/oidc_test.go b/authority/provisioner/oidc_test.go index 532bd2e0..7bf6ad7a 100644 --- a/authority/provisioner/oidc_test.go +++ b/authority/provisioner/oidc_test.go @@ -321,32 +321,26 @@ func TestOIDC_AuthorizeSign(t *testing.T) { assert.Fatal(t, ok, "error does not implement StatusCoder interface") assert.Equals(t, sc.StatusCode(), tt.code) assert.Nil(t, got) - } else { - if assert.NotNil(t, got) { - if tt.name == "admin" { - assert.Len(t, 5, got) - } else { - assert.Len(t, 5, got) - } - for _, o := range got { - switch v := o.(type) { - case certificateOptionsFunc: - case *provisionerExtensionOption: - assert.Equals(t, v.Type, int(TypeOIDC)) - assert.Equals(t, v.Name, tt.prov.GetName()) - assert.Equals(t, v.CredentialID, tt.prov.ClientID) - assert.Len(t, 0, v.KeyValuePairs) - case profileDefaultDuration: - assert.Equals(t, time.Duration(v), tt.prov.claimer.DefaultTLSCertDuration()) - case defaultPublicKeyValidator: - case *validityValidator: - assert.Equals(t, v.min, tt.prov.claimer.MinTLSCertDuration()) - assert.Equals(t, v.max, tt.prov.claimer.MaxTLSCertDuration()) - case emailOnlyIdentity: - assert.Equals(t, string(v), "name@smallstep.com") - default: - assert.FatalError(t, errors.Errorf("unexpected sign option of type %T", v)) - } + } else if assert.NotNil(t, got) { + assert.Len(t, 5, got) + for _, o := range got { + switch v := o.(type) { + case certificateOptionsFunc: + case *provisionerExtensionOption: + assert.Equals(t, v.Type, int(TypeOIDC)) + assert.Equals(t, v.Name, tt.prov.GetName()) + assert.Equals(t, v.CredentialID, tt.prov.ClientID) + assert.Len(t, 0, v.KeyValuePairs) + case profileDefaultDuration: + assert.Equals(t, time.Duration(v), tt.prov.claimer.DefaultTLSCertDuration()) + case defaultPublicKeyValidator: + case *validityValidator: + assert.Equals(t, v.min, tt.prov.claimer.MinTLSCertDuration()) + assert.Equals(t, v.max, tt.prov.claimer.MaxTLSCertDuration()) + case emailOnlyIdentity: + assert.Equals(t, string(v), "name@smallstep.com") + default: + assert.FatalError(t, errors.Errorf("unexpected sign option of type %T", v)) } } } diff --git a/authority/provisioner/options.go b/authority/provisioner/options.go index 100aa588..f86c4863 100644 --- a/authority/provisioner/options.go +++ b/authority/provisioner/options.go @@ -138,7 +138,7 @@ func unsafeParseSigned(s string) (map[string]interface{}, error) { return nil, err } claims := make(map[string]interface{}) - if err = token.UnsafeClaimsWithoutVerification(&claims); err != nil { + if err := token.UnsafeClaimsWithoutVerification(&claims); err != nil { return nil, err } return claims, nil diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 652cb888..5d6b2f80 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -123,7 +123,7 @@ func (a Audiences) WithFragment(fragment string) Audiences { // generateSignAudience generates a sign audience with the format // https:///1.0/sign#provisionerID -func generateSignAudience(caURL string, provisionerID string) (string, error) { +func generateSignAudience(caURL, provisionerID string) (string, error) { u, err := url.Parse(caURL) if err != nil { return "", errors.Wrapf(err, "error parsing %s", caURL) diff --git a/authority/provisioner/sign_ssh_options_test.go b/authority/provisioner/sign_ssh_options_test.go index 693690f6..3a1ff324 100644 --- a/authority/provisioner/sign_ssh_options_test.go +++ b/authority/provisioner/sign_ssh_options_test.go @@ -44,7 +44,7 @@ func TestSSHOptions_Modify(t *testing.T) { valid func(*ssh.Certificate) err error } - tests := map[string](func() test){ + tests := map[string]func() test{ "fail/unexpected-cert-type": func() test { return test{ so: SignSSHOptions{CertType: "foo"}, @@ -117,7 +117,7 @@ func TestSSHOptions_Match(t *testing.T) { cmp SignSSHOptions err error } - tests := map[string](func() test){ + tests := map[string]func() test{ "fail/cert-type": func() test { return test{ so: SignSSHOptions{CertType: "foo"}, @@ -208,7 +208,7 @@ func Test_sshCertPrincipalsModifier_Modify(t *testing.T) { cert *ssh.Certificate expected []string } - tests := map[string](func() test){ + tests := map[string]func() test{ "ok": func() test { a := []string{"foo", "bar"} return test{ @@ -234,7 +234,7 @@ func Test_sshCertKeyIDModifier_Modify(t *testing.T) { cert *ssh.Certificate expected string } - tests := map[string](func() test){ + tests := map[string]func() test{ "ok": func() test { a := "foo" return test{ @@ -260,7 +260,7 @@ func Test_sshCertTypeModifier_Modify(t *testing.T) { cert *ssh.Certificate expected uint32 } - tests := map[string](func() test){ + tests := map[string]func() test{ "ok/user": func() test { return test{ modifier: sshCertTypeModifier("user"), @@ -299,7 +299,7 @@ func Test_sshCertValidAfterModifier_Modify(t *testing.T) { cert *ssh.Certificate expected uint64 } - tests := map[string](func() test){ + tests := map[string]func() test{ "ok": func() test { return test{ modifier: sshCertValidAfterModifier(15), @@ -324,7 +324,7 @@ func Test_sshCertDefaultsModifier_Modify(t *testing.T) { cert *ssh.Certificate valid func(*ssh.Certificate) } - tests := map[string](func() test){ + tests := map[string]func() test{ "ok/changes": func() test { n := time.Now() va := NewTimeDuration(n.Add(1 * time.Minute)) @@ -388,7 +388,7 @@ func Test_sshDefaultExtensionModifier_Modify(t *testing.T) { valid func(*ssh.Certificate) err error } - tests := map[string](func() test){ + tests := map[string]func() test{ "fail/unexpected-cert-type": func() test { cert := &ssh.Certificate{CertType: 3} return test{ diff --git a/authority/provisioner/sshpop_test.go b/authority/provisioner/sshpop_test.go index 79d82e00..3d343967 100644 --- a/authority/provisioner/sshpop_test.go +++ b/authority/provisioner/sshpop_test.go @@ -46,7 +46,7 @@ func createSSHCert(cert *ssh.Certificate, signer ssh.Signer) (*ssh.Certificate, if err != nil { return nil, nil, err } - if err = cert.SignCert(rand.Reader, signer); err != nil { + if err := cert.SignCert(rand.Reader, signer); err != nil { return nil, nil, err } return cert, jwk, nil @@ -214,10 +214,8 @@ func TestSSHPOP_authorizeToken(t *testing.T) { if assert.NotNil(t, tc.err) { assert.HasPrefix(t, err.Error(), tc.err.Error()) } - } else { - if assert.Nil(t, tc.err) { - assert.NotNil(t, claims) - } + } else if assert.Nil(t, tc.err) { + assert.NotNil(t, claims) } }) } diff --git a/authority/provisioner/utils_test.go b/authority/provisioner/utils_test.go index 534e83cf..e39efbcf 100644 --- a/authority/provisioner/utils_test.go +++ b/authority/provisioner/utils_test.go @@ -732,7 +732,7 @@ func withSSHPOPFile(cert *ssh.Certificate) tokOption { } } -func generateToken(sub, iss, aud string, email string, sans []string, iat time.Time, jwk *jose.JSONWebKey, tokOpts ...tokOption) (string, error) { +func generateToken(sub, iss, aud, email string, sans []string, iat time.Time, jwk *jose.JSONWebKey, tokOpts ...tokOption) (string, error) { so := new(jose.SignerOptions) so.WithType("JWT") so.WithHeader("kid", jwk.KeyID) @@ -773,7 +773,7 @@ func generateToken(sub, iss, aud string, email string, sans []string, iat time.T return jose.Signed(sig).Claims(claims).CompactSerialize() } -func generateOIDCToken(sub, iss, aud string, email string, preferredUsername string, iat time.Time, jwk *jose.JSONWebKey, tokOpts ...tokOption) (string, error) { +func generateOIDCToken(sub, iss, aud, email, preferredUsername string, iat time.Time, jwk *jose.JSONWebKey, tokOpts ...tokOption) (string, error) { so := new(jose.SignerOptions) so.WithType("JWT") so.WithHeader("kid", jwk.KeyID) diff --git a/authority/ssh.go b/authority/ssh.go index 1c873279..762319ae 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -108,7 +108,7 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin // GetSSHBastion returns the bastion configuration, for the given pair user, // hostname. -func (a *Authority) GetSSHBastion(ctx context.Context, user string, hostname string) (*config.Bastion, error) { +func (a *Authority) GetSSHBastion(ctx context.Context, user, hostname string) (*config.Bastion, error) { if a.sshBastionFunc != nil { bs, err := a.sshBastionFunc(ctx, user, hostname) return bs, errs.Wrap(http.StatusInternalServerError, err, "authority.GetSSHBastion") @@ -477,7 +477,7 @@ func (a *Authority) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, subje } // CheckSSHHost checks the given principal has been registered before. -func (a *Authority) CheckSSHHost(ctx context.Context, principal string, token string) (bool, error) { +func (a *Authority) CheckSSHHost(ctx context.Context, principal, token string) (bool, error) { if a.sshCheckHostFunc != nil { exists, err := a.sshCheckHostFunc(ctx, principal, token, a.GetRootCertificates()) if err != nil { @@ -531,5 +531,5 @@ func (a *Authority) getAddUserCommand(principal string) string { } else { cmd = a.config.SSH.AddUserCommand } - return strings.Replace(cmd, "", principal, -1) + return strings.ReplaceAll(cmd, "", principal) } diff --git a/authority/tls.go b/authority/tls.go index b434be55..839866a2 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -55,10 +55,10 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { if len(crt.Subject.StreetAddress) == 0 && def.StreetAddress != "" { crt.Subject.StreetAddress = append(crt.Subject.StreetAddress, def.StreetAddress) } - if len(crt.Subject.SerialNumber) == 0 && def.SerialNumber != "" { + if crt.Subject.SerialNumber == "" && def.SerialNumber != "" { crt.Subject.SerialNumber = def.SerialNumber } - if len(crt.Subject.CommonName) == 0 && def.CommonName != "" { + if crt.Subject.CommonName == "" && def.CommonName != "" { crt.Subject.CommonName = def.CommonName } return nil @@ -387,14 +387,14 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke; could not get ID for token") } - opts = append(opts, errs.WithKeyVal("provisionerID", rci.ProvisionerID)) - opts = append(opts, errs.WithKeyVal("tokenID", rci.TokenID)) - } else { + opts = append(opts, + errs.WithKeyVal("provisionerID", rci.ProvisionerID), + errs.WithKeyVal("tokenID", rci.TokenID), + ) + } else if p, err = a.LoadProvisionerByCertificate(revokeOpts.Crt); err == nil { // Load the Certificate provisioner if one exists. - if p, err = a.LoadProvisionerByCertificate(revokeOpts.Crt); err == nil { - rci.ProvisionerID = p.GetID() - opts = append(opts, errs.WithKeyVal("provisionerID", rci.ProvisionerID)) - } + rci.ProvisionerID = p.GetID() + opts = append(opts, errs.WithKeyVal("provisionerID", rci.ProvisionerID)) } if provisioner.MethodFromContext(ctx) == provisioner.SSHRevokeMethod { diff --git a/authority/tls_test.go b/authority/tls_test.go index cdd4c59a..f1d1748d 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -426,6 +426,7 @@ ZYtQ9Ot36qc= {Id: stepOIDProvisioner, Value: []byte("foo")}, {Id: []int{1, 1, 1}, Value: []byte("bar")}})) now := time.Now().UTC() + // nolint:gocritic enforcedExtraOptions := append(extraOpts, &certificateDurationEnforcer{ NotBefore: now, NotAfter: now.Add(365 * 24 * time.Hour), diff --git a/ca/acmeClient.go b/ca/acmeClient.go index 5633dac5..d1f40f32 100644 --- a/ca/acmeClient.go +++ b/ca/acmeClient.go @@ -345,7 +345,7 @@ func readACMEError(r io.ReadCloser) error { ae := new(acme.Error) err = json.Unmarshal(b, &ae) // If we successfully marshaled to an ACMEError then return the ACMEError. - if err != nil || len(ae.Error()) == 0 { + if err != nil || ae.Error() == "" { fmt.Printf("b = %s\n", b) // Throw up our hands. return errors.Errorf("%s", b) diff --git a/ca/acmeClient_test.go b/ca/acmeClient_test.go index f5963de4..656a82cf 100644 --- a/ca/acmeClient_test.go +++ b/ca/acmeClient_test.go @@ -1247,6 +1247,7 @@ func TestACMEClient_GetCertificate(t *testing.T) { Type: "Certificate", Bytes: leaf.Raw, }) + // nolint:gocritic certBytes := append(leafb, leafb...) certBytes = append(certBytes, leafb...) ac := &ACMEClient{ diff --git a/ca/adminClient.go b/ca/adminClient.go index 2f3d4b5d..6022f677 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -70,7 +70,7 @@ func NewAdminClient(endpoint string, opts ...ClientOption) (*AdminClient, error) }, nil } -func (c *AdminClient) generateAdminToken(path string) (string, error) { +func (c *AdminClient) generateAdminToken(urlPath string) (string, error) { // A random jwt id will be used to identify duplicated tokens jwtID, err := randutil.Hex(64) // 256 bits if err != nil { @@ -82,7 +82,7 @@ func (c *AdminClient) generateAdminToken(path string) (string, error) { token.WithJWTID(jwtID), token.WithKid(c.x5cJWK.KeyID), token.WithIssuer(c.x5cIssuer), - token.WithAudience(path), + token.WithAudience(urlPath), token.WithValidity(now, now.Add(token.DefaultValidity)), token.WithX5CCerts(c.x5cCertStrs), } @@ -348,14 +348,15 @@ func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provi return nil, err } var u *url.URL - if len(o.id) > 0 { + switch { + case len(o.id) > 0: u = c.endpoint.ResolveReference(&url.URL{ Path: "/admin/provisioners/id", RawQuery: o.rawQuery(), }) - } else if len(o.name) > 0 { + case len(o.name) > 0: u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) - } else { + default: return nil, errors.New("must set either name or id in method options") } tok, err := c.generateAdminToken(u.Path) @@ -456,14 +457,15 @@ func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error { return err } - if len(o.id) > 0 { + switch { + case len(o.id) > 0: u = c.endpoint.ResolveReference(&url.URL{ Path: path.Join(adminURLPrefix, "provisioners/id"), RawQuery: o.rawQuery(), }) - } else if len(o.name) > 0 { + case len(o.name) > 0: u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) - } else { + default: return errors.New("must set either name or id in method options") } tok, err := c.generateAdminToken(u.Path) diff --git a/ca/bootstrap.go b/ca/bootstrap.go index 5f06e986..42087985 100644 --- a/ca/bootstrap.go +++ b/ca/bootstrap.go @@ -30,7 +30,7 @@ func Bootstrap(token string) (*Client, error) { // Validate bootstrap token switch { - case len(claims.SHA) == 0: + case claims.SHA == "": return nil, errors.New("invalid bootstrap token: sha claim is not present") case !strings.HasPrefix(strings.ToLower(claims.Audience[0]), "http"): return nil, errors.New("invalid bootstrap token: aud claim is not a url") diff --git a/ca/ca.go b/ca/ca.go index 00a5970a..c76e8c0a 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -88,9 +88,9 @@ func WithIssuerPassword(password []byte) Option { } // WithDatabase sets the given authority database to the CA options. -func WithDatabase(db db.AuthDB) Option { +func WithDatabase(d db.AuthDB) Option { return func(o *options) { - o.database = db + o.database = d } } @@ -113,17 +113,17 @@ type CA struct { } // New creates and initializes the CA with the given configuration and options. -func New(config *config.Config, opts ...Option) (*CA, error) { +func New(cfg *config.Config, opts ...Option) (*CA, error) { ca := &CA{ - config: config, + config: cfg, opts: new(options), } ca.opts.apply(opts) - return ca.Init(config) + return ca.Init(cfg) } // Init initializes the CA with the given configuration. -func (ca *CA) Init(config *config.Config) (*CA, error) { +func (ca *CA) Init(cfg *config.Config) (*CA, error) { // Set password, it's ok to set nil password, the ca will prompt for them if // they are required. opts := []authority.Option{ @@ -140,7 +140,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { opts = append(opts, authority.WithDatabase(ca.opts.database)) } - auth, err := authority.New(config, opts...) + auth, err := authority.New(cfg, opts...) if err != nil { return nil, err } @@ -166,8 +166,8 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) //Add ACME api endpoints in /acme and /1.0/acme - dns := config.DNSNames[0] - u, err := url.Parse("https://" + config.Address) + dns := cfg.DNSNames[0] + u, err := url.Parse("https://" + cfg.Address) if err != nil { return nil, err } @@ -179,7 +179,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { // ACME Router prefix := "acme" var acmeDB acme.DB - if config.DB == nil { + if cfg.DB == nil { acmeDB = nil } else { acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB)) @@ -188,7 +188,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { } } acmeHandler := acmeAPI.NewHandler(acmeAPI.HandlerOptions{ - Backdate: *config.AuthorityConfig.Backdate, + Backdate: *cfg.AuthorityConfig.Backdate, DB: acmeDB, DNS: dns, Prefix: prefix, @@ -204,7 +204,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) // Admin API Router - if config.AuthorityConfig.EnableAdmin { + if cfg.AuthorityConfig.EnableAdmin { adminDB := auth.GetAdminDatabase() if adminDB != nil { adminHandler := adminAPI.NewHandler(auth) @@ -248,8 +248,8 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { //dumpRoutes(mux) // Add monitoring if configured - if len(config.Monitoring) > 0 { - m, err := monitoring.New(config.Monitoring) + if len(cfg.Monitoring) > 0 { + m, err := monitoring.New(cfg.Monitoring) if err != nil { return nil, err } @@ -258,8 +258,8 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { } // Add logger if configured - if len(config.Logger) > 0 { - logger, err := logging.New("ca", config.Logger) + if len(cfg.Logger) > 0 { + logger, err := logging.New("ca", cfg.Logger) if err != nil { return nil, err } @@ -267,16 +267,16 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { insecureHandler = logger.Middleware(insecureHandler) } - ca.srv = server.New(config.Address, handler, tlsConfig) + ca.srv = server.New(cfg.Address, handler, tlsConfig) // only start the insecure server if the insecure address is configured // and, currently, also only when it should serve SCEP endpoints. - if ca.shouldServeSCEPEndpoints() && config.InsecureAddress != "" { + if ca.shouldServeSCEPEndpoints() && cfg.InsecureAddress != "" { // TODO: instead opt for having a single server.Server but two // http.Servers handling the HTTP and HTTPS handler? The latter // will probably introduce more complexity in terms of graceful // reload. - ca.insecureSrv = server.New(config.InsecureAddress, insecureHandler, nil) + ca.insecureSrv = server.New(cfg.InsecureAddress, insecureHandler, nil) } return ca, nil @@ -285,24 +285,24 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { // Run starts the CA calling to the server ListenAndServe method. func (ca *CA) Run() error { var wg sync.WaitGroup - errors := make(chan error, 1) + errs := make(chan error, 1) if ca.insecureSrv != nil { wg.Add(1) go func() { defer wg.Done() - errors <- ca.insecureSrv.ListenAndServe() + errs <- ca.insecureSrv.ListenAndServe() }() } wg.Add(1) go func() { defer wg.Done() - errors <- ca.srv.ListenAndServe() + errs <- ca.srv.ListenAndServe() }() // wait till error occurs; ensures the servers keep listening - err := <-errors + err := <-errs wg.Wait() @@ -331,7 +331,7 @@ func (ca *CA) Stop() error { // Reload reloads the configuration of the CA and calls to the server Reload // method. func (ca *CA) Reload() error { - config, err := config.LoadConfiguration(ca.opts.configFile) + cfg, err := config.LoadConfiguration(ca.opts.configFile) if err != nil { return errors.Wrap(err, "error reloading ca configuration") } @@ -343,12 +343,12 @@ func (ca *CA) Reload() error { } // Do not allow reload if the database configuration has changed. - if !reflect.DeepEqual(ca.config.DB, config.DB) { + if !reflect.DeepEqual(ca.config.DB, cfg.DB) { logContinue("Reload failed because the database configuration has changed.") return errors.New("error reloading ca: database configuration cannot change") } - newCA, err := New(config, + newCA, err := New(cfg, WithPassword(ca.opts.password), WithSSHHostPassword(ca.opts.sshHostPassword), WithSSHUserPassword(ca.opts.sshUserPassword), diff --git a/ca/ca_test.go b/ca/ca_test.go index 6e297733..ff264db7 100644 --- a/ca/ca_test.go +++ b/ca/ca_test.go @@ -322,7 +322,7 @@ ZEp7knvU2psWRw== assert.Equals(t, intermediate, realIntermediate) } else { err := readError(body) - if len(tc.errMsg) == 0 { + if tc.errMsg == "" { assert.FatalError(t, errors.New("must validate response error")) } assert.HasPrefix(t, err.Error(), tc.errMsg) @@ -375,7 +375,7 @@ func TestCAProvisioners(t *testing.T) { assert.Equals(t, a, b) } else { err := readError(body) - if len(tc.errMsg) == 0 { + if tc.errMsg == "" { assert.FatalError(t, errors.New("must validate response error")) } assert.HasPrefix(t, err.Error(), tc.errMsg) @@ -436,7 +436,7 @@ func TestCAProvisionerEncryptedKey(t *testing.T) { assert.Equals(t, ek.Key, tc.expectedKey) } else { err := readError(body) - if len(tc.errMsg) == 0 { + if tc.errMsg == "" { assert.FatalError(t, errors.New("must validate response error")) } assert.HasPrefix(t, err.Error(), tc.errMsg) @@ -497,7 +497,7 @@ func TestCARoot(t *testing.T) { assert.Equals(t, root.RootPEM.Certificate, rootCrt) } else { err := readError(body) - if len(tc.errMsg) == 0 { + if tc.errMsg == "" { assert.FatalError(t, errors.New("must validate response error")) } assert.HasPrefix(t, err.Error(), tc.errMsg) @@ -665,7 +665,7 @@ func TestCARenew(t *testing.T) { assert.Equals(t, *sign.TLSOptions, authority.DefaultTLSOptions) } else { err := readError(body) - if len(tc.errMsg) == 0 { + if tc.errMsg == "" { assert.FatalError(t, errors.New("must validate response error")) } assert.HasPrefix(t, err.Error(), tc.errMsg) diff --git a/ca/client.go b/ca/client.go index 8997fbd5..cfeddba0 100644 --- a/ca/client.go +++ b/ca/client.go @@ -74,17 +74,17 @@ func (c *uaClient) SetTransport(tr http.RoundTripper) { c.Client.Transport = tr } -func (c *uaClient) Get(url string) (*http.Response, error) { - req, err := http.NewRequest("GET", url, nil) +func (c *uaClient) Get(u string) (*http.Response, error) { + req, err := http.NewRequest("GET", u, nil) if err != nil { - return nil, errors.Wrapf(err, "new request GET %s failed", url) + return nil, errors.Wrapf(err, "new request GET %s failed", u) } req.Header.Set("User-Agent", UserAgent) return c.Client.Do(req) } -func (c *uaClient) Post(url, contentType string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest("POST", url, body) +func (c *uaClient) Post(u, contentType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest("POST", u, body) if err != nil { return nil, err } @@ -305,7 +305,7 @@ func WithAdminX5C(certs []*x509.Certificate, key interface{}, passwordFile strin err error opts []jose.Option ) - if len(passwordFile) != 0 { + if passwordFile != "" { opts = append(opts, jose.WithPasswordFile(passwordFile)) } blk, err := pemutil.Serialize(key) @@ -326,14 +326,14 @@ func WithAdminX5C(certs []*x509.Certificate, key interface{}, passwordFile strin for _, e := range o.x5cCert.Extensions { if e.Id.Equal(stepOIDProvisioner) { - var provisioner stepProvisionerASN1 - if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil { + var prov stepProvisionerASN1 + if _, err := asn1.Unmarshal(e.Value, &prov); err != nil { return errors.Wrap(err, "error unmarshaling provisioner OID from certificate") } - o.x5cIssuer = string(provisioner.Name) + o.x5cIssuer = string(prov.Name) } } - if len(o.x5cIssuer) == 0 { + if o.x5cIssuer == "" { return errors.New("provisioner extension not found in certificate") } @@ -631,7 +631,7 @@ retry: // do not match. func (c *Client) Root(sha256Sum string) (*api.RootResponse, error) { var retried bool - sha256Sum = strings.ToLower(strings.Replace(sha256Sum, "-", "", -1)) + sha256Sum = strings.ToLower(strings.ReplaceAll(sha256Sum, "-", "")) u := c.endpoint.ResolveReference(&url.URL{Path: "/root/" + sha256Sum}) retry: resp, err := newInsecureClient().Get(u.String()) @@ -651,7 +651,7 @@ retry: } // verify the sha256 sum := sha256.Sum256(root.RootPEM.Raw) - if sha256Sum != strings.ToLower(hex.EncodeToString(sum[:])) { + if !strings.EqualFold(sha256Sum, strings.ToLower(hex.EncodeToString(sum[:]))) { return nil, errs.BadRequest("client.Root; root certificate SHA256 fingerprint do not match") } return &root, nil @@ -1066,16 +1066,16 @@ retry: } return nil, readError(resp.Body) } - var config api.SSHConfigResponse - if err := readJSON(resp.Body, &config); err != nil { + var cfg api.SSHConfigResponse + if err := readJSON(resp.Body, &cfg); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } - return &config, nil + return &cfg, nil } // SSHCheckHost performs the POST /ssh/check-host request to the CA with the // given principal. -func (c *Client) SSHCheckHost(principal string, token string) (*api.SSHCheckPrincipalResponse, error) { +func (c *Client) SSHCheckHost(principal, token string) (*api.SSHCheckPrincipalResponse, error) { var retried bool body, err := json.Marshal(&api.SSHCheckPrincipalRequest{ Type: provisioner.SSHHostCert, diff --git a/ca/client_test.go b/ca/client_test.go index 30669e6e..187066f0 100644 --- a/ca/client_test.go +++ b/ca/client_test.go @@ -135,7 +135,7 @@ func parseCertificateRequest(data string) *x509.CertificateRequest { return csr } -func equalJSON(t *testing.T, a interface{}, b interface{}) bool { +func equalJSON(t *testing.T, a, b interface{}) bool { if reflect.DeepEqual(a, b) { return true } diff --git a/ca/identity/client_test.go b/ca/identity/client_test.go index c792a6dc..402ec7b8 100644 --- a/ca/identity/client_test.go +++ b/ca/identity/client_test.go @@ -187,11 +187,12 @@ func TestLoadClient(t *testing.T) { } else { gotTransport := got.Client.Transport.(*http.Transport) wantTransport := tt.want.Client.Transport.(*http.Transport) - if gotTransport.TLSClientConfig.GetClientCertificate == nil { + switch { + case gotTransport.TLSClientConfig.GetClientCertificate == nil: t.Error("LoadClient() transport does not define GetClientCertificate") - } else if !reflect.DeepEqual(got.CaURL, tt.want.CaURL) || !reflect.DeepEqual(gotTransport.TLSClientConfig.RootCAs.Subjects(), wantTransport.TLSClientConfig.RootCAs.Subjects()) { + case !reflect.DeepEqual(got.CaURL, tt.want.CaURL) || !reflect.DeepEqual(gotTransport.TLSClientConfig.RootCAs.Subjects(), wantTransport.TLSClientConfig.RootCAs.Subjects()): t.Errorf("LoadClient() = %#v, want %#v", got, tt.want) - } else { + default: crt, err := gotTransport.TLSClientConfig.GetClientCertificate(nil) if err != nil { t.Errorf("LoadClient() GetClientCertificate error = %v", err) diff --git a/ca/tls.go b/ca/tls.go index cb9f4707..3a3b6766 100644 --- a/ca/tls.go +++ b/ca/tls.go @@ -105,7 +105,7 @@ func (c *Client) getClientTLSConfig(ctx context.Context, sign *api.SignResponse, tr := getDefaultTransport(tlsConfig) // Use mutable tls.Config on renew - tr.DialTLS = c.buildDialTLS(tlsCtx) // nolint:staticcheck + tr.DialTLS = c.buildDialTLS(tlsCtx) // nolint:staticcheck,gocritic // tr.DialTLSContext = c.buildDialTLSContext(tlsCtx) renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) @@ -154,7 +154,7 @@ func (c *Client) GetServerTLSConfig(ctx context.Context, sign *api.SignResponse, // Update renew function with transport tr := getDefaultTransport(tlsConfig) // Use mutable tls.Config on renew - tr.DialTLS = c.buildDialTLS(tlsCtx) // nolint:staticcheck + tr.DialTLS = c.buildDialTLS(tlsCtx) // nolint:staticcheck,gocritic // tr.DialTLSContext = c.buildDialTLSContext(tlsCtx) renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) @@ -195,7 +195,7 @@ func (c *Client) buildDialTLS(ctx *TLSOptionCtx) func(network, addr string) (net } // buildDialTLSContext returns an implementation of DialTLSContext callback in http.Transport. -// nolint:unused +// nolint:unused,gocritic func (c *Client) buildDialTLSContext(tlsCtx *TLSOptionCtx) func(ctx context.Context, network, addr string) (net.Conn, error) { return func(ctx context.Context, network, addr string) (net.Conn, error) { d := getDefaultDialer() @@ -253,6 +253,8 @@ func TLSCertificate(sign *api.SignResponse, pk crypto.PrivateKey) (*tls.Certific return nil, err } + // nolint:gocritic + // using a new variable for clarity chain := append(certPEM, caPEM...) cert, err := tls.X509KeyPair(chain, keyPEM) if err != nil { diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index 2e9da260..e3e956a9 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -29,9 +29,7 @@ func init() { }) } -var now = func() time.Time { - return time.Now() -} +var now = time.Now // The actual regular expression that matches a certificate authority is: // ^projects/[a-z][a-z0-9-]{4,28}[a-z0-9]/locations/[a-z0-9-]+/caPools/[a-zA-Z0-9-_]+/certificateAuthorities/[a-zA-Z0-9-_]+$ diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index 0561000c..7f996c15 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -12,7 +12,6 @@ import ( "encoding/pem" "fmt" "io" - "log" "net" "os" "reflect" @@ -103,7 +102,7 @@ MHcCAQEEIN51Rgg6YcQVLeCRzumdw4pjM3VWqFIdCbnsV3Up1e/goAoGCCqGSM49 AwEHoUQDQgAEjJIcDhvvxi7gu4aFkiW/8+E3BfPhmhXU5RlDQusre+MHXc7XYMtk Lm6PXPeTF1DNdS21Ju1G/j1yUykGJOmxkg== -----END EC PRIVATE KEY-----` - // nolint:unused,deadcode + // nolint:unused,deadcode,gocritic testIntermediateKey = `-----BEGIN EC PRIVATE KEY----- MHcCAQEEIMMX/XkXGnRDD4fYu7Z4rHACdJn/iyOy2UTwsv+oZ0C+oAoGCCqGSM49 AwEHoUQDQgAE8u6rGAFj5CZpdzzMogLwUyCMnp0X9wtv4OKDRcpzkYf9PU5GuGA6 @@ -190,7 +189,7 @@ func (b *badSigner) Public() crypto.PublicKey { return b.pub } -func (b *badSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { +func (b *badSigner) Sign(rnd io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return nil, fmt.Errorf("💥") } @@ -730,7 +729,7 @@ func TestCloudCAS_RevokeCertificate(t *testing.T) { func Test_createCertificateID(t *testing.T) { buf := new(bytes.Buffer) setTeeReader(t, buf) - uuid, err := uuid.NewRandomFromReader(rand.Reader) + id, err := uuid.NewRandomFromReader(rand.Reader) if err != nil { t.Fatal(err) } @@ -741,7 +740,7 @@ func Test_createCertificateID(t *testing.T) { want string wantErr bool }{ - {"ok", uuid.String(), false}, + {"ok", id.String(), false}, {"fail", "", true}, } for _, tt := range tests { @@ -858,7 +857,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) { return lis.Dial() })) if err != nil { - log.Fatal(err) + t.Fatal(err) } client, err := lroauto.NewOperationsClient(context.Background(), option.WithGRPCConn(conn)) diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index 23dac91b..87dfa5c5 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -19,9 +19,7 @@ func init() { }) } -var now = func() time.Time { - return time.Now() -} +var now = time.Now // SoftCAS implements a Certificate Authority Service using Golang or KMS // crypto. This is the default CAS used in step-ca. diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index c8e1a8e9..bd13f310 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -133,7 +133,7 @@ func (b *badSigner) Public() crypto.PublicKey { return testSigner.Public() } -func (b *badSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { +func (b *badSigner) Sign(_ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) { return nil, fmt.Errorf("💥") } diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index a124b4ae..9fcbd36c 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -90,9 +90,9 @@ func (s *StepCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R return nil, apiv1.ErrNotImplemented{Message: "stepCAS does not support mTLS renewals"} } +// RevokeCertificate revokes a certificate. func (s *StepCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { - switch { - case req.SerialNumber == "" && req.Certificate == nil: + if req.SerialNumber == "" && req.Certificate == nil { return nil, errors.New("revokeCertificateRequest `serialNumber` or `certificate` are required") } diff --git a/cas/stepcas/x5c_issuer.go b/cas/stepcas/x5c_issuer.go index 636d22f9..76ed9c3c 100644 --- a/cas/stepcas/x5c_issuer.go +++ b/cas/stepcas/x5c_issuer.go @@ -19,9 +19,7 @@ const defaultValidity = 5 * time.Minute // timeNow returns the current time. // This method is used for unit testing purposes. -var timeNow = func() time.Time { - return time.Now() -} +var timeNow = time.Now type x5cIssuer struct { caURL *url.URL diff --git a/cas/stepcas/x5c_issuer_test.go b/cas/stepcas/x5c_issuer_test.go index a3190255..b1bc653d 100644 --- a/cas/stepcas/x5c_issuer_test.go +++ b/cas/stepcas/x5c_issuer_test.go @@ -22,7 +22,7 @@ func (b noneSigner) Public() crypto.PublicKey { return []byte(b) } -func (b noneSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { +func (b noneSigner) Sign(rnd io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { return digest, nil } diff --git a/cmd/step-awskms-init/main.go b/cmd/step-awskms-init/main.go index 0678ef39..8e30745f 100644 --- a/cmd/step-awskms-init/main.go +++ b/cmd/step-awskms-init/main.go @@ -24,10 +24,10 @@ import ( func main() { var credentialsFile, region string - var ssh bool + var enableSSH bool flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the AWS KMS credentials.") flag.StringVar(®ion, "region", "", "AWS KMS region name.") - flag.BoolVar(&ssh, "ssh", false, "Create SSH keys.") + flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.") flag.Usage = usage flag.Parse() @@ -47,7 +47,7 @@ func main() { fatal(err) } - if ssh { + if enableSSH { ui.Println() if err := createSSH(c); err != nil { fatal(err) @@ -120,7 +120,7 @@ func createX509(c *awskms.KMS) error { return err } - if err = fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -163,7 +163,7 @@ func createX509(c *awskms.KMS) error { return err } - if err = fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -193,7 +193,7 @@ func createSSH(c *awskms.KMS) error { return err } - if err = fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { + if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { return err } @@ -214,7 +214,7 @@ func createSSH(c *awskms.KMS) error { return err } - if err = fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { + if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { return err } diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index e0123678..bed1c14a 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -116,7 +116,7 @@ func main() { app.HelpName = "step-ca" app.Version = config.Version() app.Usage = "an online certificate authority for secure automated certificate management" - app.UsageText = `**step-ca** [**--password-file**=] + app.UsageText = `**step-ca** [**--password-file**=] [**--ssh-host-password-file**=] [**--ssh-user-password-file**=] [**--issuer-password-file**=] [**--resolver**=] [**--help**] [**--version**]` app.Description = `**step-ca** runs the Step Online Certificate Authority @@ -191,8 +191,8 @@ var placeholderString = regexp.MustCompile(`<.*?>`) func stringifyFlag(f cli.Flag) string { fv := flagValue(f) - usage := fv.FieldByName("Usage").String() - placeholder := placeholderString.FindString(usage) + usg := fv.FieldByName("Usage").String() + placeholder := placeholderString.FindString(usg) if placeholder == "" { switch f.(type) { case cli.BoolFlag, cli.BoolTFlag: @@ -200,5 +200,5 @@ func stringifyFlag(f cli.Flag) string { placeholder = "" } } - return cli.FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder) + "\t" + usage + return cli.FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder) + "\t" + usg } diff --git a/cmd/step-cloudkms-init/main.go b/cmd/step-cloudkms-init/main.go index 14bf50f1..27dc82ad 100644 --- a/cmd/step-cloudkms-init/main.go +++ b/cmd/step-cloudkms-init/main.go @@ -27,13 +27,13 @@ func main() { var credentialsFile string var project, location, ring string var protectionLevelName string - var ssh bool + var enableSSH bool flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the Google's Cloud KMS credentials.") flag.StringVar(&project, "project", "", "Google Cloud Project ID.") flag.StringVar(&location, "location", "global", "Cloud KMS location name.") flag.StringVar(&ring, "ring", "pki", "Cloud KMS ring name.") flag.StringVar(&protectionLevelName, "protection-level", "SOFTWARE", "Protection level to use, SOFTWARE or HSM.") - flag.BoolVar(&ssh, "ssh", false, "Create SSH keys.") + flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.") flag.Usage = usage flag.Parse() @@ -77,7 +77,7 @@ func main() { fatal(err) } - if ssh { + if enableSSH { ui.Println() if err := createSSH(c, project, location, ring, protectionLevel); err != nil { fatal(err) @@ -153,7 +153,7 @@ func createPKI(c *cloudkms.CloudKMS, project, location, keyRing string, protecti return err } - if err = fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -197,7 +197,7 @@ func createPKI(c *cloudkms.CloudKMS, project, location, keyRing string, protecti return err } - if err = fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -230,7 +230,7 @@ func createSSH(c *cloudkms.CloudKMS, project, location, keyRing string, protecti return err } - if err = fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { + if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { return err } @@ -252,7 +252,7 @@ func createSSH(c *cloudkms.CloudKMS, project, location, keyRing string, protecti return err } - if err = fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { + if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { return err } diff --git a/cmd/step-pkcs11-init/main.go b/cmd/step-pkcs11-init/main.go index 78c531c6..8e7bc075 100644 --- a/cmd/step-pkcs11-init/main.go +++ b/cmd/step-pkcs11-init/main.go @@ -329,7 +329,7 @@ func createPKI(k kms.KeyManager, c Config) error { } if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts { - if err = cm.StoreCertificate(&apiv1.StoreCertificateRequest{ + if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ Name: c.RootObject, Certificate: root, }); err != nil { @@ -337,7 +337,7 @@ func createPKI(k kms.KeyManager, c Config) error { } } - if err = fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -406,7 +406,7 @@ func createPKI(k kms.KeyManager, c Config) error { } if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts { - if err = cm.StoreCertificate(&apiv1.StoreCertificateRequest{ + if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ Name: c.CrtObject, Certificate: intermediate, }); err != nil { @@ -414,7 +414,7 @@ func createPKI(k kms.KeyManager, c Config) error { } } - if err = fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { diff --git a/cmd/step-yubikey-init/main.go b/cmd/step-yubikey-init/main.go index 163d0fcb..8b0ffab5 100644 --- a/cmd/step-yubikey-init/main.go +++ b/cmd/step-yubikey-init/main.go @@ -228,7 +228,7 @@ func createPKI(k kms.KeyManager, c Config) error { } if cm, ok := k.(kms.CertificateManager); ok { - if err = cm.StoreCertificate(&apiv1.StoreCertificateRequest{ + if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ Name: c.RootSlot, Certificate: root, }); err != nil { @@ -236,7 +236,7 @@ func createPKI(k kms.KeyManager, c Config) error { } } - if err = fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { @@ -305,7 +305,7 @@ func createPKI(k kms.KeyManager, c Config) error { } if cm, ok := k.(kms.CertificateManager); ok { - if err = cm.StoreCertificate(&apiv1.StoreCertificateRequest{ + if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ Name: c.CrtSlot, Certificate: intermediate, }); err != nil { @@ -313,7 +313,7 @@ func createPKI(k kms.KeyManager, c Config) error { } } - if err = fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ + if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: b, }), 0600); err != nil { diff --git a/commands/app.go b/commands/app.go index 3aaee0f5..84232a6c 100644 --- a/commands/app.go +++ b/commands/app.go @@ -24,7 +24,7 @@ var AppCommand = cli.Command{ Name: "start", Action: appAction, UsageText: `**step-ca** [**--password-file**=] -[**--ssh-host-password-file**=] [**--ssh-user-password-file**=] +[**--ssh-host-password-file**=] [**--ssh-user-password-file**=] [**--issuer-password-file**=] [**--resolver**=]`, Flags: []cli.Flag{ cli.StringFlag{ @@ -79,13 +79,13 @@ func appAction(ctx *cli.Context) error { } configFile := ctx.Args().Get(0) - config, err := config.LoadConfiguration(configFile) + cfg, err := config.LoadConfiguration(configFile) if err != nil { fatal(err) } - if config.AuthorityConfig != nil { - if token == "" && strings.EqualFold(config.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) { + if cfg.AuthorityConfig != nil { + if token == "" && strings.EqualFold(cfg.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) { return errors.New(`'step-ca' requires the '--token' flag for linked deploy type. To get a linked authority token: @@ -136,7 +136,7 @@ To get a linked authority token: } } - srv, err := ca.New(config, + srv, err := ca.New(cfg, ca.WithConfigFile(configFile), ca.WithPassword(password), ca.WithSSHHostPassword(sshHostPassword), diff --git a/commands/export.go b/commands/export.go index be6d88e5..5586f576 100644 --- a/commands/export.go +++ b/commands/export.go @@ -63,11 +63,11 @@ func exportAction(ctx *cli.Context) error { passwordFile := ctx.String("password-file") issuerPasswordFile := ctx.String("issuer-password-file") - config, err := config.LoadConfiguration(configFile) + cfg, err := config.LoadConfiguration(configFile) if err != nil { return err } - if err := config.Validate(); err != nil { + if err := cfg.Validate(); err != nil { return err } @@ -76,19 +76,19 @@ func exportAction(ctx *cli.Context) error { if err != nil { return errors.Wrapf(err, "error reading %s", passwordFile) } - config.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) + cfg.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) } if issuerPasswordFile != "" { b, err := ioutil.ReadFile(issuerPasswordFile) if err != nil { return errors.Wrapf(err, "error reading %s", issuerPasswordFile) } - if config.AuthorityConfig.CertificateIssuer != nil { - config.AuthorityConfig.CertificateIssuer.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) + if cfg.AuthorityConfig.CertificateIssuer != nil { + cfg.AuthorityConfig.CertificateIssuer.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace)) } } - auth, err := authority.New(config) + auth, err := authority.New(cfg) if err != nil { return err } diff --git a/commands/onboard.go b/commands/onboard.go index eb8285aa..ebd468f5 100644 --- a/commands/onboard.go +++ b/commands/onboard.go @@ -103,8 +103,8 @@ func onboardAction(ctx *cli.Context) error { return errors.Wrap(msg, "error receiving onboarding guide") } - var config onboardingConfiguration - if err := readJSON(res.Body, &config); err != nil { + var cfg onboardingConfiguration + if err := readJSON(res.Body, &cfg); err != nil { return errors.Wrap(err, "error unmarshaling response") } @@ -112,16 +112,16 @@ func onboardAction(ctx *cli.Context) error { if err != nil { return err } - config.password = []byte(password) + cfg.password = []byte(password) ui.Println("Initializing step-ca with the following configuration:") - ui.PrintSelected("Name", config.Name) - ui.PrintSelected("DNS", config.DNS) - ui.PrintSelected("Address", config.Address) + ui.PrintSelected("Name", cfg.Name) + ui.PrintSelected("DNS", cfg.DNS) + ui.PrintSelected("Address", cfg.Address) ui.PrintSelected("Password", password) ui.Println() - caConfig, fp, err := onboardPKI(config) + caConfig, fp, err := onboardPKI(cfg) if err != nil { return err } @@ -149,23 +149,23 @@ func onboardAction(ctx *cli.Context) error { ui.Println("Initialized!") ui.Println("Step CA is starting. Please return to the onboarding guide in your browser to continue.") - srv, err := ca.New(caConfig, ca.WithPassword(config.password)) + srv, err := ca.New(caConfig, ca.WithPassword(cfg.password)) if err != nil { fatal(err) } go ca.StopReloaderHandler(srv) - if err = srv.Run(); err != nil && err != http.ErrServerClosed { + if err := srv.Run(); err != nil && err != http.ErrServerClosed { fatal(err) } return nil } -func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) { +func onboardPKI(cfg onboardingConfiguration) (*config.Config, string, error) { var opts = []pki.Option{ - pki.WithAddress(config.Address), - pki.WithDNSNames([]string{config.DNS}), + pki.WithAddress(cfg.Address), + pki.WithDNSNames([]string{cfg.DNS}), pki.WithProvisioner("admin"), } @@ -179,25 +179,25 @@ func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) // Generate pki ui.Println("Generating root certificate...") - root, err := p.GenerateRootCertificate(config.Name, config.Name, config.Name, config.password) + root, err := p.GenerateRootCertificate(cfg.Name, cfg.Name, cfg.Name, cfg.password) if err != nil { return nil, "", err } ui.Println("Generating intermediate certificate...") - err = p.GenerateIntermediateCertificate(config.Name, config.Name, config.Name, root, config.password) + err = p.GenerateIntermediateCertificate(cfg.Name, cfg.Name, cfg.Name, root, cfg.password) if err != nil { return nil, "", err } // Write files to disk - if err = p.WriteFiles(); err != nil { + if err := p.WriteFiles(); err != nil { return nil, "", err } // Generate provisioner ui.Println("Generating admin provisioner...") - if err = p.GenerateKeyPairs(config.password); err != nil { + if err := p.GenerateKeyPairs(cfg.password); err != nil { return nil, "", err } @@ -211,7 +211,7 @@ func onboardPKI(config onboardingConfiguration) (*config.Config, string, error) if err != nil { return nil, "", errors.Wrapf(err, "error marshaling %s", p.GetCAConfigPath()) } - if err = fileutil.WriteFile(p.GetCAConfigPath(), b, 0666); err != nil { + if err := fileutil.WriteFile(p.GetCAConfigPath(), b, 0666); err != nil { return nil, "", errs.FileError(err, p.GetCAConfigPath()) } diff --git a/db/db_test.go b/db/db_test.go index 7efc623e..40f59215 100644 --- a/db/db_test.go +++ b/db/db_test.go @@ -144,15 +144,15 @@ func TestUseToken(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ok, err := tc.db.UseToken(tc.id, tc.tok) - if err != nil { + switch ok, err := tc.db.UseToken(tc.id, tc.tok); { + case err != nil: if assert.NotNil(t, tc.want.err) { assert.HasPrefix(t, err.Error(), tc.want.err.Error()) } assert.False(t, ok) - } else if ok { + case ok: assert.True(t, tc.want.ok) - } else { + default: assert.False(t, tc.want.ok) } }) diff --git a/kms/sshagentkms/sshagentkms_test.go b/kms/sshagentkms/sshagentkms_test.go index 30edd5d1..d3a9e9f5 100644 --- a/kms/sshagentkms/sshagentkms_test.go +++ b/kms/sshagentkms/sshagentkms_test.go @@ -378,6 +378,7 @@ func TestSSHAgentKMS_CreateSigner(t *testing.T) { t.Errorf("SSHAgentKMS.CreateSigner() error = %v, wantErr %v", err, tt.wantErr) return } + // nolint:gocritic switch s := got.(type) { case *WrappedSSHSigner: gotPkS := s.Sshsigner.PublicKey().(*agent.Key).String() + "\n" @@ -562,6 +563,7 @@ func TestSSHAgentKMS_GetPublicKey(t *testing.T) { t.Errorf("SSHAgentKMS.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr) return } + // nolint:gocritic switch tt.want.(type) { case ssh.PublicKey: // If we want a ssh.PublicKey, protote got to a diff --git a/pki/pki.go b/pki/pki.go index 12e71e47..18cd0dda 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -128,7 +128,7 @@ func GetTemplatesPath() string { // GetProvisioners returns the map of provisioners on the given CA. func GetProvisioners(caURL, rootFile string) (provisioner.List, error) { - if len(rootFile) == 0 { + if rootFile == "" { rootFile = GetRootCAPath() } client, err := ca.NewClient(caURL, ca.WithRootFile(rootFile)) @@ -153,7 +153,7 @@ func GetProvisioners(caURL, rootFile string) (provisioner.List, error) { // GetProvisionerKey returns the encrypted provisioner key with the for the // given kid. func GetProvisionerKey(caURL, rootFile, kid string) (string, error) { - if len(rootFile) == 0 { + if rootFile == "" { rootFile = GetRootCAPath() } client, err := ca.NewClient(caURL, ca.WithRootFile(rootFile)) @@ -315,17 +315,17 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { // Use /home/step as the step path in helm configurations. // Use the current step path when creating pki in files. - var public, private, config string + var public, private, cfg string if p.options.isHelm { public = "/home/step/certs" private = "/home/step/secrets" - config = "/home/step/config" + cfg = "/home/step/config" } else { public = GetPublicPath() private = GetSecretsPath() - config = GetConfigPath() + cfg = GetConfigPath() // Create directories - dirs := []string{public, private, config, GetTemplatesPath()} + dirs := []string{public, private, cfg, GetTemplatesPath()} for _, name := range dirs { if _, err := os.Stat(name); os.IsNotExist(err) { if err = os.MkdirAll(name, 0700); err != nil { @@ -380,10 +380,10 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { if p.Ssh.UserKey, err = getPath(private, "ssh_user_ca_key"); err != nil { return nil, err } - if p.defaults, err = getPath(config, "defaults.json"); err != nil { + if p.defaults, err = getPath(cfg, "defaults.json"); err != nil { return nil, err } - if p.config, err = getPath(config, "ca.json"); err != nil { + if p.config, err = getPath(cfg, "ca.json"); err != nil { return nil, err } p.Defaults.CaConfig = p.config @@ -620,16 +620,17 @@ func (p *PKI) askFeedback() { func (p *PKI) tellPKI() { ui.Println() - if p.casOptions.Is(apiv1.SoftCAS) { + switch { + case p.casOptions.Is(apiv1.SoftCAS): ui.PrintSelected("Root certificate", p.Root[0]) ui.PrintSelected("Root private key", p.RootKey[0]) ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint) ui.PrintSelected("Intermediate certificate", p.Intermediate) ui.PrintSelected("Intermediate private key", p.IntermediateKey) - } else if p.Defaults.Fingerprint != "" { + case p.Defaults.Fingerprint != "": ui.PrintSelected("Root certificate", p.Root[0]) ui.PrintSelected("Root fingerprint", p.Defaults.Fingerprint) - } else { + default: ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad) } if p.options.enableSSH { @@ -657,7 +658,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { authorityOptions = &p.casOptions } - config := &authconfig.Config{ + cfg := &authconfig.Config{ Root: p.Root, FederatedRoots: p.FederatedRoots, IntermediateCert: p.Intermediate, @@ -681,7 +682,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { // Add linked as a deployment type to detect it on start and provide a // message if the token is not given. if p.options.deploymentType == LinkedDeployment { - config.AuthorityConfig.DeploymentType = LinkedDeployment.String() + cfg.AuthorityConfig.DeploymentType = LinkedDeployment.String() } // On standalone deployments add the provisioners to either the ca.json or @@ -711,7 +712,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { if p.options.enableSSH { enableSSHCA := true - config.SSH = &authconfig.SSHConfig{ + cfg.SSH = &authconfig.SSHConfig{ HostKey: p.Ssh.HostKey, UserKey: p.Ssh.UserKey, } @@ -733,19 +734,19 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { // Apply configuration modifiers for _, o := range opt { - if err := o(config); err != nil { + if err := o(cfg); err != nil { return nil, err } } // Set authority.enableAdmin to true if p.options.enableAdmin { - config.AuthorityConfig.EnableAdmin = true + cfg.AuthorityConfig.EnableAdmin = true } if p.options.deploymentType == StandaloneDeployment { - if !config.AuthorityConfig.EnableAdmin { - config.AuthorityConfig.Provisioners = provisioners + if !cfg.AuthorityConfig.EnableAdmin { + cfg.AuthorityConfig.Provisioners = provisioners } else { // At this moment this code path is never used because `step ca // init` will always set enableAdmin to false for a standalone @@ -754,11 +755,11 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { // // Note that we might want to be able to define the database as a // flag in `step ca init` so we can write to the proper place. - db, err := db.New(config.DB) + _db, err := db.New(cfg.DB) if err != nil { return nil, err } - adminDB, err := admindb.New(db.(nosql.DB), admin.DefaultAuthorityID) + adminDB, err := admindb.New(_db.(nosql.DB), admin.DefaultAuthorityID) if err != nil { return nil, err } @@ -788,7 +789,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { } } - return config, nil + return cfg, nil } // Save stores the pki on a json file that will be used as the certificate @@ -804,12 +805,12 @@ func (p *PKI) Save(opt ...ConfigOption) error { // Generate and write ca.json if !p.options.pkiOnly { - config, err := p.GenerateConfig(opt...) + cfg, err := p.GenerateConfig(opt...) if err != nil { return err } - b, err := json.MarshalIndent(config, "", "\t") + b, err := json.MarshalIndent(cfg, "", "\t") if err != nil { return errors.Wrapf(err, "error marshaling %s", p.config) } @@ -833,14 +834,14 @@ func (p *PKI) Save(opt ...ConfigOption) error { } // Generate and write templates - if err := generateTemplates(config.Templates); err != nil { + if err := generateTemplates(cfg.Templates); err != nil { return err } - if config.DB != nil { - ui.PrintSelected("Database folder", config.DB.DataSource) + if cfg.DB != nil { + ui.PrintSelected("Database folder", cfg.DB.DataSource) } - if config.Templates != nil { + if cfg.Templates != nil { ui.PrintSelected("Templates folder", GetTemplatesPath()) } diff --git a/scep/api/api.go b/scep/api/api.go index e64eef83..4e02d4a1 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -198,14 +198,14 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return } - provisioner, ok := p.(*provisioner.SCEP) + prov, ok := p.(*provisioner.SCEP) if !ok { api.WriteError(w, errors.New("provisioner must be of type SCEP")) return } ctx := r.Context() - ctx = context.WithValue(ctx, scep.ProvisionerContextKey, scep.Provisioner(provisioner)) + ctx = context.WithValue(ctx, scep.ProvisionerContextKey, scep.Provisioner(prov)) next(w, r.WithContext(ctx)) } } diff --git a/templates/templates.go b/templates/templates.go index f98fb866..16e891d9 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "text/template" "github.com/Masterminds/sprig/v3" @@ -226,14 +227,11 @@ func (t *Template) Output(data interface{}) (Output, error) { // backfill updates old templates with the required data. func (t *Template) backfill(b []byte) { - switch t.Name { - case "sshd_config.tpl": - if len(t.RequiredData) == 0 { - a := bytes.TrimSpace(b) - b := bytes.TrimSpace([]byte(DefaultSSHTemplateData[t.Name])) - if bytes.Equal(a, b) { - t.RequiredData = []string{"Certificate", "Key"} - } + if strings.EqualFold(t.Name, "sshd_config.tpl") && len(t.RequiredData) == 0 { + a := bytes.TrimSpace(b) + b := bytes.TrimSpace([]byte(DefaultSSHTemplateData[t.Name])) + if bytes.Equal(a, b) { + t.RequiredData = []string{"Certificate", "Key"} } } } From 5fc24c697cd433536707719456f6d577206be5a8 Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 8 Oct 2021 15:26:01 -0400 Subject: [PATCH 268/291] Fix a few more linter warnings and remove GOFLAGS from make lint --- Makefile | 2 +- kms/pkcs11/other_test.go | 4 ++-- kms/pkcs11/pkcs11.go | 11 +++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 108efa1d..09e342df 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ fmt: $Q gofmt -l -w $(SRC) lint: - $Q $(GOFLAGS) LOG_LEVEL=error golangci-lint run --timeout=30m + $Q golangci-lint run --timeout=30m lintcgo: $Q LOG_LEVEL=error golangci-lint run --timeout=30m diff --git a/kms/pkcs11/other_test.go b/kms/pkcs11/other_test.go index 680d3860..3e168716 100644 --- a/kms/pkcs11/other_test.go +++ b/kms/pkcs11/other_test.go @@ -166,10 +166,10 @@ func (s *privateKey) Delete() error { return nil } -func (s *privateKey) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) { +func (s *privateKey) Decrypt(rnd io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) { k, ok := s.Signer.(*rsa.PrivateKey) if !ok { return nil, errors.New("key is not an rsa key") } - return k.Decrypt(rand, msg, opts) + return k.Decrypt(rnd, msg, opts) } diff --git a/kms/pkcs11/pkcs11.go b/kms/pkcs11/pkcs11.go index 07d40c05..7924f106 100644 --- a/kms/pkcs11/pkcs11.go +++ b/kms/pkcs11/pkcs11.go @@ -145,8 +145,7 @@ func (k *PKCS11) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespons // CreateSigner creates a signer using the key present in the PKCS#11 MODULE signature // slot. func (k *PKCS11) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) { - switch { - case req.SigningKey == "": + if req.SigningKey == "" { return nil, errors.New("createSignerRequest 'signingKey' cannot be empty") } @@ -204,8 +203,8 @@ func (k *PKCS11) StoreCertificate(req *apiv1.StoreCertificateRequest) error { } // DeleteKey is a utility function to delete a key given an uri. -func (k *PKCS11) DeleteKey(uri string) error { - id, object, err := parseObject(uri) +func (k *PKCS11) DeleteKey(u string) error { + id, object, err := parseObject(u) if err != nil { return errors.Wrap(err, "deleteKey failed") } @@ -223,8 +222,8 @@ func (k *PKCS11) DeleteKey(uri string) error { } // DeleteCertificate is a utility function to delete a certificate given an uri. -func (k *PKCS11) DeleteCertificate(uri string) error { - id, object, err := parseObject(uri) +func (k *PKCS11) DeleteCertificate(u string) error { + id, object, err := parseObject(u) if err != nil { return errors.Wrap(err, "deleteCertificate failed") } From bdc9ffbe9039c292e4cf55b1e2119e79160553de Mon Sep 17 00:00:00 2001 From: max furman Date: Fri, 8 Oct 2021 15:44:04 -0400 Subject: [PATCH 269/291] changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a902ee2f..c5319526 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased - 0.17.5] - DATE ### Added +- gocritic linter ### Changed ### Deprecated ### Removed ### Fixed +- gocritic warnings ### Security ## [0.17.4] - 2021-09-28 From 781d5fb6e8a0754c3b93ff7be27e21d64724a820 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 8 Oct 2021 14:25:24 -0700 Subject: [PATCH 270/291] Fix creation of ssh certificates on step ca init. --- pki/pki.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 41a644e1..b4ac26da 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -341,7 +341,7 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) { } // Use default key manager - if p.keyManager != nil { + if p.keyManager == nil { p.keyManager = kms.Default } @@ -634,7 +634,7 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { // Create SSH key used to sign host certificates. Using // kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm. - name := p.Ssh.HostPublicKey + name := p.Ssh.HostKey if uri := p.options.hostKeyURI; uri != "" { name = uri } @@ -649,7 +649,7 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return errors.Wrapf(err, "error converting public key") } - p.Files[resp.Name] = ssh.MarshalAuthorizedKey(sshKey) + p.Files[p.Ssh.HostPublicKey] = ssh.MarshalAuthorizedKey(sshKey) // On softkms we will have the private key if resp.PrivateKey != nil { @@ -657,11 +657,13 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return err } + } else { + p.Ssh.HostKey = resp.Name } // Create SSH key used to sign user certificates. Using // kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm. - name = p.Ssh.UserPublicKey + name = p.Ssh.UserKey if uri := p.options.userKeyURI; uri != "" { name = uri } @@ -676,7 +678,7 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return errors.Wrapf(err, "error converting public key") } - p.Files[resp.Name] = ssh.MarshalAuthorizedKey(sshKey) + p.Files[p.Ssh.UserPublicKey] = ssh.MarshalAuthorizedKey(sshKey) // On softkms we will have the private key if resp.PrivateKey != nil { @@ -684,6 +686,8 @@ func (p *PKI) GenerateSSHSigningKeys(password []byte) error { if err != nil { return err } + } else { + p.Ssh.UserKey = resp.Name } return nil From 5d0bd7d1557985fa76443aff78ce848437d09a78 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 12 Oct 2021 15:14:01 -0700 Subject: [PATCH 271/291] Fix grammar in comments. --- kms/azurekms/key_vault.go | 8 ++++---- kms/azurekms/signer.go | 2 +- pki/pki.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 1fb8572c..b9a17b1c 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -19,7 +19,7 @@ func init() { }) } -// Scheme is the scheme used for Azure Key Vault uris. +// Scheme is the scheme used for the Azure Key Vault uris. const Scheme = "azurekms" // keyIDRegexp is the regular expression that Key Vault uses for on the kid. We @@ -96,11 +96,11 @@ var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]keyType{ }, } -// vaultResource is that the client will use as audience. +// vaultResource is the value the client will use as audience. const vaultResource = "https://vault.azure.net" -// KeyVaultClient is the interface implemented by keyvault.BaseClient. It it -// will be used for testing purposes. +// KeyVaultClient is the interface implemented by keyvault.BaseClient. It will +// be used for testing purposes. type KeyVaultClient interface { GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (keyvault.KeyBundle, error) CreateKey(ctx context.Context, vaultBaseURL string, keyName string, parameters keyvault.KeyCreateParameters) (keyvault.KeyBundle, error) diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index cf0197fb..405c625a 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -99,7 +99,7 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] return sig, nil } - // Convert to ans1 + // Convert to asn1 if len(sig) != octetSize*2 { return nil, errors.Errorf("keyVault Sign failed: unexpected signature length") } diff --git a/pki/pki.go b/pki/pki.go index b4ac26da..8a61d025 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -263,7 +263,7 @@ func WithDeploymentType(dt DeploymentType) Option { } } -// WithKMS enabled the kms with the given name. +// WithKMS enables the kms with the given name. func WithKMS(name string) Option { return func(p *PKI) { typ := linkedca.KMS_Type_value[strings.ToUpper(name)] From 2aee71b4c0772a2e9a8b9bb9df1b9a510f9fa9e0 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 12 Oct 2021 15:18:17 -0700 Subject: [PATCH 272/291] Fix typo. --- kms/azurekms/key_vault.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index b9a17b1c..27090f30 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -22,8 +22,8 @@ func init() { // Scheme is the scheme used for the Azure Key Vault uris. const Scheme = "azurekms" -// keyIDRegexp is the regular expression that Key Vault uses for on the kid. We -// can extract the vault, name and version of the key. +// keyIDRegexp is the regular expression that Key Vault uses on the kid. We can +// extract the vault, name and version of the key. var keyIDRegexp = regexp.MustCompile("^https://([0-9a-zA-Z-]+).vault.azure.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$") var ( From a2b03083c85c204f2cbb9d8687086cfbbb9bfab5 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 12 Oct 2021 15:28:08 -0700 Subject: [PATCH 273/291] Fix gocritic warnings. --- kms/azurekms/key_vault.go | 5 ++--- kms/azurekms/signer_test.go | 31 ++++++++++++++++--------------- pki/pki.go | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 27090f30..93be8241 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -24,7 +24,7 @@ const Scheme = "azurekms" // keyIDRegexp is the regular expression that Key Vault uses on the kid. We can // extract the vault, name and version of the key. -var keyIDRegexp = regexp.MustCompile("^https://([0-9a-zA-Z-]+).vault.azure.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$") +var keyIDRegexp = regexp.MustCompile(`^https://([0-9a-zA-Z-]+)\.vault\.azure\.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$`) var ( valueTrue = true @@ -162,8 +162,7 @@ func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { // GetPublicKey loads a public key from Azure Key Vault by its resource name. func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) { - switch { - case req.Name == "": + if req.Name == "" { return nil, errors.New("getPublicKeyRequest 'name' cannot be empty") } diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go index 90740b9f..01921e2a 100644 --- a/kms/azurekms/signer_test.go +++ b/kms/azurekms/signer_test.go @@ -134,6 +134,7 @@ func TestSigner_Sign(t *testing.T) { sBytes := s.Bytes() sBytesPadded := make([]byte, keyBytes) copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + // nolint:gocritic resultSig = append(rBytesPadded, sBytesPadded...) var b cryptobyte.Builder @@ -256,61 +257,61 @@ func TestSigner_Sign(t *testing.T) { wantErr bool }{ {"ok P-256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ - rand.Reader, p256Digest[:], crypto.SHA256, + rand.Reader, p256Digest, crypto.SHA256, }, p256Sig, false}, {"ok P-384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p384}, args{ - rand.Reader, p384Digest[:], crypto.SHA384, + rand.Reader, p384Digest, crypto.SHA384, }, p384Sig, false}, {"ok P-521", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p521}, args{ - rand.Reader, p521Digest[:], crypto.SHA512, + rand.Reader, p521Digest, crypto.SHA512, }, p521Sig, false}, {"ok RSA SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ - rand.Reader, rsaSHA256Digest[:], crypto.SHA256, + rand.Reader, rsaSHA256Digest, crypto.SHA256, }, rsaSHA256Sig, false}, {"ok RSA SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA384}, args{ - rand.Reader, rsaSHA384Digest[:], crypto.SHA384, + rand.Reader, rsaSHA384Digest, crypto.SHA384, }, rsaSHA384Sig, false}, {"ok RSA SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA512}, args{ - rand.Reader, rsaSHA512Digest[:], crypto.SHA512, + rand.Reader, rsaSHA512Digest, crypto.SHA512, }, rsaSHA512Sig, false}, {"ok RSA-PSS SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{ - rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{ + rand.Reader, rsaPSSSHA256Digest, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA256, }, }, rsaPSSSHA256Sig, false}, {"ok RSA-PSS SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA384}, args{ - rand.Reader, rsaPSSSHA384Digest[:], &rsa.PSSOptions{ + rand.Reader, rsaPSSSHA384Digest, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA384, }, }, rsaPSSSHA384Sig, false}, {"ok RSA-PSS SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA512}, args{ - rand.Reader, rsaPSSSHA512Digest[:], &rsa.PSSOptions{ + rand.Reader, rsaPSSSHA512Digest, &rsa.PSSOptions{ SaltLength: 64, Hash: crypto.SHA512, }, }, rsaPSSSHA512Sig, false}, {"fail Sign", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ - rand.Reader, rsaSHA256Digest[:], crypto.SHA256, + rand.Reader, rsaSHA256Digest, crypto.SHA256, }, nil, true}, {"fail sign length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ - rand.Reader, p256Digest[:], crypto.SHA256, + rand.Reader, p256Digest, crypto.SHA256, }, nil, true}, {"fail base64", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ - rand.Reader, p256Digest[:], crypto.SHA256, + rand.Reader, p256Digest, crypto.SHA256, }, nil, true}, {"fail RSA-PSS salt length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{ - rand.Reader, rsaPSSSHA256Digest[:], &rsa.PSSOptions{ + rand.Reader, rsaPSSSHA256Digest, &rsa.PSSOptions{ SaltLength: 64, Hash: crypto.SHA256, }, }, nil, true}, {"fail RSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{ - rand.Reader, rsaSHA256Digest[:], crypto.SHA1, + rand.Reader, rsaSHA256Digest, crypto.SHA1, }, nil, true}, {"fail ECDSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ - rand.Reader, p256Digest[:], crypto.MD5, + rand.Reader, p256Digest, crypto.MD5, }, nil, true}, {"fail Ed25519", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", ed25519Key}, args{ rand.Reader, []byte("message"), crypto.Hash(0), diff --git a/pki/pki.go b/pki/pki.go index 92d640cf..61e20b6b 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -792,7 +792,7 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { // Enable KMS if necessary if p.Kms != nil { - config.KMS = &kmsapi.Options{ + cfg.KMS = &kmsapi.Options{ Type: strings.ToLower(p.Kms.Type.String()), } } From 44f0d61354ae4cfdd894a27842f932a0749a2d4c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 12 Oct 2021 15:41:41 -0700 Subject: [PATCH 274/291] Fix typo. --- kms/uri/uri.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/uri/uri.go b/kms/uri/uri.go index 3a1b8981..36e15e7d 100644 --- a/kms/uri/uri.go +++ b/kms/uri/uri.go @@ -95,8 +95,8 @@ func (u *URI) Get(key string) string { return v } -// GetBool returns true if a given key has the value "true". It will returns -// false otherwise. +// GetBool returns true if a given key has the value "true". It returns false +// otherwise. func (u *URI) GetBool(key string) bool { v := u.Values.Get(key) if v == "" { From 02d601861b040b29bd7ed099441979ef506fc3f8 Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 12 Oct 2021 15:44:04 -0700 Subject: [PATCH 275/291] [action] Simply goreleaser targets --- .goreleaser.yml | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 97b9c24c..a2f9969d 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -12,25 +12,16 @@ builds: id: step-ca env: - CGO_ENABLED=0 - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm - - arm64 - - 386 - goarm: - - 6 - - 7 - ignore: - - goos: windows - goarch: 386 - - goos: windows - goarm: 6 - - goos: windows - goarm: 7 + targets: + - darwin_amd64 + - darwin_arm64 + - linux_386 + - linux_amd64 + - linux_arm64 + - linux_arm_6 + - linux_arm_7 + - windows_amd64 + - windows_arm64 flags: - -trimpath main: ./cmd/step-ca/main.go From 9f8ffcf19625d7952f570af748afbece63481a3f Mon Sep 17 00:00:00 2001 From: max furman Date: Tue, 12 Oct 2021 16:40:24 -0700 Subject: [PATCH 276/291] [action] remove windows_amd64, add freebsd_amd64 --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index a2f9969d..60eff7e7 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -15,13 +15,13 @@ builds: targets: - darwin_amd64 - darwin_arm64 + - freebsd_amd64 - linux_386 - linux_amd64 - linux_arm64 - linux_arm_6 - linux_arm_7 - windows_amd64 - - windows_arm64 flags: - -trimpath main: ./cmd/step-ca/main.go From edd475b81bb0ee9b65ec8df9c041910b0e4edf19 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 12 Oct 2021 18:24:58 -0700 Subject: [PATCH 277/291] Allow to configure azurekms using the URI With an URI, azurekms can be configured with client credentials, and it can define a default vault and protection level. --- go.mod | 1 + kms/azurekms/key_vault.go | 79 +++++++++++++++++++++++++++++++--- kms/azurekms/key_vault_test.go | 72 +++++++++++++++++++++++++++++++ kms/azurekms/signer.go | 4 +- kms/azurekms/signer_test.go | 27 +++++++++--- kms/azurekms/utils.go | 22 +++++++--- kms/azurekms/utils_test.go | 33 ++++++++------ 7 files changed, 203 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index f3d2e358..791e0927 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( cloud.google.com/go v0.83.0 github.com/Azure/azure-sdk-for-go v58.0.0+incompatible + github.com/Azure/go-autorest/autorest v0.11.17 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/date v0.3.0 github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect diff --git a/kms/azurekms/key_vault.go b/kms/azurekms/key_vault.go index 93be8241..34d9c3f1 100644 --- a/kms/azurekms/key_vault.go +++ b/kms/azurekms/key_vault.go @@ -7,10 +7,12 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/Azure/go-autorest/autorest/date" "github.com/pkg/errors" "github.com/smallstep/certificates/kms/apiv1" + "github.com/smallstep/certificates/kms/uri" ) func init() { @@ -126,9 +128,60 @@ type KeyVaultClient interface { // functionality in /sdk/keyvault, we should migrate to that once available. type KeyVault struct { baseClient KeyVaultClient + defaults DefaultOptions +} + +// DefaultOptions are custom options that can be passed as defaults using the +// URI in apiv1.Options. +type DefaultOptions struct { + Vault string + ProtectionLevel apiv1.ProtectionLevel } var createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + baseClient := keyvault.New() + + // With an URI, try to log in only using client credentials in the URI. + // Client credentials requires: + // - client-id + // - client-secret + // - tenant-id + // And optionally the aad-endpoint to support custom clouds: + // - aad-endpoint (defaults to https://login.microsoftonline.com/) + if opts.URI != "" { + u, err := uri.ParseWithScheme(Scheme, opts.URI) + if err != nil { + return nil, err + } + + // Required options + clientID := u.Get("client-id") + clientSecret := u.Get("client-secret") + tenantID := u.Get("tenant-id") + // optional + aadEndpoint := u.Get("aad-endpoint") + + if clientID != "" && clientSecret != "" && tenantID != "" { + s := auth.EnvironmentSettings{ + Values: map[string]string{ + auth.ClientID: clientID, + auth.ClientSecret: clientSecret, + auth.TenantID: tenantID, + auth.Resource: vaultResource, + }, + Environment: azure.PublicCloud, + } + if aadEndpoint != "" { + s.Environment.ActiveDirectoryEndpoint = aadEndpoint + } + baseClient.Authorizer, err = s.GetAuthorizer() + if err != nil { + return nil, err + } + return baseClient, nil + } + } + // Attempt to authorize with the following methods: // 1. Environment variables. // - Client credentials @@ -143,8 +196,6 @@ var createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient return nil, errors.Wrap(err, "error getting authorizer for key vault") } } - - baseClient := keyvault.New() baseClient.Authorizer = authorizer return &baseClient, nil } @@ -155,8 +206,24 @@ func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) { if err != nil { return nil, err } + + // step and step-ca do not need and URI, but having a default vault and + // protection level is useful if this package is used as an api + var defaults DefaultOptions + if opts.URI != "" { + u, err := uri.ParseWithScheme(Scheme, opts.URI) + if err != nil { + return nil, err + } + defaults.Vault = u.Get("vault") + if u.GetBool("hsm") { + defaults.ProtectionLevel = apiv1.HSM + } + } + return &KeyVault{ baseClient: baseClient, + defaults: defaults, }, nil } @@ -166,7 +233,7 @@ func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe return nil, errors.New("getPublicKeyRequest 'name' cannot be empty") } - vault, name, version, _, err := parseKeyName(req.Name) + vault, name, version, _, err := parseKeyName(req.Name, k.defaults) if err != nil { return nil, err } @@ -188,7 +255,7 @@ func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespo return nil, errors.New("createKeyRequest 'name' cannot be empty") } - vault, name, _, hsm, err := parseKeyName(req.Name) + vault, name, _, hsm, err := parseKeyName(req.Name, k.defaults) if err != nil { return nil, err } @@ -260,7 +327,7 @@ func (k *KeyVault) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, if req.SigningKey == "" { return nil, errors.New("createSignerRequest 'signingKey' cannot be empty") } - return NewSigner(k.baseClient, req.SigningKey) + return NewSigner(k.baseClient, req.SigningKey, k.defaults) } // Close closes the client connection to the Azure Key Vault. This is a noop. @@ -270,6 +337,6 @@ func (k *KeyVault) Close() error { // ValidateName validates that the given string is a valid URI. func (k *KeyVault) ValidateName(s string) error { - _, _, _, _, err := parseKeyName(s) + _, _, _, _, err := parseKeyName(s, k.defaults) return err } diff --git a/kms/azurekms/key_vault_test.go b/kms/azurekms/key_vault_test.go index 1f26e1ef..8f968189 100644 --- a/kms/azurekms/key_vault_test.go +++ b/kms/azurekms/key_vault_test.go @@ -89,11 +89,44 @@ func TestNew(t *testing.T) { }, args{context.Background(), apiv1.Options{}}, &KeyVault{ baseClient: client, }, false}, + {"ok with vault", func() { + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return client, nil + } + }, args{context.Background(), apiv1.Options{ + URI: "azurekms:vault=my-vault", + }}, &KeyVault{ + baseClient: client, + defaults: DefaultOptions{ + Vault: "my-vault", + ProtectionLevel: apiv1.UnspecifiedProtectionLevel, + }, + }, false}, + {"ok with vault + hsm", func() { + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return client, nil + } + }, args{context.Background(), apiv1.Options{ + URI: "azurekms:vault=my-vault;hsm=true", + }}, &KeyVault{ + baseClient: client, + defaults: DefaultOptions{ + Vault: "my-vault", + ProtectionLevel: apiv1.HSM, + }, + }, false}, {"fail", func() { createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { return nil, errTest } }, args{context.Background(), apiv1.Options{}}, nil, true}, + {"fail uri", func() { + createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) { + return client, nil + } + }, args{context.Background(), apiv1.Options{ + URI: "kms:vault=my-vault;hsm=true", + }}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -110,6 +143,45 @@ func TestNew(t *testing.T) { } } +func TestKeyVault_createClient(t *testing.T) { + type args struct { + ctx context.Context + opts apiv1.Options + } + tests := []struct { + name string + args args + skip bool + wantErr bool + }{ + {"ok", args{context.Background(), apiv1.Options{}}, true, false}, + {"ok with uri", args{context.Background(), apiv1.Options{ + URI: "azurekms:client-id=id;client-secret=secret;tenant-id=id", + }}, false, false}, + {"ok with uri+aad", args{context.Background(), apiv1.Options{ + URI: "azurekms:client-id=id;client-secret=secret;tenant-id=id;aad-enpoint=https%3A%2F%2Flogin.microsoftonline.us%2F", + }}, false, false}, + {"ok with uri no config", args{context.Background(), apiv1.Options{ + URI: "azurekms:", + }}, true, false}, + {"fail uri", args{context.Background(), apiv1.Options{ + URI: "kms:client-id=id;client-secret=secret;tenant-id=id", + }}, false, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.SkipNow() + } + _, err := createClient(tt.args.ctx, tt.args.opts) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + func TestKeyVault_GetPublicKey(t *testing.T) { key, err := keyutil.GenerateDefaultSigner() if err != nil { diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index 405c625a..e3aca5fe 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -24,8 +24,8 @@ type Signer struct { } // NewSigner creates a new signer using a key in the AWS KMS. -func NewSigner(client KeyVaultClient, signingKey string) (crypto.Signer, error) { - vault, name, version, _, err := parseKeyName(signingKey) +func NewSigner(client KeyVaultClient, signingKey string, defaults DefaultOptions) (crypto.Signer, error) { + vault, name, version, _, err := parseKeyName(signingKey, defaults) if err != nil { return nil, err } diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go index 01921e2a..381c3577 100644 --- a/kms/azurekms/signer_test.go +++ b/kms/azurekms/signer_test.go @@ -12,6 +12,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" "github.com/golang/mock/gomock" + "github.com/smallstep/certificates/kms/apiv1" "go.step.sm/crypto/keyutil" "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" @@ -32,11 +33,16 @@ func TestNewSigner(t *testing.T) { client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{ Key: jwk, }, nil) + client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{ + Key: jwk, + }, nil) client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest) + var noOptions DefaultOptions type args struct { client KeyVaultClient signingKey string + defaults DefaultOptions } tests := []struct { name string @@ -44,28 +50,35 @@ func TestNewSigner(t *testing.T) { want crypto.Signer wantErr bool }{ - {"ok", args{client, "azurekms:vault=my-vault;name=my-key"}, &Signer{ + {"ok", args{client, "azurekms:vault=my-vault;name=my-key", noOptions}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", name: "my-key", version: "", publicKey: pub, }, false}, - {"ok with version", args{client, "azurekms:name=my-key;vault=my-vault?version=my-version"}, &Signer{ + {"ok with version", args{client, "azurekms:name=my-key;vault=my-vault?version=my-version", noOptions}, &Signer{ + client: client, + vaultBaseURL: "https://my-vault.vault.azure.net/", + name: "my-key", + version: "my-version", + publicKey: pub, + }, false}, + {"ok with options", args{client, "azurekms:name=my-key?version=my-version", DefaultOptions{Vault: "my-vault", ProtectionLevel: apiv1.HSM}}, &Signer{ client: client, vaultBaseURL: "https://my-vault.vault.azure.net/", name: "my-key", version: "my-version", publicKey: pub, }, false}, - {"fail GetKey", args{client, "azurekms:name=not-found;vault=my-vault?version=my-version"}, nil, true}, - {"fail vault", args{client, "azurekms:name=not-found;vault="}, nil, true}, - {"fail id", args{client, "azurekms:name=;vault=my-vault?version=my-version"}, nil, true}, - {"fail scheme", args{client, "kms:name=not-found;vault=my-vault?version=my-version"}, nil, true}, + {"fail GetKey", args{client, "azurekms:name=not-found;vault=my-vault?version=my-version", noOptions}, nil, true}, + {"fail vault", args{client, "azurekms:name=not-found;vault=", noOptions}, nil, true}, + {"fail id", args{client, "azurekms:name=;vault=my-vault?version=my-version", noOptions}, nil, true}, + {"fail scheme", args{client, "kms:name=not-found;vault=my-vault?version=my-version", noOptions}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewSigner(tt.args.client, tt.args.signingKey) + got, err := NewSigner(tt.args.client, tt.args.signingKey, tt.args.defaults) if (err != nil) != tt.wantErr { t.Errorf("NewSigner() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/kms/azurekms/utils.go b/kms/azurekms/utils.go index 52bed868..d4201907 100644 --- a/kms/azurekms/utils.go +++ b/kms/azurekms/utils.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" "github.com/pkg/errors" + "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/uri" "go.step.sm/crypto/jose" ) @@ -50,10 +51,10 @@ func getKeyName(vault, name string, bundle keyvault.KeyBundle) string { // // HSM can also be passed to define the protection level if this is not given in // CreateQuery. -func parseKeyName(rawURI string) (vault, name, version string, hsm bool, err error) { +func parseKeyName(rawURI string, defaults DefaultOptions) (vault, name, version string, hsm bool, err error) { var u *uri.URI - u, err = uri.ParseWithScheme("azurekms", rawURI) + u, err = uri.ParseWithScheme(Scheme, rawURI) if err != nil { return } @@ -62,12 +63,21 @@ func parseKeyName(rawURI string) (vault, name, version string, hsm bool, err err return } if vault = u.Get("vault"); vault == "" { - err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI) - name = "" - return + if defaults.Vault == "" { + name = "" + err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI) + return + } + vault = defaults.Vault + } + if u.Get("hsm") == "" { + hsm = (defaults.ProtectionLevel == apiv1.HSM) + } else { + hsm = u.GetBool("hsm") } + version = u.Get("version") - hsm = u.GetBool("hsm") + return } diff --git a/kms/azurekms/utils_test.go b/kms/azurekms/utils_test.go index 000a9d6b..cded50ea 100644 --- a/kms/azurekms/utils_test.go +++ b/kms/azurekms/utils_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/smallstep/certificates/kms/apiv1" ) func Test_getKeyName(t *testing.T) { @@ -42,8 +43,10 @@ func Test_getKeyName(t *testing.T) { } func Test_parseKeyName(t *testing.T) { + var noOptions DefaultOptions type args struct { - rawURI string + rawURI string + defaults DefaultOptions } tests := []struct { name string @@ -54,22 +57,24 @@ func Test_parseKeyName(t *testing.T) { wantHsm bool wantErr bool }{ - {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version"}, "my-vault", "my-key", "my-version", false, false}, - {"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version"}, "my-vault", "my-key", "my-version", false, false}, - {"ok no version", args{"azurekms:name=my-key;vault=my-vault"}, "my-vault", "my-key", "", false, false}, - {"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true"}, "my-vault", "my-key", "", true, false}, - {"ok hsm false", args{"azurekms:name=my-key;vault=my-vault?hsm=false"}, "my-vault", "my-key", "", false, false}, - {"fail scheme", args{"azure:name=my-key;vault=my-vault"}, "", "", "", false, true}, - {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, "", "", "", false, true}, - {"fail no name", args{"azurekms:vault=my-vault"}, "", "", "", false, true}, - {"fail empty name", args{"azurekms:name=;vault=my-vault"}, "", "", "", false, true}, - {"fail no vault", args{"azurekms:name=my-key"}, "", "", "", false, true}, - {"fail empty vault", args{"azurekms:name=my-key;vault="}, "", "", "", false, true}, - {"fail empty", args{""}, "", "", "", false, true}, + {"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version", noOptions}, "my-vault", "my-key", "my-version", false, false}, + {"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version", noOptions}, "my-vault", "my-key", "my-version", false, false}, + {"ok no version", args{"azurekms:name=my-key;vault=my-vault", noOptions}, "my-vault", "my-key", "", false, false}, + {"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true", noOptions}, "my-vault", "my-key", "", true, false}, + {"ok hsm false", args{"azurekms:name=my-key;vault=my-vault?hsm=false", noOptions}, "my-vault", "my-key", "", false, false}, + {"ok default vault", args{"azurekms:name=my-key?version=my-version", DefaultOptions{Vault: "my-vault"}}, "my-vault", "my-key", "my-version", false, false}, + {"ok default hsm", args{"azurekms:name=my-key;vault=my-vault?version=my-version", DefaultOptions{Vault: "other-vault", ProtectionLevel: apiv1.HSM}}, "my-vault", "my-key", "my-version", true, false}, + {"fail scheme", args{"azure:name=my-key;vault=my-vault", noOptions}, "", "", "", false, true}, + {"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault", noOptions}, "", "", "", false, true}, + {"fail no name", args{"azurekms:vault=my-vault", noOptions}, "", "", "", false, true}, + {"fail empty name", args{"azurekms:name=;vault=my-vault", noOptions}, "", "", "", false, true}, + {"fail no vault", args{"azurekms:name=my-key", noOptions}, "", "", "", false, true}, + {"fail empty vault", args{"azurekms:name=my-key;vault=", noOptions}, "", "", "", false, true}, + {"fail empty", args{"", noOptions}, "", "", "", false, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotVault, gotName, gotVersion, gotHsm, err := parseKeyName(tt.args.rawURI) + gotVault, gotName, gotVersion, gotHsm, err := parseKeyName(tt.args.rawURI, tt.args.defaults) if (err != nil) != tt.wantErr { t.Errorf("parseKeyName() error = %v, wantErr %v", err, tt.wantErr) return From 36b622bfc2832652bab668255be1beae735954f8 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 15 Oct 2021 14:12:43 -0700 Subject: [PATCH 278/291] Use Golang's default keep-alive. Since Go 1.13 a net.Listen keep-alive is enabled by default if the protocol and OS supports it. The new one is 15s to match the net.Dial default one. Previously http.Server ListenAndServe and ListenAndServeTLS used to add a wrapper with 3m that we replicated. See https://github.com/golang/go/issues/31510 --- ca/tls.go | 4 ++-- examples/basic-client/client.go | 1 - server/server.go | 22 ++-------------------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/ca/tls.go b/ca/tls.go index 3a3b6766..0738d0e0 100644 --- a/ca/tls.go +++ b/ca/tls.go @@ -279,9 +279,9 @@ func getDefaultTLSConfig(sign *api.SignResponse) *tls.Config { // getDefaultDialer returns a new dialer with the default configuration. func getDefaultDialer() *net.Dialer { + // With the KeepAlive parameter set to 0, it will be use Golang's default. return &net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, + Timeout: 30 * time.Second, } } diff --git a/examples/basic-client/client.go b/examples/basic-client/client.go index db6092bf..42358ac8 100644 --- a/examples/basic-client/client.go +++ b/examples/basic-client/client.go @@ -116,7 +116,6 @@ func main() { Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, MaxIdleConns: 100, diff --git a/server/server.go b/server/server.go index d3968c4a..2b864148 100644 --- a/server/server.go +++ b/server/server.go @@ -72,10 +72,10 @@ func (srv *Server) Serve(ln net.Listener) error { // Start server if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) { log.Printf("Serving HTTP on %s ...", srv.Addr) - err = srv.Server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) + err = srv.Server.Serve(ln) } else { log.Printf("Serving HTTPS on %s ...", srv.Addr) - err = srv.Server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, "", "") + err = srv.Server.ServeTLS(ln, "", "") } // log unexpected errors @@ -155,21 +155,3 @@ func (srv *Server) Forbidden(w http.ResponseWriter) { w.WriteHeader(http.StatusForbidden) w.Write([]byte("Forbidden.\n")) } - -// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted -// connections. It's used by ListenAndServe and ListenAndServeTLS so -// dead TCP connections (e.g. closing laptop mid-download) eventually -// go away. -type tcpKeepAliveListener struct { - *net.TCPListener -} - -func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { - tc, err := ln.AcceptTCP() - if err != nil { - return - } - tc.SetKeepAlive(true) - tc.SetKeepAlivePeriod(3 * time.Minute) - return tc, nil -} From 482482e71729e41c22d4d9f731452eae71d5e4d8 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 19 Oct 2021 15:22:30 -0700 Subject: [PATCH 279/291] install-step-ra.sh: Don't try to create a JWK provisioner, because the web app's OAuth flow doesn't support OOB with STEP_CONSOLE=true. --- scripts/install-step-ra.sh | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/scripts/install-step-ra.sh b/scripts/install-step-ra.sh index 1920b17d..227af5db 100644 --- a/scripts/install-step-ra.sh +++ b/scripts/install-step-ra.sh @@ -126,25 +126,17 @@ fi echo "Bootstrapping with the CA..." export STEPPATH=$(mktemp -d) -export STEP_CONSOLE=true step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT if [ -z "$CA_PROVISIONER_NAME" ]; then declare -a provisioners readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name') - provisioners+=("Create provisioner") printf '%s\n' "${provisioners[@]}" printf "%b" "\nSelect a JWK provisioner:\n" >&2 select provisioner in "${provisioners[@]}"; do - if [ "$provisioner" == "Create provisioner" ]; then - echo "Creating a JWK provisioner on the upstream CA..." - echo "" - read -p "Label your provisioner (e.g. example-ra): " CA_PROVISIONER_NAME < /dev/tty - step beta ca provisioner add $CA_PROVISIONER_NAME --type JWK --create - break - elif [ -n "$provisioner" ]; then + if [ -n "$provisioner" ]; then echo "Using existing provisioner $provisioner." CA_PROVISIONER_NAME=$provisioner break From 655d7f59fde2bebcae6a2b716aeabf18e32f00ec Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 19 Oct 2021 16:14:41 -0700 Subject: [PATCH 280/291] install-step-ra.sh: Properly quote the RA DNS names in ca.json --- scripts/install-step-ra.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/install-step-ra.sh b/scripts/install-step-ra.sh index 227af5db..1da64ed6 100644 --- a/scripts/install-step-ra.sh +++ b/scripts/install-step-ra.sh @@ -154,6 +154,27 @@ if [ -z "$RA_DNS_NAMES" ]; then done fi + +count=0 +ra_dns_names_quoted="" + +for i in ${RA_DNS_NAMES//,/ } +do + if [ "$count" = "0" ]; then + ra_dns_names_quoted="\"$i\"" + else + ra_dns_names_quoted="${ra_dns_names_quoted}, \"$i\"" + fi + count=$((count+1)) +done + +if [ "$count" = "0" ]; then + echo "You must supply at least one RA DNS name" + exit 1 +fi + +echo "Got here" + if [ -z "$RA_ADDRESS" ]; then RA_ADDRESS="" while [[ $RA_ADDRESS = "" ]] ; do @@ -189,7 +210,7 @@ mkdir -p $(step path)/config cat < $(step path)/config/ca.json { "address": "$RA_ADDRESS", - "dnsNames": ["$RA_DNS_NAMES"], + "dnsNames": [$ra_dns_names_quoted], "db": { "type": "badgerV2", "dataSource": "/etc/step-ca/db" From 6f1693877495a071641478736ba2487b4211e9b0 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Tue, 19 Oct 2021 16:53:41 -0700 Subject: [PATCH 281/291] Update README.md * Add SCEP support * Fix ACME tutorial URLs --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 64458929..bfc056b4 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,8 @@ You can issue certificates in exchange for: - [Cloud instance identity documents](https://smallstep.com/blog/embarrassingly-easy-certificates-on-aws-azure-gcp/), for VMs on AWS, GCP, and Azure - [Single-use, short-lived JWK tokens](https://smallstep.com/docs/step-ca/provisioners#jwk) issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc. - A trusted X.509 certificate (X5C provisioner) -- Expiring SSH host certificates needing rotation (the SSHPOP provisioner) +- A SCEP challenge (SCEP provisioner) +- An SSH host certificates needing renewal (the SSHPOP provisioner) - Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/provisioners) ### 🏔 Your own private ACME server @@ -80,16 +81,17 @@ ACME is the protocol used by Let's Encrypt to automate the issuance of HTTPS cer - For `tls-alpn-01`, respond to the challenge at the TLS layer ([as Caddy does](https://caddy.community/t/caddy-supports-the-acme-tls-alpn-challenge/4860)) to prove that you control the web server - Works with any ACME client. We've written examples for: - - [certbot](https://smallstep.com/blog/private-acme-server/#certbotuploadsacme-certbotpng-certbot-example) - - [acme.sh](https://smallstep.com/blog/private-acme-server/#acmeshuploadsacme-acme-shpng-acmesh-example) - - [Caddy](https://smallstep.com/blog/private-acme-server/#caddyuploadsacme-caddypng-caddy-example) - - [Traefik](https://smallstep.com/blog/private-acme-server/#traefikuploadsacme-traefikpng-traefik-example) - - [Apache](https://smallstep.com/blog/private-acme-server/#apacheuploadsacme-apachepng-apache-example) - - [nginx](https://smallstep.com/blog/private-acme-server/#nginxuploadsacme-nginxpng-nginx-example) + - [certbot](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#certbot) + - [acme.sh](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#acmesh) + - [win-acme](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#win-acme) + - [Caddy](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#caddy-v2) + - [Traefik](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#traefik) + - [Apache](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#apache) + - [nginx](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#nginx) - Get certificates programmatically using ACME, using these libraries: - - [`lego`](https://github.com/go-acme/lego) for Golang ([example usage](https://smallstep.com/blog/private-acme-server/#golanguploadsacme-golangpng-go-example)) - - certbot's [`acme` module](https://github.com/certbot/certbot/tree/master/acme) for Python ([example usage](https://smallstep.com/blog/private-acme-server/#pythonuploadsacme-pythonpng-python-example)) - - [`acme-client`](https://github.com/publishlab/node-acme-client) for Node.js ([example usage](https://smallstep.com/blog/private-acme-server/#nodejsuploadsacme-node-jspng-nodejs-example)) + - [`lego`](https://github.com/go-acme/lego) for Golang ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#golang)) + - certbot's [`acme` module](https://github.com/certbot/certbot/tree/master/acme) for Python ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#python)) + - [`acme-client`](https://github.com/publishlab/node-acme-client) for Node.js ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#node)) - Our own [`step` CLI tool](https://github.com/smallstep/cli) is also an ACME client! - See our [ACME tutorial](https://smallstep.com/docs/tutorials/acme-challenge) for more From 12b32b0a905a8cd58ff58ccc36c0654a8ebe65ae Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 20 Oct 2021 13:41:26 -0700 Subject: [PATCH 282/291] Changelog update for v0.17.5 --- CHANGELOG.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ddbc15..4f031a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,18 +4,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased - 0.17.5] - DATE +## [Unreleased - 0.17.6] - DATE ### Added -- Support for Azure Key Vault as a KMS. -- Adapt `pki` package to support key managers. -- gocritic linter ### Changed ### Deprecated ### Removed ### Fixed -- gocritic warnings ### Security +## [0.17.5] - 2021-10-20 +### Added +- Support for Azure Key Vault as a KMS. +- Adapt `pki` package to support key managers. +- gocritic linter +### Fixed +- gocritic warnings + ## [0.17.4] - 2021-09-28 ### Fixed - Support host-only or user-only SSH CA. From 5c71e8a0ee61f1a7c15422a4a167297154d70671 Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 20 Oct 2021 14:10:40 -0700 Subject: [PATCH 283/291] [action] replace goreleaser complicated build targets with 'targets' --- .goreleaser.yml | 58 +++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 60eff7e7..207c75bd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -32,25 +32,16 @@ builds: id: step-cloudkms-init env: - CGO_ENABLED=0 - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm - - arm64 - - 386 - goarm: - - 6 - - 7 - ignore: - - goos: windows - goarch: 386 - - goos: windows - goarm: 6 - - goos: windows - goarm: 7 + targets: + - darwin_amd64 + - darwin_arm64 + - freebsd_amd64 + - linux_386 + - linux_amd64 + - linux_arm64 + - linux_arm_6 + - linux_arm_7 + - windows_amd64 flags: - -trimpath main: ./cmd/step-cloudkms-init/main.go @@ -61,25 +52,16 @@ builds: id: step-awskms-init env: - CGO_ENABLED=0 - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm - - arm64 - - 386 - goarm: - - 6 - - 7 - ignore: - - goos: windows - goarch: 386 - - goos: windows - goarm: 6 - - goos: windows - goarm: 7 + targets: + - darwin_amd64 + - darwin_arm64 + - freebsd_amd64 + - linux_386 + - linux_amd64 + - linux_arm64 + - linux_arm_6 + - linux_arm_7 + - windows_amd64 flags: - -trimpath main: ./cmd/step-awskms-init/main.go From da3c6af10fb85a8b813a5617f937806ba45bee2b Mon Sep 17 00:00:00 2001 From: max furman Date: Wed, 20 Oct 2021 14:31:33 -0700 Subject: [PATCH 284/291] changelog update --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f031a5a..ca792f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased - 0.17.6] - DATE +## [Unreleased - 0.17.7] - DATE ### Added ### Changed ### Deprecated @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed ### Security +## [0.17.6] - 2021-10-20 +### Notes +- 0.17.5 failed in CI/CD + ## [0.17.5] - 2021-10-20 ### Added - Support for Azure Key Vault as a KMS. From 66a8158f26d86c36d23fbd2d73b737b688bbbb9d Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Wed, 20 Oct 2021 15:53:04 -0700 Subject: [PATCH 285/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bfc056b4..65116b38 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ To get up and running quickly, or as an alternative to running your own `step-ca --- -**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/step-discord).** +**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://u.step.sm/discord).** [Website](https://smallstep.com/certificates) | [Documentation](https://smallstep.com/docs) | From ead394fba7de7d1aaaad18402aad89780ce3b59a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 20 Oct 2021 18:09:50 -0700 Subject: [PATCH 286/291] Add strategy to retry the sign operation if the key is not yet ready --- kms/azurekms/signer.go | 36 +++++++-- kms/azurekms/signer_test.go | 141 ++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 7 deletions(-) diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index e3aca5fe..2fb5951a 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -7,8 +7,10 @@ import ( "encoding/base64" "io" "math/big" + "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/Azure/go-autorest/autorest/azure" "github.com/pkg/errors" "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" @@ -69,15 +71,10 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] return nil, err } - ctx, cancel := defaultContext() - defer cancel() - b64 := base64.RawURLEncoding.EncodeToString(digest) - resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{ - Algorithm: alg, - Value: &b64, - }) + // Sign with retry if the key is not ready + resp, err := s.signWithRetry(alg, b64, 3) if err != nil { return nil, errors.Wrap(err, "keyVault Sign failed") } @@ -111,6 +108,31 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] return b.Bytes() } +func (s *Signer) signWithRetry(alg keyvault.JSONWebKeySignatureAlgorithm, b64 string, retryAttemps int) (keyvault.KeyOperationResult, error) { +retry: + ctx, cancel := defaultContext() + defer cancel() + + resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{ + Algorithm: alg, + Value: &b64, + }) + if err != nil && retryAttemps > 0 { + var requestError *azure.RequestError + if errors.As(err, &requestError) { + if se := requestError.ServiceError; se != nil && se.InnerError != nil { + code, ok := se.InnerError["code"].(string) + if ok && code == "KeyNotYetValid" { + time.Sleep(time.Second / time.Duration(retryAttemps)) + retryAttemps-- + goto retry + } + } + } + } + return resp, err +} + func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) { switch key.(type) { case *rsa.PublicKey: diff --git a/kms/azurekms/signer_test.go b/kms/azurekms/signer_test.go index 381c3577..bd072b25 100644 --- a/kms/azurekms/signer_test.go +++ b/kms/azurekms/signer_test.go @@ -11,6 +11,8 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" "github.com/golang/mock/gomock" "github.com/smallstep/certificates/kms/apiv1" "go.step.sm/crypto/keyutil" @@ -350,3 +352,142 @@ func TestSigner_Sign(t *testing.T) { }) } } + +func TestSigner_Sign_signWithRetry(t *testing.T) { + sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) { + key, err := keyutil.GenerateSigner(kty, crv, bits) + if err != nil { + t.Fatal(err) + } + h := opts.HashFunc().New() + h.Write([]byte("random-data")) + sum := h.Sum(nil) + + var sig, resultSig []byte + if priv, ok := key.(*ecdsa.PrivateKey); ok { + r, s, err := ecdsa.Sign(rand.Reader, priv, sum) + if err != nil { + t.Fatal(err) + } + curveBits := priv.Params().BitSize + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes++ + } + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + // nolint:gocritic + resultSig = append(rBytesPadded, sBytesPadded...) + + var b cryptobyte.Builder + b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1BigInt(r) + b.AddASN1BigInt(s) + }) + sig, err = b.Bytes() + if err != nil { + t.Fatal(err) + } + } else { + sig, err = key.Sign(rand.Reader, sum, opts) + if err != nil { + t.Fatal(err) + } + resultSig = sig + } + + return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig + } + + p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256) + okResult := keyvault.KeyOperationResult{ + Result: &p256ResultSig, + } + failResult := keyvault.KeyOperationResult{} + retryError := autorest.DetailedError{ + Original: &azure.RequestError{ + ServiceError: &azure.ServiceError{ + InnerError: map[string]interface{}{ + "code": "KeyNotYetValid", + }, + }, + }, + } + + client := mockClient(t) + expects := []struct { + name string + keyVersion string + alg keyvault.JSONWebKeySignatureAlgorithm + digest []byte + result keyvault.KeyOperationResult + err error + }{ + {"ok 1", "", keyvault.ES256, p256Digest, failResult, retryError}, + {"ok 2", "", keyvault.ES256, p256Digest, failResult, retryError}, + {"ok 3", "", keyvault.ES256, p256Digest, failResult, retryError}, + {"ok 4", "", keyvault.ES256, p256Digest, okResult, nil}, + {"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError}, + {"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError}, + {"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError}, + {"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError}, + } + for _, e := range expects { + value := base64.RawURLEncoding.EncodeToString(e.digest) + client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{ + Algorithm: e.alg, + Value: &value, + }).Return(e.result, e.err) + } + + type fields struct { + client KeyVaultClient + vaultBaseURL string + name string + version string + publicKey crypto.PublicKey + } + type args struct { + rand io.Reader + digest []byte + opts crypto.SignerOpts + } + tests := []struct { + name string + fields fields + args args + want []byte + wantErr bool + }{ + {"ok", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{ + rand.Reader, p256Digest, crypto.SHA256, + }, p256Sig, false}, + {"fail", fields{client, "https://my-vault.vault.azure.net/", "my-key", "fail-version", p256}, args{ + rand.Reader, p256Digest, crypto.SHA256, + }, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Signer{ + client: tt.fields.client, + vaultBaseURL: tt.fields.vaultBaseURL, + name: tt.fields.name, + version: tt.fields.version, + publicKey: tt.fields.publicKey, + } + got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts) + if (err != nil) != tt.wantErr { + t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Signer.Sign() = %v, want %v", got, tt.want) + } + }) + } +} From bef50bd7d981908ac62944e4ce9824d1f90d059e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 26 Oct 2021 17:57:59 -0700 Subject: [PATCH 287/291] Fix typo in variable name. --- kms/azurekms/signer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kms/azurekms/signer.go b/kms/azurekms/signer.go index 2fb5951a..b0349108 100644 --- a/kms/azurekms/signer.go +++ b/kms/azurekms/signer.go @@ -108,7 +108,7 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([] return b.Bytes() } -func (s *Signer) signWithRetry(alg keyvault.JSONWebKeySignatureAlgorithm, b64 string, retryAttemps int) (keyvault.KeyOperationResult, error) { +func (s *Signer) signWithRetry(alg keyvault.JSONWebKeySignatureAlgorithm, b64 string, retryAttempts int) (keyvault.KeyOperationResult, error) { retry: ctx, cancel := defaultContext() defer cancel() @@ -117,14 +117,14 @@ retry: Algorithm: alg, Value: &b64, }) - if err != nil && retryAttemps > 0 { + if err != nil && retryAttempts > 0 { var requestError *azure.RequestError if errors.As(err, &requestError) { if se := requestError.ServiceError; se != nil && se.InnerError != nil { code, ok := se.InnerError["code"].(string) if ok && code == "KeyNotYetValid" { - time.Sleep(time.Second / time.Duration(retryAttemps)) - retryAttemps-- + time.Sleep(time.Second / time.Duration(retryAttempts)) + retryAttempts-- goto retry } } From 0927e0d22a90d2e52c912f84d63d2ef6777ada36 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 27 Oct 2021 11:48:29 -0700 Subject: [PATCH 288/291] Upgrade go.step.sm/crypto dependency The new version removes "env" and "expandenv" sprig functions. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 791e0927..04ff3bca 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.6.0 - go.step.sm/crypto v0.11.0 + go.step.sm/crypto v0.13.0 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 diff --git a/go.sum b/go.sum index c0855569..ece817f0 100644 --- a/go.sum +++ b/go.sum @@ -569,8 +569,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.step.sm/cli-utils v0.6.0 h1:sH4FxBcjmbxyilKXheSyJuKF/QjpojpiW90ERwUWOgQ= go.step.sm/cli-utils v0.6.0/go.mod h1:jklBMavFl2PbmGlyxgax08ZnB0uWpadjuOlSKKXz+0U= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.11.0 h1:VDpeVgEmqme/FK2w5QINxkOQ1FWOm/Wi2TwQXiacKr8= -go.step.sm/crypto v0.11.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= +go.step.sm/crypto v0.13.0 h1:mQuP9Uu2FNmqCJNO0OTbvolnYXzONy4wdUBtUVcP1s8= +go.step.sm/crypto v0.13.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg= 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 0f63d43a91eaba3292d942760ce6b87998d2b3c1 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 27 Oct 2021 11:50:55 -0700 Subject: [PATCH 289/291] Remove sprig "env" and "expandenv" functions. --- pki/helm.go | 8 +++++++- templates/templates.go | 11 ++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pki/helm.go b/pki/helm.go index 817c1bf4..852e5aa4 100644 --- a/pki/helm.go +++ b/pki/helm.go @@ -21,8 +21,14 @@ type helmVariables struct { Provisioners []provisioner.Interface } +// WriteHelmTemplate a helm template to configure the +// smallstep/step-certificates helm chart. func (p *PKI) WriteHelmTemplate(w io.Writer) error { - tmpl, err := template.New("helm").Funcs(sprig.TxtFuncMap()).Parse(helmTemplate) + funcs := sprig.TxtFuncMap() + delete(funcs, "env") + delete(funcs, "expandenv") + + tmpl, err := template.New("helm").Funcs(funcs).Parse(helmTemplate) if err != nil { return errors.Wrap(err, "error writing helm template") } diff --git a/templates/templates.go b/templates/templates.go index 16e891d9..8f10d8a4 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -183,7 +183,7 @@ func (t *Template) Load() error { // the template fails. func (t *Template) LoadBytes(b []byte) error { t.backfill(b) - tmpl, err := template.New(t.Name).Funcs(sprig.TxtFuncMap()).Parse(string(b)) + tmpl, err := template.New(t.Name).Funcs(getFuncMap()).Parse(string(b)) if err != nil { return errors.Wrapf(err, "error parsing template %s", t.Name) } @@ -270,3 +270,12 @@ func mkdir(path string, perm os.FileMode) error { } return nil } + +// getFuncMap returns sprig.TxtFuncMap but removing the "env" and "expandenv" +// functions to avoid any leak of information. +func getFuncMap() template.FuncMap { + m := sprig.TxtFuncMap() + delete(m, "env") + delete(m, "expandenv") + return m +} From 9958e0645f7edacadd018a2c2cc7a983794ca2ab Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 27 Oct 2021 12:38:16 -0700 Subject: [PATCH 290/291] Replace promptui with apache-compatible fork. Promptui depends on github.com/juju/ansiterm that is licensed under LGPL. The fork replaces ansiterm.TabWriter with the one in the standard library. --- go.mod | 6 +++++- go.sum | 12 ++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 04ff3bca..48299d6a 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/smallstep/nosql v0.3.8 github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 - go.step.sm/cli-utils v0.6.0 + go.step.sm/cli-utils v0.6.1 go.step.sm/crypto v0.13.0 go.step.sm/linkedca v0.7.0 golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 @@ -44,6 +44,10 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 ) +// avoid license conflict from juju/ansiterm until https://github.com/manifoldco/promptui/pull/181 +// is merged or other dependency in path currently in violation fixes compliance +replace github.com/manifoldco/promptui => github.com/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797 + // 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 ece817f0..252832ea 100644 --- a/go.sum +++ b/go.sum @@ -357,8 +357,6 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u 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/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -377,12 +375,8 @@ 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/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 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.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= -github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= @@ -432,6 +426,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797 h1:unCiBzwNjcuVbP3bgM76z0ORyIuI4sspop1qhkQJ044= +github.com/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797/go.mod h1:CBMXL3a2sC3Q8TjpLcQt8w/3aQ23VSy6r7UFeCG6phA= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -566,8 +562,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.step.sm/cli-utils v0.6.0 h1:sH4FxBcjmbxyilKXheSyJuKF/QjpojpiW90ERwUWOgQ= -go.step.sm/cli-utils v0.6.0/go.mod h1:jklBMavFl2PbmGlyxgax08ZnB0uWpadjuOlSKKXz+0U= +go.step.sm/cli-utils v0.6.1 h1:v31ctEh/BFPGU067fF9Y8u2EIg6LRldUbN2dc/+u/V8= +go.step.sm/cli-utils v0.6.1/go.mod h1:stgyXHHHi9KwcR86sgzDdFC6e/tAmpF4NbqwSK7q/GM= 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= From cb4a2a5f9a0d51a950a0c67a34a3b7d34f646fa9 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 27 Oct 2021 16:11:47 -0700 Subject: [PATCH 291/291] Use the same method to return the templating functions. --- pki/helm.go | 8 ++------ templates/templates.go | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pki/helm.go b/pki/helm.go index 852e5aa4..0a2f7f02 100644 --- a/pki/helm.go +++ b/pki/helm.go @@ -4,11 +4,11 @@ import ( "io" "text/template" - "github.com/Masterminds/sprig/v3" "github.com/pkg/errors" "github.com/smallstep/certificates/authority" authconfig "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/templates" "go.step.sm/linkedca" ) @@ -24,11 +24,7 @@ type helmVariables struct { // WriteHelmTemplate a helm template to configure the // smallstep/step-certificates helm chart. func (p *PKI) WriteHelmTemplate(w io.Writer) error { - funcs := sprig.TxtFuncMap() - delete(funcs, "env") - delete(funcs, "expandenv") - - tmpl, err := template.New("helm").Funcs(funcs).Parse(helmTemplate) + tmpl, err := template.New("helm").Funcs(templates.StepFuncMap()).Parse(helmTemplate) if err != nil { return errors.Wrap(err, "error writing helm template") } diff --git a/templates/templates.go b/templates/templates.go index 8f10d8a4..09416b68 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -183,7 +183,7 @@ func (t *Template) Load() error { // the template fails. func (t *Template) LoadBytes(b []byte) error { t.backfill(b) - tmpl, err := template.New(t.Name).Funcs(getFuncMap()).Parse(string(b)) + tmpl, err := template.New(t.Name).Funcs(StepFuncMap()).Parse(string(b)) if err != nil { return errors.Wrapf(err, "error parsing template %s", t.Name) } @@ -271,9 +271,9 @@ func mkdir(path string, perm os.FileMode) error { return nil } -// getFuncMap returns sprig.TxtFuncMap but removing the "env" and "expandenv" +// StepFuncMap returns sprig.TxtFuncMap but removing the "env" and "expandenv" // functions to avoid any leak of information. -func getFuncMap() template.FuncMap { +func StepFuncMap() template.FuncMap { m := sprig.TxtFuncMap() delete(m, "env") delete(m, "expandenv")