diff --git a/cmd/boulder-wfe2/main.go b/cmd/boulder-wfe2/main.go index 90ad2241749..1fd10769019 100644 --- a/cmd/boulder-wfe2/main.go +++ b/cmd/boulder-wfe2/main.go @@ -155,11 +155,11 @@ type Config struct { // the CA and RA configurations. MaxNames int `validate:"min=0,max=100"` - // CertificateProfileNames is the list of acceptable certificate profile - // names for newOrder requests. Requests with a profile name not in this - // list will be rejected. This field is optional; if unset, no profile - // names are accepted. - CertificateProfileNames []string `validate:"omitempty,dive,alphanum,min=1,max=32"` + // CertProfiles is a map of acceptable certificate profile names to + // descriptions (perhaps including URLs) of those profiles. NewOrder + // Requests with a profile name not present in this map will be rejected. + // This field is optional; if unset, no profile names are accepted. + CertProfiles map[string]string `validate:"omitempty,dive,keys,alphanum,min=1,max=32,endkeys"` } Syslog cmd.SyslogConfig @@ -355,7 +355,7 @@ func main() { limiter, txnBuilder, maxNames, - c.WFE.CertificateProfileNames, + c.WFE.CertProfiles, ) cmd.FailOnError(err, "Unable to create WFE") diff --git a/test/config-next/wfe2.json b/test/config-next/wfe2.json index a5a297dd852..8b2a8787903 100644 --- a/test/config-next/wfe2.json +++ b/test/config-next/wfe2.json @@ -131,9 +131,9 @@ "TrackReplacementCertificatesARI": true, "CheckRenewalExemptionAtWFE": true }, - "certificateProfileNames": [ - "defaultBoulderCertificateProfile" - ] + "certProfiles": { + "defaultBoulderCertificateProfile": "The normal profile you know and love" + } }, "syslog": { "stdoutlevel": 4, diff --git a/wfe2/wfe.go b/wfe2/wfe.go index 708fbad94cb..5cebb47acf9 100644 --- a/wfe2/wfe.go +++ b/wfe2/wfe.go @@ -21,6 +21,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel/trace" + "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/emptypb" "github.com/letsencrypt/boulder/core" @@ -29,18 +30,16 @@ import ( "github.com/letsencrypt/boulder/features" "github.com/letsencrypt/boulder/goodkey" bgrpc "github.com/letsencrypt/boulder/grpc" - "github.com/letsencrypt/boulder/policy" - "github.com/letsencrypt/boulder/ratelimits" - - // 'grpc/noncebalancer' is imported for its init function. - _ "github.com/letsencrypt/boulder/grpc/noncebalancer" + _ "github.com/letsencrypt/boulder/grpc/noncebalancer" // imported for its init function. "github.com/letsencrypt/boulder/identifier" "github.com/letsencrypt/boulder/issuance" blog "github.com/letsencrypt/boulder/log" "github.com/letsencrypt/boulder/metrics/measured_http" "github.com/letsencrypt/boulder/nonce" + "github.com/letsencrypt/boulder/policy" "github.com/letsencrypt/boulder/probs" rapb "github.com/letsencrypt/boulder/ra/proto" + "github.com/letsencrypt/boulder/ratelimits" "github.com/letsencrypt/boulder/revocation" sapb "github.com/letsencrypt/boulder/sa/proto" "github.com/letsencrypt/boulder/web" @@ -165,10 +164,10 @@ type WebFrontEndImpl struct { txnBuilder *ratelimits.TransactionBuilder maxNames int - // certificateProfileNames is a list of profile names that are allowed to be - // passed to the newOrder endpoint. If a profile name is not in this list, - // the request will be rejected as malformed. - certificateProfileNames []string + // certProfiles is a map of acceptable certificate profile names to + // descriptions (perhaps including URLs) of those profiles. NewOrder + // Requests with a profile name not present in this map will be rejected. + certProfiles map[string]string } // NewWebFrontEndImpl constructs a web service for Boulder @@ -192,7 +191,7 @@ func NewWebFrontEndImpl( limiter *ratelimits.Limiter, txnBuilder *ratelimits.TransactionBuilder, maxNames int, - certificateProfileNames []string, + certProfiles map[string]string, ) (WebFrontEndImpl, error) { if len(issuerCertificates) == 0 { return WebFrontEndImpl{}, errors.New("must provide at least one issuer certificate") @@ -230,7 +229,7 @@ func NewWebFrontEndImpl( limiter: limiter, txnBuilder: txnBuilder, maxNames: maxNames, - certificateProfileNames: certificateProfileNames, + certProfiles: certProfiles, } return wfe, nil @@ -550,6 +549,9 @@ func (wfe *WebFrontEndImpl) Directory( wfe.DirectoryCAAIdentity, } } + if len(wfe.certProfiles) != 0 { + metaMap["profiles"] = wfe.certProfiles + } // The "meta" directory entry may also include a string with a website URL if wfe.DirectoryWebsite != "" { metaMap["website"] = wfe.DirectoryWebsite @@ -2190,7 +2192,7 @@ func (wfe *WebFrontEndImpl) validateCertificateProfileName(profile string) error // No profile name is specified. return nil } - if !slices.Contains(wfe.certificateProfileNames, profile) { + if !slices.Contains(maps.Keys(wfe.certProfiles), profile) { // The profile name is not in the list of configured profiles. return errors.New("not a recognized profile name") } diff --git a/wfe2/wfe_test.go b/wfe2/wfe_test.go index 754c7562d95..3d614a00e2e 100644 --- a/wfe2/wfe_test.go +++ b/wfe2/wfe_test.go @@ -407,7 +407,7 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) { limiter, txnBuilder, 100, - []string{""}, + nil, ) test.AssertNotError(t, err, "Unable to create WFE") @@ -3816,7 +3816,7 @@ func TestNewOrderWithProfile(t *testing.T) { expectProfileName := "test-profile" wfe.ra = &mockRA{expectProfileName: expectProfileName} mux := wfe.Handler(metrics.NoopRegisterer) - wfe.certificateProfileNames = []string{expectProfileName} + wfe.certProfiles = map[string]string{expectProfileName: "description"} // Test that the newOrder endpoint returns the proper error if an invalid // profile is specified. @@ -3855,8 +3855,8 @@ func TestNewOrderWithProfile(t *testing.T) { test.AssertNotError(t, err, "Failed to unmarshal order response") test.AssertEquals(t, errorResp1["status"], "valid") - // Set the acceptable profiles to an empty list, the WFE should no longer accept any profiles. - wfe.certificateProfileNames = []string{} + // Set the acceptable profiles to the empty set, the WFE should no longer accept any profiles. + wfe.certProfiles = map[string]string{} responseWriter = httptest.NewRecorder() r = signAndPost(signer, newOrderPath, "http://localhost"+newOrderPath, validOrderBody) mux.ServeHTTP(responseWriter, r)