#cloud-config # OpenClaw Gateway Bootstrap # Update packages package_update: true package_upgrade: true # Install required packages packages: - curl - git - fail2ban - ufw - jq - gnupg - ca-certificates - software-properties-common # Create admin user (if different from root) users: - name: ${admin_user} sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: ${jsonencode(admin_ssh_keys)} groups: [sudo, systemd-journal] # Write system configuration files write_files: # SSH hardening configuration - path: /etc/ssh/sshd_config.d/99-hardening.conf content: | # SSH Security Hardening Port ${ssh_port} PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys ChallengeResponseAuthentication no UsePAM yes X11Forwarding no PrintMotd no AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server permissions: '0644' # Fail2ban SSH configuration %{if enable_fail2ban} - path: /etc/fail2ban/jail.d/sshd.local content: | [sshd] enabled = true port = ${ssh_port} filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 findtime = 600 permissions: '0644' %{endif} # OpenClaw environment file - path: /etc/openclaw.env content: | # Secrets injected during provisioning - DO NOT commit to version control # Inference API keys %{if venice_api_key != ""} VENICE_API_KEY=${venice_api_key} %{endif} %{if brave_search_api_key != ""} BRAVE_SEARCH_API_KEY=${brave_search_api_key} %{endif} # Discord configuration %{if discord_bot_token != ""} DISCORD_BOT_TOKEN=${discord_bot_token} %{endif} %{if discord_server_id != ""} DISCORD_SERVER_ID=${discord_server_id} %{endif} %{if length(discord_user_id) > 0} DISCORD_USER_ID=${jsonencode(discord_user_id)} %{endif} # Tailscale auth key (if enabled) %{if enable_tailscale && tailscale_auth_key != ""} TAILSCALE_AUTH_KEY=${tailscale_auth_key} %{endif} permissions: '0600' # OpenClaw configuration file (uses env var references for secrets) - path: /home/${admin_user}/.openclaw/openclaw.json content: | { "auth": { "profiles": { "venice:default": { "provider": "venice", "mode": "api_key" } } }, "models": { "mode": "merge", "providers": ${models_config} }, "agents": { "defaults": { "model": { "primary": "${primary_model}", "fallbacks": ${fallback_models} }, "workspace": "/home/${admin_user}/.openclaw/workspace" }, "list": [ { "id": "${agent_name}", "default": true, "workspace": "/home/${admin_user}/.openclaw/workspace" } ] }, "tools": { "web": { "search": { "enabled": true, "provider": "brave", "apiKey": "$${BRAVE_SEARCH_API_KEY}" }, "fetch": { "enabled": true } }, "exec": { "security": "allowlist", "ask": "on-miss" } }, "messages": { "queue": { "mode": "collect" }, "ackReactionScope": "all" }, "channels": { "discord": { "enabled": true, "token": "$${DISCORD_BOT_TOKEN}", "groupPolicy": "allowlist", "guilds": { "${discord_server_id}": { "requireMention": false, "users": ${jsonencode(discord_user_id)}, "channels": { "*": { "allow": true } } } } } }, "gateway": { "port": 18789, "mode": "local", "bind": "loopback" } } permissions: '0644' # Systemd service for OpenClaw Gateway - path: /etc/systemd/system/openclaw-gateway.service content: | [Unit] Description=OpenClaw Gateway Service After=network.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/openclaw gateway run Restart=on-failure RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=openclaw-gateway EnvironmentFile=/etc/openclaw.env WorkingDirectory=/home/${admin_user}/.openclaw [Install] WantedBy=multi-user.target permissions: '0644' # Health check script - path: /usr/local/bin/openclaw-health-check.sh content: | #!/bin/bash # OpenClaw Gateway Health Check set -e echo "=== OpenClaw Health Check ===" echo "" # Check if OpenClaw is installed if command -v openclaw &> /dev/null; then echo "✓ OpenClaw installed: $(openclaw --version)" else echo "✗ OpenClaw not found" exit 1 fi # Check if gateway is running if systemctl is-active --quiet openclaw-gateway; then echo "✓ Gateway service running" else echo "✗ Gateway service not running" fi # Check if gateway is listening if lsof -i :18789 > /dev/null 2>&1; then echo "✓ Gateway listening on port 18789" else echo "✗ Gateway not listening on port 18789" fi # Check firewall if ufw status | grep -q "Status: active"; then echo "✓ Firewall active" else echo "⚠ Firewall not active" fi # Check fail2ban %{if enable_fail2ban} if systemctl is-active --quiet fail2ban; then echo "✓ fail2ban running" else echo "⚠ fail2ban not running" fi %{endif} echo "" echo "=== End Health Check ===" permissions: '0755' %{if enable_swap} # Swap creation script - path: /usr/local/bin/create-swap.sh content: | #!/bin/bash set -e if [ ! -f /swapfile ]; then fallocate -l ${swap_size}G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab echo "Created ${swap_size}GB swap" else echo "Swap already exists" fi permissions: '0755' %{endif} %{if enable_tailscale} # Tailscale setup script - path: /usr/local/bin/setup-tailscale.sh content: | #!/bin/bash set -e curl -fsSL https://tailscale.com/install.sh | sh tailscale up --authkey=${tailscale_auth_key} --ssh # Wait for Tailscale to connect sleep 5 # Enable Tailscale Serve for gateway access # This exposes the gateway on the tailnet via HTTPS tailscale serve --bg 18789 || echo "Note: Tailscale serve may require 'Serve' to be enabled in admin console" echo "Tailscale configured and serving on port 18789" permissions: '0755' %{endif} # Run commands runcmd: # Create admin user home directory - mkdir -p /home/${admin_user} - chown ${admin_user}:${admin_user} /home/${admin_user} %{if enable_swap} # Create swapfile - /usr/local/bin/create-swap.sh %{endif} # Install Node.js ${node_version} - curl -fsSL https://deb.nodesource.com/setup_${node_version}.x | bash - - apt-get install -y nodejs - node --version - npm --version # Install OpenClaw %{if openclaw_version == "latest"} - curl -fsSL https://openclaw.ai/install.sh | bash %{else} %{if openclaw_version == "lts"} - curl -fsSL https://openclaw.ai/install.sh | bash -s -- --version lts %{else} - curl -fsSL https://openclaw.ai/install.sh | bash -s -- --version ${openclaw_version} %{endif} %{endif} - openclaw --version || echo "OpenClaw installed, needs configuration" # Create OpenClaw config directory - mkdir -p /home/${admin_user}/.openclaw/workspace - chown -R ${admin_user}:${admin_user} /home/${admin_user}/.openclaw # Configure firewall (SSH only) - ufw default deny incoming - ufw default allow outgoing - ufw allow ${ssh_port}/tcp - ufw --force enable %{if enable_fail2ban} # Enable fail2ban - systemctl enable fail2ban - systemctl start fail2ban %{endif} %{if enable_unattended_upgrades} # Enable automatic security updates - apt-get install -y unattended-upgrades - dpkg-reconfigure --priority=low unattended-upgrades %{endif} # Set up SSH hardening - systemctl restart sshd %{if enable_tailscale} # Install and configure Tailscale - /usr/local/bin/setup-tailscale.sh %{endif} # Set permissions on environment file - chown root:root /etc/openclaw.env - chmod 600 /etc/openclaw.env # Enable and start OpenClaw gateway service # Config is pre-seeded, so gateway can start immediately - systemctl daemon-reload - systemctl enable openclaw-gateway.service - systemctl start openclaw-gateway.service # Print completion message - | echo "" echo "=======================================" echo " OpenClaw Gateway Bootstrap Complete!" echo "=======================================" echo "" echo "Server is ready." echo "" %{if enable_tailscale} echo "Access via Tailscale:" echo " https://${server_name}.TAILNET.ts.net/" echo "" echo "If Tailscale Serve didn't start, run:" echo " sudo tailscale serve --bg 18789" echo "" %{else} echo "SSH into this server:" echo " ${ssh_port != 22 ? "ssh -p ${ssh_port}" : "ssh"} ${admin_user}@$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)" echo "" %{endif} echo "Check gateway status:" echo " systemctl status openclaw-gateway" echo "" echo "View logs:" echo " journalctl -u openclaw-gateway -f" echo ""