# ===================================================================== # owuitools-vmagent - the sub-agent that drives a VM. # # It holds no secrets and can reach exactly one address. Everything # dangerous is on the other side of two boundaries it cannot cross: # # the network owuitools-vmguard owns this container's namespace with a # default-deny filter whose allowlist is owuitools-vmgw # and nothing else. vmgw is what holds the Open WebUI # admin key and the one permitted Ollama path. # # the socket /run/owuivm/agent/agent.sock, bind-mounted from the # host, mode 0660 group owuitools-agent (gid 10001 - this # container's own uid, since no userns remapping is # configured here). vmd is on the far side with /dev/kvm, # the frozen base images and the Gitea token. # # Note this image is NOT the code-execution sandbox. Nothing a user or a # model writes runs here; it runs in a VM. This container runs our own # loop, which is why it is an ordinary hardened container rather than # something as locked down as owuitools-octave. # ===================================================================== FROM python:3.12-slim # Pillow is used for one thing: re-encoding a PNG screenshot as a small # JPEG before it is posted into the chat. Every embed is a database write # in Open WebUI, and a desktop PNG is an order of magnitude larger than # it needs to be for someone watching a window open. events.py degrades # gracefully if it is ever absent, so this is an optimisation rather than # a dependency. RUN pip install --no-cache-dir --disable-pip-version-check Pillow==11.0.0 \ && find /usr/local -name '__pycache__' -type d -prune -exec rm -rf {} + WORKDIR /app COPY vmagent/app/ /app/ # The socket protocol client is shared with the host's own vmctl, so it # lives with vmd and is copied in rather than duplicated. One definition # of the wire format, used by both ends of it. COPY vm/vmd/client.py /app/client.py # Same uid as owuitools-api and the owuitools-agent group on the host, so # the bind-mounted socket directory can be mode 0660 rather than # world-writable. It grants no privilege of its own. RUN useradd --uid 10001 --create-home --home-dir /home/agent --shell /usr/sbin/nologin agent USER 10001:10001 ENV HOME=/home/agent PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["python3", "/app/runner.py"]