This commit is contained in:
parent
34e1ae34db
commit
fb47979787
3 changed files with 22 additions and 25 deletions
17
Dockerfile
17
Dockerfile
|
|
@ -55,22 +55,25 @@ COPY --from=ghcr.io/amacneil/dbmate:2 /usr/local/bin/dbmate /usr/local/bin/dbmat
|
|||
COPY db/migrations/ /app/db/migrations/
|
||||
|
||||
# debian:*-slim ships without /etc/nsswitch.conf, and glibc then cannot turn a
|
||||
# uid back into a user record. libvirt does exactly that lookup before connecting
|
||||
# and gives up with "Failed to find user record for uid", whatever the socket
|
||||
# permissions are. Restore the plain file-based lookup.
|
||||
# uid back into a user record. Restore the plain file-based lookup.
|
||||
RUN printf 'passwd: files\ngroup: files\nshadow: files\nhosts: files dns\n' \
|
||||
> /etc/nsswitch.conf
|
||||
|
||||
# Unprivileged, but it must land in the host's libvirt group to reach the
|
||||
# socket: `group_add` in the compose file does that at run time.
|
||||
RUN useradd --system --create-home --uid 10001 app
|
||||
# Runs as root, on purpose. libvirt resolves the calling uid to a user record
|
||||
# before it even opens the socket, and refused to do so for a custom uid here
|
||||
# ("Failed to find user record for uid") even with /etc/passwd and the libvirt
|
||||
# group both in place. Root always resolves.
|
||||
#
|
||||
# The privilege actually gained is small: any process that can reach the libvirt
|
||||
# socket already starts and destroys the VMs of this host, which is the whole
|
||||
# point of this container. It holds no other mount and no secret beyond the
|
||||
# database password.
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=backend /build/target/release/app-template /usr/local/bin/vm-selector
|
||||
COPY --from=frontend /build/dist/ /app/frontend/
|
||||
COPY docker/config.yml /etc/app-template/config.yml
|
||||
|
||||
USER app
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -196,24 +196,22 @@ cargo sqlx prepare # dev database must be up and migrated
|
|||
|
||||
### Portainer stack
|
||||
|
||||
Deploy `docker-compose.yml` and set the four stack variables it documents at the top
|
||||
(`IMAGE`, `POSTGRES_PASSWORD`, `LIBVIRT_GID`, `APP_PORT`). Get the group id from the
|
||||
host:
|
||||
|
||||
```bash
|
||||
getent group libvirt | cut -d: -f3
|
||||
```
|
||||
Deploy `docker-compose.yml` and set the stack variables it documents at the top
|
||||
(`IMAGE`, `POSTGRES_PASSWORD`, `APP_PORT`).
|
||||
|
||||
Then create a webhook on the stack, **tick "Re-pull image"**, and put its url in the
|
||||
`PORTAINER_WEBHOOK` secret of the repository. Without the re-pull, the webhook
|
||||
recreates the containers from the layer already on disk and nothing changes.
|
||||
|
||||
The two things that make libvirt reachable from inside the container:
|
||||
What makes libvirt reachable from inside the container: `/var/run/libvirt` is
|
||||
bind-mounted from the host — the whole directory, since the socket is `libvirt-sock`
|
||||
or `virtqemud-sock` depending on the host's setup — and the container runs as root.
|
||||
|
||||
- `/var/run/libvirt` bind-mounted from the host — the whole directory, since the
|
||||
socket is `libvirt-sock` or `virtqemud-sock` depending on the host's setup
|
||||
- the container in the host's `libvirt` group (`group_add`) — without it the socket
|
||||
is visible and every call is denied
|
||||
Running as root is not laziness about the group: with a custom uid, libvirt resolves
|
||||
the caller to a user record before opening the socket and refused to do so here even
|
||||
with `/etc/passwd` correct and the `libvirt` group added through `group_add`
|
||||
(`Failed to find user record for uid '10001'`). What root actually adds is small —
|
||||
anything that reaches this socket already controls every VM on the host.
|
||||
|
||||
### Domain name
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
# Environment variables to set in the Portainer stack:
|
||||
# IMAGE registry.tibiscuit.ch/vm-selector:latest
|
||||
# POSTGRES_PASSWORD anything, it never leaves the internal network
|
||||
# LIBVIRT_GID the host's libvirt group id, from:
|
||||
# getent group libvirt | cut -d: -f3
|
||||
# APP_PORT host port for the reverse proxy to point at (default 3010)
|
||||
|
||||
services:
|
||||
|
|
@ -59,10 +57,8 @@ services:
|
|||
# runs the monolithic libvirtd or the split daemons, the app needs
|
||||
# libvirt-sock or virtqemud-sock.
|
||||
- /var/run/libvirt:/var/run/libvirt
|
||||
# Reaching that socket requires being in the host's libvirt group. Without
|
||||
# this the socket is visible and every call is denied.
|
||||
group_add:
|
||||
- '${LIBVIRT_GID:?see the header of this file}'
|
||||
# No `group_add`: the container runs as root (see the Dockerfile for why), so
|
||||
# it needs no group membership to reach the socket.
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue