fix(deps): update module github.com/containers/storage to v1.52.0

... and c/image/v5 to main

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
renovate[bot]
2024-01-19 23:01:59 +00:00
committed by Miloslav Trmač
parent 6baa928c1b
commit 58ff9fdb27
384 changed files with 16717 additions and 5937 deletions

View File

@@ -75,11 +75,16 @@ type OCSPStatus string
const (
OCSPStatusGood = OCSPStatus("good")
OCSPStatusRevoked = OCSPStatus("revoked")
// Not a real OCSP status. This is a placeholder we write before the
// actual precertificate is issued, to ensure we never return "good" before
// issuance succeeds, for BR compliance reasons.
OCSPStatusNotReady = OCSPStatus("wait")
)
var OCSPStatusToInt = map[OCSPStatus]int{
OCSPStatusGood: ocsp.Good,
OCSPStatusRevoked: ocsp.Revoked,
OCSPStatusGood: ocsp.Good,
OCSPStatusRevoked: ocsp.Revoked,
OCSPStatusNotReady: -1,
}
// DNSPrefix is attached to DNS names in DNS challenges
@@ -120,7 +125,7 @@ type ValidationRecord struct {
URL string `json:"url,omitempty"`
// Shared
Hostname string `json:"hostname"`
Hostname string `json:"hostname,omitempty"`
Port string `json:"port,omitempty"`
AddressesResolved []net.IP `json:"addressesResolved,omitempty"`
AddressUsed net.IP `json:"addressUsed,omitempty"`
@@ -337,11 +342,18 @@ type Authorization struct {
// slice and the order of these challenges may not be predictable.
Challenges []Challenge `json:"challenges,omitempty" db:"-"`
// Wildcard is a Boulder-specific Authorization field that indicates the
// authorization was created as a result of an order containing a name with
// a `*.`wildcard prefix. This will help convey to users that an
// Authorization with the identifier `example.com` and one DNS-01 challenge
// corresponds to a name `*.example.com` from an associated order.
// https://datatracker.ietf.org/doc/html/rfc8555#page-29
//
// wildcard (optional, boolean): This field MUST be present and true
// for authorizations created as a result of a newOrder request
// containing a DNS identifier with a value that was a wildcard
// domain name. For other authorizations, it MUST be absent.
// Wildcard domain names are described in Section 7.1.3.
//
// This is not represented in the database because we calculate it from
// the identifier stored in the database. Unlike the identifier returned
// as part of the authorization, the identifier we store in the database
// can contain an asterisk.
Wildcard bool `json:"wildcard,omitempty" db:"-"`
}
@@ -406,53 +418,46 @@ type Certificate struct {
}
// CertificateStatus structs are internal to the server. They represent the
// latest data about the status of the certificate, required for OCSP updating
// and for validating that the subscriber has accepted the certificate.
// latest data about the status of the certificate, required for generating new
// OCSP responses and determining if a certificate has been revoked.
type CertificateStatus struct {
ID int64 `db:"id"`
Serial string `db:"serial"`
// status: 'good' or 'revoked'. Note that good, expired certificates remain
// with status 'good' but don't necessarily get fresh OCSP responses.
// with status 'good' but don't necessarily get fresh OCSP responses.
Status OCSPStatus `db:"status"`
// ocspLastUpdated: The date and time of the last time we generated an OCSP
// response. If we have never generated one, this has the zero value of
// time.Time, i.e. Jan 1 1970.
// response. If we have never generated one, this has the zero value of
// time.Time, i.e. Jan 1 1970.
OCSPLastUpdated time.Time `db:"ocspLastUpdated"`
// revokedDate: If status is 'revoked', this is the date and time it was
// revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970.
// revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970.
RevokedDate time.Time `db:"revokedDate"`
// revokedReason: If status is 'revoked', this is the reason code for the
// revocation. Otherwise it is zero (which happens to be the reason
// code for 'unspecified').
// revocation. Otherwise it is zero (which happens to be the reason
// code for 'unspecified').
RevokedReason revocation.Reason `db:"revokedReason"`
LastExpirationNagSent time.Time `db:"lastExpirationNagSent"`
// The encoded and signed OCSP response.
OCSPResponse []byte `db:"ocspResponse"`
// For performance reasons[0] we duplicate the `Expires` field of the
// `Certificates` object/table in `CertificateStatus` to avoid a costly `JOIN`
// later on just to retrieve this `Time` value. This helps both the OCSP
// updater and the expiration-mailer stay performant.
//
// Similarly, we add an explicit `IsExpired` boolean to `CertificateStatus`
// table that the OCSP updater so that the database can create a meaningful
// index on `(isExpired, ocspLastUpdated)` without a `JOIN` on `certificates`.
// For more detail see Boulder #1864[0].
//
// [0]: https://github.com/letsencrypt/boulder/issues/1864
// NotAfter and IsExpired are convenience columns which allow expensive
// queries to quickly filter out certificates that we don't need to care about
// anymore. These are particularly useful for the expiration mailer and CRL
// updater. See https://github.com/letsencrypt/boulder/issues/1864.
NotAfter time.Time `db:"notAfter"`
IsExpired bool `db:"isExpired"`
// TODO(#5152): Change this to an issuance.Issuer(Name)ID after it no longer
// has to support both IssuerNameIDs and IssuerIDs.
IssuerID int64
// Note: this is not an issuance.IssuerNameID because that would create an
// import cycle between core and issuance.
// Note2: This field used to be called `issuerID`. We keep the old name in
// the DB, but update the Go field name to be clear which type of ID this
// is.
IssuerNameID int64 `db:"issuerID"`
}
// FQDNSet contains the SHA256 hash of the lowercased, comma joined dNSNames
@@ -501,7 +506,7 @@ func RenewalInfoSimple(issued time.Time, expires time.Time) RenewalInfo {
}
// RenewalInfoImmediate constructs a `RenewalInfo` object with a suggested
// window in the past. Per the draft-ietf-acme-ari-00 spec, clients should
// window in the past. Per the draft-ietf-acme-ari-01 spec, clients should
// attempt to renew immediately if the suggested window is in the past. The
// passed `now` is assumed to be a timestamp representing the current moment in
// time.

View File

@@ -1,9 +1,10 @@
package core
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
@@ -16,6 +17,7 @@ import (
"math/big"
mrand "math/rand"
"os"
"path"
"reflect"
"regexp"
"sort"
@@ -23,7 +25,7 @@ import (
"time"
"unicode"
jose "gopkg.in/go-jose/go-jose.v2"
"gopkg.in/go-jose/go-jose.v2"
)
const Unspecified = "Unspecified"
@@ -96,7 +98,7 @@ func KeyDigest(key crypto.PublicKey) (Sha256Digest, error) {
switch t := key.(type) {
case *jose.JSONWebKey:
if t == nil {
return Sha256Digest{}, fmt.Errorf("Cannot compute digest of nil key")
return Sha256Digest{}, errors.New("cannot compute digest of nil key")
}
return KeyDigest(t.Key)
case jose.JSONWebKey:
@@ -132,21 +134,16 @@ func KeyDigestEquals(j, k crypto.PublicKey) bool {
return digestJ == digestK
}
// PublicKeysEqual determines whether two public keys have the same marshalled
// bytes as one another
func PublicKeysEqual(a, b interface{}) (bool, error) {
if a == nil || b == nil {
return false, errors.New("One or more nil arguments to PublicKeysEqual")
// PublicKeysEqual determines whether two public keys are identical.
func PublicKeysEqual(a, b crypto.PublicKey) (bool, error) {
switch ak := a.(type) {
case *rsa.PublicKey:
return ak.Equal(b), nil
case *ecdsa.PublicKey:
return ak.Equal(b), nil
default:
return false, fmt.Errorf("unsupported public key type %T", ak)
}
aBytes, err := x509.MarshalPKIXPublicKey(a)
if err != nil {
return false, err
}
bBytes, err := x509.MarshalPKIXPublicKey(b)
if err != nil {
return false, err
}
return bytes.Equal(aBytes, bBytes), nil
}
// SerialToString converts a certificate serial number (big.Int) to a String
@@ -160,7 +157,7 @@ func SerialToString(serial *big.Int) string {
func StringToSerial(serial string) (*big.Int, error) {
var serialNum big.Int
if !ValidSerial(serial) {
return &serialNum, errors.New("Invalid serial number")
return &serialNum, fmt.Errorf("invalid serial number %q", serial)
}
_, err := fmt.Sscanf(serial, "%036x", &serialNum)
return &serialNum, err
@@ -245,6 +242,14 @@ func UniqueLowerNames(names []string) (unique []string) {
return
}
// HashNames returns a hash of the names requested. This is intended for use
// when interacting with the orderFqdnSets table and rate limiting.
func HashNames(names []string) []byte {
names = UniqueLowerNames(names)
hash := sha256.Sum256([]byte(strings.Join(names, ",")))
return hash[:]
}
// LoadCert loads a PEM certificate specified by filename or returns an error
func LoadCert(filename string) (*x509.Certificate, error) {
certPEM, err := os.ReadFile(filename)
@@ -253,7 +258,7 @@ func LoadCert(filename string) (*x509.Certificate, error) {
}
block, _ := pem.Decode(certPEM)
if block == nil {
return nil, fmt.Errorf("No data in cert PEM file %s", filename)
return nil, fmt.Errorf("no data in cert PEM file %q", filename)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
@@ -298,3 +303,7 @@ func IsASCII(str string) bool {
}
return true
}
func Command() string {
return path.Base(os.Args[0])
}

View File

@@ -1,194 +0,0 @@
// Package errors provides internal-facing error types for use in Boulder. Many
// of these are transformed directly into Problem Details documents by the WFE.
// Some, like NotFound, may be handled internally. We avoid using Problem
// Details documents as part of our internal error system to avoid layering
// confusions.
//
// These errors are specifically for use in errors that cross RPC boundaries.
// An error type that does not need to be passed through an RPC can use a plain
// Go type locally. Our gRPC code is aware of these error types and will
// serialize and deserialize them automatically.
package errors
import (
"fmt"
"time"
"github.com/letsencrypt/boulder/identifier"
)
// ErrorType provides a coarse category for BoulderErrors.
// Objects of type ErrorType should never be directly returned by other
// functions; instead use the methods below to create an appropriate
// BoulderError wrapping one of these types.
type ErrorType int
// These numeric constants are used when sending berrors through gRPC.
const (
// InternalServer is deprecated. Instead, pass a plain Go error. That will get
// turned into a probs.InternalServerError by the WFE.
InternalServer ErrorType = iota
_
Malformed
Unauthorized
NotFound
RateLimit
RejectedIdentifier
InvalidEmail
ConnectionFailure
_ // Reserved, previously WrongAuthorizationState
CAA
MissingSCTs
Duplicate
OrderNotReady
DNS
BadPublicKey
BadCSR
AlreadyRevoked
BadRevocationReason
)
func (ErrorType) Error() string {
return "urn:ietf:params:acme:error"
}
// BoulderError represents internal Boulder errors
type BoulderError struct {
Type ErrorType
Detail string
SubErrors []SubBoulderError
// RetryAfter the duration a client should wait before retrying the request
// which resulted in this error.
RetryAfter time.Duration
}
// SubBoulderError represents sub-errors specific to an identifier that are
// related to a top-level internal Boulder error.
type SubBoulderError struct {
*BoulderError
Identifier identifier.ACMEIdentifier
}
func (be *BoulderError) Error() string {
return be.Detail
}
func (be *BoulderError) Unwrap() error {
return be.Type
}
// WithSubErrors returns a new BoulderError instance created by adding the
// provided subErrs to the existing BoulderError.
func (be *BoulderError) WithSubErrors(subErrs []SubBoulderError) *BoulderError {
return &BoulderError{
Type: be.Type,
Detail: be.Detail,
SubErrors: append(be.SubErrors, subErrs...),
RetryAfter: be.RetryAfter,
}
}
// New is a convenience function for creating a new BoulderError
func New(errType ErrorType, msg string, args ...interface{}) error {
return &BoulderError{
Type: errType,
Detail: fmt.Sprintf(msg, args...),
}
}
func InternalServerError(msg string, args ...interface{}) error {
return New(InternalServer, msg, args...)
}
func MalformedError(msg string, args ...interface{}) error {
return New(Malformed, msg, args...)
}
func UnauthorizedError(msg string, args ...interface{}) error {
return New(Unauthorized, msg, args...)
}
func NotFoundError(msg string, args ...interface{}) error {
return New(NotFound, msg, args...)
}
func RateLimitError(retryAfter time.Duration, msg string, args ...interface{}) error {
return &BoulderError{
Type: RateLimit,
Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/", args...),
RetryAfter: retryAfter,
}
}
func DuplicateCertificateError(retryAfter time.Duration, msg string, args ...interface{}) error {
return &BoulderError{
Type: RateLimit,
Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/duplicate-certificate-limit/", args...),
RetryAfter: retryAfter,
}
}
func FailedValidationError(retryAfter time.Duration, msg string, args ...interface{}) error {
return &BoulderError{
Type: RateLimit,
Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/failed-validation-limit/", args...),
RetryAfter: retryAfter,
}
}
func RegistrationsPerIPError(retryAfter time.Duration, msg string, args ...interface{}) error {
return &BoulderError{
Type: RateLimit,
Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/too-many-registrations-for-this-ip/", args...),
RetryAfter: retryAfter,
}
}
func RejectedIdentifierError(msg string, args ...interface{}) error {
return New(RejectedIdentifier, msg, args...)
}
func InvalidEmailError(msg string, args ...interface{}) error {
return New(InvalidEmail, msg, args...)
}
func ConnectionFailureError(msg string, args ...interface{}) error {
return New(ConnectionFailure, msg, args...)
}
func CAAError(msg string, args ...interface{}) error {
return New(CAA, msg, args...)
}
func MissingSCTsError(msg string, args ...interface{}) error {
return New(MissingSCTs, msg, args...)
}
func DuplicateError(msg string, args ...interface{}) error {
return New(Duplicate, msg, args...)
}
func OrderNotReadyError(msg string, args ...interface{}) error {
return New(OrderNotReady, msg, args...)
}
func DNSError(msg string, args ...interface{}) error {
return New(DNS, msg, args...)
}
func BadPublicKeyError(msg string, args ...interface{}) error {
return New(BadPublicKey, msg, args...)
}
func BadCSRError(msg string, args ...interface{}) error {
return New(BadCSR, msg, args...)
}
func AlreadyRevokedError(msg string, args ...interface{}) error {
return New(AlreadyRevoked, msg, args...)
}
func BadRevocationReasonError(reason int64) error {
return New(BadRevocationReason, "disallowed revocation reason: %d", reason)
}

View File

@@ -9,8 +9,7 @@ import (
"os"
"github.com/letsencrypt/boulder/core"
yaml "gopkg.in/yaml.v3"
"github.com/letsencrypt/boulder/strictyaml"
)
// blockedKeys is a type for maintaining a map of SHA256 hashes
@@ -58,7 +57,7 @@ func loadBlockedKeysList(filename string) (*blockedKeys, error) {
BlockedHashes []string `yaml:"blocked"`
BlockedHashesHex []string `yaml:"blockedHashesHex"`
}
err = yaml.Unmarshal(yamlBytes, &list)
err = strictyaml.Unmarshal(yamlBytes, &list)
if err != nil {
return nil, err
}

View File

@@ -12,7 +12,6 @@ import (
"sync"
"github.com/letsencrypt/boulder/core"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/titanous/rocacheck"
)
@@ -136,7 +135,7 @@ func (policy *KeyPolicy) GoodKey(ctx context.Context, key crypto.PublicKey) erro
// that has been administratively blocked.
if policy.blockedList != nil {
if blocked, err := policy.blockedList.blocked(key); err != nil {
return berrors.InternalServerError("error checking blocklist for key: %v", key)
return fmt.Errorf("error checking blocklist for key: %v", key)
} else if blocked {
return badKey("public key is forbidden")
}

View File

@@ -7,29 +7,33 @@ import (
"github.com/letsencrypt/boulder/identifier"
)
// Error types that can be used in ACME payloads
const (
// Error types that can be used in ACME payloads. These are sorted in the
// same order as they are defined in RFC8555 Section 6.7. We do not implement
// the `compound`, `externalAccountRequired`, or `userActionRequired` errors,
// because we have no path that would return them.
AccountDoesNotExistProblem = ProblemType("accountDoesNotExist")
AlreadyRevokedProblem = ProblemType("alreadyRevoked")
BadCSRProblem = ProblemType("badCSR")
BadNonceProblem = ProblemType("badNonce")
BadPublicKeyProblem = ProblemType("badPublicKey")
BadRevocationReasonProblem = ProblemType("badRevocationReason")
BadSignatureAlgorithmProblem = ProblemType("badSignatureAlgorithm")
CAAProblem = ProblemType("caa")
ConnectionProblem = ProblemType("connection")
DNSProblem = ProblemType("dns")
InvalidContactProblem = ProblemType("invalidContact")
MalformedProblem = ProblemType("malformed")
OrderNotReadyProblem = ProblemType("orderNotReady")
RateLimitedProblem = ProblemType("rateLimited")
RejectedIdentifierProblem = ProblemType("rejectedIdentifier")
ServerInternalProblem = ProblemType("serverInternal")
TLSProblem = ProblemType("tls")
UnauthorizedProblem = ProblemType("unauthorized")
RateLimitedProblem = ProblemType("rateLimited")
BadNonceProblem = ProblemType("badNonce")
InvalidEmailProblem = ProblemType("invalidEmail")
RejectedIdentifierProblem = ProblemType("rejectedIdentifier")
AccountDoesNotExistProblem = ProblemType("accountDoesNotExist")
CAAProblem = ProblemType("caa")
DNSProblem = ProblemType("dns")
AlreadyRevokedProblem = ProblemType("alreadyRevoked")
OrderNotReadyProblem = ProblemType("orderNotReady")
BadSignatureAlgorithmProblem = ProblemType("badSignatureAlgorithm")
BadPublicKeyProblem = ProblemType("badPublicKey")
BadRevocationReasonProblem = ProblemType("badRevocationReason")
BadCSRProblem = ProblemType("badCSR")
UnsupportedContactProblem = ProblemType("unsupportedContact")
UnsupportedIdentifierProblem = ProblemType("unsupportedIdentifier")
V1ErrorNS = "urn:acme:error:"
V2ErrorNS = "urn:ietf:params:acme:error:"
ErrorNS = "urn:ietf:params:acme:error:"
)
// ProblemType defines the error types in the ACME protocol
@@ -71,40 +75,35 @@ func (pd *ProblemDetails) WithSubProblems(subProbs []SubProblemDetails) *Problem
}
}
// statusTooManyRequests is the HTTP status code meant for rate limiting
// errors. It's not currently in the net/http library so we add it here.
const statusTooManyRequests = 429
// Helper functions which construct the basic RFC8555 Problem Documents, with
// the Type already set and the Details supplied by the caller.
// ProblemDetailsToStatusCode inspects the given ProblemDetails to figure out
// what HTTP status code it should represent. It should only be used by the WFE
// but is included in this package because of its reliance on ProblemTypes.
func ProblemDetailsToStatusCode(prob *ProblemDetails) int {
if prob.HTTPStatus != 0 {
return prob.HTTPStatus
// AccountDoesNotExist returns a ProblemDetails representing an
// AccountDoesNotExistProblem error
func AccountDoesNotExist(detail string) *ProblemDetails {
return &ProblemDetails{
Type: AccountDoesNotExistProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
switch prob.Type {
case
ConnectionProblem,
MalformedProblem,
BadSignatureAlgorithmProblem,
BadPublicKeyProblem,
TLSProblem,
BadNonceProblem,
InvalidEmailProblem,
RejectedIdentifierProblem,
AccountDoesNotExistProblem,
BadRevocationReasonProblem:
return http.StatusBadRequest
case ServerInternalProblem:
return http.StatusInternalServerError
case
UnauthorizedProblem,
CAAProblem:
return http.StatusForbidden
case RateLimitedProblem:
return statusTooManyRequests
default:
return http.StatusInternalServerError
}
// AlreadyRevoked returns a ProblemDetails with a AlreadyRevokedProblem and a 400 Bad
// Request status code.
func AlreadyRevoked(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: AlreadyRevokedProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// BadCSR returns a ProblemDetails representing a BadCSRProblem.
func BadCSR(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: BadCSRProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
@@ -118,75 +117,9 @@ func BadNonce(detail string) *ProblemDetails {
}
}
// RejectedIdentifier returns a ProblemDetails with a RejectedIdentifierProblem and a 400 Bad
// Request status code.
func RejectedIdentifier(detail string) *ProblemDetails {
return &ProblemDetails{
Type: RejectedIdentifierProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// Conflict returns a ProblemDetails with a MalformedProblem and a 409 Conflict
// status code.
func Conflict(detail string) *ProblemDetails {
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusConflict,
}
}
// AlreadyRevoked returns a ProblemDetails with a AlreadyRevokedProblem and a 400 Bad
// Request status code.
func AlreadyRevoked(detail string, a ...interface{}) *ProblemDetails {
return &ProblemDetails{
Type: AlreadyRevokedProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// Malformed returns a ProblemDetails with a MalformedProblem and a 400 Bad
// Request status code.
func Malformed(detail string, args ...interface{}) *ProblemDetails {
if len(args) > 0 {
detail = fmt.Sprintf(detail, args...)
}
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// Canceled returns a ProblemDetails with a MalformedProblem and a 408 Request
// Timeout status code.
func Canceled(detail string, args ...interface{}) *ProblemDetails {
if len(args) > 0 {
detail = fmt.Sprintf(detail, args...)
}
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusRequestTimeout,
}
}
// BadSignatureAlgorithm returns a ProblemDetails with a BadSignatureAlgorithmProblem
// and a 400 Bad Request status code.
func BadSignatureAlgorithm(detail string, a ...interface{}) *ProblemDetails {
return &ProblemDetails{
Type: BadSignatureAlgorithmProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// BadPublicKey returns a ProblemDetails with a BadPublicKeyProblem and a 400 Bad
// Request status code.
func BadPublicKey(detail string, a ...interface{}) *ProblemDetails {
func BadPublicKey(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: BadPublicKeyProblem,
Detail: fmt.Sprintf(detail, a...),
@@ -194,13 +127,101 @@ func BadPublicKey(detail string, a ...interface{}) *ProblemDetails {
}
}
// NotFound returns a ProblemDetails with a MalformedProblem and a 404 Not Found
// status code.
func NotFound(detail string) *ProblemDetails {
// BadRevocationReason returns a ProblemDetails representing
// a BadRevocationReasonProblem
func BadRevocationReason(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: BadRevocationReasonProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// BadSignatureAlgorithm returns a ProblemDetails with a BadSignatureAlgorithmProblem
// and a 400 Bad Request status code.
func BadSignatureAlgorithm(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: BadSignatureAlgorithmProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// CAA returns a ProblemDetails representing a CAAProblem
func CAA(detail string) *ProblemDetails {
return &ProblemDetails{
Type: CAAProblem,
Detail: detail,
HTTPStatus: http.StatusForbidden,
}
}
// Connection returns a ProblemDetails representing a ConnectionProblem
// error
func Connection(detail string) *ProblemDetails {
return &ProblemDetails{
Type: ConnectionProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// DNS returns a ProblemDetails representing a DNSProblem
func DNS(detail string) *ProblemDetails {
return &ProblemDetails{
Type: DNSProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// InvalidContact returns a ProblemDetails representing an InvalidContactProblem.
func InvalidContact(detail string) *ProblemDetails {
return &ProblemDetails{
Type: InvalidContactProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// Malformed returns a ProblemDetails with a MalformedProblem and a 400 Bad
// Request status code.
func Malformed(detail string, a ...any) *ProblemDetails {
if len(a) > 0 {
detail = fmt.Sprintf(detail, a...)
}
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusNotFound,
HTTPStatus: http.StatusBadRequest,
}
}
// OrderNotReady returns a ProblemDetails representing a OrderNotReadyProblem
func OrderNotReady(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: OrderNotReadyProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusForbidden,
}
}
// RateLimited returns a ProblemDetails representing a RateLimitedProblem error
func RateLimited(detail string) *ProblemDetails {
return &ProblemDetails{
Type: RateLimitedProblem,
Detail: detail,
HTTPStatus: http.StatusTooManyRequests,
}
}
// RejectedIdentifier returns a ProblemDetails with a RejectedIdentifierProblem and a 400 Bad
// Request status code.
func RejectedIdentifier(detail string) *ProblemDetails {
return &ProblemDetails{
Type: RejectedIdentifierProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
@@ -214,6 +235,15 @@ func ServerInternal(detail string) *ProblemDetails {
}
}
// TLS returns a ProblemDetails representing a TLSProblem error
func TLS(detail string) *ProblemDetails {
return &ProblemDetails{
Type: TLSProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// Unauthorized returns a ProblemDetails with an UnauthorizedProblem and a 403
// Forbidden status code.
func Unauthorized(detail string) *ProblemDetails {
@@ -224,13 +254,49 @@ func Unauthorized(detail string) *ProblemDetails {
}
}
// MethodNotAllowed returns a ProblemDetails representing a disallowed HTTP
// method error.
func MethodNotAllowed() *ProblemDetails {
// UnsupportedContact returns a ProblemDetails representing an
// UnsupportedContactProblem
func UnsupportedContact(detail string) *ProblemDetails {
return &ProblemDetails{
Type: UnsupportedContactProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// UnsupportedIdentifier returns a ProblemDetails representing an
// UnsupportedIdentifierProblem
func UnsupportedIdentifier(detail string, a ...any) *ProblemDetails {
return &ProblemDetails{
Type: UnsupportedIdentifierProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// Additional helper functions that return variations on MalformedProblem with
// different HTTP status codes set.
// Canceled returns a ProblemDetails with a MalformedProblem and a 408 Request
// Timeout status code.
func Canceled(detail string, a ...any) *ProblemDetails {
if len(a) > 0 {
detail = fmt.Sprintf(detail, a...)
}
return &ProblemDetails{
Type: MalformedProblem,
Detail: "Method not allowed",
HTTPStatus: http.StatusMethodNotAllowed,
Detail: detail,
HTTPStatus: http.StatusRequestTimeout,
}
}
// Conflict returns a ProblemDetails with a MalformedProblem and a 409 Conflict
// status code.
func Conflict(detail string) *ProblemDetails {
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusConflict,
}
}
@@ -254,96 +320,22 @@ func InvalidContentType(detail string) *ProblemDetails {
}
}
// InvalidEmail returns a ProblemDetails representing an invalid email address
// error
func InvalidEmail(detail string) *ProblemDetails {
// MethodNotAllowed returns a ProblemDetails representing a disallowed HTTP
// method error.
func MethodNotAllowed() *ProblemDetails {
return &ProblemDetails{
Type: InvalidEmailProblem,
Type: MalformedProblem,
Detail: "Method not allowed",
HTTPStatus: http.StatusMethodNotAllowed,
}
}
// NotFound returns a ProblemDetails with a MalformedProblem and a 404 Not Found
// status code.
func NotFound(detail string) *ProblemDetails {
return &ProblemDetails{
Type: MalformedProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// ConnectionFailure returns a ProblemDetails representing a ConnectionProblem
// error
func ConnectionFailure(detail string) *ProblemDetails {
return &ProblemDetails{
Type: ConnectionProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// RateLimited returns a ProblemDetails representing a RateLimitedProblem error
func RateLimited(detail string) *ProblemDetails {
return &ProblemDetails{
Type: RateLimitedProblem,
Detail: detail,
HTTPStatus: statusTooManyRequests,
}
}
// TLSError returns a ProblemDetails representing a TLSProblem error
func TLSError(detail string) *ProblemDetails {
return &ProblemDetails{
Type: TLSProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// AccountDoesNotExist returns a ProblemDetails representing an
// AccountDoesNotExistProblem error
func AccountDoesNotExist(detail string) *ProblemDetails {
return &ProblemDetails{
Type: AccountDoesNotExistProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// CAA returns a ProblemDetails representing a CAAProblem
func CAA(detail string) *ProblemDetails {
return &ProblemDetails{
Type: CAAProblem,
Detail: detail,
HTTPStatus: http.StatusForbidden,
}
}
// DNS returns a ProblemDetails representing a DNSProblem
func DNS(detail string) *ProblemDetails {
return &ProblemDetails{
Type: DNSProblem,
Detail: detail,
HTTPStatus: http.StatusBadRequest,
}
}
// OrderNotReady returns a ProblemDetails representing a OrderNotReadyProblem
func OrderNotReady(detail string, a ...interface{}) *ProblemDetails {
return &ProblemDetails{
Type: OrderNotReadyProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusForbidden,
}
}
// BadRevocationReason returns a ProblemDetails representing
// a BadRevocationReasonProblem
func BadRevocationReason(detail string, a ...interface{}) *ProblemDetails {
return &ProblemDetails{
Type: BadRevocationReasonProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
}
}
// BadCSR returns a ProblemDetails representing a BadCSRProblem.
func BadCSR(detail string, a ...interface{}) *ProblemDetails {
return &ProblemDetails{
Type: BadCSRProblem,
Detail: fmt.Sprintf(detail, a...),
HTTPStatus: http.StatusBadRequest,
HTTPStatus: http.StatusNotFound,
}
}

View File

@@ -0,0 +1,46 @@
// Package strictyaml provides a strict YAML unmarshaller based on `go-yaml/yaml`
package strictyaml
import (
"bytes"
"errors"
"fmt"
"io"
"gopkg.in/yaml.v3"
)
// Unmarshal takes a byte array and an interface passed by reference. The
// d.Decode will read the next YAML-encoded value from its input and store it in
// the value pointed to by yamlObj. Any config keys from the incoming YAML
// document which do not correspond to expected keys in the config struct will
// result in errors.
//
// TODO(https://github.com/go-yaml/yaml/issues/639): Replace this function with
// yaml.Unmarshal once a more ergonomic way to set unmarshal options is added
// upstream.
func Unmarshal(b []byte, yamlObj interface{}) error {
r := bytes.NewReader(b)
d := yaml.NewDecoder(r)
d.KnownFields(true)
// d.Decode will mutate yamlObj
err := d.Decode(yamlObj)
if err != nil {
// io.EOF is returned when the YAML document is empty.
if errors.Is(err, io.EOF) {
return fmt.Errorf("unmarshalling YAML, bytes cannot be nil: %w", err)
}
return fmt.Errorf("unmarshalling YAML: %w", err)
}
// As bytes are read by the decoder, the length of the byte buffer should
// decrease. If it doesn't, there's a problem.
if r.Len() != 0 {
return fmt.Errorf("yaml object of size %d bytes had %d bytes of unexpected unconsumed trailers", r.Size(), r.Len())
}
return nil
}