Initial Commit

This commit is contained in:
2026-09-15 15:24:03 -04:00
commit 4dc7086e7e
605 changed files with 101380 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
# =====================================================================
# vmd: the privileged side of the VM sandbox.
#
# Runs as owuivmd, NOT root. Its four privileged needs - start and stop
# the VM unit, create a project dataset with a quota, start and stop
# virtiofsd - go through /usr/local/sbin/owuivm-ctl, which is the single
# entry in its sudoers file and accepts a fixed verb list.
#
# ITS NETWORK IS AN ALLOW-LIST, WHICH IS THE OPPOSITE OF THE VM'S.
#
# The sandbox unit uses deny-only, because the guest should reach the
# whole internet except a few ranges. vmd is the other way round: it
# needs exactly one destination, Gitea on the LAN, and nothing else -
# not the internet, not Ollama, not Open WebUI, not the other hosts.
#
# systemd.resource-control(5) checks IPAddressAllow= FIRST and grants on
# a match, so an allow-list is written as `deny any` plus the addresses
# that are wanted. That is the documented recipe, and it is the exact
# inverse of the mistake that was made in owuivm.service - where an
# `IPAddressAllow=0.0.0.0/0` silently disabled the whole deny list.
# =====================================================================
[Unit]
Description=owuitools VM sandbox daemon
After=network-online.target zfs-mount.service
Wants=network-online.target
# A daemon that can start a VM while the guard table is absent would be
# a daemon that can start an unfiltered VM.
Requires=owuivm-nft-guard.service
After=owuivm-nft-guard.service
[Service]
Type=simple
User=owuivmd
Group=owuivmd
SupplementaryGroups=owuivm owuitools-agent
UMask=0007
Environment=PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
EnvironmentFile=-/etc/owuivm/vmd.env
ExecStart=/usr/bin/python3 /usr/local/lib/owuivm/vmd.py
# --- network: one destination -----------------------------------------
IPAddressDeny=any
# BOTH of these, and the second is not redundant.
#
# Gitea is published on 192.168.1.10:7200, and allowing only that
# address does not work: systemd's filter is a BPF program on the
# cgroup's packets, and by the time it sees them Docker's DNAT has
# already rewritten the destination to the container's own address.
# Measured - with only the .10 rule, a connect to 192.168.1.10:7200
# times out; adding gitea's docker network makes it succeed immediately.
#
# The second entry is gitea_default, whose only member is the gitea
# container. Naming the container's current address instead would be
# narrower and would break the next time it is recreated.
@GITEA_ALLOW@
# The tool gateway, for the agent's web_search and read_page. The whole
# owuitools_core subnet rather than the gw's address, for the reason
# above: it is pinned in docker-compose.yml, and naming one container's
# address is a rule that breaks the next time it is recreated.
#
# Measured the hard way: this was missing, and `web_search` inside a
# session came back "could not reach the search service: timed out"
# while the same call from a shell as the same user worked perfectly -
# because a shell is not in the unit's cgroup, and the filter is on the
# cgroup. The same shape as every other time this has bitten.
IPAddressAllow=@TOOLS_SUBNET@
RestrictAddressFamilies=AF_UNIX AF_INET
# It answers on a unix socket and listens on nothing else.
SocketBindDeny=any
# --- filesystem --------------------------------------------------------
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
# /var/lib/owuivm/warm holds the saved machines vmd writes and reads
# back. Not the image directory next to it: vmd must never be able to
# touch a base image, and this keeps that true while giving it
# somewhere of its own.
ReadWritePaths=/run/owuivm @POOL_ROOT@/vmprojects @POOL_ROOT@/vmrun /var/lib/owuivm/warm /var/lib/owuivm/sessions
# /etc/sudoers.d/60-cis-logfile makes every sudo call write here. That is
# an audit control and is not being disabled for this daemon's
# convenience; ProtectSystem=strict just has to make room for it, or vmd
# cannot invoke its own privileged helper at all.
ReadWritePaths=/var/log/sudo.log
# Explicitly NOT writable: /var/lib/owuivm/base. vmd reads the frozen
# images to clone from them and must never be able to change one.
ReadOnlyPaths=/var/lib/owuivm/base
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=no
LockPersonality=yes
# NoNewPrivileges cannot be set: sudo to owuivm-ctl is how vmd starts the
# VM at all, and sudo is setuid. The compensating control is that the
# sudoers entry names exactly one program, and that program takes a fixed
# verb list with a pattern-checked argument.
NoNewPrivileges=no
SystemCallArchitectures=native
SystemCallFilter=@system-service
MemoryMax=512M
TasksMax=128
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target