github.com/letsencrypt/boulder@v0.20251208.0/errors/errors.go (about)

     1  // Package errors provide a special error type for use in Boulder. This error
     2  // type carries additional type information with it, and has two special powers:
     3  //
     4  // 1. It is recognized by our gRPC code, and the type metadata and detail string
     5  // will cross gRPC boundaries intact.
     6  //
     7  // 2. It is recognized by our frontend API "rendering" code, and will be
     8  // automatically converted to the corresponding urn:ietf:params:acme:error:...
     9  // ACME Problem Document.
    10  //
    11  // This means that a deeply-nested service (such as the SA) that wants to ensure
    12  // that the ACME client sees a particular problem document (such as NotFound)
    13  // can return a BoulderError and be sure that it will be propagated all the way
    14  // to the client.
    15  //
    16  // Note, however, that any additional context wrapped *around* the BoulderError
    17  // (such as by fmt.Errorf("oops: %w")) will be lost when the error is converted
    18  // into a problem document. Similarly, any type information wrapped *by* a
    19  // BoulderError (such as a sql.ErrNoRows) is lost at the gRPC serialization
    20  // boundary.
    21  package errors
    22  
    23  import (
    24  	"fmt"
    25  	"time"
    26  
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/status"
    29  
    30  	"github.com/letsencrypt/boulder/identifier"
    31  )
    32  
    33  // ErrorType provides a coarse category for BoulderErrors.
    34  // Objects of type ErrorType should never be directly returned by other
    35  // functions; instead use the methods below to create an appropriate
    36  // BoulderError wrapping one of these types.
    37  type ErrorType int
    38  
    39  // These numeric constants are used when sending berrors through gRPC.
    40  const (
    41  	// InternalServer is deprecated. Instead, pass a plain Go error. That will get
    42  	// turned into a probs.InternalServerError by the WFE.
    43  	InternalServer ErrorType = iota
    44  	_                        // Reserved, previously NotSupported
    45  	Malformed
    46  	Unauthorized
    47  	NotFound
    48  	RateLimit
    49  	RejectedIdentifier
    50  	InvalidEmail
    51  	ConnectionFailure
    52  	_ // Reserved, previously WrongAuthorizationState
    53  	CAA
    54  	MissingSCTs
    55  	Duplicate
    56  	OrderNotReady
    57  	DNS
    58  	BadPublicKey
    59  	BadCSR
    60  	AlreadyRevoked
    61  	BadRevocationReason
    62  	UnsupportedContact
    63  	// The requested serial number does not exist in the `serials` table.
    64  	UnknownSerial
    65  	Conflict
    66  	// Defined in https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/00/
    67  	InvalidProfile
    68  	// The certificate being indicated for replacement already has a replacement
    69  	// order.
    70  	AlreadyReplaced
    71  	BadSignatureAlgorithm
    72  	AccountDoesNotExist
    73  	BadNonce
    74  )
    75  
    76  func (ErrorType) Error() string {
    77  	return "urn:ietf:params:acme:error"
    78  }
    79  
    80  // BoulderError represents internal Boulder errors
    81  type BoulderError struct {
    82  	Type      ErrorType
    83  	Detail    string
    84  	SubErrors []SubBoulderError
    85  
    86  	// RetryAfter the duration a client should wait before retrying the request
    87  	// which resulted in this error.
    88  	RetryAfter time.Duration
    89  }
    90  
    91  // SubBoulderError represents sub-errors specific to an identifier that are
    92  // related to a top-level internal Boulder error.
    93  type SubBoulderError struct {
    94  	*BoulderError
    95  	Identifier identifier.ACMEIdentifier
    96  }
    97  
    98  // Error implements the error interface, returning a string representation of
    99  // this error.
   100  func (be *BoulderError) Error() string {
   101  	return be.Detail
   102  }
   103  
   104  // Unwrap implements the optional error-unwrapping interface. It returns the
   105  // underlying type, all of when themselves implement the error interface, so
   106  // that `if errors.Is(someError, berrors.Malformed)` works.
   107  func (be *BoulderError) Unwrap() error {
   108  	return be.Type
   109  }
   110  
   111  // GRPCStatus implements the interface implicitly defined by gRPC's
   112  // status.FromError, which uses this function to detect if the error produced
   113  // by the gRPC server implementation code is a gRPC status.Status. Implementing
   114  // this means that BoulderErrors serialized in gRPC response metadata can be
   115  // accompanied by a gRPC status other than "UNKNOWN".
   116  func (be *BoulderError) GRPCStatus() *status.Status {
   117  	var c codes.Code
   118  	switch be.Type {
   119  	case InternalServer:
   120  		c = codes.Internal
   121  	case Malformed:
   122  		c = codes.InvalidArgument
   123  	case Unauthorized:
   124  		c = codes.PermissionDenied
   125  	case NotFound:
   126  		c = codes.NotFound
   127  	case RateLimit:
   128  		c = codes.Unknown
   129  	case RejectedIdentifier:
   130  		c = codes.InvalidArgument
   131  	case InvalidEmail:
   132  		c = codes.InvalidArgument
   133  	case ConnectionFailure:
   134  		c = codes.Unavailable
   135  	case CAA:
   136  		c = codes.FailedPrecondition
   137  	case MissingSCTs:
   138  		c = codes.Internal
   139  	case Duplicate:
   140  		c = codes.AlreadyExists
   141  	case OrderNotReady:
   142  		c = codes.FailedPrecondition
   143  	case DNS:
   144  		c = codes.Unknown
   145  	case BadPublicKey:
   146  		c = codes.InvalidArgument
   147  	case BadCSR:
   148  		c = codes.InvalidArgument
   149  	case AlreadyRevoked:
   150  		c = codes.AlreadyExists
   151  	case BadRevocationReason:
   152  		c = codes.InvalidArgument
   153  	case UnsupportedContact:
   154  		c = codes.InvalidArgument
   155  	default:
   156  		c = codes.Unknown
   157  	}
   158  	return status.New(c, be.Error())
   159  }
   160  
   161  // WithSubErrors returns a new BoulderError instance created by adding the
   162  // provided subErrs to the existing BoulderError.
   163  func (be *BoulderError) WithSubErrors(subErrs []SubBoulderError) *BoulderError {
   164  	return &BoulderError{
   165  		Type:       be.Type,
   166  		Detail:     be.Detail,
   167  		SubErrors:  append(be.SubErrors, subErrs...),
   168  		RetryAfter: be.RetryAfter,
   169  	}
   170  }
   171  
   172  // New is a convenience function for creating a new BoulderError.
   173  func New(errType ErrorType, msg string) error {
   174  	return &BoulderError{
   175  		Type:   errType,
   176  		Detail: msg,
   177  	}
   178  }
   179  
   180  // newf is a convenience function for creating a new BoulderError with a
   181  // formatted message.
   182  func newf(errType ErrorType, msg string, args ...any) error {
   183  	return &BoulderError{
   184  		Type:   errType,
   185  		Detail: fmt.Sprintf(msg, args...),
   186  	}
   187  }
   188  
   189  func InternalServerError(msg string, args ...any) error {
   190  	return newf(InternalServer, msg, args...)
   191  }
   192  
   193  func MalformedError(msg string, args ...any) error {
   194  	return newf(Malformed, msg, args...)
   195  }
   196  
   197  func UnauthorizedError(msg string, args ...any) error {
   198  	return newf(Unauthorized, msg, args...)
   199  }
   200  
   201  func NotFoundError(msg string, args ...any) error {
   202  	return newf(NotFound, msg, args...)
   203  }
   204  
   205  func RateLimitError(retryAfter time.Duration, msg string, args ...any) error {
   206  	return &BoulderError{
   207  		Type:       RateLimit,
   208  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/", args...),
   209  		RetryAfter: retryAfter,
   210  	}
   211  }
   212  
   213  func RegistrationsPerIPAddressError(retryAfter time.Duration, msg string, args ...any) error {
   214  	return &BoulderError{
   215  		Type:       RateLimit,
   216  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ip-address", args...),
   217  		RetryAfter: retryAfter,
   218  	}
   219  }
   220  
   221  func RegistrationsPerIPv6RangeError(retryAfter time.Duration, msg string, args ...any) error {
   222  	return &BoulderError{
   223  		Type:       RateLimit,
   224  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ipv6-range", args...),
   225  		RetryAfter: retryAfter,
   226  	}
   227  }
   228  
   229  func NewOrdersPerAccountError(retryAfter time.Duration, msg string, args ...any) error {
   230  	return &BoulderError{
   231  		Type:       RateLimit,
   232  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account", args...),
   233  		RetryAfter: retryAfter,
   234  	}
   235  }
   236  
   237  func CertificatesPerDomainError(retryAfter time.Duration, msg string, args ...any) error {
   238  	return &BoulderError{
   239  		Type:       RateLimit,
   240  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-registered-domain", args...),
   241  		RetryAfter: retryAfter,
   242  	}
   243  }
   244  
   245  func CertificatesPerFQDNSetError(retryAfter time.Duration, msg string, args ...any) error {
   246  	return &BoulderError{
   247  		Type:       RateLimit,
   248  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-identifiers", args...),
   249  		RetryAfter: retryAfter,
   250  	}
   251  }
   252  
   253  func FailedAuthorizationsPerDomainPerAccountError(retryAfter time.Duration, msg string, args ...any) error {
   254  	return &BoulderError{
   255  		Type:       RateLimit,
   256  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account", args...),
   257  		RetryAfter: retryAfter,
   258  	}
   259  }
   260  
   261  func LimitOverrideRequestsPerIPAddressError(retryAfter time.Duration, msg string, args ...any) error {
   262  	return &BoulderError{
   263  		Type:       RateLimit,
   264  		Detail:     fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#limit-override-requests-per-ip-address", args...),
   265  		RetryAfter: retryAfter,
   266  	}
   267  }
   268  
   269  func RejectedIdentifierError(msg string, args ...any) error {
   270  	return newf(RejectedIdentifier, msg, args...)
   271  }
   272  
   273  func InvalidEmailError(msg string, args ...any) error {
   274  	return newf(InvalidEmail, msg, args...)
   275  }
   276  
   277  func UnsupportedContactError(msg string, args ...any) error {
   278  	return newf(UnsupportedContact, msg, args...)
   279  }
   280  
   281  func ConnectionFailureError(msg string, args ...any) error {
   282  	return newf(ConnectionFailure, msg, args...)
   283  }
   284  
   285  func CAAError(msg string, args ...any) error {
   286  	return newf(CAA, msg, args...)
   287  }
   288  
   289  func MissingSCTsError(msg string, args ...any) error {
   290  	return newf(MissingSCTs, msg, args...)
   291  }
   292  
   293  func DuplicateError(msg string, args ...any) error {
   294  	return newf(Duplicate, msg, args...)
   295  }
   296  
   297  func OrderNotReadyError(msg string, args ...any) error {
   298  	return newf(OrderNotReady, msg, args...)
   299  }
   300  
   301  func DNSError(msg string, args ...any) error {
   302  	return newf(DNS, msg, args...)
   303  }
   304  
   305  func BadPublicKeyError(msg string, args ...any) error {
   306  	return newf(BadPublicKey, msg, args...)
   307  }
   308  
   309  func BadCSRError(msg string, args ...any) error {
   310  	return newf(BadCSR, msg, args...)
   311  }
   312  
   313  func AlreadyReplacedError(msg string, args ...any) error {
   314  	return newf(AlreadyReplaced, msg, args...)
   315  }
   316  
   317  func AlreadyRevokedError(msg string, args ...any) error {
   318  	return newf(AlreadyRevoked, msg, args...)
   319  }
   320  
   321  func BadRevocationReasonError(reason int64) error {
   322  	return newf(BadRevocationReason, "disallowed revocation reason: %d", reason)
   323  }
   324  
   325  func UnknownSerialError() error {
   326  	return newf(UnknownSerial, "unknown serial")
   327  }
   328  
   329  func InvalidProfileError(msg string, args ...any) error {
   330  	return newf(InvalidProfile, msg, args...)
   331  }
   332  
   333  func BadSignatureAlgorithmError(msg string, args ...any) error {
   334  	return newf(BadSignatureAlgorithm, msg, args...)
   335  }
   336  
   337  func AccountDoesNotExistError(msg string, args ...any) error {
   338  	return newf(AccountDoesNotExist, msg, args...)
   339  }
   340  
   341  func BadNonceError(msg string, args ...any) error {
   342  	return newf(BadNonce, msg, args...)
   343  }