mirror of
https://codeberg.org/lauralani/ipam.git
synced 2024-11-27 22:00:01 +01:00
Merge pull request 'Fix DNS Zone matching for PowerDNS API integration' (#8) from bug-1 into main
Reviewed-on: https://codeberg.org/lauralani/ipam/pulls/8
This commit is contained in:
commit
34e4e6fc3e
3 changed files with 370 additions and 371 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -23,5 +23,6 @@ go.work
|
||||||
# build output
|
# build output
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
# vs code stuff
|
# IDE stuff
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
142
cmd/ip-add.go
142
cmd/ip-add.go
|
@ -5,96 +5,96 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ipaddCmd = &cobra.Command{
|
var ipaddCmd = &cobra.Command{
|
||||||
Use: "add ipaddress [hostname]",
|
Use: "add ipaddress [hostname]",
|
||||||
Short: "Add new IP address",
|
Short: "Add new IP address",
|
||||||
Long: `Add new IP address`,
|
Long: `Add new IP address`,
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Args: cobra.RangeArgs(1, 2),
|
Args: cobra.RangeArgs(1, 2),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var ipaddress, hostname string
|
var ipaddress, hostname string
|
||||||
|
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
ipaddress = args[0]
|
ipaddress = args[0]
|
||||||
hostname = ""
|
hostname = ""
|
||||||
} else {
|
} else {
|
||||||
ipaddress = args[0]
|
ipaddress = args[0]
|
||||||
hostname = args[1]
|
hostname = args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, parseerr := netip.ParseAddr(ipaddress)
|
ip, parseerr := netip.ParseAddr(ipaddress)
|
||||||
|
|
||||||
// Exit if parsed value is no valid IP
|
// Exit if parsed value is no valid IP
|
||||||
if parseerr != nil {
|
if parseerr != nil {
|
||||||
fmt.Println("[ERROR]", parseerr)
|
fmt.Println("[ERROR]", parseerr)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if parsed value is an IPv6 Address
|
// Exit if parsed value is an IPv6 Address
|
||||||
// TODO: Implement IPv6 support
|
// TODO: Implement IPv6 support
|
||||||
//if !ip.Is4() {
|
//if !ip.Is4() {
|
||||||
// fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
|
// fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
|
||||||
// os.Exit(1)
|
// os.Exit(1)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
subnet, subnetexists := FindBestSubnet(ip)
|
subnet, subnetexists := FindBestSubnet(ip)
|
||||||
|
|
||||||
if !subnetexists {
|
if !subnetexists {
|
||||||
fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress)
|
fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress)
|
||||||
fmt.Printf("[ERROR] Maybe you need to add it first?\n")
|
fmt.Printf("[ERROR] Maybe you need to add it first?\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if subnet.HasIP(ip) {
|
if subnet.HasIP(ip) {
|
||||||
fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", ip.String(), subnet.Subnet.String())
|
fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", ip.String(), subnet.Subnet.String())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentuser, _ := user.Current()
|
currentuser, _ := user.Current()
|
||||||
timestamp := time.Now()
|
timestamp := time.Now()
|
||||||
|
|
||||||
subnet.Addresses = append(subnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username})
|
subnet.Addresses = append(subnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username})
|
||||||
subnet.ChangedBy = currentuser.Username
|
subnet.ChangedBy = currentuser.Username
|
||||||
subnet.ChangedAt = timestamp
|
subnet.ChangedAt = timestamp
|
||||||
|
|
||||||
writeerr := subnet.WriteSubnet()
|
writeerr := subnet.WriteSubnet()
|
||||||
if writeerr != nil {
|
if writeerr != nil {
|
||||||
fmt.Println("[ERROR]", writeerr)
|
fmt.Println("[ERROR]", writeerr)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hostname == "" {
|
if hostname == "" {
|
||||||
fmt.Printf("added ip:\nip: %v\n", ipaddress)
|
fmt.Printf("added ip:\nip: %v\n", ipaddress)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname)
|
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname)
|
||||||
dnserr := AddDNSFqdn(hostname, ip)
|
dnserr := AddDNSFqdn(hostname, ip)
|
||||||
if dnserr != nil {
|
if dnserr != nil {
|
||||||
fmt.Println("[ERROR]", writeerr)
|
fmt.Println("[ERROR]", dnserr)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ipCmd.AddCommand(ipaddCmd)
|
ipCmd.AddCommand(ipaddCmd)
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
// and all subcommands, e.g.:
|
// and all subcommands, e.g.:
|
||||||
// addCmd.PersistentFlags().String("foo", "", "A help for foo")
|
// addCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
// Cobra supports local flags which will only run when this command
|
||||||
// is called directly, e.g.:
|
// is called directly, e.g.:
|
||||||
// addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
}
|
}
|
||||||
|
|
596
cmd/powerdns.go
596
cmd/powerdns.go
|
@ -5,32 +5,32 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DNSZone struct {
|
type DNSZone struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
RRsets []DNSRecordSet `json:"rrsets"`
|
RRsets []DNSRecordSet `json:"rrsets"`
|
||||||
Metadata map[string]string `json:"metadata"`
|
Metadata map[string]string `json:"metadata"`
|
||||||
DNSSEC bool `json:"dnssec"`
|
DNSSEC bool `json:"dnssec"`
|
||||||
NSEC3Param string `json:"nsec3param,omitempty"`
|
NSEC3Param string `json:"nsec3param,omitempty"`
|
||||||
Account string `json:"account,omitempty"`
|
Account string `json:"account,omitempty"`
|
||||||
Serial int `json:"serial"`
|
Serial int `json:"serial"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Patch struct {
|
type Patch struct {
|
||||||
Rrsets []DNSRecordSet `json:"rrsets"`
|
Rrsets []DNSRecordSet `json:"rrsets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecord checks if a given Record already exists in the DNSRecordSet list.
|
// GetRecord checks if a given Record already exists in the DNSRecordSet list.
|
||||||
|
@ -38,106 +38,102 @@ type Patch struct {
|
||||||
// Returns the DNSRecordSet and true if record exists, empty
|
// Returns the DNSRecordSet and true if record exists, empty
|
||||||
// DNSRecordSet and false if not.
|
// DNSRecordSet and false if not.
|
||||||
func (z DNSZone) GetRecord(fqdn string, rtype string, rcontent string) (DNSRecordSet, bool) {
|
func (z DNSZone) GetRecord(fqdn string, rtype string, rcontent string) (DNSRecordSet, bool) {
|
||||||
if !strings.HasSuffix(fqdn, ".") {
|
if !strings.HasSuffix(fqdn, ".") {
|
||||||
fqdn = fqdn + "."
|
fqdn = fqdn + "."
|
||||||
}
|
}
|
||||||
if (rtype == "PTR") && !strings.HasSuffix(rcontent, ".") {
|
if (rtype == "PTR") && !strings.HasSuffix(rcontent, ".") {
|
||||||
rcontent = rcontent + "."
|
rcontent = rcontent + "."
|
||||||
}
|
}
|
||||||
for _, recordset := range z.RRsets {
|
for _, recordset := range z.RRsets {
|
||||||
if recordset.Name == fqdn && recordset.Type == rtype {
|
if recordset.Name == fqdn && recordset.Type == rtype {
|
||||||
for _, record := range recordset.Records {
|
for _, record := range recordset.Records {
|
||||||
if record.Content == rcontent {
|
if record.Content == rcontent {
|
||||||
return recordset, true
|
return recordset, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DNSRecordSet{}, false
|
return DNSRecordSet{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendPATCH sends a PATCH API request for DNSZone z. Returns error or nil
|
// SendPATCH sends a PATCH API request for DNSZone z. Returns error or nil
|
||||||
//
|
//
|
||||||
// Example args for "test.example.com IN A 127.0.0.1"
|
// Example args for "test.example.com IN A 127.0.0.1"
|
||||||
//
|
//
|
||||||
// z.Name = "example.com."
|
// z.Name = "example.com."
|
||||||
// record = "test"
|
// record = "test"
|
||||||
// value = "127.0.0.1"
|
// value = "127.0.0.1"
|
||||||
// recordtype = "A"
|
// recordtype = "A"
|
||||||
// changetype = "REPLACE"
|
// changetype = "REPLACE"
|
||||||
func (z DNSZone) SendPATCH(record string, value string, recordtype string, changetype string) error {
|
func (z DNSZone) SendPATCH(record string, value string, recordtype string, changetype string) error {
|
||||||
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
||||||
pdnsapikey := viper.GetString("powerdnsapikey")
|
pdnsapikey := viper.GetString("powerdnsapikey")
|
||||||
debug, _ := rootCmd.Flags().GetBool("debug")
|
debug, _ := rootCmd.Flags().GetBool("debug")
|
||||||
|
|
||||||
if !viper.GetBool("powerdnsenabled") {
|
if !viper.GetBool("powerdnsenabled") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + z.Name
|
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + z.Name
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Println("[DEBUG] PowerDNS URL: " + url)
|
fmt.Println("[DEBUG] PowerDNS URL: " + url)
|
||||||
}
|
}
|
||||||
|
|
||||||
rset := DNSRecordSet{}
|
rset := DNSRecordSet{}
|
||||||
rset.Changetype = changetype
|
rset.Changetype = changetype
|
||||||
rset.Name = strings.Join([]string{record, z.Name}, ".")
|
rset.Name = strings.Join([]string{record, z.Name}, ".")
|
||||||
rset.TTL = 3600
|
rset.TTL = 3600
|
||||||
rset.Type = recordtype
|
rset.Type = recordtype
|
||||||
rec := DNSRecord{}
|
rec := DNSRecord{}
|
||||||
if recordtype == "PTR" {
|
if recordtype == "PTR" {
|
||||||
rec.Content = value + "."
|
rec.Content = value + "."
|
||||||
} else {
|
} else {
|
||||||
rec.Content = value
|
rec.Content = value
|
||||||
}
|
}
|
||||||
rset.Records = append(rset.Records, rec)
|
rset.Records = append(rset.Records, rec)
|
||||||
|
|
||||||
patch := Patch{}
|
patch := Patch{}
|
||||||
patch.Rrsets = append(patch.Rrsets, rset)
|
patch.Rrsets = append(patch.Rrsets, rset)
|
||||||
|
|
||||||
payload, marsherr := json.Marshal(patch)
|
payload, marsherr := json.Marshal(patch)
|
||||||
if marsherr != nil {
|
if marsherr != nil {
|
||||||
return marsherr
|
return marsherr
|
||||||
}
|
}
|
||||||
|
|
||||||
req, reqerr := http.NewRequest("PATCH", url, bytes.NewBuffer(payload))
|
req, reqerr := http.NewRequest("PATCH", url, bytes.NewBuffer(payload))
|
||||||
if reqerr != nil {
|
if reqerr != nil {
|
||||||
return reqerr
|
return reqerr
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("X-API-Key", pdnsapikey)
|
req.Header.Add("X-API-Key", pdnsapikey)
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, resperr := client.Do(req)
|
resp, _ := client.Do(req)
|
||||||
if resperr != nil {
|
if resp.StatusCode != 204 {
|
||||||
return resperr
|
defer resp.Body.Close()
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != 204 {
|
body, readerr := io.ReadAll(resp.Body)
|
||||||
defer resp.Body.Close()
|
if readerr != nil {
|
||||||
|
fmt.Println(readerr)
|
||||||
body, readerr := io.ReadAll(resp.Body)
|
}
|
||||||
if readerr != nil {
|
return fmt.Errorf("[HTTP ERROR] %v: %q", resp.Status, string(body))
|
||||||
fmt.Println(readerr)
|
}
|
||||||
}
|
return nil
|
||||||
return errors.New("HTTP Error: " + resp.Status + "\n" + string(body))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSRecordSet struct {
|
type DNSRecordSet struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
TTL int `json:"ttl"`
|
TTL int `json:"ttl"`
|
||||||
Records []DNSRecord `json:"records"`
|
Records []DNSRecord `json:"records"`
|
||||||
Changetype string `json:"changetype,omitempty"`
|
Changetype string `json:"changetype,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSRecord struct {
|
type DNSRecord struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Disabled bool `json:"disabled,omitempty"`
|
Disabled bool `json:"disabled,omitempty"`
|
||||||
SetPTR bool `json:"set-ptr,omitempty"`
|
SetPTR bool `json:"set-ptr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDNSZone retrieves the corresponding DNSZone for string zone
|
// GetDNSZone retrieves the corresponding DNSZone for string zone
|
||||||
|
@ -146,45 +142,45 @@ type DNSRecord struct {
|
||||||
// Returns the DNSZone and nil if successful, empty DNSZone and an
|
// Returns the DNSZone and nil if successful, empty DNSZone and an
|
||||||
// error otherwise
|
// error otherwise
|
||||||
func GetDNSZone(zone string) (DNSZone, error) {
|
func GetDNSZone(zone string) (DNSZone, error) {
|
||||||
if !strings.HasSuffix(zone, ".") {
|
if !strings.HasSuffix(zone, ".") {
|
||||||
zone = zone + "."
|
zone = zone + "."
|
||||||
}
|
}
|
||||||
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
||||||
pdnsapikey := viper.GetString("powerdnsapikey")
|
pdnsapikey := viper.GetString("powerdnsapikey")
|
||||||
|
|
||||||
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + zone
|
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + zone
|
||||||
|
|
||||||
req, reqerr := http.NewRequest("GET", url, nil)
|
req, reqerr := http.NewRequest("GET", url, nil)
|
||||||
if reqerr != nil {
|
if reqerr != nil {
|
||||||
fmt.Println(reqerr)
|
fmt.Println(reqerr)
|
||||||
return DNSZone{}, reqerr
|
return DNSZone{}, reqerr
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("X-API-Key", pdnsapikey)
|
req.Header.Add("X-API-Key", pdnsapikey)
|
||||||
req.Header.Add("Accept", "application/json")
|
req.Header.Add("Accept", "application/json")
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, resperr := client.Do(req)
|
resp, resperr := client.Do(req)
|
||||||
if resperr != nil {
|
if resperr != nil {
|
||||||
fmt.Println(resperr)
|
fmt.Println(resperr)
|
||||||
return DNSZone{}, resperr
|
return DNSZone{}, resperr
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, readerr := io.ReadAll(resp.Body)
|
body, readerr := io.ReadAll(resp.Body)
|
||||||
if readerr != nil {
|
if readerr != nil {
|
||||||
fmt.Println(readerr)
|
fmt.Println(readerr)
|
||||||
return DNSZone{}, readerr
|
return DNSZone{}, readerr
|
||||||
}
|
}
|
||||||
|
|
||||||
var zoneobj DNSZone
|
var zoneobj DNSZone
|
||||||
marsherr := json.Unmarshal(body, &zoneobj)
|
marsherr := json.Unmarshal(body, &zoneobj)
|
||||||
if marsherr != nil {
|
if marsherr != nil {
|
||||||
fmt.Println(marsherr)
|
fmt.Println(marsherr)
|
||||||
return DNSZone{}, marsherr
|
return DNSZone{}, marsherr
|
||||||
}
|
}
|
||||||
|
|
||||||
return zoneobj, nil
|
return zoneobj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBestDNSZone requests a list of all zones from PowerDNS
|
// GetBestDNSZone requests a list of all zones from PowerDNS
|
||||||
|
@ -193,70 +189,70 @@ func GetDNSZone(zone string) (DNSZone, error) {
|
||||||
// Returns the found DNSZone and nil if a suitable zone was
|
// Returns the found DNSZone and nil if a suitable zone was
|
||||||
// found, an empty DNSZone object and an error if not.
|
// found, an empty DNSZone object and an error if not.
|
||||||
func GetBestDNSZone(fqdn string) (DNSZone, error) {
|
func GetBestDNSZone(fqdn string) (DNSZone, error) {
|
||||||
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
pdnsendpoint := viper.GetString("powerdnsendpoint")
|
||||||
pdnsapikey := viper.GetString("powerdnsapikey")
|
pdnsapikey := viper.GetString("powerdnsapikey")
|
||||||
fqdn = fqdn + "."
|
fqdn = fqdn + "."
|
||||||
|
|
||||||
if !viper.GetBool("powerdnsenabled") {
|
if !viper.GetBool("powerdnsenabled") {
|
||||||
return DNSZone{}, errors.New("PowerDNS integration not enabled")
|
return DNSZone{}, errors.New("PowerDNS integration not enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
url := pdnsendpoint + "/api/v1/servers/localhost/zones"
|
url := pdnsendpoint + "/api/v1/servers/localhost/zones"
|
||||||
|
|
||||||
req, reqerr := http.NewRequest("GET", url, nil)
|
req, reqerr := http.NewRequest("GET", url, nil)
|
||||||
if reqerr != nil {
|
if reqerr != nil {
|
||||||
fmt.Println(reqerr)
|
fmt.Println(reqerr)
|
||||||
return DNSZone{}, reqerr
|
return DNSZone{}, reqerr
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("X-API-Key", pdnsapikey)
|
req.Header.Add("X-API-Key", pdnsapikey)
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, resperr := client.Do(req)
|
resp, resperr := client.Do(req)
|
||||||
if resperr != nil {
|
if resperr != nil {
|
||||||
fmt.Println(resperr)
|
fmt.Println(resperr)
|
||||||
return DNSZone{}, resperr
|
return DNSZone{}, resperr
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, readerr := io.ReadAll(resp.Body)
|
body, readerr := io.ReadAll(resp.Body)
|
||||||
if readerr != nil {
|
if readerr != nil {
|
||||||
fmt.Println(readerr)
|
fmt.Println(readerr)
|
||||||
return DNSZone{}, readerr
|
return DNSZone{}, readerr
|
||||||
}
|
}
|
||||||
|
|
||||||
var zones []DNSZone
|
var zones []DNSZone
|
||||||
marsherr := json.Unmarshal(body, &zones)
|
marsherr := json.Unmarshal(body, &zones)
|
||||||
if marsherr != nil {
|
if marsherr != nil {
|
||||||
fmt.Println(marsherr)
|
fmt.Println(marsherr)
|
||||||
return DNSZone{}, marsherr
|
return DNSZone{}, marsherr
|
||||||
}
|
}
|
||||||
|
|
||||||
var bestmatch DNSZone
|
var bestmatch DNSZone
|
||||||
var matchfound bool = false
|
var matchfound bool = false
|
||||||
|
|
||||||
for _, zone := range zones {
|
for _, zone := range zones {
|
||||||
if strings.HasSuffix(fqdn, zone.Name) {
|
if strings.HasSuffix(fqdn, "."+zone.Name) {
|
||||||
if !matchfound {
|
if !matchfound {
|
||||||
matchfound = true
|
matchfound = true
|
||||||
bestmatch = zone
|
bestmatch = zone
|
||||||
}
|
}
|
||||||
if matchfound && len(zone.Name) > len(bestmatch.Name) {
|
if matchfound && len(zone.Name) > len(bestmatch.Name) {
|
||||||
bestmatch = zone
|
bestmatch = zone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matchfound {
|
if !matchfound {
|
||||||
return DNSZone{}, errors.New("No suitable zone found for " + fqdn)
|
return DNSZone{}, errors.New("No suitable zone found for " + fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
zone, geterr := GetDNSZone(bestmatch.ID)
|
zone, geterr := GetDNSZone(bestmatch.ID)
|
||||||
if geterr != nil {
|
if geterr != nil {
|
||||||
return DNSZone{}, geterr
|
return DNSZone{}, geterr
|
||||||
}
|
}
|
||||||
|
|
||||||
return zone, nil
|
return zone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDNSFqdn tries to create forward and reverse lookup records
|
// AddDNSFqdn tries to create forward and reverse lookup records
|
||||||
|
@ -265,74 +261,76 @@ func GetBestDNSZone(fqdn string) (DNSZone, error) {
|
||||||
//
|
//
|
||||||
// Returns nil on success, error otherwise
|
// Returns nil on success, error otherwise
|
||||||
func AddDNSFqdn(fqdn string, addr netip.Addr) error {
|
func AddDNSFqdn(fqdn string, addr netip.Addr) error {
|
||||||
debug, _ := rootCmd.Flags().GetBool("debug")
|
debug, _ := rootCmd.Flags().GetBool("debug")
|
||||||
if !viper.GetBool("powerdnsenabled") {
|
if !viper.GetBool("powerdnsenabled") {
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
|
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordtype string
|
var recordtype string
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
recordtype = "A"
|
recordtype = "A"
|
||||||
} else if addr.Is6() {
|
} else if addr.Is6() {
|
||||||
recordtype = "AAAA"
|
recordtype = "AAAA"
|
||||||
} else {
|
} else {
|
||||||
return errors.New(addr.String() + " is not a valid IP address")
|
return errors.New(addr.String() + " is not a valid IP address")
|
||||||
}
|
}
|
||||||
|
|
||||||
fzone, fzoneerr := GetBestDNSZone(fqdn)
|
fzone, fzoneerr := GetBestDNSZone(fqdn)
|
||||||
if fzoneerr != nil {
|
if fzoneerr != nil {
|
||||||
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", fqdn)
|
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", fqdn)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
|
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
|
||||||
|
|
||||||
if frecordexists {
|
if frecordexists {
|
||||||
fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn)
|
fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn)
|
||||||
} else {
|
} else {
|
||||||
fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "REPLACE")
|
var dotfqdn, dotfzone string = fqdn + ".", "." + fzone.Name
|
||||||
if fpatcherr != nil {
|
record := strings.Replace(dotfqdn, dotfzone, "", 1)
|
||||||
return fpatcherr
|
fpatcherr := fzone.SendPATCH(record, addr.String(), recordtype, "REPLACE")
|
||||||
}
|
if fpatcherr != nil {
|
||||||
fmt.Printf("[DNS] + %v IN %v %v\n", fqdn, recordtype, addr.String())
|
return fpatcherr
|
||||||
}
|
}
|
||||||
}
|
fmt.Printf("[DNS] + %v IN %v %v\n", fqdn, recordtype, addr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
baseip := addr.StringExpanded()
|
baseip := addr.StringExpanded()
|
||||||
var rfqdn string
|
var rfqdn string
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
a := strings.Split(baseip, ".")
|
a := strings.Split(baseip, ".")
|
||||||
b := reverse(a)
|
b := reverse(a)
|
||||||
rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
|
rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
|
||||||
} else if addr.Is6() {
|
} else if addr.Is6() {
|
||||||
a := strings.Replace(baseip, ":", "", -1)
|
a := strings.Replace(baseip, ":", "", -1)
|
||||||
b := strings.Split(a, "")
|
b := strings.Split(a, "")
|
||||||
c := reverse(b)
|
c := reverse(b)
|
||||||
rfqdn = strings.Join(c, ".") + ".ip6.arpa"
|
rfqdn = strings.Join(c, ".") + ".ip6.arpa"
|
||||||
}
|
}
|
||||||
|
|
||||||
rzone, rzoneerr := GetBestDNSZone(rfqdn)
|
rzone, rzoneerr := GetBestDNSZone(rfqdn)
|
||||||
if rzoneerr != nil {
|
if rzoneerr != nil {
|
||||||
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", rfqdn)
|
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", rfqdn)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
|
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
|
||||||
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
|
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
|
||||||
|
|
||||||
if rrecordexists {
|
if rrecordexists {
|
||||||
fmt.Printf("[DNS] Reverse DNS Record for %v already exists, no need to change DNS.\n", addr.String())
|
fmt.Printf("[DNS] Reverse DNS Record for %v already exists, no need to change DNS.\n", addr.String())
|
||||||
} else {
|
} else {
|
||||||
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "REPLACE")
|
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "REPLACE")
|
||||||
if rpatcherr != nil {
|
if rpatcherr != nil {
|
||||||
return rpatcherr
|
return rpatcherr
|
||||||
}
|
}
|
||||||
fmt.Printf("[DNS] + %v IN %v %v\n", rfqdn, "PTR", fqdn)
|
fmt.Printf("[DNS] + %v IN %v %v\n", rfqdn, "PTR", fqdn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDNSFqdn tries to delete the corresponding record for
|
// DeleteDNSFqdn tries to delete the corresponding record for
|
||||||
|
@ -340,70 +338,70 @@ func AddDNSFqdn(fqdn string, addr netip.Addr) error {
|
||||||
//
|
//
|
||||||
// Returns nil on success, error otherwise
|
// Returns nil on success, error otherwise
|
||||||
func DeleteDNSFqdn(fqdn string, addr netip.Addr) error {
|
func DeleteDNSFqdn(fqdn string, addr netip.Addr) error {
|
||||||
debug, _ := rootCmd.Flags().GetBool("debug")
|
debug, _ := rootCmd.Flags().GetBool("debug")
|
||||||
if !viper.GetBool("powerdnsenabled") {
|
if !viper.GetBool("powerdnsenabled") {
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
|
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordtype string
|
var recordtype string
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
recordtype = "A"
|
recordtype = "A"
|
||||||
} else if addr.Is6() {
|
} else if addr.Is6() {
|
||||||
recordtype = "AAAA"
|
recordtype = "AAAA"
|
||||||
} else {
|
} else {
|
||||||
return errors.New(addr.String() + " is not a valid IP address")
|
return errors.New(addr.String() + " is not a valid IP address")
|
||||||
}
|
}
|
||||||
|
|
||||||
fzone, fzoneerr := GetBestDNSZone(fqdn)
|
fzone, fzoneerr := GetBestDNSZone(fqdn)
|
||||||
if fzoneerr != nil {
|
if fzoneerr != nil {
|
||||||
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", fqdn)
|
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", fqdn)
|
||||||
} else {
|
} else {
|
||||||
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
|
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
|
||||||
|
|
||||||
if !frecordexists {
|
if !frecordexists {
|
||||||
fmt.Printf("[DNS] DNS Record for %v doesn't exists, no need to change DNS.\n", fqdn)
|
fmt.Printf("[DNS] DNS Record for %v doesn't exists, no need to change DNS.\n", fqdn)
|
||||||
} else {
|
} else {
|
||||||
fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "DELETE")
|
fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "DELETE")
|
||||||
if fpatcherr != nil {
|
if fpatcherr != nil {
|
||||||
return fpatcherr
|
return fpatcherr
|
||||||
}
|
}
|
||||||
fmt.Printf("[DNS] - %v IN %v %v\n", fqdn, recordtype, addr.String())
|
fmt.Printf("[DNS] - %v IN %v %v\n", fqdn, recordtype, addr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
baseip := addr.StringExpanded()
|
baseip := addr.StringExpanded()
|
||||||
var rfqdn string
|
var rfqdn string
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
a := strings.Split(baseip, ".")
|
a := strings.Split(baseip, ".")
|
||||||
b := reverse(a)
|
b := reverse(a)
|
||||||
rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
|
rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
|
||||||
} else if addr.Is6() {
|
} else if addr.Is6() {
|
||||||
a := strings.Replace(baseip, ":", "", -1)
|
a := strings.Replace(baseip, ":", "", -1)
|
||||||
b := strings.Split(a, "")
|
b := strings.Split(a, "")
|
||||||
c := reverse(b)
|
c := reverse(b)
|
||||||
rfqdn = strings.Join(c, ".") + ".ip6.arpa"
|
rfqdn = strings.Join(c, ".") + ".ip6.arpa"
|
||||||
}
|
}
|
||||||
|
|
||||||
rzone, rzoneerr := GetBestDNSZone(rfqdn)
|
rzone, rzoneerr := GetBestDNSZone(rfqdn)
|
||||||
if rzoneerr != nil {
|
if rzoneerr != nil {
|
||||||
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", rfqdn)
|
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", rfqdn)
|
||||||
} else {
|
} else {
|
||||||
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
|
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
|
||||||
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
|
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
|
||||||
|
|
||||||
if !rrecordexists {
|
if !rrecordexists {
|
||||||
fmt.Printf("[DNS] Reverse DNS Record for %v doesn't exists, no need to change DNS.\n", addr.String())
|
fmt.Printf("[DNS] Reverse DNS Record for %v doesn't exists, no need to change DNS.\n", addr.String())
|
||||||
} else {
|
} else {
|
||||||
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "DELETE")
|
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "DELETE")
|
||||||
if rpatcherr != nil {
|
if rpatcherr != nil {
|
||||||
return rpatcherr
|
return rpatcherr
|
||||||
}
|
}
|
||||||
fmt.Printf("[DNS] - %v IN %v %v\n", rfqdn, "PTR", fqdn)
|
fmt.Printf("[DNS] - %v IN %v %v\n", rfqdn, "PTR", fqdn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue