add multi instance support

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

View File

@ -21,7 +21,7 @@ BUILDDIR := ${PREFIX}/build
DOCKER_IMAGE := $(docker images --format "{{.Repository}}" --filter=reference='$(PROJECTNAME):$(GITCOMMIT)') DOCKER_IMAGE := $(docker images --format "{{.Repository}}" --filter=reference='$(PROJECTNAME):$(GITCOMMIT)')
GOOSARCHES := linux/amd64 GOOSARCHES := linux/amd64
all: build fmt lint test all: build fmt test
.PHONY: fmt .PHONY: fmt
fmt: ## Verifies all files have been `gofmt`ed. fmt: ## Verifies all files have been `gofmt`ed.
@ -30,13 +30,6 @@ fmt: ## Verifies all files have been `gofmt`ed.
@if [ ! -z "${var}" ]; then exit 1; fi @if [ ! -z "${var}" ]; then exit 1; fi
$(MAKE) -C client fmt $(MAKE) -C client fmt
.PHONY: lint
lint: ## Verifies `golint` passes.
@echo "+ $@"
$(eval var = $(shell golint ./... | grep -v vendor | tee /dev/stderr))
@if [ ! -z "${var}" ]; then exit 1; fi
$(MAKE) -C client lint
.PHONY: tag .PHONY: tag
tag: ## Create a new git tag to prepare to build a release tag: ## Create a new git tag to prepare to build a release
git tag -sa $(VERSION) -m "$(VERSION)" git tag -sa $(VERSION) -m "$(VERSION)"
@ -45,8 +38,8 @@ tag: ## Create a new git tag to prepare to build a release
.PHONY: test .PHONY: test
test: ## Generates test certificates & run unit tests. test: ## Generates test certificates & run unit tests.
$(MAKE) -C fixtures/test $(MAKE) -C fixtures/test
go vet -mod=vendor go vet
go test -v -mod=vendor -coverprofile=coverage.out go test -v -coverprofile=coverage.out
$(MAKE) -C client test $(MAKE) -C client test
.PHONY: resetreplay .PHONY: resetreplay
@ -62,7 +55,7 @@ deb:
.PHONY: build .PHONY: build
build: ## Builds a static executable. build: ## Builds a static executable.
@echo "+ $@" @echo "+ $@"
CGO_ENABLED=0 $(GO) build -mod=vendor \ CGO_ENABLED=0 $(GO) build \
-o $(PROJECTNAME) \ -o $(PROJECTNAME) \
-tags "static_build netgo" \ -tags "static_build netgo" \
-trimpath \ -trimpath \

View File

@ -1,29 +1,72 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"regexp"
"strings" "strings"
"github.com/pyke369/golang-support/uconfig" "github.com/pyke369/golang-support/uconfig"
) )
func getPDNSInstances(config *uconfig.UConfig) ([]pdnsInstance, error) {
var err error
instances := []pdnsInstance{}
hasDefaultInstance := false
i := pdnsInstance{}
for _, profile := range config.GetPaths("config.pdns") {
if i.dns, err = NewClient(
config.GetString(profile+".api-url", "http://127.0.0.1:8081/api/v1/servers/localhost"),
config.GetString(profile+".api-key", ""),
int(config.GetInteger(profile+".timeout", 3)),
int(config.GetInteger(profile+".defaultTTL", 3600))); err != nil {
return nil, err
}
i.isDefault = config.GetBoolean(profile+".default", false)
if i.isDefault && hasDefaultInstance {
return nil, errors.New("There can be only one default instance")
} else {
hasDefaultInstance = true
}
for _, r := range parseConfigArray(config, profile+".whenRegexp") {
re, err := regexp.Compile(r)
if err != nil {
return nil, err
}
i.regexp = append(i.regexp, re)
}
instances = append(instances, i)
}
if len(instances) == 0 {
return nil, errors.New("There must be at least one instance")
}
if len(instances) > 1 && !hasDefaultInstance {
return nil, errors.New("There must be at least one default instance")
}
return instances, nil
}
func loadConfig(configFile string) (*HTTPServer, error) { func loadConfig(configFile string) (*HTTPServer, error) {
// Parse the configuration file // Parse the configuration file
config, err := uconfig.New(configFile) config, err := uconfig.New(configFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
instances, err := getPDNSInstances(config)
if err != nil {
return nil, err
}
h := NewHTTPServer( h := NewHTTPServer(
config.GetString("config.http.port", "127.0.0.01:8080"), config.GetString("config.http.port", "127.0.0.01:8080"),
config.GetString("config.http.key", ""), config.GetString("config.http.key", ""),
config.GetString("config.http.cert", ""), config.GetString("config.http.cert", ""),
config.GetString("config.http.crl", ""), config.GetString("config.http.crl", ""),
config.GetString("config.http.ca", ""), config.GetString("config.http.ca", ""),
config.GetString("config.pdns.api-url", "http://127.0.0.1:8081/api/v1/servers/localhost"), instances,
config.GetString("config.pdns.api-key", ""),
int(config.GetInteger("config.pdns.timeout", 3)),
int(config.GetInteger("config.pdns.defaultTTL", 3600)),
) )
populateHTTPServerZoneProfiles(config, h) populateHTTPServerZoneProfiles(config, h)
populateHTTPServerAcls(config, h) populateHTTPServerAcls(config, h)

57
http.go
View File

@ -40,11 +40,16 @@ type (
certPool *x509.CertPool certPool *x509.CertPool
debug bool debug bool
m sync.RWMutex m sync.RWMutex
dns *PowerDNS dnsInstances []pdnsInstance
nonceGen string nonceGen string
certCache map[string]time.Time certCache map[string]time.Time
zoneProfiles map[string]*zoneProfile zoneProfiles map[string]*zoneProfile
} }
pdnsInstance struct {
dns *PowerDNS
isDefault bool
regexp []*regexp.Regexp
}
zoneProfile struct { zoneProfile struct {
Default bool Default bool
NameServers []string NameServers []string
@ -73,7 +78,7 @@ func (e JSONRPCError) String() string {
} }
// NewHTTPServer initializes HTTPServer // 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()) rand.Seed(time.Now().UnixNano())
h := HTTPServer{ h := HTTPServer{
Port: port, Port: port,
@ -87,6 +92,7 @@ func NewHTTPServer(port, key, cert, crl, ca, pdnsServer, pdnsKey string, timeout
pdnsAcls: []*PdnsACL{}, pdnsAcls: []*PdnsACL{},
certPool: x509.NewCertPool(), certPool: x509.NewCertPool(),
decodedCA: []*x509.Certificate{}, decodedCA: []*x509.Certificate{},
dnsInstances: instances,
} }
rawCA, err := ioutil.ReadFile(ca) 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.certPool.AddCert(cert)
h.decodedCA = append(h.decodedCA, 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 { if _, err := h.RefreshCRL(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -177,7 +180,9 @@ func (h *HTTPServer) unlock() {
// Debug facilities // Debug facilities
func (h *HTTPServer) Debug() { func (h *HTTPServer) Debug() {
h.debug = true 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 // 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) b, message := clearsign.Decode(body)
// this is not a valid PGP signed payload, meaning message is all we got // this is not a valid PGP signed payload, meaning message is all we got
if b == nil { if b == nil {
jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h.dns) jsonRPC, wasArray, err := ParsejsonRPCRequest(message, h)
return jsonRPC, message, nil, wasArray, err return jsonRPC, message, nil, wasArray, err
} }
// this is a valid PGP signed payload, we can extract the real payload and // 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 { if err != nil {
return JSONArray{}, message, signature, false, err 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 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")) 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 // PowerDNS native API support. Add certificate support for auth
func (h *HTTPServer) nativeAPIServe(w http.ResponseWriter, r *http.Request) { func (h *HTTPServer) nativeAPIServe(w http.ResponseWriter, r *http.Request) {
// Check if the user/server is allowed to perform the required action // 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) http.Error(w, certError.Error(), 403)
return 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) 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) 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") log.Println("[Native API] User ", commonName, " was not authorized to perform the action")
return return
} }
h.dns.Proxy(w, r) dns.Proxy(w, r)
} }
// GetZoneConfig returns a configuration for a new zone // GetZoneConfig returns a configuration for a new zone
func (h *HTTPServer) GetZoneConfig(s string) (string, string, []string, JSONArray, bool, error) { func (h *HTTPServer) GetZoneConfig(s string) (string, string, []string, JSONArray, bool, error) {
valid := "" valid := ""
def := JSONArray{} def := JSONArray{}
for zoneType, profile := range h.zoneProfiles { 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 { for _, re := range profile.Regexp {
if !re.MatchString(s) { if !re.MatchString(s) {
continue continue
@ -557,7 +592,7 @@ func (h *HTTPServer) Run() {
mux.HandleFunc("/stats/", h.nativeAPIServe) mux.HandleFunc("/stats/", h.nativeAPIServe)
mux.HandleFunc("/style.css", h.nativeAPIServe) mux.HandleFunc("/style.css", h.nativeAPIServe)
if _, err := os.Stat("./web"); err == nil && h.debug { 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"))) mux.Handle("/", http.FileServer(http.Dir("web")))
} else { } else {
mux.Handle("/", http.FileServer(http.FS(serverRoot))) mux.Handle("/", http.FileServer(http.FS(serverRoot)))

View File

@ -106,10 +106,10 @@ func (j JSONInput) String() string {
} }
// SetDefaults modify the input by setting the right default parameters // SetDefaults modify the input by setting the right default parameters
func (ja JSONArray) SetDefaults(p *PowerDNS) JSONArray { func (ja JSONArray) SetDefaults(h *HTTPServer) JSONArray {
for i, j := range ja { for i, j := range ja {
if j.Params.TTL == 0 { if j.Params.TTL == 0 {
ja[i].Params.TTL = p.DefaultTTL ja[i].Params.TTL = h.findDNSInstance(j.Params.Name).DefaultTTL
} }
} }
return ja return ja
@ -136,7 +136,8 @@ func (ja JSONArray) Run(h *HTTPServer, user string, wasArray, textOnly bool) str
continue continue
} }
listResult = append(listResult, JSONRPCNewError(-32000, j.ID, last.Error)) listResult = append(listResult, JSONRPCNewError(-32000, j.ID, last.Error))
h.dns.LogDebug(last.Error)
h.findDNSInstance(j.Params.Name).LogDebug(last.Error)
if !j.Params.IgnoreError { if !j.Params.IgnoreError {
break break
} }
@ -162,23 +163,31 @@ func (j *JSONInput) Run(h *HTTPServer, user string) []*JSONRPCResult {
if err := j.Normalize(); err != nil { if err := j.Normalize(); err != nil {
return append(ret, j.JSONRPCResult("", "", err)) return append(ret, j.JSONRPCResult("", "", err))
} }
d := h.findDNSInstance(j.Params.Name)
switch j.Method { switch j.Method {
// list is a spacial case, it doesn't imply a DNSQuery() object // list is a spacial case, it doesn't imply a DNSQuery() object
case "list": case "list":
result, err := h.dns.ListZones(j.Params.Name) var err error
total := DNSZones{}
for _, dns := range h.dnsInstances {
result, err := dns.dns.ListZones(j.Params.Name)
// we apply the acl after the fact for the list method // we apply the acl after the fact for the list method
result = j.FilterList(result) result = j.FilterList(result)
if err == nil && len(result) == 0 { if err == nil && len(result) == 0 {
err = errors.New("Unknown domain") err = errors.New("Unknown domain")
} }
res := j.JSONRPCResult(result.List("\n"), "", err) total = append(total, result...)
for i := range result { }
res.Raw = append(res.Raw, result[i].Name) res := j.JSONRPCResult(total.List("\n"), "", err)
for i := range total {
res.Raw = append(res.Raw, total[i].Name)
} }
return append(ret, res) return append(ret, res)
case "domain": case "domain":
parentName, err := h.dns.GetDomain(j.Params.Name) parentName, err := d.GetDomain(j.Params.Name)
res := j.JSONRPCResult(parentName, "", err) res := j.JSONRPCResult(parentName, "", err)
res.Raw = append(res.Raw, parentName) res.Raw = append(res.Raw, parentName)
return append(ret, res) return append(ret, res)
@ -212,7 +221,7 @@ func (j *JSONInput) Run(h *HTTPServer, user string) []*JSONRPCResult {
ret = append(ret, result) ret = append(ret, result)
continue continue
} }
code, _, err := h.dns.Execute(act) code, _, err := d.Execute(act)
switch { switch {
case err == nil && code == 204: case err == nil && code == 204:
result.Result = "Command Successfull" result.Result = "Command Successfull"
@ -277,15 +286,17 @@ func (j *JSONInput) Normalize() error {
// pdns. It can change the content of j to force dry run mode // pdns. It can change the content of j to force dry run mode
func (j *JSONInput) DNSQueries(h *HTTPServer) ([]*DNSQuery, error) { func (j *JSONInput) DNSQueries(h *HTTPServer) ([]*DNSQuery, error) {
var err error var err error
d := h.findDNSInstance(j.Params.Name)
switch j.Method { switch j.Method {
case "search": case "search":
result, err := h.dns.Search(j.Params.Name) result, err := d.Search(j.Params.Name)
// we apply the acl after the fact for the search method // we apply the acl after the fact for the search method
result = j.FilterSearch(result) result = j.FilterSearch(result)
j.Params.DryRun = true j.Params.DryRun = true
return []*DNSQuery{result.DNSQuery()}, err return []*DNSQuery{result.DNSQuery()}, err
case "dump": case "dump":
result, err := h.dns.Zone(j.Params.Name) result, err := d.Zone(j.Params.Name)
j.Params.DryRun = true j.Params.DryRun = true
return []*DNSQuery{result}, err return []*DNSQuery{result}, err
case "newzone": case "newzone":
@ -297,13 +308,13 @@ func (j *JSONInput) DNSQueries(h *HTTPServer) ([]*DNSQuery, error) {
return append(otherActions, newZone.TransformIntoDNSQuery()), nil return append(otherActions, newZone.TransformIntoDNSQuery()), nil
} }
result := &DNSQuery{} result := &DNSQuery{}
code, _, err := h.dns.ExecuteZone(newZone, result) code, _, err := d.ExecuteZone(newZone, result)
if err == nil && code != 201 { if err == nil && code != 201 {
err = fmt.Errorf("The return code was %d for the creation of zone %s", code, j.Params.Name) err = fmt.Errorf("The return code was %d for the creation of zone %s", code, j.Params.Name)
} }
return append(otherActions, result), err return append(otherActions, result), err
} }
current, err := h.dns.GetRecord(j.Params.Name, j.Method, j.ignoreBadDomain) current, err := d.GetRecord(j.Params.Name, j.Method, j.ignoreBadDomain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -371,7 +382,8 @@ func (j *JSONInput) DNSQueriesNS(h *HTTPServer, current *DNSQuery) ([]*DNSQuery,
if trimPoint(j.Params.Name) == trimPoint(current.Domain) { if trimPoint(j.Params.Name) == trimPoint(current.Domain) {
return nil, fmt.Errorf("You cannot change the NS of a local zone") return nil, fmt.Errorf("You cannot change the NS of a local zone")
} }
subZone, err := h.dns.Zone(j.Params.Name) d := h.findDNSInstance(j.Params.Name)
subZone, err := d.Zone(j.Params.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -443,7 +455,8 @@ func (j *JSONInput) DNSQueriesTTL(current *DNSQuery) ([]*DNSQuery, error) {
// DNSQueriesA is the DNSQueries method for the A and AAAA commands // DNSQueriesA is the DNSQueries method for the A and AAAA commands
// it checks the command is legal, and can make reverse DNS changes if needed // it checks the command is legal, and can make reverse DNS changes if needed
func (j *JSONInput) DNSQueriesA(h *HTTPServer, forward *DNSQuery) ([]*DNSQuery, error) { func (j *JSONInput) DNSQueriesA(h *HTTPServer, forward *DNSQuery) ([]*DNSQuery, error) {
reverse, err := h.dns.GetReverse(j.Params.Value, j.Method == "a") d := h.findDNSInstance(j.Params.Name)
reverse, err := d.GetReverse(j.Params.Value, j.Method == "a")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -458,13 +471,14 @@ func (j *JSONInput) DNSQueriesA(h *HTTPServer, forward *DNSQuery) ([]*DNSQuery,
forward.ChangeValue(j.Params.Name, j.Params.Value, j.Method, false, askForReverse) forward.ChangeValue(j.Params.Name, j.Params.Value, j.Method, false, askForReverse)
return []*DNSQuery{forward}, nil return []*DNSQuery{forward}, nil
} }
actions, err := h.dns.ReverseChanges(forward, j.Params.Value) actions, err := d.ReverseChanges(forward, j.Params.Value)
forward.ChangeValue(j.Params.Name, j.Params.Value, j.Method, true, askForReverse) forward.ChangeValue(j.Params.Name, j.Params.Value, j.Method, true, askForReverse)
return append(actions, forward), err return append(actions, forward), err
} }
// DNSQueriesDelete is the DNSQueries method for the Delete command // DNSQueriesDelete is the DNSQueries method for the Delete command
func (j *JSONInput) DNSQueriesDelete(h *HTTPServer, current *DNSQuery) ([]*DNSQuery, error) { func (j *JSONInput) DNSQueriesDelete(h *HTTPServer, current *DNSQuery) ([]*DNSQuery, error) {
d := h.findDNSInstance(j.Params.Name)
reverses, useful, err := current.SplitDeletionQuery(j.Params.Name, j.Params.Value) reverses, useful, err := current.SplitDeletionQuery(j.Params.Name, j.Params.Value)
if err != nil { if err != nil {
return nil, err return nil, err
@ -477,7 +491,7 @@ func (j *JSONInput) DNSQueriesDelete(h *HTTPServer, current *DNSQuery) ([]*DNSQu
ret := []*DNSQuery{current} ret := []*DNSQuery{current}
// add the reverse changes if needed // add the reverse changes if needed
for _, r := range reverses { for _, r := range reverses {
parentName, err := h.dns.GetDomain(r.Name) parentName, err := d.GetDomain(r.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -486,7 +500,7 @@ func (j *JSONInput) DNSQueriesDelete(h *HTTPServer, current *DNSQuery) ([]*DNSQu
continue continue
} }
ip := ptrToIP(r.Name) ip := ptrToIP(r.Name)
if h.dns.IsUsed(ip, j.Params.Name, []string{"A", "AAAA"}) { if d.IsUsed(ip, j.Params.Name, []string{"A", "AAAA"}) {
message := "Reverse issue : %s is the reverse for %s and will be removed\n" message := "Reverse issue : %s is the reverse for %s and will be removed\n"
message += "But other records are pointing to %s as well. Please cleanup first\n" message += "But other records are pointing to %s as well. Please cleanup first\n"
return ret, fmt.Errorf(message, j.Params.Name, ip, ip) return ret, fmt.Errorf(message, j.Params.Name, ip, ip)
@ -497,12 +511,13 @@ func (j *JSONInput) DNSQueriesDelete(h *HTTPServer, current *DNSQuery) ([]*DNSQu
// CheckPTR validates that the query is a valid PTR // CheckPTR validates that the query is a valid PTR
func (j *JSONInput) CheckPTR(h *HTTPServer) error { func (j *JSONInput) CheckPTR(h *HTTPServer) error {
d := h.findDNSInstance(j.Params.Name)
ip := ptrToIP(j.Params.Name) ip := ptrToIP(j.Params.Name)
if ip == "" { if ip == "" {
return fmt.Errorf("%s is not a valid PTR", j.Params.Name) return fmt.Errorf("%s is not a valid PTR", j.Params.Name)
} }
target := &DNSQuery{} target := &DNSQuery{}
if err := h.dns.CanCreate(j.Params.Value, true, target); err != nil || target.Len() == 0 { if err := d.CanCreate(j.Params.Value, true, target); err != nil || target.Len() == 0 {
return err return err
} }
for _, rec := range target.RRSets[0].Records { for _, rec := range target.RRSets[0].Records {
@ -515,11 +530,12 @@ func (j *JSONInput) CheckPTR(h *HTTPServer) error {
// CheckSRV validates that the query is a valid SRV // CheckSRV validates that the query is a valid SRV
func (j *JSONInput) CheckSRV(h *HTTPServer) error { func (j *JSONInput) CheckSRV(h *HTTPServer) error {
d := h.findDNSInstance(j.Params.Name)
name := validSRV(j.Params.Value) name := validSRV(j.Params.Value)
if name == "" { if name == "" {
return fmt.Errorf("%s is not a valid SRV", j.Params.Value) return fmt.Errorf("%s is not a valid SRV", j.Params.Value)
} }
return h.dns.CanCreate(name, false, nil) return d.CanCreate(name, false, nil)
} }
// CheckCAA validates that the query is a valid CAA // CheckCAA validates that the query is a valid CAA
@ -535,10 +551,11 @@ func (j *JSONInput) CheckCAA() error {
// CheckMX validates that the query is a valid MX // CheckMX validates that the query is a valid MX
func (j *JSONInput) CheckMX(h *HTTPServer) error { func (j *JSONInput) CheckMX(h *HTTPServer) error {
name := validMX(j.Params.Value) name := validMX(j.Params.Value)
d := h.findDNSInstance(j.Params.Name)
if name == "" { if name == "" {
return fmt.Errorf("%s is not a valid MX", j.Params.Value) return fmt.Errorf("%s is not a valid MX", j.Params.Value)
} }
return h.dns.CanCreate(name, true, nil) return d.CanCreate(name, true, nil)
} }
// CheckCNAME validates that the query is a valid CNAME // CheckCNAME validates that the query is a valid CNAME
@ -550,7 +567,8 @@ func (j *JSONInput) CheckCNAME(h *HTTPServer) error {
if !validName(j.Params.Value, false) { if !validName(j.Params.Value, false) {
return fmt.Errorf("%s is not a valid DNS Name", j.Params.Value) return fmt.Errorf("%s is not a valid DNS Name", j.Params.Value)
} }
return h.dns.CanCreate(j.Params.Value, false, nil) d := h.findDNSInstance(j.Params.Name)
return d.CanCreate(j.Params.Value, false, nil)
} }
// NewZone create a new zone in the DNS. // NewZone create a new zone in the DNS.
@ -565,10 +583,11 @@ func (j *JSONInput) NewZone(h *HTTPServer) (z *DNSZone, otherActions []*DNSQuery
if soa == "" { if soa == "" {
soa = fmt.Sprintf("%s hostmaster.%s 0 28800 7200 604800 86400", nameServers[0], j.Params.Name) soa = fmt.Sprintf("%s hostmaster.%s 0 28800 7200 604800 86400", nameServers[0], j.Params.Name)
} }
parentName, zoneErr := h.dns.GetDomain(j.Params.Name) dns := h.findDNSInstance(j.Params.Name)
parentName, zoneErr := dns.GetDomain(j.Params.Name)
parentName = trimPoint(parentName) parentName = trimPoint(parentName)
z, err = h.dns.NewZone(j.Params.Name, zoneType, soa, j.user, j.Params.Comment, z, err = dns.NewZone(j.Params.Name, zoneType, soa, j.user, j.Params.Comment,
j.Params.TTL, nameServers, autoInc) j.Params.TTL, nameServers, autoInc)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -589,7 +608,7 @@ func (j *JSONInput) NewZone(h *HTTPServer) (z *DNSZone, otherActions []*DNSQuery
entry.Params.Name = fmt.Sprintf("%s.%s", d.Params.Name, j.Params.Name) entry.Params.Name = fmt.Sprintf("%s.%s", d.Params.Name, j.Params.Name)
} }
if err := entry.Normalize(); err != nil { if err := entry.Normalize(); err != nil {
h.dns.LogDebug(err) dns.LogDebug(err)
continue continue
} }
actions, err := entry.DNSQueries(h) actions, err := entry.DNSQueries(h)
@ -605,14 +624,14 @@ func (j *JSONInput) NewZone(h *HTTPServer) (z *DNSZone, otherActions []*DNSQuery
return return
} }
// we must create the NS records in the parent zone too // we must create the NS records in the parent zone too
glue, err := h.dns.SetNameServers(j.Params.Name, parentName, nameServers) glue, err := dns.SetNameServers(j.Params.Name, parentName, nameServers)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
otherActions = append(otherActions, glue) otherActions = append(otherActions, glue)
// check if there are records in the parent zone // check if there are records in the parent zone
records, err := h.dns.Zone(j.Params.Name) records, err := dns.Zone(j.Params.Name)
if err != nil { if err != nil {
err = nil err = nil
return return
@ -622,7 +641,7 @@ func (j *JSONInput) NewZone(h *HTTPServer) (z *DNSZone, otherActions []*DNSQuery
z.AddEntries(records) z.AddEntries(records)
// and delete them in the parent // and delete them in the parent
delete, err := h.dns.Zone(j.Params.Name) delete, err := dns.Zone(j.Params.Name)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -677,16 +696,16 @@ func (j *JSONInput) FilterList(z []*DNSZone) []*DNSZone {
} }
// ParsejsonRPCRequest read the payload of the query and put it in the structure // ParsejsonRPCRequest read the payload of the query and put it in the structure
func ParsejsonRPCRequest(s []byte, d *PowerDNS) (JSONArray, bool, error) { func ParsejsonRPCRequest(s []byte, h *HTTPServer) (JSONArray, bool, error) {
var inSimple *JSONInput var inSimple *JSONInput
var inArray JSONArray var inArray JSONArray
if json.Unmarshal(s, &inArray); len(inArray) > 0 { if json.Unmarshal(s, &inArray); len(inArray) > 0 {
return inArray.SetDefaults(d), true, nil return inArray.SetDefaults(h), true, nil
} }
if err := json.Unmarshal(s, &inSimple); err != nil { if err := json.Unmarshal(s, &inSimple); err != nil {
return nil, false, err return nil, false, err
} }
return JSONArray{inSimple}.SetDefaults(d), false, nil return JSONArray{inSimple}.SetDefaults(h), false, nil
} }
func isDryRun(j JSONArray) bool { func isDryRun(j JSONArray) bool {

View File

@ -69,10 +69,13 @@ config
cert: "/etc/ssl/certs/server-bundle.pem" cert: "/etc/ssl/certs/server-bundle.pem"
} }
pdns: pdns:
{
myServer:
{ {
api-key: "<pdns_api_key>" api-key: "<pdns_api_key>"
api-url: "http://127.0.0.1:8081/api/v1/servers/localhost" api-url: "http://127.0.0.1:8081/api/v1/servers/localhost"
} }
}
zoneProfile: zoneProfile:
{ {
private: private:

View File

@ -76,14 +76,15 @@ config
key: "fixtures/test/server-key.pem" key: "fixtures/test/server-key.pem"
cert: "fixtures/test/server-cert.pem" cert: "fixtures/test/server-cert.pem"
} }
pdns pdns:
{
instance:
{ {
api-key: "123password" api-key: "123password"
api-url: "http://127.0.0.1:8081/api/v1/servers/localhost" api-url: "http://127.0.0.1:8081/api/v1/servers/localhost"
timeout: 300 timeout: 300
defaultTTL: 172800 defaultTTL: 172800
}
} }
zoneProfile zoneProfile
{ {

View File

@ -64,11 +64,14 @@ config
crl: "fixtures/test/root.crl.pem" crl: "fixtures/test/root.crl.pem"
} }
pdns: pdns:
{
instance:
{ {
api-key: "123password" api-key: "123password"
api-url: "http://127.0.0.1:8081/api/v1/servers/localhost" api-url: "http://127.0.0.1:8081/api/v1/servers/localhost"
defaultTTL: 172800 defaultTTL: 172800
} }
}
zoneProfile: zoneProfile:
{ {
Native: Native: