feat: change openclaw to a vm and give read access to a db

This commit is contained in:
Dominik Polakovics Polakovics 2026-02-09 03:06:46 +01:00
parent 5847c04acd
commit f3ef4ff11c
10 changed files with 372 additions and 75 deletions

View file

@ -38,7 +38,8 @@
./modules/ai-mailer.nix
# ./modules/wazuh.nix
./modules/openclaw.nix
# ./modules/openclaw.nix # Container: gateway/webchat on .97.60
./modules/openclaw-vm.nix # VM: daemon/onboarding on .97.61
# web
./modules/web
@ -71,8 +72,8 @@
./modules/setupnetwork.nix
./modules/set-nix-channel.nix # Automatically manage nix-channel from /var/bento/channel
./modules/grafana-monitor.nix # Grafana online status monitor
./hardware-configuration.nix
];
@ -100,8 +101,8 @@
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver # VAAPI driver (iHD) for modern Intel GPUs
vpl-gpu-rt # Intel VPL/QSV runtime for Gen 12+ (N100)
intel-media-driver # VAAPI driver (iHD) for modern Intel GPUs
vpl-gpu-rt # Intel VPL/QSV runtime for Gen 12+ (N100)
intel-compute-runtime # OpenCL support for tone-mapping
];
};
@ -114,15 +115,15 @@
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.defaultSopsFile = ./secrets.yaml;
environment.systemPackages = with pkgs; [
bento
conntrack-tools # view network connection states
ethtool # manage NIC settings (offload, NIC feeatures, ...)
conntrack-tools # view network connection states
ethtool # manage NIC settings (offload, NIC feeatures, ...)
git
htop # to see the system load
tcpdump # view network traffic
vim # my preferred editor
htop # to see the system load
tcpdump # view network traffic
vim # my preferred editor
wol
inotify-tools
];

View file

@ -5,7 +5,7 @@
enable = true;
settings = {
port = "53";
bind-interfaces = true; # force dnsmasq to bind immediately
bind-interfaces = true; # force dnsmasq to bind immediately
expand-hosts = true;
log-dhcp = true;
@ -67,7 +67,7 @@
dhcp-host = [
"24:df:a7:b1:1b:74,${config.networkPrefix}.96.101,rmproplus-b1-1b-74"
"30:05:5c:56:62:37,${config.networkPrefix}.99.100,brn30055c566237"
"1a:c4:04:6e:29:bd,${config.networkPrefix}.97.2,omada"
"02:00:00:00:00:04,${config.networkPrefix}.97.6,matrix"
@ -85,6 +85,8 @@
"cc:50:e3:bc:27:64,${config.networkPrefix}.100.112,Nuki_Bridge_1A753F72"
"34:6f:24:f3:af:ad,${config.networkPrefix}.100.137,daikin86604"
"34:6f:24:c1:f8:54,${config.networkPrefix}.100.139,daikin53800"
"02:00:00:00:03:01,${config.networkPrefix}.97.61,openclaw-vm"
];
address = [
@ -92,13 +94,13 @@
"/omada.cloonar.com/${config.networkPrefix}.97.2"
"/web-02.cloonar.com/${config.networkPrefix}.97.5"
"/pla.cloonar.com/${config.networkPrefix}.97.5"
"/piped.cloonar.com/${config.networkPrefix}.97.5" # Replaced by Invidious
"/pipedapi.cloonar.com/${config.networkPrefix}.97.5" # Replaced by Invidious
"/piped.cloonar.com/${config.networkPrefix}.97.5" # Replaced by Invidious
"/pipedapi.cloonar.com/${config.networkPrefix}.97.5" # Replaced by Invidious
"/invidious.cloonar.com/${config.networkPrefix}.97.5"
"/fivefilters.cloonar.com/${config.networkPrefix}.97.5"
"/n8n.cloonar.com/${config.networkPrefix}.97.5"
"/dev.cloonar.com/${config.networkPrefix}.97.15"
"/.ddev.site/${config.networkPrefix}.97.15" # Wildcard for ddev projects
"/.ddev.site/${config.networkPrefix}.97.15" # Wildcard for ddev projects
"/home-assistant.cloonar.com/${config.networkPrefix}.97.20"
"/mopidy.cloonar.com/${config.networkPrefix}.97.21"
"/snapcast.cloonar.com/${config.networkPrefix}.97.21"
@ -167,13 +169,15 @@
"/bath-bulb-0.cloonar.smart/${config.networkPrefix}.100.42"
"/paraclub.at/188.34.191.144"
"/openclaw-vm.cloonar.com/${config.networkPrefix}.97.61"
];
};
};
systemd.services.dnsmasq = {
requires = [ "network-online.target" ];
after = [ "network-online.target" ];
after = [ "network-online.target" ];
};
networking.firewall.allowedUDPPorts = [ 53 67 ];

View file

@ -0,0 +1,206 @@
# OpenClaw VM — Ubuntu 24.04 QEMU guest for interactive `openclaw onboard` setup.
# Runs alongside the Podman container (openclaw.nix on .97.60) on a separate IP (.97.61).
# The container serves the gateway/webchat; this VM is for the daemon/onboarding workflow.
{ config, pkgs, lib, ... }:
let
vmName = "openclaw-vm";
vmStateDir = "/var/lib/${vmName}";
vmMac = "02:00:00:00:03:01";
vmIp = "${config.networkPrefix}.97.61";
gateway = "${config.networkPrefix}.97.1";
tapDevice = "vm-openclaw";
sshAuthorizedKeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
gitRepoUrl = "https://git.cloonar.com/openclawd/config.git";
# Cloud-init user-data
userData = pkgs.writeText "user-data" ''
#cloud-config
users:
- name: openclaw
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL # needed for openclaw onboard --install-daemon; VM is internal-only, SSH-key-only
groups: [sudo]
ssh_authorized_keys:
${lib.concatMapStringsSep "\n " (k: "- ${k}") sshAuthorizedKeys}
package_update: true
package_upgrade: true
packages:
- git
- curl
- build-essential
runcmd:
- [bash, /var/lib/cloud/scripts/setup-openclaw.sh]
write_files:
- path: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
content: |
network: {config: disabled}
- path: /var/lib/cloud/scripts/setup-openclaw.sh
permissions: "0755"
content: |
#!/bin/bash
set -euxo pipefail
# Wait for network to be fully ready
for i in $(seq 1 30); do
if curl -fsS --max-time 5 https://nodejs.org/ >/dev/null 2>&1; then
break
fi
echo "Waiting for network... ($i/30)"
sleep 2
done
# Install Node.js 22 from official binary tarball
NODE_MAJOR=22
NODE_VERSION=$(curl -fsSL "https://nodejs.org/dist/latest-v''${NODE_MAJOR}.x/" | grep -oP 'node-v\K[0-9.]+' | head -1)
curl -fsSL "https://nodejs.org/dist/v''${NODE_VERSION}/node-v''${NODE_VERSION}-linux-x64.tar.xz" \
| tar -xJf - -C /usr/local --strip-components=1
# Verify node and npm are available
node --version
npm --version
# Install OpenClaw globally
npm install -g openclaw@latest
# Clone the repo as openclaw user
mkdir -p /home/openclaw/.openclaw
su - openclaw -c 'git clone ${gitRepoUrl} /home/openclaw/.openclaw/workspace'
'';
# Cloud-init meta-data
metaData = pkgs.writeText "meta-data" ''
instance-id: ${vmName}
local-hostname: ${vmName}
'';
# Cloud-init network-config (netplan v2, MAC-based match)
networkConfig = pkgs.writeText "network-config" ''
network:
version: 2
ethernets:
id0:
match:
macaddress: "${vmMac}"
addresses: [${vmIp}/24]
routes:
- to: default
via: ${gateway}
nameservers:
addresses: [${gateway}]
'';
ubuntuImageUrl = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img";
ubuntuImageName = "noble-server-cloudimg-amd64.img";
in
{
# Ensure KVM is available
boot.kernelModules = [ "kvm-intel" ];
# State directory
systemd.tmpfiles.rules = [
"d ${vmStateDir} 0755 root root - -"
];
# Init service: download cloud image, create disk, generate seed ISO
systemd.services."${vmName}-init" = {
description = "Initialize ${vmName} disk and cloud-init seed";
wantedBy = [ "multi-user.target" ];
before = [ "${vmName}.service" ];
requiredBy = [ "${vmName}.service" ];
path = with pkgs; [ curl qemu_kvm cdrkit ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
mkdir -p "${vmStateDir}"
# Download Ubuntu cloud image if not present
if [ ! -f "${vmStateDir}/${ubuntuImageName}" ]; then
echo "Downloading Ubuntu 24.04 cloud image..."
curl -fSL -o "${vmStateDir}/${ubuntuImageName}" "${ubuntuImageUrl}"
fi
# Create qcow2 disk from cloud image if not present
if [ ! -f "${vmStateDir}/disk.qcow2" ]; then
echo "Creating qcow2 disk from cloud image..."
qemu-img convert -f qcow2 -O qcow2 "${vmStateDir}/${ubuntuImageName}" "${vmStateDir}/disk.qcow2"
qemu-img resize "${vmStateDir}/disk.qcow2" 20G
fi
# Always regenerate seed ISO (picks up config changes)
# Cloud-init expects files named user-data, meta-data, network-config
echo "Generating cloud-init seed ISO..."
tmpdir=$(mktemp -d)
cp ${userData} "$tmpdir/user-data"
cp ${metaData} "$tmpdir/meta-data"
cp ${networkConfig} "$tmpdir/network-config"
genisoimage -output "${vmStateDir}/seed.iso" \
-volid cidata -joliet -rock \
"$tmpdir/user-data" "$tmpdir/meta-data" "$tmpdir/network-config"
rm -rf "$tmpdir"
'';
};
# QEMU VM service
systemd.services."${vmName}" = {
description = "OpenClaw QEMU VM";
wantedBy = [ "multi-user.target" ];
after = [ "${vmName}-init.service" "network-online.target" ];
requires = [ "${vmName}-init.service" ];
wants = [ "network-online.target" ];
path = with pkgs; [ iproute2 qemu_kvm ];
serviceConfig = {
Type = "simple";
Restart = "on-failure";
RestartSec = 10;
ExecStartPre = pkgs.writeShellScript "${vmName}-tap-setup" ''
set -euo pipefail
# Clean up stale TAP device if it exists
if ip link show ${tapDevice} &>/dev/null; then
ip link set ${tapDevice} down || true
ip link delete ${tapDevice} || true
fi
# Create TAP device and attach to server bridge
ip tuntap add dev ${tapDevice} mode tap
ip link set ${tapDevice} master server
ip link set ${tapDevice} up
'';
ExecStart = lib.concatStringsSep " " [
"${pkgs.qemu_kvm}/bin/qemu-system-x86_64"
"-machine type=q35,accel=kvm"
"-cpu host"
"-smp 2"
"-m 4096"
"-drive file=${vmStateDir}/disk.qcow2,format=qcow2,if=virtio"
"-drive file=${vmStateDir}/seed.iso,format=raw,if=virtio,media=cdrom"
"-netdev tap,id=net0,ifname=${tapDevice},script=no,downscript=no"
"-device virtio-net-pci,netdev=net0,mac=${vmMac}"
"-nographic"
"-serial mon:stdio"
];
ExecStopPost = pkgs.writeShellScript "${vmName}-tap-cleanup" ''
if ip link show ${tapDevice} &>/dev/null; then
ip link set ${tapDevice} down || true
ip link delete ${tapDevice} || true
fi
'';
};
};
}

View file

@ -33,6 +33,10 @@
publicKey = "tLsvuXo6Cp8tzjJau1yJZ9apeQvYa+cGrnAXBBifO3Y=";
allowedIPs = [ "${config.networkPrefix}.98.205/32" ];
}
{ # web-arm — replace publicKey after generating keypair
publicKey = "VjbYy4qKkFrjxxNU+fCam+FycBOkjNiB0t5YOOCIpFU=";
allowedIPs = [ "${config.networkPrefix}.98.10/32" ];
}
];
};
wg_epicenter = {