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"
"net/http" "net/http"
"net/url" "net/url"
"github.com/smallstep/certificates/errs"
) )
var oidExtensionNameConstraints = []int{2, 5, 29, 30} var oidExtensionNameConstraints = []int{2, 5, 29, 30}
@ -23,9 +25,18 @@ func (e ConstraintError) Error() string {
return e.Detail return e.Detail
} }
// StatusCode implements an status coder interface. // As implements the As(any) bool interface and allows to use "errors.As()" to
func (e ConstraintError) StatusCode() int { // convert the ConstraintError to an errs.Error.
return http.StatusForbidden 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 // Engine implements a constraint validator for DNS names, IP addresses, Email

View file

@ -6,7 +6,6 @@ import (
"crypto/x509" "crypto/x509"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -20,7 +19,6 @@ import (
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
policy "github.com/smallstep/certificates/policy"
"github.com/smallstep/certificates/templates" "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 // Check if authority is allowed to sign the certificate
if err := a.isAllowedToSignSSHCertificate(certTpl); err != nil { if err := a.isAllowedToSignSSHCertificate(certTpl); err != nil {
var pe *policy.NamePolicyError var ee *errs.Error
if errors.As(err, &pe) && pe.Reason == policy.NotAllowed { if errors.As(err, &ee) {
return nil, &errs.Error{ return nil, ee
// 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()),
}
} }
return nil, errs.InternalServerErr(err, return nil, errs.InternalServerErr(err,
errs.WithMessage("authority.SignSSH: error creating ssh certificate"), errs.WithMessage("authority.SignSSH: error creating ssh certificate"),

View file

@ -28,7 +28,6 @@ import (
casapi "github.com/smallstep/certificates/cas/apiv1" casapi "github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/policy"
) )
// GetTLSOptions returns the tls options configured. // 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 // Check if authority is allowed to sign the certificate
if err := a.isAllowedToSignX509Certificate(leaf); err != nil { if err := a.isAllowedToSignX509Certificate(leaf); err != nil {
var pe *policy.NamePolicyError var ee *errs.Error
if errors.As(err, &pe) && pe.Reason == policy.NotAllowed { if errors.As(err, &ee) {
return nil, errs.ApplyOptions(&errs.Error{ return nil, ee
// 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...)
} }
return nil, errs.InternalServerErr(err, return nil, errs.InternalServerErr(err,
errs.WithKeyVal("csr", csr), 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 // Check if the certificate is allowed to be renewed, policies or
// constraints might change over time. // constraints might change over time.
if err := a.isAllowedToSignX509Certificate(newCert); err != nil { 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{ resp, err := a.x509CAService.RenewCertificate(&casapi.RenewCertificateRequest{

View file

@ -4,11 +4,13 @@ import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"net" "net"
"net/http"
"net/url" "net/url"
"go.step.sm/crypto/x509util"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"go.step.sm/crypto/x509util" "github.com/smallstep/certificates/errs"
) )
type NamePolicyReason int 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 { func (e *NamePolicyError) Detail() string {
return e.detail return e.detail
} }