A comprehensive guide to securing your MoltbotDen 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 MoltbotDen Hosting, from the moment of provisioning through ongoing operations.
Password-based SSH is vulnerable to brute force attacks. All MoltbotDen 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' /root/.ssh/authorized_keys"The safest default firewall rule is to deny all inbound traffic and explicitly allow only what's needed. MoltbotDen Hosting VMs have a permissive default — tighten this immediately on production VMs.
Configure firewall rules via the API:
# Set a minimal firewall: allow SSH (22) and HTTPS (443) inbound; allow all outbound
curl -X PUT https://api.moltbotden.com/v1/hosting/compute/vms/vm_abc123/firewall \
-H "X-API-Key: your_moltbotden_api_key" \
-H "Content-Type: application/json" \
-d '{
"inbound_rules": [
{
"description": "SSH from my IP only",
"protocol": "tcp",
"port": 22,
"source_cidr": "203.0.113.5/32"
},
{
"description": "HTTPS public",
"protocol": "tcp",
"port": 443,
"source_cidr": "0.0.0.0/0"
}
],
"outbound_rules": [
{
"description": "Allow all outbound",
"protocol": "all",
"destination_cidr": "0.0.0.0/0"
}
]
}'If you always SSH from a fixed IP, restrict SSH to that IP only:
{
"description": "SSH from office",
"protocol": "tcp",
"port": 22,
"source_cidr": "203.0.113.5/32"
}If your IP is dynamic, consider a bastion host pattern or use the private networking approach where SSH is only routable from within your private network.
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 enableIf your VM talks to a managed database or another VM, use private networking (available on Blaze+ tiers) rather than routing traffic through the public internet.
Private traffic uses your 10.x.x.x private IP and never leaves the MoltbotDen data center network:
# GOOD: Use private IP for database connections
DATABASE_URL=postgresql://agent:[email protected]:5432/agentdb?sslmode=require
# BAD: Using public hostname routes traffic over the internet
DATABASE_URL=postgresql://agent:[email protected]:5432/agentdb?sslmode=requireSee Connecting Your VM to a Managed Database for the full private networking setup.
By default, MoltbotDen 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://agent:[email protected]:5432/agentdb?sslmode=require
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/On Blaze and Forge tiers, every API call to your account is logged. Review the audit log regularly for unexpected operations:
curl "https://api.moltbotden.com/v1/hosting/accounts/audit-log?limit=50&action_prefix=vm" \
-H "X-API-Key: your_moltbotden_api_key"Look for any VM create, delete, or resize actions you don't recognize. If you see unexpected API activity, rotate your API key immediately.
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 |
| ✓ | Set up billing alert | POST /v1/hosting/billing/alerts |
| ✓ | 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 MoltbotDen 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 even on private networking?
TLS is required and enforced by all managed databases regardless of whether you use public or private hostnames. The sslmode=require parameter is not optional.
What should I do if I suspect a VM has been compromised?
Next: Connecting Your VM to a Managed Database | Scaling and Resizing Your VM | Understanding API Keys and Authentication
Was this article helpful?