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 }