feat: implement DNS server
This commit is contained in:
parent
deaad3f695
commit
225057c6e9
2 changed files with 76 additions and 0 deletions
2
go.mod
2
go.mod
|
|
@ -4,6 +4,8 @@ go 1.20
|
|||
|
||||
require (
|
||||
github.com/caddyserver/caddy/v2 v2.6.4
|
||||
github.com/mholt/acmez v1.1.0
|
||||
github.com/miekg/dns v1.1.50
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
74
stub.go
74
stub.go
|
|
@ -1,14 +1,23 @@
|
|||
package stub
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/mholt/acmez"
|
||||
"github.com/mholt/acmez/acme"
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
)
|
||||
|
||||
// TTL of the challenge TXT record to serve
|
||||
const challenge_ttl = 600 // (anything is probably fine here)
|
||||
|
||||
type StubDNS struct {
|
||||
// the address & port on which to serve DNS for the challenge
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
server *dns.Server // set in Present()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -69,8 +78,73 @@ func (s *StubDNS) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
}
|
||||
|
||||
|
||||
func (s *StubDNS) Present(ctx context.Context, challenge acme.Challenge) error {
|
||||
// get challenge parameters
|
||||
fqdn := dns.Fqdn(challenge.DNS01TXTRecordName())
|
||||
content := challenge.DNS01KeyAuthorization()
|
||||
// spawn the server
|
||||
handler := s.make_handler(fqdn, content)
|
||||
dns.HandleFunc(".", handler)
|
||||
server := &dns.Server{Addr: s.Address, Net: "udp", TsigSecret: nil,}
|
||||
go server.ListenAndServe()
|
||||
|
||||
// store the server for shutdown later
|
||||
s.server = server
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *StubDNS) CleanUp(ctx context.Context, _ acme.Challenge) error {
|
||||
if p.server == nil {
|
||||
return nil
|
||||
} else {
|
||||
return p.server.ShutdownContext(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StubDNS) make_handler(fqdn string, txt string) dns.HandlerFunc {
|
||||
handler := func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
if len(r.Question) != 1 {
|
||||
m.Rcode = dns.RcodeRefused
|
||||
m.Answer = []dns.RR{}
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
|
||||
q := r.Question[0]
|
||||
domain := q.Name
|
||||
|
||||
valid := r.Response == false &&
|
||||
(q.Qclass == dns.ClassINET || q.Qclass == dns.ClassANY) &&
|
||||
q.Qtype == dns.TypeTXT
|
||||
if !valid {
|
||||
m.Rcode = dns.RcodeNotImplemented
|
||||
m.Answer = []dns.RR{}
|
||||
} else if domain != fqdn {
|
||||
m.Rcode = dns.RcodeNameError
|
||||
m.Answer = []dns.RR{}
|
||||
} else {
|
||||
m.Authoritative = true
|
||||
rr := new(dns.TXT)
|
||||
rr.Hdr = dns.RR_Header{
|
||||
Name: domain,
|
||||
Rrtype: dns.TypeTXT,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: uint32(challenge_ttl),
|
||||
}
|
||||
rr.Txt = []string{txt}
|
||||
m.Answer = []dns.RR{rr}
|
||||
}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ acmez.Solver = (*StubDNS)(nil)
|
||||
_ caddy.Provisioner = (*StubDNS)(nil)
|
||||
_ caddyfile.Unmarshaler = (*StubDNS)(nil)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue