first steps

This commit is contained in:
max furman 2021-05-06 17:03:12 -07:00
parent 2f60f20b0b
commit af3cf7dae9
15 changed files with 251 additions and 205 deletions

View file

@ -31,7 +31,7 @@ import (
// Authority implements the Certificate Authority internal interface.
type Authority struct {
config *config.Config
mgmtDB *mgmt.DB
mgmtDB mgmt.DB
keyManager kms.KeyManager
provisioners *provisioner.Collection
db db.AuthDB
@ -146,20 +146,26 @@ func (a *Authority) init() error {
// Pull AuthConfig from DB.
if true {
if len(a.config.AuthConfig.AuthorityID)_== 0 {
mgmtDB, err := authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID)
// Check if AuthConfig already exists
a.mgmtDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID)
if err != nil {
return err
}
mgmtAuthConfig, err := mgmt.CreateAuthority(context.Background, mgmtDB, WithDefaultAuthorityID)
mgmtAuthConfig, err := a.mgmtDB.GetAuthConfig(context.Background(), mgmt.DefaultAuthorityID)
if err != nil {
if k, ok := err.(*mgmt.Error); ok && k.IsType(mgmt.ErrorNotFoundType) {
mgmtAuthConfig, err = mgmt.CreateAuthority(context.Background(), a.mgmtDB, mgmt.WithDefaultAuthorityID)
if err != nil {
return mgmt.WrapErrorISE(err, "error creating authConfig")
}
} else {
return mgmt.WrapErrorISE(err, "error getting authConfig from db")
}
}
a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates()
if err != nil {
return err
}
a.config.AuthConfig, err := mgmtAuthConfig.ToCertificates()
if err != nil {
return err
}
}
}
// Initialize key manager if it has not been set in the options.
@ -394,6 +400,11 @@ func (a *Authority) GetDatabase() db.AuthDB {
return a.db
}
// GetMgmtDatabase returns the mgmt database, if one exists.
func (a *Authority) GetMgmtDatabase() mgmt.DB {
return a.mgmtDB
}
// Shutdown safely shuts down any clients, databases, etc. held by the Authority.
func (a *Authority) Shutdown() error {
if err := a.keyManager.Close(); err != nil {

View file

@ -32,8 +32,13 @@ var (
MaxVersion: 1.2,
Renegotiation: false,
}
// DefaultBackdate length of time to backdate certificates to avoid
// clock skew validation issues.
DefaultBackdate = time.Minute
// DefaultDisableRenewal disables renewals per provisioner.
DefaultDisableRenewal = false
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally
// for all provisioners.
DefaultEnableSSHCA = false
// GlobalProvisionerClaims default claims for the Authority. Can be overriden
// by provisioner specific claims.
@ -153,7 +158,7 @@ func LoadConfiguration(filename string) (*Config, error) {
return nil, errors.Wrapf(err, "error parsing %s", filename)
}
c.init()
c.Init()
return &c, nil
}

View file

@ -58,6 +58,7 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
// CreateAdmin creates a new admin.
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
/*
ctx := r.Context()
var body CreateAdminRequest
@ -79,10 +80,12 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
return
}
api.JSONStatus(w, adm, http.StatusCreated)
*/
}
// UpdateAdmin updates an existing admin.
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
/*
ctx := r.Context()
id := chi.URLParam(r, "id")
@ -109,4 +112,5 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
return
}
api.JSON(w, adm)
*/
}

View file

@ -5,15 +5,14 @@ import (
"github.com/go-chi/chi"
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/mgmt"
)
// CreateAuthConfigRequest represents the body for a CreateAuthConfig request.
type CreateAuthConfigRequest struct {
ASN1DN *authority.ASN1DN `json:"asn1dn,omitempty"`
Claims *config.Claims `json:"claims,omitempty"`
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
ASN1DN *config.ASN1DN `json:"asn1dn,omitempty"`
Claims *mgmt.Claims `json:"claims,omitempty"`
Backdate string `json:"backdate,omitempty"`
}
@ -24,9 +23,8 @@ func (car *CreateAuthConfigRequest) Validate() error {
// UpdateAuthConfigRequest represents the body for a UpdateAuthConfig request.
type UpdateAuthConfigRequest struct {
ASN1DN *authority.ASN1DN `json:"asn1dn"`
Claims *config.Claims `json:"claims"`
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
ASN1DN *config.ASN1DN `json:"asn1dn"`
Claims *mgmt.Claims `json:"claims"`
Backdate string `json:"backdate,omitempty"`
}
@ -53,7 +51,7 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var body CreateAuthConfigRequest
if err := ReadJSON(r.Body, &body); err != nil {
if err := api.ReadJSON(r.Body, &body); err != nil {
api.WriteError(w, err)
return
}
@ -61,9 +59,8 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) {
api.WriteError(w, err)
}
ac := config.AuthConfig{
Status: config.StatusActive,
DisableIssuedAtCheck: body.DisableIssuedAtCheck,
ac := &mgmt.AuthConfig{
Status: mgmt.StatusActive,
Backdate: "1m",
}
if body.ASN1DN != nil {
@ -84,11 +81,12 @@ func (h *Handler) CreateAuthConfig(w http.ResponseWriter, r *http.Request) {
// UpdateAuthConfig updates an existing AuthConfig.
func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) {
/*
ctx := r.Context()
id := chi.URLParam(r, "id")
var body UpdateAuthConfigRequest
if err := ReadJSON(r.Body, &body); err != nil {
if err := api.ReadJSON(r.Body, &body); err != nil {
api.WriteError(w, err)
return
}
@ -96,12 +94,12 @@ func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) {
api.WriteError(w, err)
return
}
if ac, err := h.db.GetAuthConfig(ctx, id); err != nil {
ac, err := h.db.GetAuthConfig(ctx, id)
if err != nil {
api.WriteError(w, err)
return
}
ac.DisableIssuedAtCheck = body.DisableIssuedAtCheck
ac.Status = body.Status
if body.ASN1DN != nil {
ac.ASN1DN = body.ASN1DN
@ -118,4 +116,5 @@ func (h *Handler) UpdateAuthConfig(w http.ResponseWriter, r *http.Request) {
return
}
api.JSON(w, ac)
*/
}

View file

@ -4,7 +4,7 @@ import (
"time"
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/mgmt"
)
// Clock that returns time in UTC rounded to seconds.
@ -19,14 +19,12 @@ var clock Clock
// Handler is the ACME API request handler.
type Handler struct {
db config.DB
db mgmt.DB
}
// NewHandler returns a new Authority Config Handler.
func NewHandler(db config.DB) api.RouterHandler {
return &Handler{
db: ops.DB,
}
func NewHandler(db mgmt.DB) api.RouterHandler {
return &Handler{db}
}
// Route traffic and implement the Router interface.

View file

@ -5,14 +5,14 @@ import (
"github.com/go-chi/chi"
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/mgmt"
)
// CreateProvisionerRequest represents the body for a CreateProvisioner request.
type CreateProvisionerRequest struct {
Type string `json:"type"`
Name string `json:"name"`
Claims *config.Claims `json:"claims"`
Claims *mgmt.Claims `json:"claims"`
Details interface{} `json:"details"`
X509Template string `json:"x509Template"`
SSHTemplate string `json:"sshTemplate"`
@ -25,7 +25,7 @@ func (car *CreateProvisionerRequest) Validate() error {
// UpdateProvisionerRequest represents the body for a UpdateProvisioner request.
type UpdateProvisionerRequest struct {
Claims *config.Claims `json:"claims"`
Claims *mgmt.Claims `json:"claims"`
Details interface{} `json:"details"`
X509Template string `json:"x509Template"`
SSHTemplate string `json:"sshTemplate"`
@ -66,7 +66,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var body CreateProvisionerRequest
if err := ReadJSON(r.Body, &body); err != nil {
if err := api.ReadJSON(r.Body, &body); err != nil {
api.WriteError(w, err)
return
}
@ -74,7 +74,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
api.WriteError(w, err)
}
prov := &config.Provisioner{
prov := &mgmt.Provisioner{
Type: body.Type,
Name: body.Name,
Claims: body.Claims,
@ -91,6 +91,7 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
// UpdateProvisioner updates an existing prov.
func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
/*
ctx := r.Context()
id := chi.URLParam(r, "id")
@ -119,4 +120,5 @@ func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
return
}
api.JSON(w, prov)
*/
}

View file

@ -19,28 +19,8 @@ type AuthConfig struct {
func NewDefaultAuthConfig() *AuthConfig {
return &AuthConfig{
Claims: &Claims{
X509: &X509Claims{
Durations: &Durations{
Min: config.GlobalProvisionerClaims.MinTLSDur.String(),
Max: config.GlobalProvisionerClaims.MaxTLSDur.String(),
Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(),
},
},
SSH: &SSHClaims{
UserDurations: &Durations{
Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(),
Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(),
Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(),
},
HostDurations: &Durations{
Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(),
Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(),
Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(),
},
},
DisableRenewal: config.DefaultDisableRenewal,
},
Claims: NewDefaultClaims(),
ASN1DN: &config.ASN1DN{},
Backdate: config.DefaultBackdate.String(),
Status: StatusActive,
}

View file

@ -4,6 +4,7 @@ import (
"context"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/config"
)
const (
@ -50,6 +51,31 @@ type Durations struct {
Default string `json:"default"`
}
func NewDefaultClaims() *Claims {
return &Claims{
X509: &X509Claims{
Durations: &Durations{
Min: config.GlobalProvisionerClaims.MinTLSDur.String(),
Max: config.GlobalProvisionerClaims.MaxTLSDur.String(),
Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(),
},
},
SSH: &SSHClaims{
UserDurations: &Durations{
Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(),
Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(),
Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(),
},
HostDurations: &Durations{
Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(),
Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(),
Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(),
},
},
DisableRenewal: config.DefaultDisableRenewal,
}
}
type AuthorityOption func(*AuthConfig) error
func WithDefaultAuthorityID(ac *AuthConfig) error {

View file

@ -18,7 +18,7 @@ type DB interface {
UpdateProvisioner(ctx context.Context, prov *Provisioner) error
CreateAdmin(ctx context.Context, admin *Admin) error
GetAdmin(ctx context.Context, id string) error
GetAdmin(ctx context.Context, id string) (*Admin, error)
GetAdmins(ctx context.Context) ([]*Admin, error)
UpdateAdmin(ctx context.Context, admin *Admin) error
@ -116,7 +116,7 @@ func (m *MockDB) GetAdmins(ctx context.Context) ([]*Admin, error) {
// UpdateAdmin mock
func (m *MockDB) UpdateAdmin(ctx context.Context, adm *Admin) error {
if m.UpdateAdmin != nil {
if m.MockUpdateAdmin != nil {
return m.MockUpdateAdmin(ctx, adm)
}
return m.MockError
@ -142,7 +142,7 @@ func (m *MockDB) GetAuthConfig(ctx context.Context, id string) (*AuthConfig, err
// UpdateAuthConfig mock
func (m *MockDB) UpdateAuthConfig(ctx context.Context, adm *AuthConfig) error {
if m.UpdateAuthConfig != nil {
if m.MockUpdateAuthConfig != nil {
return m.MockUpdateAuthConfig(ctx, adm)
}
return m.MockError

View file

@ -6,7 +6,6 @@ import (
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/acme"
"github.com/smallstep/certificates/authority/mgmt"
"github.com/smallstep/nosql"
)
@ -109,7 +108,7 @@ func unmarshalAdmin(data []byte, id string) (*mgmt.Admin, error) {
// GetAdmins retrieves and unmarshals all active (not deleted) admins
// from the database.
// TODO should we be paginating?
func (db *DB) GetAdmins(ctx context.Context, az *acme.Authorization) ([]*mgmt.Admin, error) {
func (db *DB) GetAdmins(ctx context.Context) ([]*mgmt.Admin, error) {
dbEntries, err := db.db.List(authorityAdminsTable)
if err != nil {
return nil, errors.Wrap(err, "error loading admins")

View file

@ -15,7 +15,6 @@ type dbAuthConfig struct {
ID string `json:"id"`
ASN1DN *config.ASN1DN `json:"asn1dn"`
Claims *mgmt.Claims `json:"claims"`
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
Backdate string `json:"backdate,omitempty"`
CreatedAt time.Time `json:"createdAt"`
DeletedAt time.Time `json:"deletedAt"`
@ -68,23 +67,23 @@ func (db *DB) GetAuthConfig(ctx context.Context, id string) (*mgmt.AuthConfig, e
ASN1DN: dba.ASN1DN,
Backdate: dba.Backdate,
Claims: dba.Claims,
DisableIssuedAtCheck: dba.DisableIssuedAtCheck,
}, nil
}
// CreateAuthConfig stores a new provisioner to the database.
func (db *DB) CreateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error {
var err error
if ac.ID == "" {
ac.ID, err = randID()
if err != nil {
return errors.Wrap(err, "error generating random id for provisioner")
}
}
dba := &dbAuthConfig{
ID: ac.ID,
ASN1DN: ac.ASN1DN,
Claims: ac.Claims,
DisableIssuedAtCheck: ac.DisableIssuedAtCheck,
Backdate: ac.Backdate,
CreatedAt: clock.Now(),
}
@ -106,7 +105,6 @@ func (db *DB) UpdateAuthConfig(ctx context.Context, ac *mgmt.AuthConfig) error {
nu.DeletedAt = clock.Now()
}
nu.Claims = ac.Claims
nu.DisableIssuedAtCheck = ac.DisableIssuedAtCheck
nu.Backdate = ac.Backdate
return db.save(ctx, old.ID, nu, old, "authConfig", authorityProvisionersTable)

View file

@ -87,6 +87,11 @@ type Error struct {
Status int `json:"-"`
}
// IsType returns true if the error type matches the input type.
func (e *Error) IsType(pt ProblemType) bool {
return pt.String() == e.Type
}
// NewError creates a new Error type.
func NewError(pt ProblemType, msg string, args ...interface{}) *Error {
return newError(pt, errors.Errorf(msg, args...))

View file

@ -20,6 +20,16 @@ type ProvisionerCtx struct {
Password string
}
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
pc := &ProvisionerCtx{
Claims: NewDefaultClaims(),
}
for _, o := range opts {
o(pc)
}
return pc
}
func WithJWK(jwk *jose.JSONWebKey, jwe *jose.JSONWebEncryption) func(*ProvisionerCtx) {
return func(ctx *ProvisionerCtx) {
ctx.JWK = jwk
@ -62,10 +72,7 @@ func (p *Provisioner) GetOptions() *provisioner.Options {
}
func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...ProvisionerOption) (*Provisioner, error) {
pc := new(ProvisionerCtx)
for _, o := range opts {
o(pc)
}
pc := NewProvisionerCtx(opts...)
details, err := createJWKDetails(pc)
if err != nil {

View file

@ -18,6 +18,7 @@ import (
"github.com/smallstep/certificates/authority"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/mgmt"
mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api"
"github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/logging"
"github.com/smallstep/certificates/monitoring"
@ -143,6 +144,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) {
dns = fmt.Sprintf("%s:%s", dns, port)
}
// ACME Router
prefix := "acme"
var acmeDB acme.DB
if config.DB == nil {
@ -169,6 +171,16 @@ func (ca *CA) Init(config *config.Config) (*CA, error) {
acmeHandler.Route(r)
})
// MGMT Router
mgmtDB := auth.GetMgmtDatabase()
if mgmtDB != nil {
mgmtHandler := mgmtAPI.NewHandler(mgmtDB)
mux.Route("/mgmt", func(r chi.Router) {
mgmtHandler.Route(r)
})
}
// helpful routine for logging all routes //
/*
walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {

View file

@ -60,10 +60,10 @@ func (c *MgmtClient) retryOnError(r *http.Response) bool {
return false
}
// GetAdmin performs the GET /config/admin/{id} request to the CA.
// GetAdmin performs the GET /mgmt/admin/{id} request to the CA.
func (c *MgmtClient) GetAdmin(id string) (*mgmt.Admin, error) {
var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/config/admin", id)})
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)})
retry:
resp, err := c.client.Get(u.String())
if err != nil {
@ -83,10 +83,10 @@ retry:
return adm, nil
}
// GetAdmins performs the GET /config/admins request to the CA.
// GetAdmins performs the GET /mgmt/admins request to the CA.
func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) {
var retried bool
u := c.endpoint.ResolveReference(&url.URL{Path: "/config/admins"})
u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admins"})
retry:
resp, err := c.client.Get(u.String())
if err != nil {