Skip to content

Commit 2e4ab94

Browse files
committed
Add Caddyfile support
1 parent 8b05eb4 commit 2e4ab94

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

handler.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"github.com/caddyserver/caddy/v2"
99
"github.com/caddyserver/caddy/v2/caddyconfig"
10+
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
11+
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
1012
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
1113
"github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth"
1214
"github.com/caddyserver/certmagic"
@@ -18,6 +20,12 @@ import (
1820

1921
func init() {
2022
caddy.RegisterModule(Handler{})
23+
httpcaddyfile.RegisterHandlerDirective("dns01proxy", parseHandler)
24+
httpcaddyfile.RegisterDirectiveOrder(
25+
"dns01proxy",
26+
httpcaddyfile.After,
27+
"acme_server",
28+
)
2129
}
2230

2331
// A Caddy `http.handlers` module that implements the dns01proxy API.
@@ -43,6 +51,7 @@ type Handler struct {
4351
var _ caddy.Module = (*Handler)(nil)
4452
var _ caddy.Provisioner = (*Handler)(nil)
4553
var _ caddyhttp.MiddlewareHandler = (*Handler)(nil)
54+
var _ caddyfile.Unmarshaler = (*Handler)(nil)
4655

4756
type RawAccount struct {
4857
ClientPolicy
@@ -236,3 +245,155 @@ func (h *Handler) handleDNSRequest(
236245
fmt.Errorf("unknown handler mode: %q", mode)
237246
}
238247
}
248+
249+
// Parses a dns01proxy directive into a Handler instance.
250+
//
251+
// Syntax:
252+
//
253+
// dns01proxy {
254+
// dns <provider_name> [<params...>]
255+
// dns_ttl <ttl>
256+
// resolvers <resolvers...>
257+
// user <userID> {
258+
// password <hashed_password>
259+
// allow_domains <domains...>
260+
// deny_domains <domains...>
261+
// }
262+
// }
263+
func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
264+
// Consume directive name.
265+
d.Next()
266+
267+
// No inline arguments allowed.
268+
if d.NextArg() {
269+
return d.ArgErr()
270+
}
271+
272+
for nesting := d.Nesting(); d.NextBlock(nesting); {
273+
switch d.Val() {
274+
case "dns":
275+
// Expect a provider name.
276+
if !d.NextArg() {
277+
return d.ArgErr()
278+
}
279+
provName := d.Val()
280+
unm, err := caddyfile.UnmarshalModule(d, "dns.providers."+provName)
281+
if err != nil {
282+
return err
283+
}
284+
h.DNS.ProviderRaw = caddyconfig.JSONModuleObject(
285+
unm,
286+
"name",
287+
provName,
288+
nil,
289+
)
290+
291+
case "dns_ttl":
292+
var ttl string
293+
if !d.AllArgs(&ttl) {
294+
return d.ArgErr()
295+
}
296+
parsedTTL, err := caddy.ParseDuration(ttl)
297+
if err != nil {
298+
return err
299+
}
300+
h.DNS.TTL = optionals.Some(caddy.Duration(parsedTTL))
301+
302+
case "resolvers":
303+
h.DNS.Resolvers = d.RemainingArgs()
304+
if len(h.DNS.Resolvers) == 0 {
305+
return d.Errf("must specify at least one resolver address")
306+
}
307+
308+
case "user":
309+
var userID string
310+
if !d.AllArgs(&userID) {
311+
return d.ArgErr()
312+
}
313+
314+
account := RawAccount{
315+
ClientPolicy: ClientPolicy{
316+
UserID: userID,
317+
},
318+
}
319+
320+
// Parse the client declaration.
321+
for nesting := d.Nesting(); d.NextBlock(nesting); {
322+
var curDomainsRaw *[]string
323+
324+
fieldName := d.Val()
325+
switch fieldName {
326+
case "password":
327+
var password string
328+
if !d.AllArgs((&password)) {
329+
return d.ArgErr()
330+
}
331+
if account.Password.IsSome() {
332+
return fmt.Errorf("cannot specify more than one password per user")
333+
}
334+
account.Password = optionals.Some(password)
335+
continue
336+
337+
case "allow_domains":
338+
curDomainsRaw = &account.AllowDomainsRaw
339+
340+
case "deny_domains":
341+
curDomainsRaw = &account.DenyDomainsRaw
342+
343+
default:
344+
return d.Errf("unrecognized user directive: %q", fieldName)
345+
}
346+
347+
if *curDomainsRaw != nil {
348+
return d.Errf(
349+
"cannot specify more than one %q policy per user",
350+
fieldName,
351+
)
352+
}
353+
354+
domainList := d.RemainingArgs()
355+
if len(domainList) == 0 {
356+
return d.Errf("must specify at least one domain")
357+
}
358+
359+
*curDomainsRaw = domainList
360+
}
361+
362+
// Register the account.
363+
h.AccountsRaw = append(h.AccountsRaw, account)
364+
365+
default:
366+
return d.Errf("unrecognized dns01proxy handler directive: %q", d.Val())
367+
}
368+
}
369+
370+
return nil
371+
}
372+
373+
// Unmarshals tokens from h into a new Handler instance that is ready for
374+
// provisioning.
375+
func parseHandler(
376+
h httpcaddyfile.Helper,
377+
) (caddyhttp.MiddlewareHandler, error) {
378+
var result Handler
379+
err := result.UnmarshalCaddyfile(h.Dispenser)
380+
381+
if len(result.DNS.ProviderRaw) == 0 {
382+
// No locally configured DNS provider. Use the global option.
383+
val := h.Option("acme_dns")
384+
if val == nil {
385+
val = h.Option("dns")
386+
if val == nil {
387+
return nil, fmt.Errorf("must configure a DNS provider")
388+
}
389+
}
390+
result.DNS.ProviderRaw = caddyconfig.JSONModuleObject(
391+
val,
392+
"name",
393+
val.(caddy.Module).CaddyModule().ID.Name(),
394+
nil,
395+
)
396+
}
397+
398+
return &result, err
399+
}

0 commit comments

Comments
 (0)