This commit is contained in:
max furman 2021-05-20 13:01:58 -07:00
parent 638766c615
commit 9bf9bf142d
8 changed files with 121 additions and 58 deletions

View file

@ -32,7 +32,7 @@ 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
mgmtDB mgmt.DB adminDB mgmt.DB
keyManager kms.KeyManager keyManager kms.KeyManager
provisioners *provisioner.Collection provisioners *provisioner.Collection
admins *admin.Collection admins *admin.Collection
@ -130,7 +130,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
} }
func (a *Authority) ReloadAuthConfig() error { func (a *Authority) ReloadAuthConfig() error {
mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID)
if err != nil { if err != nil {
return mgmt.WrapErrorISE(err, "error getting authConfig from db") return mgmt.WrapErrorISE(err, "error getting authConfig from db")
} }
@ -204,14 +204,14 @@ func (a *Authority) init() error {
// Pull AuthConfig from DB. // Pull AuthConfig from DB.
if true { if true {
// Check if AuthConfig already exists // Check if AuthConfig already exists
a.mgmtDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID)
if err != nil { if err != nil {
return err return err
} }
mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID) mgmtAuthConfig, err := a.adminDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID)
if err != nil { if err != nil {
if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) { if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) {
mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.mgmtDB, mgmt.WithDefaultAuthorityID) mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.adminDB, mgmt.WithDefaultAuthorityID)
if err != nil { if err != nil {
return mgmt.WrapErrorISE(err, "error creating authConfig") return mgmt.WrapErrorISE(err, "error creating authConfig")
} }
@ -465,9 +465,9 @@ func (a *Authority) GetDatabase() db.AuthDB {
return a.db return a.db
} }
// GetMgmtDatabase returns the mgmt database, if one exists. // GetAdminDatabase returns the mgmt database, if one exists.
func (a *Authority) GetMgmtDatabase() mgmt.DB { func (a *Authority) GetAdminDatabase() mgmt.DB {
return a.mgmtDB return a.adminDB
} }
// GetAdminCollection returns the admin collection. // GetAdminCollection returns the admin collection.

37
authority/config.go Normal file
View file

@ -0,0 +1,37 @@
package authority
import "github.com/smallstep/certificates/authority/config"
// Config is an alias to support older APIs.
type Config = config.Config
// AuthConfig is an alias to support older APIs.
type AuthConfig = config.AuthConfig
// ASN1DN is an alias to support older APIs.
type ASN1DN = config.ASN1DN
// TLS
// TLSOptions is an alias to support older APIs.
type TLSOptions = config.TLSOptions
// SSH
// SSHConfig is an alias to support older APIs.
type SSHConfig = config.SSHConfig
// Bastion is an alias to support older APIs.
type Bastion = config.Bastion
// HostTag is an alias to support older APIs.
type HostTag = config.HostTag
// Host is an alias to support older APIs.
type Host = config.Host
// SSHPublicKey is an alias to support older APIs.
type SSHPublicKey = config.SSHPublicKey
// SSHKeys is an alias to support older APIs.
type SSHKeys = config.SSHKeys

View file

@ -25,8 +25,8 @@ type Handler struct {
} }
// NewHandler returns a new Authority Config Handler. // NewHandler returns a new Authority Config Handler.
func NewHandler(db mgmt.DB, auth *authority.Authority) api.RouterHandler { func NewHandler(auth *authority.Authority) api.RouterHandler {
return &Handler{db, auth} return &Handler{auth.GetAdminDatabase(), auth}
} }
// Route traffic and implement the Router interface. // Route traffic and implement the Router interface.

View file

@ -9,6 +9,7 @@ import (
"github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/mgmt"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/authority/status" "github.com/smallstep/certificates/authority/status"
"github.com/smallstep/certificates/errs"
) )
// CreateProvisionerRequest represents the body for a CreateProvisioner request. // CreateProvisionerRequest represents the body for a CreateProvisioner request.
@ -31,6 +32,12 @@ func (cpr *CreateProvisionerRequest) Validate(c *provisioner.Collection) error {
return nil 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. // UpdateProvisionerRequest represents the body for a UpdateProvisioner request.
type UpdateProvisionerRequest struct { type UpdateProvisionerRequest struct {
Type string `json:"type"` Type string `json:"type"`
@ -72,14 +79,22 @@ func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) {
// GetProvisioners returns all provisioners associated with the authority. // GetProvisioners returns all provisioners associated with the authority.
func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() cursor, limit, err := api.ParseCursor(r)
provs, err := h.db.GetProvisioners(ctx)
if err != nil { if err != nil {
api.WriteError(w, err) api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err,
"error parsing cursor / limt query params"))
return return
} }
api.JSON(w, provs)
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. // CreateProvisioner creates a new prov.

View file

@ -60,17 +60,17 @@ func WithPassword(pass string) func(*ProvisionerCtx) {
// Provisioner type. // Provisioner type.
type Provisioner struct { type Provisioner struct {
ID string `json:"-"` ID string `json:"-"`
AuthorityID string `json:"-"` AuthorityID string `json:"-"`
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
Claims *Claims `json:"claims"` Claims *Claims `json:"claims"`
Details interface{} `json:"details"` Details ProvisionerDetails `json:"details"`
X509Template string `json:"x509Template"` X509Template string `json:"x509Template"`
X509TemplateData []byte `json:"x509TemplateData"` X509TemplateData []byte `json:"x509TemplateData"`
SSHTemplate string `json:"sshTemplate"` SSHTemplate string `json:"sshTemplate"`
SSHTemplateData []byte `json:"sshTemplateData"` SSHTemplateData []byte `json:"sshTemplateData"`
Status status.Type `json:"status"` Status status.Type `json:"status"`
} }
func (p *Provisioner) GetOptions() *provisioner.Options { func (p *Provisioner) GetOptions() *provisioner.Options {
@ -111,6 +111,8 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro
return p, nil return p, nil
} }
// ProvisionerDetails is the interface implemented by all provisioner details
// attributes.
type ProvisionerDetails interface { type ProvisionerDetails interface {
isProvisionerDetails() isProvisionerDetails()
} }
@ -118,8 +120,8 @@ type ProvisionerDetails interface {
// ProvisionerDetailsJWK represents the values required by a JWK provisioner. // ProvisionerDetailsJWK represents the values required by a JWK provisioner.
type ProvisionerDetailsJWK struct { type ProvisionerDetailsJWK struct {
Type ProvisionerType `json:"type"` Type ProvisionerType `json:"type"`
PubKey []byte `json:"pubKey"` PublicKey []byte `json:"publicKey"`
EncPrivKey string `json:"privKey"` PrivateKey string `json:"PrivateKey"`
} }
// ProvisionerDetailsOIDC represents the values required by a OIDC provisioner. // ProvisionerDetailsOIDC represents the values required by a OIDC provisioner.
@ -232,8 +234,8 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
return &ProvisionerDetailsJWK{ return &ProvisionerDetailsJWK{
Type: ProvisionerTypeJWK, Type: ProvisionerTypeJWK,
PubKey: jwkPubBytes, PublicKey: jwkPubBytes,
EncPrivKey: jwePrivStr, PrivateKey: jwePrivStr,
}, nil }, nil
} }
@ -248,7 +250,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
switch details := p.Details.(type) { switch details := p.Details.(type) {
case *ProvisionerDetailsJWK: case *ProvisionerDetailsJWK:
jwk := new(jose.JSONWebKey) jwk := new(jose.JSONWebKey)
if err := json.Unmarshal(details.PubKey, &jwk); err != nil { if err := json.Unmarshal(details.PublicKey, &jwk); err != nil {
return nil, err return nil, err
} }
return &provisioner.JWK{ return &provisioner.JWK{
@ -256,7 +258,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
Type: p.Type, Type: p.Type,
Name: p.Name, Name: p.Name,
Key: jwk, Key: jwk,
EncryptedKey: details.EncPrivKey, EncryptedKey: details.PrivateKey,
Claims: claims, Claims: claims,
Options: p.GetOptions(), Options: p.GetOptions(),
}, nil }, nil

View file

@ -8,6 +8,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"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"
@ -187,6 +188,14 @@ func WithX509FederatedBundle(pemCerts []byte) Option {
} }
} }
// WithAdminDB is an option to set the database backing the admin APIs.
func WithAdminDB(db mgmt.DB) Option {
return func(a *Authority) error {
a.adminDB = db
return nil
}
}
func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) { func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) {
var block *pem.Block var block *pem.Block
var certs []*x509.Certificate var certs []*x509.Certificate

View file

@ -171,11 +171,11 @@ func (ca *CA) Init(config *config.Config) (*CA, error) {
acmeHandler.Route(r) acmeHandler.Route(r)
}) })
// MGMT Router // Admin API Router
mgmtDB := auth.GetMgmtDatabase() adminDB := auth.GetAdminDatabase()
if mgmtDB != nil { if adminDB != nil {
mgmtHandler := mgmtAPI.NewHandler(mgmtDB, auth) mgmtHandler := mgmtAPI.NewHandler(auth)
mux.Route("/mgmt", func(r chi.Router) { mux.Route("/admin", func(r chi.Router) {
mgmtHandler.Route(r) mgmtHandler.Route(r)
}) })
} }

View file

@ -134,7 +134,7 @@ func WithAdminLimit(limit int) AdminOption {
} }
} }
// GetAdmins performs the GET /mgmt/admins request to the CA. // GetAdmins performs the GET /admin/admins request to the CA.
func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) {
var retried bool var retried bool
o := new(adminOptions) o := new(adminOptions)
@ -142,7 +142,7 @@ func (c *MgmtClient) GetAdmins(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse,
return nil, err return nil, err
} }
u := c.endpoint.ResolveReference(&url.URL{ u := c.endpoint.ResolveReference(&url.URL{
Path: "/mgmt/admins", Path: "/admin/admins",
RawQuery: o.rawQuery(), RawQuery: o.rawQuery(),
}) })
retry: retry:
@ -164,14 +164,14 @@ retry:
return body, nil return body, nil
} }
// CreateAdmin performs the POST /mgmt/admin request to the CA. // CreateAdmin performs the POST /admin/admin request to the CA.
func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) { func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) {
var retried bool var retried bool
body, err := json.Marshal(req) body, err := json.Marshal(req)
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: "/mgmt/admin"}) u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admin"})
retry: retry:
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body))
if err != nil { if err != nil {
@ -191,10 +191,10 @@ retry:
return adm, nil return adm, nil
} }
// RemoveAdmin performs the DELETE /mgmt/admin/{id} request to the CA. // RemoveAdmin performs the DELETE /admin/admin/{id} request to the CA.
func (c *MgmtClient) RemoveAdmin(id string) error { func (c *MgmtClient) RemoveAdmin(id string) error {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)})
req, err := http.NewRequest("DELETE", u.String(), nil) 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)
@ -214,14 +214,14 @@ retry:
return nil return nil
} }
// UpdateAdmin performs the PUT /mgmt/admin/{id} request to the CA. // UpdateAdmin performs the PUT /admin/admin/{id} request to the CA.
func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.Admin, error) { func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*admin.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("/mgmt/admin", id)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/admin", id)})
req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) 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)
@ -245,10 +245,10 @@ retry:
return adm, nil return adm, nil
} }
// GetProvisioner performs the GET /mgmt/provisioner/{id} request to the CA. // GetProvisioner performs the GET /admin/provisioner/{name} request to the CA.
func (c *MgmtClient) GetProvisioner(id string) (*mgmt.Provisioner, error) { func (c *MgmtClient) GetProvisioner(name string) (*mgmt.Provisioner, error) {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", id)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)})
retry: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Get(u.String())
if err != nil { if err != nil {
@ -268,10 +268,10 @@ retry:
return prov, nil return prov, nil
} }
// GetProvisioners performs the GET /mgmt/provisioners request to the CA. // GetProvisioners performs the GET /admin/provisioners request to the CA.
func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) { func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioners"}) u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"})
retry: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Get(u.String())
if err != nil { if err != nil {
@ -291,10 +291,10 @@ retry:
return *provs, nil return *provs, nil
} }
// RemoveProvisioner performs the DELETE /mgmt/provisioner/{name} request to the CA. // RemoveProvisioner performs the DELETE /admin/provisioner/{name} request to the CA.
func (c *MgmtClient) RemoveProvisioner(name string) error { func (c *MgmtClient) RemoveProvisioner(name string) error {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/provisioner", name)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", name)})
req, err := http.NewRequest("DELETE", u.String(), nil) 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)
@ -314,14 +314,14 @@ retry:
return nil return nil
} }
// CreateProvisioner performs the POST /mgmt/provisioner request to the CA. // CreateProvisioner performs the POST /admin/provisioner request to the CA.
func (c *MgmtClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) { func (c *MgmtClient) CreateProvisioner(req *mgmtAPI.CreateProvisionerRequest) (*mgmt.Provisioner, error) {
var retried bool var retried bool
body, err := json.Marshal(req) body, err := json.Marshal(req)
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: "/mgmt/provisioner"}) u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioner"})
retry: retry:
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body))
if err != nil { if err != nil {
@ -341,14 +341,14 @@ retry:
return prov, nil return prov, nil
} }
// UpdateProvisioner performs the PUT /mgmt/provisioner/{id} request to the CA. // UpdateProvisioner performs the PUT /admin/provisioner/{id} request to the CA.
func (c *MgmtClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) { func (c *MgmtClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*mgmt.Provisioner, error) {
var retried bool var retried bool
body, err := json.Marshal(upr) body, err := json.Marshal(upr)
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("/mgmt/provisioner", id)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/provisioner", id)})
req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body)) 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 nil, errors.Wrapf(err, "create PUT %s request failed", u)
@ -372,10 +372,10 @@ retry:
return prov, nil return prov, nil
} }
// GetAuthConfig performs the GET /mgmt/authconfig/{id} request to the CA. // GetAuthConfig performs the GET /admin/authconfig/{id} request to the CA.
func (c *MgmtClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) { func (c *MgmtClient) GetAuthConfig(id string) (*mgmt.AuthConfig, error) {
var retried bool var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/authconfig", id)}) u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/admin/authconfig", id)})
retry: retry:
resp, err := c.client.Get(u.String()) resp, err := c.client.Get(u.String())
if err != nil { if err != nil {