feat: add openclaw sandboxed user, sudo policy, fs restrictions, docker proxy, watchdog
This commit is contained in:
6
modules/openclaw-docker-env.nix
Normal file
6
modules/openclaw-docker-env.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
environment.variables = {
|
||||||
|
DOCKER_HOST = "tcp://127.0.0.1:2375";
|
||||||
|
};
|
||||||
|
}
|
||||||
32
modules/openclaw-docker.nix
Normal file
32
modules/openclaw-docker.nix
Normal 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
29
modules/openclaw-fs.nix
Normal 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
17
modules/openclaw-sudo.nix
Normal 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
15
modules/openclaw-user.nix
Normal 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 = { };
|
||||||
|
}
|
||||||
82
modules/openclaw-watchdog.nix
Normal file
82
modules/openclaw-watchdog.nix
Normal 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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user