add multi instance support

This commit is contained in:
2025-03-26 13:47:05 +01:00
parent 3dc275d7e3
commit 546b08039f
7 changed files with 170 additions and 73 deletions

57
http.go
View File

@@ -40,11 +40,16 @@ type (
certPool *x509.CertPool
debug bool
m sync.RWMutex
dns *PowerDNS
dnsInstances []pdnsInstance
nonceGen string
certCache map[string]time.Time
zoneProfiles map[string]*zoneProfile
}
pdnsInstance struct {
dns *PowerDNS
isDefault bool
regexp []*regexp.Regexp
}
zoneProfile struct {
Default bool
NameServers []string
@@ -73,7 +78,7 @@ func (e JSONRPCError) String() string {
}
// NewHTTPServer initializes HTTPServer
func NewHTTPServer(port, key, cert, crl, ca, pdnsServer, pdnsKey string, timeout, ttl int) *HTTPServer {
func NewHTTPServer(port string, key string, cert string, crl string, ca string, instances []pdnsInstance) *HTTPServer {
rand.Seed(time.Now().UnixNano())
h := HTTPServer{
Port: port,
@@ -87,6 +92,7 @@ func NewHTTPServer(port, key, cert, crl, ca, pdnsServer, pdnsKey string, timeout
pdnsAcls: []*PdnsACL{},
certPool: x509.NewCertPool(),
decodedCA: []*x509.Certificate{},
dnsInstances: instances,
}
rawCA, err := ioutil.ReadFile(ca)
@@ -109,9 +115,6 @@ func NewHTTPServer(port, key, cert, crl, ca, pdnsServer, pdnsKey string, timeout
h.certPool.AddCert(cert)
h.decodedCA = append(h.decodedCA, cert)
}
if h.dns, err = NewClient(pdnsServer, pdnsKey, timeout, ttl); err != nil {
log.Fatal(err)
}
if _, err := h.RefreshCRL(); err != nil {
log.Fatal(err)
}
@@ -177,7 +180,9 @@ func (h *HTTPServer) unlock() {
// Debug facilities
func (h *HTTPServer) Debug() {
h.debug = true
h.dns.Debug()
for _, d := range h.dnsInstances {
d.dns.Debug()
}
}
// verifyNonce check that the once is valid and less than 10s old
@@ -359,7 +364,7 @@ func (h *HTTPServer) jrpcDecodeQuery(body []byte) (JSONArray, []byte, []byte, bo
b, message := clearsign.Decode(body)
// this is not a valid PGP signed payload, meaning message is all we got
if b == nil {
jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h.dns)
jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h)
return jsonRPC, message, nil, wasArray, err
}
// this is a valid PGP signed payload, we can extract the real payload and
@@ -375,7 +380,7 @@ func (h *HTTPServer) jrpcDecodeQuery(body []byte) (JSONArray, []byte, []byte, bo
if err != nil {
return JSONArray{}, message, signature, false, err
}
jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h.dns)
jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h)
return jsonRPC, message, signature, wasArray, err
}
@@ -416,6 +421,27 @@ func (h *HTTPServer) jsonRPCServe(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, jsonRPC.Run(h, username, wasArray, r.Header.Get("PDNS-Output") == "plaintext"))
}
// Find the right PDNS instance to use for a given query
func (h *HTTPServer) findDNSInstance(query string) *PowerDNS {
var d *PowerDNS
if len(h.dnsInstances) == 1 {
return h.dnsInstances[0].dns
}
for _, i := range h.dnsInstances {
if i.isDefault {
d = i.dns
}
for _, re := range i.regexp {
if !re.MatchString(query) {
continue
}
return i.dns
}
}
return d
}
// PowerDNS native API support. Add certificate support for auth
func (h *HTTPServer) nativeAPIServe(w http.ResponseWriter, r *http.Request) {
// Check if the user/server is allowed to perform the required action
@@ -424,20 +450,29 @@ func (h *HTTPServer) nativeAPIServe(w http.ResponseWriter, r *http.Request) {
http.Error(w, certError.Error(), 403)
return
}
// find the target PDNS instance based on the URL
dns := h.findDNSInstance(r.RequestURI)
log.Println("[Native API] User", commonName, "requested", r.Method, "on", r.RequestURI)
if !h.nativeValidAuth(strings.TrimPrefix(r.RequestURI, h.dns.apiURL+"/"), commonName, r.Method) {
if !h.nativeValidAuth(strings.TrimPrefix(r.RequestURI, dns.apiURL+"/"), commonName, r.Method) {
http.Error(w, "The user "+commonName+" is not authorized to perform this action", 403)
log.Println("[Native API] User ", commonName, " was not authorized to perform the action")
return
}
h.dns.Proxy(w, r)
dns.Proxy(w, r)
}
// GetZoneConfig returns a configuration for a new zone
func (h *HTTPServer) GetZoneConfig(s string) (string, string, []string, JSONArray, bool, error) {
valid := ""
def := JSONArray{}
for zoneType, profile := range h.zoneProfiles {
// if there is only one profile, it's the good one
if len(h.zoneProfiles) == 1 {
return zoneType, profile.SOA, profile.NameServers, def, profile.AutoInc, nil
}
for _, re := range profile.Regexp {
if !re.MatchString(s) {
continue
@@ -557,7 +592,7 @@ func (h *HTTPServer) Run() {
mux.HandleFunc("/stats/", h.nativeAPIServe)
mux.HandleFunc("/style.css", h.nativeAPIServe)
if _, err := os.Stat("./web"); err == nil && h.debug {
h.dns.LogDebug("serving local files")
h.dnsInstances[0].dns.LogDebug("serving local files")
mux.Handle("/", http.FileServer(http.Dir("web")))
} else {
mux.Handle("/", http.FileServer(http.FS(serverRoot)))