Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bf78b16b1 | ||
|
|
0ff74cc8c9 | ||
|
|
a7b29e0655 | ||
|
|
c377801be0 |
No files matched your search
@@ -55,7 +55,7 @@ address is picked up at the next run of `vpn-router-setup`.
|
||||
|
||||
## ADR-3: /config is authenticated with a short-lived JWT signed with a shared secret
|
||||
|
||||
Status: Accepted; amended by ADR-7
|
||||
Status: Accepted; amended by ADR-7 and ADR-8
|
||||
|
||||
Context: A client such as manage-ui needs the configuration and the resolved state of the
|
||||
router, which `/` and `/status` must not show. The service has only the standard library, listens
|
||||
@@ -174,3 +174,61 @@ setup is in, and follows a changed address, port or `web.enabled` within 30 seco
|
||||
restart. `web.json` no longer holds the address, the port or the secret, so the secret is only in
|
||||
`vpn-router.json`. A change of the secret applies to the next request. The interfaces are read
|
||||
every 30 seconds, which costs a few `ip` commands.
|
||||
|
||||
## ADR-8: /config leaves secret options out
|
||||
|
||||
Status: Accepted
|
||||
|
||||
Context: `/config` showed the secret options of the configuration with their values replaced by
|
||||
`********`. That still tells a reader which secrets are set, and a secret has no use outside the
|
||||
router, so a client never needs it.
|
||||
|
||||
Decision: The configuration that `vpn-router-setup` writes for the web service has no secret
|
||||
options at all. They are recognised by name as before: `psk`, and options with `secret`,
|
||||
`password`, `token` or a `_key` ending in the name.
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
`router_cert.pem`.
|
||||
|
||||
If road-warrior or OpenVPN access is enabled and this directory is empty, a certificate
|
||||
authority, a server certificate and a CRL valid for ten years are created there
|
||||
automatically; a CA without a CRL gets one. User certificates are issued by
|
||||
`vpn-router-user` into `users/`. To use your own instead, place the
|
||||
files in that layout before enabling the feature; nothing is generated or overwritten when
|
||||
material is already present.
|
||||
Once a platform is chosen and while this directory is empty, a certificate authority and a CRL
|
||||
valid for ten years are created there automatically, whether or not road-warrior or OpenVPN
|
||||
access is enabled. A CA of the router's own (with `ca_key.pem`) without a CRL gets one, and
|
||||
without a server certificate gets one once `wan.local_fqdn` is set. User certificates are issued
|
||||
by `vpn-router-user` into `users/`. To use your own CA instead, place the files in that layout
|
||||
before choosing the platform; nothing present is overwritten.
|
||||
|
||||
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
|
||||
@@ -314,8 +314,9 @@ certificate names:
|
||||
|
||||
### The user directory
|
||||
|
||||
While OpenVPN or road-warrior access is configured, `vpn-router-setup` keeps a directory of
|
||||
users in the local slapd:
|
||||
Once a platform is chosen, `vpn-router-setup` keeps a directory of 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
|
||||
slapd package creates; root manages it through the local socket, with no administrator
|
||||
@@ -328,7 +329,7 @@ users in the local slapd:
|
||||
(`pwdAccountLockedTime`);
|
||||
- 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
|
||||
|
||||
@@ -413,12 +414,12 @@ authentication, and shows no configuration: no key, identity, address or subnet.
|
||||
| --- | --- |
|
||||
| `/` | The status for people: plain text for `curl` and `wget`, a page styled with Tailwind CSS for a browser, in the browser's light or dark scheme, that reloads itself every minute (the style sheet is loaded from a CDN, so the browser needs internet access; the text is readable without it) |
|
||||
| `/status` | The same status as JSON |
|
||||
| `/config` | The configuration with its secrets masked, 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 |
|
||||
|
||||
The status holds the state of `vpn-router-setup`, of `strongswan` when site-to-site or road-warrior
|
||||
access is configured, of `wg-quick@wg0` when WireGuard is, of `openvpn-server@vpn-router` when
|
||||
OpenVPN is, and of `slapd` when either OpenVPN or road-warrior access is; the number of connected
|
||||
The status holds the state of `vpn-router-setup` and `slapd`, of `strongswan` when site-to-site or
|
||||
road-warrior access is configured, of `wg-quick@wg0` when WireGuard is, and of
|
||||
`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
|
||||
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
|
||||
@@ -437,7 +438,7 @@ curl http://10.0.0.4:8080/status
|
||||
|
||||
| Key | Content |
|
||||
| --- | --- |
|
||||
| `configuration` | `vpn-router.json` with the defaults applied and the secrets masked (`psk`, `jwt_secret`, and any option named like a password, token or key) |
|
||||
| `configuration` | `vpn-router.json` with the defaults applied, without its secret options (`psk`, `jwt_secret`, and any option named like a password, token or key) |
|
||||
| `network` | The external and internal interface, their addresses, the internal gateway, the local and remote IKE identities and subnets, and the IPv4 routes as `ip -j -4 route` prints them |
|
||||
| `ipsec` | `connections` and `sas`: one object per connection and per IKE SA, read from `swanctl --list-conns --pretty` and `swanctl --list-sas --pretty`, with the connection's name under `name` and swanctl's keys and string values |
|
||||
| `wireguard` | The public key and port of `wg0`, and each peer's public key, endpoint, allowed IPs, handshake age and traffic; no private or preshared key |
|
||||
@@ -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 |
|
||||
|
||||
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
|
||||
access is configured, `/users` answers 404. Passwords and private keys cross the internal
|
||||
exist, 409 for a user that exists and 500 otherwise. `/users` works whether or not OpenVPN or
|
||||
road-warrior access is enabled; the profile needs OpenVPN. Passwords and private keys cross the internal
|
||||
network in plain HTTP, like the token.
|
||||
|
||||
```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
|
||||
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
|
||||
|
||||
`apt-get remove` stops `vpn-router-docker`, which removes the Docker rules. It terminates the
|
||||
|
||||
Whitespace-only changes.
@@ -1,3 +1,47 @@
|
||||
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
|
||||
|
||||
fix: /config no longer shows secret options.
|
||||
|
||||
* The configuration shown by /config leaves out the secret options (psk,
|
||||
jwt_secret, and options named like a password, token or key) instead of
|
||||
showing them masked.
|
||||
|
||||
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Sat, 24 Oct 2026 11:00:00 +0200
|
||||
|
||||
vpn-router (0.5.1-1) unstable; urgency=medium
|
||||
|
||||
fix: the web service works in every state of the router.
|
||||
|
||||
@@ -11,10 +11,10 @@ strip_ufw_blocks() {
|
||||
|
||||
tmp="$(mktemp)"
|
||||
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
|
||||
}
|
||||
/^# (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 { next }
|
||||
|
||||
@@ -31,9 +31,14 @@ UFW_DEFAULTS = pathlib.Path('/etc/default/ufw')
|
||||
WG_DIR = pathlib.Path('/etc/wireguard')
|
||||
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'
|
||||
|
||||
#: Configuration options whose values are masked wherever the configuration is shown.
|
||||
#: Configuration options left out wherever the configuration is shown.
|
||||
SECRET_OPTION = re.compile(r'(^psk$)|secret|password|token|_key$')
|
||||
|
||||
BLOCKS = (
|
||||
@@ -42,6 +47,7 @@ BLOCKS = (
|
||||
('# OPENVPN RULES START', '# OPENVPN RULES END'),
|
||||
('# P2S DNS RULES START', '# P2S DNS RULES END'),
|
||||
('# WEB RULES START', '# WEB RULES END'),
|
||||
('# MANAGER RULES START', '# MANAGER RULES END'),
|
||||
('# ROUTER FORWARD RULES START', '# ROUTER FORWARD RULES END'),
|
||||
('# ROUTER NAT RULES START', '# ROUTER NAT RULES END'),
|
||||
('# ROUTER MSS RULES START', '# ROUTER MSS RULES END'),
|
||||
@@ -129,6 +135,15 @@ def is_configured(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):
|
||||
lines = [
|
||||
'',
|
||||
@@ -185,6 +200,15 @@ def filter_block(ctx):
|
||||
'# 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):
|
||||
lines += ['', '# ROUTER FORWARD RULES START']
|
||||
for subnet in ctx['local_cidr_list']:
|
||||
@@ -323,13 +347,8 @@ def manage_wireguard(ctx, changed):
|
||||
run(['systemctl', 'restart', unit], check=False)
|
||||
|
||||
|
||||
def manage_directory(ctx):
|
||||
"""Provision the user directory in slapd while a P2S system that has users 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
|
||||
def manage_directory():
|
||||
"""Provision the user directory in slapd, whether or not a P2S system is configured."""
|
||||
if vpnrouter_ldap.ensure_services() or not unit_is_active('slapd'):
|
||||
run(['systemctl', 'restart', 'slapd'])
|
||||
try:
|
||||
@@ -381,14 +400,13 @@ def refresh_web(cfg, ctx):
|
||||
"""Hand the web service the features and facts it shows.
|
||||
|
||||
The service reads the file at each request: the features of the router,
|
||||
the configuration with its secrets masked and the facts resolved from the
|
||||
system, which it cannot work out with the standard library alone. The file
|
||||
is readable by root only. The service follows a changed address or port
|
||||
itself.
|
||||
the configuration without its secret options and the facts resolved from
|
||||
the system, which it cannot work out with the standard library alone. The
|
||||
file is readable by root only. The service follows a changed address or
|
||||
port itself.
|
||||
"""
|
||||
masked = {
|
||||
section: {option: '********' if SECRET_OPTION.search(option) and value else value
|
||||
for option, value in options.items()}
|
||||
public = {
|
||||
section: {option: value for option, value in options.items() if not SECRET_OPTION.search(option)}
|
||||
for section, options in cfg.items()
|
||||
}
|
||||
WEB_STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
@@ -399,8 +417,7 @@ def refresh_web(cfg, ctx):
|
||||
'p2s': bool(vpnrouter.p2s_ready(ctx)),
|
||||
'wireguard': bool(vpnrouter.wg_ready(ctx)),
|
||||
'openvpn': bool(vpnrouter.openvpn_ready(ctx)),
|
||||
'users': bool(vpnrouter.p2s_ready(ctx) or vpnrouter.openvpn_ready(ctx)),
|
||||
'configuration': masked,
|
||||
'configuration': public,
|
||||
'network': {
|
||||
'external_interface': ctx['wan_iface'],
|
||||
'internal_interface': ctx['int_iface'],
|
||||
@@ -446,7 +463,7 @@ def main():
|
||||
elif rules_changed:
|
||||
run(['ufw', 'reload'], check=False)
|
||||
|
||||
manage_directory(ctx)
|
||||
manage_directory()
|
||||
manage_strongswan(ctx, changed)
|
||||
manage_wireguard(ctx, changed)
|
||||
manage_openvpn(ctx, changed)
|
||||
|
||||
@@ -565,24 +565,28 @@ def _simple_ca(*args):
|
||||
|
||||
|
||||
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)
|
||||
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():
|
||||
fail(f'{SIMPLE_CA} is missing')
|
||||
|
||||
_simple_ca('make-ca', '--ca-dir', str(PKI_DIR), ctx['p2s_ca_name'])
|
||||
_simple_ca('make-cert', '--ca-dir', str(PKI_DIR),
|
||||
ctx['local_fqdn'], ctx['local_fqdn'])
|
||||
_simple_ca('make-crl', '--ca-dir', str(PKI_DIR), '--days', str(CRL_DAYS))
|
||||
if not any(PKI_DIR.iterdir()):
|
||||
if not ctx['p2s_ca_name']:
|
||||
warn('p2s.ca_name is empty, not creating a local CA')
|
||||
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):
|
||||
@@ -628,8 +632,7 @@ def configure(cfg=None, local_id_override=None):
|
||||
|
||||
changed = set()
|
||||
|
||||
if p2s_ready(ctx) or openvpn_ready(ctx):
|
||||
provision_pki(ctx)
|
||||
provision_pki(ctx)
|
||||
if distribute_pki(ctx):
|
||||
changed.add('swanctl')
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Pages:
|
||||
/status the status as JSON
|
||||
/config the configuration without its secrets, and what the router resolved and
|
||||
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())
|
||||
|
||||
/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):
|
||||
"""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'):
|
||||
units['strongswan'] = unit_state('strongswan.service')
|
||||
if state.get('wireguard'):
|
||||
units['wg-quick@wg0'] = unit_state('wg-quick@wg0.service')
|
||||
if state.get('openvpn'):
|
||||
units['openvpn-server@vpn-router'] = unit_state('openvpn-server@vpn-router.service')
|
||||
if state.get('users'):
|
||||
units['slapd'] = unit_state('slapd.service')
|
||||
|
||||
status = {
|
||||
'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
|
||||
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)):
|
||||
return 400, {'error': 'the body must be a JSON object'}
|
||||
body = body or {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/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
|
||||
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):
|
||||
secret = password()
|
||||
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:
|
||||
connection.add_s(dn_of(name), ldap.modlist.addModlist({
|
||||
'objectClass': [b'inetOrgPerson'], 'uid': [name.encode()], 'cn': [name.encode()], 'sn': [name.encode()],
|
||||
|
||||
File renamed without changes.
Reference in new issue
Block a user