Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
015fba50ca | ||
|
|
9255d56276 | ||
|
|
07dbe91d0c |
No files matched your search
@@ -913,7 +913,7 @@ ticket gets the shell. The WebSocket of RFC 6455 is implemented on the standard
|
|||||||
web service's `http.server`, server side only, without extensions. `systemd-run --pty` starts the
|
web service's `http.server`, server side only, without extensions. `systemd-run --pty` starts the
|
||||||
shell in a transient unit with a PAM login session, outside the sandbox, and `setsid --ctty`
|
shell in a transient unit with a PAM login session, outside the sandbox, and `setsid --ctty`
|
||||||
makes the terminal its controlling terminal so window sizes reach the shell. A shell idle for
|
makes the terminal its controlling terminal so window sizes reach the shell. A shell idle for
|
||||||
`web.shell_idle_minutes` is closed, and the journal records every opening and end. The Router
|
`web.shell_idle_minutes` is closed, and the journal records every opening, end and refusal. The Router
|
||||||
Manager checks the session and the WebSocket's origin, and relays the bytes to the router after
|
Manager checks the session and the WebSocket's origin, and relays the bytes to the router after
|
||||||
both handshakes.
|
both handshakes.
|
||||||
|
|
||||||
|
|||||||
@@ -885,7 +885,7 @@ The status holds the state of `vpn-router-setup`, `slapd` and each PostgreSQL cl
|
|||||||
`vpn-router-manager` when that package is installed, of `strongswan` when site-to-site or
|
`vpn-router-manager` when that package is installed, of `strongswan` when site-to-site or
|
||||||
road-warrior access is configured, of `wg-quick@wg0` when WireGuard is, and of
|
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-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 and,
|
OpenVPN clients, from the server's status file, which OpenVPN rewrites every 5 seconds; the package version and,
|
||||||
as `manager_version`, the installed version of `vpn-router-manager` (`null`, shown as not installed, without it); whether
|
as `manager_version`, the installed version of `vpn-router-manager` (`null`, shown as not installed, without it); 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`; as `site_to_site`, the number of site-to-site
|
(the Docker rules), which do not change `status`; as `site_to_site`, the number of site-to-site
|
||||||
@@ -901,7 +901,7 @@ curl http://10.0.0.4:7081/status
|
|||||||
|
|
||||||
### Tokens and signing in
|
### Tokens and signing in
|
||||||
|
|
||||||
`/config`, `/users`, `/service-accounts`, `/groups`, `/site-to-site`, `/shell` and `/account` need a JWT in an `Authorization: Bearer` header, signed with
|
`/config`, `/users`, `/service-accounts`, `/groups`, `/site-to-site`, `/backup`, `/remote-access`, `/ca`, `/shell` and `/account` need a JWT in an `Authorization: Bearer` header, signed with
|
||||||
HS256 by the key the web service makes on its first run, `/etc/vpn-router/web.key` (root only).
|
HS256 by the key the web service makes on its first run, `/etc/vpn-router/web.key` (root only).
|
||||||
The key never leaves the router; while it does not exist these paths and `/auth` are not served
|
The key never leaves the router; while it does not exist these paths and `/auth` are not served
|
||||||
(404). A missing, expired or wrongly signed token, or one of another algorithm, gets 401 with
|
(404). A missing, expired or wrongly signed token, or one of another algorithm, gets 401 with
|
||||||
@@ -984,7 +984,7 @@ the router are the terminal's output. The shell is a PAM login session of the co
|
|||||||
run by `systemd-run` in a transient unit `vpn-router-shell-<id>`; sudo works as for SSH. It is closed after `web.shell_idle_minutes` without a keystroke, when the shell
|
run by `systemd-run` in a transient unit `vpn-router-shell-<id>`; sudo works as for SSH. It is closed after `web.shell_idle_minutes` without a keystroke, when the shell
|
||||||
exits, or when the connection closes; the router then sends a close frame with the reason. The
|
exits, or when the connection closes; the router then sends a close frame with the reason. The
|
||||||
journal of `vpn-router-web` records who opened each shell, from which address, in which unit,
|
journal of `vpn-router-web` records who opened each shell, from which address, in which unit,
|
||||||
when it ended and why.
|
when it ended and why, and each refused ticket or shell with the reason.
|
||||||
|
|
||||||
### The /config page
|
### The /config page
|
||||||
|
|
||||||
@@ -1142,6 +1142,23 @@ The answer of `GET /backup` holds:
|
|||||||
|
|
||||||
A change is made, checked and applied as for `/site-to-site`.
|
A change is made, checked and applied as for `/site-to-site`.
|
||||||
|
|
||||||
|
### The /remote-access API
|
||||||
|
|
||||||
|
`/remote-access` shows and changes the remote access systems, IKEv2 road-warrior (`p2s`),
|
||||||
|
OpenVPN (`openvpn`) and WireGuard (`wireguard`), with the same token as `/users`.
|
||||||
|
|
||||||
|
| Request | Body | Answer |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `GET /remote-access` | | `{"p2s", "openvpn", "wireguard", "running", "local_fqdn"}` |
|
||||||
|
| `PUT /remote-access/<system>` | any of the settings of the section `p2s`, `openvpn` or `wireguard` | the section |
|
||||||
|
|
||||||
|
`running` tells for each system whether setup runs it. IKEv2 and OpenVPN run once enabled with an
|
||||||
|
address pool, `wan.local_fqdn` set and the router's VPN server certificate issued by the P2S CA;
|
||||||
|
WireGuard once enabled with an address. A system enabled without its address pool or address is
|
||||||
|
refused. A change is made, checked and applied as for `/site-to-site`. The router's firewall
|
||||||
|
follows the settings; a cloud firewall in front of the router, such as an Azure network security
|
||||||
|
group, must allow the OpenVPN and WireGuard ports itself.
|
||||||
|
|
||||||
### The /ca API
|
### The /ca API
|
||||||
|
|
||||||
`/ca` manages the PKI (see Certificates) with the same token as `/users`, with the module
|
`/ca` manages the PKI (see Certificates) with the same token as `/users`, with the module
|
||||||
|
|||||||
@@ -1,3 +1,35 @@
|
|||||||
|
vpn-router (0.24.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
fix: the status gives the number of connected OpenVPN clients at most 5 seconds old instead of up to a minute.
|
||||||
|
|
||||||
|
* The OpenVPN server writes its status file every 5 seconds, so a
|
||||||
|
refresh of the status shows clients that connected or left since.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 23:30:00 +0100
|
||||||
|
|
||||||
|
vpn-router (0.24.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* GET /remote-access answers the p2s, openvpn and wireguard sections,
|
||||||
|
which of them setup runs, and wan.local_fqdn; PUT
|
||||||
|
/remote-access/<system> changes one section and restarts setup.
|
||||||
|
* p2s.enabled and openvpn.enabled need their address_pool, and
|
||||||
|
wireguard.enabled its address.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 23:00:00 +0100
|
||||||
|
|
||||||
|
vpn-router (0.23.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
fix: the shell of a user's own POSIX account opens on the first setup that sets up console login, and the journal records each refused shell with the reason.
|
||||||
|
|
||||||
|
* Setup restarts vpn-router-web when it adds ldap to the name service
|
||||||
|
switch, so the web service finds the POSIX accounts of the directory.
|
||||||
|
* The journal of vpn-router-web records each refused ticket and shell,
|
||||||
|
with the user, the address and the reason.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 22:00:00 +0100
|
||||||
|
|
||||||
vpn-router (0.23.1-1) unstable; urgency=medium
|
vpn-router (0.23.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
fix: the shell runs as the console account of each member of admins, a local account or the user's own POSIX account in the directory, which also signs in with SSH and sudo, instead of one web.shell_user.
|
fix: the shell runs as the console account of each member of admins, a local account or the user's own POSIX account in the directory, which also signs in with SSH and sudo, instead of one web.shell_user.
|
||||||
|
|||||||
@@ -555,8 +555,8 @@ def manage_console(cfg):
|
|||||||
|
|
||||||
The local group vpnrouter_ldap.CONSOLE_GROUP is created when missing, and sudo lets its members run any command
|
The local group vpnrouter_ldap.CONSOLE_GROUP is created when missing, and sudo lets its members run any command
|
||||||
with their password. nslcd serves the directory's posixAccounts with console login on through the local socket,
|
with their password. nslcd serves the directory's posixAccounts with console login on through the local socket,
|
||||||
and checks their passwords by binding as the user; the passwd line of the name service switch gains ldap, and PAM
|
and checks their passwords by binding as the user; the passwd line of the name service switch gains ldap, which
|
||||||
creates a home at the first login. While ssh.ldap_login is true, sshd accepts the password of the group's members;
|
restarts the web service, and PAM creates a home at the first login. While ssh.ldap_login is true, sshd accepts the password of the group's members;
|
||||||
a drop-in sshd -t refuses is removed again.
|
a drop-in sshd -t refuses is removed again.
|
||||||
"""
|
"""
|
||||||
group = vpnrouter_ldap.CONSOLE_GROUP
|
group = vpnrouter_ldap.CONSOLE_GROUP
|
||||||
@@ -601,6 +601,9 @@ def manage_console(cfg):
|
|||||||
for line in lines]
|
for line in lines]
|
||||||
if switched != lines:
|
if switched != lines:
|
||||||
NSSWITCH.write_text(''.join(switched), encoding='utf-8')
|
NSSWITCH.write_text(''.join(switched), encoding='utf-8')
|
||||||
|
# A running process keeps the name service switch it read first, so the web service's shell would not find
|
||||||
|
# the console accounts of the directory until it restarts.
|
||||||
|
run(['systemctl', 'try-restart', 'vpn-router-web.service'], check=False)
|
||||||
|
|
||||||
if 'pam_mkhomedir' not in PAM_SESSION.read_text(encoding='utf-8'):
|
if 'pam_mkhomedir' not in PAM_SESSION.read_text(encoding='utf-8'):
|
||||||
run(['pam-auth-update', '--enable', 'mkhomedir'], check=False)
|
run(['pam-auth-update', '--enable', 'mkhomedir'], check=False)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ if str(LIB_DIR) not in sys.path:
|
|||||||
import vpnrouter_ldap
|
import vpnrouter_ldap
|
||||||
import vpnrouter_pki
|
import vpnrouter_pki
|
||||||
import vpnrouter_platforms
|
import vpnrouter_platforms
|
||||||
|
import vpnrouter_status
|
||||||
from vpnrouter_net import DEFAULT_LISTEN, LISTEN_CHOICES, LOOPBACK, ConfigError, detect_interfaces, interface_addresses, ip_output
|
from vpnrouter_net import DEFAULT_LISTEN, LISTEN_CHOICES, LOOPBACK, ConfigError, detect_interfaces, interface_addresses, ip_output
|
||||||
|
|
||||||
CONFIG_FILE = pathlib.Path('/etc/vpn-router/vpn-router.json')
|
CONFIG_FILE = pathlib.Path('/etc/vpn-router/vpn-router.json')
|
||||||
@@ -435,6 +436,9 @@ def validate(cfg):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ConfigError(f'openvpn.address_pool: {openvpn_pool!r} is not an IPv4 network')
|
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_addr'), 'local.int_addr')
|
||||||
_check_address(_get(cfg, 'local', 'int_gateway_ip'), 'local.int_gateway_ip')
|
_check_address(_get(cfg, 'local', 'int_gateway_ip'), 'local.int_gateway_ip')
|
||||||
|
|
||||||
@@ -893,6 +897,8 @@ def build_context(cfg, public_ip=None):
|
|||||||
'openvpn_protocol': _get(cfg, 'openvpn', 'protocol'),
|
'openvpn_protocol': _get(cfg, 'openvpn', 'protocol'),
|
||||||
'openvpn_device': OPENVPN_DEVICE,
|
'openvpn_device': OPENVPN_DEVICE,
|
||||||
'openvpn_ldap_conf': str(OPENVPN_LDAP),
|
'openvpn_ldap_conf': str(OPENVPN_LDAP),
|
||||||
|
'openvpn_status': vpnrouter_status.OPENVPN_STATUS,
|
||||||
|
'openvpn_status_seconds': vpnrouter_status.OPENVPN_STATUS_SECONDS,
|
||||||
'p2s_dns_list': _split_list(_join(local_addrs, _split_list(extras['EXTRA_P2S_DNS']))),
|
'p2s_dns_list': _split_list(_join(local_addrs, _split_list(extras['EXTRA_P2S_DNS']))),
|
||||||
'openvpn_ca': str(vpnrouter_pki.OPENVPN_CA),
|
'openvpn_ca': str(vpnrouter_pki.OPENVPN_CA),
|
||||||
'openvpn_crl': str(vpnrouter_pki.OPENVPN_CRL),
|
'openvpn_crl': str(vpnrouter_pki.OPENVPN_CRL),
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ COMMAND_TIMEOUT = 5
|
|||||||
|
|
||||||
OPENVPN_STATUS = '/run/openvpn-server/status-vpn-router.log'
|
OPENVPN_STATUS = '/run/openvpn-server/status-vpn-router.log'
|
||||||
|
|
||||||
|
#: Seconds between two writes of OPENVPN_STATUS by the OpenVPN server, whose configuration sets it (OpenVPN's default is 60).
|
||||||
|
OPENVPN_STATUS_SECONDS = 5
|
||||||
|
|
||||||
#: Seconds since its last handshake within which a WireGuard peer counts as connected.
|
#: Seconds since its last handshake within which a WireGuard peer counts as connected.
|
||||||
WIREGUARD_CONNECTED_SECONDS = 180
|
WIREGUARD_CONNECTED_SECONDS = 180
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ Pages:
|
|||||||
applied by vpn-router-setup (see site_to_site())
|
applied by vpn-router-setup (see site_to_site())
|
||||||
/backup the backup settings, the last run and the local archives: show, change and run
|
/backup the backup settings, the last run and the local archives: show, change and run
|
||||||
now (see backup())
|
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
|
/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
|
settings, issue and revoke certificates, and take the certificate of an issuing CA
|
||||||
and a root CRL signed offline (see 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
|
the databases of each PostgreSQL cluster on the router with their sizes, as JSON (see
|
||||||
postgresql_databases()); /config has the clusters' settings
|
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;
|
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
|
/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
|
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.
|
#: 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')
|
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.
|
#: 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}
|
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'])}
|
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):
|
def ca(method, path, body):
|
||||||
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
||||||
|
|
||||||
@@ -1193,9 +1241,11 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
The ticket must come from POST /shell for the user named by the token. Binary messages of the client are its
|
The ticket must come from POST /shell for the user named by the token. Binary messages of the client are its
|
||||||
keystrokes, a text message {"cols", "rows"} sets the window size, and binary messages of the server are the
|
keystrokes, a text message {"cols", "rows"} sets the window size, and binary messages of the server are the
|
||||||
terminal's output. The shell ends after web.shell_idle_minutes without a keystroke, when it exits, or when the
|
terminal's output. The shell ends after web.shell_idle_minutes without a keystroke, when it exits, or when the
|
||||||
client closes the connection; the server then closes it with the reason. Each opening and end is logged.
|
client closes the connection; the server then closes it with the reason. Each opening, end and refusal is logged
|
||||||
|
with its reason.
|
||||||
"""
|
"""
|
||||||
def refuse(code, message):
|
def refuse(code, message):
|
||||||
|
log(f'shell of {name} from {self.client()} refused: {message}')
|
||||||
body = json.dumps({'error': message}).encode() + b'\n'
|
body = json.dumps({'error': message}).encode() + b'\n'
|
||||||
self.send_response(code)
|
self.send_response(code)
|
||||||
self.send_header('Content-Type', 'application/json')
|
self.send_header('Content-Type', 'application/json')
|
||||||
@@ -1317,7 +1367,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
if self.command not in ('GET', 'HEAD'):
|
if self.command not in ('GET', 'HEAD'):
|
||||||
self.close_connection = True
|
self.close_connection = True
|
||||||
directory = next((name for name in ('users', 'service-accounts', 'groups', 'account', 'site-to-site', 'backup',
|
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)
|
if path == f'/{name}' or path.startswith(f'/{name}/')), None)
|
||||||
protected = path in ('/status/full', '/config', '/metrics/history', '/postgresql') or directory is not None
|
protected = path in ('/status/full', '/config', '/metrics/history', '/postgresql') or directory is not None
|
||||||
signing = path in ('/auth', '/auth/refresh', '/auth/logout')
|
signing = path in ('/auth', '/auth/refresh', '/auth/logout')
|
||||||
@@ -1360,6 +1410,8 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
elif directory == 'shell':
|
elif directory == 'shell':
|
||||||
code, document = shell_ticket(claims.get('sub'), payload) if (path, self.command) == ('/shell', 'POST') \
|
code, document = shell_ticket(claims.get('sub'), payload) if (path, self.command) == ('/shell', 'POST') \
|
||||||
else (404, {'error': f'no route {self.command} {path}'})
|
else (404, {'error': f'no route {self.command} {path}'})
|
||||||
|
if code != 200:
|
||||||
|
log(f'shell ticket of {claims.get("sub")} from {self.client()} refused: {document["error"]}')
|
||||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
||||||
elif directory == 'account':
|
elif directory == 'account':
|
||||||
code, document = account(claims['sub'], self.command, path, payload)
|
code, document = account(claims['sub'], self.command, path, payload)
|
||||||
@@ -1367,6 +1419,9 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
elif directory == 'site-to-site':
|
elif directory == 'site-to-site':
|
||||||
code, document = site_to_site(self.command, path, payload)
|
code, document = site_to_site(self.command, path, payload)
|
||||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
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':
|
elif directory == 'backup':
|
||||||
code, document = backup(self.command, path, payload)
|
code, document = backup(self.command, path, payload)
|
||||||
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ push "route {{ network }} {{ netmask }}"
|
|||||||
push "dhcp-option DNS {{ address }}"
|
push "dhcp-option DNS {{ address }}"
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
|
||||||
|
status {{ openvpn_status }} {{ openvpn_status_seconds }}
|
||||||
|
|
||||||
keepalive 10 120
|
keepalive 10 120
|
||||||
{% if openvpn_protocol == 'udp' -%}
|
{% if openvpn_protocol == 'udp' -%}
|
||||||
explicit-exit-notify 1
|
explicit-exit-notify 1
|
||||||
|
|||||||
Reference in new issue
Block a user