Skip to content

Commit

Permalink
feat: adding configurable gateway for DHCP server (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaile authored Dec 20, 2024
1 parent ecc03f9 commit 554f186
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 14 deletions.
8 changes: 4 additions & 4 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
FROM docker.io/node:18.20.5 as base
FROM docker.io/node:18.20.5 AS base
RUN corepack enable \
&& yarn set version berry
WORKDIR /app
ENV NODE_OPTIONS=--openssl-legacy-provider

FROM base as dev
FROM base AS dev
CMD exec /bin/bash -c "yarn install && yarn watch"

FROM base as builder
FROM base AS builder
COPY ui/.yarnrc.yml .
COPY ui/package.json .
COPY ui/yarn.lock .
Expand All @@ -20,7 +20,7 @@ COPY ui/babel.config.js .
COPY ui/vue.config.js .
RUN yarn build

FROM scratch as dist
FROM scratch AS dist
COPY imageroot imageroot
COPY --from=builder /app/dist ui
LABEL org.nethserver.rootfull=1
Expand Down
6 changes: 3 additions & 3 deletions container/Containerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM docker.io/debian:12.8-slim as base
FROM docker.io/debian:12.8-slim AS base

FROM base as build
FROM base AS build
# Setup image
RUN apt-get update \
&& apt-get install -y build-essential
Expand All @@ -14,7 +14,7 @@ WORKDIR /tmp/dnsmasq
# Build dnsmasq
RUN make

FROM base as dist
FROM base AS dist
# Copy built dnsmasq and configuration
COPY --from=build /tmp/dnsmasq/src/dnsmasq /usr/local/sbin/dnsmasq
COPY dnsmasq.conf /etc/dnsmasq.conf
Expand Down
14 changes: 14 additions & 0 deletions imageroot/actions/configure-module/10validate
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ if request["dhcp-server"]["enabled"]:
# check if start and end are in the same network being configured
start = ipaddress.IPv4Address(request["dhcp-server"]["start"])
end = ipaddress.IPv4Address(request["dhcp-server"]["end"])
gateway = ipaddress.IPv4Address(request["dhcp-server"]["gateway"])
interface_network = next(interface for interface in interfaces if interface["name"] == request["interface"])
start_found = False
for address in interface_network["addresses"]:
Expand All @@ -48,6 +49,12 @@ if request["dhcp-server"]["enabled"]:
end_found = True
break

gateway_found = False
for address in interface_network["addresses"]:
if gateway in address["network"]:
gateway_found = True
break

if not start_found:
agent.set_status('validation-failed')
json.dump([{'field':'dhcp-server.start', 'parameter':'dhcp-server.start','value': request['dhcp-server']['start'], 'error':'not_in_network'}], fp=sys.stdout)
Expand All @@ -64,6 +71,13 @@ if request["dhcp-server"]["enabled"]:
json.dump([{'field':'dhcp-server.start', 'parameter':'dhcp-server.start','value': request['dhcp-server']['start'], 'error':'must_be_less_than_end'}], fp=sys.stdout)
sys.exit(2)

if not gateway_found:
agent.set_status('validation-failed')
json.dump([{'field': 'dhcp-server.gateway', 'parameter': 'dhcp-server.gateway',
'value': request["dhcp-server"]["gateway"],
'error': 'not_in_network'}], fp=sys.stdout)
sys.exit(2)

if request["dns-server"]["enabled"]:
is_dns_bound = network.are_ports_53_bound() or bool(network.get_local_samba_dcs())
# read config.json and determine if dns is used for this instance
Expand Down
10 changes: 9 additions & 1 deletion imageroot/actions/configure-module/validate-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"description": "Lease time in hours",
"type": "integer",
"minimum": 1
},
"gateway": {
"description": "Gateway to be assigned to clients",
"type": "string"
}
},
"if": {
Expand All @@ -43,14 +47,18 @@
},
"end": {
"format": "ipv4"
},
"gateway": {
"format": "ipv4"
}
}
},
"required": [
"enabled",
"start",
"end",
"lease"
"lease",
"gateway"
]
},
"dns-server": {
Expand Down
2 changes: 1 addition & 1 deletion imageroot/actions/get-configuration/10get
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import json
import sys
import agent
import network

config = json.load(open("config.json"))
Expand All @@ -17,6 +16,7 @@ if config["interface"] != "" and config["dhcp-server"]["start"] == "" and config
interface = next(interface for interface in interfaces if interface["name"] == config["interface"])
config["dhcp-server"]["start"] = str(interface["start"])
config["dhcp-server"]["end"] = str(interface["end"])
config["dhcp-server"]["gateway"] = str(interface["gateway"])

# we test if tcp/53 or udp/53 is bound to the interface, or local Samba DCs are present
local_samba_dcs = network.get_local_samba_dcs()
Expand Down
15 changes: 14 additions & 1 deletion imageroot/actions/get-configuration/validate-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,26 @@
"description": "Lease time in hours",
"type": "integer",
"minimum": 1
},
"gateway": {
"description": "Gateway to be assigned to clients",
"type": "string",
"oneOf": [
{
"format": "ipv4"
},
{
"maxLength": 0
}
]
}
},
"required": [
"enabled",
"start",
"end",
"lease"
"lease",
"gateway"
]
},
"dns-server": {
Expand Down
5 changes: 4 additions & 1 deletion imageroot/bin/expand-config
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ except FileNotFoundError:
"enabled": False,
"start": "",
"end": "",
"lease": 12
"lease": 12,
"gateway": "",
},
"dns-server": {
"enabled": False,
Expand All @@ -45,6 +46,8 @@ with open("dnsmasq.d/00config.conf", "w") as file:
# write dhcp-server configuration
if config["dhcp-server"]["enabled"]:
file.write("dhcp-range=set:default," + config["dhcp-server"]["start"] + "," + config["dhcp-server"]["end"] + "," + str(config["dhcp-server"]["lease"]) + "h\n")
if "gateway" in config["dhcp-server"] and config["dhcp-server"]["gateway"] != "":
file.write("dhcp-option=tag:default,option:router," + config["dhcp-server"]["gateway"] + "\n")

# write dns-server configuration, if no local Samba DC is present
if len(local_samba_dcs) > 0:
Expand Down
13 changes: 11 additions & 2 deletions imageroot/pypkg/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#

import sys
import os
import agent
import ipaddress
Expand Down Expand Up @@ -41,8 +40,16 @@ def __format_interface(interface):
"name": interface["ifname"],
"addresses": [],
"start": "",
"end": ""
"end": "",
"gateway": ""
}
gateway = subprocess.run(["ip", "-4", "-j", "route", "show", "dev", interface["ifname"], "default"], check=True,
capture_output=True, text=True).stdout
gateway = json.loads(gateway)
if len(gateway) > 0:
gateway = gateway[0]['gateway']
else:
gateway = None
for address in interface["addr_info"]:
if address["family"] == "inet":
network = ipaddress.IPv4Network(address["local"] + "/" + str(address["prefixlen"]), strict=False)
Expand All @@ -53,6 +60,8 @@ def __format_interface(interface):
# last address wins the start and end fields
interface_data["start"] = network.network_address + 1
interface_data["end"] = network.network_address + network.num_addresses - 2
if gateway is not None and ipaddress.IPv4Address(gateway) in network:
interface_data["gateway"] = gateway

return interface_data

Expand Down
19 changes: 19 additions & 0 deletions imageroot/update-module.d/20gateway_default
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import json

config = json.load(open('config.json'))

if config['dhcp-server']['enabled']:
if 'gateway' not in config['dhcp-server']:
# the field is left empty, to avoid assigning a default value
# this is because if in any case the interface the dhcp-server is running
# has two or more ips, a default gateway might cause way too many issues
config['dhcp-server']['gateway'] = ''

json.dump(config, fp=open('config.json', 'w'))
1 change: 1 addition & 0 deletions ui/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18.20.5
5 changes: 4 additions & 1 deletion ui/public/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"DHCP_end_label": "IP range end",
"DHCP_lease_label": "Lease time",
"DHCP_lease_hint": "Hours",
"DHCP_gateway_label": "Gateway",
"DHCP_gateway_hint": "Default gateway for the DHCP clients",
"DNS_title": "DNS",
"DNS_description": "Network names forward and cache service, listening on port 53.",
"DNS_enable_label": "Enable DNS",
Expand All @@ -55,7 +57,8 @@
"dns-server_format": "IPv4 format required",
"dns-server_number_one_of": "IPv4 or IPv6 required",
"dns_server_is_running": "A DNS server is currently running",
"dns_server_is_running_description": "You cannot enable the DNS feature because a DNS server is already active on this node. Another application, such as Samba DC, might be using the DNS port."
"dns_server_is_running_description": "You cannot enable the DNS feature because a DNS server is already active on this node. Another application, such as Samba DC, might be using the DNS port.",
"not_in_network": "Address provided is not in the network of the selected interface"
},
"dns_records": {
"title": "DNS records",
Expand Down
27 changes: 27 additions & 0 deletions ui/src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@
ref="dhcpLeaseField"
>
</cv-text-input>
<cv-text-input
:label="$t('settings.DHCP_gateway_label')"
:helper-text="$t('settings.DHCP_gateway_hint')"
v-model="dhcpGatewayField"
:disabled="
loading.getConfiguration || loading.configureModule
"
:invalid-message="error.dhcpGatewayField"
ref="dhcpGatewayField"
/>
</div>
<NsButton
kind="secondary"
Expand Down Expand Up @@ -269,6 +279,7 @@ export default {
dhcpStartField: "",
dhcpEndField: "",
dhcpLeaseField: 12,
dhcpGatewayField: "",
is_dns_bound: false,
is_dns_enabled: false,
dnsEnableField: false,
Expand All @@ -288,6 +299,7 @@ export default {
dhcpStartField: "",
dhcpEndField: "",
dhcpLeaseField: "",
dhcpGatewayField: "",
dnsEnableField: "",
dnsPrimaryField: "",
dnsSecondaryField: "",
Expand Down Expand Up @@ -424,6 +436,7 @@ export default {
this.dhcpStartField = dhcp_server["start"];
this.dhcpEndField = dhcp_server["end"];
this.dhcpLeaseField = dhcp_server["lease"].toString();
this.dhcpGatewayField = dhcp_server["gateway"];
this.dnsEnableField = dns_server["enabled"];
this.dnsPrimaryField = dns_server["primary-server"];
this.dnsSecondaryField = dns_server["secondary-server"];
Expand Down Expand Up @@ -467,6 +480,14 @@ export default {
isValidationOk = false;
}
}
if (!this.dhcpGatewayField) {
this.error.dhcpGatewayField = this.$t("common.required");

if (isValidationOk) {
this.focusElement("dhcpGatewayField");
isValidationOk = false;
}
}
}
if (this.dnsEnableField && !this.dnsPrimaryField) {
this.error.dnsPrimaryField = this.$t("common.required");
Expand Down Expand Up @@ -498,6 +519,11 @@ export default {
"settings." + validationError.error
);
}
if (validationError.field === "dhcp-server.gateway") {
this.error.dhcpGatewayField = this.$t(
"settings." + validationError.error
);
}
if (validationError.field === "dns-server.primary-server") {
this.error.dnsPrimaryField = this.$t(
"settings." + validationError.error
Expand Down Expand Up @@ -548,6 +574,7 @@ export default {
start: this.dhcpStartField,
end: this.dhcpEndField,
lease: parseInt(this.dhcpLeaseField),
gateway: this.dhcpGatewayField,
},
"dns-server": {
enabled: this.dnsEnableField,
Expand Down

0 comments on commit 554f186

Please sign in to comment.