forked from TrueCloudLab/certificates
Add authority policy API
This commit is contained in:
parent
23676d3bcc
commit
0e052fe299
9 changed files with 284 additions and 129 deletions
|
@ -35,7 +35,7 @@ type GetExternalAccountKeysResponse struct {
|
|||
|
||||
// requireEABEnabled is a middleware that ensures ACME EAB is enabled
|
||||
// before serving requests that act on ACME EAB credentials.
|
||||
func (h *Handler) requireEABEnabled(next nextHTTP) nextHTTP {
|
||||
func (h *Handler) requireEABEnabled(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
provName := chi.URLParam(r, "provisionerName")
|
||||
|
|
|
@ -12,12 +12,15 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"go.step.sm/linkedca"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func readProtoJSON(r io.ReadCloser, m proto.Message) error {
|
||||
|
@ -34,7 +37,7 @@ func TestHandler_requireEABEnabled(t *testing.T) {
|
|||
ctx context.Context
|
||||
adminDB admin.DB
|
||||
auth adminAuthority
|
||||
next nextHTTP
|
||||
next http.HandlerFunc
|
||||
err *admin.Error
|
||||
statusCode int
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
|
@ -29,19 +31,19 @@ func NewHandler(auth adminAuthority, adminDB admin.DB, acmeDB acme.DB, acmeRespo
|
|||
// Route traffic and implement the Router interface.
|
||||
func (h *Handler) Route(r api.Router) {
|
||||
|
||||
authnz := func(next nextHTTP) nextHTTP {
|
||||
authnz := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next))
|
||||
}
|
||||
|
||||
requireEABEnabled := func(next nextHTTP) nextHTTP {
|
||||
requireEABEnabled := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return h.requireEABEnabled(next)
|
||||
}
|
||||
|
||||
enabledInStandalone := func(next nextHTTP) nextHTTP {
|
||||
enabledInStandalone := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return h.checkAction(next, true)
|
||||
}
|
||||
|
||||
disabledInStandalone := func(next nextHTTP) nextHTTP {
|
||||
disabledInStandalone := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return h.checkAction(next, false)
|
||||
}
|
||||
|
||||
|
@ -73,10 +75,10 @@ func (h *Handler) Route(r api.Router) {
|
|||
|
||||
// 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)))
|
||||
r.MethodFunc("GET", "/provisioners/{provisionerName}/policy", authnz(disabledInStandalone(h.loadProvisionerByName(h.policyResponder.GetProvisionerPolicy))))
|
||||
r.MethodFunc("POST", "/provisioners/{provisionerName}/policy", authnz(disabledInStandalone(h.loadProvisionerByName(h.policyResponder.CreateProvisionerPolicy))))
|
||||
r.MethodFunc("PUT", "/provisioners/{provisionerName}/policy", authnz(disabledInStandalone(h.loadProvisionerByName(h.policyResponder.UpdateProvisionerPolicy))))
|
||||
r.MethodFunc("DELETE", "/provisioners/{provisionerName}/policy", authnz(disabledInStandalone(h.loadProvisionerByName(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)
|
||||
|
|
|
@ -5,16 +5,17 @@ import (
|
|||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
"github.com/smallstep/certificates/authority/admin/db/nosql"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
)
|
||||
|
||||
type nextHTTP = func(http.ResponseWriter, *http.Request)
|
||||
|
||||
// requireAPIEnabled is a middleware that ensures the Administration API
|
||||
// is enabled before servicing requests.
|
||||
func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP {
|
||||
func (h *Handler) requireAPIEnabled(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.auth.IsAdminAPIEnabled() {
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType,
|
||||
|
@ -26,7 +27,7 @@ func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP {
|
|||
}
|
||||
|
||||
// extractAuthorizeTokenAdmin is a middleware that extracts and caches the bearer token.
|
||||
func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP {
|
||||
func (h *Handler) extractAuthorizeTokenAdmin(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
tok := r.Header.Get("Authorization")
|
||||
|
@ -47,8 +48,35 @@ func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP {
|
|||
}
|
||||
}
|
||||
|
||||
// loadProvisioner is a middleware that searches for a provisioner
|
||||
// by name and stores it in the context.
|
||||
func (h *Handler) loadProvisionerByName(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx := r.Context()
|
||||
name := chi.URLParam(r, "provisionerName")
|
||||
var (
|
||||
p provisioner.Interface
|
||||
err error
|
||||
)
|
||||
if p, err = h.auth.LoadProvisionerByName(name); err != nil {
|
||||
api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := h.adminDB.GetProvisioner(ctx, p.GetID())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = linkedca.NewContextWithProvisioner(ctx, prov)
|
||||
next(w, r.WithContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
// checkAction checks if an action is supported in standalone or not
|
||||
func (h *Handler) checkAction(next nextHTTP, supportedInStandalone bool) nextHTTP {
|
||||
func (h *Handler) checkAction(next http.HandlerFunc, supportedInStandalone bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// actions allowed in standalone mode are always supported
|
||||
|
|
|
@ -12,17 +12,19 @@ import (
|
|||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
"go.step.sm/linkedca"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestHandler_requireAPIEnabled(t *testing.T) {
|
||||
type test struct {
|
||||
ctx context.Context
|
||||
auth adminAuthority
|
||||
next nextHTTP
|
||||
next http.HandlerFunc
|
||||
err *admin.Error
|
||||
statusCode int
|
||||
}
|
||||
|
@ -102,7 +104,7 @@ func TestHandler_extractAuthorizeTokenAdmin(t *testing.T) {
|
|||
ctx context.Context
|
||||
auth adminAuthority
|
||||
req *http.Request
|
||||
next nextHTTP
|
||||
next http.HandlerFunc
|
||||
err *admin.Error
|
||||
statusCode int
|
||||
}
|
||||
|
|
|
@ -3,14 +3,11 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/api/read"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
)
|
||||
|
||||
type policyAdminResponderInterface interface {
|
||||
|
@ -54,7 +51,7 @@ func (par *PolicyAdminResponder) GetAuthorityPolicy(w http.ResponseWriter, r *ht
|
|||
}
|
||||
|
||||
if policy == nil {
|
||||
api.JSONNotFound(w)
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -117,7 +114,7 @@ func (par *PolicyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r
|
|||
}
|
||||
|
||||
if policy == nil {
|
||||
api.JSONNotFound(w)
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -152,7 +149,7 @@ func (par *PolicyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r
|
|||
}
|
||||
|
||||
if policy == nil {
|
||||
api.JSONNotFound(w)
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -167,27 +164,12 @@ func (par *PolicyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r
|
|||
|
||||
// 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
|
||||
}
|
||||
prov := linkedca.ProvisionerFromContext(r.Context())
|
||||
|
||||
policy := prov.GetPolicy()
|
||||
if policy == nil {
|
||||
api.JSONNotFound(w)
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -196,41 +178,28 @@ func (par *PolicyAdminResponder) GetProvisionerPolicy(w http.ResponseWriter, r *
|
|||
|
||||
// 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
|
||||
}
|
||||
ctx := r.Context()
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
|
||||
policy := prov.GetPolicy()
|
||||
if policy != nil {
|
||||
adminErr := admin.NewError(admin.ErrorBadRequestType, "provisioner %s already has a policy", name)
|
||||
adminErr := admin.NewError(admin.ErrorBadRequestType, "provisioner %s already has a policy", prov.Name)
|
||||
adminErr.Status = http.StatusConflict
|
||||
api.WriteError(w, adminErr)
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
api.WriteError(w, err)
|
||||
if !api.ReadProtoJSONWithCheck(w, r.Body, newPolicy) {
|
||||
return
|
||||
}
|
||||
|
||||
prov.Policy = newPolicy
|
||||
|
||||
err = par.auth.UpdateProvisioner(ctx, prov)
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error creating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -239,88 +208,65 @@ func (par *PolicyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter,
|
|||
|
||||
// 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))
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
|
||||
if prov.Policy == nil {
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := par.adminDB.GetProvisioner(ctx, p.GetID())
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if !api.ReadProtoJSONWithCheck(w, r.Body, newPolicy) {
|
||||
return
|
||||
}
|
||||
|
||||
prov.Policy = newPolicy
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error updating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(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)
|
||||
api.ProtoJSONStatus(w, newPolicy, http.StatusOK)
|
||||
}
|
||||
|
||||
// DeleteProvisionerPolicy handles the DELETE /admin/provisioners/{name}/policy request
|
||||
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
|
||||
}
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
|
||||
if prov.Policy == nil {
|
||||
api.JSONNotFound(w)
|
||||
api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
// remove the policy
|
||||
prov.Policy = nil
|
||||
|
||||
err = par.auth.UpdateProvisioner(ctx, prov)
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.JSON(w, &DeleteResponse{Status: "ok"})
|
||||
api.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) GetACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
api.JSON(w, "ok")
|
||||
api.JSON(w, "not implemented yet")
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
api.JSON(w, "ok")
|
||||
api.JSON(w, "not implemented yet")
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
api.JSON(w, "ok")
|
||||
api.JSON(w, "not implemented yet")
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
api.JSON(w, "ok")
|
||||
api.JSON(w, "not implemented yet")
|
||||
}
|
||||
|
|
|
@ -218,6 +218,7 @@ func (a *Authority) reloadPolicyEngines(ctx context.Context) error {
|
|||
err error
|
||||
policyOptions *policy.Options
|
||||
)
|
||||
// if admin API is enabled, the CA is running in linked mode
|
||||
if a.config.AuthorityConfig.EnableAdmin {
|
||||
linkedPolicy, err := a.adminDB.GetAuthorityPolicy(ctx)
|
||||
if err != nil {
|
||||
|
|
|
@ -77,6 +77,8 @@ func (a *Authority) RemoveAuthorityPolicy(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkPolicy checks if a new or updated policy configuration results in the user
|
||||
// locking themselves or other admins out of the CA.
|
||||
func (a *Authority) checkPolicy(ctx context.Context, adm *linkedca.Admin, p *linkedca.Policy) error {
|
||||
|
||||
// convert the policy; return early if nil
|
||||
|
@ -90,9 +92,15 @@ func (a *Authority) checkPolicy(ctx context.Context, adm *linkedca.Admin, p *lin
|
|||
return admin.WrapErrorISE(err, "error creating temporary policy engine")
|
||||
}
|
||||
|
||||
// when an empty policy is provided, the resulting engine is nil
|
||||
// and there's no policy to evaluate.
|
||||
if engine == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(hs): Provide option to force the policy, even when the admin subject would be locked out?
|
||||
|
||||
sans := []string{adm.Subject}
|
||||
sans := []string{adm.GetSubject()}
|
||||
if err := isAllowed(engine, sans); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -151,16 +159,32 @@ func policyToCertificates(p *linkedca.Policy) *authPolicy.Options {
|
|||
// 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.Allow.Dns != nil {
|
||||
opts.X509.AllowedNames.DNSDomains = p.X509.Allow.Dns
|
||||
}
|
||||
if p.X509.Allow.Ips != nil {
|
||||
opts.X509.AllowedNames.IPRanges = p.X509.Allow.Ips
|
||||
}
|
||||
if p.X509.Allow.Emails != nil {
|
||||
opts.X509.AllowedNames.EmailAddresses = p.X509.Allow.Emails
|
||||
}
|
||||
if p.X509.Allow.Uris != nil {
|
||||
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
|
||||
if p.X509.Deny.Dns != nil {
|
||||
opts.X509.DeniedNames.DNSDomains = p.X509.Deny.Dns
|
||||
}
|
||||
if p.X509.Deny.Ips != nil {
|
||||
opts.X509.DeniedNames.IPRanges = p.X509.Deny.Ips
|
||||
}
|
||||
if p.X509.Deny.Emails != nil {
|
||||
opts.X509.DeniedNames.EmailAddresses = p.X509.Deny.Emails
|
||||
}
|
||||
if p.X509.Deny.Uris != nil {
|
||||
opts.X509.DeniedNames.URIDomains = p.X509.Deny.Uris
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,24 +192,44 @@ func policyToCertificates(p *linkedca.Policy) *authPolicy.Options {
|
|||
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.Allow.Dns != nil {
|
||||
opts.SSH.Host.AllowedNames.DNSDomains = p.Ssh.Host.Allow.Dns
|
||||
}
|
||||
if p.Ssh.Host.Allow.Ips != nil {
|
||||
opts.SSH.Host.AllowedNames.IPRanges = p.Ssh.Host.Allow.Ips
|
||||
}
|
||||
if p.Ssh.Host.Allow.Principals != nil {
|
||||
opts.SSH.Host.AllowedNames.Principals = 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.Host.Deny.Dns != nil {
|
||||
opts.SSH.Host.DeniedNames.DNSDomains = p.Ssh.Host.Deny.Dns
|
||||
}
|
||||
if p.Ssh.Host.Deny.Ips != nil {
|
||||
opts.SSH.Host.DeniedNames.IPRanges = p.Ssh.Host.Deny.Ips
|
||||
}
|
||||
if p.Ssh.Host.Deny.Principals != nil {
|
||||
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.Allow.Emails != nil {
|
||||
opts.SSH.User.AllowedNames.EmailAddresses = p.Ssh.User.Allow.Emails
|
||||
}
|
||||
if p.Ssh.User.Allow.Principals != nil {
|
||||
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
|
||||
if p.Ssh.User.Deny.Emails != nil {
|
||||
opts.SSH.User.DeniedNames.EmailAddresses = p.Ssh.User.Deny.Emails
|
||||
}
|
||||
if p.Ssh.User.Deny.Principals != nil {
|
||||
opts.SSH.User.DeniedNames.Principals = p.Ssh.User.Deny.Principals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -679,6 +680,134 @@ retry:
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) GetAuthorityPolicy() (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating GET %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client GET %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) CreateAuthorityPolicy(p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating POST %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client POST %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) UpdateAuthorityPolicy(p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating PUT %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client PUT %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) RemoveAuthorityPolicy() error {
|
||||
var retried bool
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodDelete, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating DELETE %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client DELETE %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return readAdminError(resp.Body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAdminError(r io.ReadCloser) error {
|
||||
// TODO: not all errors can be read (i.e. 404); seems to be a bigger issue
|
||||
defer r.Close()
|
||||
|
|
Loading…
Reference in a new issue