Initial commit — OpenClaw VM infrastructure
- ansible/: VM provisioning playbooks and roles - provision-vm.yml: create KVM VM from Ubuntu cloud image - install.yml: install OpenClaw on guest (upstream) - customize.yml: swappiness, virtiofs fstab, linger - roles/vm/: libvirt domain XML, cloud-init templates - inventory.yml + host_vars/zap.yml: zap instance config - backup-openclaw-vm.sh: daily rsync + MinIO upload - restore-openclaw-vm.sh: full redeploy from scratch - README.md: full operational documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
189
ansible/AGENTS.md
Normal file
189
ansible/AGENTS.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Agent Guidelines
|
||||
|
||||
## Project Overview
|
||||
|
||||
Ansible playbook for automated, hardened OpenClaw installation on Debian/Ubuntu systems.
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Security First**: Firewall must be configured before Docker installation
|
||||
2. **One Command Install**: `curl | bash` should work out of the box
|
||||
3. **Localhost Only**: All container ports bind to 127.0.0.1
|
||||
4. **Defense in Depth**: UFW + DOCKER-USER + localhost binding + non-root container
|
||||
|
||||
## Critical Components
|
||||
|
||||
### Task Order
|
||||
Docker must be installed **before** firewall configuration.
|
||||
|
||||
Task order in `roles/openclaw/tasks/main.yml`:
|
||||
```yaml
|
||||
- tailscale.yml # VPN setup
|
||||
- user.yml # Create system user
|
||||
- docker.yml # Install Docker (creates /etc/docker)
|
||||
- firewall.yml # Configure UFW + daemon.json (needs /etc/docker to exist)
|
||||
- nodejs.yml # Node.js + pnpm
|
||||
- openclaw.yml # Container setup
|
||||
```
|
||||
|
||||
Reason: `firewall.yml` writes `/etc/docker/daemon.json` and restarts Docker service.
|
||||
|
||||
### DOCKER-USER Chain
|
||||
Located in `/etc/ufw/after.rules`. Uses dynamic interface detection (not hardcoded `eth0`).
|
||||
|
||||
**Never** use `iptables: false` in Docker daemon config - this would break container networking.
|
||||
|
||||
### Port Binding
|
||||
Always use `127.0.0.1:HOST_PORT:CONTAINER_PORT` in docker-compose.yml, never `HOST_PORT:CONTAINER_PORT`.
|
||||
|
||||
## Code Style
|
||||
|
||||
### Ansible
|
||||
- Use loops instead of repeated tasks
|
||||
- No `become_user` (playbook already runs as root)
|
||||
- Use `community.docker.docker_compose_v2` (not deprecated `docker_compose`)
|
||||
- Always specify collections in `requirements.yml`
|
||||
|
||||
### Docker
|
||||
- Multi-stage builds if needed
|
||||
- USER directive for non-root
|
||||
- Proper healthchecks (test the app, not just Node)
|
||||
- Use `docker compose` (V2) not `docker-compose` (V1)
|
||||
- No `version:` in compose files
|
||||
|
||||
### Templates
|
||||
- Use variables for all paths/ports
|
||||
- Add comments explaining security decisions
|
||||
- Keep jinja2 logic simple
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
Before committing changes:
|
||||
|
||||
```bash
|
||||
# 1. Syntax check
|
||||
ansible-playbook playbook.yml --syntax-check
|
||||
|
||||
# 2. Dry run
|
||||
ansible-playbook playbook.yml --check
|
||||
|
||||
# 3. Full install (on test VM)
|
||||
curl -fsSL https://raw.githubusercontent.com/.../install.sh | bash
|
||||
|
||||
# 4. Verify security
|
||||
sudo ufw status verbose
|
||||
sudo iptables -L DOCKER-USER -n
|
||||
sudo ss -tlnp # Only SSH + localhost should listen
|
||||
|
||||
# 5. External port scan
|
||||
nmap -p- TEST_SERVER_IP # Only port 22 should be open
|
||||
|
||||
# 6. Test isolation
|
||||
sudo docker run -p 80:80 nginx
|
||||
curl http://TEST_SERVER_IP:80 # Should fail
|
||||
curl http://localhost:80 # Should work
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
1. ❌ Installing Docker before configuring firewall
|
||||
2. ❌ Using `0.0.0.0` port binding
|
||||
3. ❌ Hardcoding network interface names (use dynamic detection)
|
||||
4. ❌ Setting `iptables: false` in Docker daemon
|
||||
5. ❌ Running container as root
|
||||
6. ❌ Using deprecated `docker-compose` (V1)
|
||||
7. ❌ Forgetting to add collections to requirements.yml
|
||||
|
||||
## Documentation
|
||||
|
||||
### User-Facing
|
||||
- **README.md**: Installation, quick start, basic management
|
||||
- **docs/**: Technical details, architecture, troubleshooting
|
||||
|
||||
### Developer-Facing
|
||||
- **AGENTS.md**: This file - guidelines for AI agents/contributors
|
||||
- Code comments: Explain *why*, not *what*
|
||||
|
||||
Keep docs concise. No progress logs, no refactoring summaries.
|
||||
|
||||
## File Locations
|
||||
|
||||
### Host System
|
||||
```
|
||||
/opt/openclaw/ # Installation files
|
||||
/home/openclaw/.openclaw/ # Config and data
|
||||
/etc/systemd/system/openclaw.service
|
||||
/etc/docker/daemon.json
|
||||
/etc/ufw/after.rules
|
||||
```
|
||||
|
||||
### Repository
|
||||
```
|
||||
roles/openclaw/
|
||||
├── tasks/ # Ansible tasks (order matters!)
|
||||
├── templates/ # Jinja2 configs
|
||||
├── defaults/ # Variables
|
||||
└── handlers/ # Service restart handlers
|
||||
|
||||
docs/ # Technical documentation (frontmatter format)
|
||||
requirements.yml # Ansible Galaxy collections
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
### Why UFW + DOCKER-USER?
|
||||
Docker bypasses UFW by default. DOCKER-USER chain is evaluated first, allowing us to block before Docker sees the traffic.
|
||||
|
||||
### Why Fail2ban?
|
||||
SSH is exposed to the internet. Fail2ban automatically bans IPs after 5 failed attempts for 1 hour.
|
||||
|
||||
### Why Unattended-Upgrades?
|
||||
Security patches should be applied promptly. Automatic security-only updates reduce vulnerability windows.
|
||||
|
||||
### Why Scoped Sudo?
|
||||
The openclaw user only needs to manage its own service and Tailscale. Full root access would be dangerous if the app is compromised.
|
||||
|
||||
### Why Localhost Binding?
|
||||
Defense in depth. If DOCKER-USER fails, localhost binding prevents external access.
|
||||
|
||||
### Why Non-Root Container?
|
||||
Least privilege. Limits damage if container is compromised.
|
||||
|
||||
### Why Systemd?
|
||||
Clean lifecycle, auto-start, logging integration.
|
||||
|
||||
### Known Limitations
|
||||
- **macOS**: Incomplete support (no launchd, basic firewall). Test thoroughly.
|
||||
- **IPv6**: Disabled in Docker. Review if your network uses IPv6.
|
||||
- **curl | bash**: Inherent risks. For production, clone and audit first.
|
||||
|
||||
## Making Changes
|
||||
|
||||
### Adding a New Task
|
||||
1. Add to appropriate file in `roles/openclaw/tasks/`
|
||||
2. Update main.yml if new task file
|
||||
3. Test with `--check` first
|
||||
4. Verify idempotency (can run multiple times safely)
|
||||
|
||||
### Changing Firewall Rules
|
||||
1. Test on disposable VM first
|
||||
2. Always keep SSH accessible
|
||||
3. Update `docs/security.md` with changes
|
||||
4. Verify with external port scan
|
||||
|
||||
### Updating Docker Config
|
||||
1. Changes to `daemon.json.j2` trigger Docker restart (via handler)
|
||||
2. Test container networking after restart
|
||||
3. Verify DOCKER-USER chain still works
|
||||
|
||||
## Version Management
|
||||
|
||||
- Use semantic versioning for releases
|
||||
- Tag releases in git
|
||||
- Update CHANGELOG.md with user-facing changes
|
||||
- No version numbers in code (use git tags)
|
||||
|
||||
## Support Channels
|
||||
|
||||
- OpenClaw issues: https://github.com/openclaw/openclaw
|
||||
- This installer: https://github.com/openclaw/openclaw-ansible
|
||||
Reference in New Issue
Block a user