feature: /remote-access of the web service shows and changes the settings of IKEv2 road-warrior, OpenVPN and WireGuard access and whether each runs, for the Router Manager's Remote access page.
Release / release (push) Successful in 14s
Release / release (push) Successful in 14s
This commit is contained in:
1 parent
07dbe91d0c
commit
9255d56276
4 files changed
+86
-3
No files matched your search
@@ -435,6 +435,9 @@ def validate(cfg):
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
raise ConfigError(f'openvpn.address_pool: {openvpn_pool!r} is not an IPv4 network')
|
||||
for system, option in (('p2s', 'address_pool'), ('openvpn', 'address_pool'), ('wireguard', 'address')):
|
||||
if _get(cfg, system, 'enabled') and not _get(cfg, system, option):
|
||||
raise ConfigError(f'{system}.enabled needs {system}.{option}')
|
||||
_check_address(_get(cfg, 'local', 'int_addr'), 'local.int_addr')
|
||||
_check_address(_get(cfg, 'local', 'int_gateway_ip'), 'local.int_gateway_ip')
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ Pages:
|
||||
applied by vpn-router-setup (see site_to_site())
|
||||
/backup the backup settings, the last run and the local archives: show, change and run
|
||||
now (see backup())
|
||||
/remote-access
|
||||
the settings of IKEv2 road-warrior, OpenVPN and WireGuard access and whether each
|
||||
runs: show and change, applied by vpn-router-setup (see remote_access())
|
||||
/ca the PKI: its settings, the root and issuing CAs, the certificates issued; change the
|
||||
settings, issue and revoke certificates, and take the certificate of an issuing CA
|
||||
and a root CRL signed offline (see ca())
|
||||
@@ -46,7 +49,7 @@ Pages:
|
||||
the databases of each PostgreSQL cluster on the router with their sizes, as JSON (see
|
||||
postgresql_databases()); /config has the clusters' settings
|
||||
|
||||
/status/full, /config, /metrics/history, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /ca, /shell and /account need an HS256 JWT in an Authorization: Bearer header signed
|
||||
/status/full, /config, /metrics/history, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /remote-access, /ca, /shell and /account need an HS256 JWT in an Authorization: Bearer header signed
|
||||
with vpnrouter_auth.WEB_KEY (see vpnrouter_auth.verify). A token of /auth names a user in sub;
|
||||
/account serves that user, and the other paths need a member of vpnrouter_users.ADMIN_GROUP. A
|
||||
token of vpn-router-token has the admin claim and names no user; it may call every path but
|
||||
@@ -150,6 +153,9 @@ BODY_LIMIT = 65536
|
||||
#: The settings of a site_to_site connection that /site-to-site adds and changes.
|
||||
CONNECTION_SETTINGS = ('addrs', 'id', 'cidrs', 'local_cidrs', 'local_id_mode', 'auth', 'psk', 'remote_ca')
|
||||
|
||||
#: The sections of the remote access systems /remote-access changes: IKEv2 road-warrior, OpenVPN and WireGuard.
|
||||
REMOTE_ACCESS = ('p2s', 'openvpn', 'wireguard')
|
||||
|
||||
#: The HTTP status for each status of a vpnrouter_users.Failure; any other is 500.
|
||||
USER_STATUS = {2: 400, 3: 404, 4: 409, 5: 401}
|
||||
|
||||
@@ -814,6 +820,48 @@ def backup(method, path, body):
|
||||
return 200, {'settings': backup_settings(cfg['backup'])}
|
||||
|
||||
|
||||
def remote_access(state, method, path, body):
|
||||
"""Answer a /remote-access request: the HTTP status and the JSON document.
|
||||
|
||||
GET /remote-access {"p2s", "openvpn", "wireguard", "running", "local_fqdn"}: the settings of the three
|
||||
remote access systems (REMOTE_ACCESS), whether setup runs each (running: {p2s,
|
||||
openvpn, wireguard}), and wan.local_fqdn, which IKEv2 and OpenVPN need with the
|
||||
router's VPN server certificate
|
||||
PUT /remote-access/<system> change any of the settings of the system's section; answers the section
|
||||
|
||||
A change is made as /site-to-site makes one, and applied by restarting vpn-router-setup without waiting for it.
|
||||
"""
|
||||
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 {}
|
||||
parts = path.strip('/').split('/')
|
||||
try:
|
||||
if (method, len(parts)) == ('GET', 1):
|
||||
cfg = vpnrouter.load_config()
|
||||
return 200, {**{system: cfg[system] for system in REMOTE_ACCESS},
|
||||
'running': {system: bool(state.get(system)) for system in REMOTE_ACCESS},
|
||||
'local_fqdn': cfg['wan']['local_fqdn']}
|
||||
if method != 'PUT' or len(parts) != 2 or parts[1] not in REMOTE_ACCESS:
|
||||
return 404, {'error': f'no route {method} {path}'}
|
||||
system = parts[1]
|
||||
settings = {option: body[option] for option in vpnrouter.SCHEMA[system] if option in body}
|
||||
with vpnrouter.config_lock():
|
||||
data = vpnrouter.read_settings()
|
||||
current = data.get(system)
|
||||
data[system] = {**(current if isinstance(current, dict) else {}), **settings}
|
||||
cfg = vpnrouter.with_defaults(data)
|
||||
vpnrouter.validate(cfg)
|
||||
vpnrouter.save_config(data)
|
||||
except vpnrouter.ConfigError as exc:
|
||||
return 400, {'error': str(exc)}
|
||||
except OSError as exc:
|
||||
return 500, {'error': f'the configuration cannot be written: {exc}'}
|
||||
except SystemExit as exc:
|
||||
return 500, {'error': str(exc.code)}
|
||||
run('systemctl', 'restart', '--no-block', 'vpn-router-setup.service')
|
||||
return 200, cfg[system]
|
||||
|
||||
|
||||
def ca(method, path, body):
|
||||
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
||||
|
||||
@@ -1319,7 +1367,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if self.command not in ('GET', 'HEAD'):
|
||||
self.close_connection = True
|
||||
directory = next((name for name in ('users', 'service-accounts', 'groups', 'account', 'site-to-site', 'backup',
|
||||
'ca', 'shell')
|
||||
'ca', 'shell', 'remote-access')
|
||||
if path == f'/{name}' or path.startswith(f'/{name}/')), None)
|
||||
protected = path in ('/status/full', '/config', '/metrics/history', '/postgresql') or directory is not None
|
||||
signing = path in ('/auth', '/auth/refresh', '/auth/logout')
|
||||
@@ -1371,6 +1419,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
elif directory == 'site-to-site':
|
||||
code, document = site_to_site(self.command, path, payload)
|
||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
||||
elif directory == 'remote-access':
|
||||
code, document = remote_access(state, self.command, path, payload)
|
||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
||||
elif directory == 'backup':
|
||||
code, document = backup(self.command, path, payload)
|
||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
||||
|
||||
Reference in new issue
Block a user