github.com/letsencrypt/boulder@v0.20251208.0/cmd/crl-storer/main.go (about) 1 package notmain 2 3 import ( 4 "context" 5 "flag" 6 "net/http" 7 "os" 8 9 "github.com/aws/aws-sdk-go-v2/aws" 10 "github.com/aws/aws-sdk-go-v2/config" 11 "github.com/aws/aws-sdk-go-v2/service/s3" 12 awsl "github.com/aws/smithy-go/logging" 13 "github.com/jmhodges/clock" 14 15 "github.com/letsencrypt/boulder/cmd" 16 "github.com/letsencrypt/boulder/crl/storer" 17 cspb "github.com/letsencrypt/boulder/crl/storer/proto" 18 "github.com/letsencrypt/boulder/features" 19 bgrpc "github.com/letsencrypt/boulder/grpc" 20 "github.com/letsencrypt/boulder/issuance" 21 blog "github.com/letsencrypt/boulder/log" 22 ) 23 24 type Config struct { 25 CRLStorer struct { 26 cmd.ServiceConfig 27 28 // IssuerCerts is a list of paths to issuer certificates on disk. These will 29 // be used to validate the CRLs received by this service before uploading 30 // them. 31 IssuerCerts []string `validate:"min=1,dive,required"` 32 33 // S3Endpoint is the URL at which the S3-API-compatible object storage 34 // service can be reached. This can be used to point to a non-Amazon storage 35 // service, or to point to a fake service for testing. It should be left 36 // blank by default. 37 S3Endpoint string 38 // S3Bucket is the AWS Bucket that uploads should go to. Must be created 39 // (and have appropriate permissions set) beforehand. 40 S3Bucket string 41 // AWSConfigFile is the path to a file on disk containing an AWS config. 42 // The format of the configuration file is specified at 43 // https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html. 44 AWSConfigFile string 45 // AWSCredsFile is the path to a file on disk containing AWS credentials. 46 // The format of the credentials file is specified at 47 // https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html. 48 AWSCredsFile string 49 50 Features features.Config 51 } 52 53 Syslog cmd.SyslogConfig 54 OpenTelemetry cmd.OpenTelemetryConfig 55 } 56 57 // awsLogger implements the github.com/aws/smithy-go/logging.Logger interface. 58 type awsLogger struct { 59 blog.Logger 60 } 61 62 func (log awsLogger) Logf(c awsl.Classification, format string, v ...any) { 63 switch c { 64 case awsl.Debug: 65 log.Debugf(format, v...) 66 case awsl.Warn: 67 log.Warningf(format, v...) 68 } 69 } 70 71 func main() { 72 grpcAddr := flag.String("addr", "", "gRPC listen address override") 73 debugAddr := flag.String("debug-addr", "", "Debug server address override") 74 configFile := flag.String("config", "", "File path to the configuration file for this service") 75 flag.Parse() 76 if *configFile == "" { 77 flag.Usage() 78 os.Exit(1) 79 } 80 81 var c Config 82 err := cmd.ReadConfigFile(*configFile, &c) 83 cmd.FailOnError(err, "Reading JSON config file into config structure") 84 85 features.Set(c.CRLStorer.Features) 86 87 if *grpcAddr != "" { 88 c.CRLStorer.GRPC.Address = *grpcAddr 89 } 90 if *debugAddr != "" { 91 c.CRLStorer.DebugAddr = *debugAddr 92 } 93 94 scope, logger, oTelShutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.CRLStorer.DebugAddr) 95 defer oTelShutdown(context.Background()) 96 logger.Info(cmd.VersionString()) 97 clk := clock.New() 98 99 tlsConfig, err := c.CRLStorer.TLS.Load(scope) 100 cmd.FailOnError(err, "TLS config") 101 102 issuers := make([]*issuance.Certificate, 0, len(c.CRLStorer.IssuerCerts)) 103 for _, filepath := range c.CRLStorer.IssuerCerts { 104 cert, err := issuance.LoadCertificate(filepath) 105 cmd.FailOnError(err, "Failed to load issuer cert") 106 issuers = append(issuers, cert) 107 } 108 109 // Load the "default" AWS configuration, but override the set of config and 110 // credential files it reads from to just those specified in our JSON config, 111 // to ensure that it's not accidentally reading anything from the homedir or 112 // its other default config locations. 113 awsConfig, err := config.LoadDefaultConfig( 114 context.Background(), 115 config.WithSharedConfigFiles([]string{c.CRLStorer.AWSConfigFile}), 116 config.WithSharedCredentialsFiles([]string{c.CRLStorer.AWSCredsFile}), 117 config.WithHTTPClient(new(http.Client)), 118 config.WithLogger(awsLogger{logger}), 119 config.WithClientLogMode(aws.LogRequestEventMessage|aws.LogResponseEventMessage), 120 ) 121 cmd.FailOnError(err, "Failed to load AWS config") 122 123 s3opts := make([]func(*s3.Options), 0) 124 if c.CRLStorer.S3Endpoint != "" { 125 s3opts = append( 126 s3opts, 127 s3.WithEndpointResolver(s3.EndpointResolverFromURL(c.CRLStorer.S3Endpoint)), 128 func(o *s3.Options) { o.UsePathStyle = true }, 129 ) 130 } 131 s3client := s3.NewFromConfig(awsConfig, s3opts...) 132 133 csi, err := storer.New(issuers, s3client, c.CRLStorer.S3Bucket, scope, logger, clk) 134 cmd.FailOnError(err, "Failed to create CRLStorer impl") 135 136 start, err := bgrpc.NewServer(c.CRLStorer.GRPC, logger).Add( 137 &cspb.CRLStorer_ServiceDesc, csi).Build(tlsConfig, scope, clk) 138 cmd.FailOnError(err, "Unable to setup CRLStorer gRPC server") 139 140 cmd.FailOnError(start(), "CRLStorer gRPC service failed") 141 } 142 143 func init() { 144 cmd.RegisterCommand("crl-storer", main, &cmd.ConfigValidator{Config: &Config{}}) 145 }