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.
Release / release (push) Successful in 20s
Release / release (push) Successful in 20s
This commit is contained in:
1 parent
ec93bc6a2d
commit
07dbe91d0c
5 files changed
+23
-5
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
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)
|
||||||
|
|||||||
@@ -1193,9 +1193,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')
|
||||||
@@ -1360,6 +1362,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)
|
||||||
|
|||||||
Reference in new issue
Block a user