Sanitized for public release: - Removed all API keys, tokens, and secrets - Removed personal Discord IDs from hermes-openclaw.json - Updated git URLs to be generic placeholders - All sensitive data uses environment variable interpolation
372 lines
No EOL
9.9 KiB
Smarty
372 lines
No EOL
9.9 KiB
Smarty
#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 "" |