feat: add openclaw sandboxed user, sudo policy, fs restrictions, docker proxy, watchdog

This commit is contained in:
2026-03-14 17:02:37 +02:00
parent 2a16184fba
commit eebf3f6159
6 changed files with 181 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
{ ... }:
{
environment.variables = {
DOCKER_HOST = "tcp://127.0.0.1:2375";
};
}

View File

@@ -0,0 +1,32 @@
{ pkgs, ... }:
{
virtualisation.oci-containers.containers.docker-socket-proxy = {
image = "tecnativa/docker-socket-proxy:latest";
autoStart = true;
volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ];
environment = {
CONTAINERS = "1";
IMAGES = "1";
NETWORKS = "1";
VOLUMES = "1";
INFO = "1";
POST = "1";
BUILD = "1";
COMMIT = "0";
CONFIGS = "0";
DISTRIBUTION = "0";
EXEC = "0";
GRPC = "0";
PLUGINS = "0";
SECRETS = "0";
SERVICES = "0";
SESSION = "0";
SWARM = "0";
SYSTEM = "0";
TASKS = "0";
AUTH = "0";
ALLOW_RESTARTS = "1";
};
ports = [ "127.0.0.1:2375:2375" ];
};
}

29
modules/openclaw-fs.nix Normal file
View File

@@ -0,0 +1,29 @@
{ ... }:
{
fileSystems =
let
bindRO = src: {
device = src;
fsType = "none";
options = [ "bind" "ro" ];
};
bindHide = src: {
device = "tmpfs";
fsType = "tmpfs";
options = [ "size=0" "mode=000" ];
};
in
{
"/home/openclaw/private/AT Protocol" = bindHide "/private/AT Protocol";
"/home/openclaw/private/cloudflared" = bindHide "/private/cloudflared";
"/home/openclaw/private/vaultwarden" = bindHide "/private/vaultwarden";
"/home/openclaw/protected" = bindHide "/protected";
};
systemd.tmpfiles.rules = [
"d /home/openclaw/private 0750 openclaw openclaw -"
"d /home/openclaw/protected 0000 root root -"
"f /home/openclaw/private/tangled.env 0000 root root -"
"f /home/openclaw/private/cloudflared.pem 0000 root root -"
];
}

17
modules/openclaw-sudo.nix Normal file
View File

@@ -0,0 +1,17 @@
{
security.sudo.extraRules = [
{
users = [ "openclaw" ];
commands = [
{
command = "/run/current-system/sw/bin/cat";
options = [ "NOPASSWD" ];
}
{
command = "/run/current-system/sw/bin/docker";
options = [ "NOPASSWD" ];
}
];
}
];
}

15
modules/openclaw-user.nix Normal file
View File

@@ -0,0 +1,15 @@
{ pkgs, ... }:
{
users.users.openclaw = {
isSystemUser = false;
isNormalUser = true;
home = "/home/openclaw";
createHome = true;
group = "openclaw";
extraGroups = [ "docker" ];
shell = pkgs.bash;
description = "OpenClaw agent sandboxed user";
};
users.groups.openclaw = { };
}

View File

@@ -0,0 +1,82 @@
{ pkgs, ... }:
{
systemd.services.openclaw-watchdog = {
description = "Post-rebuild health watchdog";
after = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "/etc/openclaw/nixos-rollback.sh check";
};
onFailure = [ "nixos-rollback.service" ];
};
systemd.services.nixos-rollback = {
description = "Autonomous NixOS rollback";
serviceConfig = {
Type = "oneshot";
ExecStart = "/etc/openclaw/nixos-rollback.sh rollback";
};
};
environment.etc."openclaw/nixos-rollback.sh" = {
mode = "0750";
text = ''
#!/usr/bin/env bash
set -euo pipefail
WEBHOOK="$(cat /run/secrets/discord-webhook 2>/dev/null || echo "")"
UNITS=("sshd" "docker" "bluesky-pds" "cloudflared")
HOSTNAME="$(hostname)"
notify() {
[ -z "$WEBHOOK" ] && return
curl -s -X POST "$WEBHOOK" \
-H "Content-Type: application/json" \
-d "{\"content\": \"$1\"}"
}
check_units() {
for unit in "''${UNITS[@]}"; do
if ! systemctl is-active --quiet "$unit"; then
return 1
fi
done
return 0
}
check_ssh() {
timeout 5 bash -c 'echo > /dev/tcp/127.0.0.1/22' 2>/dev/null
}
do_check() {
for i in $(seq 1 6); do
sleep 10
if check_units && check_ssh; then
notify "**[$HOSTNAME] NixOS switch healthy** all units OK after rebuild."
exit 0
fi
done
exit 1
}
do_rollback() {
notify "**[$HOSTNAME] ROLLBACK TRIGGERED** health check failed. Rolling back..."
if nixos-rebuild switch --rollback; then
sleep 15
if check_units && check_ssh; then
notify "**[$HOSTNAME] Rollback successful** previous generation restored."
else
notify "**[$HOSTNAME] URGENT rollback also failed.** Manual intervention needed."
fi
else
notify "**[$HOSTNAME] URGENT rollback command failed.** Manual intervention needed."
fi
}
case "''${1:-check}" in
check) do_check ;;
rollback) do_rollback ;;
esac
'';
};
}