Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bf78b16b1 | ||
|
|
0ff74cc8c9 | ||
|
|
a7b29e0655 |
No files matched your search
@@ -189,3 +189,46 @@ 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.
|
||||||
|
|
||||||
|
## ADR-10: Users and their certificates do not depend on a P2S system being enabled
|
||||||
|
|
||||||
|
Status: Accepted
|
||||||
|
|
||||||
|
Context: The user directory and the CA were provisioned, and `/users` was served, only while
|
||||||
|
OpenVPN or road-warrior access was enabled. Users could not be prepared before a P2S system was
|
||||||
|
switched on, and the Router Manager could not manage them on a router without one.
|
||||||
|
|
||||||
|
Decision: Once a platform is chosen, `vpn-router-setup` always provisions the user directory in
|
||||||
|
slapd and the CA with its CRL; the server certificate follows once `wan.local_fqdn` is set.
|
||||||
|
`/users` and `vpn-router-user` rely only on the directory and the CA. Only the OpenVPN client
|
||||||
|
profile needs OpenVPN to be enabled.
|
||||||
|
|
||||||
|
Consequences: slapd always holds the directory and the CA always exists on a configured router,
|
||||||
|
even when no P2S system uses them. An operator who wants a CA of their own must place it before
|
||||||
|
choosing the platform.
|
||||||
@@ -272,12 +272,12 @@ ca_cert.pem ca_key.pem ca_bundle.pem <label>_cert.pem <label>_key.pem crl.p
|
|||||||
`<label>` is the first component of the router FQDN, so `router.example.com` gives
|
`<label>` is the first component of the router FQDN, so `router.example.com` gives
|
||||||
`router_cert.pem`.
|
`router_cert.pem`.
|
||||||
|
|
||||||
If road-warrior or OpenVPN access is enabled and this directory is empty, a certificate
|
Once a platform is chosen and while this directory is empty, a certificate authority and a CRL
|
||||||
authority, a server certificate and a CRL valid for ten years are created there
|
valid for ten years are created there automatically, whether or not road-warrior or OpenVPN
|
||||||
automatically; a CA without a CRL gets one. User certificates are issued by
|
access is enabled. A CA of the router's own (with `ca_key.pem`) without a CRL gets one, and
|
||||||
`vpn-router-user` into `users/`. To use your own instead, place the
|
without a server certificate gets one once `wan.local_fqdn` is set. User certificates are issued
|
||||||
files in that layout before enabling the feature; nothing is generated or overwritten when
|
by `vpn-router-user` into `users/`. To use your own CA instead, place the files in that layout
|
||||||
material is already present.
|
before choosing the platform; nothing present is overwritten.
|
||||||
|
|
||||||
Either way, the package copies what each consumer needs into place: the CA into
|
Either way, the package copies what each consumer needs into place: the CA into
|
||||||
`/etc/swanctl/x509ca/`, the server certificate and key into `/etc/swanctl/x509/` and
|
`/etc/swanctl/x509ca/`, the server certificate and key into `/etc/swanctl/x509/` and
|
||||||
@@ -314,8 +314,9 @@ certificate names:
|
|||||||
|
|
||||||
### The user directory
|
### The user directory
|
||||||
|
|
||||||
While OpenVPN or road-warrior access is configured, `vpn-router-setup` keeps a directory of
|
Once a platform is chosen, `vpn-router-setup` keeps a directory of users in the local slapd,
|
||||||
users in the local slapd:
|
whether or not OpenVPN or road-warrior access is enabled, so users can be managed before either
|
||||||
|
is switched on:
|
||||||
|
|
||||||
- its own database, `dc=vpn-router,dc=local` in `/var/lib/ldap/vpn-router`, next to the one the
|
- its own database, `dc=vpn-router,dc=local` in `/var/lib/ldap/vpn-router`, next to the one the
|
||||||
slapd package creates; root manages it through the local socket, with no administrator
|
slapd package creates; root manages it through the local socket, with no administrator
|
||||||
@@ -328,7 +329,7 @@ users in the local slapd:
|
|||||||
(`pwdAccountLockedTime`);
|
(`pwdAccountLockedTime`);
|
||||||
- slapd listens on its local socket only (`SLAPD_SERVICES` in `/etc/default/slapd`).
|
- slapd listens on its local socket only (`SLAPD_SERVICES` in `/etc/default/slapd`).
|
||||||
|
|
||||||
The directory is kept when OpenVPN and road-warrior access are switched off, and on purge.
|
The directory is kept on purge.
|
||||||
|
|
||||||
### Managing users
|
### Managing users
|
||||||
|
|
||||||
@@ -416,9 +417,9 @@ authentication, and shows no configuration: no key, identity, address or subnet.
|
|||||||
| `/config` | The configuration without its secret options, and what the router resolved and runs, as JSON; needs a token |
|
| `/config` | The configuration without its secret options, and what the router resolved and runs, as JSON; needs a token |
|
||||||
| `/users` | The users of OpenVPN and road-warrior access; needs a token |
|
| `/users` | The users of OpenVPN and road-warrior access; needs a token |
|
||||||
|
|
||||||
The status holds the state of `vpn-router-setup`, of `strongswan` when site-to-site or road-warrior
|
The status holds the state of `vpn-router-setup` and `slapd`, of `strongswan` when site-to-site or
|
||||||
access is configured, of `wg-quick@wg0` when WireGuard is, of `openvpn-server@vpn-router` when
|
road-warrior access is configured, of `wg-quick@wg0` when WireGuard is, and of
|
||||||
OpenVPN is, and of `slapd` when either OpenVPN or road-warrior access is; the number of connected
|
`openvpn-server@vpn-router` when OpenVPN is; the number of connected
|
||||||
OpenVPN clients, from the server's status file, which OpenVPN rewrites every minute; the package version; whether
|
OpenVPN clients, from the server's status file, which OpenVPN rewrites every minute; the package version; whether
|
||||||
Docker is installed, and when it is, the state of `docker.service` and of `vpn-router-docker`
|
Docker is installed, and when it is, the state of `docker.service` and of `vpn-router-docker`
|
||||||
(the Docker rules), which do not change `status`; whether the
|
(the Docker rules), which do not change `status`; whether the
|
||||||
@@ -484,8 +485,8 @@ and answer bodies are JSON; a request body is at most 4096 bytes.
|
|||||||
| `GET /users/<name>/profile` | | `{"name", "profile"}`, the OpenVPN client profile |
|
| `GET /users/<name>/profile` | | `{"name", "profile"}`, the OpenVPN client profile |
|
||||||
|
|
||||||
A failure is `{"error": ...}` with 400 for an invalid request, 404 for a user that does not
|
A failure is `{"error": ...}` with 400 for an invalid request, 404 for a user that does not
|
||||||
exist, 409 for a user that exists and 500 otherwise. While neither OpenVPN nor road-warrior
|
exist, 409 for a user that exists and 500 otherwise. `/users` works whether or not OpenVPN or
|
||||||
access is configured, `/users` answers 404. Passwords and private keys cross the internal
|
road-warrior access is enabled; the profile needs OpenVPN. Passwords and private keys cross the internal
|
||||||
network in plain HTTP, like the token.
|
network in plain HTTP, like the token.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -502,6 +503,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
|
||||||
|
|||||||
Whitespace-only changes.
@@ -1,3 +1,37 @@
|
|||||||
|
vpn-router (0.5.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
feature: users and their certificates no longer need a P2S system enabled.
|
||||||
|
|
||||||
|
* vpn-router-setup provisions the user directory, the CA and its CRL once
|
||||||
|
a platform is chosen, whether or not OpenVPN or road-warrior access is
|
||||||
|
enabled. The server certificate is issued once wan.local_fqdn is set,
|
||||||
|
also for an existing CA that has none.
|
||||||
|
* /users and vpn-router-user manage users and issue and revoke their
|
||||||
|
certificates without a P2S system; only the OpenVPN client profile
|
||||||
|
needs OpenVPN.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Sat, 24 Oct 2026 16:00:00 +0200
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
fix: the status always shows the state of slapd.
|
||||||
|
|
||||||
|
* /status and the status page show the state of slapd whether or not
|
||||||
|
OpenVPN or road-warrior access is configured, since the package always
|
||||||
|
runs it.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Sat, 24 Oct 2026 12:00:00 +0200
|
||||||
|
|
||||||
vpn-router (0.5.2-1) unstable; urgency=medium
|
vpn-router (0.5.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
fix: /config no longer shows secret options.
|
fix: /config no longer shows secret options.
|
||||||
|
|||||||
@@ -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']:
|
||||||
@@ -323,13 +347,8 @@ def manage_wireguard(ctx, changed):
|
|||||||
run(['systemctl', 'restart', unit], check=False)
|
run(['systemctl', 'restart', unit], check=False)
|
||||||
|
|
||||||
|
|
||||||
def manage_directory(ctx):
|
def manage_directory():
|
||||||
"""Provision the user directory in slapd while a P2S system that has users is configured.
|
"""Provision the user directory in slapd, whether or not a P2S system is configured."""
|
||||||
|
|
||||||
The directory is left in place when they are switched off, so the users are kept.
|
|
||||||
"""
|
|
||||||
if not (vpnrouter.p2s_ready(ctx) or vpnrouter.openvpn_ready(ctx)):
|
|
||||||
return
|
|
||||||
if vpnrouter_ldap.ensure_services() or not unit_is_active('slapd'):
|
if vpnrouter_ldap.ensure_services() or not unit_is_active('slapd'):
|
||||||
run(['systemctl', 'restart', 'slapd'])
|
run(['systemctl', 'restart', 'slapd'])
|
||||||
try:
|
try:
|
||||||
@@ -398,7 +417,6 @@ def refresh_web(cfg, ctx):
|
|||||||
'p2s': bool(vpnrouter.p2s_ready(ctx)),
|
'p2s': bool(vpnrouter.p2s_ready(ctx)),
|
||||||
'wireguard': bool(vpnrouter.wg_ready(ctx)),
|
'wireguard': bool(vpnrouter.wg_ready(ctx)),
|
||||||
'openvpn': bool(vpnrouter.openvpn_ready(ctx)),
|
'openvpn': bool(vpnrouter.openvpn_ready(ctx)),
|
||||||
'users': bool(vpnrouter.p2s_ready(ctx) or vpnrouter.openvpn_ready(ctx)),
|
|
||||||
'configuration': public,
|
'configuration': public,
|
||||||
'network': {
|
'network': {
|
||||||
'external_interface': ctx['wan_iface'],
|
'external_interface': ctx['wan_iface'],
|
||||||
@@ -445,7 +463,7 @@ def main():
|
|||||||
elif rules_changed:
|
elif rules_changed:
|
||||||
run(['ufw', 'reload'], check=False)
|
run(['ufw', 'reload'], check=False)
|
||||||
|
|
||||||
manage_directory(ctx)
|
manage_directory()
|
||||||
manage_strongswan(ctx, changed)
|
manage_strongswan(ctx, changed)
|
||||||
manage_wireguard(ctx, changed)
|
manage_wireguard(ctx, changed)
|
||||||
manage_openvpn(ctx, changed)
|
manage_openvpn(ctx, changed)
|
||||||
|
|||||||
@@ -565,24 +565,28 @@ def _simple_ca(*args):
|
|||||||
|
|
||||||
|
|
||||||
def provision_pki(ctx):
|
def provision_pki(ctx):
|
||||||
"""Create the CA, server certificate and CRL when the PKI directory is empty; a CA without a CRL gets one."""
|
"""Create the CA and CRL when the PKI directory is empty, and the server certificate once wan.local_fqdn is set.
|
||||||
|
|
||||||
|
A CA of the router's own (ca_key.pem present) without a CRL gets one, and without a
|
||||||
|
certificate for wan.local_fqdn gets one too; nothing present is overwritten.
|
||||||
|
"""
|
||||||
PKI_DIR.mkdir(parents=True, exist_ok=True)
|
PKI_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
os.chmod(PKI_DIR, 0o700)
|
os.chmod(PKI_DIR, 0o700)
|
||||||
|
|
||||||
if any(PKI_DIR.iterdir()):
|
|
||||||
if (PKI_DIR / 'ca_key.pem').is_file() and not (PKI_DIR / 'crl.pem').is_file():
|
|
||||||
_simple_ca('make-crl', '--ca-dir', str(PKI_DIR), '--days', str(CRL_DAYS))
|
|
||||||
return
|
|
||||||
if not ctx['p2s_ca_name']:
|
|
||||||
warn('p2s.ca_name is empty, not creating a local CA')
|
|
||||||
return
|
|
||||||
if not SIMPLE_CA.is_file():
|
if not SIMPLE_CA.is_file():
|
||||||
fail(f'{SIMPLE_CA} is missing')
|
fail(f'{SIMPLE_CA} is missing')
|
||||||
|
|
||||||
_simple_ca('make-ca', '--ca-dir', str(PKI_DIR), ctx['p2s_ca_name'])
|
if not any(PKI_DIR.iterdir()):
|
||||||
_simple_ca('make-cert', '--ca-dir', str(PKI_DIR),
|
if not ctx['p2s_ca_name']:
|
||||||
ctx['local_fqdn'], ctx['local_fqdn'])
|
warn('p2s.ca_name is empty, not creating a local CA')
|
||||||
_simple_ca('make-crl', '--ca-dir', str(PKI_DIR), '--days', str(CRL_DAYS))
|
return
|
||||||
|
_simple_ca('make-ca', '--ca-dir', str(PKI_DIR), ctx['p2s_ca_name'])
|
||||||
|
if not (PKI_DIR / 'ca_key.pem').is_file():
|
||||||
|
return
|
||||||
|
if not (PKI_DIR / 'crl.pem').is_file():
|
||||||
|
_simple_ca('make-crl', '--ca-dir', str(PKI_DIR), '--days', str(CRL_DAYS))
|
||||||
|
label = ctx['local_fqdn'].split('.', 1)[0] if ctx['local_fqdn'] else ''
|
||||||
|
if label and not (PKI_DIR / f'{label}_cert.pem').is_file():
|
||||||
|
_simple_ca('make-cert', '--ca-dir', str(PKI_DIR), ctx['local_fqdn'], ctx['local_fqdn'])
|
||||||
|
|
||||||
|
|
||||||
def distribute_pki(ctx):
|
def distribute_pki(ctx):
|
||||||
@@ -628,8 +632,7 @@ def configure(cfg=None, local_id_override=None):
|
|||||||
|
|
||||||
changed = set()
|
changed = set()
|
||||||
|
|
||||||
if p2s_ready(ctx) or openvpn_ready(ctx):
|
provision_pki(ctx)
|
||||||
provision_pki(ctx)
|
|
||||||
if distribute_pki(ctx):
|
if distribute_pki(ctx):
|
||||||
changed.add('swanctl')
|
changed.add('swanctl')
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Pages:
|
|||||||
/status the status as JSON
|
/status the status as JSON
|
||||||
/config the configuration without its secrets, and what the router resolved and
|
/config the configuration without its secrets, and what the router resolved and
|
||||||
runs, as JSON
|
runs, as JSON
|
||||||
/users the users of the P2S systems: list, add, set the password, disable, enable,
|
/users the users in the router's directory, whether or not a P2S system is enabled: list, add, set the password, disable, enable,
|
||||||
remove, and the OpenVPN client profile (see users())
|
remove, and the OpenVPN client profile (see users())
|
||||||
|
|
||||||
/config and /users need an HS256 JWT in an Authorization: Bearer header that is signed
|
/config and /users need an HS256 JWT in an Authorization: Bearer header that is signed
|
||||||
@@ -224,15 +224,13 @@ def openvpn_clients():
|
|||||||
|
|
||||||
def collect(state):
|
def collect(state):
|
||||||
"""The status document. state says which features the router has."""
|
"""The status document. state says which features the router has."""
|
||||||
units = {'vpn-router-setup': unit_state('vpn-router-setup.service')}
|
units = {'vpn-router-setup': unit_state('vpn-router-setup.service'), 'slapd': unit_state('slapd.service')}
|
||||||
if state.get('site_to_site') or state.get('p2s'):
|
if state.get('site_to_site') or state.get('p2s'):
|
||||||
units['strongswan'] = unit_state('strongswan.service')
|
units['strongswan'] = unit_state('strongswan.service')
|
||||||
if state.get('wireguard'):
|
if state.get('wireguard'):
|
||||||
units['wg-quick@wg0'] = unit_state('wg-quick@wg0.service')
|
units['wg-quick@wg0'] = unit_state('wg-quick@wg0.service')
|
||||||
if state.get('openvpn'):
|
if state.get('openvpn'):
|
||||||
units['openvpn-server@vpn-router'] = unit_state('openvpn-server@vpn-router.service')
|
units['openvpn-server@vpn-router'] = unit_state('openvpn-server@vpn-router.service')
|
||||||
if state.get('users'):
|
|
||||||
units['slapd'] = unit_state('slapd.service')
|
|
||||||
|
|
||||||
status = {
|
status = {
|
||||||
'status': 'ok' if all(value == 'active' for value in units.values()) else 'degraded',
|
'status': 'ok' if all(value == 'active' for value in units.values()) else 'degraded',
|
||||||
@@ -435,8 +433,6 @@ def users(state, method, path, body):
|
|||||||
DELETE /users/<name> remove the user and revoke the certificate
|
DELETE /users/<name> remove the user and revoke the certificate
|
||||||
GET /users/<name>/profile the OpenVPN client profile
|
GET /users/<name>/profile the OpenVPN client profile
|
||||||
"""
|
"""
|
||||||
if not state.get('users'):
|
|
||||||
return 404, {'error': 'no P2S system with users is configured'}
|
|
||||||
if body == 'invalid' or (body is not None and not isinstance(body, dict)):
|
if body == 'invalid' or (body is not None and not isinstance(body, dict)):
|
||||||
return 400, {'error': 'the body must be a JSON object'}
|
return 400, {'error': 'the body must be a JSON object'}
|
||||||
body = body or {}
|
body = body or {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
"""Manage the users of the router's P2S systems.
|
"""Manage the users of the router's directory, whether or not a P2S system is enabled.
|
||||||
|
|
||||||
A user is an entry in the router's directory, whose password OpenVPN checks, and a client
|
A user is an entry in the router's directory, whose password OpenVPN checks, and a client
|
||||||
certificate of the router CA with the user name as its common name, which OpenVPN and
|
certificate of the router CA with the user name as its common name, which OpenVPN and
|
||||||
@@ -103,7 +103,7 @@ def list_users(connection):
|
|||||||
def add(connection, name):
|
def add(connection, name):
|
||||||
secret = password()
|
secret = password()
|
||||||
if not (vpnrouter.PKI_DIR / 'ca_key.pem').is_file():
|
if not (vpnrouter.PKI_DIR / 'ca_key.pem').is_file():
|
||||||
raise Failure('the router CA does not exist; enable OpenVPN or IKEv2 road-warrior access first')
|
raise Failure('the router CA does not exist; vpn-router-setup creates it once a platform is chosen')
|
||||||
try:
|
try:
|
||||||
connection.add_s(dn_of(name), ldap.modlist.addModlist({
|
connection.add_s(dn_of(name), ldap.modlist.addModlist({
|
||||||
'objectClass': [b'inetOrgPerson'], 'uid': [name.encode()], 'cn': [name.encode()], 'sn': [name.encode()],
|
'objectClass': [b'inetOrgPerson'], 'uid': [name.encode()], 'cn': [name.encode()], 'sn': [name.encode()],
|
||||||
|
|||||||
File renamed without changes.
Reference in new issue
Block a user