forked from TrueCloudLab/certificates
Add API implementation for authority and provisioner policy
This commit is contained in:
parent
3ec9a7310c
commit
81b0c6c37c
19 changed files with 883 additions and 43 deletions
|
@ -66,6 +66,13 @@ func JSONStatus(w http.ResponseWriter, v interface{}, status int) {
|
||||||
LogEnabledResponse(w, v)
|
LogEnabledResponse(w, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSONNotFound writes a HTTP Not Found response with empty body.
|
||||||
|
func JSONNotFound(w http.ResponseWriter) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
LogEnabledResponse(w, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// ProtoJSON writes the passed value into the http.ResponseWriter.
|
// ProtoJSON writes the passed value into the http.ResponseWriter.
|
||||||
func ProtoJSON(w http.ResponseWriter, m proto.Message) {
|
func ProtoJSON(w http.ResponseWriter, m proto.Message) {
|
||||||
ProtoJSONStatus(w, m, http.StatusOK)
|
ProtoJSONStatus(w, m, http.StatusOK)
|
||||||
|
|
|
@ -25,6 +25,10 @@ type adminAuthority interface {
|
||||||
LoadProvisionerByID(id string) (provisioner.Interface, error)
|
LoadProvisionerByID(id string) (provisioner.Interface, error)
|
||||||
UpdateProvisioner(ctx context.Context, nu *linkedca.Provisioner) error
|
UpdateProvisioner(ctx context.Context, nu *linkedca.Provisioner) error
|
||||||
RemoveProvisioner(ctx context.Context, id string) error
|
RemoveProvisioner(ctx context.Context, id string) error
|
||||||
|
GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error)
|
||||||
|
StoreAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
RemoveAuthorityPolicy(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAdminRequest represents the body for a CreateAdmin request.
|
// CreateAdminRequest represents the body for a CreateAdmin request.
|
||||||
|
|
|
@ -37,6 +37,11 @@ type mockAdminAuthority struct {
|
||||||
MockLoadProvisionerByID func(id string) (provisioner.Interface, error)
|
MockLoadProvisionerByID func(id string) (provisioner.Interface, error)
|
||||||
MockUpdateProvisioner func(ctx context.Context, nu *linkedca.Provisioner) error
|
MockUpdateProvisioner func(ctx context.Context, nu *linkedca.Provisioner) error
|
||||||
MockRemoveProvisioner func(ctx context.Context, id string) error
|
MockRemoveProvisioner func(ctx context.Context, id string) error
|
||||||
|
|
||||||
|
MockGetAuthorityPolicy func(ctx context.Context) (*linkedca.Policy, error)
|
||||||
|
MockStoreAuthorityPolicy func(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
MockUpdateAuthorityPolicy func(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
MockRemoveAuthorityPolicy func(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAdminAuthority) IsAdminAPIEnabled() bool {
|
func (m *mockAdminAuthority) IsAdminAPIEnabled() bool {
|
||||||
|
@ -130,6 +135,22 @@ func (m *mockAdminAuthority) RemoveProvisioner(ctx context.Context, id string) e
|
||||||
return m.MockErr
|
return m.MockErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockAdminAuthority) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
||||||
|
return nil, errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAdminAuthority) StoreAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAdminAuthority) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAdminAuthority) RemoveAuthorityPolicy(ctx context.Context) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateAdminRequest_Validate(t *testing.T) {
|
func TestCreateAdminRequest_Validate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Subject string
|
Subject string
|
||||||
|
|
|
@ -8,32 +8,44 @@ import (
|
||||||
|
|
||||||
// Handler is the Admin API request handler.
|
// Handler is the Admin API request handler.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
adminDB admin.DB
|
adminDB admin.DB
|
||||||
auth adminAuthority
|
auth adminAuthority
|
||||||
acmeDB acme.DB
|
acmeDB acme.DB
|
||||||
acmeResponder acmeAdminResponderInterface
|
acmeResponder acmeAdminResponderInterface
|
||||||
|
policyResponder policyAdminResponderInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHandler returns a new Authority Config Handler.
|
// NewHandler returns a new Authority Config Handler.
|
||||||
func NewHandler(auth adminAuthority, adminDB admin.DB, acmeDB acme.DB, acmeResponder acmeAdminResponderInterface) api.RouterHandler {
|
func NewHandler(auth adminAuthority, adminDB admin.DB, acmeDB acme.DB, acmeResponder acmeAdminResponderInterface, policyResponder policyAdminResponderInterface) api.RouterHandler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
auth: auth,
|
auth: auth,
|
||||||
adminDB: adminDB,
|
adminDB: adminDB,
|
||||||
acmeDB: acmeDB,
|
acmeDB: acmeDB,
|
||||||
acmeResponder: acmeResponder,
|
acmeResponder: acmeResponder,
|
||||||
|
policyResponder: policyResponder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|
||||||
authnz := func(next nextHTTP) nextHTTP {
|
authnz := func(next nextHTTP) nextHTTP {
|
||||||
return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next))
|
//return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next))
|
||||||
|
return h.requireAPIEnabled(next) // TODO(hs): remove this; temporarily no auth checks for simple testing...
|
||||||
}
|
}
|
||||||
|
|
||||||
requireEABEnabled := func(next nextHTTP) nextHTTP {
|
requireEABEnabled := func(next nextHTTP) nextHTTP {
|
||||||
return h.requireEABEnabled(next)
|
return h.requireEABEnabled(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enabledInStandalone := func(next nextHTTP) nextHTTP {
|
||||||
|
return h.checkAction(next, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledInStandalone := func(next nextHTTP) nextHTTP {
|
||||||
|
return h.checkAction(next, false)
|
||||||
|
}
|
||||||
|
|
||||||
// Provisioners
|
// Provisioners
|
||||||
r.MethodFunc("GET", "/provisioners/{name}", authnz(h.GetProvisioner))
|
r.MethodFunc("GET", "/provisioners/{name}", authnz(h.GetProvisioner))
|
||||||
r.MethodFunc("GET", "/provisioners", authnz(h.GetProvisioners))
|
r.MethodFunc("GET", "/provisioners", authnz(h.GetProvisioners))
|
||||||
|
@ -53,4 +65,24 @@ func (h *Handler) Route(r api.Router) {
|
||||||
r.MethodFunc("GET", "/acme/eab/{provisionerName}", authnz(requireEABEnabled(h.acmeResponder.GetExternalAccountKeys)))
|
r.MethodFunc("GET", "/acme/eab/{provisionerName}", authnz(requireEABEnabled(h.acmeResponder.GetExternalAccountKeys)))
|
||||||
r.MethodFunc("POST", "/acme/eab/{provisionerName}", authnz(requireEABEnabled(h.acmeResponder.CreateExternalAccountKey)))
|
r.MethodFunc("POST", "/acme/eab/{provisionerName}", authnz(requireEABEnabled(h.acmeResponder.CreateExternalAccountKey)))
|
||||||
r.MethodFunc("DELETE", "/acme/eab/{provisionerName}/{id}", authnz(requireEABEnabled(h.acmeResponder.DeleteExternalAccountKey)))
|
r.MethodFunc("DELETE", "/acme/eab/{provisionerName}/{id}", authnz(requireEABEnabled(h.acmeResponder.DeleteExternalAccountKey)))
|
||||||
|
|
||||||
|
// Policy - Authority
|
||||||
|
r.MethodFunc("GET", "/policy", authnz(enabledInStandalone(h.policyResponder.GetAuthorityPolicy)))
|
||||||
|
r.MethodFunc("POST", "/policy", authnz(enabledInStandalone(h.policyResponder.CreateAuthorityPolicy)))
|
||||||
|
r.MethodFunc("PUT", "/policy", authnz(enabledInStandalone(h.policyResponder.UpdateAuthorityPolicy)))
|
||||||
|
r.MethodFunc("DELETE", "/policy", authnz(enabledInStandalone(h.policyResponder.DeleteAuthorityPolicy)))
|
||||||
|
|
||||||
|
// Policy - Provisioner
|
||||||
|
//r.MethodFunc("GET", "/provisioners/{name}/policy", noauth(h.policyResponder.GetProvisionerPolicy))
|
||||||
|
r.MethodFunc("GET", "/provisioners/{name}/policy", authnz(disabledInStandalone(h.policyResponder.GetProvisionerPolicy)))
|
||||||
|
r.MethodFunc("POST", "/provisioners/{name}/policy", authnz(disabledInStandalone(h.policyResponder.CreateProvisionerPolicy)))
|
||||||
|
r.MethodFunc("PUT", "/provisioners/{name}/policy", authnz(disabledInStandalone(h.policyResponder.UpdateProvisionerPolicy)))
|
||||||
|
r.MethodFunc("DELETE", "/provisioners/{name}/policy", authnz(disabledInStandalone(h.policyResponder.DeleteProvisionerPolicy)))
|
||||||
|
|
||||||
|
// Policy - ACME Account
|
||||||
|
// TODO: ensure we don't clash with eab; might want to change eab paths slightly (as long as we don't have it released completely; needs changes in adminClient too)
|
||||||
|
r.MethodFunc("GET", "/acme/{provisionerName}/{accountID}/policy", authnz(disabledInStandalone(h.policyResponder.GetACMEAccountPolicy)))
|
||||||
|
r.MethodFunc("POST", "/acme/{provisionerName}/{accountID}/policy", authnz(disabledInStandalone(h.policyResponder.CreateACMEAccountPolicy)))
|
||||||
|
r.MethodFunc("PUT", "/acme/{provisionerName}/{accountID}/policy", authnz(disabledInStandalone(h.policyResponder.UpdateACMEAccountPolicy)))
|
||||||
|
r.MethodFunc("DELETE", "/acme/{provisionerName}/{accountID}/policy", authnz(disabledInStandalone(h.policyResponder.DeleteACMEAccountPolicy)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"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/admin/db/nosql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type nextHTTP = func(http.ResponseWriter, *http.Request)
|
type nextHTTP = func(http.ResponseWriter, *http.Request)
|
||||||
|
@ -44,6 +45,28 @@ func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkAction checks if an action is supported in standalone or not
|
||||||
|
func (h *Handler) checkAction(next nextHTTP, supportedInStandalone bool) nextHTTP {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// actions allowed in standalone mode are always allowed
|
||||||
|
if supportedInStandalone {
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// when in standalone mode, actions are not supported
|
||||||
|
if _, ok := h.adminDB.(*nosql.DB); ok {
|
||||||
|
api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType,
|
||||||
|
"operation not supported in standalone mode"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue to next http handler
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ContextKey is the key type for storing and searching for ACME request
|
// ContextKey is the key type for storing and searching for ACME request
|
||||||
// essentials in the context of a request.
|
// essentials in the context of a request.
|
||||||
type ContextKey string
|
type ContextKey string
|
||||||
|
|
313
authority/admin/api/policy.go
Normal file
313
authority/admin/api/policy.go
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/smallstep/certificates/api"
|
||||||
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
|
)
|
||||||
|
|
||||||
|
type policyAdminResponderInterface interface {
|
||||||
|
GetAuthorityPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
CreateAuthorityPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
UpdateAuthorityPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
DeleteAuthorityPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
GetProvisionerPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
CreateProvisionerPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
UpdateProvisionerPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
DeleteProvisionerPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
GetACMEAccountPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
CreateACMEAccountPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
UpdateACMEAccountPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
DeleteACMEAccountPolicy(w http.ResponseWriter, r *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyAdminResponder is responsible for writing ACME admin responses
|
||||||
|
type PolicyAdminResponder struct {
|
||||||
|
auth adminAuthority
|
||||||
|
adminDB admin.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewACMEAdminResponder returns a new ACMEAdminResponder
|
||||||
|
func NewPolicyAdminResponder(auth adminAuthority, adminDB admin.DB) *PolicyAdminResponder {
|
||||||
|
return &PolicyAdminResponder{
|
||||||
|
auth: auth,
|
||||||
|
adminDB: adminDB,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorityPolicy handles the GET /admin/authority/policy request
|
||||||
|
func (par *PolicyAdminResponder) GetAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
policy, err := par.auth.GetAuthorityPolicy(r.Context())
|
||||||
|
if ae, ok := err.(*admin.Error); ok {
|
||||||
|
if !ae.IsType(admin.ErrorNotFoundType) {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy == nil {
|
||||||
|
api.JSONNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.ProtoJSONStatus(w, policy, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAuthorityPolicy handles the POST /admin/authority/policy request
|
||||||
|
func (par *PolicyAdminResponder) CreateAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
policy, err := par.auth.GetAuthorityPolicy(ctx)
|
||||||
|
|
||||||
|
shouldWriteError := false
|
||||||
|
if ae, ok := err.(*admin.Error); ok {
|
||||||
|
shouldWriteError = !ae.IsType(admin.ErrorNotFoundType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldWriteError {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error retrieving authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy != nil {
|
||||||
|
adminErr := admin.NewError(admin.ErrorBadRequestType, "authority already has a policy")
|
||||||
|
adminErr.Status = http.StatusConflict
|
||||||
|
api.WriteError(w, adminErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPolicy = new(linkedca.Policy)
|
||||||
|
if err := api.ReadProtoJSON(r.Body, newPolicy); err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := par.auth.StoreAuthorityPolicy(ctx, newPolicy); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error storing authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storedPolicy, err := par.auth.GetAuthorityPolicy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error retrieving authority policy after updating"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.JSONStatus(w, storedPolicy, http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAuthorityPolicy handles the PUT /admin/authority/policy request
|
||||||
|
func (par *PolicyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var policy = new(linkedca.Policy)
|
||||||
|
if err := api.ReadProtoJSON(r.Body, policy); err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
if err := par.auth.UpdateAuthorityPolicy(ctx, policy); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error updating authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPolicy, err := par.auth.GetAuthorityPolicy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error retrieving authority policy after updating"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.ProtoJSONStatus(w, newPolicy, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAuthorityPolicy handles the DELETE /admin/authority/policy request
|
||||||
|
func (par *PolicyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
policy, err := par.auth.GetAuthorityPolicy(ctx)
|
||||||
|
|
||||||
|
if ae, ok := err.(*admin.Error); ok {
|
||||||
|
if !ae.IsType(admin.ErrorNotFoundType) {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy == nil {
|
||||||
|
api.JSONNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = par.auth.RemoveAuthorityPolicy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error deleting authority policy"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProvisionerPolicy handles the GET /admin/provisioners/{name}/policy request
|
||||||
|
func (par *PolicyAdminResponder) GetProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO: move getting provisioner to middleware?
|
||||||
|
ctx := r.Context()
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
var (
|
||||||
|
p provisioner.Interface
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if p, err = par.auth.LoadProvisionerByName(name); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov, err := par.adminDB.GetProvisioner(ctx, p.GetID())
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := prov.GetPolicy()
|
||||||
|
if policy == nil {
|
||||||
|
api.JSONNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.ProtoJSONStatus(w, policy, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProvisionerPolicy handles the POST /admin/provisioners/{name}/policy request
|
||||||
|
func (par *PolicyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
var (
|
||||||
|
p provisioner.Interface
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if p, err = par.auth.LoadProvisionerByName(name); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov, err := par.adminDB.GetProvisioner(ctx, p.GetID())
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := prov.GetPolicy()
|
||||||
|
if policy != nil {
|
||||||
|
adminErr := admin.NewError(admin.ErrorBadRequestType, "provisioner %s already has a policy", name)
|
||||||
|
adminErr.Status = http.StatusConflict
|
||||||
|
api.WriteError(w, adminErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPolicy = new(linkedca.Policy)
|
||||||
|
if err := api.ReadProtoJSON(r.Body, newPolicy); err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov.Policy = newPolicy
|
||||||
|
|
||||||
|
err = par.auth.UpdateProvisioner(ctx, prov)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.ProtoJSONStatus(w, newPolicy, http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProvisionerPolicy handles the PUT /admin/provisioners/{name}/policy request
|
||||||
|
func (par *PolicyAdminResponder) UpdateProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
var (
|
||||||
|
p provisioner.Interface
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if p, err = par.auth.LoadProvisionerByName(name); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov, err := par.adminDB.GetProvisioner(ctx, p.GetID())
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var policy = new(linkedca.Policy)
|
||||||
|
if err := api.ReadProtoJSON(r.Body, policy); err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov.Policy = policy
|
||||||
|
err = par.auth.UpdateProvisioner(ctx, prov)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.ProtoJSONStatus(w, policy, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProvisionerPolicy ...
|
||||||
|
func (par *PolicyAdminResponder) DeleteProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
var (
|
||||||
|
p provisioner.Interface
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if p, err = par.auth.LoadProvisionerByName(name); err != nil {
|
||||||
|
api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prov, err := par.adminDB.GetProvisioner(ctx, p.GetID())
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if prov.Policy == nil {
|
||||||
|
api.JSONNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the policy
|
||||||
|
prov.Policy = nil
|
||||||
|
|
||||||
|
err = par.auth.UpdateProvisioner(ctx, prov)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
api.JSON(w, &DeleteResponse{Status: "ok"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetACMEAccountPolicy ...
|
||||||
|
func (par *PolicyAdminResponder) GetACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.JSON(w, "ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (par *PolicyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.JSON(w, "ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (par *PolicyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.JSON(w, "ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (par *PolicyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.JSON(w, "ok")
|
||||||
|
}
|
|
@ -69,6 +69,11 @@ type DB interface {
|
||||||
GetAdmins(ctx context.Context) ([]*linkedca.Admin, error)
|
GetAdmins(ctx context.Context) ([]*linkedca.Admin, error)
|
||||||
UpdateAdmin(ctx context.Context, admin *linkedca.Admin) error
|
UpdateAdmin(ctx context.Context, admin *linkedca.Admin) error
|
||||||
DeleteAdmin(ctx context.Context, id string) error
|
DeleteAdmin(ctx context.Context, id string) error
|
||||||
|
|
||||||
|
CreateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error)
|
||||||
|
UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
DeleteAuthorityPolicy(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockDB is an implementation of the DB interface that should only be used as
|
// MockDB is an implementation of the DB interface that should only be used as
|
||||||
|
@ -86,6 +91,11 @@ type MockDB struct {
|
||||||
MockUpdateAdmin func(ctx context.Context, adm *linkedca.Admin) error
|
MockUpdateAdmin func(ctx context.Context, adm *linkedca.Admin) error
|
||||||
MockDeleteAdmin func(ctx context.Context, id string) error
|
MockDeleteAdmin func(ctx context.Context, id string) error
|
||||||
|
|
||||||
|
MockCreateAuthorityPolicy func(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
MockGetAuthorityPolicy func(ctx context.Context) (*linkedca.Policy, error)
|
||||||
|
MockUpdateAuthorityPolicy func(ctx context.Context, policy *linkedca.Policy) error
|
||||||
|
MockDeleteAuthorityPolicy func(ctx context.Context) error
|
||||||
|
|
||||||
MockError error
|
MockError error
|
||||||
MockRet1 interface{}
|
MockRet1 interface{}
|
||||||
}
|
}
|
||||||
|
@ -179,3 +189,30 @@ func (m *MockDB) DeleteAdmin(ctx context.Context, id string) error {
|
||||||
}
|
}
|
||||||
return m.MockError
|
return m.MockError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockDB) CreateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
if m.MockCreateAuthorityPolicy != nil {
|
||||||
|
return m.MockCreateAuthorityPolicy(ctx, policy)
|
||||||
|
}
|
||||||
|
return m.MockError
|
||||||
|
}
|
||||||
|
func (m *MockDB) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
||||||
|
if m.MockGetAuthorityPolicy != nil {
|
||||||
|
return m.MockGetAuthorityPolicy(ctx)
|
||||||
|
}
|
||||||
|
return m.MockRet1.(*linkedca.Policy), m.MockError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockDB) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
if m.MockUpdateAuthorityPolicy != nil {
|
||||||
|
return m.MockUpdateAuthorityPolicy(ctx, policy)
|
||||||
|
}
|
||||||
|
return m.MockError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockDB) DeleteAuthorityPolicy(ctx context.Context) error {
|
||||||
|
if m.MockDeleteAuthorityPolicy != nil {
|
||||||
|
return m.MockDeleteAuthorityPolicy(ctx)
|
||||||
|
}
|
||||||
|
return m.MockError
|
||||||
|
}
|
||||||
|
|
|
@ -11,8 +11,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
adminsTable = []byte("admins")
|
adminsTable = []byte("admins")
|
||||||
provisionersTable = []byte("provisioners")
|
provisionersTable = []byte("provisioners")
|
||||||
|
authorityPoliciesTable = []byte("authority_policies")
|
||||||
)
|
)
|
||||||
|
|
||||||
// DB is a struct that implements the AdminDB interface.
|
// DB is a struct that implements the AdminDB interface.
|
||||||
|
@ -23,7 +24,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{adminsTable, provisionersTable}
|
tables := [][]byte{adminsTable, provisionersTable, authorityPoliciesTable}
|
||||||
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",
|
||||||
|
|
144
authority/admin/db/nosql/policy.go
Normal file
144
authority/admin/db/nosql/policy.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package nosql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
|
"github.com/smallstep/nosql"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbAuthorityPolicy struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
AuthorityID string `json:"authorityID"`
|
||||||
|
Policy *linkedca.Policy `json:"policy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbap *dbAuthorityPolicy) convert() *linkedca.Policy {
|
||||||
|
return dbap.Policy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbap *dbAuthorityPolicy) clone() *dbAuthorityPolicy {
|
||||||
|
u := *dbap
|
||||||
|
return &u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) getDBAuthorityPolicyBytes(ctx context.Context, authorityID string) ([]byte, error) {
|
||||||
|
data, err := db.db.Get(authorityPoliciesTable, []byte(authorityID))
|
||||||
|
if nosql.IsErrNotFound(err) {
|
||||||
|
return nil, admin.NewError(admin.ErrorNotFoundType, "policy %s not found", authorityID)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error loading admin %s", authorityID)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) unmarshalDBAuthorityPolicy(data []byte, authorityID string) (*dbAuthorityPolicy, error) {
|
||||||
|
var dba = new(dbAuthorityPolicy)
|
||||||
|
if err := json.Unmarshal(data, dba); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", authorityID)
|
||||||
|
}
|
||||||
|
// if !dba.DeletedAt.IsZero() {
|
||||||
|
// return nil, admin.NewError(admin.ErrorDeletedType, "admin %s is deleted", authorityID)
|
||||||
|
// }
|
||||||
|
if dba.AuthorityID != db.authorityID {
|
||||||
|
return nil, admin.NewError(admin.ErrorAuthorityMismatchType,
|
||||||
|
"admin %s is not owned by authority %s", dba.ID, db.authorityID)
|
||||||
|
}
|
||||||
|
return dba, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) getDBAuthorityPolicy(ctx context.Context, authorityID string) (*dbAuthorityPolicy, error) {
|
||||||
|
data, err := db.getDBAuthorityPolicyBytes(ctx, authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbap, err := db.unmarshalDBAuthorityPolicy(data, authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dbap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) unmarshalAuthorityPolicy(data []byte, authorityID string) (*linkedca.Policy, error) {
|
||||||
|
dbap, err := db.unmarshalDBAuthorityPolicy(data, authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dbap.convert(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) CreateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
|
||||||
|
dbap := &dbAuthorityPolicy{
|
||||||
|
ID: db.authorityID,
|
||||||
|
AuthorityID: db.authorityID,
|
||||||
|
Policy: policy,
|
||||||
|
}
|
||||||
|
|
||||||
|
old, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.save(ctx, dbap.ID, dbap, old, "authority_policy", authorityPoliciesTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
||||||
|
// policy := &linkedca.Policy{
|
||||||
|
// X509: &linkedca.X509Policy{
|
||||||
|
// Allow: &linkedca.X509Names{
|
||||||
|
// Dns: []string{".localhost"},
|
||||||
|
// },
|
||||||
|
// Deny: &linkedca.X509Names{
|
||||||
|
// Dns: []string{"denied.localhost"},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Ssh: &linkedca.SSHPolicy{
|
||||||
|
// User: &linkedca.SSHUserPolicy{
|
||||||
|
// Allow: &linkedca.SSHUserNames{},
|
||||||
|
// Deny: &linkedca.SSHUserNames{},
|
||||||
|
// },
|
||||||
|
// Host: &linkedca.SSHHostPolicy{
|
||||||
|
// Allow: &linkedca.SSHHostNames{},
|
||||||
|
// Deny: &linkedca.SSHHostNames{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
dbap, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbap.convert(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
old, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbap := &dbAuthorityPolicy{
|
||||||
|
ID: db.authorityID,
|
||||||
|
AuthorityID: db.authorityID,
|
||||||
|
Policy: policy,
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.save(ctx, dbap.ID, dbap, old, "authority_policy", authorityPoliciesTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) DeleteAuthorityPolicy(ctx context.Context) error {
|
||||||
|
dbap, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
old := dbap.clone()
|
||||||
|
|
||||||
|
dbap.Policy = nil
|
||||||
|
return db.save(ctx, dbap.ID, dbap, old, "authority_policy", authorityPoliciesTable)
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ type dbProvisioner struct {
|
||||||
Type linkedca.Provisioner_Type `json:"type"`
|
Type linkedca.Provisioner_Type `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Claims *linkedca.Claims `json:"claims"`
|
Claims *linkedca.Claims `json:"claims"`
|
||||||
|
Policy *linkedca.Policy `json:"policy"`
|
||||||
Details []byte `json:"details"`
|
Details []byte `json:"details"`
|
||||||
X509Template *linkedca.Template `json:"x509Template"`
|
X509Template *linkedca.Template `json:"x509Template"`
|
||||||
SSHTemplate *linkedca.Template `json:"sshTemplate"`
|
SSHTemplate *linkedca.Template `json:"sshTemplate"`
|
||||||
|
@ -43,6 +44,7 @@ func (dbp *dbProvisioner) convert2linkedca() (*linkedca.Provisioner, error) {
|
||||||
Type: dbp.Type,
|
Type: dbp.Type,
|
||||||
Name: dbp.Name,
|
Name: dbp.Name,
|
||||||
Claims: dbp.Claims,
|
Claims: dbp.Claims,
|
||||||
|
Policy: dbp.Policy,
|
||||||
Details: details,
|
Details: details,
|
||||||
X509Template: dbp.X509Template,
|
X509Template: dbp.X509Template,
|
||||||
SshTemplate: dbp.SSHTemplate,
|
SshTemplate: dbp.SSHTemplate,
|
||||||
|
@ -160,6 +162,7 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner)
|
||||||
Type: prov.Type,
|
Type: prov.Type,
|
||||||
Name: prov.Name,
|
Name: prov.Name,
|
||||||
Claims: prov.Claims,
|
Claims: prov.Claims,
|
||||||
|
Policy: prov.Policy,
|
||||||
Details: details,
|
Details: details,
|
||||||
X509Template: prov.X509Template,
|
X509Template: prov.X509Template,
|
||||||
SSHTemplate: prov.SshTemplate,
|
SSHTemplate: prov.SshTemplate,
|
||||||
|
@ -187,6 +190,7 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner)
|
||||||
}
|
}
|
||||||
nu.Name = prov.Name
|
nu.Name = prov.Name
|
||||||
nu.Claims = prov.Claims
|
nu.Claims = prov.Claims
|
||||||
|
nu.Policy = prov.Policy
|
||||||
nu.Details, err = json.Marshal(prov.Details.GetData())
|
nu.Details, err = json.Marshal(prov.Details.GetData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return admin.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name)
|
return admin.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name)
|
||||||
|
|
|
@ -205,6 +205,47 @@ func (a *Authority) reloadAdminResources(ctx context.Context) error {
|
||||||
a.provisioners = provClxn
|
a.provisioners = provClxn
|
||||||
a.config.AuthorityConfig.Admins = adminList
|
a.config.AuthorityConfig.Admins = adminList
|
||||||
a.admins = adminClxn
|
a.admins = adminClxn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reloadPolicyEngines reloads x509 and SSH policy engines using
|
||||||
|
// configuration stored in the DB or from the configuration file.
|
||||||
|
func (a *Authority) reloadPolicyEngines(ctx context.Context) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
policyOptions *policy.Options
|
||||||
|
)
|
||||||
|
if a.config.AuthorityConfig.EnableAdmin {
|
||||||
|
linkedPolicy, err := a.adminDB.GetAuthorityPolicy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error getting policy to initialize authority")
|
||||||
|
}
|
||||||
|
policyOptions = policyToCertificates(linkedPolicy)
|
||||||
|
} else {
|
||||||
|
policyOptions = a.config.AuthorityConfig.Policy
|
||||||
|
}
|
||||||
|
|
||||||
|
// return early if no policy options set
|
||||||
|
if policyOptions == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the x509 allow/deny policy engine
|
||||||
|
if a.x509Policy, err = policy.NewX509PolicyEngine(policyOptions.GetX509Options()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Initialize the SSH allow/deny policy engine for host certificates
|
||||||
|
if a.sshHostPolicy, err = policy.NewSSHHostPolicyEngine(policyOptions.GetSSHOptions()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Initialize the SSH allow/deny policy engine for user certificates
|
||||||
|
if a.sshUserPolicy, err = policy.NewSSHUserPolicyEngine(policyOptions.GetSSHOptions()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,6 +574,11 @@ func (a *Authority) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load Policy Engines
|
||||||
|
if err := a.reloadPolicyEngines(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
|
||||||
|
@ -545,21 +591,6 @@ func (a *Authority) init() error {
|
||||||
a.templates.Data["Step"] = tmplVars
|
a.templates.Data["Step"] = tmplVars
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the x509 allow/deny policy engine
|
|
||||||
if a.x509Policy, err = policy.NewX509PolicyEngine(a.config.AuthorityConfig.Policy.GetX509Options()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Initialize the SSH allow/deny policy engine for host certificates
|
|
||||||
if a.sshHostPolicy, err = policy.NewSSHHostPolicyEngine(a.config.AuthorityConfig.Policy.GetSSHOptions()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Initialize the SSH allow/deny policy engine for user certificates
|
|
||||||
if a.sshUserPolicy, err = policy.NewSSHUserPolicyEngine(a.config.AuthorityConfig.Policy.GetSSHOptions()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// JWT numeric dates are seconds.
|
// JWT numeric dates are seconds.
|
||||||
a.startTime = time.Now().Truncate(time.Second)
|
a.startTime = time.Now().Truncate(time.Second)
|
||||||
// Set flag indicating that initialization has been completed, and should
|
// Set flag indicating that initialization has been completed, and should
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
"go.step.sm/crypto/keyutil"
|
"go.step.sm/crypto/keyutil"
|
||||||
|
@ -34,6 +35,9 @@ type linkedCaClient struct {
|
||||||
authorityID string
|
authorityID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interface guard
|
||||||
|
var _ admin.DB = (*linkedCaClient)(nil)
|
||||||
|
|
||||||
type linkedCAClaims struct {
|
type linkedCAClaims struct {
|
||||||
jose.Claims
|
jose.Claims
|
||||||
SANs []string `json:"sans"`
|
SANs []string `json:"sans"`
|
||||||
|
@ -310,6 +314,22 @@ func (c *linkedCaClient) IsSSHRevoked(serial string) (bool, error) {
|
||||||
return resp.Status != linkedca.RevocationStatus_ACTIVE, nil
|
return resp.Status != linkedca.RevocationStatus_ACTIVE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *linkedCaClient) CreateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linkedCaClient) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
||||||
|
return nil, errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linkedCaClient) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linkedCaClient) DeleteAuthorityPolicy(ctx context.Context) error {
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
func serializeCertificate(crt *x509.Certificate) string {
|
func serializeCertificate(crt *x509.Certificate) string {
|
||||||
if crt == nil {
|
if crt == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|
132
authority/policy.go
Normal file
132
authority/policy.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package authority
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Authority) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
||||||
|
a.adminMutex.Lock()
|
||||||
|
defer a.adminMutex.Unlock()
|
||||||
|
|
||||||
|
policy, err := a.adminDB.GetAuthorityPolicy(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) StoreAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
a.adminMutex.Lock()
|
||||||
|
defer a.adminMutex.Unlock()
|
||||||
|
|
||||||
|
if err := a.adminDB.CreateAuthorityPolicy(ctx, policy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.reloadPolicyEngines(ctx); err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error reloading admin resources when creating authority policy")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
||||||
|
a.adminMutex.Lock()
|
||||||
|
defer a.adminMutex.Unlock()
|
||||||
|
|
||||||
|
if err := a.adminDB.UpdateAuthorityPolicy(ctx, policy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.reloadPolicyEngines(ctx); err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error reloading admin resources when updating authority policy")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) RemoveAuthorityPolicy(ctx context.Context) error {
|
||||||
|
a.adminMutex.Lock()
|
||||||
|
defer a.adminMutex.Unlock()
|
||||||
|
|
||||||
|
if err := a.adminDB.DeleteAuthorityPolicy(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.reloadPolicyEngines(ctx); err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error reloading admin resources when deleting authority policy")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func policyToCertificates(p *linkedca.Policy) *policy.Options {
|
||||||
|
// return early
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// prepare full policy struct
|
||||||
|
opts := &policy.Options{
|
||||||
|
X509: &policy.X509PolicyOptions{
|
||||||
|
AllowedNames: &policy.X509NameOptions{},
|
||||||
|
DeniedNames: &policy.X509NameOptions{},
|
||||||
|
},
|
||||||
|
SSH: &policy.SSHPolicyOptions{
|
||||||
|
Host: &policy.SSHHostCertificateOptions{
|
||||||
|
AllowedNames: &policy.SSHNameOptions{},
|
||||||
|
DeniedNames: &policy.SSHNameOptions{},
|
||||||
|
},
|
||||||
|
User: &policy.SSHUserCertificateOptions{
|
||||||
|
AllowedNames: &policy.SSHNameOptions{},
|
||||||
|
DeniedNames: &policy.SSHNameOptions{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// fill x509 policy configuration
|
||||||
|
if p.X509 != nil {
|
||||||
|
if p.X509.Allow != nil {
|
||||||
|
opts.X509.AllowedNames.DNSDomains = p.X509.Allow.Dns
|
||||||
|
opts.X509.AllowedNames.IPRanges = p.X509.Allow.Ips
|
||||||
|
opts.X509.AllowedNames.EmailAddresses = p.X509.Allow.Emails
|
||||||
|
opts.X509.AllowedNames.URIDomains = p.X509.Allow.Uris
|
||||||
|
}
|
||||||
|
if p.X509.Deny != nil {
|
||||||
|
opts.X509.DeniedNames.DNSDomains = p.X509.Deny.Dns
|
||||||
|
opts.X509.DeniedNames.IPRanges = p.X509.Deny.Ips
|
||||||
|
opts.X509.DeniedNames.EmailAddresses = p.X509.Deny.Emails
|
||||||
|
opts.X509.DeniedNames.URIDomains = p.X509.Deny.Uris
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fill ssh policy configuration
|
||||||
|
if p.Ssh != nil {
|
||||||
|
if p.Ssh.Host != nil {
|
||||||
|
if p.Ssh.Host.Allow != nil {
|
||||||
|
opts.SSH.Host.AllowedNames.DNSDomains = p.Ssh.Host.Allow.Dns
|
||||||
|
opts.SSH.Host.AllowedNames.IPRanges = p.Ssh.Host.Allow.Ips
|
||||||
|
opts.SSH.Host.AllowedNames.EmailAddresses = p.Ssh.Host.Allow.Principals
|
||||||
|
}
|
||||||
|
if p.Ssh.Host.Deny != nil {
|
||||||
|
opts.SSH.Host.DeniedNames.DNSDomains = p.Ssh.Host.Deny.Dns
|
||||||
|
opts.SSH.Host.DeniedNames.IPRanges = p.Ssh.Host.Deny.Ips
|
||||||
|
opts.SSH.Host.DeniedNames.Principals = p.Ssh.Host.Deny.Principals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Ssh.User != nil {
|
||||||
|
if p.Ssh.User.Allow != nil {
|
||||||
|
opts.SSH.User.AllowedNames.EmailAddresses = p.Ssh.User.Allow.Emails
|
||||||
|
opts.SSH.User.AllowedNames.Principals = p.Ssh.User.Allow.Principals
|
||||||
|
}
|
||||||
|
if p.Ssh.User.Deny != nil {
|
||||||
|
opts.SSH.User.DeniedNames.EmailAddresses = p.Ssh.User.Deny.Emails
|
||||||
|
opts.SSH.User.DeniedNames.Principals = p.Ssh.User.Deny.Principals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
package policy
|
package policy
|
||||||
|
|
||||||
|
// Options is a container for authority level x509 and SSH
|
||||||
|
// policy configuration.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
X509 *X509PolicyOptions `json:"x509,omitempty"`
|
X509 *X509PolicyOptions `json:"x509,omitempty"`
|
||||||
SSH *SSHPolicyOptions `json:"ssh,omitempty"`
|
SSH *SSHPolicyOptions `json:"ssh,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetX509Options returns the x509 authority level policy
|
||||||
|
// configuration
|
||||||
func (o *Options) GetX509Options() *X509PolicyOptions {
|
func (o *Options) GetX509Options() *X509PolicyOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -12,6 +16,8 @@ func (o *Options) GetX509Options() *X509PolicyOptions {
|
||||||
return o.X509
|
return o.X509
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSSHOptions returns the SSH authority level policy
|
||||||
|
// configuration
|
||||||
func (o *Options) GetSSHOptions() *SSHPolicyOptions {
|
func (o *Options) GetSSHOptions() *SSHPolicyOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -19,16 +25,19 @@ func (o *Options) GetSSHOptions() *SSHPolicyOptions {
|
||||||
return o.SSH
|
return o.SSH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X509PolicyOptionsInterface is an interface for providers
|
||||||
|
// of x509 allowed and denied names.
|
||||||
type X509PolicyOptionsInterface interface {
|
type X509PolicyOptionsInterface interface {
|
||||||
GetAllowedNameOptions() *X509NameOptions
|
GetAllowedNameOptions() *X509NameOptions
|
||||||
GetDeniedNameOptions() *X509NameOptions
|
GetDeniedNameOptions() *X509NameOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X509PolicyOptions is a container for x509 allowed and denied
|
||||||
|
// names.
|
||||||
type X509PolicyOptions struct {
|
type X509PolicyOptions struct {
|
||||||
// AllowedNames ...
|
// AllowedNames contains the x509 allowed names
|
||||||
AllowedNames *X509NameOptions `json:"allow,omitempty"`
|
AllowedNames *X509NameOptions `json:"allow,omitempty"`
|
||||||
|
// DeniedNames contains the x509 denied names
|
||||||
// DeniedNames ...
|
|
||||||
DeniedNames *X509NameOptions `json:"deny,omitempty"`
|
DeniedNames *X509NameOptions `json:"deny,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +58,8 @@ func (o *X509NameOptions) HasNames() bool {
|
||||||
len(o.URIDomains) > 0
|
len(o.URIDomains) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHPolicyOptionsInterface is an interface for providers of
|
||||||
|
// SSH user and host name policy configuration.
|
||||||
type SSHPolicyOptionsInterface interface {
|
type SSHPolicyOptionsInterface interface {
|
||||||
GetAllowedUserNameOptions() *SSHNameOptions
|
GetAllowedUserNameOptions() *SSHNameOptions
|
||||||
GetDeniedUserNameOptions() *SSHNameOptions
|
GetDeniedUserNameOptions() *SSHNameOptions
|
||||||
|
@ -56,16 +67,16 @@ type SSHPolicyOptionsInterface interface {
|
||||||
GetDeniedHostNameOptions() *SSHNameOptions
|
GetDeniedHostNameOptions() *SSHNameOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHPolicyOptions is a container for SSH user and host policy
|
||||||
|
// configuration
|
||||||
type SSHPolicyOptions struct {
|
type SSHPolicyOptions struct {
|
||||||
// User contains SSH user certificate options.
|
// User contains SSH user certificate options.
|
||||||
User *SSHUserCertificateOptions `json:"user,omitempty"`
|
User *SSHUserCertificateOptions `json:"user,omitempty"`
|
||||||
|
|
||||||
// Host contains SSH host certificate options.
|
// Host contains SSH host certificate options.
|
||||||
Host *SSHHostCertificateOptions `json:"host,omitempty"`
|
Host *SSHHostCertificateOptions `json:"host,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllowedNameOptions returns AllowedNames, which models the
|
// GetAllowedNameOptions returns x509 allowed name policy configuration
|
||||||
// SANs that ...
|
|
||||||
func (o *X509PolicyOptions) GetAllowedNameOptions() *X509NameOptions {
|
func (o *X509PolicyOptions) GetAllowedNameOptions() *X509NameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -73,8 +84,7 @@ func (o *X509PolicyOptions) GetAllowedNameOptions() *X509NameOptions {
|
||||||
return o.AllowedNames
|
return o.AllowedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDeniedNameOptions returns the DeniedNames, which models the
|
// GetDeniedNameOptions returns the x509 denied name policy configuration
|
||||||
// SANs that ...
|
|
||||||
func (o *X509PolicyOptions) GetDeniedNameOptions() *X509NameOptions {
|
func (o *X509PolicyOptions) GetDeniedNameOptions() *X509NameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -82,6 +92,8 @@ func (o *X509PolicyOptions) GetDeniedNameOptions() *X509NameOptions {
|
||||||
return o.DeniedNames
|
return o.DeniedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllowedUserNameOptions returns the SSH allowed user name policy
|
||||||
|
// configuration.
|
||||||
func (o *SSHPolicyOptions) GetAllowedUserNameOptions() *SSHNameOptions {
|
func (o *SSHPolicyOptions) GetAllowedUserNameOptions() *SSHNameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -92,6 +104,8 @@ func (o *SSHPolicyOptions) GetAllowedUserNameOptions() *SSHNameOptions {
|
||||||
return o.User.AllowedNames
|
return o.User.AllowedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDeniedUserNameOptions returns the SSH denied user name policy
|
||||||
|
// configuration.
|
||||||
func (o *SSHPolicyOptions) GetDeniedUserNameOptions() *SSHNameOptions {
|
func (o *SSHPolicyOptions) GetDeniedUserNameOptions() *SSHNameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -102,6 +116,8 @@ func (o *SSHPolicyOptions) GetDeniedUserNameOptions() *SSHNameOptions {
|
||||||
return o.User.DeniedNames
|
return o.User.DeniedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllowedHostNameOptions returns the SSH allowed host name policy
|
||||||
|
// configuration.
|
||||||
func (o *SSHPolicyOptions) GetAllowedHostNameOptions() *SSHNameOptions {
|
func (o *SSHPolicyOptions) GetAllowedHostNameOptions() *SSHNameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -112,6 +128,8 @@ func (o *SSHPolicyOptions) GetAllowedHostNameOptions() *SSHNameOptions {
|
||||||
return o.Host.AllowedNames
|
return o.Host.AllowedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDeniedHostNameOptions returns the SSH denied host name policy
|
||||||
|
// configuration.
|
||||||
func (o *SSHPolicyOptions) GetDeniedHostNameOptions() *SSHNameOptions {
|
func (o *SSHPolicyOptions) GetDeniedHostNameOptions() *SSHNameOptions {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
"github.com/smallstep/certificates/authority/config"
|
"github.com/smallstep/certificates/authority/config"
|
||||||
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
"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/cli-utils/step"
|
"go.step.sm/cli-utils/step"
|
||||||
|
@ -395,6 +396,58 @@ func optionsToCertificates(p *linkedca.Provisioner) *provisioner.Options {
|
||||||
ops.SSH.Template = string(p.SshTemplate.Template)
|
ops.SSH.Template = string(p.SshTemplate.Template)
|
||||||
ops.SSH.TemplateData = p.SshTemplate.Data
|
ops.SSH.TemplateData = p.SshTemplate.Data
|
||||||
}
|
}
|
||||||
|
if p.Policy != nil {
|
||||||
|
if p.Policy.X509 != nil {
|
||||||
|
if p.Policy.X509.Allow != nil {
|
||||||
|
ops.X509.AllowedNames = &policy.X509NameOptions{
|
||||||
|
DNSDomains: p.Policy.X509.Allow.Dns,
|
||||||
|
IPRanges: p.Policy.X509.Allow.Ips,
|
||||||
|
EmailAddresses: p.Policy.X509.Allow.Emails,
|
||||||
|
URIDomains: p.Policy.X509.Allow.Uris,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy.X509.Deny != nil {
|
||||||
|
ops.X509.DeniedNames = &policy.X509NameOptions{
|
||||||
|
DNSDomains: p.Policy.X509.Deny.Dns,
|
||||||
|
IPRanges: p.Policy.X509.Deny.Ips,
|
||||||
|
EmailAddresses: p.Policy.X509.Deny.Emails,
|
||||||
|
URIDomains: p.Policy.X509.Deny.Uris,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy.Ssh != nil {
|
||||||
|
if p.Policy.Ssh.Host != nil {
|
||||||
|
ops.SSH.Host = &policy.SSHHostCertificateOptions{}
|
||||||
|
if p.Policy.Ssh.Host.Allow != nil {
|
||||||
|
ops.SSH.Host.AllowedNames = &policy.SSHNameOptions{
|
||||||
|
DNSDomains: p.Policy.Ssh.Host.Allow.Dns,
|
||||||
|
IPRanges: p.Policy.Ssh.Host.Allow.Ips,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy.Ssh.Host.Deny != nil {
|
||||||
|
ops.SSH.Host.DeniedNames = &policy.SSHNameOptions{
|
||||||
|
DNSDomains: p.Policy.Ssh.Host.Deny.Dns,
|
||||||
|
IPRanges: p.Policy.Ssh.Host.Deny.Ips,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy.Ssh.User != nil {
|
||||||
|
ops.SSH.User = &policy.SSHUserCertificateOptions{}
|
||||||
|
if p.Policy.Ssh.User.Allow != nil {
|
||||||
|
ops.SSH.User.AllowedNames = &policy.SSHNameOptions{
|
||||||
|
EmailAddresses: p.Policy.Ssh.User.Allow.Emails,
|
||||||
|
Principals: p.Policy.Ssh.User.Allow.Principals,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy.Ssh.User.Deny != nil {
|
||||||
|
ops.SSH.User.DeniedNames = &policy.SSHNameOptions{
|
||||||
|
EmailAddresses: p.Policy.Ssh.User.Deny.Emails,
|
||||||
|
Principals: p.Policy.Ssh.User.Deny.Principals,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return ops
|
return ops
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,10 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a policy is configured, perform allow/deny policy check on authority level
|
// If a policy is configured, perform allow/deny policy check on authority level
|
||||||
if a.x509Policy != nil {
|
// TODO: policy currently also applies to admin token certs; how to circumvent?
|
||||||
|
// Allow any name of an admin in the DB? Or in the admin collection?
|
||||||
|
todoRemoveThis := false
|
||||||
|
if todoRemoveThis && a.x509Policy != nil {
|
||||||
allowed, err := a.x509Policy.AreCertificateNamesAllowed(leaf)
|
allowed, err := a.x509Policy.AreCertificateNamesAllowed(leaf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.InternalServerErr(err,
|
return nil, errs.InternalServerErr(err,
|
||||||
|
|
3
ca/ca.go
3
ca/ca.go
|
@ -208,7 +208,8 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
||||||
adminDB := auth.GetAdminDatabase()
|
adminDB := auth.GetAdminDatabase()
|
||||||
if adminDB != nil {
|
if adminDB != nil {
|
||||||
acmeAdminResponder := adminAPI.NewACMEAdminResponder()
|
acmeAdminResponder := adminAPI.NewACMEAdminResponder()
|
||||||
adminHandler := adminAPI.NewHandler(auth, adminDB, acmeDB, acmeAdminResponder)
|
policyAdminResponder := adminAPI.NewPolicyAdminResponder(auth, adminDB)
|
||||||
|
adminHandler := adminAPI.NewHandler(auth, adminDB, acmeDB, acmeAdminResponder, policyAdminResponder)
|
||||||
mux.Route("/admin", func(r chi.Router) {
|
mux.Route("/admin", func(r chi.Router) {
|
||||||
adminHandler.Route(r)
|
adminHandler.Route(r)
|
||||||
})
|
})
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -49,4 +49,4 @@ require (
|
||||||
// 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.step.sm/cli-utils => ../cli-utils
|
||||||
// replace go.step.sm/linkedca => ../linkedca
|
replace go.step.sm/linkedca => ../linkedca
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -685,10 +685,6 @@ go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/
|
||||||
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
||||||
go.step.sm/crypto v0.15.0 h1:VioBln+x3+RoejgeBhvxkLGVYdWRy6PFiAaUUN29/E0=
|
go.step.sm/crypto v0.15.0 h1:VioBln+x3+RoejgeBhvxkLGVYdWRy6PFiAaUUN29/E0=
|
||||||
go.step.sm/crypto v0.15.0/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g=
|
go.step.sm/crypto v0.15.0/go.mod h1:3G0yQr5lQqfEG0CMYz8apC/qMtjLRQlzflL2AxkcN+g=
|
||||||
go.step.sm/linkedca v0.9.2 h1:CpAkd174sLXFfrOZrbPEiTzik91QRj3+L0omsiwsiok=
|
|
||||||
go.step.sm/linkedca v0.9.2/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo=
|
|
||||||
go.step.sm/linkedca v0.10.0 h1:+bqymMRulHYkVde4l16FnqFVskoS6HCWJN5Z5cxAqF8=
|
|
||||||
go.step.sm/linkedca v0.10.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
|
Loading…
Reference in a new issue