Files
infra/users/ai-worker.nix
Hermes 9459839d74 fix: restrict docker commands for ai-worker user
Remove ai-worker from docker group and enforce sudo whitelist.

SECURITY: Being in the docker group gives unrestricted access to the
Docker daemon socket (/var/run/docker.sock), allowing any docker command:
docker exec, docker cp, docker run -v /:/host, docker commit, etc.

Changes:
- Remove extraGroups = ["docker"] from ai-worker user definition
- Add comprehensive sudo NOPASSWD whitelist for safe docker subcommands
  ALLOWED: ps, inspect, logs, images, info, version, stats, start, stop,
  restart, rm, rmi, wait, pull, build, run, compose, system,
  network ls, volume ls
  BLOCKED (implicitly): exec, cp, commit, diff, export, import, load,
  save, attach, push, tag, create, plugin, network create, volume create
- Update ai-worker-restricted.nix module to reflect new approach
- Update README-ai-worker.md with new security model and examples

All docker commands must now be prefixed with sudo.
The Hermes agent's host_run tool needs to be updated to prepend sudo.
2026-05-20 20:34:19 -04:00

206 lines
6.9 KiB
Nix

{ pkgs, inputs, config, keys, ... }: {
users.users.ai-worker = {
isSystemUser = true;
group = "ai-worker";
home = "/home/ai-worker";
createHome = true;
# SECURITY: ai-worker is NOT in the docker group.
# Docker access is restricted via sudo whitelist — only specific subcommands allowed.
# extraGroups = [ "docker" ]; — REMOVED: docker group gives unrestricted docker daemon access
shell = pkgs.bashInteractive;
openssh.authorizedKeys.keys = [
keys.users.ai-worker.main
];
# No password login - SSH key only
hashedPassword = "!";
};
users.groups.ai-worker = {};
# Enable restricted AI worker SSH access for ollama benchmarking
# SECURITY: ai-worker can only:
# - SSH into host from Hermes container
# - Run docker commands via sudo (whitelist below — no exec/cp/commit)
# - Run specific security audit commands
# - NO access to infra repo (no bind mount)
# - NO nix/nixos-rebuild/nh commands
# WORKFLOW: SSH from Hermes container, run docker commands via sudo, return and save results
services.aiWorkerAccess = true;
# Restricted sudo for ai-worker
# IMPORTANT: ai-worker is NOT in docker group. All docker access goes through sudo.
# Only the subcommands listed below are allowed — everything else is denied.
# This prevents: docker exec, docker cp, docker commit, and other file-modifying operations.
security.sudo.extraRules = [
{
users = [ "ai-worker" ];
commands = [
# === Docker commands: lifecycle management (NO file modification) ===
# ps/inspect/logs — read-only status checks
{
command = "/run/current-system/sw/bin/docker ps";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker inspect *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker logs *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker images";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker info";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker version";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker stats *";
options = [ "NOPASSWD" ];
}
# start/stop/restart — container lifecycle
{
command = "/run/current-system/sw/bin/docker start *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker stop *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker restart *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker rm *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker rmi *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker wait *";
options = [ "NOPASSWD" ];
}
# pull/build/run — image management and container creation
{
command = "/run/current-system/sw/bin/docker pull *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker build *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker run *";
options = [ "NOPASSWD" ];
}
# compose — orchestration
{
command = "/run/current-system/sw/bin/docker compose *";
options = [ "NOPASSWD" ];
}
# system — disk cleanup
{
command = "/run/current-system/sw/bin/docker system *";
options = [ "NOPASSWD" ];
}
# network — list only (create/modify not needed)
{
command = "/run/current-system/sw/bin/docker network ls";
options = [ "NOPASSWD" ];
}
# volume — list only (create/modify not needed)
{
command = "/run/current-system/sw/bin/docker volume ls";
options = [ "NOPASSWD" ];
}
# === EXPLICITLY DENIED docker commands (not in whitelist — sudo rejects them) ===
# docker exec — executes arbitrary commands inside running containers (FILE MODIFICATION)
# docker cp — copies files between containers and host (FILE ACCESS)
# docker commit — creates images from running containers (DATA EXFIL)
# docker diff — inspects filesystem changes (INFO LEAK)
# docker export — exports container filesystem (DATA EXFIL)
# docker import — imports filesystem archives
# docker load — loads docker images
# docker save — saves docker images to tar (DATA EXFIL)
# docker attach — attaches to running containers (INTERACTIVE ACCESS)
# docker push — pushes images to registries (DATA EXFIL)
# docker tag — renames images
# docker create — creates containers (use 'docker run' instead)
# docker plugin — manages plugins
# docker network create/rm — network management
# docker volume create/rm — volume management
# === Firewall checks ===
{
command = "/run/wrappers/bin/sudo iptables -L -n -v";
options = [ "NOPASSWD" ];
}
{
command = "/run/wrappers/bin/sudo iptables -S";
options = [ "NOPASSWD" ];
}
# === Fail2ban status ===
{
command = "/run/current-system/sw/bin/fail2ban-client status";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/fail2ban-client status *";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/fail2ban-client get * banned";
options = [ "NOPASSWD" ];
}
# === Log inspection ===
{
command = "/run/current-system/sw/bin/journalctl -t kernel -n 100";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/journalctl -u fail2ban -n 50";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/journalctl -u firewall -n 50";
options = [ "NOPASSWD" ];
}
# === SSH config verification ===
{
command = "/run/current-system/sw/bin/sshd -T";
options = [ "NOPASSWD" ];
}
# === Network diagnostics ===
{
command = "/run/current-system/sw/bin/ss -tlnp";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/cat /proc/net/tcp";
options = [ "NOPASSWD" ];
}
];
}
];
}