20
"fmt"
21
"io/ioutil"
22
"net/url"
23
+ "strconv"
24
"time"
25
26
"github.com/caddyserver/caddy/v2"
27
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
28
"github.com/caddyserver/certmagic"
29
"github.com/mholt/acmez"
30
"github.com/mholt/acmez/acme"
87
}
88
}
89
88
-// Provision sets up m.
89
-func (m *ACMEIssuer) Provision(ctx caddy.Context) error {
90
- m.logger = ctx.Logger(m)
90
+// Provision sets up iss.
91
+func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
92
+ iss.logger = ctx.Logger(iss)
93
94
// DNS providers
93
- if m.Challenges != nil && m.Challenges.DNS != nil && m.Challenges.DNS.ProviderRaw != nil {
94
- val, err := ctx.LoadModule(m.Challenges.DNS, "ProviderRaw")
95
+ if iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.ProviderRaw != nil {
96
+ val, err := ctx.LoadModule(iss.Challenges.DNS, "ProviderRaw")
97
if err != nil {
98
return fmt.Errorf("loading DNS provider module: %v", err)
99
}
106
// acmez.Solver type, so we use it directly. The user must set environment
107
// variables to configure it. Remove this shim once a sufficient number of
108
// DNS providers are implemented for the libdns APIs instead.
107
- m.Challenges.DNS.solver = deprecatedProvider
109
+ iss.Challenges.DNS.solver = deprecatedProvider
110
} else {
109
- m.Challenges.DNS.solver = &certmagic.DNS01Solver{
111
+ iss.Challenges.DNS.solver = &certmagic.DNS01Solver{
112
DNSProvider: val.(certmagic.ACMEDNSProvider),
111
- TTL: time.Duration(m.Challenges.DNS.TTL),
112
- PropagationTimeout: time.Duration(m.Challenges.DNS.PropagationTimeout),
113
+ TTL: time.Duration(iss.Challenges.DNS.TTL),
114
+ PropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout),
115
}
116
}
117
}
118
119
// add any custom CAs to trust store
118
- if len(m.TrustedRootsPEMFiles) > 0 {
119
- m.rootPool = x509.NewCertPool()
120
- for _, pemFile := range m.TrustedRootsPEMFiles {
120
+ if len(iss.TrustedRootsPEMFiles) > 0 {
121
+ iss.rootPool = x509.NewCertPool()
122
+ for _, pemFile := range iss.TrustedRootsPEMFiles {
123
pemData, err := ioutil.ReadFile(pemFile)
124
if err != nil {
125
return fmt.Errorf("loading trusted root CA's PEM file: %s: %v", pemFile, err)
126
}
125
- if !m.rootPool.AppendCertsFromPEM(pemData) {
127
+ if !iss.rootPool.AppendCertsFromPEM(pemData) {
128
return fmt.Errorf("unable to add %s to trust pool: %v", pemFile, err)
129
}
130
}
131
}
132
133
var err error
132
- m.template, err = m.makeIssuerTemplate()
134
+ iss.template, err = iss.makeIssuerTemplate()
135
if err != nil {
136
return err
137
}
139
return nil
140
}
141
140
-func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
142
+func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
143
template := certmagic.ACMEManager{
142
- CA: m.CA,
143
- TestCA: m.TestCA,
144
- Email: m.Email,
145
- CertObtainTimeout: time.Duration(m.ACMETimeout),
146
- TrustedRoots: m.rootPool,
147
- ExternalAccount: m.ExternalAccount,
148
- Logger: m.logger,
144
+ CA: iss.CA,
145
+ TestCA: iss.TestCA,
146
+ Email: iss.Email,
147
+ CertObtainTimeout: time.Duration(iss.ACMETimeout),
148
+ TrustedRoots: iss.rootPool,
149
+ ExternalAccount: iss.ExternalAccount,
150
+ Logger: iss.logger,
151
}
152
151
- if m.Challenges != nil {
152
- if m.Challenges.HTTP != nil {
153
- template.DisableHTTPChallenge = m.Challenges.HTTP.Disabled
154
- template.AltHTTPPort = m.Challenges.HTTP.AlternatePort
153
+ if iss.Challenges != nil {
154
+ if iss.Challenges.HTTP != nil {
155
+ template.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled
156
+ template.AltHTTPPort = iss.Challenges.HTTP.AlternatePort
157
}
156
- if m.Challenges.TLSALPN != nil {
157
- template.DisableTLSALPNChallenge = m.Challenges.TLSALPN.Disabled
158
- template.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort
158
+ if iss.Challenges.TLSALPN != nil {
159
+ template.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled
160
+ template.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort
161
}
160
- if m.Challenges.DNS != nil {
161
- template.DNS01Solver = m.Challenges.DNS.solver
162
+ if iss.Challenges.DNS != nil {
163
+ template.DNS01Solver = iss.Challenges.DNS.solver
164
}
163
- template.ListenHost = m.Challenges.BindHost
165
+ template.ListenHost = iss.Challenges.BindHost
166
}
167
168
return template, nil
172
// This is required because ACME needs values from the config in
173
// order to solve the challenges during issuance. This implements
174
// the ConfigSetter interface.
173
-func (m *ACMEIssuer) SetConfig(cfg *certmagic.Config) {
174
- m.magic = cfg
175
+func (iss *ACMEIssuer) SetConfig(cfg *certmagic.Config) {
176
+ iss.magic = cfg
177
}
178
179
// TODO: I kind of hate how each call to these methods needs to
181
// we find the right place to do that just once and then re-use?
182
183
// PreCheck implements the certmagic.PreChecker interface.
182
-func (m *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
183
- return certmagic.NewACMEManager(m.magic, m.template).PreCheck(ctx, names, interactive)
184
+func (iss *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
185
+ return certmagic.NewACMEManager(iss.magic, iss.template).PreCheck(ctx, names, interactive)
186
}
187
188
// Issue obtains a certificate for the given csr.
187
-func (m *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) {
188
- return certmagic.NewACMEManager(m.magic, m.template).Issue(ctx, csr)
189
+func (iss *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) {
190
+ return certmagic.NewACMEManager(iss.magic, iss.template).Issue(ctx, csr)
191
}
192
193
// IssuerKey returns the unique issuer key for the configured CA endpoint.
192
-func (m *ACMEIssuer) IssuerKey() string {
193
- return certmagic.NewACMEManager(m.magic, m.template).IssuerKey()
194
+func (iss *ACMEIssuer) IssuerKey() string {
195
+ return certmagic.NewACMEManager(iss.magic, iss.template).IssuerKey()
196
}
197
198
// Revoke revokes the given certificate.
197
-func (m *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error {
198
- return certmagic.NewACMEManager(m.magic, m.template).Revoke(ctx, cert, reason)
199
+func (iss *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error {
200
+ return certmagic.NewACMEManager(iss.magic, iss.template).Revoke(ctx, cert, reason)
201
+}
202
+
203
+// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because
204
+// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface
205
+// with only this method will succeed, and will still allow the embedded ACMEIssuer
206
+// to be accessed and manipulated.
207
+func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
208
+
209
+// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
210
+//
211
+// ... acme {
212
+// dir <directory_url>
213
+// test_dir <test_directory_url>
214
+// email <email>
215
+// timeout <duration>
216
+// disable_http_challenge
217
+// disable_tlsalpn_challenge
218
+// alt_http_port <port>
219
+// alt_tlsalpn_port <port>
220
+// eab <key_id> <mac_key>
221
+// trusted_roots <pem_files...>
222
+// }
223
+//
224
+func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
225
+ for d.Next() {
226
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
227
+ switch d.Val() {
228
+ case "dir":
229
+ if !d.AllArgs(&iss.CA) {
230
+ return d.ArgErr()
231
+ }
232
+
233
+ case "test_dir":
234
+ if !d.AllArgs(&iss.TestCA) {
235
+ return d.ArgErr()
236
+ }
237
+
238
+ case "email":
239
+ if !d.AllArgs(&iss.Email) {
240
+ return d.ArgErr()
241
+ }
242
+
243
+ case "timeout":
244
+ var timeoutStr string
245
+ if !d.AllArgs(&timeoutStr) {
246
+ return d.ArgErr()
247
+ }
248
+ timeout, err := caddy.ParseDuration(timeoutStr)
249
+ if err != nil {
250
+ return d.Errf("invalid timeout duration %s: %v", timeoutStr, err)
251
+ }
252
+ iss.ACMETimeout = caddy.Duration(timeout)
253
+
254
+ case "disable_http_challenge":
255
+ if d.NextArg() {
256
+ return d.ArgErr()
257
+ }
258
+ if iss.Challenges == nil {
259
+ iss.Challenges = new(ChallengesConfig)
260
+ }
261
+ if iss.Challenges.HTTP == nil {
262
+ iss.Challenges.HTTP = new(HTTPChallengeConfig)
263
+ }
264
+ iss.Challenges.HTTP.Disabled = true
265
+
266
+ case "disable_tlsalpn_challenge":
267
+ if d.NextArg() {
268
+ return d.ArgErr()
269
+ }
270
+ if iss.Challenges == nil {
271
+ iss.Challenges = new(ChallengesConfig)
272
+ }
273
+ if iss.Challenges.TLSALPN == nil {
274
+ iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
275
+ }
276
+ iss.Challenges.TLSALPN.Disabled = true
277
+
278
+ case "alt_http_port":
279
+ if !d.NextArg() {
280
+ return d.ArgErr()
281
+ }
282
+ port, err := strconv.Atoi(d.Val())
283
+ if err != nil {
284
+ return d.Errf("invalid port %s: %v", d.Val(), err)
285
+ }
286
+ if iss.Challenges == nil {
287
+ iss.Challenges = new(ChallengesConfig)
288
+ }
289
+ if iss.Challenges.HTTP == nil {
290
+ iss.Challenges.HTTP = new(HTTPChallengeConfig)
291
+ }
292
+ iss.Challenges.HTTP.AlternatePort = port
293
+
294
+ case "alt_tlsalpn_port":
295
+ if !d.NextArg() {
296
+ return d.ArgErr()
297
+ }
298
+ port, err := strconv.Atoi(d.Val())
299
+ if err != nil {
300
+ return d.Errf("invalid port %s: %v", d.Val(), err)
301
+ }
302
+ if iss.Challenges == nil {
303
+ iss.Challenges = new(ChallengesConfig)
304
+ }
305
+ if iss.Challenges.TLSALPN == nil {
306
+ iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
307
+ }
308
+ iss.Challenges.TLSALPN.AlternatePort = port
309
+
310
+ case "eab":
311
+ iss.ExternalAccount = new(acme.EAB)
312
+ if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) {
313
+ return d.ArgErr()
314
+ }
315
+
316
+ case "trusted_roots":
317
+ iss.TrustedRootsPEMFiles = d.RemainingArgs()
318
+
319
+ default:
320
+ return d.Errf("unrecognized ACME issuer property: %s", d.Val())
321
+ }
322
+ }
323
+ }
324
+ return nil
325
}
326
327
// onDemandAskRequest makes a request to the ask URL
354
355
// Interface guards
356
var (
231
- _ certmagic.PreChecker = (*ACMEIssuer)(nil)
232
- _ certmagic.Issuer = (*ACMEIssuer)(nil)
233
- _ certmagic.Revoker = (*ACMEIssuer)(nil)
234
- _ caddy.Provisioner = (*ACMEIssuer)(nil)
235
- _ ConfigSetter = (*ACMEIssuer)(nil)
357
+ _ certmagic.PreChecker = (*ACMEIssuer)(nil)
358
+ _ certmagic.Issuer = (*ACMEIssuer)(nil)
359
+ _ certmagic.Revoker = (*ACMEIssuer)(nil)
360
+ _ caddy.Provisioner = (*ACMEIssuer)(nil)
361
+ _ ConfigSetter = (*ACMEIssuer)(nil)
362
+ _ caddyfile.Unmarshaler = (*ACMEIssuer)(nil)
363
)