add multi instance support
This commit is contained in:
		
							parent
							
								
									3dc275d7e3
								
							
						
					
					
						commit
						546b08039f
					
				
							
								
								
									
										15
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								Makefile
									
									
									
									
									
								
							| @ -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 \
 | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								config.go
									
									
									
									
									
								
							| @ -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
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								http.go
									
									
									
									
									
								
							| @ -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))) | ||||||
|  | |||||||
							
								
								
									
										81
									
								
								jsonrpc.go
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								jsonrpc.go
									
									
									
									
									
								
							| @ -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 { | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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 | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user