Nginx 安装与配置

Nginx 基础知识-安装与配置。


nginx配置文件优化

sudo yum install yum-utils

sudo vim /etc/yum.repos.d/nginx.repo

添加:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

启用 nginx 源

sudo yum-config-manager --enable nginx-mainline

安装 nginx

sudo yum install nginx

开机启动 nginx

systemctl enable nginx

nginx 开机启动

通过上面的操作就自动将 nginx 开机启动了,所以下面的操作可不用执行:

sudo vim /usr/lib/systemd/system/nginx.service

添加:

[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

开机启动 nginx: sudo systemctl enable nginx

配置 nginx

下面贴上完整的 nginx.conf 配置文件:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
worker_rlimit_nofile 65535;

events {
    use epoll;
    worker_connections 65535;
    multi_accept on;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    client_max_body_size 50m;

    sendfile   on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    #fastcgi_intercept_errors on;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 256k;

    gzip on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 4;
    gzip_types     text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss;
    gzip_vary on;
    gzip_proxied   expired no-cache no-store private auth;
    gzip_disable   "MSIE [1-6]\.";

    server_tokens off;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    include /etc/nginx/conf.d/default.conf;
}

下面的是虚拟主机配置文件内容:

server {
    listen 80;
    server_name api.echoxu.cn;
    rewrite ^/(.*)$ https://api.echoxu.cn/$1 permanent;
}

server {
    listen 443 ssl;
    server_name  api.echoxu.cn;
    root /usr/share/nginx/html/iweb;
    index index.php index.html index.htm;

    ssl_certificate /etc/nginx/ssl/api.echoxu.cn.pem;
    ssl_certificate_key /etc/nginx/ssl/api.echoxu.cn.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    access_log /usr/share/nginx/logs/Access.log;
    error_log /usr/share/nginx/logs/Error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~* \.php$ {
        root      /usr/share/nginx/html/iweb;
        fastcgi_index   index.php;
        fastcgi_pass    127.0.0.1:9000;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
    }

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location ~ /\. {
        deny all;
    }

    location ~* \.(sh|docx|txt|doc|php|php5|pl|py|zip|gz|bx|)$ {
         deny all;
    }

    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 30d;
        log_not_found off;
    }
}

分析 nginx 日志

打开 nginx 日志你会看到有很多进行破解的 ip,我们需要找到他们并封禁其 ip:

cat nginx.log | cut -d ' ' -f 1 | sort | uniq -c | awk '{if ($1 > 5) print $0}' | sort -nr | less > ip.txt

通过上面的命令我们找到了进行 5 次尝试破解的 ip 地址.下面我们对其 ip 进行封禁:

sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address="181.174.83.226" drop'
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address="129.213.59.142" drop'
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address="60.248.159.139" drop'

重启防火墙firewall-cmd --reload,可通过firewall-cmd --zone=public --list-rich-rules查看刚添加的规则.

如果发现误封了 ip,可通过sudo firewall-cmd --permanent --remove-rich-rule='rule family=ipv4 source address="181.174.83.226" drop'进行解封 ip。

下面是自动化封禁 ip 的脚本 (分析 Error.log 日志也能得到相同的效果) :

#!/bin/bash
# drop ip
# author: echoxu
# todo: 异步执行,提高运行效率

ip_lock_file=/root/ip_lock_file.txt

ip_arr=()

ip_limit_count=5

ip_from_firewall_rules=`firewall-cmd --zone=public --list-rich-rules |awk -F ' ' '{print $4}' |grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"`

ip_from_nginx_log=`cat /usr/share/nginx/logs/Access.log | awk -F ' ' '{if ($9 == 404 || $9 == 403 || $9 == 400) print $1}' | sort -rn  |uniq -c | awk -F ' ' '{if ($1 > $ip_limit_count) print $2}'`

ip_from_secure_log=`cat /var/log/secure-20231119 |grep -E 'Did not|Bad protocol' | grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"  |sort -u`





getIPFromFirewallRules(){
	for firewallip in $ip_from_firewall_rules 
    	do
            echo "$(date +'%F %T') 已封禁入侵ip: $firewallip " >> $ip_lock_file
    done
}


getIPFromNginxLog(){
    for nginxlogip in $ip_from_nginx_log
        do
            ip_arr[${#ip_arr[*]}]=${nginxlogip}
    done

     
    #echo "从 Nginx log 中获得 ${#ip_arr[*]} 个 ip,分别为:${ip_arr[*]}"
}


# kill ip from /var/log/secure
getIPFromSecureLog(){
    for securelogip in $ip_from_secure_log
        do
	        ip_arr[${#ip_arr[*]}]=${securelogip}
    done


    #echo "从 secure log 中获得 ${#ip_arr[*]} 个 ip,分别为:${ip_arr[*]}"
}


generateBaseData(){
    if [ ! -f $ip_lock_file ];then
        getIPFromFirewallRules      # 将 firewall rules 里的数据导入到 ip_lock_file (程序首次运行时执行此函数)
    fi
}


killIP(){
    for ip in ${ip_arr[*]}
        do
            ip_is_exist=`cat $ip_lock_file |grep $ip|wc -l`

            if [ $ip_is_exist -eq 0 ];then
                firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$ip drop"
                echo "$(date +'%F %T') 已封禁入侵ip: $ip " >> $ip_lock_file
            fi
            
            #echo "入侵ip: $ip 已在黑名单中存在。 "
    done
}


main(){
    generateBaseData
    getIPFromNginxLog
    getIPFromSecureLog
    killIP
    firewall-cmd --reload
}


main

添加定时任务,让其每半小时执行一次脚本并封禁破解次数>7 的 ip 地址:

crontab -e 然后添加如下:

0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.daily.0/nginx >/dev/null 2>&1

*/30 * * * * sh /root/killIP.sh > /dev/null 2>&1 &

最好对 nginx 的日志进行一些切割处理,让其每天产生一个日志:

sudo vim /etc/logrotate.daily.0/nginx

添加如下内容:

/usr/share/nginx/logs/*.log {
        daily
        missingok
        rotate 30
        compress
        dateext
        delaycompress
        notifempty
        create 640 root root
        sharedscripts
        postrotate
                if [ -f /var/run/nginx.pid ]; then
                        kill -USR1 `cat /var/run/nginx.pid`
                fi
        endscript
}

添加定时任务,每天 00:00 产生新的日志文件:

0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.daily.0/nginx >/dev/null 2>&1

根据偏移量读取 Nginx logs,请参考:使用偏移量读取 nginx logs

nginx 日志切割,请参考:nginx 日志切割

nginx 配置文件

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    server_name example.com;
    
    rewrite ^/(.*)$ https://www.example.com/$1 permanent;
}

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=15r/s;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  www.example.com;
    
    root /usr/share/nginx/html/example;
    index index.html index.htm;

    ssl_certificate /etc/nginx/ssl/9705728_www.example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/9705728_www.example.com.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions

    # intermediate configuration
    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/ssl/9705728_www.example.com.pem;
    resolver 127.0.0.1 223.5.5.5 8.8.8.8 8.8.4.4 valid=60s ipv6=off; #设置OCSP请求的DNS服务器地址
    resolver_timeout 5s;

    access_log /usr/share/nginx/logs/Access.log;
    error_log /usr/share/nginx/logs/Error.log;

    location / {

        limit_req zone=mylimit;

        # 只接受 GET 请求
		add_header Allow "GET" always; 

		if ( $request_method !~ ^(GET)$ ){
            return 444;
        }

        # 如果是空user_agent 直接返回444
        if ($http_user_agent ~ ^$){
            return 444;
        }

        # 访问的文件不存在时直接返回 444,因为我只需要开放那几个文件能访问就行
        if (!-e  $request_filename){
            return 444;
		}

        #userAgent中如果包含下面的关键字,直接返回444
        if ($http_user_agent ~* "Scrapy|python|curl|wget|httpclient|MJ12bot|Expanse*|DnBCrawler-Analytics|ahrefsbot|seznambot|serpstatbot|sindresorhus|zgrab|python-requests*|Go-http-client|serpstatbot|abuse.xmco.fr"){
            return 444;
        }

        # 只接受指定的请求,其它请求全部拒绝访问
		if ($request_uri ~* "\/|index.html|aboutUS.html|docDetail.html|doc.html|getStart.html|index.html|medicalTesting.html|price.html|researchService.html|favicon.ico|favicon.svg") {
			break;
		}

        # 允许访问 favicon.ico
        location ~ ^/favicon* {
            log_not_found off;
            access_log off;
			expires 90d;
            break;
        }

        # 允许通过域名访问
        location ~ ^/$ {
            break;
        }

        # 限制 \x00\x01 这样的请求访问,参考: https://github.com/mariusv/nginx-badbot-blocker/issues/157
		if ($request_uri ~* ".*\\x.*"){
			return 444;
		}

        # 屏蔽恶意访问
        # if ($request_uri ~* \.(php|php5|asp|aspx|jsp|sh|swp|git|env|yaml|yml|conf|ini|txt|pl|py|json|sql|db|bak|ini|docx|doc|rar|tar|gz|zip|log|aws)$) {
        #     return 444;
        # }
        
        # 屏蔽指定关键词
        # if ($request_uri ~* (wordpress|wp-content|wp-*|nextcloud|owncloud|credentials|phpinfo|wlwmanifest|tmp|credentials|phpMyAdmin|xmlrpc|wcm|shell|login|admin)) {
        #     return 444;
        # }
		
        

        # 当请求不是上面开放的路径时直接拒绝访问,达到了只能访问特定路径的目的
        return 444;
    }
	
}


nginx 支持 http1.2

nginx 开启 OCSP Stapling : https://developer.aliyun.com/article/764381

strings /usr/sbin/nginx | grep _module | grep -v configure| sort | grep ngx_http_v2_module

  • --with-http_v2_module和--with-http_ssl_module用于支持HTTP2和ssl加密,
  • --with-openssl=用于指定openssl库的安装目录
  • --with-openssl-opt=enable-tls1_3用于开启openssl库的tls1.3支持,但是现在的新版本已经默认开启,无需额外添加这个参数

nginx 支持 http2,tls1.3 需要将 openssl 升级到 1.1.1

可通过 https://ssl-config.mozilla.org 查看配置 tls1.2/tls1.3 的参数

通过 https://www.ssllabs.com/ssltest/analyze.html 可查看是否已启用了 tls1.2/tls1.3

参考: - https://blog.csdn.net/qq_42287535/article/details/126040255

# generated 2023-12-05, Mozilla Guideline v5.7, nginx 1.24.0, OpenSSL 1.0.2k-fips, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.24.0&config=intermediate&openssl=1.0.2k-fips&guideline=5.7
# https://www.ssllabs.com/ssltest/analyze.html 检查是否支持 tls1.2
# links: https://blog.csdn.net/qq_42287535/article/details/126040255
# links: https://zhuanlan.zhihu.com/p/223304805

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
    ssl_dhparam /path/to/dhparam;

    # intermediate configuration
    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

    # replace with the IP address of your resolver
    resolver 127.0.0.1;
}

检查 nginx 是否开启 ocsp: openssl s_client -connect www.digitalocean.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:' | grep -B 17 'Next Update'

nginx 配置 ocsp: how-to-configure-ocsp-stapling-on-apache-and-nginx

nginx 日志出现 "\x15\x03\x01\x00\x02\x02P"

Nginx 400错误归因于随机IP地址中以“ \ x”开头的随机编码字符串,request_body中含有中文时,nginx日志会转换为十六进制这个其实很难防住,不过可尝试以下方法:

部分日志:

35.203.211.189 - - [01/Dec/2023:15:15:19 +0800] "\x00\x00\x001\xFFSMBr\x00\x00\x00\x00\x18Eh\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xB5}\x00\x00\x01\x00\x00\x0E\x00\x02NT LM 0.12\x00\x02\x00" 400 150 "-" "-"
35.203.211.189 - - [01/Dec/2023:15:15:19 +0800] "\x00\x00\x00f\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02" 400 150 "-" "-"
35.203.211.189 - - [01/Dec/2023:15:15:19 +0800] "\x00\x00\x00f\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456\x00\x00\x00\x00\x00\x00\x00\x00\x10\x02" 400 150 "-" "-"
35.203.211.189 - - [01/Dec/2023:15:15:20 +0800] "\x00\x00\x00f\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03" 400 150 "-" "-"
35.203.211.189 - - [01/Dec/2023:15:15:20 +0800] "\x00\x00\x00f\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456\x00\x00\x00\x00\x00\x00\x00\x00\x02\x03" 400 150 "-" "-"
35.203.211.189 - - [01/Dec/2023:15:15:21 +0800] "\x00\x00\x00\xAC\xFESMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456$\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x001234567890123456h\x00\x00\x00\x02\x00\x00\x00\x11\x03\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x01\x00,\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 400 150 "-" "-"
54.91.91.71 - - [01/Dec/2023:15:23:55 +0800] "\x15\x03\x01\x00\x02\x02P" 400 150 "-" "-"
54.91.91.71 - - [01/Dec/2023:15:24:17 +0800] "\x15\x03\x01\x00\x02\x02P" 400 150 "-" "-"
  • 你最有可能看到此消息,因为你正在向HTTP端点发出HTTPS请求。例如,你将HTTPS请求发送到Web服务器的端口80而不是443。因此,HTTP端点获得了一堆加密数据,不费力气地解密它(因为HTTP应该是纯文本) ,因此你在日志文件中会看到一堆乱码。
  • https://bbs.csdn.net/topics/398495210
  • https://www.openssl.org/source/openssl-1.1.1w.tar.gz
  • https://blog.csdn.net/abccyz/article/details/128748255
  • https://github.com/mariusv/nginx-badbot-blocker/issues/157
  • https://blog.csdn.net/weixin_45122740/article/details/121189540
上次更新:
贡献者: iEchoxu