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
1921func 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 {
4351var _ caddy.Module = (* Handler )(nil )
4452var _ caddy.Provisioner = (* Handler )(nil )
4553var _ caddyhttp.MiddlewareHandler = (* Handler )(nil )
54+ var _ caddyfile.Unmarshaler = (* Handler )(nil )
4655
4756type 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