github.com/letsencrypt/boulder@v0.20251208.0/cmd/nonce-service/main.go (about) 1 package notmain 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "net" 8 "net/netip" 9 "os" 10 11 "github.com/jmhodges/clock" 12 13 "github.com/letsencrypt/boulder/cmd" 14 bgrpc "github.com/letsencrypt/boulder/grpc" 15 "github.com/letsencrypt/boulder/nonce" 16 noncepb "github.com/letsencrypt/boulder/nonce/proto" 17 ) 18 19 type Config struct { 20 NonceService struct { 21 cmd.ServiceConfig 22 23 MaxUsed int 24 25 // NonceHMACKey is a path to a file containing an HMAC key which is a 26 // secret used for deriving the prefix of each nonce instance. It should 27 // contain 256 bits (32 bytes) of random data to be suitable as an 28 // HMAC-SHA256 key (e.g. the output of `openssl rand -hex 32`). In a 29 // multi-DC deployment this value should be the same across all 30 // boulder-wfe and nonce-service instances. 31 NonceHMACKey cmd.HMACKeyConfig `validate:"required"` 32 33 Syslog cmd.SyslogConfig 34 OpenTelemetry cmd.OpenTelemetryConfig 35 } 36 } 37 38 func derivePrefix(key []byte, grpcAddr string) (string, error) { 39 host, port, err := net.SplitHostPort(grpcAddr) 40 if err != nil { 41 return "", fmt.Errorf("parsing gRPC listen address: %w", err) 42 } 43 if host == "" { 44 return "", fmt.Errorf("nonce service gRPC address must include an IP address: got %q", grpcAddr) 45 } 46 if host != "" && port != "" { 47 hostIP, err := netip.ParseAddr(host) 48 if err != nil { 49 return "", fmt.Errorf("gRPC address host part was not an IP address") 50 } 51 if hostIP.IsUnspecified() { 52 return "", fmt.Errorf("nonce service gRPC address must be a specific IP address: got %q", grpcAddr) 53 } 54 } 55 return nonce.DerivePrefix(grpcAddr, key), nil 56 } 57 58 func main() { 59 grpcAddr := flag.String("addr", "", "gRPC listen address override. Also used to derive the nonce prefix.") 60 debugAddr := flag.String("debug-addr", "", "Debug server address override") 61 configFile := flag.String("config", "", "File path to the configuration file for this service") 62 flag.Parse() 63 64 if *configFile == "" { 65 flag.Usage() 66 os.Exit(1) 67 } 68 69 var c Config 70 err := cmd.ReadConfigFile(*configFile, &c) 71 cmd.FailOnError(err, "Reading JSON config file into config structure") 72 73 if *grpcAddr != "" { 74 c.NonceService.GRPC.Address = *grpcAddr 75 } 76 if *debugAddr != "" { 77 c.NonceService.DebugAddr = *debugAddr 78 } 79 80 key, err := c.NonceService.NonceHMACKey.Load() 81 cmd.FailOnError(err, "Failed to load nonceHMACKey file.") 82 83 noncePrefix, err := derivePrefix(key, c.NonceService.GRPC.Address) 84 cmd.FailOnError(err, "Failed to derive nonce prefix") 85 86 scope, logger, oTelShutdown := cmd.StatsAndLogging(c.NonceService.Syslog, c.NonceService.OpenTelemetry, c.NonceService.DebugAddr) 87 defer oTelShutdown(context.Background()) 88 logger.Info(cmd.VersionString()) 89 90 ns, err := nonce.NewNonceService(scope, c.NonceService.MaxUsed, noncePrefix) 91 cmd.FailOnError(err, "Failed to initialize nonce service") 92 93 tlsConfig, err := c.NonceService.TLS.Load(scope) 94 cmd.FailOnError(err, "tlsConfig config") 95 96 start, err := bgrpc.NewServer(c.NonceService.GRPC, logger).Add( 97 &noncepb.NonceService_ServiceDesc, ns).Build(tlsConfig, scope, clock.New()) 98 cmd.FailOnError(err, "Unable to setup nonce service gRPC server") 99 100 logger.Info(fmt.Sprintf("Nonce server listening on %s with prefix %q", c.NonceService.GRPC.Address, noncePrefix)) 101 102 cmd.FailOnError(start(), "Nonce service gRPC server failed") 103 } 104 105 func init() { 106 cmd.RegisterCommand("nonce-service", main, &cmd.ConfigValidator{Config: &Config{}}) 107 }