From d908671962b6793c4ba89303b22fd3867f99cff1 Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Sun, 29 Sep 2024 23:15:34 +0200 Subject: [PATCH] Remove hardcoded port 8080 and replace with configurable port (#7) This PR is: * replacing the hardcoded port 8080 used by DHCP client backend with a configurable port, which defaults to 8967 (much less common than 8080 to make port conflicts less likely) * improving hostname and MAC address validations on the addon configuration using regex --- config.yaml | 21 +++++++++----- .../pkg/uibackend/uibackend.go | 28 ++++++++++++++----- rootfs/etc/cont-init.d/32-nginx_ingress.sh | 5 ++++ rootfs/etc/nginx/servers/ingress.conf | 2 +- rootfs/etc/nginx/servers/ssl.conf | 2 +- translations/en.yaml | 7 +++-- 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/config.yaml b/config.yaml index 6f6ee37..62b8056 100644 --- a/config.yaml +++ b/config.yaml @@ -3,9 +3,9 @@ # as reported by the Github Action workflow 'publish.yaml', so that you can force HomeAssistant # to use the docker image of that feature branch instead of the docker image of 'main', by pointing # HomeAssistant to that feature branch -version: 1.1.0 -slug: dnsmasq-dhcp -name: Dnsmasq-DHCP +version: beta +slug: dnsmasq-dhcp-beta +name: Dnsmasq-DHCP BETA description: A DHCP server based on dnsmasq url: https://github.com/f18m/ha-addon-dnsmasq-dhcp-server/tree/main advanced: true @@ -51,13 +51,17 @@ options: end_ip: 192.168.1.250 ip_address_reservations: - mac: aa:bb:cc:dd:ee:ff - name: "An important host with a static IP address assigned" + name: "An-important-host-with-reserved-IP" ip: 192.168.1.15 dhcp_clients_friendly_names: - mac: dd:ee:aa:dd:bb:ee name: "This is a friendly name to label this host, even if it gets a dynamic IP" log_dhcp: true log_web_ui: false + # this addon uses "host_network: true" so the internal HTTP server will bind on the interface + # provided as network.interface and will occupy a port there; the following parameter makes + # that port configurable to avoid conflicts with other services + web_ui_port: 8976 schema: default_lease: str address_reservation_lease: str @@ -75,13 +79,16 @@ schema: end_ip: str ip_address_reservations: - ip: str - mac: str - name: str + mac: match(^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$) + # the name in this case must be a valid hostname as per RFC 1123 since it is passed to dnsmasq + # that will refuse to start if an invalid hostname format is used + name: match(^[a-zA-Z0-9\-.]*$) dhcp_clients_friendly_names: - - mac: str + - mac: match(^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$) name: str log_dhcp: bool log_web_ui: bool + web_ui_port: int startup: system privileged: - NET_ADMIN diff --git a/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go b/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go index 881640b..b4dc800 100644 --- a/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go +++ b/dhcp-clients-webapp-backend/pkg/uibackend/uibackend.go @@ -21,7 +21,6 @@ import ( "github.com/netdata/go.d.plugin/pkg/iprange" ) -var bindAddress = ":8080" var defaultDnsmasqLeasesFile = "/data/dnsmasq.leases" var defaultHomeAssistantConfigFile = "/data/options.json" var dnsmasqMarkerForMissingHostname = "*" @@ -114,8 +113,9 @@ type UIBackend struct { dhcpEndIP net.IP // the actual HTTP server - server http.Server - upgrader websocket.Upgrader + serverPort int + server http.Server + upgrader websocket.Upgrader // map of connected websockets clients map[*websocket.Conn]bool @@ -125,10 +125,10 @@ type UIBackend struct { dhcpClientData []DhcpClientData dhcpClientDataLock sync.Mutex - // ch used to broadcast tabular data from backend->frontend + // channel used to broadcast tabular data from backend->frontend broadcastCh chan []DhcpClientData - // ch used to link a goroutine watching for DHCP lease file changes and the lease file processor + // channel used to link a goroutine watching for DHCP lease file changes and the lease file processor leasesCh chan []*dnsmasq.Lease } @@ -145,7 +145,7 @@ func NewUIBackend() UIBackend { }, }, server: http.Server{ - Addr: bindAddress, + Addr: "", Handler: nil, }, } @@ -374,6 +374,8 @@ type AddonConfig struct { LogDHCP bool `json:"log_dhcp"` LogWebUI bool `json:"log_web_ui"` + + WebUIPort int `json:"web_ui_port"` } func (b *UIBackend) readAddonConfig() error { @@ -401,6 +403,17 @@ func (b *UIBackend) readAddonConfig() error { // convert DHCP IP strings to net.IP b.dhcpStartIP = net.ParseIP(cfg.DhcpRange.StartIP) b.dhcpEndIP = net.ParseIP(cfg.DhcpRange.EndIP) + if b.dhcpStartIP == nil || b.dhcpEndIP == nil { + log.Default().Fatalf("Invalid DHCP range found in addon config file") + return os.ErrInvalid // I'm lazy, recycle a similar error type + } + + // ensure we have a valid port for web UI + if cfg.WebUIPort <= 0 || cfg.WebUIPort > 32768 { + log.Default().Fatalf("Invalid Web UI port in addon config file") + return os.ErrInvalid // I'm lazy, recycle a similar error type + } + b.serverPort = cfg.WebUIPort // convert to a slice of DhcpClientFriendlyName instances for _, client := range cfg.DhcpClientsFriendlyNames { @@ -464,7 +477,8 @@ func (b *UIBackend) ListenAndServe() error { go b.broadcastUpdatesToClients() // Start server - log.Default().Printf("Starting server to listen on %s\n", bindAddress) + log.Default().Printf("Starting server to listen on port %d\n", b.serverPort) + b.server.Addr = fmt.Sprintf(":%d", b.serverPort) b.server.Handler = mux return b.server.ListenAndServe() } diff --git a/rootfs/etc/cont-init.d/32-nginx_ingress.sh b/rootfs/etc/cont-init.d/32-nginx_ingress.sh index 959b63f..d73b19b 100755 --- a/rootfs/etc/cont-init.d/32-nginx_ingress.sh +++ b/rootfs/etc/cont-init.d/32-nginx_ingress.sh @@ -10,10 +10,12 @@ bashio::log.info "Configuring nginx ingress..." declare ingress_interface declare ingress_port declare ingress_entry +declare web_ui_port ingress_port=$(bashio::addon.ingress_port) ingress_interface=$(bashio::addon.ip_address) ingress_entry=$(bashio::addon.ingress_entry) +web_ui_port=$(bashio::config 'web_ui_port') if [ -z "$ingress_port" ]; then ingress_port=8100 @@ -26,3 +28,6 @@ sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ingress.conf sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ssl.conf + +sed -i "s|%%web_ui_port%%|${web_ui_port}|g" /etc/nginx/servers/ingress.conf +sed -i "s|%%web_ui_port%%|${web_ui_port}|g" /etc/nginx/servers/ssl.conf diff --git a/rootfs/etc/nginx/servers/ingress.conf b/rootfs/etc/nginx/servers/ingress.conf index c723e9b..2991216 100644 --- a/rootfs/etc/nginx/servers/ingress.conf +++ b/rootfs/etc/nginx/servers/ingress.conf @@ -11,7 +11,7 @@ server { location / { # Proxy - proxy_pass http://127.0.0.1:8080/; + proxy_pass http://127.0.0.1:%%web_ui_port%%/; proxy_read_timeout 30000; proxy_redirect off; diff --git a/rootfs/etc/nginx/servers/ssl.conf b/rootfs/etc/nginx/servers/ssl.conf index c47d047..71ed825 100644 --- a/rootfs/etc/nginx/servers/ssl.conf +++ b/rootfs/etc/nginx/servers/ssl.conf @@ -10,7 +10,7 @@ server { } location %%ingress_entry%%/ { - set $upstream_port 8080; + set $upstream_port %%web_ui_port%%; proxy_pass http://127.0.0.1:$upstream_port; } } diff --git a/translations/en.yaml b/translations/en.yaml index fb2c667..32cdeee 100644 --- a/translations/en.yaml +++ b/translations/en.yaml @@ -41,10 +41,10 @@ configuration: # description: The last IP address of the DHCP range that defines the DHCP address pool. ip_address_reservations: name: IP Address Reservations - description: List of MAC addresses / IP addresses pairs that are reserved. + description: List of MAC addresses / IP addresses pairs that are reserved. Strict regex validation is performed on MAC addresses and hostnames (use alphanumeric chars plus dot or hyphens only). dhcp_clients_friendly_names: name: DHCP Clients Friendly Names - description: List of MAC addresses / friendly-name pairs to help identify the DHCP clients in the Web UI. + description: List of MAC addresses / friendly-name pairs to help identify the DHCP clients in the Web UI. Strict regex validation is performed on MAC addresses. log_dhcp: name: Log DHCP @@ -52,3 +52,6 @@ configuration: log_web_ui: name: Log Web UI description: Log all HTTP requests served by the add-on UI + web_ui_port: + name: Web UI Port + description: Port used by the internal HTTP server. Change only if you get a conflict on the default port. \ No newline at end of file