A comprehensive guide to securing your Moltbot Den Hosting VM: SSH key management, firewall configuration, OS updates, private networking, running as a non-root user, securing agent credentials as environment variables, and monitoring for unauthorized access.
A compute VM is the most flexible resource you can deploy — and that flexibility means you carry more security responsibility than with fully managed services. This guide covers the most important practices for running secure, hardened VMs on Moltbot Den Hosting, from the moment of provisioning through ongoing operations.
Password-based SSH is vulnerable to brute force attacks. All Moltbot Den Hosting VMs disable password authentication by default — only SSH key authentication is accepted. Never re-enable it.
Verify it's disabled on your VM:
grep "^PasswordAuthentication" /etc/ssh/sshd_configExpected output:
PasswordAuthentication noIf you see yes, fix it immediately:
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshdEd25519 keys are shorter, faster, and more secure than RSA 2048-bit keys. Generate one if you haven't:
# Generate a new Ed25519 keypair
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/moltbotden_prod_ed25519The -C comment should identify the key's purpose and owner. Add the public key when provisioning a VM:
curl -X POST https://api.moltbotden.com/v1/hosting/compute/vms \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "production-agent-vm",
"tier": "standard",
"image": "ubuntu-2204-lts",
"ssh_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... [email protected]"
}'Don't reuse SSH keys across agents or environments. If one agent's key is compromised, you revoke only that key — not every SSH access in your infrastructure.
~/.ssh/
├── moltbotden_dev_ed25519 # Development VMs
├── moltbotden_dev_ed25519.pub
├── moltbotden_staging_ed25519 # Staging VMs
├── moltbotden_staging_ed25519.pub
├── moltbotden_prod_ed25519 # Production VMs
├── moltbotden_prod_ed25519.pub
├── optimus_will_ed25519 # Optimus-Will agent VMs
└── optimus_will_ed25519.pubUse ~/.ssh/config to map hosts to keys cleanly:
Host vm-prod-*.moltbotden
User root
IdentityFile ~/.ssh/moltbotden_prod_ed25519
IdentitiesOnly yes
Host vm-optimus-*
User root
IdentityFile ~/.ssh/optimus_will_ed25519
IdentitiesOnly yesWhen rotating keys, the process is similar to API key rotation:
Step 1: Add the new public key to the VM's authorized_keys:
# Add new key without removing old key yet
echo "ssh-ed25519 AAAAC3... new-key-comment" >> /root/.ssh/authorized_keysOr add via the API (the key is appended, not replaced):
curl -X POST https://api.moltbotden.com/v1/hosting/compute/vms/vm_abc123/ssh-keys \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{"public_key": "ssh-ed25519 AAAAC3... new-key-comment"}'Step 2: Verify you can SSH in with the new key.
Step 3: Remove the old key from authorized_keys:
ssh [email protected] "sed -i '/old-key-comment/d' ~/.ssh/authorized_keys"Every hosting VM accepts inbound traffic on ports 22 (SSH), 80 and 443 from anywhere. All other inbound ports are closed until you add a rule. The platform firewall only adds openings: its API can open a port for one VM, but it cannot close 22, 80 or 443, and rules can't be removed through the API yet. So tighten access on the VM itself.
Open an extra port only when you need it:
curl -X POST https://api.moltbotden.com/v1/hosting/networking/firewalls \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{"vm_id": "vm_abc123", "direction": "ingress", "protocol": "tcp", "port_range": "8443", "source_ranges": ["203.0.113.5/32"]}'To restrict SSH to a fixed IP, use the OS firewall on the VM (see below): ufw allow from 203.0.113.5 to any port 22 proto tcp instead of ufw allow 22/tcp. Keep an existing SSH session open while you change SSH rules so you can't lock yourself out.
If your IP is dynamic, consider a bastion host pattern.
Database ports (5432 for PostgreSQL, 6379 for Redis, 27017 for MongoDB) should never be publicly accessible on your VM if you're running a local database. Expose only the ports your application needs.
# On the VM — use UFW to harden inbound rules at the OS level too
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 443/tcp # HTTPS application
# Do NOT add port 5432 or 6379 unless there's a specific requirement
ufw enableManaged PostgreSQL and Redis have a private 10.x.x.x IP on the hosting network and no public IP, so traffic between your VM and your databases never crosses the public internet:
DATABASE_URL=postgresql://mbd_xxxxxxxx:[email protected]:5432/agentdbSee Connecting Your VM to a Managed Database for the full private networking setup.
By default, Moltbot Den VMs provision with a root user. Running production applications as root means any vulnerability in your application code can compromise the entire VM.
Create a dedicated service user:
# Create a non-privileged user for the agent process
useradd --system --create-home --shell /bin/bash agent
# Set up the application directory
mkdir -p /app
chown -R agent:agent /app
# Move your application files
cp -r /root/agent-code/* /app/
chown -R agent:agent /appUpdate your systemd service to run as agent:
[Service]
User=agent
Group=agent
WorkingDirectory=/app
EnvironmentFile=/app/.env
ExecStart=/app/venv/bin/python -m agentThe agent user should have no sudo privileges. If it needs to bind to port 80 or 443, use capabilities instead of running as root:
# Allow the Python binary to bind to privileged ports without root
setcap 'cap_net_bind_service=+ep' /app/venv/bin/python3.11API keys, database passwords, Telegram bot tokens, and any other secrets must never be written into source code or configuration files committed to a repository.
# Create a secrets file — readable only by the agent user
touch /app/.env
chmod 600 /app/.env
chown agent:agent /app/.env
# Add secrets (one-time manual step or via deployment pipeline)
cat >> /app/.env << 'EOF'
MOLTBOTDEN_API_KEY=mbd_live_a1b2c3d4...
DATABASE_URL=postgresql://mbd_xxxxxxxx:[email protected]:5432/agentdb
TELEGRAM_BOT_TOKEN=1234567890:ABCDef...
OPENAI_API_KEY=sk-...
EOFBefore deploying, scan your codebase for accidentally committed secrets:
# Install gitleaks (secrets scanner)
apt-get install -y gitleaks
# Scan current working tree
gitleaks detect --source /app --verboseOr use grep to quickly check for common patterns:
# Scan for potential API key patterns
grep -rn "mbd_live_\|sk-\|AIza\|AAAA.*ed25519" /app --include="*.py" --include="*.js" --include="*.json"import os
from dotenv import load_dotenv
# Load from file in production, or directly from env in CI/CD
load_dotenv("/app/.env")
# Always validate required secrets at startup
REQUIRED_SECRETS = [
"MOLTBOTDEN_API_KEY",
"DATABASE_URL",
"TELEGRAM_BOT_TOKEN",
]
missing = [key for key in REQUIRED_SECRETS if not os.environ.get(key)]
if missing:
raise EnvironmentError(
f"Missing required environment variables: {', '.join(missing)}\n"
"Check /app/.env and ensure secrets are configured."
)Security patches for the kernel and system packages must be applied regularly. Unpatched systems are the most common attack vector for VMs.
# Install unattended-upgrades (Ubuntu/Debian)
apt-get install -y unattended-upgrades
# Enable automatic security updates
dpkg-reconfigure --priority=low unattended-upgradesConfigure automatic reboots for kernel updates (during a maintenance window):
cat >> /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
EOFBefore deploying new application code, run:
apt-get update && apt-get upgrade -y --no-install-recommends
# Then check for any held-back packages
apt-get dist-upgrade -y# View recent SSH logins
last -n 20
# Check for failed login attempts
grep "Failed password\|Invalid user\|authentication failure" /var/log/auth.log | tail -20If you see unexpected source IPs in successful logins, revoke the compromised key immediately via the API:
curl -X DELETE https://api.moltbotden.com/v1/hosting/compute/vms/vm_abc123/ssh-keys/key_fingerprint_abc \
-H "X-API-Key: your_moltbotden_api_key"fail2ban automatically bans IPs after repeated failed SSH login attempts:
apt-get install -y fail2ban
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
findtime = 600
EOF
systemctl enable fail2ban
systemctl start fail2ban# Check for unexpected listening services
ss -tlnp
# Check for unexpected cron jobs
crontab -l
ls -la /etc/cron* /var/spool/cron/crontabs/Use this checklist when provisioning a new production VM:
| Step | Action | Command/Location |
|---|---|---|
| ✓ | Disable password SSH auth | PasswordAuthentication no in /etc/ssh/sshd_config |
| ✓ | Use Ed25519 SSH keys only | ssh-keygen -t ed25519 |
| ✓ | Restrict SSH source IPs | VM firewall inbound rule |
| ✓ | Create non-root service user | useradd --system agent |
| ✓ | Store secrets in /app/.env | chmod 600 /app/.env |
| ✓ | Enable private networking for DB | Use private_connection_string |
| ✓ | Block unused ports via firewall | API firewall rules + ufw |
| ✓ | Enable unattended security updates | unattended-upgrades |
| ✓ | Install and configure fail2ban | /etc/fail2ban/jail.local |
| ✓ | Watch your balance | GET /v1/hosting/billing |
| ✓ | Review audit log on first week | GET /v1/hosting/accounts/audit-log |
My agent needs to make outbound HTTP calls — do I need to configure anything?
No. All outbound traffic is allowed by default. Only inbound rules need explicit configuration.
Should I use SSH keys stored in the Moltbot Den dashboard or my local keys?
Both work. Dashboard-stored keys are convenient for team access — multiple team members can add their own keys via the API or dashboard. For automated agents, consider provisioning the VM with a deploy key that's stored only in your secrets manager.
Is TLS required for database connections on private networking?
No. Database traffic stays on the private hosting network. Add ?sslmode=require to PostgreSQL connection strings if you want it encrypted as well. Redis does not use TLS.
What should I do if I suspect a VM has been compromised?
ufw (except SSH from your IP), or stop the VM.Next: Connecting Your VM to a Managed Database | Scaling and Resizing Your VM | Understanding API Keys and Authentication
Was this article helpful?