Skip to content

Commit d4ad1bb

Browse files
authored
fix(snap): only enable gateway user service for one user (#4615)
* fix(snap): only enable gateway user service for one user Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * docs(snap): correct snap docs for the gateway user service Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * fix(snap): add documentation of global to per-user enablement migration Signed-off-by: Oliver Calder <oliver.calder@canonical.com> --------- Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
1 parent d2a3508 commit d4ad1bb

11 files changed

Lines changed: 121 additions & 28 deletions

File tree

‎.github/workflows/release-canary.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ jobs:
221221
- name: Install and check status
222222
run: |
223223
set -euo pipefail
224+
other_user=openshell-canary-other
225+
sudo useradd --create-home "$other_user"
226+
other_uid=$(id -u "$other_user")
227+
sudo loginctl enable-linger "$other_user"
228+
sudo systemctl start "user@${other_uid}.service"
224229
systemctl --user set-environment \
225230
"OPENSHELL_TELEMETRY_ENABLED=${OPENSHELL_TELEMETRY_ENABLED}"
226231
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh |
@@ -238,6 +243,21 @@ jobs:
238243
snap services openshell
239244
systemctl --user is-enabled --quiet snap.openshell.user-gateway.service
240245
systemctl --user is-active snap.openshell.user-gateway.service
246+
sudo -u "$other_user" env \
247+
HOME="/home/${other_user}" \
248+
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
249+
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
250+
systemctl --user daemon-reload
251+
! sudo -u "$other_user" env \
252+
HOME="/home/${other_user}" \
253+
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
254+
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
255+
systemctl --user is-enabled --quiet snap.openshell.user-gateway.service
256+
! sudo -u "$other_user" env \
257+
HOME="/home/${other_user}" \
258+
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
259+
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
260+
systemctl --user is-active --quiet snap.openshell.user-gateway.service
241261
! systemctl is-enabled --quiet snap.openshell.system-gateway.service
242262
! systemctl is-active --quiet snap.openshell.system-gateway.service
243263
journalctl --user -u snap.openshell.user-gateway.service --no-pager |

‎docs/about/installation.mdx‎

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ The snap does not migrate existing Debian, RPM, or Homebrew installs. Remove any
125125

126126
```shell
127127
sudo snap install openshell
128+
systemctl --user enable --now snap.openshell.user-gateway.service
129+
```
130+
131+
The user gateway is disabled globally by default. The install script enables it only for the user who ran the script. After a direct snap installation, choose one user account to run the gateway and enable it with the command above. Do not enable the gateway for multiple users who may be logged in at the same time: every user gateway binds to `127.0.0.1:17670`, so only one can run on the system at a time.
132+
133+
Older OpenShell snap revisions could enable the user gateway globally, causing it to start for every user. Remove that global enablement once, then enable the gateway from the account that will run it:
134+
135+
```shell
136+
sudo systemctl disable --global snap.openshell.user-gateway.service
137+
systemctl --user enable --now snap.openshell.user-gateway.service
138+
```
139+
140+
The global command prevents automatic activation for other users but does not stop gateways already running in their user sessions. Run the following command from each other affected user's session:
141+
142+
```shell
143+
systemctl --user disable --now snap.openshell.user-gateway.service
128144
```
129145

130146
The snap installs the standalone policy prover as `openshell.prover`. The
@@ -187,6 +203,7 @@ To use the user gateway, start it and replace the previous CLI registration afte
187203

188204
```shell
189205
sudo snap set openshell gateway-mode=user
206+
systemctl --user enable --now snap.openshell.user-gateway.service
190207
systemctl --user is-active snap.openshell.user-gateway.service
191208
openshell gateway list
192209
old_registration="NAME_FROM_GATEWAY_LIST"
@@ -195,6 +212,8 @@ openshell gateway add https://127.0.0.1:17670 --local --name openshell
195212
openshell status
196213
```
197214

215+
Setting `gateway-mode=user` disables the system gateway but does not enable or disable any user's gateway service. Enable the service for only one selected user. To hand the gateway to another user, first run `systemctl --user disable --now snap.openshell.user-gateway.service` from the current gateway user's session, then run the enable command from the new user's session. Otherwise, the two services will compete for `127.0.0.1:17670`.
216+
198217
To use the system gateway, start it, wait for it to generate its root-owned credentials, then copy its client bundle and replace the previous CLI registration:
199218

200219
```shell
@@ -225,7 +244,7 @@ sudo snap connect openshell:log-observe
225244
sudo snap connect openshell:system-observe
226245
sudo snap connect openshell:docker :docker
227246
systemctl --user reset-failed snap.openshell.user-gateway.service
228-
snap start --user openshell.user-gateway
247+
systemctl --user enable --now snap.openshell.user-gateway.service
229248
```
230249

231250
For an installation upgraded from a legacy system gateway, use `sudo systemctl reset-failed snap.openshell.system-gateway.service` and `sudo snap restart openshell.system-gateway` instead.
@@ -254,6 +273,13 @@ brew uninstall nvidia/openshell/openshell
254273
rm -rf "$(brew --prefix)/var/openshell"
255274
```
256275

276+
Snap:
277+
278+
```shell
279+
systemctl --user disable --now snap.openshell.user-gateway.service
280+
sudo snap remove --purge openshell
281+
```
282+
257283
Debian and Ubuntu:
258284

259285
```shell
@@ -270,12 +296,6 @@ sudo dnf remove openshell-gateway openshell-prover openshell
270296
rm -rf "${XDG_STATE_HOME:-$HOME/.local/state}/openshell"
271297
```
272298

273-
Snap:
274-
275-
```shell
276-
sudo snap remove --purge openshell
277-
```
278-
279299
Remove any custom config or database set through `OPENSHELL_GATEWAY_CONFIG` or `OPENSHELL_DB_URL` separately.
280300

281301
## Next Steps

‎install.sh‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,12 +1496,16 @@ Install Docker Engine from a system package or Docker's package repository, then
14961496
if [ "$_gateway_mode" = system ]; then
14971497
warn "restarting the OpenShell gateway to use the refreshed snap; active sandbox sessions will be interrupted"
14981498
as_root snap restart openshell.system-gateway
1499-
elif [ "$_existing_snap" = true ]; then
1500-
warn "restarting the OpenShell user gateway to use the refreshed snap; active sandbox sessions will be interrupted"
1501-
# as root, snap restart automatically restarts the given user services for
1502-
# all active users (there should just be one), and is forwards-compatible
1503-
# with planned changes to the snap user daemon commands.
1504-
as_root snap restart openshell.user-gateway
1499+
else
1500+
# In case refreshing from an old snap which had a globally-enabled user
1501+
# gateway service, globally disable it, then just enable for the target user
1502+
as_root systemctl --global disable snap.openshell.user-gateway.service
1503+
as_target_user systemctl --user daemon-reload
1504+
as_target_user systemctl --user enable --now snap.openshell.user-gateway.service
1505+
if [ "$_existing_snap" = true ]; then
1506+
warn "restarting the OpenShell user gateway to use the refreshed snap; active sandbox sessions will be interrupted"
1507+
as_target_user systemctl --user restart snap.openshell.user-gateway.service
1508+
fi
15051509
fi
15061510

15071511
info "registering local gateway as ${TARGET_USER}..."

‎skills/debug-openshell-cluster/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Common findings:
7272
- `No active gateway`: register one with `openshell gateway add <endpoint>`.
7373
- Connection refused: gateway process is not running, service exposure is wrong, or a port-forward/proxy is not active.
7474
- TLS/certificate errors: the endpoint scheme or trust chain is wrong, a CLI mTLS bundle does not match the gateway CA, a supervisor is missing the gateway CA, or TLS termination does not match the gateway listener. Workloads and supervisors should not contain a user TLS client certificate or private key.
75-
- Fresh snap installations use `openshell.user-gateway`; upgraded installations retain their system-owned state through `openshell.system-gateway`. Existing HTTPS registrations continue to work after the service rename. Follow the published snap installation steps to enroll a new user, replace an old HTTP registration, or change modes safely through the `disable` maintenance state. If `gateway-mode=disable`, no local snap gateway is expected to run.
75+
- Fresh snap installations select `gateway-mode=user`, but the user gateway is globally disabled by default. `install.sh` enables `snap.openshell.user-gateway.service` only for its target user; direct snap installations must select one user and run `systemctl --user enable --now snap.openshell.user-gateway.service` from that user's session. Do not enable it for multiple concurrently logged-in users: every instance binds to `127.0.0.1:17670`, so later instances fail with an address-in-use error. Older snap revisions could leave the unit globally enabled; remove that state with `sudo systemctl disable --global snap.openshell.user-gateway.service`, then disable and stop the service from every other affected user's session. The global command does not stop running user services. Setting `gateway-mode=user` disables the system gateway without changing any user's service. To change the selected user, disable and stop the current user's unit before enabling the new user's unit. Upgraded installations retain their system-owned state through `openshell.system-gateway`. Existing HTTPS registrations continue to work after the service rename. Follow the published snap installation steps to enroll a new user, replace an old HTTP registration, or change modes safely through the `disable` maintenance state. If `gateway-mode=disable`, no local snap gateway is expected to run.
7676
- `Unauthenticated` from an edge or OIDC gateway: refresh stored credentials with `openshell gateway login [name]`, then retry. Use `gateway logout` only when intentionally clearing local credentials.
7777
- Operator credential retrieval requires direct gateway mTLS, `[openshell.gateway.mtls_auth] operator_enabled = true`, and a verified certificate with exact `OU=operator`. OIDC admin roles and forwarded certificate headers do not grant access. Do not combine the operator certificate with an `Authorization` header. Enabling this role grants gateway-wide administration; never repair a denial by issuing operator certificates to ordinary users. See the published gateway authentication and provider references.
7878
- A direct development endpoint with a private or self-signed certificate can be isolated with `--gateway-endpoint <url> --gateway-insecure`; do not persist or recommend insecure verification for shared gateways.

‎snap/hooks/configure‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
# SPDX-License-Identifier: Apache-2.0
44

5-
# Apply the configured gateway service model. The disabled mode provides a
6-
# safe intermediate state while changing between user and system gateways.
5+
# Apply the configured gateway service model. User gateway activation belongs
6+
# to each user's systemd manager; user mode only disables the conflicting
7+
# system gateway. The disabled mode provides a safe intermediate state while
8+
# changing between user and system gateways.
79

810
set -eu
911

1012
gateway_mode=$(snapctl get gateway-mode)
1113
case "$gateway_mode" in
1214
user)
1315
snapctl stop --disable "${SNAP_INSTANCE_NAME}.system-gateway"
14-
snapctl start --enable "${SNAP_INSTANCE_NAME}.user-gateway"
1516
;;
1617
system)
1718
snapctl stop --disable "${SNAP_INSTANCE_NAME}.user-gateway"

‎snap/hooks/install‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
# SPDX-License-Identifier: Apache-2.0
44

5-
# Fresh installations use the per-user gateway. The configure hook applies the
6-
# service selection after this transaction commits.
5+
# Fresh installations select the per-user gateway model. Each user enables the
6+
# gateway through their own systemd user manager.
77

88
set -eu
99

‎snap/hooks/post-refresh‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
# One-time migration for installations that predate per-user snap gateways.
66
# Existing installations retain the system gateway; fresh installations record
77
# gateway-mode=user in the install hook and skip this migration. The configure
8-
# hook applies the selected service after this transaction commits.
8+
# hook applies system and disabled modes after this transaction commits. User
9+
# gateway activation remains owned by each user's systemd manager.
910

1011
set -eu
1112

‎snapcraft.yaml‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ description: |
1313
1414
The OpenShell snap ships a CLI (`openshell`), a terminal UI
1515
(`openshell.term`), a standalone policy prover (`openshell.prover`), and a
16-
managed gateway daemon (`openshell.user-gateway`).
16+
managed gateway daemon (`openshell.user-gateway`). The user gateway is
17+
disabled by default so that one selected user can run it.
1718
1819
**Setup instructions**
1920
@@ -23,12 +24,30 @@ description: |
2324
2425
Snap Store installations automatically connect the required interfaces.
2526
27+
Enable and start the gateway from the single user account that will run
28+
it:
29+
30+
systemctl --user enable --now snap.openshell.user-gateway.service
31+
32+
Do not enable the gateway for multiple users who may be logged in at the
33+
same time. Every user gateway binds to 127.0.0.1:17670, so only one can
34+
run on the system at a time.
35+
36+
Older OpenShell snap revisions could enable the user gateway globally.
37+
When upgrading such an installation, remove that global enablement so
38+
the gateway does not start automatically for other users:
39+
40+
sudo systemctl disable --global snap.openshell.user-gateway.service
41+
42+
This does not stop gateways already running in other user sessions.
43+
Those users must disable and stop the service from their own sessions.
44+
2645
If the OpenShell snap was installed before Docker was running, its user
2746
service may have hit the systemd start limit. Resolve this from the
2847
affected user's session after Docker is available:
2948
3049
systemctl --user reset-failed snap.openshell.user-gateway.service
31-
snap start --user openshell.user-gateway
50+
systemctl --user restart snap.openshell.user-gateway.service
3251
3352
2. Register the per-user gateway after it provisions its mTLS credentials:
3453

‎tasks/scripts/test-install-sh.sh‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,10 @@ assert_snap_user_refresh_flow() {
366366
calls="$(cat "$out")"
367367
expected="wait:user-docker
368368
root:snap refresh openshell --channel=latest/stable
369-
root:snap restart openshell.user-gateway
369+
root:systemctl --global disable snap.openshell.user-gateway.service
370+
target:systemctl --user daemon-reload
371+
target:systemctl --user enable --now snap.openshell.user-gateway.service
372+
target:systemctl --user restart snap.openshell.user-gateway.service
370373
wait:user-gateway-listener
371374
register:user-gateway
372375
wait:gateway-status"
@@ -382,6 +385,9 @@ assert_snap_install_flow \
382385
1 0 "" \
383386
"wait:user-docker
384387
root:snap install openshell --channel=latest/stable
388+
root:systemctl --global disable snap.openshell.user-gateway.service
389+
target:systemctl --user daemon-reload
390+
target:systemctl --user enable --now snap.openshell.user-gateway.service
385391
wait:user-gateway-listener
386392
register:user-gateway
387393
wait:gateway-status"

‎tasks/scripts/test-packaging-assets.sh‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ assert_contains "$snapcraft" 'daemon-scope: system'
172172
assert_contains "$snapcraft" 'daemon-scope: user'
173173
assert_contains "$snapcraft" 'install-mode: disable'
174174
assert_not_contains "$snapcraft" 'install-mode: enable'
175+
for app in system-gateway user-gateway; do
176+
if ! awk -v app="$app" '
177+
$0 == " " app ":" { found = 1; in_app = 1; next }
178+
in_app && /^ [[:alnum:]_-]+:$/ { exit !disabled }
179+
in_app && /install-mode: disable/ { disabled = 1 }
180+
END { exit !(found && disabled) }
181+
' "$snapcraft"; then
182+
echo "FAIL: snap ${app} must use install-mode: disable" >&2
183+
exit 1
184+
fi
185+
done
175186
if grep -Eq '^ gateway:$' "$snapcraft"; then
176187
echo "FAIL: removed snap gateway app must not remain declared" >&2
177188
exit 1
@@ -219,8 +230,19 @@ assert_not_contains "$snap_install_docs" "snap connect openshell:network"
219230
assert_not_contains "$snap_install_docs" "snap connect openshell:network-bind"
220231
assert_contains "$snap_install_docs" "snap connect openshell:docker :docker"
221232
assert_contains "$snap_install_docs" "systemctl --user reset-failed snap.openshell.user-gateway.service"
222-
assert_contains "$snap_install_docs" "snap start --user openshell.user-gateway"
223-
assert_contains "$snapcraft" "snap start --user openshell.user-gateway"
233+
assert_contains "$snap_install_docs" "systemctl --user enable --now snap.openshell.user-gateway.service"
234+
assert_contains "$snapcraft" "systemctl --user enable --now snap.openshell.user-gateway.service"
235+
assert_contains "$ROOT/install.sh" "systemctl --user enable --now snap.openshell.user-gateway.service"
236+
assert_contains "$ROOT/install.sh" "systemctl --global disable snap.openshell.user-gateway.service"
237+
assert_contains "$snap_install_docs" 'every user gateway binds to `127.0.0.1:17670`'
238+
assert_contains "$snap_install_docs" "systemctl --user disable --now snap.openshell.user-gateway.service"
239+
assert_contains "$snapcraft" "Every user gateway binds to 127.0.0.1:17670"
240+
assert_contains "$snap_install_docs" "sudo systemctl disable --global snap.openshell.user-gateway.service"
241+
assert_contains "$snapcraft" "sudo systemctl disable --global snap.openshell.user-gateway.service"
242+
for snap_file in "$ROOT/install.sh" "$snapcraft" "$snap_install_docs"; do
243+
assert_not_contains "$snap_file" "snap start --user openshell.user-gateway"
244+
assert_not_contains "$snap_file" "snap restart openshell.user-gateway"
245+
done
224246
assert_contains "$snap_install_docs" "openshell.user-gateway"
225247
assert_contains "$snap_install_docs" "openshell.system-gateway"
226248
assert_contains "$snap_install_docs" "Existing HTTPS registrations continue to work"

0 commit comments

Comments
 (0)