Nginx (简体中文)

Nginx (读作"engine X") 由Igor Sysoev(俄罗斯)于2005年编写,是一个免费、开源、高性能的HTTP服务器和反向代理,也可以作为一个IMAP/POP3代理服务器。Nginx因为稳定,丰富的功能集,配置简单,资源占用低而闻名世界。

翻译状态:本文是 Nginx翻译。上次翻译日期:2020-07-16。如果英文版本有所更改,则您可以帮助同步翻译。

这篇文章描述了如何设置nginx并且如何通过#FastCGI集成PHP.

安装

安装nginx-mainline (主线(mainline)分支:新功能,更新,bug解决) 或者 nginx (稳定分支: 只是bug解决).

推荐使用主线分支。使用稳定分支的主要原因是担心新功能的造成的不良影响,比如与第三方模块不兼容或者开发者疏忽导致引入新功能时出现bug.

注意: 所有在official repositories可用的模块都需要nginx(与nginx-mainline相反)作为依赖.你可能需要在下决定选nginx还是nginx-mainline前,先查看下模块名单。nginx-mainline的模块能在Arch User Repository找到.

对于在基于chroot环境下的额外安全性,可查阅#在chroot环境下安装.

启动

启动/启用 nginx.service.

默认在 http://127.0.0.1 页面服务的页面是 /usr/share/nginx/html/index.html.

配置

安装nginx后的第一步该干什么写在用户手册里了. 你可以通过编辑在/etc/nginx/下的文件来修改配置。主配置文件在/etc/nginx/nginx.conf.

更多细节和例子,你可以在 https://wiki.nginx.org/Configuration官方文档找到.

下面的例子包含了最常见的使用案例.我们假定你使用的是默认文件路径(/usr/share/nginx/html). 如果你改了路径,用你自己的路径替代.

提示: DigitalOcean 提供了一个 Nginx 配置工具

配置例子

/etc/nginx/nginx.conf
user http;
worker_processes auto;
worker_cpu_affinity auto;

events {
    multi_accept on;
    worker_connections 1024;
}

http {
    charset utf-8;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off;
    log_not_found off;
    types_hash_max_size 4096;
    client_max_body_size 16M;

    # MIME
    include mime.types;
    default_type application/octet-stream;

    # logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # load configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

进程和连接

你应该为选一个合适的值. 这项设置最终决定了nginx接受多少连接和它会使用多少处理器。通常来说,把它设置成你系统里的硬件线程数就好了。可选的是,自从 1.3.8 和 1.2.5版本以后, 接受 作为值了, 他会自动检测最优值 (source).

nginx的最大连接数有下面的公式给出.

不同用户间的使用

默认的, nginx 用 身份运行主进程而用http用户运行worker进程. 如果要用其它用户运行worker进程, 改变里的参数就好了:

如果组(group)忽略不写的话,就会用和user相同的名字来代替

提示: 其实也可以使用systemdroot身份在没有任何东西运行的情况下运行nginx . 可以看 #使用 systemd无权限运行.

server代码块

通过使用 模块,可以实现服务多个域名. 这些模块可以类比为Apache HTTP Server中的"VirtualHosts" . 也可查阅 上游文献.

下面的例子,服务器监听IPv4和IPv6的80端口的进入流量的两个域名,分别是 和:

Restart(重启) nginx.service 来让改变的配置生效.

确保主机名是可以被设置好的DNS服务器比如BIND 或者 dnsmasq解析的,或者可以查看下Network configuration#Local network hostname resolution.

管理服务器入口

把不同的模块放到不同的文件里是可能的.这样的话可以让你很轻易的开启和禁用特定的站点.

创建以下文件夹:

# mkdir /etc/nginx/sites-available
# mkdir /etc/nginx/sites-enabled

在文件夹下创建一个文件包含一个或更多服务器模块:

/etc/nginx/sites-available/example.conf
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
}

把放到http 模块的末尾:

如果要启用一个网站, 只需要简单的创建一个符号链接:

# ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf

如果要移除一个网站

# unlink /etc/nginx/sites-enabled/example.conf

Reload/restart nginx.service 来让配置生效.

TLS

OpenSSL 提供了TLS支持并且默认在Arch系统安装时安装了.

警告: 如果你打算实现TLS, 你必须知道一些变化和实现 仍然被攻击时很脆弱. 如果想知道TLS内现存的漏洞和怎样用合适的方法到nginx上,可访问 https://weakdh.org/sysadmin.html

创建一个私钥和自签名的证书,这对大多数不需要 CSR的安装来说足够了:

# mkdir /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl req -new -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.crt -days 1095
# chmod 400 server.key
# chmod 444 server.crt

如果你需要创建一个CSR, 用下面的教程而不是上面的:

# mkdir /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out server.key
# chmod 400 server.key
# openssl req -new -sha256 -key server.key -out server.csr
# openssl x509 -req -days 1095 -in server.csr -signkey server.key -out server.crt

使用TLS的 /etc/nginx/nginx.conf 的基本例子:

Restart nginx.service 来让配置生效.

分用户目录

如果要复制Apache式的 URLs 到用户的 ~/public_html 目录, 可以尝试下面的. (Note: 如果下面的两个规则都用了的话一定要把更清楚地PHP规则放在前面.)

查阅 #PHP实现 来阅读更多 的PHP配置.

重启 nginx.service 来启用新配置.

FastCGI

FastCGI, 也叫 FCGI, 是适用于web服务器和交互式程序的接口的协议. FastCGI 是早期的 Common Gateway Interface (CGI)的变种; FastCGI的主要目的是减少CGI程序和web服务器交互的开销,来允许服务器同时处理更多网页请求.

FastCGI技术被引进nginx是为了与许多外部工具,比如. Perl, PHP and Python

PHP实现

PHP-FPM 是建议使用的用作PHP的FastCGI服务器的解决方案.

Install(安装) 然后确保 PHP 被安装配置正确. PHP-FPM的主要配置文件是 . 基础使用的话默认配置就足够了.

最后, enablestart .

nginx 配置

当服务一个PHP web程序时,一个PHP-FPM的应该包括在 #server代码块,比如.:

/etc/nginx/sites-available/example.conf
server {
    root /usr/share/nginx/html;

    location / {
        index index.html index.htm;
    }

    location ~ \.php$ {
        # 404
        try_files $fastcgi_script_name =404;

        # default fastcgi_params
        include fastcgi_params;

        # fastcgi settings
        fastcgi_pass			unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index			index.php;
        fastcgi_buffers			8 16k;
        fastcgi_buffer_size		32k;

        # fastcgi params
        fastcgi_param DOCUMENT_ROOT	$realpath_root;
        fastcgi_param SCRIPT_FILENAME	$realpath_root$fastcgi_script_name;
        #fastcgi_param PHP_ADMIN_VALUE	"open_basedir=$base/:/usr/lib/php/:/tmp/";
    }
}

如果需要用PHP处理其他文件扩展名 (比如. .html and .htm):

location ~ [^/]\.(php|html|htm)(/|$) {
    ...
}

.php 的PHP-FPM扩展处理都需要被直接加入到 :

security.limit_extensions = .php .html .htm

如果你使用多个 块来启用PHP支持, 创建一个 配置文件可能更简单:

为特定的服务器启用PHP支持, 只需要简单的加入 include php.conf就好了:

测试配置

你需要restartnginx.service 服务单元来让配置生效,如果配置之前被改变过的话.

测试FastCGI实现, 在目录下创建一个新的PHP文件,内容是:

<?php phpinfo(); ?>

把这个文件用浏览器打开,你应该就可以看到现在的PHP配置的信息页了.

CGI 实现

这个实现是CGI程序所需要的.

fcgiwrap

Install . 配置文件是 . Enable 并且 start .

多个工人线程

如果你想生成多个工人线程建议你使用, 它会处理崩溃的子进程. 如果你需要使用spawn-fcgi 来创建Unix套接字,因为multiwatch无法处理systemd创建的套接字,尽管fcgiwarp它自己在单元文件中直接调用页没有任何问题.

从 复制单元文件到 (和 单元, 如果存在的话), 修改行到满足你需要的地方. 下面是使用的单元文件. 确保 没有被start(开始)或enable(启用), 因为他会和这些单元冲突:

可以自定义 来改变生成的子进程的数量.

nginx配置

在每个服务了一个CGI程序的 块内都应该有一个像下面的 块:

location ~ \.cgi$ {
     root           /path/to/server/cgi-bin;
     fastcgi_pass   unix:/run/fcgiwrap.sock;
     include        fastcgi.conf;
}
fcgiwrap 的默认套接字文件是 .

如果你一直出现 错误,你应该检查你的CGI程序是否宣告了下面内容的mime类型。如果是html,他应该是.

在chroot环境下安装

chroot环境里运行nginx是有一个额外的安全层的.为了最大化安全性chroot环境应该只包括nginx服务器需要的文件,并且所有的文件都应该尽可能有严格的权限,比如像这样的文件夹应该不可读和不可写.

Arch默认有一个http用户和组来运行服务器chroot将会在.

创建这个监狱(指chroot环境)的Perl脚本能在jail.pl gist找到. 你可以直接用那个脚本或者跟着这篇文章的指示继续阅读下去. 它应该用root运行. 你需要在它起作用之前取消一行注释.

创建必要的设备

nginx需要 , /dev/random, 和 . 为了安装这些需要在chroot环境下创建 目录和用mknod添加设备尽量别挂载所有的来确保, 尽管在chroot受到攻击的情况下, 攻击者必须突破chroot来获取像的重要文件.

提示: 确保 /srv/http 挂载了并且没有nodev选项
# export JAIL=/srv/http
# mkdir $JAIL/dev
# mknod -m 0666 $JAIL/dev/null c 1 3
# mknod -m 0666 $JAIL/dev/random c 1 8
# mknod -m 0444 $JAIL/dev/urandom c 1 9

创建必要目录

nginx需要大量文件来运转正常. 在把它们拷贝过去之前创建一个文件夹来存储它们。假设你的nginx文档目录在 .

# mkdir -p $JAIL/etc/nginx/logs
# mkdir -p $JAIL/usr/{lib,bin}
# mkdir -p $JAIL/usr/share/nginx
# mkdir -p $JAIL/var/{log,lib}/nginx
# mkdir -p $JAIL/www/cgi-bin
# mkdir -p $JAIL/{run,tmp}
# cd $JAIL; ln -s usr/lib lib
# cd $JAIL; ln -s usr/lib lib64
# cd $JAIL/usr; ln -s lib lib64

然后挂载 和 作为tmpfs. 大小应该限制确保攻击者无法吃掉所有的内存.

# mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
# mount -t tmpfs none $JAIL/tmp -o 'noexec,size=100M'

为了在重启后保留挂载,下面的入口应该被添加到:

填充chroot

首先把简单的文件复制过去.

# cp -r /usr/share/nginx/* $JAIL/usr/share/nginx
# cp -r /usr/share/nginx/html/* $JAIL/www
# cp /usr/bin/nginx $JAIL/usr/bin/
# cp -r /var/lib/nginx $JAIL/var/lib/nginx

然后复制必要的库过去。使用ldd来把它们列出来然后复制到正确的地方。最好是复制而不是链接来确保就算攻击者获取的写权限也服务法修改真实的系统文件.

$ ldd /usr/bin/nginx
linux-vdso.so.1 (0x00007fffc41fe000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f57ec3e8000)
libcrypt.so.1 => /usr/lib/libcrypt.so.1 (0x00007f57ec1b1000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f57ebead000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f57ebbaf000)
libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f57eb94c000)
libssl.so.1.0.0 => /usr/lib/libssl.so.1.0.0 (0x00007f57eb6e0000)
libcrypto.so.1.0.0 => /usr/lib/libcrypto.so.1.0.0 (0x00007f57eb2d6000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f57eb0d2000)
libz.so.1 => /usr/lib/libz.so.1 (0x00007f57eaebc000)
libGeoIP.so.1 => /usr/lib/libGeoIP.so.1 (0x00007f57eac8d000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f57eaa77000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f57ea6ca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f57ec604000)

对于 你可以使用下面的命令:

# cp $(ldd /usr/bin/nginx | grep /usr/lib/ | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib

用下面的命令来复制 :

# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib

复制一些复杂但必要的库和系统文件.

# cp /usr/lib/libnss_* $JAIL/usr/lib
# cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} $JAIL/etc

为chroot创建严格的 user/group文件. 这样只有chroot正常工作所需要的用户才会被chroot知道,其他的users/groups并不会被泄露给攻击者,如果他们已经获取chroot权限的话.

$JAIL/etc/passwd
http:x:33:33:http:/:/bin/false
nobody:x:99:99:nobody:/:/bin/false
# touch $JAIL/etc/shells
# touch $JAIL/run/nginx.pid

最后设置权限。应该尽可能把权限设置为不可写 be owned by root and set unwritable.

# chown -R root:root $JAIL/

# chown -R http:http $JAIL/www
# chown -R http:http $JAIL/etc/nginx
# chown -R http:http $JAIL/var/{log,lib}/nginx
# chown http:http $JAIL/run/nginx.pid

# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod -rw
# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod +x
# find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs chmod -x
# find $JAIL/usr/bin -type f -print | xargs chmod ug+rx
# find $JAIL/ -group http -user http -print | xargs chmod o-rwx
# chmod +rw $JAIL/tmp
# chmod +rw $JAIL/run

如果你的端口绑定为 80 (或者其他在 [1-1023]范围内的端口),给chroot无需root就可绑定的权限.

# setcap 'cap_net_bind_service=+ep' $JAIL/usr/bin/nginx

修改nginx.service来开启chroot

在修改nginx.service单元文件之前, 把它复制进 因为这里的单元文件的优先级比 高. 这样更新nginx就不会修改你自定义的 .service 文件.

# cp /usr/lib/systemd/system/nginx.service /etc/systemd/system/nginx.service

systemd单元必须在开启chroot里的nginx之前修改,以http用户的身份,将pid文件放到chroot里.

注意: 用pacman升级nginx并不会升级chroot里的nginx。如果你要升级的话可能需要手动重复上面的步骤。不要忘记更新它链接的库.

你现在可以安全的写在非chroot环境下的nginx安装.

# pacman -Rsc nginx

如果你不移除非chroot环境的nginx安装,你可能想要确认运行的nginx是不是chroot环境里的, 你可以查询 链接到哪里. 如果链接到 而不是那运行的就是chroot环境下的nginx.

# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done

建议和技巧

使用 systemd无权限运行

编辑 nginx.service 并在 下设置 ,还有可选的设置  :

/etc/systemd/system/nginx.service.d/user.conf
[Service]
User=''user''
Group=''group''

我们可以通过强化服务以防止提升特权:

然后我们需要确保 (即前面设置的用户)有权限做它需要做的事:

Port
Linux默认不允许非用户的进程绑定低于1024的端口,高于1024的端口可以用:
提示: 如果你想nginx可以访问80或者443端口,配置你的firewall(防火墙)来重定向从80或者443的请求到nginx监听的端口.

或者你可以提升nginx进程的CAP_NET_BIND_SERVICE权限,它能允许绑定低于1024的端口:

PID文件
nginx 默认使用 . 我们可以创建一个文件夹,user有写权限,然后把我们的PID文件放到那里. 一个使用systemd-tmpfiles的例子: 运行配置: # systemd-tmpfiles --create 基于最初的nginx.serviceEdit(编辑)PID的值:
一些/var/lib/nginx下面的目录需要被以 运行的nginx的引导. 所以有必要启动整个服务器老这么做, nginx会在一次简单的 #配置验证之后自己这么做. 所以只要运行这些中的一个就好啦.
日志文件& 目录权限
运行配置测试的步骤会创建一个悬空的拥有的日志文件. 移除下面的日志文件开始刷新.
启用nginx服务的用户需要的写权限. 这可能需要 改变权限 和/或 你系统这个目录的所有权.

现在一切都好了。开始start(启动) nginx, 然后就可以想用你的非root下的nginx了.

针对systemd的可选脚本

在纯systemd的情况下,你可以享受chroot + systemd的好处. 基于在下面的文件设置 用户组一个 pid :

文件的就绝对路径是.

/etc/systemd/system/nginx.service
[Unit]
Description=nginx (Chroot)
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/srv/http/run/nginx.pid
RootDirectory=/srv/http
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reload
ExecStop=/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop

[Install]
WantedBy=multi-user.target

没必要设置默认路径,nginx默认加载,二这也是一个好主意.

作为可选项,你可以在chroot环境下仅仅运行 ,参数要调成,查看 systemd服务手册 或者在挂载点生效前启用它,或者一个 systemd路径也行.

Enable(启用)创建的 然后把 里的WantedBy=default.target 变成 .

单元文件里的允许systemd监控进程(需要绝对路径). 如果这不是你想要的, 你可以改变默认的“一击”类型, 然后删除单元文件里的参照.

Nginx Beautifier (nginx美化器)

是一个命令行工具,用来美化和格式化nginx配置文件.

更好的头文件管理

Nginx有一个非常不直观的头文件管理系统,头文件只能在一个上下文中定义, 任何其它的头文件都会被忽略. 为了补救这个,我们可以安装 headers-more-nginx 模块.

Install (安装) 包. 这样会把模块安装到 目录下.

如果要加载模块把下面加入到你的nginx配置文件的第一行.

/etc/nginx/nginx.conf
load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so";
...

故障排除

接入本地IP重定向到 localhost

Arch Linux 论坛的解决方案.

/etc/nginx/nginx.conf有一行 ,前面没有 , 把下面的加入到这一行之下:

server_name_in_redirect off;

默认行为事nginx将任何请求重定向到配置里给定的值里.

Error: The page you are looking for is temporarily unavailable. Please try again later. (502 Bad Gateway)

这是因为FastCGI服务器还没有开启,或者套接字有错误的权限.

试一试 这个回答 来解决502问题.

在Arch Linux, 上面提到的配置文件在 .

Error: No input file specified

1. 确定里的 open_basedir变量包含正确的路径,要和 (通常在)里的变量一致. 当使用PHP-FPM 作为PHP的FastCGI服务器时, 你可以添加 到里的 代码块里,它能处理PHP文件.

2. 另一种情况是, 文件里,代码块里的错误的 变量. 确保 指向相同服务器里的的同样的目录. 或者你直接把root设为全局,不要把它定义到任何地方

3. 检查权限: 比如. http 是用户/组, 是目录权限, 是文件权限. 记住,到html目录的路径目录也必须有相同的权限. 可以查看 File permissions and attributes#Bulk chmod 来批量修改大量文件夹的权限.

4. 你没有让 包含你脚本的全部路径. 如果nginx的配置 () 是对的的话, 这种错误意味着PHP无法加载请求的脚本. 通常它知识简单的权限问题, 你可以用root运行php-cgi:

# spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi

或者你应该创建一个组和用户来开启php-cgi:

# groupadd www
# useradd -g www www
# chmod +w /srv/www/nginx/html
# chown -R www:www /srv/www/nginx/html
# spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/bin/php-cgi

5. 如果你是在chroot的环境下的nginx运行php-fpm,确保里的 设置正确(或者 如果是旧版本的话)

Warning: Could not build optimal types_hash

当开启 nginx.service, 进程可能会有下面的日志信息:

[warn] 18872#18872: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

解决这个警告, 提升http代码块的下面的键的值 :

/etc/nginx/nginx.conf
http {
    types_hash_max_size 4096;
    server_names_hash_bucket_size 128;
    ...
}

不能指定请求地址

来自的全部错误是

[emerg] 460#460: bind() to A.B.C.D:443 failed (99: Cannot assign requested address)

尽管,你的单元文件配置为用systemd运行在 , nginx可能还是企图监听一个配置了但没添加到任何接口的地址. 确保这种情况下手动start (启动) nginx (从而确保IP地址配置正确). 配置 nginx监听任何地址都会解决这个问题. 如果你的情况是需要监听特定的地址, 一个可能的解决方案是重新配置systemd.

在所有网络配置工作和指定IP地址都结束后开启nginx, 在nginx.service里附加 到 然后 start/enable systemd-networkd-wait-online.service.

参见

This article is issued from Archlinux. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.