Add server security configs and setup script
- Nginx configs with security headers (X-Frame-Options, CSP, etc.) - fail2ban jails for nginx (botsearch, bad-request, forbidden) - Kernel hardening via sysctl (rp_filter, no redirects, log martians) - SSH hardening (no root, max 3 attempts, no X11) - UFW rules export - Idempotent setup.sh to restore all configs on fresh install - Flask bound to 127.0.0.1 (nginx-only access) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d00ced622
commit
6ea457d01d
@ -3958,4 +3958,4 @@ def run_extract(config):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("\n LLM Team UI running at http://localhost:5000\n")
|
print("\n LLM Team UI running at http://localhost:5000\n")
|
||||||
app.run(host="0.0.0.0", port=5000, debug=False)
|
app.run(host="127.0.0.1", port=5000, debug=False)
|
||||||
|
|||||||
45
server/fail2ban-jail.local
Normal file
45
server/fail2ban-jail.local
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
bantime = 3600
|
||||||
|
maxretry = 3
|
||||||
|
findtime = 600
|
||||||
|
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
filter = sshd
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 3600
|
||||||
|
backend = systemd
|
||||||
|
|
||||||
|
[samba]
|
||||||
|
enabled = true
|
||||||
|
port = 139,445
|
||||||
|
filter = samba
|
||||||
|
logpath = /var/log/samba/log.smbd
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 3600
|
||||||
|
|
||||||
|
[nginx-botsearch]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-botsearch
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 3600
|
||||||
|
|
||||||
|
[nginx-bad-request]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-bad-request
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 3600
|
||||||
|
|
||||||
|
[nginx-forbidden]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-forbidden
|
||||||
|
logpath = /var/log/nginx/error.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 3600
|
||||||
14
server/llm-team-ui.service
Normal file
14
server/llm-team-ui.service
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=LLM Team UI - Multi-model team web interface
|
||||||
|
After=network.target ollama.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/root
|
||||||
|
ExecStart=/usr/bin/python3 /root/llm_team_ui.py
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
26
server/nginx-kb.conf
Normal file
26
server/nginx-kb.conf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen 8080;
|
||||||
|
server_name island37.com www.island37.com;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:3031/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
add_header Access-Control-Allow-Origin "https://island37.com" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
server/nginx-llms3.conf
Normal file
26
server/nginx-llms3.conf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name llms3.com www.llms3.com 192.168.1.176;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
location ^~ /.well-known/ {
|
||||||
|
root /var/www/html/llms3;
|
||||||
|
default_type "text/plain";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
server/setup.sh
Executable file
73
server/setup.sh
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Server security setup for LLM Team UI (brain / island37.com)
|
||||||
|
# Run as root. Idempotent — safe to re-run.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
echo "=== LLM Team UI Server Setup ==="
|
||||||
|
|
||||||
|
# --- Nginx ---
|
||||||
|
echo "[1/6] Installing nginx configs..."
|
||||||
|
cp "$SCRIPT_DIR/nginx-llms3.conf" /etc/nginx/sites-available/llms3
|
||||||
|
cp "$SCRIPT_DIR/nginx-kb.conf" /etc/nginx/sites-available/kb
|
||||||
|
ln -sf /etc/nginx/sites-available/llms3 /etc/nginx/sites-enabled/llms3
|
||||||
|
ln -sf /etc/nginx/sites-available/kb /etc/nginx/sites-enabled/kb
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
echo " nginx OK"
|
||||||
|
|
||||||
|
# --- Systemd ---
|
||||||
|
echo "[2/6] Installing systemd unit..."
|
||||||
|
cp "$SCRIPT_DIR/llm-team-ui.service" /etc/systemd/system/llm-team-ui.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable llm-team-ui
|
||||||
|
echo " systemd OK"
|
||||||
|
|
||||||
|
# --- Fail2ban ---
|
||||||
|
echo "[3/6] Installing fail2ban config..."
|
||||||
|
cp "$SCRIPT_DIR/fail2ban-jail.local" /etc/fail2ban/jail.local
|
||||||
|
systemctl restart fail2ban
|
||||||
|
echo " fail2ban OK"
|
||||||
|
|
||||||
|
# --- Sysctl ---
|
||||||
|
echo "[4/6] Installing kernel security settings..."
|
||||||
|
cp "$SCRIPT_DIR/sysctl-security.conf" /etc/sysctl.d/99-security.conf
|
||||||
|
sysctl --system > /dev/null 2>&1
|
||||||
|
echo " sysctl OK"
|
||||||
|
|
||||||
|
# --- SSH ---
|
||||||
|
echo "[5/6] Installing SSH config..."
|
||||||
|
cp "$SCRIPT_DIR/sshd_config" /etc/ssh/sshd_config
|
||||||
|
sshd -t && systemctl reload sshd
|
||||||
|
echo " sshd OK"
|
||||||
|
|
||||||
|
# --- UFW ---
|
||||||
|
echo "[6/6] Configuring firewall..."
|
||||||
|
ufw --force enable
|
||||||
|
ufw default deny incoming
|
||||||
|
ufw default allow outgoing
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
ufw allow 22/tcp
|
||||||
|
# HTTP/HTTPS
|
||||||
|
ufw allow 80/tcp comment "HTTP web server"
|
||||||
|
ufw allow 443/tcp comment "HTTPS web server"
|
||||||
|
# LAN services
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 139,445 proto tcp
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 137,138 proto udp
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 5000 comment "LLM Team UI"
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 9000 comment "MinIO LAN only"
|
||||||
|
ufw deny 9000 comment "Block MinIO external"
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 11434 comment "Ollama internal"
|
||||||
|
ufw allow from 192.168.1.0/24 to any port 18789 comment "OpenClaw brain"
|
||||||
|
# llms3.com Bun app
|
||||||
|
ufw allow 3030/tcp
|
||||||
|
echo " ufw OK"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Setup complete ==="
|
||||||
|
echo "Remaining manual steps:"
|
||||||
|
echo " 1. Add SSH public key: ssh-copy-id profit@<this-host>"
|
||||||
|
echo " 2. Then set PasswordAuthentication no in /etc/ssh/sshd_config"
|
||||||
|
echo " 3. Update DNS to point to this server, then run:"
|
||||||
|
echo " certbot --nginx -d llms3.com -d www.llms3.com"
|
||||||
135
server/sshd_config
Normal file
135
server/sshd_config
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
# This is the sshd server system-wide configuration file. See
|
||||||
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
|
# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
|
||||||
|
|
||||||
|
# The strategy used for options in the default sshd_config shipped with
|
||||||
|
# OpenSSH is to specify options with their default value where
|
||||||
|
# possible, but leave them commented. Uncommented options override the
|
||||||
|
# default value.
|
||||||
|
|
||||||
|
Include /etc/ssh/sshd_config.d/*.conf
|
||||||
|
|
||||||
|
#Port 22
|
||||||
|
#AddressFamily any
|
||||||
|
#ListenAddress 0.0.0.0
|
||||||
|
#ListenAddress ::
|
||||||
|
|
||||||
|
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||||
|
|
||||||
|
# Ciphers and keying
|
||||||
|
#RekeyLimit default none
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
#SyslogFacility AUTH
|
||||||
|
#LogLevel INFO
|
||||||
|
|
||||||
|
# Authentication:
|
||||||
|
|
||||||
|
#LoginGraceTime 2m
|
||||||
|
#PermitRootLogin prohibit-password
|
||||||
|
#StrictModes yes
|
||||||
|
#MaxAuthTries 6
|
||||||
|
#MaxSessions 10
|
||||||
|
|
||||||
|
#PubkeyAuthentication yes
|
||||||
|
|
||||||
|
# Expect .ssh/authorized_keys2 to be disregarded by default in future.
|
||||||
|
#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
|
||||||
|
|
||||||
|
#AuthorizedPrincipalsFile none
|
||||||
|
|
||||||
|
#AuthorizedKeysCommand none
|
||||||
|
#AuthorizedKeysCommandUser nobody
|
||||||
|
|
||||||
|
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||||
|
#HostbasedAuthentication no
|
||||||
|
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||||
|
# HostbasedAuthentication
|
||||||
|
#IgnoreUserKnownHosts no
|
||||||
|
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||||
|
#IgnoreRhosts yes
|
||||||
|
|
||||||
|
# To disable tunneled clear text passwords, change to "no" here!
|
||||||
|
#PasswordAuthentication yes
|
||||||
|
#PermitEmptyPasswords no
|
||||||
|
|
||||||
|
# Change to "yes" to enable keyboard-interactive authentication. Depending on
|
||||||
|
# the system's configuration, this may involve passwords, challenge-response,
|
||||||
|
# one-time passwords or some combination of these and other methods.
|
||||||
|
# Beware issues with some PAM modules and threads.
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
|
||||||
|
# Kerberos options
|
||||||
|
#KerberosAuthentication no
|
||||||
|
#KerberosOrLocalPasswd yes
|
||||||
|
#KerberosTicketCleanup yes
|
||||||
|
#KerberosGetAFSToken no
|
||||||
|
|
||||||
|
# GSSAPI options
|
||||||
|
#GSSAPIAuthentication no
|
||||||
|
#GSSAPICleanupCredentials yes
|
||||||
|
#GSSAPIStrictAcceptorCheck yes
|
||||||
|
#GSSAPIKeyExchange no
|
||||||
|
|
||||||
|
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||||
|
# and session processing. If this is enabled, PAM authentication will
|
||||||
|
# be allowed through the KbdInteractiveAuthentication and
|
||||||
|
# PasswordAuthentication. Depending on your PAM configuration,
|
||||||
|
# PAM authentication via KbdInteractiveAuthentication may bypass
|
||||||
|
# the setting of "PermitRootLogin prohibit-password".
|
||||||
|
# If you just want the PAM account and session checks to run without
|
||||||
|
# PAM authentication, then enable this but set PasswordAuthentication
|
||||||
|
# and KbdInteractiveAuthentication to 'no'.
|
||||||
|
UsePAM yes
|
||||||
|
|
||||||
|
#AllowAgentForwarding yes
|
||||||
|
#AllowTcpForwarding yes
|
||||||
|
#GatewayPorts no
|
||||||
|
#X11Forwarding yes
|
||||||
|
#X11DisplayOffset 10
|
||||||
|
#X11UseLocalhost yes
|
||||||
|
#PermitTTY yes
|
||||||
|
PrintMotd no
|
||||||
|
#PrintLastLog yes
|
||||||
|
#TCPKeepAlive yes
|
||||||
|
#PermitUserEnvironment no
|
||||||
|
#Compression delayed
|
||||||
|
#ClientAliveInterval 0
|
||||||
|
#ClientAliveCountMax 3
|
||||||
|
#UseDNS no
|
||||||
|
#PidFile /run/sshd.pid
|
||||||
|
#MaxStartups 10:30:100
|
||||||
|
#PermitTunnel no
|
||||||
|
#ChrootDirectory none
|
||||||
|
#VersionAddendum none
|
||||||
|
|
||||||
|
# no default banner path
|
||||||
|
#Banner none
|
||||||
|
|
||||||
|
# Allow client to pass locale and color environment variables
|
||||||
|
AcceptEnv LANG LC_* COLORTERM NO_COLOR
|
||||||
|
|
||||||
|
# override default of no subsystems
|
||||||
|
Subsystem sftp /usr/lib/openssh/sftp-server
|
||||||
|
|
||||||
|
# Example of overriding settings on a per-user basis
|
||||||
|
#Match User anoncvs
|
||||||
|
# X11Forwarding no
|
||||||
|
# AllowTcpForwarding no
|
||||||
|
# PermitTTY no
|
||||||
|
# ForceCommand cvs server
|
||||||
|
|
||||||
|
# Added by Failover - 2026-02-18 (partial hardening, awaiting J's SSH key)
|
||||||
|
PermitRootLogin no
|
||||||
|
MaxAuthTries 3
|
||||||
|
MaxSessions 2
|
||||||
|
Banner /etc/ssh/banner
|
||||||
|
X11Forwarding no
|
||||||
|
PermitEmptyPasswords no
|
||||||
|
ClientAliveInterval 300
|
||||||
|
ClientAliveCountMax 2
|
||||||
|
LoginGraceTime 60
|
||||||
21
server/sysctl-security.conf
Normal file
21
server/sysctl-security.conf
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Reverse path filtering - drop packets with spoofed source IPs
|
||||||
|
net.ipv4.conf.all.rp_filter = 1
|
||||||
|
net.ipv4.conf.default.rp_filter = 1
|
||||||
|
|
||||||
|
# Don't send ICMP redirects (not a router)
|
||||||
|
net.ipv4.conf.all.send_redirects = 0
|
||||||
|
net.ipv4.conf.default.send_redirects = 0
|
||||||
|
|
||||||
|
# Don't accept ICMP redirects
|
||||||
|
net.ipv4.conf.all.accept_redirects = 0
|
||||||
|
net.ipv4.conf.default.accept_redirects = 0
|
||||||
|
net.ipv6.conf.all.accept_redirects = 0
|
||||||
|
net.ipv6.conf.default.accept_redirects = 0
|
||||||
|
|
||||||
|
# Don't accept source-routed packets
|
||||||
|
net.ipv4.conf.all.accept_source_route = 0
|
||||||
|
net.ipv4.conf.default.accept_source_route = 0
|
||||||
|
|
||||||
|
# Log martian packets (spoofed, source-routed, redirect)
|
||||||
|
net.ipv4.conf.all.log_martians = 1
|
||||||
|
net.ipv4.conf.default.log_martians = 1
|
||||||
24
server/ufw-rules.txt
Normal file
24
server/ufw-rules.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
Status: active
|
||||||
|
Logging: on (low)
|
||||||
|
Default: deny (incoming), allow (outgoing), deny (routed)
|
||||||
|
New profiles: skip
|
||||||
|
|
||||||
|
To Action From
|
||||||
|
-- ------ ----
|
||||||
|
22/tcp ALLOW IN Anywhere
|
||||||
|
139,445/tcp ALLOW IN 192.168.1.0/24
|
||||||
|
137,138/udp ALLOW IN 192.168.1.0/24
|
||||||
|
3030/tcp ALLOW IN Anywhere
|
||||||
|
11434 ALLOW IN 192.168.1.0/24 # Ollama internal
|
||||||
|
18789 ALLOW IN 192.168.1.0/24 # OpenClaw brain
|
||||||
|
80/tcp ALLOW IN Anywhere # HTTP web server
|
||||||
|
443/tcp ALLOW IN Anywhere # HTTPS web server
|
||||||
|
5000 ALLOW IN 192.168.1.0/24 # LLM Team UI
|
||||||
|
9000 ALLOW IN 192.168.1.0/24 # MinIO LAN only
|
||||||
|
9000 DENY IN Anywhere # Block MinIO external
|
||||||
|
22/tcp (v6) ALLOW IN Anywhere (v6)
|
||||||
|
3030/tcp (v6) ALLOW IN Anywhere (v6)
|
||||||
|
80/tcp (v6) ALLOW IN Anywhere (v6) # HTTP web server
|
||||||
|
443/tcp (v6) ALLOW IN Anywhere (v6) # HTTPS web server
|
||||||
|
9000 (v6) DENY IN Anywhere (v6) # Block MinIO external
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user