139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
type IPAddress struct {
|
|
Address string `json:"address"`
|
|
DNSName string `json:"dns_name"`
|
|
}
|
|
|
|
type NetBoxResponse struct {
|
|
Results []IPAddress `json:"results"`
|
|
}
|
|
|
|
func FetchNetboxIPAddresses(apiBaseURL, token string) ([]IPAddress, error) {
|
|
u, _ := url.Parse(apiBaseURL)
|
|
u.Path = path.Join(u.Path, "ipam/ip-addresses")
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Accept", "application/json")
|
|
req.Header.Set("Authorization", "Token "+token)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status: %s, body: %s", resp.Status, string(body))
|
|
}
|
|
|
|
var nbResp NetBoxResponse
|
|
decoder := json.NewDecoder(resp.Body)
|
|
if err := decoder.Decode(&nbResp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Filter out entries with empty DNSName and strip CIDR from Address
|
|
var filtered []IPAddress
|
|
for _, ip := range nbResp.Results {
|
|
if ip.DNSName != "" {
|
|
ip.Address = strings.Split(ip.Address, "/")[0]
|
|
filtered = append(filtered, ip)
|
|
}
|
|
}
|
|
return filtered, nil
|
|
}
|
|
|
|
func CreateDnsMasqConfig(writeConfig bool) {
|
|
token := os.Getenv("NETBOX_TOKEN")
|
|
if token == "" {
|
|
home := os.Getenv("HOME")
|
|
paths := []string{
|
|
home + "/.netbox/token",
|
|
"/etc/netbox/token",
|
|
}
|
|
for _, path := range paths {
|
|
data, err := os.ReadFile(path)
|
|
if err == nil {
|
|
token = strings.TrimSpace(string(data))
|
|
break
|
|
}
|
|
}
|
|
if token == "" {
|
|
log.Fatal("NETBOX_TOKEN not set and no token file found")
|
|
}
|
|
}
|
|
|
|
ips, err := FetchNetboxIPAddresses(apiBaseURL, token)
|
|
if err != nil {
|
|
log.Fatalf("Error fetching IP addresses: %v", err)
|
|
}
|
|
|
|
dir := "/etc/dnsmasq.d"
|
|
if stat, err := os.Stat(dir); err == nil && stat.IsDir() && !writeConfig {
|
|
file, err := os.Create(dir + "/netbox.conf")
|
|
if err != nil {
|
|
log.Fatalf("Failed to create netbox.conf: %v", err)
|
|
}
|
|
defer file.Close()
|
|
for _, ip := range ips {
|
|
fmt.Fprintf(file, "address=/%s/%s\n", ip.DNSName, ip.Address)
|
|
}
|
|
} else {
|
|
for _, ip := range ips {
|
|
fmt.Printf("address=/%s/%s\n", ip.DNSName, ip.Address)
|
|
}
|
|
}
|
|
}
|
|
|
|
var apiBaseURL string
|
|
|
|
func main() {
|
|
listenAddr := flag.String("listen", ":8080", "address and port to listen on (e.g. :8080 or 127.0.0.1:8080)")
|
|
flag.StringVar(&apiBaseURL, "api-url", "https://netbox.koszewscy.waw.pl/api", "NetBox API URL to fetch IP addresses")
|
|
dryRun := flag.Bool("dry-run", false, "if set, do not write to dnsmasq config file, just print the output")
|
|
flag.Parse()
|
|
|
|
if *dryRun {
|
|
log.Println("Dry run mode enabled, not writing to dnsmasq config file.")
|
|
CreateDnsMasqConfig(true)
|
|
return
|
|
}
|
|
|
|
http.HandleFunc("/update-dnsmasq", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
w.Write([]byte("Method not allowed"))
|
|
return
|
|
}
|
|
CreateDnsMasqConfig(false)
|
|
cmd := exec.Command("systemctl", "restart", "dnsmasq.service")
|
|
if err := cmd.Run(); err != nil {
|
|
log.Printf("Failed to restart dnsmasq.service: %v", err)
|
|
w.Write([]byte("\nWarning: Failed to restart dnsmasq.service.\n"))
|
|
}
|
|
w.Write([]byte("DNSMasq config updated."))
|
|
})
|
|
log.Printf("Starting web service on %s...", *listenAddr)
|
|
if err := http.ListenAndServe(*listenAddr, nil); err != nil {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}
|