This document describes the security model, threat mitigations, and best practices implemented in RemoteVibeServer.
All credentials and API tokens are provided exclusively at deploy-time via the cloud-init user-data. The repository contains only:
- Example files with clearly marked
<PLACEHOLDER>values - Scripts that read secrets from the runtime environment file
| Artifact | Path | Permissions | Owner |
|---|---|---|---|
| Environment / secrets file | /etc/dev-server/env |
0600 |
root |
| Coder admin API token | /etc/dev-server/coder-admin-token |
0644 |
root |
| Agent environment profile | /etc/profile.d/agent-env.sh |
0644 |
root |
| Deployment status | /etc/dev-server/status |
0644 |
root |
| Provisioning log | /var/log/dev-server-provision.log |
0644 |
root |
The environment file is:
- Written by cloud-init before any script executes
- Readable only by root (mode
0600) - Never exposed over the network
- Never logged or printed by any script
Cloud Provider Console / API
│
▼
cloud-init user-data ──► /etc/dev-server/env (0600 root:root)
│ │
│ ├──► bootstrap.sh (source)
│ ├──► setup.sh (source)
│ └──► systemd EnvironmentFile
│
╳ Secrets never written to stdout, logs, or /tmp
- Must be a scoped API token (not a Global API Key)
- Required permissions:
Zone → DNS → Editfor the target zone only - The token is used once during provisioning to create/update the DNS A record
- It remains in the env file for potential re-runs but is never exposed
Best practice: Create a dedicated API token in the Cloudflare dashboard → My Profile → API Tokens → Create Token → Edit zone DNS.
| Key | Used By | Scope |
|---|---|---|
GITHUB_TOKEN |
Copilot CLI, Codex | read:user, copilot |
ANTHROPIC_API_KEY |
Claude Code CLI | API access |
GOOGLE_API_KEY |
Gemini CLI | Generative AI API |
OPENAI_API_KEY |
Codex CLI, OpenCode | OpenAI API access |
CODEX_OPENAI_AUTH_CODE |
Codex CLI | OpenAI OAuth (ChatGPT plan) |
- Keys are loaded into the shell environment via
/etc/profile.d/*.shscripts - Each profile script reads from the env file — it does not hardcode values
- If an agent is disabled (
ENABLE_AGENT_*=false), its key is ignored entirely
Caddy handles the entire TLS lifecycle:
- Certificate issuance — via Let's Encrypt ACME HTTP-01 challenge
- Certificate renewal — automatic, ~30 days before expiry
- Certificate storage — Caddy's data directory (
/var/lib/caddy/) - Protocol — TLS 1.2+ (TLS 1.3 preferred)
The Caddyfile injects the following headers on every response:
| Header | Value |
|---|---|
Strict-Transport-Security |
max-age=63072000; includeSubDomains; preload |
X-Content-Type-Options |
nosniff |
X-Frame-Options |
SAMEORIGIN |
Referrer-Policy |
strict-origin-when-cross-origin |
Server |
(removed) |
| Aspect | Caddy (chosen) | Certbot + Nginx |
|---|---|---|
| Certificate mgmt | Fully automatic | Cron job + hooks |
| Renewal failures | Self-healing | Silent failure risk |
| Config lines | ~20 | ~60+ |
| WebSocket config | Automatic | Manual proxy_pass config |
Default incoming: DENY
Default outgoing: ALLOW
Port 22/tcp: ALLOW (SSH)
Port 80/tcp: ALLOW (HTTP → HTTPS redirect)
Port 443/tcp: ALLOW (HTTPS)
All other inbound traffic is silently dropped. Coder listens on 127.0.0.1:3000 and is never directly accessible from the internet.
fail2ban is enabled with default settings to protect SSH:
- Monitors
/var/log/auth.log - Bans IPs after 5 failed login attempts
- Ban duration: 10 minutes (progressive)
The provisioning scripts do not modify SSH configuration to avoid locking out the operator. However, we strongly recommend:
# /etc/ssh/sshd_config additions
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3- Docker is installed from the official Docker repository (not distro packages)
- Coder workspaces run as unprivileged containers by default
- The workspace image uses
codercom/enterprise-basewhich runs as a non-root user
| Log | Contents |
|---|---|
/var/log/dev-server-provision.log |
Full provisioning output |
/var/log/caddy/access.log |
HTTPS access logs (rotated) |
journalctl -u coder |
Coder server logs |
journalctl -u caddy |
Caddy proxy logs |
/var/log/auth.log |
SSH authentication (fail2ban) |
| Threat | Mitigation |
|---|---|
| Secrets in source control | All secrets injected via cloud-init at deploy-time |
| Unencrypted traffic | Caddy enforces HTTPS with HSTS |
| SSH brute-force | fail2ban + key-only auth (recommended) |
| Unauthorized port access | UFW denies all except 22, 80, 443 |
| Certificate expiry | Caddy auto-renews certificates |
| Env file exposure | Mode 0600, root-only access |
| Compromised workspace container | Unprivileged containers, no host mounts by default |