cli/third-party/github.com/letsencrypt/boulder/cmd/nonce-service/main.go
2025-05-30 12:50:20 -04:00

114 lines
3.6 KiB
Go

package notmain
import (
"context"
"flag"
"fmt"
"net"
"os"
"github.com/letsencrypt/boulder/cmd"
bgrpc "github.com/letsencrypt/boulder/grpc"
"github.com/letsencrypt/boulder/nonce"
noncepb "github.com/letsencrypt/boulder/nonce/proto"
)
type Config struct {
NonceService struct {
cmd.ServiceConfig
MaxUsed int
// UseDerivablePrefix indicates whether to use a nonce prefix derived
// from the gRPC listening address. If this is false, the nonce prefix
// will be the value of the NoncePrefix field. If this is true, the
// NoncePrefixKey field is required.
// TODO(#6610): Remove this.
//
// Deprecated: this value is ignored, and treated as though it is always true.
UseDerivablePrefix bool `validate:"-"`
// NoncePrefixKey is a secret used for deriving the prefix of each nonce
// instance. It should contain 256 bits (32 bytes) of random data to be
// suitable as an HMAC-SHA256 key (e.g. the output of `openssl rand -hex
// 32`). In a multi-DC deployment this value should be the same across
// all boulder-wfe and nonce-service instances.
NoncePrefixKey cmd.PasswordConfig `validate:"required"`
Syslog cmd.SyslogConfig
OpenTelemetry cmd.OpenTelemetryConfig
}
}
func derivePrefix(key string, grpcAddr string) (string, error) {
host, port, err := net.SplitHostPort(grpcAddr)
if err != nil {
return "", fmt.Errorf("parsing gRPC listen address: %w", err)
}
if host == "" {
return "", fmt.Errorf("nonce service gRPC address must include an IP address: got %q", grpcAddr)
}
if host != "" && port != "" {
hostIP := net.ParseIP(host)
if hostIP == nil {
return "", fmt.Errorf("gRPC address host part was not an IP address")
}
if hostIP.IsUnspecified() {
return "", fmt.Errorf("nonce service gRPC address must be a specific IP address: got %q", grpcAddr)
}
}
return nonce.DerivePrefix(grpcAddr, key), nil
}
func main() {
grpcAddr := flag.String("addr", "", "gRPC listen address override. Also used to derive the nonce prefix.")
debugAddr := flag.String("debug-addr", "", "Debug server address override")
configFile := flag.String("config", "", "File path to the configuration file for this service")
flag.Parse()
if *configFile == "" {
flag.Usage()
os.Exit(1)
}
var c Config
err := cmd.ReadConfigFile(*configFile, &c)
cmd.FailOnError(err, "Reading JSON config file into config structure")
if *grpcAddr != "" {
c.NonceService.GRPC.Address = *grpcAddr
}
if *debugAddr != "" {
c.NonceService.DebugAddr = *debugAddr
}
if c.NonceService.NoncePrefixKey.PasswordFile == "" {
cmd.Fail("NoncePrefixKey PasswordFile must be set")
}
key, err := c.NonceService.NoncePrefixKey.Pass()
cmd.FailOnError(err, "Failed to load 'noncePrefixKey' file.")
noncePrefix, err := derivePrefix(key, c.NonceService.GRPC.Address)
cmd.FailOnError(err, "Failed to derive nonce prefix")
scope, logger, oTelShutdown := cmd.StatsAndLogging(c.NonceService.Syslog, c.NonceService.OpenTelemetry, c.NonceService.DebugAddr)
defer oTelShutdown(context.Background())
logger.Info(cmd.VersionString())
ns, err := nonce.NewNonceService(scope, c.NonceService.MaxUsed, noncePrefix)
cmd.FailOnError(err, "Failed to initialize nonce service")
tlsConfig, err := c.NonceService.TLS.Load(scope)
cmd.FailOnError(err, "tlsConfig config")
nonceServer := nonce.NewServer(ns)
start, err := bgrpc.NewServer(c.NonceService.GRPC, logger).Add(
&noncepb.NonceService_ServiceDesc, nonceServer).Build(tlsConfig, scope, cmd.Clock())
cmd.FailOnError(err, "Unable to setup nonce service gRPC server")
cmd.FailOnError(start(), "Nonce service gRPC server failed")
}
func init() {
cmd.RegisterCommand("nonce-service", main, &cmd.ConfigValidator{Config: &Config{}})
}