Package as a dedicated tsunagi user; add Arch packages
Release / tsunagi-linux-aarch64 (push) Successful in 2m39s
Release / tsunagi-linux-x86_64 (push) Successful in 2m32s
Release / tsunagi-macos-aarch64 (push) Canceled after 0s
Release / tsunagi-windows-x86_64 (push) Canceled after 0s
Release / Publish release (push) Canceled after 0s
Release / tsunagi-linux-aarch64 (push) Successful in 2m39s
Release / tsunagi-linux-x86_64 (push) Successful in 2m32s
Release / tsunagi-macos-aarch64 (push) Canceled after 0s
Release / tsunagi-windows-x86_64 (push) Canceled after 0s
Release / Publish release (push) Canceled after 0s
Reworks the Linux packaging so the service runs as an unprivileged, dedicated "tsunagi" user instead of root — the point of a native package is to set things up properly for that OS's users — and adds Arch Linux packages beside the deb. Both package families are built from the already-compiled static binary; nothing is recompiled. Service model (shared systemd unit, dist/linux/tsunagi.service): - User=tsunagi with AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE, so it creates the TUN and binds low ports without root. - StateDirectory/CacheDirectory/RuntimeDirectory give it /var/lib/tsunagi, /var/cache/tsunagi and /run/tsunagi, and the agent is pointed at them (and the socket at /run/tsunagi/agent.sock) through TSUNAGI_* env in the unit. - The polkit rule now grants the resolve1 actions to the "tsunagi" group; the service user is in it, and admins can add themselves. CLI: when no state dir is chosen, the control commands prefer the system service's socket at /run/tsunagi/agent.sock when it exists, so `sudo tsunagi status`/`join` reach the packaged daemon with no flags. A per-user agent (its socket absent from /run) still uses its own. Packaging: - deb (build-deb.sh): postinst now creates the tsunagi system user+group; unit installed to /lib/systemd/system, binary to /usr/bin. - Arch (new dist/arch/build-pkg.sh): a dependency-free .pkg.tar.zst assembled with bsdtar + zstd (no makepkg, no Arch host) — .PKGINFO, a .INSTALL scriptlet that creates the user and reloads systemd, and a gzipped .MTREE; unit to /usr/lib/systemd/system. - The polkit rule moved to dist/linux (shared by deb, Arch and the tarball), and the tarball install.sh now creates the user and installs the unit + rule. - Both release workflows build the deb and the Arch package on the two Linux matrix entries (deb_arch/pkg_arch), install libarchive-tools+zstd for it, and attach both to the release (Gitea API loop / gh). Not exercised in this session: the .deb and .pkg.tar.zst build and install only on Linux (no dpkg-deb/bsdtar+pacman locally); CLI build, clippy, fmt and tests pass, and all shell/YAML is syntax-checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
a3856eed3b
commit
63178574ce
11 files changed
+306
-81
No files matched your search
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
// Installed by the tsunagi packages.
|
||||
//
|
||||
// polkit decides who may configure systemd-resolved by user/group id, not by
|
||||
// capability, so the agent needs this even though it holds CAP_NET_ADMIN. It
|
||||
// grants exactly the four resolve1 actions the agent calls, and nothing else,
|
||||
// to anyone in the "tsunagi" group.
|
||||
//
|
||||
// The packaged service runs as the "tsunagi" user (which is in that group), so
|
||||
// its resolver configuration is allowed. To also run the agent as your own
|
||||
// user, add yourself to the group:
|
||||
//
|
||||
// sudo usermod -aG tsunagi "$USER"
|
||||
polkit.addRule(function(action, subject) {
|
||||
var allowed = [
|
||||
"org.freedesktop.resolve1.set-dns-servers",
|
||||
"org.freedesktop.resolve1.set-domains",
|
||||
"org.freedesktop.resolve1.set-default-route",
|
||||
"org.freedesktop.resolve1.revert"
|
||||
];
|
||||
if (allowed.indexOf(action.id) >= 0 && subject.isInGroup("tsunagi")) {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
Vendored
+17
-12
@@ -2,40 +2,45 @@
|
||||
|
||||
This archive holds a **static** `tsunagi` binary (musl), so it runs on any
|
||||
distribution — Arch, Alpine, NixOS, old glibc systems — with no shared-library
|
||||
dependencies. Alongside it are a systemd unit and an installer.
|
||||
dependencies. Alongside it are a systemd unit, a polkit rule, and an installer.
|
||||
|
||||
Creating the TUN interface needs `CAP_NET_ADMIN`, and configuring
|
||||
systemd-resolved is decided by polkit (by user id, not capability). There are
|
||||
three ways to satisfy that; pick one.
|
||||
systemd-resolved is decided by polkit. There are three ways to run it; pick one.
|
||||
|
||||
## Quick install — systemd service as root
|
||||
> On Debian/Ubuntu and Arch, prefer the native package (`.deb` / `.pkg.tar.zst`)
|
||||
> from the release — it does all of the below for you.
|
||||
|
||||
## Quick install — systemd service as a dedicated user
|
||||
|
||||
```sh
|
||||
sudo ./install.sh
|
||||
sudo systemctl enable --now tsunagi
|
||||
```
|
||||
|
||||
The service runs the agent as root, so the interface, low ports and the
|
||||
resolver all just work (root is not subject to polkit). Control it **as root**,
|
||||
so the client resolves the same state directory the daemon uses:
|
||||
The service runs as an unprivileged `tsunagi` user with just `CAP_NET_ADMIN`
|
||||
and `CAP_NET_BIND_SERVICE` (granted by systemd), not root. systemd creates its
|
||||
state (`/var/lib/tsunagi`), cache (`/var/cache/tsunagi`) and runtime
|
||||
(`/run/tsunagi`) directories, and the polkit rule lets it configure the
|
||||
resolver. Control it with `sudo` — the CLI finds the running service's socket at
|
||||
`/run/tsunagi/agent.sock` with no flags:
|
||||
|
||||
```sh
|
||||
sudo tsunagi status
|
||||
sudo tsunagi join <network-name> <tsn1…secret>
|
||||
```
|
||||
|
||||
## As your own user — one capability, no root at runtime
|
||||
## As your own user — one capability, no service
|
||||
|
||||
```sh
|
||||
sudo install -m 0755 tsunagi /usr/local/bin/tsunagi
|
||||
sudo setcap cap_net_admin,cap_net_bind_service+p /usr/local/bin/tsunagi
|
||||
sudo usermod -aG tsunagi "$USER" # so the resolver works; log in again after
|
||||
tsunagi up
|
||||
```
|
||||
|
||||
Nothing is left behind and nothing runs as root. The capability is lost on every
|
||||
rebuild or copy of the binary, so re-run `setcap` after replacing it. For the
|
||||
local DNS resolver, run `tsunagi dns` once: it prints the exact polkit rule that
|
||||
lets your user configure systemd-resolved, ready to paste.
|
||||
Nothing runs as root. The capability is lost on every rebuild or copy of the
|
||||
binary, so re-run `setcap` after replacing it. The agent uses your per-user
|
||||
state directory and socket, so plain `tsunagi status` (no sudo) reaches it.
|
||||
|
||||
## Without touching the OS
|
||||
|
||||
|
||||
Vendored
+27
-19
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env sh
|
||||
# Installs the tsunagi binary and, where systemd is present, a root service.
|
||||
# Run it as root (it writes under /usr/local and /etc):
|
||||
# Installs the static tsunagi binary and, where systemd is present, a service
|
||||
# that runs the agent as a dedicated unprivileged "tsunagi" user. Run as root:
|
||||
#
|
||||
# sudo ./install.sh
|
||||
#
|
||||
# PREFIX overrides the install prefix (default /usr/local).
|
||||
# PREFIX overrides the install prefix (default /usr/local); if you change it,
|
||||
# edit ExecStart in tsunagi.service to match.
|
||||
set -eu
|
||||
|
||||
PREFIX="${PREFIX:-/usr/local}"
|
||||
@@ -19,8 +20,23 @@ echo "Installing tsunagi to ${PREFIX}/bin"
|
||||
install -d "${PREFIX}/bin"
|
||||
install -m 0755 "${here}/tsunagi" "${PREFIX}/bin/tsunagi"
|
||||
|
||||
# The dedicated system user the service runs as (works with either shadow's
|
||||
# useradd or Debian's adduser).
|
||||
if ! getent group tsunagi >/dev/null 2>&1; then
|
||||
groupadd -r tsunagi 2>/dev/null || addgroup --system tsunagi 2>/dev/null || true
|
||||
fi
|
||||
if ! getent passwd tsunagi >/dev/null 2>&1; then
|
||||
useradd -r -g tsunagi -d /var/lib/tsunagi -s /usr/sbin/nologin -c "tsunagi agent" tsunagi 2>/dev/null \
|
||||
|| adduser --system --ingroup tsunagi --home /var/lib/tsunagi --no-create-home \
|
||||
--gecos "tsunagi agent" --disabled-login tsunagi 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# polkit rule so the agent (as the tsunagi user) may configure systemd-resolved.
|
||||
install -d /etc/polkit-1/rules.d
|
||||
install -m 0644 "${here}/50-tsunagi-resolved.rules" /etc/polkit-1/rules.d/50-tsunagi-resolved.rules
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "Installing the systemd service (runs the agent as root)"
|
||||
echo "Installing the systemd service (runs as the tsunagi user)"
|
||||
install -m 0644 "${here}/tsunagi.service" /etc/systemd/system/tsunagi.service
|
||||
systemctl daemon-reload
|
||||
cat <<EOF
|
||||
@@ -29,7 +45,7 @@ Done. Start the agent with:
|
||||
|
||||
sudo systemctl enable --now tsunagi
|
||||
|
||||
Control it as root, so the client shares the agent's state directory:
|
||||
Control it with sudo (the CLI finds the service's socket automatically):
|
||||
|
||||
sudo tsunagi status
|
||||
sudo tsunagi join <network-name> <tsn1…secret>
|
||||
@@ -39,19 +55,11 @@ else
|
||||
|
||||
Installed the binary; no systemd found, so no service was set up. Either:
|
||||
|
||||
* run it directly as root:
|
||||
|
||||
sudo tsunagi up
|
||||
|
||||
* or grant the capability and run it as your own user (no root at runtime):
|
||||
|
||||
sudo setcap cap_net_admin,cap_net_bind_service+p ${PREFIX}/bin/tsunagi
|
||||
tsunagi up
|
||||
|
||||
For the local DNS resolver as a non-root user, run \`tsunagi dns\` once; it
|
||||
prints the polkit rule that lets your user configure systemd-resolved.
|
||||
|
||||
* or skip the interface entirely with \`tsunagi up --no-tun\`: tunnels still
|
||||
form, they just do not reach the operating system.
|
||||
* run it as root: sudo ${PREFIX}/bin/tsunagi up
|
||||
* or as your own user: sudo setcap cap_net_admin,cap_net_bind_service+p ${PREFIX}/bin/tsunagi
|
||||
tsunagi up
|
||||
(add yourself to the tsunagi group so the resolver works:
|
||||
sudo usermod -aG tsunagi "\$USER")
|
||||
* or without touching the OS: tsunagi up --no-tun
|
||||
EOF
|
||||
fi
|
||||
Vendored
+22
-12
@@ -6,21 +6,31 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Runs as a dedicated unprivileged user, not root. The two capabilities are all
|
||||
# it needs: CAP_NET_ADMIN to create and configure the TUN interface, and
|
||||
# CAP_NET_BIND_SERVICE for low ports. The packages create the `tsunagi` user.
|
||||
User=tsunagi
|
||||
Group=tsunagi
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
|
||||
# systemd creates and owns these for the service user, and the agent is pointed
|
||||
# at them: state in /var/lib/tsunagi, cache in /var/cache/tsunagi, and the
|
||||
# control socket in /run/tsunagi, which stays private to the service user.
|
||||
StateDirectory=tsunagi
|
||||
StateDirectoryMode=0700
|
||||
CacheDirectory=tsunagi
|
||||
RuntimeDirectory=tsunagi
|
||||
RuntimeDirectoryMode=0755
|
||||
Environment=TSUNAGI_STATE_DIR=%S/tsunagi
|
||||
Environment=TSUNAGI_CACHE_DIR=%C/tsunagi
|
||||
Environment=TSUNAGI_CONTROL_SOCKET=%t/tsunagi/agent.sock
|
||||
|
||||
# The tarball installer puts the binary here; the deb and the Arch package
|
||||
# rewrite this to /usr/bin where they install it.
|
||||
ExecStart=/usr/local/bin/tsunagi up
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# This unit runs the agent as root, which covers everything it needs: creating
|
||||
# the TUN interface (CAP_NET_ADMIN), binding low ports, and configuring
|
||||
# systemd-resolved — root is not subject to polkit, so no polkit rule is needed.
|
||||
#
|
||||
# To run as a non-root user instead, uncomment the three lines below, set a
|
||||
# User=, and install the polkit rule that `tsunagi dns` prints (resolved decides
|
||||
# by user id, not capability, so the rule is what lets a non-root agent set the
|
||||
# resolver):
|
||||
#User=tsunagi
|
||||
#AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
#CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in new issue
Block a user