opsgateApprovalWatchToolsConfigurationGitHub

Host provider

한국어

The optional host provider brings opsgate's flow (alert, choices, approve, applied) to a server without Kubernetes: systemd units and Docker or Compose containers on the machine opsgate runs on. It is off by default and registers nothing until you enable it and list the units or containers it may see.

Use it on a VPS or home server where you would otherwise SSH in to run systemctl restart or docker restart. Do not enable it inside a Kubernetes pod: mode: kubernetes refuses it, and opsgate warns if it finds itself in a pod anyway.

Configuration

providers:
  host:
    enabled: true
    poll_interval: 30s            # opsgate watch polling, 10s–5m
    systemd:
      units: ["app-*.service", nginx.service]   # visible units (path.Match globs)
      restart: ["app-*.service"]                # units a person may restart
    docker:
      endpoint: unix:///run/docker-proxy/docker.sock   # or http://127.0.0.1:2375
      containers: [web, "compose:shop/*"]       # names, or compose:<project>/<service>
      restart: ["compose:shop/*"]

Tools

Tool Kind What it does
host_units read Load, active and sub state, last result, restart count and state change time of the visible units
host_unit_logs read Masked journal tail of one visible unit (lines, default 100, max 500)
host_restart_unit write systemctl --no-ask-password restart -- <unit> after approval
host_containers read Image, state, health, exit code, restart count and Compose labels of the visible containers
host_container_logs read Masked, demultiplexed log tail of one visible container
host_restart_container write POST /containers/{id}/restart?t=10 after approval; the plan shows the full container ID and the restart is refused if the container was recreated since

Both restarts are catalog actions, so they can be offered as links, on the choices page and by the analyzer. They have no undo.

Names are validated before use: unit names must look like name.service (or another unit type), container names like [A-Za-z0-9][A-Za-z0-9_.-]*. Commands run with fixed argv, never through a shell, with a 15s timeout and a 4 MiB output cap. Logs are masked before they are cut to the requested length.

Choices and alerts

/approve/host-options/{unit|container}/{name} shows the current choices after sign-in, like the Kubernetes choices page:

Target Offered when Action
systemd unit it failed, or it has restarted host_restart_unit
Docker container it is unhealthy, restarting or exited with a non-zero code, or it has restarted host_restart_container

With the host provider enabled, opsgate watch polls every poll_interval and sends a warning when a unit fails (UnitFailed) or restarts (UnitRestarted), or a container becomes unhealthy (ContainerUnhealthy), exits with an error (ContainerExited) or restarts (ContainerRestarted). The first poll only records the current state, so starting watch does not repeat old problems. A recreated container (new ID) starts a new history.

Host alerts use host as both cluster and namespace, so a watch group with namespaces: [host] routes them. With watch.approval_url they carry a Choose an action link, and with watch.inbox they reach the approval inbox and push like pod warnings. The optional analyzer does not investigate host alerts yet; its tool allowlist covers Kubernetes and notes only.

Privileges

opsgate runs as an unprivileged opsgate account. Nothing in the host provider needs root, sudo or extra capabilities.

systemd. Reading unit state needs no privilege. Reading journals needs the systemd-journal group. A restart goes through systemd's D-Bus API, which asks polkit; install deploy/host/50-opsgate.rules and list the exact units the opsgate account may restart. The rule allows the restart verb on those units only. Because no setuid program is involved, the service keeps NoNewPrivileges=yes; see deploy/host/opsgate.service and opsgate-watch.service.

Docker. Access to the Docker socket is equivalent to root on the host: it can start a privileged container that mounts /. Never give opsgate the Docker socket. Run a socket proxy that forwards only these calls, and point providers.host.docker.endpoint at it:

Method Path (optionally prefixed with /v1.xx) Used by
GET /containers/json listing
GET /containers/{id or name}/json inspect
GET /containers/{id}/logs host_container_logs
POST /containers/{id}/restart host_restart_container

Any proxy that filters by method and path works; for example wollomatic/socket-proxy takes one regular expression per method. Check the flags against the version you install; an illustrative configuration:

socket-proxy \
  -proxysocketendpoint=/run/docker-proxy/docker.sock -proxysocketendpointfilemode=0660 \
  '-allowGET=^(/v[0-9.]+)?/containers/(json|[A-Za-z0-9][A-Za-z0-9_.-]*/(json|logs))$' \
  '-allowPOST=^(/v[0-9.]+)?/containers/[0-9a-f]{64}/restart$'

Give the proxy socket to a group that the opsgate account belongs to, and test that docker run through the proxy is refused before relying on it.

Security notes