1 package notmain
2
3 import (
4 "context"
5 "flag"
6 "os"
7
8 "github.com/prometheus/client_golang/prometheus"
9
10 "github.com/letsencrypt/boulder/ca"
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/loglist"
15 "github.com/letsencrypt/boulder/features"
16 "github.com/letsencrypt/boulder/goodkey"
17 "github.com/letsencrypt/boulder/goodkey/sagoodkey"
18 bgrpc "github.com/letsencrypt/boulder/grpc"
19 "github.com/letsencrypt/boulder/issuance"
20 "github.com/letsencrypt/boulder/linter"
21 "github.com/letsencrypt/boulder/policy"
22 sapb "github.com/letsencrypt/boulder/sa/proto"
23 )
24
25 type Config struct {
26 CA struct {
27 cmd.ServiceConfig
28
29 cmd.HostnamePolicyConfig
30
31 GRPCCA *cmd.GRPCServerConfig
32
33 SAService *cmd.GRPCClientConfig
34
35
36 Issuance struct {
37 Profile issuance.ProfileConfig
38 Issuers []issuance.IssuerConfig `validate:"min=1,dive"`
39 IgnoredLints []string
40 }
41
42
43 Expiry config.Duration
44
45
46 Backdate config.Duration
47
48
49 SerialPrefix int `validate:"required,min=1,max=255"`
50
51
52 MaxNames int `validate:"required,min=1,max=100"`
53
54
55
56 LifespanOCSP config.Duration
57
58
59
60
61 LifespanCRL config.Duration
62
63
64 GoodKey goodkey.Config
65
66
67
68
69 OCSPLogMaxLength int
70
71
72
73
74
75
76
77
78 OCSPLogPeriod config.Duration
79
80
81
82 ECDSAAllowListFilename string
83
84
85
86
87 CTLogListFile string
88
89
90
91
92 CRLDPBase string `validate:"required,url,startswith=http://,endsnotwith=/"`
93
94
95
96 DisableCertService bool
97
98
99 DisableOCSPService bool
100
101
102 DisableCRLService bool
103
104 Features map[string]bool
105 }
106
107 PA cmd.PAConfig
108
109 Syslog cmd.SyslogConfig
110 OpenTelemetry cmd.OpenTelemetryConfig
111 }
112
113 func loadBoulderIssuers(profileConfig issuance.ProfileConfig, issuerConfigs []issuance.IssuerConfig, ignoredLints []string) ([]*issuance.Issuer, error) {
114 issuers := make([]*issuance.Issuer, 0, len(issuerConfigs))
115 for _, issuerConfig := range issuerConfigs {
116 profile, err := issuance.NewProfile(profileConfig, issuerConfig)
117 if err != nil {
118 return nil, err
119 }
120
121 cert, signer, err := issuance.LoadIssuer(issuerConfig.Location)
122 if err != nil {
123 return nil, err
124 }
125
126 linter, err := linter.New(cert.Certificate, signer, ignoredLints)
127 if err != nil {
128 return nil, err
129 }
130
131 issuer, err := issuance.NewIssuer(cert, signer, profile, linter, cmd.Clock())
132 if err != nil {
133 return nil, err
134 }
135
136 issuers = append(issuers, issuer)
137 }
138 return issuers, nil
139 }
140
141 func main() {
142 grpcAddr := flag.String("addr", "", "gRPC listen address override")
143 debugAddr := flag.String("debug-addr", "", "Debug server address override")
144 configFile := flag.String("config", "", "File path to the configuration file for this service")
145 flag.Parse()
146 if *configFile == "" {
147 flag.Usage()
148 os.Exit(1)
149 }
150
151 var c Config
152 err := cmd.ReadConfigFile(*configFile, &c)
153 cmd.FailOnError(err, "Reading JSON config file into config structure")
154
155 err = features.Set(c.CA.Features)
156 cmd.FailOnError(err, "Failed to set feature flags")
157
158 if *grpcAddr != "" {
159 c.CA.GRPCCA.Address = *grpcAddr
160 }
161 if *debugAddr != "" {
162 c.CA.DebugAddr = *debugAddr
163 }
164
165 if c.CA.MaxNames == 0 {
166 cmd.Fail("Error in CA config: MaxNames must not be 0")
167 }
168
169 scope, logger, oTelShutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.CA.DebugAddr)
170 defer oTelShutdown(context.Background())
171 logger.Info(cmd.VersionString())
172
173
174
175 signatureCount := prometheus.NewCounterVec(
176 prometheus.CounterOpts{
177 Name: "signatures",
178 Help: "Number of signatures",
179 },
180 []string{"purpose", "issuer"})
181 scope.MustRegister(signatureCount)
182
183 signErrorCount := prometheus.NewCounterVec(prometheus.CounterOpts{
184 Name: "signature_errors",
185 Help: "A counter of signature errors labelled by error type",
186 }, []string{"type"})
187 scope.MustRegister(signErrorCount)
188
189 cmd.FailOnError(c.PA.CheckChallenges(), "Invalid PA configuration")
190
191 pa, err := policy.New(c.PA.Challenges, logger)
192 cmd.FailOnError(err, "Couldn't create PA")
193
194 if c.CA.HostnamePolicyFile == "" {
195 cmd.Fail("HostnamePolicyFile was empty")
196 }
197 err = pa.LoadHostnamePolicyFile(c.CA.HostnamePolicyFile)
198 cmd.FailOnError(err, "Couldn't load hostname policy file")
199
200
201
202 if c.CA.CTLogListFile != "" {
203 err = loglist.InitLintList(c.CA.CTLogListFile)
204 cmd.FailOnError(err, "Failed to load CT Log List")
205 }
206
207 var boulderIssuers []*issuance.Issuer
208 boulderIssuers, err = loadBoulderIssuers(c.CA.Issuance.Profile, c.CA.Issuance.Issuers, c.CA.Issuance.IgnoredLints)
209 cmd.FailOnError(err, "Couldn't load issuers")
210
211 tlsConfig, err := c.CA.TLS.Load(scope)
212 cmd.FailOnError(err, "TLS config")
213
214 clk := cmd.Clock()
215
216 conn, err := bgrpc.ClientSetup(c.CA.SAService, tlsConfig, scope, clk)
217 cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")
218 sa := sapb.NewStorageAuthorityClient(conn)
219
220 kp, err := sagoodkey.NewKeyPolicy(&c.CA.GoodKey, sa.KeyBlocked)
221 cmd.FailOnError(err, "Unable to create key policy")
222
223 var ecdsaAllowList *ca.ECDSAAllowList
224 var entries int
225 if c.CA.ECDSAAllowListFilename != "" {
226
227 ecdsaAllowList, entries, err = ca.NewECDSAAllowListFromFile(c.CA.ECDSAAllowListFilename)
228 cmd.FailOnError(err, "Unable to load ECDSA allow list from YAML file")
229 logger.Infof("Loaded an ECDSA allow list with %d entries", entries)
230 }
231
232 srv := bgrpc.NewServer(c.CA.GRPCCA, logger)
233
234 if !c.CA.DisableOCSPService {
235 ocspi, err := ca.NewOCSPImpl(
236 boulderIssuers,
237 c.CA.LifespanOCSP.Duration,
238 c.CA.OCSPLogMaxLength,
239 c.CA.OCSPLogPeriod.Duration,
240 logger,
241 scope,
242 signatureCount,
243 signErrorCount,
244 clk,
245 )
246 cmd.FailOnError(err, "Failed to create OCSP impl")
247 go ocspi.LogOCSPLoop()
248 defer ocspi.Stop()
249
250 srv = srv.Add(&capb.OCSPGenerator_ServiceDesc, ocspi)
251 }
252
253 if !c.CA.DisableCRLService {
254 crli, err := ca.NewCRLImpl(
255 boulderIssuers,
256 c.CA.LifespanCRL.Duration,
257 c.CA.CRLDPBase,
258 c.CA.OCSPLogMaxLength,
259 logger,
260 )
261 cmd.FailOnError(err, "Failed to create CRL impl")
262
263 srv = srv.Add(&capb.CRLGenerator_ServiceDesc, crli)
264 }
265
266 if !c.CA.DisableCertService {
267 cai, err := ca.NewCertificateAuthorityImpl(
268 sa,
269 pa,
270 boulderIssuers,
271 ecdsaAllowList,
272 c.CA.Expiry.Duration,
273 c.CA.Backdate.Duration,
274 c.CA.SerialPrefix,
275 c.CA.MaxNames,
276 kp,
277 logger,
278 scope,
279 signatureCount,
280 signErrorCount,
281 clk)
282 cmd.FailOnError(err, "Failed to create CA impl")
283
284 srv = srv.Add(&capb.CertificateAuthority_ServiceDesc, cai)
285 }
286
287 start, err := srv.Build(tlsConfig, scope, clk)
288 cmd.FailOnError(err, "Unable to setup CA gRPC server")
289
290 cmd.FailOnError(start(), "CA gRPC service failed")
291 }
292
293 func init() {
294 cmd.RegisterCommand("boulder-ca", main, &cmd.ConfigValidator{Config: &Config{}})
295 }
296
View as plain text