Render proper policy and constrains errors

This commit is contained in:
Mariano Cano 2022-09-21 18:35:18 -07:00
parent 4b79405dac
commit 72e2c4eb2e
4 changed files with 47 additions and 26 deletions

View file

@ -6,6 +6,8 @@ import (
"net"
"net/http"
"net/url"
"github.com/smallstep/certificates/errs"
)
var oidExtensionNameConstraints = []int{2, 5, 29, 30}
@ -23,9 +25,18 @@ func (e ConstraintError) Error() string {
return e.Detail
}
// StatusCode implements an status coder interface.
func (e ConstraintError) StatusCode() int {
return http.StatusForbidden
// As implements the As(any) bool interface and allows to use "errors.As()" to
// convert the ConstraintError to an errs.Error.
func (e ConstraintError) As(v any) bool {
if err, ok := v.(**errs.Error); ok {
*err = &errs.Error{
Status: http.StatusForbidden,
Msg: e.Detail,
Err: e,
}
return true
}
return false
}
// Engine implements a constraint validator for DNS names, IP addresses, Email

View file

@ -6,7 +6,6 @@ import (
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"net/http"
"strings"
"time"
@ -20,7 +19,6 @@ import (
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/errs"
policy "github.com/smallstep/certificates/policy"
"github.com/smallstep/certificates/templates"
)
@ -255,15 +253,9 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi
// Check if authority is allowed to sign the certificate
if err := a.isAllowedToSignSSHCertificate(certTpl); err != nil {
var pe *policy.NamePolicyError
if errors.As(err, &pe) && pe.Reason == policy.NotAllowed {
return nil, &errs.Error{
// NOTE: custom forbidden error, so that denied name is sent to client
// as well as shown in the logs.
Status: http.StatusForbidden,
Err: fmt.Errorf("authority not allowed to sign: %w", err),
Msg: fmt.Sprintf("The request was forbidden by the certificate authority: %s", err.Error()),
}
var ee *errs.Error
if errors.As(err, &ee) {
return nil, ee
}
return nil, errs.InternalServerErr(err,
errs.WithMessage("authority.SignSSH: error creating ssh certificate"),

View file

@ -28,7 +28,6 @@ import (
casapi "github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/policy"
)
// GetTLSOptions returns the tls options configured.
@ -213,15 +212,9 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
// Check if authority is allowed to sign the certificate
if err := a.isAllowedToSignX509Certificate(leaf); err != nil {
var pe *policy.NamePolicyError
if errors.As(err, &pe) && pe.Reason == policy.NotAllowed {
return nil, errs.ApplyOptions(&errs.Error{
// NOTE: custom forbidden error, so that denied name is sent to client
// as well as shown in the logs.
Status: http.StatusForbidden,
Err: fmt.Errorf("authority not allowed to sign: %w", err),
Msg: fmt.Sprintf("The request was forbidden by the certificate authority: %s", err.Error()),
}, opts...)
var ee *errs.Error
if errors.As(err, &ee) {
return nil, ee
}
return nil, errs.InternalServerErr(err,
errs.WithKeyVal("csr", csr),
@ -358,7 +351,14 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
// Check if the certificate is allowed to be renewed, policies or
// constraints might change over time.
if err := a.isAllowedToSignX509Certificate(newCert); err != nil {
return nil, err
var ee *errs.Error
if errors.As(err, &ee) {
return nil, ee
}
return nil, errs.InternalServerErr(err,
errs.WithKeyVal("serialNumber", oldCert.SerialNumber.String()),
errs.WithMessage("error renewing certificate"),
)
}
resp, err := a.x509CAService.RenewCertificate(&casapi.RenewCertificateRequest{

View file

@ -4,11 +4,13 @@ import (
"crypto/x509"
"fmt"
"net"
"net/http"
"net/url"
"go.step.sm/crypto/x509util"
"golang.org/x/crypto/ssh"
"go.step.sm/crypto/x509util"
"github.com/smallstep/certificates/errs"
)
type NamePolicyReason int
@ -62,6 +64,22 @@ func (e *NamePolicyError) Error() string {
}
}
// As implements the As(any) bool interface and allows to use "errors.As()" to
// convert a NotAllowed NamePolicyError to an errs.Error.
func (e *NamePolicyError) As(v any) bool {
if e.Reason == NotAllowed {
if err, ok := v.(**errs.Error); ok {
*err = &errs.Error{
Status: http.StatusForbidden,
Msg: fmt.Sprintf("The request was forbidden by the certificate authority: %s", e.Error()),
Err: e,
}
return true
}
}
return false
}
func (e *NamePolicyError) Detail() string {
return e.detail
}