github.com/letsencrypt/boulder@v0.20251208.0/cmd/boulder-ra/main.go (about)

     1  package notmain
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/jmhodges/clock"
    10  
    11  	capb "github.com/letsencrypt/boulder/ca/proto"
    12  	"github.com/letsencrypt/boulder/cmd"
    13  	"github.com/letsencrypt/boulder/config"
    14  	"github.com/letsencrypt/boulder/ctpolicy"
    15  	"github.com/letsencrypt/boulder/ctpolicy/ctconfig"
    16  	"github.com/letsencrypt/boulder/ctpolicy/loglist"
    17  	"github.com/letsencrypt/boulder/features"
    18  	"github.com/letsencrypt/boulder/goodkey"
    19  	"github.com/letsencrypt/boulder/goodkey/sagoodkey"
    20  	bgrpc "github.com/letsencrypt/boulder/grpc"
    21  	"github.com/letsencrypt/boulder/issuance"
    22  	"github.com/letsencrypt/boulder/policy"
    23  	pubpb "github.com/letsencrypt/boulder/publisher/proto"
    24  	"github.com/letsencrypt/boulder/ra"
    25  	rapb "github.com/letsencrypt/boulder/ra/proto"
    26  	"github.com/letsencrypt/boulder/ratelimits"
    27  	bredis "github.com/letsencrypt/boulder/redis"
    28  	sapb "github.com/letsencrypt/boulder/sa/proto"
    29  	"github.com/letsencrypt/boulder/va"
    30  	vapb "github.com/letsencrypt/boulder/va/proto"
    31  )
    32  
    33  type Config struct {
    34  	RA struct {
    35  		cmd.ServiceConfig
    36  		cmd.HostnamePolicyConfig
    37  
    38  		// RateLimitPoliciesFilename is deprecated.
    39  		RateLimitPoliciesFilename string
    40  
    41  		MaxContactsPerRegistration int
    42  
    43  		SAService        *cmd.GRPCClientConfig
    44  		VAService        *cmd.GRPCClientConfig
    45  		CAService        *cmd.GRPCClientConfig
    46  		PublisherService *cmd.GRPCClientConfig
    47  
    48  		// Deprecated: TODO(#8349): Remove this when removing the corresponding
    49  		// service from the CA.
    50  		OCSPService *cmd.GRPCClientConfig
    51  
    52  		Limiter struct {
    53  			// Redis contains the configuration necessary to connect to Redis
    54  			// for rate limiting. This field is required to enable rate
    55  			// limiting.
    56  			Redis *bredis.Config `validate:"required_with=Defaults"`
    57  
    58  			// Defaults is a path to a YAML file containing default rate limits.
    59  			// See: ratelimits/README.md for details. This field is required to
    60  			// enable rate limiting. If any individual rate limit is not set,
    61  			// that limit will be disabled. Limits passed in this file must be
    62  			// identical to those in the WFE.
    63  			//
    64  			// Note: At this time, only the Failed Authorizations rate limit is
    65  			// necessary in the RA.
    66  			Defaults string `validate:"required_with=Redis"`
    67  
    68  			// Overrides is a path to a YAML file containing overrides for the
    69  			// default rate limits. See: ratelimits/README.md for details. If
    70  			// neither this field nor OverridesFromDB is set, all requesters
    71  			// will be subject to the default rate limits. Overrides passed in
    72  			// this file must be identical to those in the WFE.
    73  			//
    74  			// Note: At this time, only the Failed Authorizations overrides are
    75  			// necessary in the RA.
    76  			Overrides string
    77  
    78  			// OverridesFromDB causes the WFE and RA to retrieve rate limit overrides
    79  			// from the database, instead of from a file.
    80  			OverridesFromDB bool
    81  		}
    82  
    83  		// MaxNames is the maximum number of subjectAltNames in a single cert.
    84  		// The value supplied MUST be greater than 0 and no more than 100. These
    85  		// limits are per section 7.1 of our combined CP/CPS, under "DV-SSL
    86  		// Subscriber Certificate". The value must match the CA and WFE
    87  		// configurations.
    88  		//
    89  		// Deprecated: Set ValidationProfiles[*].MaxNames instead.
    90  		MaxNames int `validate:"omitempty,min=1,max=100"`
    91  
    92  		// ValidationProfiles is a map of validation profiles to their
    93  		// respective issuance allow lists. If a profile is not included in this
    94  		// mapping, it cannot be used by any account. If this field is left
    95  		// empty, all profiles are open to all accounts.
    96  		ValidationProfiles map[string]*ra.ValidationProfileConfig `validate:"required"`
    97  
    98  		// DefaultProfileName sets the profile to use if one wasn't provided by the
    99  		// client in the new-order request. Must match a configured validation
   100  		// profile or the RA will fail to start. Must match a certificate profile
   101  		// configured in the CA or finalization will fail for orders using this
   102  		// default.
   103  		DefaultProfileName string `validate:"required"`
   104  
   105  		// GoodKey is an embedded config stanza for the goodkey library.
   106  		GoodKey goodkey.Config
   107  
   108  		// FinalizeTimeout is how long the RA is willing to wait for the Order
   109  		// finalization process to take. This config parameter only has an effect
   110  		// if the AsyncFinalization feature flag is enabled. Any systems which
   111  		// manage the shutdown of an RA must be willing to wait at least this long
   112  		// after sending the shutdown signal, to allow background goroutines to
   113  		// complete.
   114  		FinalizeTimeout config.Duration `validate:"-"`
   115  
   116  		// CTLogs contains groupings of CT logs organized by what organization
   117  		// operates them. When we submit precerts to logs in order to get SCTs, we
   118  		// will submit the cert to one randomly-chosen log from each group, and use
   119  		// the SCTs from the first two groups which reply. This allows us to comply
   120  		// with various CT policies that require (for certs with short lifetimes
   121  		// like ours) two SCTs from logs run by different operators. It also holds
   122  		// a `Stagger` value controlling how long we wait for one operator group
   123  		// to respond before trying a different one.
   124  		CTLogs ctconfig.CTConfig
   125  
   126  		// IssuerCerts are paths to all intermediate certificates which may have
   127  		// been used to issue certificates in the last 90 days. These are used to
   128  		// generate OCSP URLs to purge during revocation.
   129  		IssuerCerts []string `validate:"min=1,dive,required"`
   130  
   131  		Features features.Config
   132  	}
   133  
   134  	PA cmd.PAConfig
   135  
   136  	Syslog        cmd.SyslogConfig
   137  	OpenTelemetry cmd.OpenTelemetryConfig
   138  }
   139  
   140  func main() {
   141  	grpcAddr := flag.String("addr", "", "gRPC listen address override")
   142  	debugAddr := flag.String("debug-addr", "", "Debug server address override")
   143  	configFile := flag.String("config", "", "File path to the configuration file for this service")
   144  	flag.Parse()
   145  	if *configFile == "" {
   146  		flag.Usage()
   147  		os.Exit(1)
   148  	}
   149  
   150  	var c Config
   151  	err := cmd.ReadConfigFile(*configFile, &c)
   152  	cmd.FailOnError(err, "Reading JSON config file into config structure")
   153  
   154  	features.Set(c.RA.Features)
   155  
   156  	if *grpcAddr != "" {
   157  		c.RA.GRPC.Address = *grpcAddr
   158  	}
   159  	if *debugAddr != "" {
   160  		c.RA.DebugAddr = *debugAddr
   161  	}
   162  
   163  	scope, logger, oTelShutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.RA.DebugAddr)
   164  	defer oTelShutdown(context.Background())
   165  	logger.Info(cmd.VersionString())
   166  
   167  	// Validate PA config and set defaults if needed
   168  	cmd.FailOnError(c.PA.CheckChallenges(), "Invalid PA configuration")
   169  	cmd.FailOnError(c.PA.CheckIdentifiers(), "Invalid PA configuration")
   170  
   171  	pa, err := policy.New(c.PA.Identifiers, c.PA.Challenges, logger)
   172  	cmd.FailOnError(err, "Couldn't create PA")
   173  
   174  	if c.RA.HostnamePolicyFile == "" {
   175  		cmd.Fail("HostnamePolicyFile must be provided.")
   176  	}
   177  	err = pa.LoadIdentPolicyFile(c.RA.HostnamePolicyFile)
   178  	cmd.FailOnError(err, "Couldn't load identifier policy file")
   179  
   180  	tlsConfig, err := c.RA.TLS.Load(scope)
   181  	cmd.FailOnError(err, "TLS config")
   182  
   183  	clk := clock.New()
   184  
   185  	vaConn, err := bgrpc.ClientSetup(c.RA.VAService, tlsConfig, scope, clk)
   186  	cmd.FailOnError(err, "Unable to create VA client")
   187  	vac := vapb.NewVAClient(vaConn)
   188  	caaClient := vapb.NewCAAClient(vaConn)
   189  
   190  	caConn, err := bgrpc.ClientSetup(c.RA.CAService, tlsConfig, scope, clk)
   191  	cmd.FailOnError(err, "Unable to create CA client")
   192  	cac := capb.NewCertificateAuthorityClient(caConn)
   193  
   194  	saConn, err := bgrpc.ClientSetup(c.RA.SAService, tlsConfig, scope, clk)
   195  	cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")
   196  	sac := sapb.NewStorageAuthorityClient(saConn)
   197  
   198  	conn, err := bgrpc.ClientSetup(c.RA.PublisherService, tlsConfig, scope, clk)
   199  	cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to Publisher")
   200  	pubc := pubpb.NewPublisherClient(conn)
   201  
   202  	issuerCertPaths := c.RA.IssuerCerts
   203  	issuerCerts := make([]*issuance.Certificate, len(issuerCertPaths))
   204  	for i, issuerCertPath := range issuerCertPaths {
   205  		issuerCerts[i], err = issuance.LoadCertificate(issuerCertPath)
   206  		cmd.FailOnError(err, "Failed to load issuer certificate")
   207  	}
   208  
   209  	// Boulder's components assume that there will always be CT logs configured.
   210  	// Issuing a certificate without SCTs embedded is a misissuance event as per
   211  	// our CPS 4.4.2, which declares we will always include at least two SCTs.
   212  	// Exit early if no groups are configured.
   213  	var ctp *ctpolicy.CTPolicy
   214  	if len(c.RA.CTLogs.SCTLogs) <= 0 {
   215  		cmd.Fail("Must configure CTLogs")
   216  	}
   217  
   218  	allLogs, err := loglist.New(c.RA.CTLogs.LogListFile)
   219  	cmd.FailOnError(err, "Failed to parse log list")
   220  
   221  	sctLogs, err := allLogs.SubsetForPurpose(c.RA.CTLogs.SCTLogs, loglist.Issuance, c.RA.CTLogs.SubmitToTestLogs)
   222  	cmd.FailOnError(err, "Failed to load SCT logs")
   223  
   224  	infoLogs, err := allLogs.SubsetForPurpose(c.RA.CTLogs.InfoLogs, loglist.Informational, true)
   225  	cmd.FailOnError(err, "Failed to load informational logs")
   226  
   227  	finalLogs, err := allLogs.SubsetForPurpose(c.RA.CTLogs.FinalLogs, loglist.Informational, true)
   228  	cmd.FailOnError(err, "Failed to load final logs")
   229  
   230  	ctp = ctpolicy.New(pubc, sctLogs, infoLogs, finalLogs, c.RA.CTLogs.Stagger.Duration, logger, scope)
   231  
   232  	if len(c.RA.ValidationProfiles) == 0 {
   233  		cmd.Fail("At least one profile must be configured")
   234  	}
   235  
   236  	// TODO(#7993): Remove this fallback and make ValidationProfile.MaxNames a
   237  	// required config field. We don't do any validation on the value of this
   238  	// top-level MaxNames because that happens inside the call to
   239  	// NewValidationProfiles below.
   240  	for _, pc := range c.RA.ValidationProfiles {
   241  		if pc.MaxNames == 0 {
   242  			pc.MaxNames = c.RA.MaxNames
   243  		}
   244  	}
   245  
   246  	validationProfiles, err := ra.NewValidationProfiles(c.RA.DefaultProfileName, c.RA.ValidationProfiles)
   247  	cmd.FailOnError(err, "Failed to load validation profiles")
   248  
   249  	if features.Get().AsyncFinalize && c.RA.FinalizeTimeout.Duration == 0 {
   250  		cmd.Fail("finalizeTimeout must be supplied when AsyncFinalize feature is enabled")
   251  	}
   252  
   253  	kp, err := sagoodkey.NewPolicy(&c.RA.GoodKey, sac.KeyBlocked)
   254  	cmd.FailOnError(err, "Unable to create key policy")
   255  
   256  	var limiter *ratelimits.Limiter
   257  	var txnBuilder *ratelimits.TransactionBuilder
   258  	var limiterRedis *bredis.Ring
   259  	if c.RA.Limiter.Defaults != "" {
   260  		// Setup rate limiting.
   261  		limiterRedis, err = bredis.NewRingFromConfig(*c.RA.Limiter.Redis, scope, logger)
   262  		cmd.FailOnError(err, "Failed to create Redis ring")
   263  
   264  		source := ratelimits.NewRedisSource(limiterRedis.Ring, clk, scope)
   265  		limiter, err = ratelimits.NewLimiter(clk, source, scope)
   266  		cmd.FailOnError(err, "Failed to create rate limiter")
   267  		if c.RA.Limiter.OverridesFromDB {
   268  			if c.RA.Limiter.Overrides != "" {
   269  				cmd.Fail("OverridesFromDB and an overrides file were both defined, but are mutually exclusive")
   270  			}
   271  			saroc := sapb.NewStorageAuthorityReadOnlyClient(saConn)
   272  			txnBuilder, err = ratelimits.NewTransactionBuilderFromDatabase(c.RA.Limiter.Defaults, saroc.GetEnabledRateLimitOverrides, scope, logger)
   273  		} else {
   274  			txnBuilder, err = ratelimits.NewTransactionBuilderFromFiles(c.RA.Limiter.Defaults, c.RA.Limiter.Overrides, scope, logger)
   275  		}
   276  		cmd.FailOnError(err, "Failed to create rate limits transaction builder")
   277  
   278  		// The 30 minute period here must be kept in sync with the promise
   279  		// (successCommentBody) made to requesters in sfe/overridesimporter.go
   280  		overrideRefresherShutdown := txnBuilder.NewRefresher(30 * time.Minute)
   281  		defer overrideRefresherShutdown()
   282  	}
   283  
   284  	rai := ra.NewRegistrationAuthorityImpl(
   285  		clk,
   286  		logger,
   287  		scope,
   288  		c.RA.MaxContactsPerRegistration,
   289  		kp,
   290  		limiter,
   291  		txnBuilder,
   292  		c.RA.MaxNames,
   293  		validationProfiles,
   294  		pubc,
   295  		c.RA.FinalizeTimeout.Duration,
   296  		ctp,
   297  		issuerCerts,
   298  	)
   299  	defer rai.Drain()
   300  
   301  	rai.PA = pa
   302  
   303  	rai.VA = va.RemoteClients{
   304  		VAClient:  vac,
   305  		CAAClient: caaClient,
   306  	}
   307  	rai.CA = cac
   308  	rai.SA = sac
   309  
   310  	start, err := bgrpc.NewServer(c.RA.GRPC, logger).Add(
   311  		&rapb.RegistrationAuthority_ServiceDesc, rai).Add(
   312  		&rapb.SCTProvider_ServiceDesc, rai).
   313  		Build(tlsConfig, scope, clk)
   314  	cmd.FailOnError(err, "Unable to setup RA gRPC server")
   315  
   316  	cmd.FailOnError(start(), "RA gRPC service failed")
   317  }
   318  
   319  func init() {
   320  	cmd.RegisterCommand("boulder-ra", main, &cmd.ConfigValidator{Config: &Config{}})
   321  }