AlmaLinux 编译安装nginx-quic

网络上的教程众说纷纭,大部分都是使用quiche补丁的方式,这里给出一份使用nginx官方quic版本的方式,nginx 1.25版本后,quic分支已经合并进入主线,编译过程简单很多

安装编译工具

dnf install gcc gcc-c++ perl-IPC-Cmd git wget make tar autoconf

#如果是AlmaLinux9需安装FindBin
dnf install perl-FindBin

下载编译程序

wget https://nginx.org/download/nginx-1.25.0.tar.gz
wget https://github.com/jemalloc/jemalloc/archive/refs/tags/5.3.0.tar.gz -O jemalloc-5.3.0.tar.gz
wget https://sourceforge.net/projects/pcre/files/pcre/8.45/pcre-8.45.tar.gz
wget https://github.com/quictls/openssl/archive/refs/tags/openssl-3.0.12-quic1.tar.gz

git clone https://github.com/google/ngx_brotli.git && cd ngx_brotli && git submodule update --init && cd ../
git clone https://github.com/cloudflare/zlib && cd zlib && make -f Makefile.in distclean && cd ../

安装jemalloc

jemalloc是比glibc中的malloc高效很多的内存管理方案,这里使用jemalloc对nginx进行优化

tar zxvf jemalloc-5.3.0.tar.gz
cd jemalloc-5.3.0
./autogen.sh
make
make install
ln -s /usr/local/lib/libjemalloc.so.2 /usr/lib64/libjemalloc.so.2
cd ../
rm -rf jemalloc-5.3.0

注:部分系统还需在/usr/lib目录下增加一条软链接,否则编译nginx时会报错

ln -s /usr/local/lib/libjemalloc.so.2 /usr/lib/libjemalloc.so.2

安装nginx

开始编译nginx

/usr/sbin/groupadd www && /usr/sbin/useradd -g www www
tar zxvf openssl-3.0.12-quic1.tar.gz
tar zxvf pcre-8.45.tar.gz
tar zxvf nginx-1.25.0.tar.gz
cd nginx-1.25.0

./configure --prefix=/usr/local/nginx \
--user=www --group=www \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-http_v3_module \
--with-http_ssl_module \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module \
--with-http_realip_module \
--with-zlib=../zlib \
--with-http_gzip_static_module \
--with-pcre=../pcre-8.45 \
--with-pcre-jit \
--with-openssl=../openssl-openssl-3.0.12-quic1 \
--with-ld-opt=-ljemalloc \
--add-module=../ngx_brotli

make
make install
make clean
cd ../

如果系统内核支持KTLS可使用如下编译参数


./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-http_ssl_module --with-stream --with-stream_ssl_preread_module --with-stream_ssl_module --with-stream_realip_module --with-http_realip_module --with-http_gzip_static_module --with-zlib=../zlib --with-pcre=../pcre-8.45 --with-pcre-jit --with-openssl=../openssl-openssl-3.0.12-quic1 --with-openssl-opt=enable-ktls --with-ld-opt=-ljemalloc --add-module=../ngx_brotli

 

编译完成后创建站点与日志目录

mkdir -p /data/htdocs/conf/
mkdir -p /data/htdocs/ssl/

mkdir -p /data/htdocs/wwwroot/
chmod +w /data/htdocs/
chown -R www:www /data/htdocs/

mkdir -p /data/wwwlogs/
chmod +w /data/wwwlogs/
chown -R www:www /data/wwwlogs/

使系统自动切割日志

vi /etc/logrotate.d/nginx

#输入以下内容
/data/wwwlogs/*nginx.log {
  daily
  rotate 7
  missingok
  dateext
  compress
  notifempty
  sharedscripts
  postrotate
    [ -e /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
  endscript
}

配置nginx

创建默认证书

#使nginx.conf的default_server不报错
openssl genrsa -out /data/htdocs/ssl/default.key
openssl req -new -x509 -key /data/htdocs/ssl/default.key -out /data/htdocs/ssl/default.pem -days 3650

生成 session_ticket.key

#安全考虑应不定期更换session_ticket.key秘钥
openssl rand 48 > /data0/htdocs/ssl/session_ticket.key

创建配置文件

mv /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.old
vi /usr/local/nginx/conf/nginx.conf

输入以下内容后保存

user www www;
worker_processes auto;

error_log /data/wwwlogs/error_nginx.log crit;
pid /var/run/nginx.pid;
worker_rlimit_nofile 51200;

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

http {
    include 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 1024m;
    client_body_buffer_size 10m;
    server_tokens off;
    tcp_nodelay on;
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 120;

    gzip on;
    gzip_buffers 16 8k;
    gzip_comp_level 6;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_proxied any;
    gzip_vary on;
    gzip_types
      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
      text/javascript application/javascript application/x-javascript
      text/x-json application/json application/x-web-app-manifest+json
      text/css text/plain text/x-component
      font/opentype application/x-font-ttf application/vnd.ms-fontobject
      image/x-icon;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";

    brotli            on;
    brotli_comp_level 6;
    brotli_static     on;
    brotli_min_length 1k;
    brotli_types
      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
      text/javascript application/javascript application/x-javascript
      text/x-json application/json application/x-web-app-manifest+json
      text/css text/plain text/x-component
      font/opentype application/x-font-ttf application/vnd.ms-fontobject
      image/x-icon;

    log_format  http  '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent"';

    log_format  https '$remote_addr - $remote_user [$time_local] '
                      '$ssl_protocol/$ssl_cipher $ssl_early_data '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent"';


    server {
        listen 80  default_server reuseport;
        listen 443 ssl http2 default_server reuseport;
        listen 443 quic default_server reuseport;
        server_name localhost;
        access_log /data/wwwlogs/access_nginx.log combined;

        ssl_certificate /data/htdocs/ssl/default.pem;
        ssl_certificate_key /data/htdocs/ssl/default.key;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ecdh_curve X25519:secp384r1;
        ssl_conf_command Options PrioritizeChaCha;
        ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
        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;
        ssl_early_data off;
        #proxy_set_header Early-Data $ssl_early_data;
        add_header Alt-Svc 'h3=":443"; ma=86400';

        return 444;
    }

include /data/htdocs/conf/*.conf;
}

创建用户和组

/usr/sbin/groupadd www
/usr/sbin/useradd -g www www

创建系统服务

vi /usr/lib/systemd/system/nginx.service

#输入以下内容后保存

[Unit]
Description=nginx
Documentation=http://nginx.org/en/docs/
After=network.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPost=/bin/sleep 0.1
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutStartSec=120
LimitNOFILE=65535
LimitNPROC=65535
LimitCORE=65535

[Install]
WantedBy=multi-user.target

启动系统服务

systemctl start nginx
systemctl enable nginx

添加web站点

在 /data/htdocs/conf 目录中添加站点配置文件

在 /data/htdocs/ssl 目录中添加站点绑定域名的证书

完成以上步骤后,执行

#检测配置文件正确性
/usr/local/nginx/sbin/nginx -t

#重新加载配置
systemctl reload nginx

这里以wwwroot为站点目录示例,给出一个同时支持http3与http2的配置:

(如启用KTLS则去掉 ssl_conf_command Options KTLS 前的注释)

server {
        listen                      80;
        listen                      443 ssl http2;
        listen                      443 quic;
        server_name                 sitename.com;
        root                        /data/htdocs/wwwroot;
        index                       index.html index.htm;

        access_log                  /data/wwwlogs/sitename.com_nginx.log https;
        resolver                    223.5.5.5 8.8.8.8 valid=300s;
        resolver_timeout            5s;
        ssl_stapling                on;
        ssl_stapling_verify         on; 
        ssl_trusted_certificate     /data/htdocs/ssl/sitename.pem;
        ssl_certificate             /data/htdocs/ssl/sitename.pem;
        ssl_certificate_key         /data/htdocs/ssl/sitename.key;
        ssl_session_tickets         on;
        ssl_session_ticket_key      /data0/htdocs/ssl/session_ticket.key;
        ssl_session_timeout         1440m;
        ssl_session_cache           shared:SSL:32m;
        ssl_protocols               TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers   on;
        ssl_ecdh_curve X25519:secp384r1;
        #ssl_conf_command Options    KTLS;
        ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
        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;
        ssl_early_data off;
        #proxy_set_header Early-Data $ssl_early_data;

        add_header Alt-Svc 'h3=":443"; ma=86400';
        add_header Strict-Transport-Security "max-age=63072000; preload";
        #add_header X-Content-Type-Options "nosniff";
        #add_header X-Frame-Options "SAMEORIGIN";
        #add_header X-XSS-Protection "1; mode=block";

        client_max_body_size 0;

        location ~* \.(jpg|jpeg|gif|png|bmp|swf|ico|rar|zip|7z|tgz|tar.gz|txt|flv|mid|doc|docx|pptx|ppt|xlsx|xls|pdf|mp3|wma)$ {
            valid_referers none blocked sitename.com *.sitename.com *.baidu.com *.baiducontent.com;
            if ($invalid_referer) {
                 return 412;
            }
            expires     30d;
            access_log  off;
            break;
        }
        location ~* \.(js|css)?$ {
            expires     7d;
            access_log  off;
        }
        location ~* \.(eot|ttf|woff|woff2|svg)$ {
            expires     max;
            access_log  off;
            #add_header Access-Control-Allow-Origin *;
            #add_header Access-Control-Allow-Headers X-Requested-With;
            #add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
        }
        location = /favicon.ico { 
            log_not_found off; 
        }
        location = /robots.txt {
            #deny all;
            log_not_found off;
        }
        location ~ /\.(?!well-known).* {
            deny all;
        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
}