From 4b0f05fc0e0fb6a94a61e39844e06ce3ae10f470 Mon Sep 17 00:00:00 2001 From: f18m Date: Tue, 10 Dec 2024 02:02:21 +0100 Subject: [PATCH] More settings related to DNS Include DNS in the web UI --- DOCS.md | 7 ++++ Makefile | 5 ++- README.md | 15 ++++++- config.yaml | 4 ++ .../pkg/uibackend/types.go | 20 ++++++++-- .../pkg/uibackend/uibackend.go | 26 +++++++++--- .../templates/dnsmasq-dhcp.js | 6 +-- .../templates/index.templ.html | 40 +++++++++++++------ rootfs/usr/share/tempio/dnsmasq.config | 2 + 9 files changed, 100 insertions(+), 25 deletions(-) diff --git a/DOCS.md b/DOCS.md index e03dd37..b827526 100644 --- a/DOCS.md +++ b/DOCS.md @@ -46,6 +46,13 @@ non-informative, so Dnsmasq-DHCP allow users to override that by specifying a hu name for a particular DHCP client (using its MAC address as identifier). +### HomeAssistant mDNS + +HomeAssistant runs an [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) server on port 5353. +This is not impacted in any way by the DNS server functionality offered by this addon. + + + ## Links - [dnsmasq manual page](https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) diff --git a/Makefile b/Makefile index ca2af78..1106c1b 100644 --- a/Makefile +++ b/Makefile @@ -60,10 +60,13 @@ test-docker-image: ${DOCKER_RUN_OPTIONS} \ ${IMAGETAG}:localtest - +# NOTE: in the HTTP link below the port is actually the one in test-options.json, and currently it's 8976 test-docker-image-live: docker build -f Dockerfile.live -t debug-image-live . + @echo @echo "Starting container of image debug-image-live" + @echo "Point your browser at http://localhost:8976" + @echo docker run \ --rm \ --name $(TEST_CONTAINER_NAME) \ diff --git a/README.md b/README.md index 9e1516d..66cb05e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This addon also implements a UI webpage to view the list of DHCP clients with al ## About -This addon setups and manages a Dnsmasq instance configured to run as a DNS and DHCP server (despite the name 'dnsmasq' also provides DHCP server functionalities, not only DNS). +This addon setups and manages a Dnsmasq instance configured to run both as a DNS and DHCP server (despite the name 'dnsmasq' also provides DHCP server functionalities, not only DNS). [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg @@ -64,3 +64,16 @@ The UI nginx reverse-proxy configuration has been adapted from: ## How to Install Check out the [addon docs](DOCS.md). Open an issue if you hit any problem. + +## Similar Addons + +* [dnsmasq](https://github.com/home-assistant/addons/tree/master/dnsmasq): a simple DNS server addon (no DHCP). +* [AdGuard Home](https://github.com/hassio-addons/addon-adguard-home): network-wide ads & trackers blocking DNS server. It also includes an embedded DHCP server. + +## Other Noteworthy Projects + +* [pihole](https://pi-hole.net/): pi-hole embeds a modified dnsmasq variant (they named it FTL, Faster Than Light) which provides a bunch of DNS metrics that are missing from the regular dnsmasq binary. + +## Future Developments + +It might be interesting to \ No newline at end of file diff --git a/config.yaml b/config.yaml index 7f0c344..bb92f67 100644 --- a/config.yaml +++ b/config.yaml @@ -89,9 +89,12 @@ options: dns_server: # should this addon provide also a DNS server? enable: true + # on which port the dnsmasq DNS server must listen to? + port: 53 # how many entries should be cached on the DNS server to reduce traffic to upstream DNS servers? # the max size for this cache is 10k entries according to dnsmasq docs cache_size: 10000 + # log_requests will enable logging all DNS requests... which results in a very verbose log!! log_requests: false # DNS domain to resolve locally dns_domain: lan @@ -136,6 +139,7 @@ schema: link: "str?" dns_server: enable: bool + port: int cache_size: int log_requests: bool dns_domain: str diff --git a/dhcp-clients-webapp-backend/pkg/uibackend/types.go b/dhcp-clients-webapp-backend/pkg/uibackend/types.go index fed139d..b8e1a2b 100644 --- a/dhcp-clients-webapp-backend/pkg/uibackend/types.go +++ b/dhcp-clients-webapp-backend/pkg/uibackend/types.go @@ -97,8 +97,11 @@ type IpAddressReservation struct { Link *template.Template // maybe nil } -// AddonConfig is used to unmarshal HomeAssistant option file correctly -// This must be updated every time the config.yaml of the addon is changed +// AddonConfig is used to unmarshal HomeAssistant option file correctly. +// This must be updated every time the config.yaml of the addon is changed; +// however this structure contains only fields that are relevant to the +// UI backend behavior. In other words the addon config.yaml might contain +// more settings than those listed here. type AddonConfig struct { // Static IP addresses, as read from the configuration ipAddressReservationsByIP map[netip.Addr]IpAddressReservation @@ -124,9 +127,13 @@ type AddonConfig struct { // Lease times defaultLease string addressReservationLease string + + // DNS + dnsEnable bool + dnsDomain string } -// readAddonConfig reads the configuration of this Home Assistant addon and converts it +// UnmarshalJSON reads the configuration of this Home Assistant addon and converts it // into maps and slices that get stored into the UIBackend instance func (b *AddonConfig) UnmarshalJSON(data []byte) error { @@ -153,6 +160,11 @@ func (b *AddonConfig) UnmarshalJSON(data []byte) error { AddressReservationLease string `json:"address_reservation_lease"` } `json:"dhcp_server"` + DnsServer struct { + Enable bool `json:"enable"` + DnsDomain string `json:"dns_domain"` + } `json:"dns_server"` + WebUI struct { Log bool `json:"log_activity"` Port int `json:"port"` @@ -237,6 +249,8 @@ func (b *AddonConfig) UnmarshalJSON(data []byte) error { b.webUIPort = cfg.WebUI.Port b.defaultLease = cfg.DhcpServer.DefaultLease b.addressReservationLease = cfg.DhcpServer.AddressReservationLease + b.dnsEnable = cfg.DnsServer.Enable + b.dnsDomain = cfg.DnsServer.DnsDomain return nil } diff --git a/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go b/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go index 0e3ad89..f26f509 100644 --- a/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go +++ b/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go @@ -156,7 +156,7 @@ func (b *UIBackend) logRequestMiddleware(next http.Handler) http.Handler { }) } -func (b *UIBackend) getWebSocketMessage() WebSocketMessage { +func (b *UIBackend) generateWebSocketMessage() WebSocketMessage { // get a copy of latest status -- lock it during the copy, to avoid race conditions // with the dnsmasq.leases watcher goroutine: @@ -239,7 +239,7 @@ func (b *UIBackend) handleWebSocketConn(w http.ResponseWriter, r *http.Request) } defer ws.Close() - msg := b.getWebSocketMessage() + msg := b.generateWebSocketMessage() b.logger.Infof("Received new websocket client: pushing %d/%d current/past DHCP clients to it", len(msg.CurrentClients), len(msg.PastClients)) @@ -273,7 +273,7 @@ func (b *UIBackend) broadcastUpdatesToClients() { ticker := time.NewTicker(10 * time.Second) - msg := b.getWebSocketMessage() + msg := b.generateWebSocketMessage() for { select { case <-b.broadcastCh: @@ -291,7 +291,7 @@ func (b *UIBackend) broadcastUpdatesToClients() { if len(b.clients) > 0 { // regen message - msg = b.getWebSocketMessage() + msg = b.generateWebSocketMessage() // loop over all clients numSuccess := 0 @@ -388,11 +388,17 @@ func (b *UIBackend) renderPage(w http.ResponseWriter, r *http.Request) { dhcpPoolSize = int(iprange.New(b.cfg.dhcpStartIP, b.cfg.dhcpEndIP).Size().Int64()) } + // DNS + dnsEnableString := "disabled" + if b.cfg.dnsEnable { + dnsEnableString = "enabled" + } + templateData := struct { // websockets WebSocketURI string - // config info that are handy to have in the UI page + // DHCP config info that are handy to have in the UI page DhcpStartIP string DhcpEndIP string DhcpPoolSize int @@ -400,6 +406,10 @@ func (b *UIBackend) renderPage(w http.ResponseWriter, r *http.Request) { AddressReservationLease string DHCPServerStartTime int64 + // DNS config info + DnsEnabled string + DnsDomain string + // embedded contents CssFileContent template.CSS JavascriptFileContent template.JS @@ -420,6 +430,11 @@ func (b *UIBackend) renderPage(w http.ResponseWriter, r *http.Request) { // time of this app DHCPServerStartTime: b.startTimestamp.Unix(), + // DNS config info + DnsEnabled: dnsEnableString, + DnsDomain: b.cfg.dnsDomain, + + // embedded contents CssFileContent: template.CSS(b.cssContents), JavascriptFileContent: template.JS(b.jsContents), } @@ -522,6 +537,7 @@ func (b *UIBackend) evaluateLink(hostname string, ip netip.Addr, mac net.Hardwar "ip": ip.String(), "hostname": hostname, "friendly_name": friendlyName, + "dns_domain": b.cfg.dnsDomain, }) if err != nil { b.logger.Warnf("failed to render the link template [%v]", theTemplate) diff --git a/dhcp-clients-webapp-backend/templates/dnsmasq-dhcp.js b/dhcp-clients-webapp-backend/templates/dnsmasq-dhcp.js index 548aed8..d759e8d 100644 --- a/dhcp-clients-webapp-backend/templates/dnsmasq-dhcp.js +++ b/dhcp-clients-webapp-backend/templates/dnsmasq-dhcp.js @@ -61,7 +61,7 @@ function formatTimeSince(unixPastTimestamp) { const seconds = Math.floor((timeDifference % msecsInMinute) / 1000); // Format the time as a string - const dayPart = days > 0 ? `${days} day${days > 1 ? 's' : ''}, ` : ''; + const dayPart = days > 0 ? `${days}d, ` : ''; const timePart = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; return dayPart + timePart; @@ -175,7 +175,7 @@ function processWebSocketEvent(event) { console.error('Error while parsing JSON:', error); } - var message = document.getElementById("message"); + var message = document.getElementById("dhcp_stats_message"); if (data === null) { console.log("Websocket connection: received an empty JSON"); @@ -267,7 +267,7 @@ function processWebSocketEvent(event) { message.innerHTML = "" + data.current_clients.length + " DHCP current clients hold a DHCP lease.
" + dhcp_static_ip + " have a static IP address configuration.
" + dhcp_addresses_used + " are within the DHCP pool. DHCP pool usage is at " + usagePerc + "%.
" + - "" + data.past_clients.length + " DHCP past clients contacted the server some while ago but failed to do so since last DHCP server restart, " + + "" + data.past_clients.length + " DHCP past clients contacted the server some time ago but failed to do so since last DHCP server restart, " + uptime_str + " hh:mm:ss ago.
"; } } diff --git a/dhcp-clients-webapp-backend/templates/index.templ.html b/dhcp-clients-webapp-backend/templates/index.templ.html index 1fc4e0a..90eafcc 100644 --- a/dhcp-clients-webapp-backend/templates/index.templ.html +++ b/dhcp-clients-webapp-backend/templates/index.templ.html @@ -31,24 +31,29 @@ -

DHCP Clients

- -

- The configured DHCP range is: {{ .DhcpStartIP }} - {{ .DhcpEndIP }}. - The default lease time is {{ .DefaultLease }}. - The lease time for clients with IP address reservations is {{ .AddressReservationLease }}.
-

-

+

Dnsmasq-DHCP addon

- - + + + +
-
+
+

DHCP Server Summary

+ +

+ The configured DHCP range is: {{ .DhcpStartIP }} - {{ .DhcpEndIP }}.
+ The default lease time is {{ .DefaultLease }}. + The lease time for clients with IP address reservations is {{ .AddressReservationLease }}.
+

+

+
+
@@ -67,12 +72,23 @@

DHCP Clients

HH:MM:SS.
-
+
+
+

DNS Server Summary

+ +

+ DNS server is: {{ .DnsEnabled }}
+ DNS domain: {{ .DnsDomain }} +

+ +

+ +
diff --git a/rootfs/usr/share/tempio/dnsmasq.config b/rootfs/usr/share/tempio/dnsmasq.config index d71d184..c7a585d 100644 --- a/rootfs/usr/share/tempio/dnsmasq.config +++ b/rootfs/usr/share/tempio/dnsmasq.config @@ -16,6 +16,8 @@ user=root {{ if not .dns_server.enable }} # port=0 disables dnsmasq's DNS server functionality. port=0 +{{ else }} +port={{ .dns_server.port }} {{ end }} # do not use the DNS servers specified in /etc/resolv.conf: