feature: the firewall lets the Router Manager in on the internal interface.
Release / release (push) Successful in 11s

This commit is contained in:
slawek committed 2026-10-23 23:24:59 +02:00
1 parent a7b29e0655
commit 0ff74cc8c9
7 files changed
+70 -2

No files matched your search

+26
View File
@@ -189,3 +189,29 @@ options at all. They are recognised by name as before: `psk`, and options with `
Consequences: No secret reaches the web service's state file or any answer of the API. A client Consequences: No secret reaches the web service's state file or any answer of the API. A client
cannot tell whether a secret is set. A new secret option still needs a name that matches. cannot tell whether a secret is set. A new secret option still needs a name that matches.
## ADR-9: The Router Manager is a separate package with a small Python server
Status: Accepted
Context: The router needs a web application to manage it, which may run on the router or on
another Linux system. The router's web service needs a token signed with `web.jwt_secret` for
`/config` and `/users` and sends no CORS headers, so a browser cannot call it with the secret
kept away from the page.
Decision: `vpn-router-manager` is a Debian package of its own, built in the repository `linux-cloud-router-manager`. A React and
MUI application in the style of manage-ui is built with Vite when the package is built and
shipped as static files. A Python server, using the Ubuntu packages `python3-jwt` and `python3-requests`, serves them and
passes every `/api/<path>` call to the router's `/<path>`, signing a short-lived token with the
router's secret; it holds no logic of the router's own. It reads its settings from
`/etc/vpn-router-manager/manager.json` at each request; on the router the settings not given
come from `vpn-router.json`, and it listens on the internal address like the status web
service, with the same discovery thread. `vpn-router-setup` opens the manager's port on the
internal interface while the manager is installed, so the firewall stays in one place. The
manager asks for no authentication yet.
Consequences: The router needs no Node.js; the package needs only `python3` and packages of the Ubuntu archive. The secret never
reaches the browser. The application checks the shape of every answer of the router. Anyone who
reaches the manager's port manages the router, so on another system it listens on `127.0.0.1`
unless told otherwise. The router's firewall depends on whether the manager is installed, and a
changed manager port needs `vpn-router-setup` to run again.
+8
View File
@@ -502,6 +502,14 @@ request reads the token secret from `vpn-router.json`, and the features and the
before that file exists the page shows no features. `vpn-router-setup` opens `web.port` on the before that file exists the page shows no features. `vpn-router-setup` opens `web.port` on the
internal interface in the firewall. internal interface in the firewall.
## Router Manager
The `vpn-router-manager` package, built in the repository `linux-cloud-router-manager`, is a web
application that shows the state of the router and manages its users through the status web
service. It can be installed on the router or on another Linux system. While it is installed on
the router, `vpn-router-setup` opens its port (`port` in `/etc/vpn-router-manager/manager.json`,
8090 by default) on the internal interface; restart `vpn-router-setup` after changing it.
## Removal ## Removal
`apt-get remove` stops `vpn-router-docker`, which removes the Docker rules. It terminates the `apt-get remove` stops `vpn-router-docker`, which removes the Docker rules. It terminates the
View File
Whitespace-only changes.
+10
View File
@@ -1,3 +1,13 @@
vpn-router (0.5.4-1) unstable; urgency=medium
feature: the firewall lets the Router Manager in on the internal interface.
* While vpn-router-manager is installed, vpn-router-setup opens its port,
port in /etc/vpn-router-manager/manager.json or 8090, on the internal
interface. Purging the package removes the rule.
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Sat, 24 Oct 2026 13:00:00 +0200
vpn-router (0.5.3-1) unstable; urgency=medium vpn-router (0.5.3-1) unstable; urgency=medium
fix: the status always shows the state of slapd. fix: the status always shows the state of slapd.
+2 -2
View File
@@ -11,10 +11,10 @@ strip_ufw_blocks() {
tmp="$(mktemp)" tmp="$(mktemp)"
awk ' awk '
/^# (IPSEC|WIREGUARD|OPENVPN|P2S DNS|WEB|ROUTER FORWARD|ROUTER NAT|ROUTER MSS) RULES START$/ { /^# (IPSEC|WIREGUARD|OPENVPN|P2S DNS|WEB|MANAGER|ROUTER FORWARD|ROUTER NAT|ROUTER MSS) RULES START$/ {
skip = 1; pending = 0; next skip = 1; pending = 0; next
} }
/^# (IPSEC|WIREGUARD|OPENVPN|P2S DNS|WEB|ROUTER FORWARD|ROUTER NAT|ROUTER MSS) RULES END$/ { /^# (IPSEC|WIREGUARD|OPENVPN|P2S DNS|WEB|MANAGER|ROUTER FORWARD|ROUTER NAT|ROUTER MSS) RULES END$/ {
skip = 0; next skip = 0; next
} }
skip { next } skip { next }
@@ -31,6 +31,11 @@ UFW_DEFAULTS = pathlib.Path('/etc/default/ufw')
WG_DIR = pathlib.Path('/etc/wireguard') WG_DIR = pathlib.Path('/etc/wireguard')
WEB_STATE = pathlib.Path('/run/vpn-router/web.json') WEB_STATE = pathlib.Path('/run/vpn-router/web.json')
#: The Router Manager, vpn-router-manager, when it is installed, and its settings.
MANAGER = pathlib.Path('/usr/lib/vpn-router-manager/manager')
MANAGER_CONFIG = pathlib.Path('/etc/vpn-router-manager/manager.json')
MANAGER_PORT = 8090
FILTER_ANCHOR = '# End required lines' FILTER_ANCHOR = '# End required lines'
#: Configuration options left out wherever the configuration is shown. #: Configuration options left out wherever the configuration is shown.
@@ -42,6 +47,7 @@ BLOCKS = (
('# OPENVPN RULES START', '# OPENVPN RULES END'), ('# OPENVPN RULES START', '# OPENVPN RULES END'),
('# P2S DNS RULES START', '# P2S DNS RULES END'), ('# P2S DNS RULES START', '# P2S DNS RULES END'),
('# WEB RULES START', '# WEB RULES END'), ('# WEB RULES START', '# WEB RULES END'),
('# MANAGER RULES START', '# MANAGER RULES END'),
('# ROUTER FORWARD RULES START', '# ROUTER FORWARD RULES END'), ('# ROUTER FORWARD RULES START', '# ROUTER FORWARD RULES END'),
('# ROUTER NAT RULES START', '# ROUTER NAT RULES END'), ('# ROUTER NAT RULES START', '# ROUTER NAT RULES END'),
('# ROUTER MSS RULES START', '# ROUTER MSS RULES END'), ('# ROUTER MSS RULES START', '# ROUTER MSS RULES END'),
@@ -129,6 +135,15 @@ def is_configured(ctx):
vpnrouter.openvpn_ready(ctx))) vpnrouter.openvpn_ready(ctx)))
def manager_port():
"""The port of the Router Manager: port in its settings, or MANAGER_PORT when it is not set or not a port."""
try:
port = json.loads(MANAGER_CONFIG.read_text(encoding='utf-8')).get('port')
except (OSError, ValueError, AttributeError):
return MANAGER_PORT
return port if isinstance(port, int) and not isinstance(port, bool) and 1 <= port <= 65535 else MANAGER_PORT
def filter_block(ctx): def filter_block(ctx):
lines = [ lines = [
'', '',
@@ -185,6 +200,15 @@ def filter_block(ctx):
'# WEB RULES END', '# WEB RULES END',
] ]
if MANAGER.exists() and ctx['int_iface']:
port = manager_port()
lines += [
'',
'# MANAGER RULES START',
f'-A ufw-before-input -i {ctx["int_iface"]} -p tcp --dport {port} -j ACCEPT',
'# MANAGER RULES END',
]
if vpnrouter.routing_ready(ctx): if vpnrouter.routing_ready(ctx):
lines += ['', '# ROUTER FORWARD RULES START'] lines += ['', '# ROUTER FORWARD RULES START']
for subnet in ctx['local_cidr_list']: for subnet in ctx['local_cidr_list']:
View File
File renamed without changes.