原本简单部署后,日志里发现了好多漏扫以及各种报错,然后跟着AI折腾了大半天,终于重新部署完成,记录下。
1. UWSGI 参考配置文件
# UWSGI.INI 参考配置文件
[uwsgi]
# --- 用户权限 ---
uid = www-data
gid = www-data
# Django 项目配置
chdir = /srv/www/codenotes/
# Django 的 WSGI 入口文件
module = codenotes.wsgi
# 虚拟环境路径
virtualenv = /srv/work_env/codenotes_env
# python 插件
plugins = /usr/lib/uwsgi/plugins/python3
# 主进程
master = true
# 工作进程数量:通常设置为 CPU 核心数
processes = 2
# 每个进程的线程数
threads = 2
# 显式启用线程支持
enable-threads = true
# --- 通信配置 (Unix Socket 高性能)---
socket = /var/run/uwsgi/codenotes.sock
# 设置 socket 权限,确保 Nginx 用户(通常是 www-data)可以读取
chmod-socket = 660
# 兜底权限
chown-socket = www-data:www-data
# 当 uwsgi 停止时,删除 socket 文件
vacuum = true
# --- 资源限制与稳定性 (防止内存泄漏) ---
# 每个工作进程在处理指定数量的请求后自动重启
max-requests = 5000
# 重启进程时的平滑过渡,避免请求丢失
max-requests-delta = 500
# 如果单个请求超过 60 秒,杀死该进程
harakiri = 90
# 内存超1024MB自动重启
reload-on-rss = 1024
# 防止请求队列堆积导致 OOM
listen = 100
# --- 日志配置 ---
# 日志文件路径
logto = /srv/log/uwsgi.log
# 日志轮转大小 30M,防止日志文件无限增长
log-maxsize = 31457280
# 日志级别
log-level = warning
# 记录客户端IP
log-x-forwarded-for = true
# 建议:开启日志轮转时的信号处理,平滑切割
log-reopen = true
# --- 其他性能优化 ---
# 主进程加载应用,节省内存
lazy-apps = true
buffer-size = 32768
# 静态文件转发缓冲区
post-buffering = 4096
ignore-sigpipe = true
ignore-write-errors = true
# --- 进程管理 ---
# pidfile = /var/run/uwsgi/codenotes_uwsgi.pid
# 优雅退出
die-on-term = true
disable-logging = false
# 【新增】优雅处理中断
thunder-lock = true
2. UWSGI 配置文件链接
# 配置文件链接至 etc/uwsgi/apps-enabled 目录
ln -s /srv/www/codenotes/uwsgi.ini /etc/uwsgi/apps-enabled/uwsgi.ini
3. 配置 UWSGI 自启动服务, 文件目录: /etc/systemd/system/codenotes.service 创建并配置完成后,重新加载下: systemctl daemon-reload
参考配置如下:
[Unit]
Description=uWSGI Emperor for CodeNotes Django Project
Documentation=man:uwsgi(1)
# 确保在网络就绪后启动,且必须在 Nginx 之前启动
After=network.target network-online.target syslog.target
Wants=network-online.target
[Service]
# 强制服务以 root 启动 (这样 ExecStartPre 和主进程都有权限)
User=root
Group=root
# 允许写入 /var/run
ReadWritePaths=/var/run
# 预执行命令
ExecStartPre=/bin/mkdir -p /var/run/uwsgi
ExecStartPre=/bin/chown -R www-data:www-data /var/run/uwsgi
ExecStartPre=/bin/chmod 755 /var/run/uwsgi
# --- 启动命令 ---
# 有链接配置文件到 /etc/uwsgi/apps-enabled 目录时,可以不需要下面方式,要不然会多启动一个。
# 方式 A: 直接指定 ini 文件路径 (推荐)
# ExecStart=/srv/work_env/codenotes_env/bin/uwsgi --ini /srv/www/codenotes/uwsgi.ini
# 方式 B: 如果使用全局安装的 uwsgi (非虚拟环境),则改为:
# ExecStart=/usr/bin/uwsgi --ini /srv/www/codenotes/uwsgi.ini
# 注意:请确保 ExecStart 指向的是真实的 uwsgi 二进制文件路径。
# 如果不确定,可以在终端运行 'which uwsgi' 或激活虚拟环境后运行 'which uwsgi' 查看路径。
# --- 重启策略 ---
# 当进程意外退出时自动重启
Restart=always
RestartSec=5s
# --- 环境变量 (可选) ---
# 如果 Django 需要特定的环境变量,可以在这里添加
Environment="DJANGO_SETTINGS_MODULE=codenotes.settings"
Environment="PATH=/srv/work_env/codenotes_env/bin"
# --- 资源限制 (可选,防止内存泄漏拖垮系统) ---
# LimitNOFILE=65535
# LimitNPROC=65535
# --- 日志处理 ---
# 将输出发送到 systemd journal (可以通过 journalctl -u uwsgi-codenotes 查看)
StandardOutput=journal
StandardError=journal
# 如果你更倾向于 uwsgi.ini 中配置的 logto 文件,可以注释掉上面两行,或者保留它们作为备份
# --- 安全加固 ---
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
# 允许写入日志目录和 socket 目录 (需要根据实际路径调整 ReadWritePaths)
ReadWritePaths=/srv/log /var/run/uwsgi /srv/www/codenotes
[Install]
# 开机自启目标
WantedBy=multi-user.target
4. Nginx 参考配置
# --------------------------
# 全局上游定义 (Upstream)
# --------------------------
upstream django_codenotes {
# 修改为 Unix Socket 路径
# 必须与你 uwsgi.ini 中的 socket 路径完全一致
server unix:/var/run/uwsgi/codenotes.sock;
}
# 定义恶意路径匹配规则 (大小写不敏感)
# 如果 URI 匹配到以下任意正则,$block_scanner 变为 1,否则为 0
map $request_uri $block_scanner {
default 0;
# 常见后台/管理路径
~*^/(wp-admin|wp-login|administrator|phpmyadmin|pma|mysql|webadmin|shell|cmd|console|manager|login|signin|auth|api) 1;
# 常见敏感文件/备份
~*^/.(php|git|svn|htaccess|htpasswd|env|config|bak|backup|sql|tar|gz|zip|rar|7z) 1;
# 常见漏洞利用路径
~*^/(solr|actuator|eureka|swagger|api-docs) 1;
~*^/(cgi-bin|scripts|bin|tmp) 1;
~*\.(asp|aspx|jsp|jspx|php5|phtml|pl|py|rb|sh|exe|bat|cmd)$ 1;
# 特定漏洞探测字符串
~*\$(\{|%24\{) 1; # Log4j JNDI 特征
~*union.*select 1; # 基础 SQL 注入探测
~*(\.\.\/|\.\.%2f|\%2e\%2e\/|\%2e\%2e\%2f) 1; # 目录遍历
}
# --------------------------
# 1. HTTP 80 端口:强制跳转 HTTPS + 去除 www
# --------------------------
server {
listen 80;
listen [::]:80;
server_name codenotes.cn www.codenotes.cn ;
# 逻辑:
# 1. 如果是 www,跳转到非 www 的 https
# 2. 如果不是 www,直接跳转到 https
if ($host = www.codenotes.cn) { return 301 https://$host$request_uri; } # managed by Certbot
if ($host = codenotes.cn) { return 301 https://$host$request_uri; } # managed by Certbot
# 关闭日志(跳转请求无需记录)
access_log off;
log_not_found off;
}
# --------------------------
# HTTPS: codenotes.cn
# --------------------------
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name codenotes.cn;
# 拦截恶意扫描路径
if ($block_scanner) {
return 444; # 444表示直接关闭连接,不返回任何响应
}
# 限制 HTTP 方法 (只允许常用方法)
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|DELETE|PATCH)$) {
return 405;
}
# SSL 证书
ssl_certificate /etc/letsencrypt/live/codenotes.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/codenotes.cn/privkey.pem; # managed by Certbot
# 引入通用 SSL 优化配置 (也可写在里面)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# OpenSSL 3.0 兼容的加密套件(优先 TLS1.3 套件,兼顾 TLS1.2)
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# 曲线名
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 安全隐藏版本号
server_tokens off;
# --- 性能优化:静态资源处理 ---
# 开启零拷贝
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 开启 Gzip 压缩 (显著减少传输体积)
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
# 静态文件 (Static)
location /static/ {
alias /srv/www/codenotes/static/;
expires 30d;
add_header Cache-Control "public, max-age=2592000" always;
add_header X-Content-Type-Options nosniff always;
access_log off; # 静态文件不记录日志,提升 IO 性能
}
# 媒体文件 (Media)
location /media/ {
alias /srv/www/codenotes/media/;
expires 30d;
add_header Cache-Control "public, max-age=2592000" always;
access_log off;
}
# 动态请求 (Django/uWSGI)
location / {
# 使用 uWSGI 协议
uwsgi_pass django_codenotes;
include uwsgi_params;
# uWSGI 专用参数设置 (比 proxy_set_header 更准确)
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
# 优化 Buffer,避免大请求写入临时文件
uwsgi_buffer_size 128k;
uwsgi_buffers 8 256k;
uwsgi_busy_buffers_size 512k;
# 超时设置 (根据业务调整,防止长任务被切断)
uwsgi_read_timeout 60s;
uwsgi_send_timeout 60s;
uwsgi_connect_timeout 60s;
}
# 基础配置
client_max_body_size 5M; # 适当增大上传限制
charset utf-8;
access_log /srv/log/codenotes.access.log main if=$block_scanner=0;
error_log /srv/log/codenotes.error.log warn;
}
# --------------------------
# HTTPS: www 子域名跳转
# --------------------------
# www.codenotes.cn -> codenotes.cn
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.codenotes.cn;
# 拦截恶意扫描路径
if ($block_scanner) {
return 444; # 444表示直接关闭连接,不返回任何响应
}
# 限制 HTTP 方法 (只允许常用方法)
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|DELETE|PATCH)$) {
return 405;
}
# 必须加载 codenotes 的证书,否则浏览器会报证书不安全,无法完成跳转
ssl_protocols TLSv1.2 TLSv1.3;
ssl_certificate /etc/letsencrypt/live/codenotes.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/codenotes.cn/privkey.pem; # managed by Certbot
# 301 永久跳转到非 www
return 301 https://codenotes.cn$request_uri;
}
请一定注意各文件及文件夹的用户及权限,尤其像 /var/run/、 日志文件及文件夹等等,在这块折腾了好久…
发现 /var/share/uwsgi/conf 下还有个default.ini ,依据上述配置,会把这个也起来,删除后,需要把自启动服务里的 # ExecStart=/usr/bin/uwsgi --ini /srv/www/codenotes/uwsgi.ini 备注取消!!要不然会异常!!
验证各服务后完成部署。