2021-01-31 21:31:08 +00:00
|
|
|
package cooldns
|
2021-01-08 15:08:57 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
|
|
|
// See https://go-acme.github.io/lego/dns/httpreq/
|
|
|
|
|
|
|
|
type configLego struct {
|
|
|
|
Enable bool `yaml:"enable"`
|
|
|
|
Address string `yaml:"address"`
|
|
|
|
Username string `yaml:"username"`
|
|
|
|
Secret string `yaml:"secret"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type legoPresent struct {
|
|
|
|
Fqdn string `json:"fqdn"`
|
|
|
|
Value string `json:"value"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type legoMap map[string]string
|
|
|
|
|
|
|
|
func startLEGOWebSever(config configLego) *legoMap {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
|
|
acmeMap := make(legoMap)
|
|
|
|
|
|
|
|
mux.HandleFunc("/present", func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
if !checkBasicAuth(r, config) {
|
|
|
|
rw.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var presentData legoPresent
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&presentData)
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Failed to parse request for ACME: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
acmeMap[presentData.Fqdn] = presentData.Value
|
|
|
|
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
})
|
|
|
|
|
|
|
|
mux.HandleFunc("/cleanup", func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
if !checkBasicAuth(r, config) {
|
|
|
|
rw.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for k := range acmeMap {
|
|
|
|
delete(acmeMap, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
})
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
if err := http.ListenAndServe(config.Address, mux); err != nil {
|
|
|
|
log.Fatalf("Failed to start Webserver for LEGO: %s\n", err.Error())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
log.Printf("Startet webserver on %s", config.Address)
|
|
|
|
|
|
|
|
return &acmeMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkBasicAuth(r *http.Request, config configLego) bool {
|
|
|
|
if config.Username != "" && config.Secret != "" {
|
|
|
|
u, p, ok := r.BasicAuth()
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if u == config.Username && p == config.Secret {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("Failed lego authentication")
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleACMERequest(w dns.ResponseWriter, r *dns.Msg, acmeMap *legoMap) bool {
|
|
|
|
if len(r.Question) == 1 {
|
|
|
|
if r.Question[0].Qtype == dns.TypeTXT && r.Question[0].Qclass == dns.ClassINET {
|
|
|
|
if value, ok := (*acmeMap)[r.Question[0].Name]; ok {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetReply(r)
|
|
|
|
|
|
|
|
m.Answer = append(m.Answer, &dns.TXT{
|
|
|
|
Hdr: dns.RR_Header{
|
|
|
|
Name: r.Question[0].Name,
|
|
|
|
Rrtype: dns.TypeTXT,
|
|
|
|
Class: dns.ClassINET,
|
|
|
|
Ttl: 0,
|
|
|
|
},
|
|
|
|
Txt: []string{value},
|
|
|
|
})
|
|
|
|
|
|
|
|
w.WriteMsg(m)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|