From 6635d9ed74a468dd3118acf050db03266c9b1ce3 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 24 Apr 2023 21:57:47 +0200 Subject: [PATCH] Add DNS Server Backend --- internal/api/service/dns_server.go | 81 +++++++++++++++++++++++++++ internal/unbound/apply.go | 54 ++++++++++++++++++ internal/unbound/server.go | 17 ++++++ internal/unbound/template.go | 35 ++++++++++++ internal/unbound/template/server.tmpl | 15 +++++ main.go | 2 + 6 files changed, 204 insertions(+) create mode 100644 internal/api/service/dns_server.go create mode 100644 internal/unbound/apply.go create mode 100644 internal/unbound/server.go create mode 100644 internal/unbound/template.go create mode 100644 internal/unbound/template/server.tmpl diff --git a/internal/api/service/dns_server.go b/internal/api/service/dns_server.go new file mode 100644 index 0000000..9803968 --- /dev/null +++ b/internal/api/service/dns_server.go @@ -0,0 +1,81 @@ +package service + +import ( + "context" + "fmt" + + "nfsense.net/nfsense/internal/definitions/service" +) + +type GetDNSServerParameters struct { + ID uint +} + +type GetDNSServerResult struct { + service.DNSServer +} + +func (f *Service) GetDNSServer(ctx context.Context, params GetDNSServerParameters) (GetDNSServerResult, error) { + if int(params.ID) >= len(f.ConfigManager.GetPendingConfig().Service.DNSServers) { + return GetDNSServerResult{}, fmt.Errorf("DNSServer does not Exist") + } + + return GetDNSServerResult{ + DNSServer: f.ConfigManager.GetPendingConfig().Service.DNSServers[params.ID], + }, nil +} + +type GetDNSServersResult struct { + DNSServers []service.DNSServer `json:"dns_servers"` +} + +func (f *Service) GetDNSServers(ctx context.Context, params struct{}) (GetDNSServersResult, error) { + return GetDNSServersResult{ + DNSServers: f.ConfigManager.GetPendingConfig().Service.DNSServers, + }, nil +} + +type CreateDNSServerParameters struct { + service.DNSServer +} + +func (f *Service) CreateDNSServer(ctx context.Context, params CreateDNSServerParameters) (struct{}, error) { + t, conf := f.ConfigManager.StartTransaction() + defer t.Discard() + + conf.Service.DNSServers = append(conf.Service.DNSServers, params.DNSServer) + return struct{}{}, t.Commit() +} + +type UpdateDNSServerParameters struct { + Index uint64 `json:"index"` + DNSServer service.DNSServer `json:"dns_server"` +} + +func (f *Service) UpdateDNSServer(ctx context.Context, params UpdateDNSServerParameters) (struct{}, error) { + if int(params.Index) >= len(f.ConfigManager.GetPendingConfig().Service.DNSServers) { + return struct{}{}, fmt.Errorf("DNSServer does not Exist") + } + + t, conf := f.ConfigManager.StartTransaction() + defer t.Discard() + + conf.Service.DNSServers[params.Index] = params.DNSServer + return struct{}{}, t.Commit() +} + +type DeleteDNSServerParameters struct { + Index uint64 `json:"index"` +} + +func (f *Service) DeleteDNSServer(ctx context.Context, params DeleteDNSServerParameters) (struct{}, error) { + if int(params.Index) >= len(f.ConfigManager.GetPendingConfig().Service.DNSServers) { + return struct{}{}, fmt.Errorf("DNSServer does not Exist") + } + + t, conf := f.ConfigManager.StartTransaction() + defer t.Discard() + + conf.Service.DNSServers = append(conf.Service.DNSServers[:params.Index], conf.Service.DNSServers[params.Index+1:]...) + return struct{}{}, t.Commit() +} diff --git a/internal/unbound/apply.go b/internal/unbound/apply.go new file mode 100644 index 0000000..7f23abf --- /dev/null +++ b/internal/unbound/apply.go @@ -0,0 +1,54 @@ +package unbound + +import ( + "context" + "fmt" + + systemctl "github.com/coreos/go-systemd/v22/dbus" + "nfsense.net/nfsense/internal/definitions/config" + "nfsense.net/nfsense/internal/util" +) + +const unboundServerFile = "/etc/unbound/unbound.conf" + +func ApplyDNSServerConfiguration(currentConfig config.Config, pendingConfig config.Config) error { + + serverConf, err := GenerateUnboundServerConfiguration(pendingConfig) + if err != nil { + return fmt.Errorf("Generating Unbound Server Configuration: %w", err) + } + + err = util.OverwriteFile(unboundServerFile, serverConf) + if err != nil { + return fmt.Errorf("Writing server Configuration: %w", err) + } + + conn, err := systemctl.NewSystemConnectionContext(context.Background()) + if err != nil { + return fmt.Errorf("Opening Dbus Connection: %w", err) + } + + if len(pendingConfig.Service.DNSServers) == 0 { + // if there are no servers stop the service instead + _, err := conn.StopUnitContext(context.Background(), "unbound.service", "replace", nil) + if err != nil { + return fmt.Errorf("stopping unbound.service: %w", err) + } + + _, err = conn.DisableUnitFilesContext(context.Background(), []string{"unbound.service"}, false) + if err != nil { + return fmt.Errorf("disableing unbound.service: %w", err) + } + } else { + _, err := conn.ReloadOrRestartUnitContext(context.Background(), "unbound.service", "replace", nil) + if err != nil { + return fmt.Errorf("restarting unbound.service: %w", err) + } + + _, _, err = conn.EnableUnitFilesContext(context.Background(), []string{"unbound.service"}, false, true) + if err != nil { + return fmt.Errorf("enableing unbound.service: %w", err) + } + } + return nil +} diff --git a/internal/unbound/server.go b/internal/unbound/server.go new file mode 100644 index 0000000..bca001a --- /dev/null +++ b/internal/unbound/server.go @@ -0,0 +1,17 @@ +package unbound + +import ( + "bytes" + "fmt" + + "nfsense.net/nfsense/internal/definitions/config" +) + +func GenerateUnboundServerConfiguration(conf config.Config) (string, error) { + buf := new(bytes.Buffer) + err := templates.ExecuteTemplate(buf, "server.tmpl", conf) + if err != nil { + return "", fmt.Errorf("executing server.tmpl template: %w", err) + } + return buf.String(), nil +} diff --git a/internal/unbound/template.go b/internal/unbound/template.go new file mode 100644 index 0000000..a98f79b --- /dev/null +++ b/internal/unbound/template.go @@ -0,0 +1,35 @@ +package unbound + +import ( + "embed" + "text/template" + + "nfsense.net/nfsense/internal/definitions/config" + "nfsense.net/nfsense/internal/definitions/network" +) + +//go:embed template +var templateFS embed.FS +var templates *template.Template + +func init() { + var err error + templates, err = template.New("").Funcs(template.FuncMap{ + "getInterfaceNetworkAddressCIDR": getInterfaceNetworkAddressCIDR, + "getInterfaceName": getInterfaceName, + }).ParseFS(templateFS, "template/*.tmpl") + if err != nil { + panic(err) + } +} + +func getInterfaceNetworkAddressCIDR(conf config.Config, name string) string { + return conf.Network.Interfaces[name].Address.Masked().String() +} + +func getInterfaceName(conf config.Config, name string) string { + if conf.Network.Interfaces[name].Type == network.Hardware { + return *conf.Network.Interfaces[name].HardwareDevice + } + return name +} diff --git a/internal/unbound/template/server.tmpl b/internal/unbound/template/server.tmpl new file mode 100644 index 0000000..6535cf8 --- /dev/null +++ b/internal/unbound/template/server.tmpl @@ -0,0 +1,15 @@ +server: + +# Listen Interfaces +{{- range $i, $server := .Service.NTPServers }} + interface: {{ getInterfaceName $ $server.Interface }} +{{- end }} + +# Allowed Networks +{{- range $i, $server := .Service.NTPServers }} + access-control: {{ getInterfaceNetworkAddressCIDR $ $server.Interface }} allow +{{- end }} + + +remote-control: + control-enable: yes \ No newline at end of file diff --git a/main.go b/main.go index f591495..8f607b6 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "nfsense.net/nfsense/internal/jsonrpc" "nfsense.net/nfsense/internal/networkd" "nfsense.net/nfsense/internal/server" + "nfsense.net/nfsense/internal/unbound" ) func main() { @@ -106,4 +107,5 @@ func RegisterApplyFunctions(configManager *config.ConfigManager) { configManager.RegisterApplyFunction(networkd.ApplyNetworkdConfiguration) configManager.RegisterApplyFunction(dhcp.ApplyDHCPServerConfiguration) configManager.RegisterApplyFunction(chrony.ApplyNTPConfiguration) + configManager.RegisterApplyFunction(unbound.ApplyDNSServerConfiguration) }