Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ jobs:
- name: Set up Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to Docker Hardened Images (dhi.io)
uses: docker/login-action@v4
with:
registry: dhi.io
username: ${{ vars.DHI_USERNAME }}
password: ${{ secrets.DHI_TOKEN }}

- name: Log in to GHCR
uses: docker/login-action@v4
with:
Expand Down
6 changes: 6 additions & 0 deletions .mk/docker-run.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
# - host /dev tree (to stat device major/minor numbers)
# - host cgroup, pid, and user namespaces, --privileged
#
# Optional: mount the host DBus socket to enable systemctl daemon-reload
# re-apply. dhi.io/static has no /var/run→/run symlink, so the container-side
# path must be /var/run/dbus/system_bus_socket (not /run/dbus/...).
#
# Override DOCKER_RUN_ARGS to pass extra daemon flags:
# make docker-run DOCKER_RUN_ARGS="-log-level debug -log-time"

DOCKER_SOCKET ?= /var/run/docker.sock
DBUS_SOCKET ?= /run/dbus/system_bus_socket
DOCKER_RUN_ARGS ?= -log-level info -log-time

.PHONY: docker-run
Expand All @@ -25,5 +30,6 @@ docker-run: ## Run the image locally with host cgroup/pid namespaces and require
-v "$(DOCKER_SOCKET):/var/run/docker.sock" \
-v /sys:/host/sys \
-v /dev:/dev \
$(if $(wildcard $(DBUS_SOCKET)),-v "$(DBUS_SOCKET):/var/run/dbus/system_bus_socket") \
"$(IMAGE_REPO):$(IMAGE_TAG)" \
$(DOCKER_RUN_ARGS)
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ with care; the upstream PRs went into runc/containerd long ago.**

The daemon **must** run with `privileged: true`, `cgroup: host`, `pid: host`, `userns_mode: host`, and bind mounts for `/var/run/docker.sock` and
`/sys → /host/sys`. The `hostRootPath = "/host"` constant in `main.go` is the inside-container view of the host root; cgroup paths are joined against
it. The DBus socket mount (`/run/dbus/system_bus_socket`) is optional — enables reload handling.
it. The DBus socket mount is optional — enables reload handling. Mount as `-v /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket`; the container-side path must be under `/var/run/` because `dhi.io/static` has no `/var/run → /run` symlink.

## Conventions worth knowing

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ services:
- -v
- /dev:/dev
# Optional: reapply device rules after systemctl daemon-reload.
# NOTE: dhi.io/static has no /var/run→/run symlink; use /var/run/dbus/... inside the container.
# - -v
# - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket
# - /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
- ghcr.io/leinardi/swarm-device-access:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Expand Down
17 changes: 9 additions & 8 deletions deployments/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
########################
# deps: cache modules
########################
FROM --platform=$BUILDPLATFORM golang:1.26.3-alpine AS deps
FROM --platform=$BUILDPLATFORM dhi.io/golang:1-alpine3.23-dev AS deps
WORKDIR /src
RUN --mount=type=cache,target=/var/cache/apk \
apk add --no-cache ca-certificates git
apk add --no-cache git
COPY go.mod go.sum ./
# Use a named cache so we can reuse it in the build stage (no COPY needed)
RUN --mount=type=cache,target=/go/pkg/mod,id=gomodcache \
Expand All @@ -15,7 +15,7 @@ RUN --mount=type=cache,target=/go/pkg/mod,id=gomodcache \
########################
# build: compile static binary
########################
FROM --platform=$BUILDPLATFORM golang:1.26.3-alpine AS build
FROM --platform=$BUILDPLATFORM dhi.io/golang:1-alpine3.23-dev AS build
ARG VERSION="dev"
ARG COMMIT="none"
ARG DATE="unknown"
Expand All @@ -29,8 +29,6 @@ ENV CGO_ENABLED=0 \
GOARCH=${TARGETARCH}

WORKDIR /src
RUN --mount=type=cache,target=/var/cache/apk \
apk add --no-cache ca-certificates

# Bring in source
COPY . .
Expand All @@ -43,10 +41,10 @@ RUN --mount=type=cache,target=/go/pkg/mod,id=gomodcache \
-o /out/swarm-device-access ./cmd/swarm-device-access

########################
# runtime: alpine (root by default, required for cgroup BPF syscalls;
# includes a shell + busybox utilities for in-container debugging).
# runtime: dhi.io hardened static base (distroless-style, no shell).
# The daemon binary is CGO_ENABLED=0 so no libc is required.
########################
FROM alpine:3.23.4
FROM dhi.io/static:20250419

# hadolint ignore=DL3050
LABEL org.opencontainers.image.title="swarm-device-access" \
Expand All @@ -68,4 +66,7 @@ COPY --from=build /out/swarm-device-access /usr/local/bin/swarm-device-access
# the container is started with --pid=host (which would place an init shim
# outside PID 1 of its own pid namespace and prevent it from working anyway).

# dhi.io/static defaults to a non-root user; bpf(2) and cgroup writes require root.
# hadolint ignore=DL3002
USER 0
ENTRYPOINT ["/usr/local/bin/swarm-device-access"]
4 changes: 3 additions & 1 deletion deployments/docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ services:
# detect systemctl daemon-reload (which wipes cgroup BPF programs) and
# re-apply device rules. Without this mount, reload handling is skipped
# and the daemon logs a warning at startup.
# NOTE: dhi.io/static has no /var/run→/run symlink; the container-side
# path must be /var/run/dbus/system_bus_socket (not /run/dbus/...).
# - -v
# - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket
# - /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
- ghcr.io/leinardi/swarm-device-access:latest
- "-log-level"
- "info"
Expand Down
5 changes: 2 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ The daemon **must** run as root with:
- `/dev` bind-mounted (so device major/minor can be read via `unix.Stat`)
- `/var/run/docker.sock` bind-mounted

The DBus socket (`/run/dbus/system_bus_socket`) is optional — enables systemd reload handling.
The DBus socket is optional — enables systemd reload handling. Mount as `-v /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket`; the container-side path must be under `/var/run/` because `dhi.io/static` has no `/var/run → /run` symlink.

## Observability

Expand Down Expand Up @@ -217,8 +217,7 @@ level=INFO msg="dry-run: would add device rule" pid=1234 cgroup=/host/sys/fs/cgr

### Daemon does not re-apply rules after `systemctl daemon-reload`

Ensure `/run/dbus/system_bus_socket` is bind-mounted into the daemon container. Without it, the reload watcher is disabled (logged at `Warn` on
startup).
Ensure the host DBus socket is bind-mounted as `-v /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket`. Without it, the reload watcher is disabled (logged at `Warn` on startup). The container-side path must be `/var/run/dbus/system_bus_socket` — `dhi.io/static` has no `/var/run → /run` symlink.

### Daemon cannot connect to Docker

Expand Down
Loading