This document covers security best practices for deploying and operating CommandRM.
- Security Architecture
- Authentication
- Transport Security
- Agent Security
- Network Security
- Data Security
- Hardening Checklist
- Security Updates
- Reporting Vulnerabilities
CommandRM implements defense-in-depth with multiple security layers:
┌──────────────────────────────────────────────────────────────┐
│ Security Layers │
├──────────────────────────────────────────────────────────────┤
│ TLS Encryption All traffic encrypted in transit │
│ JWT Authentication Token-based user authentication │
│ Certificate Auth mTLS for agent authentication │
│ Rate Limiting Protection against brute force │
│ CORS Protection Cross-origin request control │
│ Audit Logging All mutations logged │
│ 2FA Support Optional TOTP second factor │
│ GPG Signatures Signed release binaries │
└──────────────────────────────────────────────────────────────┘
CommandRM uses JWT (JSON Web Tokens) for user authentication:
- Access tokens: Short-lived (15 minutes default), used for API requests
- Refresh tokens: Longer-lived (7 days default), used to obtain new access tokens
- Secure storage: Tokens stored in httpOnly cookies or local storage
The AUTH_JWT_SECRET is critical for security:
# Generate a secure secret (Linux/macOS)
openssl rand -base64 32
# Generate a secure secret (PowerShell)
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }) -as [byte[]])Requirements:
- Minimum 32 characters
- Use cryptographically random generation
- Never reuse across environments
- Rotate periodically (requires all users to re-authenticate)
Enable 2FA for all admin accounts:
- Go to Settings > Security
- Click "Enable 2FA"
- Scan QR code with authenticator app (Google Authenticator, Authy, etc.)
- Enter verification code
- Save backup codes securely
2FA is required for:
- Remote terminal access
- Remote desktop access
- Script execution
- User management operations
Default password policy:
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character
Always enable TLS in production:
SERVER_TLS_ENABLED=true
SERVER_TLS_CERT=/etc/commandrm/certs/server.crt
SERVER_TLS_KEY=/etc/commandrm/certs/server.keyRecommended TLS settings:
- TLS 1.2 minimum (TLS 1.3 preferred)
- Strong cipher suites only
- HSTS enabled (automatic with TLS)
-
Let's Encrypt (Recommended for public servers)
# During installation ./commandrm-server-setup --tls-mode letsencrypt --domain commandrm.example.com -
Self-Signed (Internal networks)
# Auto-generated during installation ./commandrm-server-setup --tls-mode self-signed -
Custom Certificate
# Use existing certificate ./commandrm-server-setup --tls-mode custom \ --tls-cert /path/to/cert.pem \ --tls-key /path/to/key.pem
WebSocket connections are secured via:
- Same TLS encryption as HTTP
- JWT token validation on connect
- Certificate authentication for agents
- Connection timeout handling
For high-security environments, use certificate authentication:
# Server configuration
AGENT_AUTH_MODE=certificate # Only accept certificate auth
AGENT_AUTO_GENERATE_CA=true
AGENT_CERT_VALIDITY_DAYS=365Certificate authentication provides:
- Mutual TLS (mTLS) verification
- No shared secrets to manage
- Certificate revocation capability
- Unique identity per agent
Best practices for enrollment keys:
-
Short-lived keys: Set expiration dates
./commandrm-server enrollment create \ -d "New deployment" \ --expires 24h -
Limited uses: Restrict number of enrollments
./commandrm-server enrollment create \ -d "Single server" \ --max-uses 1 -
Descriptive names: Track key purpose
-
Immediate revocation: Delete unused keys
-
Audit key usage: Monitor enrollment events
- Run agent as non-root user (except for privileged operations)
- Use filesystem permissions to protect config files
- Limit network access to server only
- Enable logging for audit trail
Server:
# Allow only necessary ports
ufw allow 443/tcp # HTTPS
ufw allow 80/tcp # HTTP (for Let's Encrypt only)
ufw deny all # Deny everything elseAgent:
# Outbound to server only
iptables -A OUTPUT -d server-ip -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT -j DROPUsing nginx as a reverse proxy adds:
- Additional TLS termination point
- Request filtering
- Rate limiting
- DDoS protection
server {
listen 443 ssl http2;
server_name commandrm.example.com;
ssl_certificate /etc/letsencrypt/live/commandrm.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/commandrm.example.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
limit_req zone=api burst=20 nodelay;
}
}Restrict allowed origins in production:
# Allow specific origins only
CORS_ALLOWED_ORIGINS=https://commandrm.example.com,https://admin.example.com
# Never use * in production
# CORS_ALLOWED_ORIGINS=* # DON'T DO THISPostgreSQL:
-- Create dedicated user with minimal privileges
CREATE USER commandrm WITH PASSWORD 'secure-password';
CREATE DATABASE commandrm OWNER commandrm;
-- Use SSL connections
ALTER SYSTEM SET ssl = on;SQLite:
- Store database file with restricted permissions (600)
- Use encrypted filesystem for additional protection
- Regular backups with encryption
Store secrets securely:
- Environment variables: Use systemd or container secrets
- Files: Restrict permissions to 600
- Never commit secrets: Use .gitignore, scan for leaks
- Rotate regularly: Especially JWT secrets
Enable and protect audit logs:
AUDIT_LOG_ENABLED=true
LOG_FILE=/var/log/commandrm/audit.logLogs capture:
- All authentication events (success/failure)
- User management operations
- Agent enrollment/deletion
- Configuration changes
- Script executions
- File operations
Forward logs to SIEM for monitoring:
# Example: forward to syslog
LOG_FORMAT=json
# Configure rsyslog or similar to forward logs- Generate strong JWT secret (32+ chars)
- Obtain TLS certificates
- Set up PostgreSQL with SSL (for large deployments)
- Configure firewall rules
- Review default settings
-
PRODUCTION_MODE=true -
SERVER_TLS_ENABLED=true -
CORS_ALLOWED_ORIGINSset to specific origins -
RATE_LIMIT_LOGIN=5or lower -
AUDIT_LOG_ENABLED=true
- Create separate admin accounts per person
- Enable 2FA for all admin accounts
- Remove default/test accounts
- Document access policies
- Use certificate authentication in high-security environments
- Set enrollment key expiration
- Limit enrollment key uses
- Monitor agent enrollments
- Monitor audit logs
- Review access regularly
- Apply updates promptly
- Rotate secrets periodically
- Test backup restoration
- Conduct security reviews
All releases are GPG-signed. Verify before deploying:
# Import public key
gpg --import commandrm-release-key.asc
# Verify signature
gpg --verify checksums.txt.asc checksums.txt
# Verify binary checksum
sha256sum -c checksums.txt --ignore-missing- Review release notes for security fixes
- Download and verify new binaries
- Test in staging environment
- Schedule maintenance window
- Deploy update
- Verify functionality
- Monitor for issues
Server can push updates to agents automatically:
# Enable auto-updates
AGENT_UPDATE_ROLLBACK_ENABLED=true # Auto-rollback on failure
AGENT_UPDATE_MAX_CONCURRENT=10 # Staged rolloutIf you discover a security vulnerability:
- Do not disclose publicly
- Email security details to the maintainers
- Include:
- Description of vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We will:
- Acknowledge within 48 hours
- Provide fix timeline
- Credit reporter (unless anonymity requested)
- Publish advisory after fix is available
Symptom: Server starts with warning about default secret
Fix:
# Generate and set strong secret
AUTH_JWT_SECRET=$(openssl rand -base64 32)Symptom: API accessible without authentication
Fix:
- Verify firewall rules
- Check CORS settings
- Ensure TLS is enabled
Symptom: Unauthorized agents appearing
Fix:
- Delete unused enrollment keys
- Set key expiration
- Limit key uses
- Monitor audit logs
Prevention:
- Always use HTTPS
- Set appropriate token expiry
- Enable 2FA for sensitive operations
- Monitor concurrent sessions