Admin level API for provisioner mgmt v1

pull/599/head
max furman 3 years ago
parent 7e82bd6ef3
commit 9fdef64709

@ -8,7 +8,7 @@ linters-settings:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (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).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
golint: revive:
min-confidence: 0 min-confidence: 0
gocyclo: gocyclo:
min-complexity: 10 min-complexity: 10
@ -44,7 +44,7 @@ linters:
disable-all: true disable-all: true
enable: enable:
- gofmt - gofmt
- golint - revive
- govet - govet
- misspell - misspell
- ineffassign - ineffassign

@ -288,13 +288,13 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
name := chi.URLParam(r, "provisionerID") nameEscaped := chi.URLParam(r, "provisionerID")
provID, err := url.PathUnescape(name) name, err := url.PathUnescape(nameEscaped)
if err != nil { 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 return
} }
p, err := h.ca.LoadProvisionerByID("acme/" + provID) p, err := h.ca.LoadProvisionerByName(name)
if err != nil { if err != nil {
api.WriteError(w, err) api.WriteError(w, err)
return return

@ -11,7 +11,7 @@ import (
// CertificateAuthority is the interface implemented by a CA authority. // CertificateAuthority is the interface implemented by a CA authority.
type CertificateAuthority interface { type CertificateAuthority interface {
Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) 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. // Clock that returns time in UTC rounded to seconds.

@ -23,12 +23,11 @@ var (
// DB is a struct that implements the AcmeDB interface. // DB is a struct that implements the AcmeDB interface.
type DB struct { 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. // 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, tables := [][]byte{accountTable, accountByKeyIDTable, authzTable,
challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable} challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable}
for _, b := range tables { for _, b := range tables {
@ -37,7 +36,7 @@ func New(db nosqlDB.DB, authorityID string) (*DB, error) {
string(b)) 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 // save writes the new data to the database, overwriting the old data if it

@ -261,10 +261,10 @@ func TestOrder_UpdateStatus(t *testing.T) {
} }
type mockSignAuth struct { type mockSignAuth struct {
sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
loadProvisionerByID func(string) (provisioner.Interface, error) loadProvisionerByName func(string) (provisioner.Interface, error)
ret1, ret2 interface{} ret1, ret2 interface{}
err error err error
} }
func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, 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 return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err
} }
func (m *mockSignAuth) LoadProvisionerByID(id string) (provisioner.Interface, error) { func (m *mockSignAuth) LoadProvisionerByName(name string) (provisioner.Interface, error) {
if m.loadProvisionerByID != nil { if m.loadProvisionerByName != nil {
return m.loadProvisionerByID(id) return m.loadProvisionerByName(name)
} }
return m.ret1.(provisioner.Interface), m.err return m.ret1.(provisioner.Interface), m.err
} }

@ -39,7 +39,7 @@ type Authority interface {
Renew(peer *x509.Certificate) ([]*x509.Certificate, error) Renew(peer *x509.Certificate) ([]*x509.Certificate, error)
Rekey(peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) Rekey(peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error)
LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, 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) GetProvisioners(cursor string, limit int) (provisioner.List, string, error)
Revoke(context.Context, *authority.RevokeOptions) error Revoke(context.Context, *authority.RevokeOptions) error
GetEncryptedKey(kid string) (string, error) GetEncryptedKey(kid string) (string, error)
@ -418,7 +418,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) {
if len(val.CredentialID) > 0 { if len(val.CredentialID) > 0 {
m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID)
} else { } else {
m["provisioner"] = fmt.Sprintf("%s", val.Name) m["provisioner"] = string(val.Name)
} }
break break
} }

@ -430,6 +430,7 @@ type mockProvisioner struct {
ret1, ret2, ret3 interface{} ret1, ret2, ret3 interface{}
err error err error
getID func() string getID func() string
getIDForToken func() string
getTokenID func(string) (string, error) getTokenID func(string) (string, error)
getName func() string getName func() string
getType func() provisioner.Type getType func() provisioner.Type
@ -452,6 +453,13 @@ func (m *mockProvisioner) GetID() string {
return m.ret1.(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) { func (m *mockProvisioner) GetTokenID(token string) (string, error) {
if m.getTokenID != nil { if m.getTokenID != nil {
return m.getTokenID(token) return m.getTokenID(token)
@ -553,7 +561,7 @@ type mockAuthority struct {
renew func(cert *x509.Certificate) ([]*x509.Certificate, error) renew func(cert *x509.Certificate) ([]*x509.Certificate, error)
rekey func(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) rekey func(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error)
loadProvisionerByCertificate func(cert *x509.Certificate) (provisioner.Interface, 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) getProvisioners func(nextCursor string, limit int) (provisioner.List, string, error)
revoke func(context.Context, *authority.RevokeOptions) error revoke func(context.Context, *authority.RevokeOptions) error
getEncryptedKey func(kid string) (string, 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 return m.ret1.(provisioner.Interface), m.err
} }
func (m *mockAuthority) LoadProvisionerByID(provID string) (provisioner.Interface, error) { func (m *mockAuthority) LoadProvisionerByName(name string) (provisioner.Interface, error) {
if m.loadProvisionerByID != nil { if m.loadProvisionerByName != nil {
return m.loadProvisionerByID(provID) return m.loadProvisionerByName(name)
} }
return m.ret1.(provisioner.Interface), m.err return m.ret1.(provisioner.Interface), m.err
} }

@ -8,7 +8,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/acme" "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/errs"
"github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/logging"
"github.com/smallstep/certificates/scep" "github.com/smallstep/certificates/scep"
@ -20,8 +20,8 @@ func WriteError(w http.ResponseWriter, err error) {
case *acme.Error: case *acme.Error:
acme.WriteError(w, k) acme.WriteError(w, k)
return return
case *mgmt.Error: case *admin.Error:
mgmt.WriteError(w, k) admin.WriteError(w, k)
return return
case *scep.Error: case *scep.Error:
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")

@ -3,11 +3,14 @@ package api
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/logging" "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. // 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) 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 // ReadJSON reads JSON from the request body and stores it in the value
// pointed by v. // pointed by v.
func ReadJSON(r io.Reader, v interface{}) error { func ReadJSON(r io.Reader, v interface{}) error {
@ -72,3 +98,13 @@ func ReadJSON(r io.Reader, v interface{}) error {
} }
return nil 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)
}

@ -1,14 +1,12 @@
package api package api
import ( import (
"fmt"
"net/http" "net/http"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/smallstep/certificates/api" "github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/admin"
"github.com/smallstep/certificates/authority/mgmt" "go.step.sm/linkedca"
"github.com/smallstep/certificates/linkedca"
) )
// CreateAdminRequest represents the body for a CreateAdmin request. // CreateAdminRequest represents the body for a CreateAdmin request.
@ -19,11 +17,7 @@ type CreateAdminRequest struct {
} }
// Validate validates a new-admin request body. // Validate validates a new-admin request body.
func (car *CreateAdminRequest) Validate(c *admin.Collection) error { func (car *CreateAdminRequest) Validate() 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)
}
return nil return nil
} }
@ -52,25 +46,29 @@ type DeleteResponse struct {
func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
adm, ok := h.auth.GetAdminCollection().LoadByID(id) adm, ok := h.auth.LoadAdminByID(id)
if !ok { if !ok {
api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, api.WriteError(w, admin.NewError(admin.ErrorNotFoundType,
"admin %s not found", id)) "admin %s not found", id))
return return
} }
api.JSON(w, adm) 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) { func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
cursor, limit, err := api.ParseCursor(r) cursor, limit, err := api.ParseCursor(r)
if err != nil { 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")) "error parsing cursor and limit from query params"))
return 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{ api.JSON(w, &GetAdminsResponse{
Admins: admins, Admins: admins,
NextCursor: nextCursor, NextCursor: nextCursor,
@ -79,91 +77,63 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
// CreateAdmin creates a new admin. // CreateAdmin creates a new admin.
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var body CreateAdminRequest var body CreateAdminRequest
if err := api.ReadJSON(r.Body, &body); err != nil { 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 return
} }
if err := body.Validate(h.auth.GetAdminCollection()); err != nil { if err := body.Validate(); err != nil {
api.WriteError(w, err) api.WriteError(w, err)
return return
} }
p, ok := h.auth.GetProvisionerCollection().LoadByName(body.Provisioner) p, err := h.auth.LoadProvisionerByName(body.Provisioner)
if !ok { if err != nil {
api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", body.Provisioner)) api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner))
return return
} }
adm := &linkedca.Admin{ adm := &linkedca.Admin{
ProvisionerId: p.GetID(), ProvisionerId: p.GetID(),
Subject: body.Subject, Subject: body.Subject,
Type: body.Type, Type: body.Type,
} }
if err := h.db.CreateAdmin(ctx, adm); err != nil { // Store to authority collection.
api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) if err := h.auth.StoreAdmin(r.Context(), adm, p); err != nil {
api.WriteError(w, admin.WrapErrorISE(err, "error storing admin"))
return return
} }
api.JSON(w, adm) api.JSON(w, adm)
if err := h.auth.ReloadAuthConfig(ctx); err != nil {
fmt.Printf("err = %+v\n", err)
}
} }
// DeleteAdmin deletes admin. // DeleteAdmin deletes admin.
func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
if h.auth.GetAdminCollection().SuperCount() == 1 { if err := h.auth.RemoveAdmin(r.Context(), id); err != nil {
api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove the last super admin")) api.WriteError(w, admin.WrapErrorISE(err, "error deleting admin %s", id))
return 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"}) 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. // UpdateAdmin updates an existing admin.
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var body UpdateAdminRequest var body UpdateAdminRequest
if err := api.ReadJSON(r.Body, &body); err != nil { 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 return
} }
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
adm, ok := h.auth.GetAdminCollection().LoadByID(id) adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type})
if !ok { if err != nil {
api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id)) api.WriteError(w, admin.WrapErrorISE(err, "error updating admin %s", id))
return
}
if adm.Type == body.Type {
api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type))
return 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) api.JSON(w, adm)
if err := h.auth.ReloadAuthConfig(ctx); err != nil {
fmt.Printf("err = %+v\n", err)
}
} }

@ -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))
}

@ -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
}
*/

@ -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)
}

@ -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
}

@ -1,16 +1,59 @@
package mgmt package admin
import ( import (
"context" "context"
"encoding/json"
"fmt"
"github.com/pkg/errors" "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 // ErrNotFound is an error that should be used by the authority.DB interface to
// indicate that an entity does not exist. // indicate that an entity does not exist.
var ErrNotFound = errors.New("not found") 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. // DB is the DB interface expected by the step-ca ACME API.
type DB interface { type DB interface {
CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error

@ -6,9 +6,10 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/admin"
"github.com/smallstep/certificates/linkedca"
"github.com/smallstep/nosql" "github.com/smallstep/nosql"
"go.step.sm/linkedca"
"google.golang.org/protobuf/types/known/timestamppb"
) )
// dbAdmin is the database representation of the Admin type. // dbAdmin is the database representation of the Admin type.
@ -22,60 +23,66 @@ type dbAdmin struct {
DeletedAt time.Time `json:"deletedAt"` DeletedAt time.Time `json:"deletedAt"`
} }
func (dbp *dbAdmin) clone() *dbAdmin { func (dba *dbAdmin) convert() *linkedca.Admin {
u := *dbp 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 return &u
} }
func (db *DB) getDBAdminBytes(ctx context.Context, id string) ([]byte, error) { 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) { 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 { } else if err != nil {
return nil, errors.Wrapf(err, "error loading admin %s", id) return nil, errors.Wrapf(err, "error loading admin %s", id)
} }
return data, nil return data, nil
} }
func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { func (db *DB) unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) {
data, err := db.getDBAdminBytes(ctx, id) var dba = new(dbAdmin)
if err != nil { if err := json.Unmarshal(data, dba); err != nil {
return nil, err return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id)
} }
dba, err := unmarshalDBAdmin(data, id) if !dba.DeletedAt.IsZero() {
if err != nil { return nil, admin.NewError(admin.ErrorDeletedType, "admin %s is deleted", id)
return nil, err
} }
if dba.AuthorityID != db.authorityID { 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) "admin %s is not owned by authority %s", dba.ID, db.authorityID)
} }
return dba, nil return dba, nil
} }
func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) {
var dba = new(dbAdmin) data, err := db.getDBAdminBytes(ctx, id)
if err := json.Unmarshal(data, dba); err != nil { if err != nil {
return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) return nil, err
} }
if !dba.DeletedAt.IsZero() { dba, err := db.unmarshalDBAdmin(data, id)
return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", id) if err != nil {
return nil, err
} }
return dba, nil return dba, nil
} }
func unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { func (db *DB) unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) {
dba, err := unmarshalDBAdmin(data, id) dba, err := db.unmarshalDBAdmin(data, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &linkedca.Admin{ return dba.convert(), nil
Id: dba.ID,
AuthorityId: dba.AuthorityID,
ProvisionerId: dba.ProvisionerID,
Subject: dba.Subject,
Type: dba.Type,
}, nil
} }
// GetAdmin retrieves and unmarshals a admin from the database. // 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 { if err != nil {
return nil, err return nil, err
} }
adm, err := unmarshalAdmin(data, id) adm, err := db.unmarshalAdmin(data, id)
if err != nil { if err != nil {
return nil, err 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 return adm, nil
} }
@ -100,15 +103,24 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error)
// from the database. // from the database.
// TODO should we be paginating? // TODO should we be paginating?
func (db *DB) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { 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 { if err != nil {
return nil, errors.Wrap(err, "error loading admins") return nil, errors.Wrap(err, "error loading admins")
} }
var admins = []*linkedca.Admin{} var admins = []*linkedca.Admin{}
for _, entry := range dbEntries { for _, entry := range dbEntries {
adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) adm, err := db.unmarshalAdmin(entry.Value, string(entry.Key))
if err != nil { 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 { if adm.AuthorityId != db.authorityID {
continue continue
@ -123,7 +135,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error {
var err error var err error
adm.Id, err = randID() adm.Id, err = randID()
if err != nil { 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 adm.AuthorityId = db.authorityID
@ -136,7 +148,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error {
CreatedAt: clock.Now(), 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. // 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 := old.clone()
nu.Type = adm.Type 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. // 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 := old.clone()
nu.DeletedAt = clock.Now() nu.DeletedAt = clock.Now()
return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) return db.save(ctx, old.ID, nu, old, "admin", adminsTable)
} }

File diff suppressed because it is too large Load Diff

@ -11,10 +11,8 @@ import (
) )
var ( var (
authorityAdminsTable = []byte("authority_admins") adminsTable = []byte("admins")
authorityConfigsTable = []byte("authority_configs") provisionersTable = []byte("provisioners")
authorityProvisionersTable = []byte("authority_provisioners")
authorityProvisionersNameIDIndexTable = []byte("authority_provisioners_name_id_index")
) )
// DB is a struct that implements the AcmeDB interface. // 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. // New configures and returns a new Authority DB backend implemented using a nosql DB.
func New(db nosqlDB.DB, authorityID string) (*DB, error) { func New(db nosqlDB.DB, authorityID string) (*DB, error) {
tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable, authorityProvisionersNameIDIndexTable} tables := [][]byte{adminsTable, provisionersTable}
for _, b := range tables { for _, b := range tables {
if err := db.CreateTable(b); err != nil { if err := db.CreateTable(b); err != nil {
return nil, errors.Wrapf(err, "error creating table %s", 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) { func randID() (val string, err error) {
val, err = randutil.UUIDv4() val, err = randutil.UUIDv4()
if err != nil { if err != nil {

@ -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)
}

File diff suppressed because it is too large Load Diff

@ -1,4 +1,4 @@
package mgmt package admin
import ( import (
"encoding/json" "encoding/json"
@ -25,6 +25,10 @@ const (
ErrorDeletedType ErrorDeletedType
// ErrorBadRequestType bad request. // ErrorBadRequestType bad request.
ErrorBadRequestType ErrorBadRequestType
// ErrorNotImplementedType not implemented.
ErrorNotImplementedType
// ErrorUnauthorizedType internal server error.
ErrorUnauthorizedType
// ErrorServerInternalType internal server error. // ErrorServerInternalType internal server error.
ErrorServerInternalType ErrorServerInternalType
) )
@ -41,6 +45,10 @@ func (ap ProblemType) String() string {
return "deleted" return "deleted"
case ErrorBadRequestType: case ErrorBadRequestType:
return "badRequest" return "badRequest"
case ErrorNotImplementedType:
return "notImplemented"
case ErrorUnauthorizedType:
return "unauthorized"
case ErrorServerInternalType: case ErrorServerInternalType:
return "internalServerError" return "internalServerError"
default: default:
@ -75,12 +83,22 @@ var (
ErrorDeletedType: { ErrorDeletedType: {
typ: ErrorDeletedType.String(), typ: ErrorDeletedType.String(),
details: "resource is deleted", details: "resource is deleted",
status: 403, status: http.StatusUnauthorized,
},
ErrorNotImplementedType: {
typ: ErrorNotImplementedType.String(),
details: "not implemented",
status: http.StatusNotImplemented,
}, },
ErrorBadRequestType: { ErrorBadRequestType: {
typ: ErrorBadRequestType.String(), typ: ErrorBadRequestType.String(),
details: "bad request", details: "bad request",
status: 400, status: http.StatusBadRequest,
},
ErrorUnauthorizedType: {
typ: ErrorUnauthorizedType.String(),
details: "unauthorized",
status: http.StatusUnauthorized,
}, },
ErrorServerInternalType: errorServerInternalMetadata, 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 { if err := json.NewEncoder(w).Encode(err); err != nil {
log.Println(err) log.Println(err)
} }

@ -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
}

@ -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
}

@ -11,14 +11,14 @@ import (
"time" "time"
"github.com/smallstep/certificates/cas" "github.com/smallstep/certificates/cas"
"github.com/smallstep/certificates/linkedca"
"github.com/smallstep/certificates/scep" "github.com/smallstep/certificates/scep"
"go.step.sm/linkedca"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/admin" "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/config"
"github.com/smallstep/certificates/authority/mgmt"
authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
casapi "github.com/smallstep/certificates/cas/apiv1" casapi "github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
@ -34,16 +34,17 @@ import (
// Authority implements the Certificate Authority internal interface. // Authority implements the Certificate Authority internal interface.
type Authority struct { type Authority struct {
config *config.Config config *config.Config
adminDB mgmt.DB
keyManager kms.KeyManager keyManager kms.KeyManager
provisioners *provisioner.Collection provisioners *provisioner.Collection
admins *admin.Collection admins *administrator.Collection
db db.AuthDB db db.AuthDB
adminDB admin.DB
templates *templates.Templates templates *templates.Templates
// X509 CA // X509 CA
x509CAService cas.CertificateAuthorityService x509CAService cas.CertificateAuthorityService
rootX509Certs []*x509.Certificate rootX509Certs []*x509.Certificate
rootX509CertPool *x509.CertPool
federatedX509Certs []*x509.Certificate federatedX509Certs []*x509.Certificate
certificates *sync.Map certificates *sync.Map
@ -67,6 +68,8 @@ type Authority struct {
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error) 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) sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)
getIdentityFunc provisioner.GetIdentityFunc getIdentityFunc provisioner.GetIdentityFunc
adminMutex sync.RWMutex
} }
// New creates and initiates a new Authority type. // New creates and initiates a new Authority type.
@ -134,62 +137,62 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
return a, nil return a, nil
} }
// ReloadAuthConfig reloads dynamic fields of the AuthConfig. // reloadAdminResources reloads admins and provisioners from the DB.
func (a *Authority) ReloadAuthConfig(ctx context.Context) error { func (a *Authority) reloadAdminResources(ctx context.Context) error {
provs, err := a.adminDB.GetProvisioners(ctx) var (
if err != nil { provList provisioner.List
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") adminList []*linkedca.Admin
} )
a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) if a.config.AuthorityConfig.EnableAdmin {
if err != nil { provs, err := a.adminDB.GetProvisioners(ctx)
return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") if err != nil {
} return admin.WrapErrorISE(err, "error getting provisioners to initialize authority")
a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(ctx) }
if err != nil { provList, err = provisionerListToCertificates(provs)
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") 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 provisionerConfig, err := a.generateProvisionerConfig(ctx)
claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims)
if err != nil { if err != nil {
return err return admin.WrapErrorISE(err, "error generating provisioner config")
}
// 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,
} }
// Store all the provisioners
for _, p := range a.config.AuthorityConfig.Provisioners { // Create provisioner collection.
if err := p.Init(config); err != nil { provClxn := provisioner.NewCollection(provisionerConfig.Audiences)
for _, p := range provList {
if err := p.Init(*provisionerConfig); err != nil {
return err return err
} }
if err := a.provisioners.Store(p); err != nil { if err := provClxn.Store(p); err != nil {
return err return err
} }
} }
// Store all the admins // Create admin collection.
a.admins = admin.NewCollection(a.provisioners) adminClxn := administrator.NewCollection(provClxn)
for _, adm := range a.config.AuthorityConfig.Admins { for _, adm := range adminList {
if err := a.admins.Store(adm); err != nil { 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 return err
} }
} }
a.config.AuthorityConfig.Provisioners = provList
a.provisioners = provClxn
a.config.AuthorityConfig.Admins = adminList
a.admins = adminClxn
return nil 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. // Initialize key manager if it has not been set in the options.
if a.keyManager == nil { if a.keyManager == nil {
var options kmsapi.Options var options kmsapi.Options
@ -329,6 +282,11 @@ func (a *Authority) init() error {
a.certificates.Store(hex.EncodeToString(sum[:]), crt) 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. // Read federated certificates and store them in the certificates map.
if len(a.federatedX509Certs) == 0 { if len(a.federatedX509Certs) == 0 {
a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots)) 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:]...) 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 // Check if a KMS with decryption capability is required and available
if a.requiresDecrypter() { if a.requiresDecrypter() {
if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { if _, ok := a.keyManager.(kmsapi.Decrypter); !ok {
@ -499,23 +431,44 @@ func (a *Authority) init() error {
// TODO: mimick the x509CAService GetCertificateAuthority here too? // TODO: mimick the x509CAService GetCertificateAuthority here too?
} }
// Store all the provisioners if a.config.AuthorityConfig.EnableAdmin {
for _, p := range a.config.AuthorityConfig.Provisioners { // Initialize step-ca Admin Database if it's not already initialized using
if err := p.Init(config); err != nil { // WithAdminDB.
return err 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")
} }
} if len(provs) == 0 {
// Store all the admins // Create First Provisioner
a.admins = admin.NewCollection(a.provisioners) prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password)
for _, adm := range a.config.AuthorityConfig.Admins { if err != nil {
if err := a.admins.Store(adm); err != nil { return admin.WrapErrorISE(err, "error creating first provisioner")
return err }
// 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. // Configure templates, currently only ssh templates are supported.
if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil {
a.templates = a.config.Templates a.templates = a.config.Templates
@ -543,21 +496,11 @@ func (a *Authority) GetDatabase() db.AuthDB {
return a.db return a.db
} }
// GetAdminDatabase returns the mgmt database, if one exists. // GetAdminDatabase returns the admin database, if one exists.
func (a *Authority) GetAdminDatabase() mgmt.DB { func (a *Authority) GetAdminDatabase() admin.DB {
return a.adminDB 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. // Shutdown safely shuts down any clients, databases, etc. held by the Authority.
func (a *Authority) Shutdown() error { func (a *Authority) Shutdown() error {
if err := a.keyManager.Close(); err != nil { if err := a.keyManager.Close(); err != nil {

@ -454,8 +454,6 @@ func TestAuthority_GetSCEPService(t *testing.T) {
// getIdentityFunc: tt.fields.getIdentityFunc, // getIdentityFunc: tt.fields.getIdentityFunc,
// } // }
a, err := New(tt.fields.config) a, err := New(tt.fields.config)
fmt.Println(err)
fmt.Println(a)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("Authority.New(), error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Authority.New(), error = %v, wantErr %v", err, tt.wantErr)
return return

@ -7,10 +7,13 @@ import (
"encoding/hex" "encoding/hex"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/smallstep/certificates/authority/admin"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"go.step.sm/crypto/jose" "go.step.sm/crypto/jose"
"go.step.sm/linkedca"
"golang.org/x/crypto/ssh" "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. // 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 we cannot get a token id from the provisioner, just hash the token.
if !SkipTokenReuseFromContext(ctx) { if !SkipTokenReuseFromContext(ctx) {
if reuseKey, err := p.GetTokenID(token); err == nil { if err = a.UseToken(token, p); err != nil {
if reuseKey == "" { return nil, err
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")
}
} }
} }
return p, nil 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 // Authorize grabs the method from the context and authorizes the request by
// validating the one-time-token. // validating the one-time-token.
func (a *Authority) Authorize(ctx context.Context, token string) ([]provisioner.SignOption, error) { func (a *Authority) Authorize(ctx context.Context, token string) ([]provisioner.SignOption, error) {

@ -822,7 +822,7 @@ func TestAuthority_authorizeRenew(t *testing.T) {
return &authorizeTest{ return &authorizeTest{
auth: a, auth: a,
cert: renewDisabledCrt, 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, code: http.StatusUnauthorized,
} }
}, },

@ -5,17 +5,26 @@ import "github.com/smallstep/certificates/authority/config"
// Config is an alias to support older APIs. // Config is an alias to support older APIs.
type Config = config.Config type Config = config.Config
// LoadConfiguration is an alias to support older APIs.
var LoadConfiguration = config.LoadConfiguration
// AuthConfig is an alias to support older APIs. // AuthConfig is an alias to support older APIs.
type AuthConfig = config.AuthConfig type AuthConfig = config.AuthConfig
// TLS
// ASN1DN is an alias to support older APIs. // ASN1DN is an alias to support older APIs.
type ASN1DN = config.ASN1DN 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. // TLSOptions is an alias to support older APIs.
type TLSOptions = config.TLSOptions type TLSOptions = config.TLSOptions
// CipherSuites is an alias to support older APIs.
type CipherSuites = config.CipherSuites
// SSH // SSH
// SSHConfig is an alias to support older APIs. // SSHConfig is an alias to support older APIs.

@ -12,8 +12,8 @@ import (
cas "github.com/smallstep/certificates/cas/apiv1" cas "github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
kms "github.com/smallstep/certificates/kms/apiv1" kms "github.com/smallstep/certificates/kms/apiv1"
"github.com/smallstep/certificates/linkedca"
"github.com/smallstep/certificates/templates" "github.com/smallstep/certificates/templates"
"go.step.sm/linkedca"
) )
const ( const (
@ -21,18 +21,6 @@ const (
) )
var ( 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 // DefaultBackdate length of time to backdate certificates to avoid
// clock skew validation issues. // clock skew validation issues.
DefaultBackdate = time.Minute DefaultBackdate = time.Minute
@ -41,7 +29,7 @@ var (
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally // DefaultEnableSSHCA enable SSH CA features per provisioner or globally
// for all provisioners. // for all provisioners.
DefaultEnableSSHCA = false DefaultEnableSSHCA = false
// GlobalProvisionerClaims default claims for the Authority. Can be overriden // GlobalProvisionerClaims default claims for the Authority. Can be overridden
// by provisioner specific claims. // by provisioner specific claims.
GlobalProvisionerClaims = provisioner.Claims{ GlobalProvisionerClaims = provisioner.Claims{
MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs
@ -65,6 +53,7 @@ type Config struct {
IntermediateCert string `json:"crt"` IntermediateCert string `json:"crt"`
IntermediateKey string `json:"key"` IntermediateKey string `json:"key"`
Address string `json:"address"` Address string `json:"address"`
InsecureAddress string `json:"insecureAddress"`
DNSNames []string `json:"dnsNames"` DNSNames []string `json:"dnsNames"`
KMS *kms.Options `json:"kms,omitempty"` KMS *kms.Options `json:"kms,omitempty"`
SSH *SSHConfig `json:"ssh,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 // ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer
// x509 Certificate blocks. // x509 Certificate blocks.
type ASN1DN struct { type ASN1DN struct {
Country string `json:"country,omitempty" step:"country"` Country string `json:"country,omitempty"`
Organization string `json:"organization,omitempty" step:"organization"` Organization string `json:"organization,omitempty"`
OrganizationalUnit string `json:"organizationalUnit,omitempty" step:"organizationalUnit"` OrganizationalUnit string `json:"organizationalUnit,omitempty"`
Locality string `json:"locality,omitempty" step:"locality"` Locality string `json:"locality,omitempty"`
Province string `json:"province,omitempty" step:"province"` Province string `json:"province,omitempty"`
StreetAddress string `json:"streetAddress,omitempty" step:"streetAddress"` StreetAddress string `json:"streetAddress,omitempty"`
CommonName string `json:"commonName,omitempty" step:"commonName"` CommonName string `json:"commonName,omitempty"`
} }
// AuthConfig represents the configuration options for the authority. An // AuthConfig represents the configuration options for the authority. An
@ -101,6 +90,7 @@ type AuthConfig struct {
Claims *provisioner.Claims `json:"claims,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"`
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
Backdate *provisioner.Duration `json:"backdate,omitempty"` Backdate *provisioner.Duration `json:"backdate,omitempty"`
EnableAdmin bool `json:"enableAdmin,omitempty"`
} }
// init initializes the required fields in the AuthConfig if they are not // init initializes the required fields in the AuthConfig if they are not

@ -8,12 +8,14 @@ import (
"github.com/smallstep/assert" "github.com/smallstep/assert"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"go.step.sm/crypto/jose" "go.step.sm/crypto/jose"
_ "github.com/smallstep/certificates/cas"
) )
func TestConfigValidate(t *testing.T) { 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) 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) assert.FatalError(t, err)
ac := &AuthConfig{ ac := &AuthConfig{
Provisioners: provisioner.List{ Provisioners: provisioner.List{
@ -39,9 +41,9 @@ func TestConfigValidate(t *testing.T) {
"empty-address": func(t *testing.T) ConfigValidateTest { "empty-address": func(t *testing.T) ConfigValidateTest {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -53,9 +55,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1", Address: "127.0.0.1",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -67,8 +69,8 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -80,8 +82,8 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -93,8 +95,8 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -106,9 +108,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
}, },
@ -119,9 +121,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -133,9 +135,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -148,9 +150,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -177,9 +179,9 @@ func TestConfigValidate(t *testing.T) {
return ConfigValidateTest{ return ConfigValidateTest{
config: &Config{ config: &Config{
Address: "127.0.0.1:443", Address: "127.0.0.1:443",
Root: []string{"testdata/secrets/root_ca.crt"}, Root: []string{"../testdata/secrets/root_ca.crt"},
IntermediateCert: "testdata/secrets/intermediate_ca.crt", IntermediateCert: "../testdata/secrets/intermediate_ca.crt",
IntermediateKey: "testdata/secrets/intermediate_ca_key", IntermediateKey: "../testdata/secrets/intermediate_ca_key",
DNSNames: []string{"test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"},
Password: "pass", Password: "pass",
AuthorityConfig: ac, AuthorityConfig: ac,
@ -207,6 +209,8 @@ func TestConfigValidate(t *testing.T) {
} }
} else { } else {
if assert.Nil(t, tc.err) { 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) assert.Equals(t, *tc.config.TLS, tc.tls)
} }
} }
@ -224,9 +228,9 @@ func TestAuthConfigValidate(t *testing.T) {
CommonName: "test", CommonName: "test",
} }
maxjwk, err := jose.ReadKey("testdata/secrets/max_pub.jwk") maxjwk, err := jose.ReadKey("../testdata/secrets/max_pub.jwk")
assert.FatalError(t, err) 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) assert.FatalError(t, err)
p := provisioner.List{ p := provisioner.List{
&provisioner.JWK{ &provisioner.JWK{

@ -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)
}
})
}
}

@ -34,6 +34,18 @@ var (
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "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. // TLSVersion represents a TLS version number.

@ -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)
}

@ -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)
*/
}

@ -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
}
*/

@ -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)
}

@ -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
}

@ -7,8 +7,8 @@ import (
"encoding/pem" "encoding/pem"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/admin"
"github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/mgmt"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/cas" "github.com/smallstep/certificates/cas"
casapi "github.com/smallstep/certificates/cas/apiv1" 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. // 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 { return func(a *Authority) error {
a.adminDB = db a.adminDB = db
return nil return nil

@ -105,7 +105,7 @@ func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
// certificate was configured to allow renewals. // certificate was configured to allow renewals.
func (p *ACME) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *ACME) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 return nil
} }

@ -61,7 +61,7 @@ func TestACME_Init(t *testing.T) {
"fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest {
return ProvisionerValidateTest{ return ProvisionerValidateTest{
p: &ACME{Name: "foo", Type: "bar", Claims: &Claims{DefaultTLSDur: &Duration{0}}}, 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 { "ok": func(t *testing.T) ProvisionerValidateTest {
@ -110,7 +110,7 @@ func TestACME_AuthorizeRenew(t *testing.T) {
p: p, p: p,
cert: &x509.Certificate{}, cert: &x509.Certificate{},
code: http.StatusUnauthorized, 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 { "ok": func(t *testing.T) test {

@ -296,7 +296,7 @@ func (p *AWS) GetTokenID(token string) (string, error) {
} }
// Use provisioner + instance-id as the identifier. // 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)) sum := sha256.Sum256([]byte(unique))
return strings.ToLower(hex.EncodeToString(sum[:])), nil return strings.ToLower(hex.EncodeToString(sum[:])), nil
} }
@ -344,7 +344,7 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) {
return "", err return "", err
} }
audience, err := generateSignAudience(caURL, p.GetID()) audience, err := generateSignAudience(caURL, p.GetIDForToken())
if err != nil { if err != nil {
return "", err 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 // 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 // per provisioner is allowed as we don't have a way to trust the given
// sans. // sans.
unique := fmt.Sprintf("%s.%s", p.GetID(), idoc.InstanceID) unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), idoc.InstanceID)
sum := sha256.Sum256([]byte(unique)) sum := sha256.Sum256([]byte(unique))
// Create a JWT from the identity document // 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 { if p.config, err = newAWSConfig(p.IIDRoots); err != nil {
return err return err
} }
p.audiences = config.Audiences.WithFragment(p.GetID()) p.audiences = config.Audiences.WithFragment(p.GetIDForToken())
// validate IMDS versions // validate IMDS versions
if len(p.IMDSVersions) == 0 { 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. // certificate was configured to allow renewals.
func (p *AWS) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *AWS) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 return nil
} }
@ -697,7 +697,7 @@ func (p *AWS) authorizeToken(token string) (*awsPayload, error) {
// AuthorizeSSHSign returns the list of SignOption for a SignSSH request. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) claims, err := p.authorizeToken(token)
if err != nil { if err != nil {

@ -334,7 +334,7 @@ func (p *Azure) AuthorizeSign(ctx context.Context, token string) ([]SignOption,
// certificate was configured to allow renewals. // certificate was configured to allow renewals.
func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 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. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) _, name, _, err := p.authorizeToken(token)

@ -7,27 +7,6 @@ import (
"golang.org/x/crypto/ssh" "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. // Claims so that individual provisioners can override global claims.
type Claims struct { type Claims struct {
// TLS CA properties // TLS CA properties
@ -92,6 +71,9 @@ func (c *Claimer) DefaultTLSCertDuration() time.Duration {
// minimum from the authority configuration will be used. // minimum from the authority configuration will be used.
func (c *Claimer) MinTLSCertDuration() time.Duration { func (c *Claimer) MinTLSCertDuration() time.Duration {
if c.claims == nil || c.claims.MinTLSDur == nil { 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.global.MinTLSDur.Duration
} }
return c.claims.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. // maximum from the authority configuration will be used.
func (c *Claimer) MaxTLSCertDuration() time.Duration { func (c *Claimer) MaxTLSCertDuration() time.Duration {
if c.claims == nil || c.claims.MaxTLSDur == nil { 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.global.MaxTLSDur.Duration
} }
return c.claims.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. // global minimum from the authority configuration will be used.
func (c *Claimer) MinUserSSHCertDuration() time.Duration { func (c *Claimer) MinUserSSHCertDuration() time.Duration {
if c.claims == nil || c.claims.MinUserSSHDur == nil { 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.global.MinUserSSHDur.Duration
} }
return c.claims.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. // global maximum from the authority configuration will be used.
func (c *Claimer) MaxUserSSHCertDuration() time.Duration { func (c *Claimer) MaxUserSSHCertDuration() time.Duration {
if c.claims == nil || c.claims.MaxUserSSHDur == nil { 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.global.MaxUserSSHDur.Duration
} }
return c.claims.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. // global minimum from the authority configuration will be used.
func (c *Claimer) MinHostSSHCertDuration() time.Duration { func (c *Claimer) MinHostSSHCertDuration() time.Duration {
if c.claims == nil || c.claims.MinHostSSHDur == nil { 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.global.MinHostSSHDur.Duration
} }
return c.claims.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. // global maximum from the authority configuration will be used.
func (c *Claimer) MaxHostSSHCertDuration() time.Duration { func (c *Claimer) MaxHostSSHCertDuration() time.Duration {
if c.claims == nil || c.claims.MaxHostSSHDur == nil { 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.global.MaxHostSSHDur.Duration
} }
return c.claims.MaxHostSSHDur.Duration return c.claims.MaxHostSSHDur.Duration

@ -12,7 +12,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin"
"go.step.sm/crypto/jose" "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 { if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil {
return nil, false return nil, false
} }
switch Type(provisioner.Type) { return c.LoadByName(string(provisioner.Name))
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))
}
} }
} }
@ -190,18 +173,21 @@ func (c *Collection) LoadEncryptedKey(keyID string) (string, bool) {
func (c *Collection) Store(p Interface) error { func (c *Collection) Store(p Interface) error {
// Store provisioner always in byID. ID must be unique. // Store provisioner always in byID. ID must be unique.
if _, loaded := c.byID.LoadOrStore(p.GetID(), p); loaded { 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. // Store provisioner always by name.
if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded { if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded {
c.byID.Delete(p.GetID()) 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. // Store provisioner always by ID presented in token.
if _, loaded := c.byTokenID.LoadOrStore(p.GetIDForToken(), p); loaded { if _, loaded := c.byTokenID.LoadOrStore(p.GetIDForToken(), p); loaded {
c.byID.Delete(p.GetID()) c.byID.Delete(p.GetID())
c.byName.Delete(p.GetName()) 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. // Store provisioner in byKey if EncryptedKey is defined.
@ -225,6 +211,65 @@ func (c *Collection) Store(p Interface) error {
return nil 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. // 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) (List, string) {
switch { switch {

@ -132,6 +132,7 @@ func TestCollection_LoadByToken(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := &Collection{ c := &Collection{
byID: tt.fields.byID, byID: tt.fields.byID,
byTokenID: tt.fields.byID,
audiences: tt.fields.audiences, audiences: tt.fields.audiences,
} }
got, got1 := c.LoadByToken(tt.args.token, tt.args.claims) got, got1 := c.LoadByToken(tt.args.token, tt.args.claims)
@ -153,10 +154,10 @@ func TestCollection_LoadByCertificate(t *testing.T) {
p3, err := generateACME() p3, err := generateACME()
assert.FatalError(t, err) assert.FatalError(t, err)
byID := new(sync.Map) byName := new(sync.Map)
byID.Store(p1.GetID(), p1) byName.Store(p1.GetName(), p1)
byID.Store(p2.GetID(), p2) byName.Store(p2.GetName(), p2)
byID.Store(p3.GetID(), p3) byName.Store(p3.GetName(), p3)
ok1Ext, err := createProvisionerExtension(1, p1.Name, p1.Key.KeyID) ok1Ext, err := createProvisionerExtension(1, p1.Name, p1.Key.KeyID)
assert.FatalError(t, err) assert.FatalError(t, err)
@ -186,7 +187,7 @@ func TestCollection_LoadByCertificate(t *testing.T) {
} }
type fields struct { type fields struct {
byID *sync.Map byName *sync.Map
audiences Audiences audiences Audiences
} }
type args struct { type args struct {
@ -199,17 +200,17 @@ func TestCollection_LoadByCertificate(t *testing.T) {
want Interface want Interface
want1 bool want1 bool
}{ }{
{"ok1", fields{byID, testAudiences}, args{ok1Cert}, p1, true}, {"ok1", fields{byName, testAudiences}, args{ok1Cert}, p1, true},
{"ok2", fields{byID, testAudiences}, args{ok2Cert}, p2, true}, {"ok2", fields{byName, testAudiences}, args{ok2Cert}, p2, true},
{"ok3", fields{byID, testAudiences}, args{ok3Cert}, p3, true}, {"ok3", fields{byName, testAudiences}, args{ok3Cert}, p3, true},
{"noExtension", fields{byID, testAudiences}, args{&x509.Certificate{}}, &noop{}, true}, {"noExtension", fields{byName, testAudiences}, args{&x509.Certificate{}}, &noop{}, true},
{"notFound", fields{byID, testAudiences}, args{notFoundCert}, nil, false}, {"notFound", fields{byName, testAudiences}, args{notFoundCert}, nil, false},
{"badCert", fields{byID, testAudiences}, args{badCert}, nil, false}, {"badCert", fields{byName, testAudiences}, args{badCert}, nil, false},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := &Collection{ c := &Collection{
byID: tt.fields.byID, byName: tt.fields.byName,
audiences: tt.fields.audiences, audiences: tt.fields.audiences,
} }
got, got1 := c.LoadByCertificate(tt.args.cert) got, got1 := c.LoadByCertificate(tt.args.cert)

@ -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 // 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 // per provisioner is allowed as we don't have a way to trust the given
// sans. // 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)) sum := sha256.Sum256([]byte(unique))
return strings.ToLower(hex.EncodeToString(sum[:])), nil 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. // GetIdentityToken does an HTTP request to the identity url.
func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) { func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) {
audience, err := generateSignAudience(caURL, p.GetID()) audience, err := generateSignAudience(caURL, p.GetIDForToken())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -216,7 +216,7 @@ func (p *GCP) Init(config Config) error {
return err return err
} }
p.audiences = config.Audiences.WithFragment(p.GetID()) p.audiences = config.Audiences.WithFragment(p.GetIDForToken())
return nil 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. // AuthorizeRenew returns an error if the renewal is disabled.
func (p *GCP) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *GCP) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 return nil
} }
@ -382,7 +382,7 @@ func (p *GCP) authorizeToken(token string) (*gcpPayload, error) {
// AuthorizeSSHSign returns the list of SignOption for a SignSSH request. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) claims, err := p.authorizeToken(token)
if err != nil { if err != nil {

@ -194,7 +194,7 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er
// certificate was configured to allow renewals. // certificate was configured to allow renewals.
func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 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. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) claims, err := p.authorizeToken(token, p.audiences.SSHSign)
if err != nil { if err != nil {

@ -77,7 +77,7 @@ func TestJWK_Init(t *testing.T) {
"fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest {
return ProvisionerValidateTest{ return ProvisionerValidateTest{
p: &JWK{Name: "foo", Type: "bar", Key: &jose.JSONWebKey{}, audiences: testAudiences, Claims: &Claims{DefaultTLSDur: &Duration{0}}}, 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 { "ok": func(t *testing.T) ProvisionerValidateTest {

@ -111,12 +111,12 @@ func (p *K8sSA) Init(config Config) (err error) {
} }
key, err := pemutil.ParseKey(pem.EncodeToMemory(block)) key, err := pemutil.ParseKey(pem.EncodeToMemory(block))
if err != nil { 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) { switch q := key.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
default: 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) 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. // AuthorizeRenew returns an error if the renewal is disabled.
func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 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. // AuthorizeSSHSign validates an request for an SSH certificate.
func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) claims, err := p.authorizeToken(token, p.audiences.SSHSign)
if err != nil { if err != nil {

@ -198,7 +198,7 @@ func TestK8sSA_AuthorizeRenew(t *testing.T) {
p: p, p: p,
cert: &x509.Certificate{}, cert: &x509.Certificate{},
code: http.StatusUnauthorized, 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 { "ok": func(t *testing.T) test {
@ -319,7 +319,7 @@ func TestK8sSA_AuthorizeSSHSign(t *testing.T) {
p: p, p: p,
token: "foo", token: "foo",
code: http.StatusUnauthorized, 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 { "fail/invalid-token": func(t *testing.T) test {

@ -377,7 +377,7 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
// certificate was configured to allow renewals. // certificate was configured to allow renewals.
func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if o.claimer.IsDisableRenewal() { 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 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. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !o.claimer.IsSSHCAEnabled() { 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) claims, err := o.authorizeToken(token)
if err != nil { if err != nil {

@ -11,6 +11,7 @@ import (
// SCEP provisioning flow // SCEP provisioning flow
type SCEP struct { type SCEP struct {
*base *base
ID string `json:"-"`
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
@ -26,8 +27,17 @@ type SCEP struct {
secretChallengePassword string secretChallengePassword string
} }
// GetID returns the provisioner unique identifier. // GetID returns the provisioner unique identifier. The name and credential id
func (s SCEP) GetID() string { // 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 return "scep/" + s.Name
} }

@ -101,7 +101,7 @@ func (p *SSHPOP) Init(config Config) error {
return err return err
} }
p.audiences = config.Audiences.WithFragment(p.GetID()) p.audiences = config.Audiences.WithFragment(p.GetIDForToken())
p.db = config.DB p.db = config.DB
p.sshPubKeys = config.SSHKeys p.sshPubKeys = config.SSHKeys
return nil return nil

@ -116,7 +116,7 @@ func (p *X5C) Init(config Config) error {
// Verify that at least one root was found. // Verify that at least one root was found.
if len(p.rootPool.Subjects()) == 0 { 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 // Update claims with global ones
@ -125,7 +125,7 @@ func (p *X5C) Init(config Config) error {
return err return err
} }
p.audiences = config.Audiences.WithFragment(p.GetID()) p.audiences = config.Audiences.WithFragment(p.GetIDForToken())
return nil return nil
} }
@ -139,7 +139,8 @@ func (p *X5C) authorizeToken(token string, audiences []string) (*x5cPayload, err
} }
verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{ verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{
Roots: p.rootPool, Roots: p.rootPool,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}) })
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusUnauthorized, err, 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. // AuthorizeRenew returns an error if the renewal is disabled.
func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { 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 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. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() { 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) claims, err := p.authorizeToken(token, p.audiences.SSHSign)

@ -70,7 +70,7 @@ func TestX5C_Init(t *testing.T) {
"fail/no-valid-root-certs": func(t *testing.T) ProvisionerValidateTest { "fail/no-valid-root-certs": func(t *testing.T) ProvisionerValidateTest {
return ProvisionerValidateTest{ return ProvisionerValidateTest{
p: &X5C{Name: "foo", Type: "bar", Roots: []byte("foo"), audiences: testAudiences}, 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 { "fail/invalid-duration": func(t *testing.T) ProvisionerValidateTest {
@ -79,7 +79,7 @@ func TestX5C_Init(t *testing.T) {
p.Claims = &Claims{DefaultTLSDur: &Duration{0}} p.Claims = &Claims{DefaultTLSDur: &Duration{0}}
return ProvisionerValidateTest{ return ProvisionerValidateTest{
p: p, 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 { "ok": func(t *testing.T) ProvisionerValidateTest {
@ -568,7 +568,7 @@ func TestX5C_AuthorizeRenew(t *testing.T) {
return test{ return test{
p: p, p: p,
code: http.StatusUnauthorized, 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 { "ok": func(t *testing.T) test {
@ -624,7 +624,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
p: p, p: p,
token: "foo", token: "foo",
code: http.StatusUnauthorized, 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 { "fail/invalid-token": func(t *testing.T) test {

@ -1,19 +1,24 @@
package authority package authority
import ( import (
"context"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"fmt" "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/authority/provisioner"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/linkedca"
"go.step.sm/crypto/jose" "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. // GetEncryptedKey returns the JWE key corresponding to the given kid argument.
func (a *Authority) GetEncryptedKey(kid string) (string, error) { func (a *Authority) GetEncryptedKey(kid string) (string, error) {
a.adminMutex.RLock()
defer a.adminMutex.RUnlock()
key, ok := a.provisioners.LoadEncryptedKey(kid) key, ok := a.provisioners.LoadEncryptedKey(kid)
if !ok { if !ok {
return "", errs.NotFound("encrypted key with kid %s was not found", kid) 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 // GetProvisioners returns a map listing each provisioner and the JWK Key Set
// with their public keys. // with their public keys.
func (a *Authority) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) { 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) provisioners, nextCursor := a.provisioners.Find(cursor, limit)
return provisioners, nextCursor, nil 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 // LoadProvisionerByCertificate returns an interface to the provisioner that
// provisioned the certificate. // provisioned the certificate.
func (a *Authority) LoadProvisionerByCertificate(crt *x509.Certificate) (provisioner.Interface, error) { func (a *Authority) LoadProvisionerByCertificate(crt *x509.Certificate) (provisioner.Interface, error) {
a.adminMutex.RLock()
defer a.adminMutex.RUnlock()
p, ok := a.provisioners.LoadByCertificate(crt) p, ok := a.provisioners.LoadByCertificate(crt)
if !ok { 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 return p, nil
} }
// LoadProvisionerByID returns an interface to the provisioner with the given ID. // LoadProvisionerByID returns an interface to the provisioner with the given ID.
func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) {
a.adminMutex.RLock()
defer a.adminMutex.RUnlock()
p, ok := a.provisioners.Load(id) p, ok := a.provisioners.Load(id)
if !ok { if !ok {
return nil, errs.NotFound("provisioner not found") return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id)
} }
return p, nil return p, nil
} }
func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { // LoadProvisionerByName returns an interface to the provisioner with the given Name.
return &provisioner.Options{ func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) {
X509: &provisioner.X509Options{ a.adminMutex.RLock()
Template: string(p.X509Template), defer a.adminMutex.RUnlock()
TemplateData: p.X509TemplateData, 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{ GetIdentityFunc: a.getIdentityFunc,
Template: string(p.SshTemplate), }, nil
TemplateData: p.SshTemplateData,
}
// 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) { func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, error) {
var nu provisioner.List var nu provisioner.List
for _, p := range l { for _, p := range l {
certProv, err := provisionerToCertificates(p) certProv, err := ProvisionerToCertificates(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -72,9 +360,87 @@ func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List,
return nu, nil return nu, nil
} }
// provisionerToCertificates converts the landlord provisioner type to the open source func optionsToCertificates(p *linkedca.Provisioner) *provisioner.Options {
// provisioner type. ops := &provisioner.Options{
func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { 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) claims, err := claimsToCertificates(p.Claims)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,9 +451,10 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface,
return nil, fmt.Errorf("provisioner does not have any details") return nil, fmt.Errorf("provisioner does not have any details")
} }
options := optionsToCertificates(p)
switch d := details.(type) { switch d := details.(type) {
case *linkedca.ProvisionerDetails_JWK: case *linkedca.ProvisionerDetails_JWK:
fmt.Printf("d = %+v\n", d)
jwk := new(jose.JSONWebKey) jwk := new(jose.JSONWebKey)
if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil {
return nil, err return nil, err
@ -99,153 +466,136 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface,
Key: jwk, Key: jwk,
EncryptedKey: string(d.JWK.EncryptedPrivateKey), EncryptedKey: string(d.JWK.EncryptedPrivateKey),
Claims: claims, 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 }, 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: default:
return nil, fmt.Errorf("provisioner %s not implemented", p.Type) return nil, fmt.Errorf("provisioner %s not implemented", p.Type)
} }
} }
// claimsToCertificates converts the landlord provisioner claims type to the open source func parseInstanceAge(age string) (provisioner.Duration, error) {
// (step-ca) claims type. var instanceAge provisioner.Duration
func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { if age != "" {
var durs = map[string]struct { iap, err := provisioner.NewDuration(age)
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 { if err != nil {
return nil, mgmt.WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) return instanceAge, err
} }
} instanceAge = *iap
return &provisioner.Claims{ }
MinTLSDur: durs["minTLSDur"].dur, return instanceAge, nil
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
} }

@ -56,7 +56,7 @@ func TestGetEncryptedKey(t *testing.T) {
} }
} else { } else {
if assert.Nil(t, tc.err) { 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) assert.Fatal(t, ok)
p, ok := val.(*provisioner.JWK) p, ok := val.(*provisioner.JWK)
assert.Fatal(t, ok) assert.Fatal(t, ok)

@ -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) { func TestAuthority_GetSSHBastion(t *testing.T) {
bastion := &Bastion{ bastion := &Bastion{
Hostname: "bastion.local", Hostname: "bastion.local",

@ -360,10 +360,9 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
} }
// This method will also validate the audiences for JWK provisioners. // This method will also validate the audiences for JWK provisioners.
var ok bool p, err = a.LoadProvisionerByToken(token, &claims.Claims)
p, ok = a.provisioners.LoadByToken(token, &claims.Claims) if err != nil {
if !ok { return err
return errs.InternalServer("authority.Revoke; provisioner not found", opts...)
} }
rci.ProvisionerID = p.GetID() rci.ProvisionerID = p.GetID()
rci.TokenID, err = p.GetTokenID(revokeOpts.OTT) rci.TokenID, err = p.GetTokenID(revokeOpts.OTT)

@ -656,7 +656,7 @@ func TestAuthority_Renew(t *testing.T) {
"fail/unauthorized": func() (*renewTest, error) { "fail/unauthorized": func() (*renewTest, error) {
return &renewTest{ return &renewTest{
cert: certNoRenew, 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, code: http.StatusUnauthorized,
}, nil }, nil
}, },
@ -856,7 +856,7 @@ func TestAuthority_Rekey(t *testing.T) {
"fail/unauthorized": func() (*renewTest, error) { "fail/unauthorized": func() (*renewTest, error) {
return &renewTest{ return &renewTest{
cert: certNoRenew, 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, code: http.StatusUnauthorized,
}, nil }, nil
}, },

@ -2,29 +2,42 @@ package ca
import ( import (
"bytes" "bytes"
"crypto/x509"
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv" "strconv"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/admin"
mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" adminAPI "github.com/smallstep/certificates/authority/admin/api"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/errs" "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" var adminURLPrefix = "admin"
// AdminClient implements an HTTP client for the CA server. // AdminClient implements an HTTP client for the CA server.
type AdminClient struct { type AdminClient struct {
client *uaClient client *uaClient
endpoint *url.URL endpoint *url.URL
retryFunc RetryFunc retryFunc RetryFunc
opts []ClientOption 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. // 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{ return &AdminClient{
client: newClient(tr), client: newClient(tr),
endpoint: u, endpoint: u,
retryFunc: o.retryFunc, retryFunc: o.retryFunc,
opts: opts, opts: opts,
x5cJWK: o.x5cJWK,
x5cCertFile: o.x5cCertFile,
x5cCertStrs: o.x5cCertStrs,
x5cCert: o.x5cCert,
x5cIssuer: o.x5cIssuer,
x5cSubject: o.x5cSubject,
}, nil }, 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 { func (c *AdminClient) retryOnError(r *http.Response) bool {
if c.retryFunc != nil { if c.retryFunc != nil {
if c.retryFunc(r.StatusCode) { 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. // 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 var retried bool
o := new(adminOptions) o := new(adminOptions)
if err := o.apply(opts); err != nil { if err := o.apply(opts); err != nil {
@ -148,8 +193,17 @@ func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdmins
Path: "/admin/admins", Path: "/admin/admins",
RawQuery: o.rawQuery(), 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: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client GET %s failed", u) return nil, errors.Wrapf(err, "client GET %s failed", u)
} }
@ -160,7 +214,7 @@ retry:
} }
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var body = new(mgmtAPI.GetAdminsResponse) var body = new(adminAPI.GetAdminsResponse)
if err := readJSON(resp.Body, body); err != nil { if err := readJSON(resp.Body, body); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) 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 cursor = resp.NextCursor
} }
return admins, nil
} }
// CreateAdmin performs the POST /admin/admins request to the CA. // 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 var retried bool
body, err := json.Marshal(req) body, err := json.Marshal(createAdminRequest)
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
} }
u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admins"}) 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: retry:
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client POST %s failed", u) return nil, errors.Wrapf(err, "client POST %s failed", u)
} }
@ -218,10 +280,15 @@ retry:
func (c *AdminClient) RemoveAdmin(id string) error { func (c *AdminClient) RemoveAdmin(id string) error {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) 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) req, err := http.NewRequest("DELETE", u.String(), nil)
if err != nil { if err != nil {
return errors.Wrapf(err, "create DELETE %s request failed", u) return errors.Wrapf(err, "create DELETE %s request failed", u)
} }
req.Header.Add("Authorization", tok)
retry: retry:
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { if err != nil {
@ -238,17 +305,22 @@ retry:
} }
// UpdateAdmin performs the PUT /admin/admins/{id} request to the CA. // 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 var retried bool
body, err := json.Marshal(uar) body, err := json.Marshal(uar)
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
} }
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) 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)) req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body))
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "create PUT %s request failed", u) return nil, errors.Wrapf(err, "create PUT %s request failed", u)
} }
req.Header.Add("Authorization", tok)
retry: retry:
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { if err != nil {
@ -268,12 +340,35 @@ retry:
return adm, nil return adm, nil
} }
// GetProvisionerByName performs the GET /admin/provisioners/{name} request to the CA. // GetProvisioner performs the GET /admin/provisioners/{name} request to the CA.
func (c *AdminClient) GetProvisionerByName(name string) (*linkedca.Provisioner, error) { func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provisioner, error) {
var retried bool 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: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client GET %s failed", u) return nil, errors.Wrapf(err, "client GET %s failed", u)
} }
@ -285,14 +380,14 @@ retry:
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var prov = new(linkedca.Provisioner) 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 nil, errors.Wrapf(err, "error reading %s", u)
} }
return prov, nil return prov, nil
} }
// GetProvisionersPaginate performs the GET /admin/provisioners request to the CA. // 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 var retried bool
o := new(provisionerOptions) o := new(provisionerOptions)
if err := o.apply(opts); err != nil { if err := o.apply(opts); err != nil {
@ -302,8 +397,17 @@ func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtA
Path: "/admin/provisioners", Path: "/admin/provisioners",
RawQuery: o.rawQuery(), 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: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client GET %s failed", u) return nil, errors.Wrapf(err, "client GET %s failed", u)
} }
@ -314,7 +418,7 @@ retry:
} }
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var body = new(mgmtAPI.GetProvisionersResponse) var body = new(adminAPI.GetProvisionersResponse)
if err := readJSON(resp.Body, body); err != nil { if err := readJSON(resp.Body, body); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) 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 cursor = resp.NextCursor
} }
return provs, nil
} }
// RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. // RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA.
func (c *AdminClient) RemoveProvisioner(name string) error { func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error {
var retried bool var (
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) 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) req, err := http.NewRequest("DELETE", u.String(), nil)
if err != nil { if err != nil {
return errors.Wrapf(err, "create DELETE %s request failed", u) return errors.Wrapf(err, "create DELETE %s request failed", u)
} }
req.Header.Add("Authorization", tok)
retry: retry:
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { if err != nil {
@ -367,13 +493,22 @@ retry:
// CreateProvisioner performs the POST /admin/provisioners request to the CA. // CreateProvisioner performs the POST /admin/provisioners request to the CA.
func (c *AdminClient) CreateProvisioner(prov *linkedca.Provisioner) (*linkedca.Provisioner, error) { func (c *AdminClient) CreateProvisioner(prov *linkedca.Provisioner) (*linkedca.Provisioner, error) {
var retried bool var retried bool
body, err := json.Marshal(prov) body, err := protojson.Marshal(prov)
if err != nil { if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") 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: retry:
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client POST %s failed", u) return nil, errors.Wrapf(err, "client POST %s failed", u)
} }
@ -385,48 +520,49 @@ retry:
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var nuProv = new(linkedca.Provisioner) 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 nil, errors.Wrapf(err, "error reading %s", u)
} }
return nuProv, nil return nuProv, nil
} }
// UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. // UpdateProvisioner performs the PUT /admin/provisioners/{name} request to the CA.
func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*linkedca.Provisioner, error) { func (c *AdminClient) UpdateProvisioner(name string, prov *linkedca.Provisioner) error {
var retried bool var retried bool
body, err := json.Marshal(upr) body, err := protojson.Marshal(prov)
if err != nil { 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)) req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body))
if err != nil { 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: retry:
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { 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 resp.StatusCode >= 400 {
if !retried && c.retryOnError(resp) { if !retried && c.retryOnError(resp) {
retried = true retried = true
goto retry goto retry
} }
return nil, readAdminError(resp.Body) return 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 prov, nil return nil
} }
func readAdminError(r io.ReadCloser) error { func readAdminError(r io.ReadCloser) error {
defer r.Close() defer r.Close()
mgmtErr := new(mgmt.Error) adminErr := new(admin.Error)
if err := json.NewDecoder(r).Decode(mgmtErr); err != nil { if err := json.NewDecoder(r).Decode(adminErr); err != nil {
return err return err
} }
return errors.New(mgmtErr.Message) return errors.New(adminErr.Message)
} }

@ -17,9 +17,8 @@ import (
acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql" acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql"
"github.com/smallstep/certificates/api" "github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority"
adminAPI "github.com/smallstep/certificates/authority/admin/api"
"github.com/smallstep/certificates/authority/config" "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/db"
"github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/logging"
"github.com/smallstep/certificates/monitoring" "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 // CA is the type used to build the complete certificate authority. It builds
// the HTTP server, set ups the middlewares and the HTTP handlers. // the HTTP server, set ups the middlewares and the HTTP handlers.
type CA struct { type CA struct {
auth *authority.Authority auth *authority.Authority
config *config.Config config *config.Config
srv *server.Server srv *server.Server
opts *options insecureSrv *server.Server
renewer *TLSRenewer opts *options
renewer *TLSRenewer
} }
// New creates and initializes the CA with the given configuration and options. // 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() mux := chi.NewRouter()
handler := http.Handler(mux) handler := http.Handler(mux)
insecureMux := chi.NewRouter()
insecureHandler := http.Handler(insecureMux)
// Add regular CA api endpoints in / and /1.0 // Add regular CA api endpoints in / and /1.0
routerHandler := api.New(auth) routerHandler := api.New(auth)
routerHandler.Route(mux) routerHandler.Route(mux)
@ -154,7 +157,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) {
if config.DB == nil { if config.DB == nil {
acmeDB = nil acmeDB = nil
} else { } else {
acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB), mgmt.DefaultAuthorityID) acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error configuring ACME DB interface") 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 // Admin API Router
adminDB := auth.GetAdminDatabase() if config.AuthorityConfig.EnableAdmin {
if adminDB != nil { adminDB := auth.GetAdminDatabase()
mgmtHandler := mgmtAPI.NewHandler(auth) if adminDB != nil {
mux.Route("/admin", func(r chi.Router) { adminHandler := adminAPI.NewHandler(auth)
mgmtHandler.Route(r) mux.Route("/admin", func(r chi.Router) {
}) adminHandler.Route(r)
})
}
} }
if ca.shouldServeSCEPEndpoints() { if ca.shouldServeSCEPEndpoints() {

@ -10,8 +10,10 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"encoding/pem"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -28,10 +30,13 @@ import (
"github.com/smallstep/certificates/ca/identity" "github.com/smallstep/certificates/ca/identity"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"go.step.sm/cli-utils/config" "go.step.sm/cli-utils/config"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/keyutil" "go.step.sm/crypto/keyutil"
"go.step.sm/crypto/pemutil" "go.step.sm/crypto/pemutil"
"go.step.sm/crypto/x509util" "go.step.sm/crypto/x509util"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"gopkg.in/square/go-jose.v2/jwt" "gopkg.in/square/go-jose.v2/jwt"
) )
@ -108,6 +113,12 @@ type clientOptions struct {
certificate tls.Certificate certificate tls.Certificate
getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error) getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
retryFunc RetryFunc retryFunc RetryFunc
x5cJWK *jose.JSONWebKey
x5cCertFile string
x5cCertStrs []string
x5cCert *x509.Certificate
x5cIssuer string
x5cSubject string
} }
func (o *clientOptions) apply(opts []ClientOption) (err error) { 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 // WithCertificate will set the given certificate as the TLS client certificate
// in the client. // in the client.
func WithCertificate(crt tls.Certificate) ClientOption { func WithCertificate(cert tls.Certificate) ClientOption {
return func(o *clientOptions) error { 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 return nil
} }
} }
@ -372,6 +440,8 @@ type ProvisionerOption func(o *provisionerOptions) error
type provisionerOptions struct { type provisionerOptions struct {
cursor string cursor string
limit int limit int
id string
name string
} }
func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) { func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) {
@ -391,6 +461,12 @@ func (o *provisionerOptions) rawQuery() string {
if o.limit > 0 { if o.limit > 0 {
v.Set("limit", strconv.Itoa(o.limit)) 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() 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. // Client implements an HTTP client for the CA server.
type Client struct { type Client struct {
client *uaClient client *uaClient
@ -1211,6 +1303,15 @@ func readJSON(r io.ReadCloser, v interface{}) error {
return json.NewDecoder(r).Decode(v) 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 { func readError(r io.ReadCloser) error {
defer r.Close() defer r.Close()
apiErr := new(errs.Error) apiErr := new(errs.Error)

@ -34,7 +34,7 @@
"y": "ZhYcFQBqtErdC_pA7sOXrO7AboCEPIKP9Ik4CHJqANk" "y": "ZhYcFQBqtErdC_pA7sOXrO7AboCEPIKP9Ik4CHJqANk"
} }
}, { }, {
"name": "max", "name": "mike",
"type": "jwk", "type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlZsWnl0dUxrWTR5enlqZXJybnN0aGcifQ.QP15wQYjZ12BLgl-XTq2Vb12G3OHAfic.X35QqAaXwnlmeCUU._2qIUp0TI8yDI7c2e9upIRdrnmB5OvtLfrYN-Su2NLBpaoYtr9O55Wo0Iryc0W2pYqnVDPvgPPes4P4nQAnzw5WhFYc1Xf1ZEetfdNhwi1x2FNwPbACBAgxm5AW40O5AAlbLcWushYASfeMBZocTGXuSGUzwFqoWD-5EDJ80TWQ7cAj3ttHrJ_3QV9hi4O9KJUCiXngN-Yz2zXrhBL4NOH2fmRbaf5c0rF8xUJIIW-TcyYJeX_Fbx1IzzKKPd9USUwkDhxD4tLa51I345xVqjuwG1PEn6nF8JKqLRVUKEKFin-ShXrfE61KceyAvm4YhWKrbJWIm3bH5Hxaphy4.TexIrIhsRxJStpE3EJ925Q", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlZsWnl0dUxrWTR5enlqZXJybnN0aGcifQ.QP15wQYjZ12BLgl-XTq2Vb12G3OHAfic.X35QqAaXwnlmeCUU._2qIUp0TI8yDI7c2e9upIRdrnmB5OvtLfrYN-Su2NLBpaoYtr9O55Wo0Iryc0W2pYqnVDPvgPPes4P4nQAnzw5WhFYc1Xf1ZEetfdNhwi1x2FNwPbACBAgxm5AW40O5AAlbLcWushYASfeMBZocTGXuSGUzwFqoWD-5EDJ80TWQ7cAj3ttHrJ_3QV9hi4O9KJUCiXngN-Yz2zXrhBL4NOH2fmRbaf5c0rF8xUJIIW-TcyYJeX_Fbx1IzzKKPd9USUwkDhxD4tLa51I345xVqjuwG1PEn6nF8JKqLRVUKEKFin-ShXrfE61KceyAvm4YhWKrbJWIm3bH5Hxaphy4.TexIrIhsRxJStpE3EJ925Q",
"key": { "key": {
@ -76,7 +76,7 @@
"minTLSCertDuration": "1s" "minTLSCertDuration": "1s"
} }
}, { }, {
"name": "mariano", "name": "maxey",
"type": "jwk", "type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6Ik5SLTk5ZkVMSm1CLW1FZGllUlFFc3cifQ.Fr314BEUGTda4ICJl2uxFdjpEUGGqJEV.gBbu_DZE1ONDu14r.X-7MKMyokZIF1HTCVqqL0tTWgaC1ZGZBLLltd11ZUhQTswo_8kvgiTv3cFShj7ATF0tAY8HStyJmzLO8mKPVOPDXSwjdNsPriZclI6JWGi9iOu8pEiN9pZM6-itxan1JMcDUNg2U-P1BmKppHRbDKsOTivymfRyeUk51dBIlS54p5xNK1HFLc1YtWC1Rc_ngYVqOgqlhIrCHArAEBe3jrfUaH2ym-8fkVdwVqtxmte3XXK9g8FchsygRNnOKtRcr0TyzTUV-7bPi8_t02Zi-EHLFaSawVXWV_Qk1GeLYJR22Rp74beo-b5-lCNVp10btO0xdGySUWmCJ4v4_QZw.c8unwWycwtfdJMM_0b0fuA", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6Ik5SLTk5ZkVMSm1CLW1FZGllUlFFc3cifQ.Fr314BEUGTda4ICJl2uxFdjpEUGGqJEV.gBbu_DZE1ONDu14r.X-7MKMyokZIF1HTCVqqL0tTWgaC1ZGZBLLltd11ZUhQTswo_8kvgiTv3cFShj7ATF0tAY8HStyJmzLO8mKPVOPDXSwjdNsPriZclI6JWGi9iOu8pEiN9pZM6-itxan1JMcDUNg2U-P1BmKppHRbDKsOTivymfRyeUk51dBIlS54p5xNK1HFLc1YtWC1Rc_ngYVqOgqlhIrCHArAEBe3jrfUaH2ym-8fkVdwVqtxmte3XXK9g8FchsygRNnOKtRcr0TyzTUV-7bPi8_t02Zi-EHLFaSawVXWV_Qk1GeLYJR22Rp74beo-b5-lCNVp10btO0xdGySUWmCJ4v4_QZw.c8unwWycwtfdJMM_0b0fuA",
"key": { "key": {

@ -143,7 +143,11 @@ func newX5CSigner(certFile, keyFile, password string) (jose.Signer, error) {
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, errors.Wrap(err, "error validating x5c certificate chain and key") 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 := new(jose.SignerOptions)
so.WithType("JWT") so.WithType("JWT")
so.WithHeader("kid", kid) so.WithHeader("kid", kid)
so.WithHeader("x5c", certs) so.WithHeader("x5c", certStrs)
return newJoseSigner(signer, so) return newJoseSigner(signer, so)
} }

@ -23,18 +23,22 @@ require (
github.com/smallstep/nosql v0.3.6 github.com/smallstep/nosql v0.3.6
github.com/urfave/cli v1.22.4 github.com/urfave/cli v1.22.4
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1
go.step.sm/cli-utils v0.2.0 go.step.sm/cli-utils v0.4.1
go.step.sm/crypto v0.8.3 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/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 golang.org/x/net v0.0.0-20210119194325-5f4716e94777
google.golang.org/api v0.33.0 google.golang.org/api v0.33.0
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154
google.golang.org/grpc v1.32.0 google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.25.0 google.golang.org/protobuf v1.26.0
gopkg.in/square/go-jose.v2 v2.5.1 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
//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 replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568

197
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.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/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 h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 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 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 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/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 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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 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-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.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 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0=
github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= 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/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 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.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/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/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/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/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/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 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/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 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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/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 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 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/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 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 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/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 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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 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-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/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/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 h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 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-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 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 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/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/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 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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-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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/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.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 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.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 h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc=
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= 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/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-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 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 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 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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-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/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/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/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.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.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.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 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 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 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/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/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/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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/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 h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 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.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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/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.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.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/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 h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 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.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.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.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= 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.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 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.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/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 h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 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.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 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/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 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/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.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.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/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/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/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-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/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/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/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/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/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-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-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-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-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-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-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-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.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-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-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/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.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 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 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/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/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/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/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/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/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 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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/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-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 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/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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 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/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.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 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= 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/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.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.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/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.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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 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/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 h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= 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/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/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 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.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 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.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 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 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/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/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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-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/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 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 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 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/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 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.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 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
<<<<<<< HEAD github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
<<<<<<< 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/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 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/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 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.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/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 h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 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/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 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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.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 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.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 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 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/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/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 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 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 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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-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/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 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/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/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.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/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-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/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.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/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/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 h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU=
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= 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/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/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/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 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk=
github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 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.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/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/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/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-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/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.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/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-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.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.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/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/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/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/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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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/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 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/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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/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.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 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.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_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-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-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-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.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/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/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/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-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.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.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/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/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/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 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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 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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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/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 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo=
github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= 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/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/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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/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 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po=
github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= 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/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/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/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/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 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 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 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 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-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/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/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.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= 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/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 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= 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/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 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 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.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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/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 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 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.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
<<<<<<< HEAD go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
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.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.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 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.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.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 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.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.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 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM=
go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= 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.8.0 h1:S4qBPyy3hR7KWLybSkHB0H14pwFfYkom4RZ96JzmXig= go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
go.step.sm/crypto v0.8.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.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/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.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/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/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.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= 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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-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-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-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-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-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-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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-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-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-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-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-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-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-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.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 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 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-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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 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.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 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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/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 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-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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/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 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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/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/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/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 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.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/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/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.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.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/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 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 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= 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= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

@ -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
}

@ -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;
}

@ -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

@ -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
}

File diff suppressed because it is too large Load Diff

@ -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;
}

@ -67,8 +67,8 @@ func New(scepAuth scep.Interface) api.RouterHandler {
// Route traffic and implement the Router interface. // Route traffic and implement the Router interface.
func (h *Handler) Route(r api.Router) { func (h *Handler) Route(r api.Router) {
getLink := h.Auth.GetLinkExplicit getLink := h.Auth.GetLinkExplicit
r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) r.MethodFunc(http.MethodGet, getLink("{name}", false, nil), h.lookupProvisioner(h.Get))
r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) r.MethodFunc(http.MethodPost, getLink("{name}", false, nil), h.lookupProvisioner(h.Post))
} }
// Get handles all SCEP GET requests // Get handles all SCEP GET requests
@ -185,14 +185,14 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) {
func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "provisionerID") nameEscaped := chi.URLParam(r, "name")
provisionerID, err := url.PathUnescape(name) name, err := url.PathUnescape(nameEscaped)
if err != nil { if err != nil {
api.WriteError(w, errors.Errorf("error url unescaping provisioner id '%s'", name)) api.WriteError(w, errors.Errorf("error url unescaping provisioner id '%s'", name))
return return
} }
p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) p, err := h.Auth.LoadProvisionerByName(name)
if err != nil { if err != nil {
api.WriteError(w, err) api.WriteError(w, err)
return return

@ -20,7 +20,7 @@ import (
// Interface is the SCEP authority interface. // Interface is the SCEP authority interface.
type Interface 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 GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
GetCACertificates() ([]*x509.Certificate, error) GetCACertificates() ([]*x509.Certificate, error)
@ -56,7 +56,7 @@ type AuthorityOptions struct {
// SignAuthority is the interface for a signing authority // SignAuthority is the interface for a signing authority
type SignAuthority interface { type SignAuthority interface {
Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) 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. // 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 // LoadProvisionerByName calls out to the SignAuthority interface to load a
// provisioner by ID. // provisioner by name.
func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) {
return a.signAuth.LoadProvisionerByID(id) return a.signAuth.LoadProvisionerByName(name)
} }
// GetLinkExplicit returns the requested link from the directory. // GetLinkExplicit returns the requested link from the directory.

Loading…
Cancel
Save