Merge pull request #915 from smallstep/max/removing-beta

exposing authority configuration for provisioner cli commands
pull/943/head
Max 2 years ago committed by GitHub
commit f8148071fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,7 +33,7 @@ jobs:
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
with: with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: 'v1.45.0' version: 'v1.45.2'
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir

@ -33,7 +33,7 @@ jobs:
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
with: with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: 'v1.45.0' version: 'v1.45.2'
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir

@ -151,7 +151,7 @@ integration: bin/$(BINNAME)
######################################### #########################################
fmt: fmt:
$Q gofmt -l -w $(SRC) $Q gofmt -l -s -w $(SRC)
lint: lint:
$Q golangci-lint run --timeout=30m $Q golangci-lint run --timeout=30m

@ -49,7 +49,7 @@ func (a *Authority) StoreAdmin(ctx context.Context, adm *linkedca.Admin, prov pr
return admin.WrapErrorISE(err, "error creating admin") return admin.WrapErrorISE(err, "error creating admin")
} }
if err := a.admins.Store(adm, prov); err != nil { if err := a.admins.Store(adm, prov); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed admin store")
} }
return admin.WrapErrorISE(err, "error storing admin in authority cache") return admin.WrapErrorISE(err, "error storing admin in authority cache")
@ -66,7 +66,7 @@ func (a *Authority) UpdateAdmin(ctx context.Context, id string, nu *linkedca.Adm
return nil, admin.WrapErrorISE(err, "error updating cached admin %s", id) return nil, admin.WrapErrorISE(err, "error updating cached admin %s", id)
} }
if err := a.adminDB.UpdateAdmin(ctx, adm); err != nil { if err := a.adminDB.UpdateAdmin(ctx, adm); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed admin update")
} }
return nil, admin.WrapErrorISE(err, "error updating admin %s", id) return nil, admin.WrapErrorISE(err, "error updating admin %s", id)
@ -88,7 +88,7 @@ func (a *Authority) removeAdmin(ctx context.Context, id string) error {
return admin.WrapErrorISE(err, "error removing admin %s from authority cache", id) return admin.WrapErrorISE(err, "error removing admin %s from authority cache", id)
} }
if err := a.adminDB.DeleteAdmin(ctx, id); err != nil { if err := a.adminDB.DeleteAdmin(ctx, id); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed admin remove")
} }
return admin.WrapErrorISE(err, "error deleting admin %s", id) return admin.WrapErrorISE(err, "error deleting admin %s", id)

@ -84,8 +84,12 @@ type Authority struct {
policyEngine *policy.Engine policyEngine *policy.Engine
adminMutex sync.RWMutex adminMutex sync.RWMutex
// Do Not initialize the authority
skipInit bool
} }
// Info contains information about the authority.
type Info struct { type Info struct {
StartTime time.Time StartTime time.Time
RootX509Certs []*x509.Certificate RootX509Certs []*x509.Certificate
@ -113,9 +117,11 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
} }
} }
// Initialize authority from options or configuration. if !a.skipInit {
if err := a.init(); err != nil { // Initialize authority from options or configuration.
return nil, err if err := a.init(); err != nil {
return nil, err
}
} }
return a, nil return a, nil
@ -151,16 +157,18 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
// Initialize config required fields. // Initialize config required fields.
a.config.Init() a.config.Init()
// Initialize authority from options or configuration. if !a.skipInit {
if err := a.init(); err != nil { // Initialize authority from options or configuration.
return nil, err if err := a.init(); err != nil {
return nil, err
}
} }
return a, nil return a, nil
} }
// reloadAdminResources reloads admins and provisioners from the DB. // ReloadAdminResources reloads admins and provisioners from the DB.
func (a *Authority) reloadAdminResources(ctx context.Context) error { func (a *Authority) ReloadAdminResources(ctx context.Context) error {
var ( var (
provList provisioner.List provList provisioner.List
adminList []*linkedca.Admin adminList []*linkedca.Admin
@ -558,7 +566,7 @@ func (a *Authority) init() error {
} }
// Load Provisioners and Admins // Load Provisioners and Admins
if err := a.reloadAdminResources(context.Background()); err != nil { if err := a.ReloadAdminResources(context.Background()); err != nil {
return err return err
} }
@ -599,6 +607,12 @@ func (a *Authority) GetAdminDatabase() admin.DB {
return a.adminDB return a.adminDB
} }
// GetConfig returns the config.
func (a *Authority) GetConfig() *config.Config {
return a.config
}
// GetInfo returns information about the authority.
func (a *Authority) GetInfo() Info { func (a *Authority) GetInfo() Info {
ai := Info{ ai := Info{
StartTime: a.startTime, StartTime: a.startTime,

@ -266,6 +266,16 @@ func WithAdminDB(d admin.DB) Option {
} }
} }
// WithProvisioners is an option to set the provisioner collection.
//
// Deprecated: provisioner collections will likely change
func WithProvisioners(ps *provisioner.Collection) Option {
return func(a *Authority) error {
a.provisioners = ps
return nil
}
}
// WithLinkedCAToken is an option to set the authentication token used to enable // WithLinkedCAToken is an option to set the authentication token used to enable
// linked ca. // linked ca.
func WithLinkedCAToken(token string) Option { func WithLinkedCAToken(token string) Option {
@ -284,6 +294,15 @@ func WithX509Enforcers(ces ...provisioner.CertificateEnforcer) Option {
} }
} }
// WithSkipInit is an option that allows the constructor to skip initializtion
// of the authority.
func WithSkipInit() Option {
return func(a *Authority) error {
a.skipInit = true
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

@ -148,7 +148,7 @@ func (a *Authority) generateProvisionerConfig(ctx context.Context) (provisioner.
} }
// StoreProvisioner stores an provisioner.Interface to the authority. // StoreProvisioner stores a provisioner to the authority.
func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisioner) error {
a.adminMutex.Lock() a.adminMutex.Lock()
defer a.adminMutex.Unlock() defer a.adminMutex.Unlock()
@ -198,7 +198,7 @@ func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisi
} }
if err := a.provisioners.Store(certProv); err != nil { if err := a.provisioners.Store(certProv); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed provisioner store")
} }
return admin.WrapErrorISE(err, "error storing provisioner in authority cache") return admin.WrapErrorISE(err, "error storing provisioner in authority cache")
@ -234,7 +234,7 @@ func (a *Authority) UpdateProvisioner(ctx context.Context, nu *linkedca.Provisio
return admin.WrapErrorISE(err, "error updating provisioner '%s' in authority cache", nu.Name) 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.adminDB.UpdateProvisioner(ctx, nu); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed provisioner update")
} }
return admin.WrapErrorISE(err, "error updating provisioner '%s'", nu.Name) return admin.WrapErrorISE(err, "error updating provisioner '%s'", nu.Name)
@ -254,31 +254,33 @@ func (a *Authority) RemoveProvisioner(ctx context.Context, id string) error {
} }
provName, provID := p.GetName(), p.GetID() provName, provID := p.GetName(), p.GetID()
// Validate if a.IsAdminAPIEnabled() {
// - Check that there will be SUPER_ADMINs that remain after we // Validate
// remove this provisioner. // - Check that there will be SUPER_ADMINs that remain after we
if a.admins.SuperCount() == a.admins.SuperCountByProvisioner(provName) { // remove this provisioner.
return admin.NewError(admin.ErrorBadRequestType, if a.IsAdminAPIEnabled() && a.admins.SuperCount() == a.admins.SuperCountByProvisioner(provName) {
"cannot remove provisioner %s because no super admins will remain", provName) return admin.NewError(admin.ErrorBadRequestType,
} "cannot remove provisioner %s because no super admins will remain", provName)
}
// Delete all admins associated with the provisioner. // Delete all admins associated with the provisioner.
admins, ok := a.admins.LoadByProvisioner(provName) admins, ok := a.admins.LoadByProvisioner(provName)
if ok { if ok {
for _, adm := range admins { for _, adm := range admins {
if err := a.removeAdmin(ctx, adm.Id); err != nil { 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) return admin.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, provName)
}
} }
} }
} }
// Remove provisioner from authority caches. // Remove provisioner from authority caches.
if err := a.provisioners.Remove(provID); err != nil { if err := a.provisioners.Remove(provID); err != nil {
return admin.WrapErrorISE(err, "error removing admin from authority cache") return admin.WrapErrorISE(err, "error removing provisioner from authority cache")
} }
// Remove provisioner from database. // Remove provisioner from database.
if err := a.adminDB.DeleteProvisioner(ctx, provID); err != nil { if err := a.adminDB.DeleteProvisioner(ctx, provID); err != nil {
if err := a.reloadAdminResources(ctx); 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 reloading admin resources on failed provisioner remove")
} }
return admin.WrapErrorISE(err, "error deleting provisioner %s", provName) return admin.WrapErrorISE(err, "error deleting provisioner %s", provName)

@ -366,19 +366,19 @@ retry:
// GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. // GetProvisioner performs the GET /admin/provisioners/{name} request to the CA.
func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provisioner, error) { func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provisioner, 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 {
return nil, err return nil, err
} }
var u *url.URL var u *url.URL
switch { switch {
case len(o.id) > 0: case o.ID != "":
u = c.endpoint.ResolveReference(&url.URL{ u = c.endpoint.ResolveReference(&url.URL{
Path: "/admin/provisioners/id", Path: "/admin/provisioners/id",
RawQuery: o.rawQuery(), RawQuery: o.rawQuery(),
}) })
case len(o.name) > 0: case o.Name != "":
u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.Name)})
default: default:
return nil, errors.New("must set either name or id in method options") return nil, errors.New("must set either name or id in method options")
} }
@ -413,8 +413,8 @@ retry:
// 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) (*adminAPI.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 {
return nil, err return nil, err
} }
u := c.endpoint.ResolveReference(&url.URL{ u := c.endpoint.ResolveReference(&url.URL{
@ -475,19 +475,19 @@ func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error {
retried bool retried bool
) )
o := new(provisionerOptions) o := new(ProvisionerOptions)
if err := o.apply(opts); err != nil { if err := o.Apply(opts); err != nil {
return err return err
} }
switch { switch {
case len(o.id) > 0: case o.ID != "":
u = c.endpoint.ResolveReference(&url.URL{ u = c.endpoint.ResolveReference(&url.URL{
Path: path.Join(adminURLPrefix, "provisioners/id"), Path: path.Join(adminURLPrefix, "provisioners/id"),
RawQuery: o.rawQuery(), RawQuery: o.rawQuery(),
}) })
case len(o.name) > 0: case o.Name != "":
u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.Name)})
default: default:
return errors.New("must set either name or id in method options") return errors.New("must set either name or id in method options")
} }

@ -425,16 +425,18 @@ func parseEndpoint(endpoint string) (*url.URL, error) {
} }
// ProvisionerOption is the type of options passed to the Provisioner method. // ProvisionerOption is the type of options passed to the Provisioner method.
type ProvisionerOption func(o *provisionerOptions) error type ProvisionerOption func(o *ProvisionerOptions) error
type provisionerOptions struct { // ProvisionerOptions stores options for the provisioner CRUD API.
cursor string type ProvisionerOptions struct {
limit int Cursor string
id string Limit int
name string ID string
Name string
} }
func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) { // Apply caches provisioner options on a struct for later use.
func (o *ProvisionerOptions) Apply(opts []ProvisionerOption) (err error) {
for _, fn := range opts { for _, fn := range opts {
if err = fn(o); err != nil { if err = fn(o); err != nil {
return return
@ -443,51 +445,51 @@ func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) {
return return
} }
func (o *provisionerOptions) rawQuery() string { func (o *ProvisionerOptions) rawQuery() string {
v := url.Values{} v := url.Values{}
if len(o.cursor) > 0 { if o.Cursor != "" {
v.Set("cursor", o.cursor) v.Set("cursor", o.Cursor)
} }
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 { if o.ID != "" {
v.Set("id", o.id) v.Set("id", o.ID)
} }
if len(o.name) > 0 { if o.Name != "" {
v.Set("name", o.name) v.Set("name", o.Name)
} }
return v.Encode() return v.Encode()
} }
// WithProvisionerCursor will request the provisioners starting with the given cursor. // WithProvisionerCursor will request the provisioners starting with the given cursor.
func WithProvisionerCursor(cursor string) ProvisionerOption { func WithProvisionerCursor(cursor string) ProvisionerOption {
return func(o *provisionerOptions) error { return func(o *ProvisionerOptions) error {
o.cursor = cursor o.Cursor = cursor
return nil return nil
} }
} }
// WithProvisionerLimit will request the given number of provisioners. // WithProvisionerLimit will request the given number of provisioners.
func WithProvisionerLimit(limit int) ProvisionerOption { func WithProvisionerLimit(limit int) ProvisionerOption {
return func(o *provisionerOptions) error { return func(o *ProvisionerOptions) error {
o.limit = limit o.Limit = limit
return nil return nil
} }
} }
// WithProvisionerID will request the given provisioner. // WithProvisionerID will request the given provisioner.
func WithProvisionerID(id string) ProvisionerOption { func WithProvisionerID(id string) ProvisionerOption {
return func(o *provisionerOptions) error { return func(o *ProvisionerOptions) error {
o.id = id o.ID = id
return nil return nil
} }
} }
// WithProvisionerName will request the given provisioner. // WithProvisionerName will request the given provisioner.
func WithProvisionerName(name string) ProvisionerOption { func WithProvisionerName(name string) ProvisionerOption {
return func(o *provisionerOptions) error { return func(o *ProvisionerOptions) error {
o.name = name o.Name = name
return nil return nil
} }
} }
@ -810,8 +812,8 @@ retry:
// paginate the provisioners. // paginate the provisioners.
func (c *Client) Provisioners(opts ...ProvisionerOption) (*api.ProvisionersResponse, error) { func (c *Client) Provisioners(opts ...ProvisionerOption) (*api.ProvisionersResponse, 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 {
return nil, err return nil, err
} }
u := c.endpoint.ResolveReference(&url.URL{ u := c.endpoint.ResolveReference(&url.URL{

@ -654,7 +654,7 @@ preferably not all - meaning it never leaves the server on which it was created.
### Passwords ### Passwords
When you intialize your PKI (`step ca init`) the root and intermediate When you initialize your PKI (`step ca init`) the root and intermediate
private keys will be encrypted with the same password. We recommend that you private keys will be encrypted with the same password. We recommend that you
change the password with which the intermediate is encrypted at your earliest change the password with which the intermediate is encrypted at your earliest
convenience. convenience.
@ -681,7 +681,7 @@ to divide the root private key password across a handful of trusted parties.
### Provisioners ### Provisioners
When you intialize your PKI (`step ca init`) a default provisioner will be created When you initialize your PKI (`step ca init`) a default provisioner will be created
and it's private key will be encrypted using the same password used to encrypt and it's private key will be encrypted using the same password used to encrypt
the root private key. Before deploying the Step CA you should remove this the root private key. Before deploying the Step CA you should remove this
provisioner and add new ones that are encrypted with new, secure, random passwords. provisioner and add new ones that are encrypted with new, secure, random passwords.

Loading…
Cancel
Save