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__":
|
||||
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