Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding configurable gateway for DHCP server #23

Merged
merged 6 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
DavidePrincipi marked this conversation as resolved.
Show resolved Hide resolved
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
Loading