Skip to main content
Glama
SECURITY.md11.7 kB
# Security Hardening Guide This document outlines security best practices for deploying SERVER-MCP in production environments. ## Table of Contents - [System User Configuration](#system-user-configuration) - [Sudo Access Control](#sudo-access-control) - [Secret Management](#secret-management) - [Network Security](#network-security) - [File System Permissions](#file-system-permissions) - [Systemd Security Features](#systemd-security-features) - [Monitoring & Audit Trail](#monitoring--audit-trail) --- ## System User Configuration ### Create Dedicated Service User ```bash # Create non-login service user sudo useradd -r -s /bin/false -d /opt/server-mcp -c "SERVER-MCP Agent" mcp-agent # Set ownership sudo chown -R mcp-agent:mcp-agent /opt/server-mcp ``` **Security Rationale**: - `-r`: System user (UID < 1000) - `-s /bin/false`: No interactive login - `-d /opt/server-mcp`: Home directory for application - Non-privileged user prevents privilege escalation --- ## Sudo Access Control ### Minimal Sudoers Configuration Create `/etc/sudoers.d/server-mcp` with **specific commands only**: ```bash # SERVER-MCP sudoers configuration # Edit with: sudo visudo -f /etc/sudoers.d/server-mcp # Service management (systemctl) mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl status * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart postgresql mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart redis mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx # Database backups mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/pg_dump * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/pg_dumpall * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/redis-cli BGSAVE # Docker operations (if needed) mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/docker ps * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/docker inspect * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/docker logs * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/docker restart * # NGINX configuration testing mcp-agent ALL=(ALL) NOPASSWD: /usr/sbin/nginx -t mcp-agent ALL=(ALL) NOPASSWD: /usr/sbin/nginx -T # Package management (read-only) mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/apt list * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/dpkg -l * # System metrics (read-only) mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/journalctl * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/netstat * mcp-agent ALL=(ALL) NOPASSWD: /usr/bin/ss * ``` **Verify sudoers syntax**: ```bash sudo visudo -c -f /etc/sudoers.d/server-mcp ``` **Security Notes**: - L **NEVER** use `mcp-agent ALL=(ALL) NOPASSWD: ALL` -  Whitelist specific commands only -  Use absolute paths (e.g., `/usr/bin/systemctl` not `systemctl`) -  Limit wildcards (e.g., `systemctl status *` not `systemctl *`) --- ## Secret Management ### Option 1: Systemd Credentials (Recommended) ```bash # Encrypt secrets with systemd-creds echo -n "your_postgres_password" | sudo systemd-creds encrypt - /etc/credstore/postgres_password echo -n "your_redis_password" | sudo systemd-creds encrypt - /etc/credstore/redis_password echo -n "your_keycloak_secret" | sudo systemd-creds encrypt - /etc/credstore/keycloak_secret # Set restrictive permissions sudo chmod 600 /etc/credstore/* sudo chown root:root /etc/credstore/* ``` Update `systemd/server-mcp.service`: ```ini [Service] LoadCredential=postgres_password:/etc/credstore/postgres_password LoadCredential=redis_password:/etc/credstore/redis_password LoadCredential=keycloak_secret:/etc/credstore/keycloak_secret ``` Access in code (already implemented): ```typescript const password = process.env.CREDENTIALS_DIRECTORY ? fs.readFileSync(`${process.env.CREDENTIALS_DIRECTORY}/postgres_password`, 'utf8') : process.env.POSTGRES_PASSWORD; ``` ### Option 2: Encrypted .env File ```bash # Install age (encryption tool) sudo apt install age # Generate key age-keygen -o /etc/server-mcp/encryption.key sudo chmod 400 /etc/server-mcp/encryption.key sudo chown mcp-agent:mcp-agent /etc/server-mcp/encryption.key # Encrypt .env age -r $(age-keygen -y /etc/server-mcp/encryption.key) \ /etc/server-mcp/server-mcp.env > /etc/server-mcp/server-mcp.env.age # Decrypt on startup (add to ExecStartPre in systemd) age -d -i /etc/server-mcp/encryption.key \ /etc/server-mcp/server-mcp.env.age > /run/server-mcp/server-mcp.env ``` ### Option 3: HashiCorp Vault For enterprise deployments, integrate with Vault: ```bash # Install Vault client sudo apt install vault # Authenticate and fetch secrets on startup ExecStartPre=/usr/local/bin/fetch-secrets.sh ``` --- ## Network Security ### Firewall Rules (UFW) ```bash # Enable firewall sudo ufw enable # Allow SSH (adjust port if non-standard) sudo ufw allow 22/tcp # Allow PostgreSQL (localhost only - already bound to 127.0.0.1) # No external access needed # Allow Redis (localhost only - already bound to 127.0.0.1) # No external access needed # Allow NGINX (if serving HTTP/HTTPS) sudo ufw allow 80/tcp sudo ufw allow 443/tcp # Deny all other inbound by default sudo ufw default deny incoming sudo ufw default allow outgoing # Verify rules sudo ufw status verbose ``` ### PostgreSQL Security ```bash # Edit /etc/postgresql/*/main/pg_hba.conf # Allow only localhost connections local all all peer host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 # Deny all other hosts sudo systemctl restart postgresql ``` ### Redis Security ```bash # Edit /etc/redis/redis.conf bind 127.0.0.1 ::1 protected-mode yes requirepass your_strong_redis_password_here maxmemory 512mb maxmemory-policy allkeys-lru sudo systemctl restart redis ``` --- ## File System Permissions ### Directory Structure and Permissions ```bash # Application directory sudo chown -R mcp-agent:mcp-agent /opt/server-mcp sudo chmod -R 750 /opt/server-mcp # Backup directory sudo mkdir -p /var/backups/server-mcp sudo chown mcp-agent:mcp-agent /var/backups/server-mcp sudo chmod 750 /var/backups/server-mcp # Log directory sudo mkdir -p /var/log/server-mcp sudo chown mcp-agent:mcp-agent /var/log/server-mcp sudo chmod 750 /var/log/server-mcp # Data directory (SQLite databases) sudo mkdir -p /var/lib/server-mcp sudo chown mcp-agent:mcp-agent /var/lib/server-mcp sudo chmod 700 /var/lib/server-mcp # Configuration directory sudo mkdir -p /etc/server-mcp sudo chown root:mcp-agent /etc/server-mcp sudo chmod 750 /etc/server-mcp # .env file (secrets) sudo chmod 640 /etc/server-mcp/server-mcp.env sudo chown root:mcp-agent /etc/server-mcp/server-mcp.env ``` ### SELinux/AppArmor (Ubuntu) For Ubuntu with AppArmor: ```bash # Create AppArmor profile sudo nano /etc/apparmor.d/opt.server-mcp.dist.index ``` ``` #include <tunables/global> /opt/server-mcp/dist/index.js { #include <abstractions/base> #include <abstractions/nameservice> capability setuid, capability setgid, capability dac_override, /opt/server-mcp/** r, /opt/server-mcp/dist/** r, /var/lib/server-mcp/** rw, /var/log/server-mcp/** rw, /var/backups/server-mcp/** rw, /etc/server-mcp/** r, /usr/bin/node ix, /usr/bin/systemctl rix, /usr/bin/pg_dump rix, /usr/bin/docker rix, deny /proc/sys/kernel/** w, deny /sys/kernel/** w, } ``` ```bash # Load profile sudo apparmor_parser -r /etc/apparmor.d/opt.server-mcp.dist.index sudo aa-enforce /opt/server-mcp/dist/index.js ``` --- ## Systemd Security Features The provided `systemd/server-mcp.service` includes extensive hardening: ### Security Directives Explained ```ini [Service] # Prevent privilege escalation NoNewPrivileges=true # Isolate /tmp PrivateTmp=true # Read-only root filesystem (with specific write paths) ProtectSystem=strict ReadWritePaths=/opt/server-mcp /var/log/server-mcp /var/backups/server-mcp # Hide /home directories ProtectHome=true # Protect kernel from modification ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true # Restrict network protocols RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 # Prevent namespace creation RestrictNamespaces=true # No realtime scheduling RestrictRealtime=true # Prevent SUID/SGID bits RestrictSUIDSGID=true # Lock personality (prevent architecture changes) LockPersonality=true # System call filtering SystemCallFilter=@system-service SystemCallFilter=~@privileged @resources ``` ### Verify Security Features ```bash # Check systemd security analysis sudo systemd-analyze security server-mcp.service # Should show score < 5.0 (lower is better) ``` --- ## Monitoring & Audit Trail ### Audit Logging Enable audit logging for all sudo commands: ```bash # Edit /etc/audit/rules.d/server-mcp.rules -w /usr/bin/sudo -p x -k sudo_execution -w /usr/bin/systemctl -p x -k systemctl_execution -w /usr/bin/pg_dump -p x -k backup_execution # Reload audit rules sudo augenrules --load ``` ### Log Aggregation SERVER-MCP logs to systemd journal: ```bash # Real-time monitoring sudo journalctl -u server-mcp -f # Filter by priority sudo journalctl -u server-mcp -p err -f # Export logs to persistent storage sudo journalctl -u server-mcp -o json > /var/log/server-mcp/journal-export.json ``` ### Intrusion Detection Install and configure AIDE (Advanced Intrusion Detection Environment): ```bash # Install AIDE sudo apt install aide # Initialize database sudo aideinit # Add SERVER-MCP to monitored paths echo "/opt/server-mcp R" | sudo tee -a /etc/aide/aide.conf echo "/etc/server-mcp R" | sudo tee -a /etc/aide/aide.conf # Run daily checks sudo crontab -e # Add: 0 4 * * * /usr/bin/aide --check | mail -s "AIDE Report" admin@example.com ``` --- ## Security Checklist Before deploying to production, verify: - [ ] Service user created with no login shell - [ ] Sudoers configured with minimal specific commands - [ ] Secrets stored in systemd credentials or encrypted .env - [ ] Firewall configured (UFW or iptables) - [ ] PostgreSQL bound to localhost only - [ ] Redis protected mode enabled with password - [ ] File permissions set correctly (750 for dirs, 640 for config) - [ ] Systemd service file includes all security directives - [ ] AppArmor/SELinux profile created and enforced - [ ] Audit logging enabled for sudo commands - [ ] Log aggregation configured (journald + external) - [ ] AIDE or similar IDS installed - [ ] Regular security updates enabled (`unattended-upgrades`) - [ ] SSH key-only authentication (password auth disabled) - [ ] fail2ban configured for SSH brute-force protection --- ## Incident Response ### If Compromise is Suspected 1. **Isolate the system**: ```bash sudo systemctl stop server-mcp sudo ufw deny from any ``` 2. **Collect forensics**: ```bash sudo journalctl -u server-mcp --no-pager > /tmp/server-mcp-logs.txt sudo last -f /var/log/wtmp > /tmp/login-history.txt sudo ausearch -k sudo_execution > /tmp/sudo-audit.txt ``` 3. **Revoke credentials**: - Rotate all database passwords - Regenerate Keycloak client secrets - Invalidate JWT tokens in Keycloak 4. **Restore from backup**: ```bash sudo systemctl stop server-mcp sudo rm -rf /opt/server-mcp sudo tar -xzf /var/backups/server-mcp-YYYYMMDD.tar.gz -C /opt sudo systemctl start server-mcp ``` --- ## References - [systemd Security Features](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Security) - [Ubuntu Server Security Guide](https://ubuntu.com/server/docs/security-introduction) - [PostgreSQL Security Best Practices](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html) - [Redis Security Guide](https://redis.io/topics/security) --- **Last Updated**: November 2025 **Maintainer**: SERVER-MCP Team

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/acampkin95/MCPCentralManager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server