Difference between revisions of "Nginx (简体中文)"

From ArchWiki
Jump to navigation Jump to search
(Created page with 'Category:Networking (简体中文) Category:HOWTOs (简体中文) Category:Web Server (简体中文) {{i18n_links_start}} {{i18n_entry|English|NginX}} {{i18n_entry|…')
 
(错别字修改)
 
(77 intermediate revisions by 18 users not shown)
Line 1: Line 1:
[[Category:Networking (简体中文)]]
+
[[Category:简体中文]]
[[Category:HOWTOs (简体中文)]]
+
[[Category:Web server (简体中文)]]
[[Category:Web Server (简体中文)]]
+
[[de:Nginx]]
{{i18n_links_start}}
+
[[en:Nginx]]
{{i18n_entry|English|NginX}}
+
[[ja:Nginx]]
{{i18n_entry|简体中文|Archlinux NginX配置指南(简体中文)}}
+
[[ru:Nginx]]
{{i18n_links_end}}
+
'''Nginx''' (读作"engine X") 由Igor Sysoev(俄罗斯)于2005年编写,是一个免费、开源、高性能的HTTP服务器和反向代理,也可以作为一个IMAP/POP3代理服务器。Nginx因为稳定,丰富的功能集,配置简单,资源占用低而闻名世界。
  
==概述==
+
这篇文章描述了如何设置nginx并且如何通过[[#FastCGI]]集成[[PHP]].
Nginx (pronounced "engine X") written by Igor Sysoev (Russia) in 2005, is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. According to the ''[http://news.netcraft.com/archives/2009/06/17/june_2009_web_server_survey.html June 2009 Web Server Survey from Netcraft]'', nginx now hosts nearly 3.37% of all domains worldwide, while lighttpd hosts about 1.8%. Nginx is now well known for its stability, rich feature set, simple configuration, and low resource consumption.
 
  
==安装==
+
== 安装 ==
The nginx package is now in the [community] repository, the latest stable version is nginx-0.7.62:
 
  # pacman -S nginx
 
or you can compile and install the latest development version with ABS.
 
  
==开启服务==
+
[[pacman (简体中文)|安装]] 包 {{Pkg|nginx-mainline}} (主线(mainline)分支:新功能,更新,bug解决) 或者 {{Pkg|nginx}} (稳定分支: 只是bug解决).
Run
 
  # /etc/rc.d/nginx start
 
The nginx server should now be running.
 
The default main page is /srv/http/nginx/index.html(the nginx package does not install a default index.html, you must create this now).
 
After creating a /srv/http/nginx/index.html webpage, you can use this URL: <nowiki>http://127.0.0.1</nowiki> to test if nginx is working.
 
  
To enable service by default at startup just add "nginx" to the DAEMONS in the /etc/rc.conf.
+
推荐使用主线分支。使用稳定分支的主要原因是担心新功能的造成的不良影响,比如与第三方模块不兼容或者开发者疏忽导致引入[https://nginx.org/en/download.html 新功能]时出现bug.
  
==配置==
+
{{Note|所有在[[official repositories]]可用的模块都需要''nginx''(与''nginx-mainline''相反)作为依赖.你可能需要在下决定选''nginx''还是''nginx-mainline''前,先查看下模块名单。''nginx-mainline''的模块能在[[Arch User Repository]]找到.}}
You can modify the configurations by editing the files in /etc/nginx/conf. (/etc/nginx/conf/nginx.conf being the main config file) .
 
  
More details can be referred from [http://wiki.codemongers.com/NginxConfiguration Nginx Configuration Examples].
+
对于在基于chroot环境下的额外安全性,可查阅[[#Installation in a chroot]]{{Broken section link}}.
  
===Use PHP/Python with nginx ===
+
== 启动 ==
FastCGI technology is introduced into nginx to work with many external tools and servers, ie: PHP and Python. So, you cannot use PHP or Python service unless FastCGI server has been started.
 
  
PHP and Python can be run as FastCGI application and can process FastCGI requests from nginx.
+
[[Start/enable]] {{ic|nginx.service}}.
  
'''Step 1: Start the FastCGI server'''<br>
+
默认在 http://127.0.0.1 页面服务的页面是 {{ic|/usr/share/nginx/html/index.html}}.
There are two different ways to run FastCGI server:
 
  
1) Directly running PHP’s built-in FastCGI server - this method does not require any third party tools.
+
== 配置 ==
  
Archlinux's '''''php''''' package in the [extra] repository has already enabled FastCGI support.
+
安装nginx后的第一步该干什么写在[http://nginx.org/en/docs/beginners_guide.html 用户手册]里了. 你可以通过编辑在{{ic|/etc/nginx/}}下的文件来修改配置。主配置文件在{{ic|/etc/nginx/nginx.conf}}.
  
For example:
+
更多细节和例子,你可以在 http://wiki.nginx.org/Configuration 和 [http://nginx.org/en/docs/ 官方文档]找到.
  $ sudo pacman -S php php-cgi
 
  $ php-cgi -b 127.0.0.1:9000 &
 
  
This command sets up the local machine as a FastCGI server using port 9000.
+
下面的例子包含了最常见的使用案例.我们假定你使用的是默认文件路径({{ic|/usr/share/nginx/html}}). 如果你改了路径,用你自己的路径替代.
  
'''Note:''' Since nginx 0.7.62, the default internal $document_root is /etc/nginx/html which is moved outside the default nginx directory and symlinked to /srv/http/nginx in the arch package. For php to work with nginx 0.7.62 the open_basedir in /etc/php/php.ini has to contain the directory /etc/nginx/html. IE. open_basedir = /etc/nginx/html/:/srv/http/:/home/:/tmp/:/usr/share/pear/
+
=== 配置例子 ===
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
user http;
  
Below is a daemon script to be used in rc.conf on boot to start php-cgi. save it as /etc/rc.d/fastcgi and add "fastcgi" to your DAEMONS array in /etc/rc.conf
+
# May be equal to `grep processor /proc/cpuinfo | wc -l`
  #!/bin/bash
+
worker_processes auto;
 
+
worker_cpu_affinity auto;
  . /etc/rc.conf
 
  . /etc/rc.d/functions
 
 
 
  case "$1" in
 
    start)
 
  stat_busy 'Starting Fastcgi Server'
 
  if /usr/bin/php-cgi -b 127.0.0.1:9000 &
 
  then
 
  add_daemon fastcgi
 
  stat_done
 
  else
 
  stat_fail fi
 
  fi
 
  ;;
 
    stop)
 
  stat_busy 'Stopping Fastcgi Server'
 
  [ -e /var/run/daemons/fastcgi ] && kill $(pidof php-cgi) &> /dev/null;
 
  if [ $? -gt 0 ]; then
 
  stat_fail
 
  else
 
  rm_daemon fastcgi
 
  stat_done
 
  fi
 
  ;;
 
    restart)
 
  $0 stop
 
  $0 start
 
  ;;
 
    *)
 
  echo "Usage: $0 {start|stop|restart}"
 
  esac
 
  
2) Running PHP inside some third-party wrapper - this can be more comfortable than first method because of more flexibility. You can install ''fcgi''(which includes ''cgi-fcgi''), ''lighttpd'' (which includes ''spawn-fcgi''), and '''''PHP-FPM ''''' (PHP FastCGI Process Manager, which is now in the AUR. Recommended.)
+
# PCRE JIT can speed up processing of regular expressions significantly.
 +
pcre_jit on;
  
For example:
+
events {
 +
    # Should be equal to `ulimit -n`
 +
    worker_connections 1024;
  
  # cgi-fcgi -start -connect localhost:9000 /usr/bin/php-cgi
+
    # Let each process accept multiple connections.
 +
    multi_accept on;
  
  # spawn-fcgi -a 127.0.0.1 -p 9000 -C 9 -f /usr/bin/php-cgi
+
    # Preferred connection method for newer linux versions.
 +
    use epoll;
 +
}
  
  # php --fpm -b 127.0.0.1:9000
+
http {
 +
    server_tokens off; # Disables the “Server” response header
 +
    charset utf-8;
  
'''Note:''' You may need to edit /etc/php-fpm.conf at first. More details about php-fpm can be referred from '''[http://php-fpm.anight.org/ php-fpm website]'''
+
    # Sendfile copies data between one FD and other from within the kernel.
 +
    # More efficient than read() + write(), since the requires transferring
 +
    # data to and from the user space.
 +
    sendfile on;
  
'''Note:''' Sometimes, you may encounter some permission issue when running some third party software to start PHP as FastCGI-server. See '''[[Nginx#Tips|Tips]]'''.
+
    # Tcp_nopush causes nginx to attempt to send its HTTP response head in one
 +
    # packet, instead of using partial frames. This is useful for prepending
 +
    # headers before calling sendfile, or for throughput optimization.
 +
    tcp_nopush on;
  
 +
    # Don't buffer data-sends (disable Nagle algorithm). Good for sending
 +
    # frequent small bursts of data in real time.
 +
    #
 +
    tcp_nodelay on;
  
'''Step 2: Edit /etc/nginx/conf/nginx.conf'''<br>
+
    # On Linux, AIO can be used starting from kernel version 2.6.22.
Un-comment these lines and edit in the file to be as follows:
+
    # It is necessary to enable directio, or otherwise reading will be blocking.
  location ~ \.php$ {
+
    # aio threads;
      root          html;
+
    # aio_write on;
      fastcgi_pass  127.0.0.1:9000;
+
    # directio 8m;
      fastcgi_index  index.php;
+
 
      fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
+
    # Caches information about open FDs, freqently accessed files.
      include        fastcgi_params
+
    # open_file_cache max=200000 inactive=20s;
 +
    # open_file_cache_valid 60s;
 +
    # open_file_cache_min_uses 2;
 +
    # open_file_cache_errors on;
 +
 
 +
    # http://nginx.org/en/docs/hash.html
 +
    types_hash_max_size 4096;
 +
    include mime.types;
 +
    default_type application/octet-stream;
 +
 
 +
    # Logging Settings
 +
    access_log off;
 +
 
 +
    # Gzip Settings
 +
    gzip on;
 +
    gzip_comp_level 6;
 +
    gzip_min_length 500;
 +
    gzip_proxied expired no-cache no-store private auth;
 +
    gzip_vary on;
 +
    gzip_disable "MSIE [1-6]\.";
 +
    gzip_types
 +
        application/atom+xml
 +
        application/javascript
 +
        application/json
 +
        application/ld+json
 +
        application/manifest+json
 +
        application/rss+xml
 +
        application/vnd.geo+json
 +
        application/vnd.ms-fontobject
 +
        application/x-font-ttf
 +
        application/x-web-app-manifest+json
 +
        application/xhtml+xml
 +
        application/xml
 +
        font/opentype
 +
        image/bmp
 +
        image/svg+xml
 +
        image/x-icon
 +
        text/cache-manifest
 +
        text/css
 +
        text/plain
 +
        text/vcard
 +
        text/vnd.rim.location.xloc
 +
        text/vtt
 +
        text/x-component
 +
        text/x-cross-domain-policy;
 +
 
 +
    # index index.php index.html index.htm;
 +
    include sites-enabled/*; # See Server blocks
 +
}
 +
</nowiki>}}
 +
 
 +
=== 通用配置 ===
 +
 
 +
==== 进程和连接 ====
 +
 
 +
你应该为{{ic|worker_processes}}选一个合适的值. 这项设置最终决定了nginx接受多少连接和它会使用多少处理器。通常来说,把它设置成你系统里的硬件线程数就好了。可选的是,自从 1.3.8 和 1.2.5版本以后, {{ic|worker_processes}} 接受 {{ic|auto}} 作为值了, 他会自动检测最优值 ([http://nginx.org/en/docs/ngx_core_module.html#worker_processes source]).
 +
 
 +
nginx的最大连接数有下面的公式给出{{ic|1=max_clients = worker_processes * worker_connections}}.
 +
 
 +
==== 不同用户间的使用 ====
 +
 
 +
默认的, {{Pkg|nginx}} 用 {{ic|root}} 身份运行主进程二用{{ic|http}}用户运行worker进程. 如果要用其它用户运行worker进程, 改变{{ic|nginx.conf}}里的{{ic|user}}参数就好了:
 +
 
 +
{{hc|/etc/nginx/nginx.conf|
 +
user ''user'' [''group''];
 +
}}
 +
 
 +
如果组(group)忽略不写的话,就会用和''user''相同的名字来代替
 +
 
 +
{{Tip|1=其实也可以使用[[systemd]]以{{ic|root}}身份在没有任何东西运行的情况下运行nginx . 可以看 [[#使用 systemd无权限运行]].}}
 +
 
 +
==== server代码块 ====
 +
 
 +
通过使用 {{ic|server}} 模块,可以实现服务多个域名. 这些模块可以类比为[[Apache]]中的"VirtualHosts" . 也可查阅 [https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/ 上游文献].
 +
 
 +
下面的例子,服务器监听IPv4和IPv6的80端口的进入流量的两个域名,分别是{{ic|domainname1.dom}} 和{{ic|domainname2.dom}}:
 +
 
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
...
 +
server {
 +
        listen 80;
 +
        listen [::]:80;
 +
        server_name domainname1.dom;
 +
        root /usr/share/nginx/domainname1.dom/html;
 +
        location / {
 +
          index index.php index.html index.htm;
 
         }
 
         }
 +
}
 +
 +
server {
 +
        listen 80;
 +
        listen [::]:80;
 +
        server_name domainname2.dom;
 +
        root /usr/share/nginx/domainname2.dom/html;
 +
        ...
 +
}
 +
...
 +
</nowiki>}}
 +
 +
[[Restart]](重启) {{ic|nginx.service}} 来让改变的配置生效.
 +
 +
确保主机名是可以被设置好的DNS服务器比如[[BIND]] 或者 [[dnsmasq]]解析的,或者可以查看下[[Network configuration#Local network hostname resolution]].
 +
 +
=====管理服务器入口=====
 +
 +
把不同的{{ic|server}}模块放到不同的文件里是可能的.这样的话可以让你很轻易的开启和禁用特定的站点.
 +
 +
创建以下文件夹:
 +
 +
# mkdir /etc/nginx/sites-available
 +
# mkdir /etc/nginx/sites-enabled
 +
 +
在{{ic|sites-available}}文件夹下创建一个文件包含一个或更多服务器模块:
 +
 +
{{hc|/etc/nginx/sites-available/example|<nowiki>
 +
server {
 +
    ..
 +
}
 +
</nowiki>}}
 +
 +
把{{ic| include sites-enabled/*;}}放到{{ic|http}} 模块的末尾:
 +
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
...
 +
http {
 +
    ...
 +
    include sites-enabled/*;
 +
}
 +
...
 +
</nowiki>}}
 +
 +
如果要启用一个 {{ic|server}} 模块, 只需要简单的创建一个符号链接:
 +
 +
# ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
 +
 +
如果要移除一个 {{ic|server}}:
 +
 +
# unlink /etc/nginx/sites-enabled/example
 +
 +
[[Reload]]/[[restart]] {{ic|nginx.service}} 来让配置生效.
 +
 +
==== TLS ====
 +
 +
{{Style|不要仅仅把 [[OpenSSL#Certificates]]的配置搬过来.}}
 +
 +
[[OpenSSL]] 提供了TLS支持并且默认在Arch系统安装时安装了.
 +
 +
{{Tip|
 +
* 你可能需要在配置SSL之前阅读 [http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate ngx_http_ssl_module] 的文档.
 +
* [[Let’s Encrypt]] 时一个免费的、自动的、开放的证书颁发机构.有一个插件可以从茉莉花请求有效的证书并自动配置的.
 +
* Mozilla有一篇有用的 [[MozillaWiki:Security/Server Side TLS|TLS文章]],也有一个[https://mozilla.github.io/server-side-tls/ssl-config-generator/ 自动化工具]来创建一个更安全的配置.
 +
* [https://cipherli.st Cipherli.st] 为大多数现代web服务器提供了强大的 TLS 实现例子和教程.
 +
}}
 +
 +
{{Warning|如果你打算实现TLS, 你必须知道一些变化和实现 [[wikipedia:Transport Layer Security#Attacks against TLS/SSL|仍然被攻击时很脆弱]][https://weakdh.org/#affected]. 如果想知道TLS内现存的漏洞和怎样用合适的方法到nginx上,可访问 https://weakdh.org/sysadmin.html}}
 +
 +
创建一个私钥和自签名的证书,这对大多数不需要 [[OpenSSL#Generate a certificate signing request|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
 +
 +
{{Note|{{ic|-days}} 是可选的并且RSA键大小最低可到2048 (默认值).}}
 +
 +
如果你需要创建一个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
 +
 +
{{Note|如果你需要更多的 ''openssl''选项, 可以阅读它们的 [https://www.openssl.org/docs/apps/openssl.html 手册] 或者读它们的[https://www.openssl.org/docs/ 扩展文档].}}
 +
 +
使用TLS的 {{ic|/etc/nginx/nginx.conf}} 的基本例子:
 +
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
http {
 +
    ...
 +
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
 +
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 +
    ..
 +
 +
    # Redirect to HTTPS
 +
    server {
 +
        listen 80;
 +
        server_name localhost;
 +
        return 301 https://$host$request_uri;
 +
    }
 +
 +
    server {
 +
        #listen 80; # Uncomment to also listen for HTTP requests
 +
        listen 443 ssl http2; # HTTP/2 is only possible when using SSL
 +
        server_name localhost;
 +
 +
        ssl_certificate ssl/server.crt;
 +
        ssl_certificate_key ssl/server.key;
 +
 +
        root /usr/share/nginx/html;
 +
        location / {
 +
            index index.html index.htm;
 +
        }
 +
    }
 +
}
 +
</nowiki>}}
 +
 +
[[Restart]] {{ic|nginx.service}} 来让配置生效.
 +
 +
==== 分用户目录 ====
 +
 +
如果要复制Apache式的 {{ic|~user}} URLs 到用户的 {{ic|~/public_html}} 目录, 可以尝试下面的. (Note: 如果下面的两个规则都用了的话一定要把更清楚地PHP规则放在前面.)
 +
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
...
 +
server {
 +
    ...
 +
    # PHP in user directories, e.g. http://example.com/~user/test.php
 +
    location ~ ^/~(.+?)(/.+\.php)$ {
 +
        alias          /home/$1/public_html$2;
 +
        fastcgi_pass  unix:/run/php-fpm/php-fpm.sock;
 +
        fastcgi_index  index.php;
 +
        include        fastcgi.conf;
 +
    }
 +
 +
    # User directories, e.g. http://example.com/~user/
 +
    location ~ ^/~(.+?)(/.*)?$ {
 +
        alias    /home/$1/public_html$2;
 +
        index    index.html index.htm;
 +
        autoindex on;
 +
    }
 +
    ...
 +
}
 +
...
 +
</nowiki>}}
 +
 +
查阅 [[#PHP实现]] 来阅读更多 {{ic|nginx}}的PHP配置.
 +
 +
重启 {{ic|nginx.service}} 来启用新配置.
 +
 +
=== FastCGI ===
 +
 +
[[Wikipedia:FastCGI|FastCGI]], 也叫 FCGI, 是适用于web服务器和交互式程序的接口的协议. FastCGI 是早期的 [[Wikipedia:Common Gateway Interface|Common Gateway Interface]] (CGI)的变种; FastCGI的主要目的是减少CGI程序和web服务器交互的开销,来允许服务器同时处理更多网页请求.
 +
 +
FastCGI技术被引进nginx是为了与许多外部工具,比如. [[Perl]], [[PHP]] and [[Python]]
 +
 +
==== PHP实现 ====
 +
 +
[https://php-fpm.org/ PHP-FPM] 是建议使用的用作[[PHP]]的FastCGI服务器的解决方案.
 +
 +
[[Install]](安装) {{Pkg|php-fpm}} 然后确保 [[PHP]] 被安装配置正确. PHP-FPM的主要配置文件是  {{ic|/etc/php/php-fpm.conf}}. 基础使用的话默认配置就足够了.
 +
 +
最后, [[enable]] 和 [[start]] {{ic|php-fpm.service}}.
 +
 +
{{Note|
 +
* 如果你 [[#不同用户间的使用|用不同的用户运行nginx]], 确保PHP-FPM套接字文件能被这个用户访问,或者通过一个TCP套接字.
 +
* 如果你在chroot的环境下运行nginx (chroot 是 {{ic|/srv/nginx-jail}}, web页面在 {{ic|/srv/nginx-jail/www}}), 你必须修改文件 {{ic|/etc/php/php-fpm.conf}} ,把 {{ic|chroot /srv/nginx-jail}} 和 {{ic|1=listen = /srv/nginx-jail/run/php-fpm/php-fpm.sock}} 两行加入到指令部分 (默认是{{ic|[www]}}). 如果缺少的话请为套接字文件创建目录。此外, 动态链接到依赖的模块,你需要将这些依赖复制到(比如. 对于php-imagick, 你需要复制ImageMagic的库复制到chroot, 而不是imagick.so他自己).
 +
}}
 +
 +
===== nginx 配置 =====
 +
 +
======添加到主配置 ======
 +
 +
当服务一个PHP web程序时,一个PHP-FPM的{{ic|location}}应该包括在 [[#server代码块]]里 [https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/],比如.:
 +
 +
{{hc|/etc/nginx/sites-available/example|<nowiki>
 +
server {
 +
    ...
 +
 +
    root /usr/share/nginx/html;
 +
    location / {
 +
        index index.html index.htm;
 +
    }
 +
 +
    location ~ [^/]\.php(/|$) {
 +
        # Correctly handle request like /test.php/foo/blah.php or /test.php/
 +
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
 +
 +
        try_files $uri $document_root$fastcgi_script_name =404;
 +
 +
        # Mitigate https://httpoxy.org/ vulnerabilities
 +
        fastcgi_param HTTP_PROXY "";
 +
 +
        fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
 +
        fastcgi_index index.php;
 +
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 +
        include fastcgi_params;
 +
    }
 +
}
 +
</nowiki>}}
 +
 +
如果需要用PHP处理其他文件扩展名 (比如. ''.html'' and ''.htm''):
 +
 +
location ~ [^/]\.php'''|html|htm'''(/|$) {
 +
    ...
 +
}
 +
 +
非 ''.php'' 的PHP-FPM扩展处理都需要被直接加入到 {{ic|/etc/php/php-fpm.d/www.conf}}:
 +
 +
security.limit_extensions = .php .html .htm
 +
 +
{{Note|需要注意下 {{ic|fastcgi_pass}} 参数, 因为它被选定的FastCGI服务器在它的配置文件里定义TCP或Unix套接字. 对应{{ic|php-fpm}}的'''default''' (Unix) 套接字是  是:
 +
 +
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
 +
 +
你可能想要使用常见的 TCP 套接字, '''not default''',
 +
 +
fastcgi_pass 127.0.0.1:9000;
 +
 +
Unix 的域名套接字应该会快一点.}}
 +
 +
======PHP配置文件======
 +
如果你使用多个 {{ic|server}} 块来启用PHP支持, 创建一个PHP配置文件可能更简单:
 +
{{hc|/etc/nginx/php.conf|<nowiki>
 +
location ~ \.php$ {
 +
    ...
 +
}
 +
</nowiki>}}
 +
 +
为特定的服务器启用PHP支持, 只需要简单的加入 {{ic|include php.conf}}就好了:
 +
{{hc|/etc/nginx/nginx.conf|<nowiki>
 +
server {
 +
    server_name example.com;
 +
    ...
 +
    include php.conf;
 +
}
 +
</nowiki>}}
 +
 +
===== 测试配置 =====
 +
 +
你需要[[restart]]  {{ic|php-fpm.service}} 和 {{ic|nginx.service}} 服务单元来让配置生效,如果配置之前被改变过的话.
 +
 +
测试FastCGI实现, 在{{ic|root}}目录下创建一个新的PHP文件,内容是:
 +
 +
<?php phpinfo(); ?>
 +
 +
把这个文件用浏览器打开,你应该就可以看到现在的PHP配置的信息页了.
 +
 +
==== CGI 实现 ====
 +
 +
这个实现是CGI程序所需要的.
 +
 +
===== fcgiwrap =====
 +
 +
[[Install]] {{Pkg|fcgiwrap}}. 配置文件是 {{ic|/usr/lib/systemd/system/fcgiwrap.socket}}. [[Enable]] 并且 [[start]] {{ic|fcgiwrap.socket}}.
 +
 +
====== 多个工人线程 ======
 +
 +
如果你想生成多个工人线程建议你使用{{AUR|multiwatch}}, 它会处理崩溃的子进程. 如果你需要使用{{ic|spawn-fcgi}} 来创建Unix套接字,因为multiwatch无法处理systemd创建的套接字,尽管fcgiwarp它自己在单元文件中直接调用页没有任何问题.
 +
 +
从 {{ic|/usr/lib/systemd/system/fcgiwrap.service}} 复制单元文件到 {{ic|/etc/systemd/system/fcgiwrap.service}} (和{{ic|fcgiwrap.socket}} 单元, 如果存在的话), 修改{{ic|ExecStart}}行到满足你需要的地方. 下面是使用{{AUR|multiwatch}}的单元文件. 确保{{ic|fcgiwrap.socket}} 没有被start(开始)或enable(启用), 因为他会和这些单元冲突:
 +
 +
{{hc|/etc/systemd/system/fcgiwrap.service|2=
 +
[Unit]
 +
Description=Simple CGI Server
 +
After=nss-user-lookup.target
 +
 +
[Service]
 +
ExecStartPre=/bin/rm -f /run/fcgiwrap.socket
 +
ExecStart=/usr/bin/spawn-fcgi -u http -g http -s /run/fcgiwrap.sock -n -- /usr/bin/multiwatch -f 10 -- /usr/sbin/fcgiwrap
 +
ExecStartPost=/usr/bin/chmod 660 /run/fcgiwrap.sock
 +
PrivateTmp=true
 +
Restart=on-failure
 +
 +
[Install]
 +
WantedBy=multi-user.target
 +
}}
 +
 +
可以自定义 {{ic|-f 10}} 来改变生成的子进程的数量.
 +
 +
{{Warning|{{ic|ExecStartPost}}行是需要的,因为当为{{ic|spawn-fcgi}}使用{{ic|-M 660}}参数选项时会出现奇怪的情况.这有可能是一个Bug}}
 +
 +
===== nginx配置 =====
 +
 +
在每个服务了一个CGI程序的{{ic|server}} 块内都应该有一个像下面的 {{ic|location}} 块:
 +
 +
location ~ \.cgi$ {
 +
      root          /path/to/server/cgi-bin;
 +
      fastcgi_pass  unix:/run/fcgiwrap.sock;
 +
      include        fastcgi.conf;
 +
}
 +
 +
{{ic|fcgiwrap}} 的默认套接字文件是 {{ic|/run/fcgiwrap.sock}}.
 +
 +
如果你一直出现 {{ic|502 - bad Gateway}} 错误,你应该检查你的CGI程序是否宣告了下面内容的mime类型。如果是html,他应该是{{ic|Content-type: text/html}}.
 +
 +
== 在chroot环境下安装 ==
 +
 +
在[[chroot]]环境里运行nginx是有一个额外的安全层的.为了最大化安全性chroot环境应该只包括nginx服务器需要的文件,并且所有的文件都应该尽可能有严格的权限,比如像{{ic|/usr/bin}}这样的文件夹应该不可读和不可写.
 +
 +
Arch默认有一个{{ic|http}}用户和组来运行服务器chroot将会在{{ic|/srv/http}}.
 +
 +
创建这个监狱(指chroot环境)的Perl脚本能在[https://gist.github.com/4365696 jail.pl gist]找到. 你可以直接用那个脚本或者跟着这篇文章的指示继续阅读下去. 它应该用root运行. 你需要在它起作用之前取消一行注释.
 +
 +
=== 创建必要的设备 ===
 +
 +
nginx需要 {{ic|/dev/null}}, {{ic|/dev/random}}, 和 {{ic|/dev/urandom}}. 为了安装这些需要在chroot环境下创建 {{ic|/dev/}} 目录和用''mknod''添加设备尽量别挂载所有的{{ic|/dev/}}来确保, 尽管在chroot受到攻击的情况下, 攻击者必须突破chroot来获取像{{ic|/dev/sda1}}的重要文件.
 +
 +
{{Tip|确保 {{ic|/srv/http}} 挂载了并且没有nodev选项}}
 +
{{Tip|查看 {{man|1|mknod}} 和 {{ic|<nowiki>ls -l /dev/{null,random,urandom}</nowiki>}} 来更好的理解''mknod''选项.}}
 +
 +
# 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文档目录在 {{ic|/srv/http/www}}.
 +
 +
# 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
 +
 +
然后挂载 {{ic|$JAIL/tmp}} 和 {{ic|$JAIL/run}} 作为tmpfs. 大小应该限制确保攻击者无法吃掉所有的内存.
 +
 +
# mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
 +
# mount -t tmpfs none $JAIL/tmp -o 'noexec,size=100M'
 +
 +
为了在重启后保留挂载,下面的入口应该被添加到{{ic|/etc/fstab}}:
 +
 +
{{hc|/etc/fstab|<nowiki>
 +
tmpfs  /srv/http/run  tmpfs  rw,noexec,relatime,size=1024k  0      0
 +
tmpfs  /srv/http/tmp  tmpfs  rw,noexec,relatime,size=102400k 0      0
 +
</nowiki>}}
 +
 +
=== 填充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''来把它们列出来然后复制到正确的地方。最好是复制而不是链接来确保就算攻击者获取的写权限也服务法修改真实的系统文件.
 +
 +
{{hc|$ ldd /usr/bin/nginx|<nowiki>
 +
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)</nowiki>}}
 +
 +
对于 {{ic|/usr/lib}} 你可以使用下面的命令:
 +
 +
# cp $(ldd /usr/bin/nginx | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
 +
 +
用下面的命令来复制 {{ic|ld-linux-x86-64.so}}:
 +
 +
# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib
 +
 +
{{Note|不要尝试复制 {{ic|linux-vdso.so}}: 他不是真实存在的库,不在{{ic|/usr/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权限的话.
 +
 +
{{hc|$JAIL/etc/group|
 +
http:x:33:
 +
nobody:x:99:
 +
}}
 +
 +
{{hc|$JAIL/etc/passwd|
 +
http:x:33:33:http:/:/bin/false
 +
nobody:x:99:99:nobody:/:/bin/false
 +
}}
 +
 +
{{hc|$JAIL/etc/shadow|
 +
http:x:14871::::::
 +
nobody:x:14871::::::
 +
}}
 +
 +
{{hc|$JAIL/etc/gshadow|
 +
http:::
 +
nobody:::
 +
}}
 +
 +
# 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 ===
 +
 +
在修改{{ic|nginx.service}}单元文件之前, 把它复制进 {{ic|/etc/systemd/system/}} 因为这里的单元文件的优先级比 {{ic|/usr/lib/systemd/system/}}高. 这样更新nginx救护不爱修改你自定义的 ''.service'' 文件.
 +
 +
# cp /usr/lib/systemd/system/nginx.service /etc/systemd/system/nginx.service
 +
 +
systemd单元必须在开启chroot里的nginx之前修改,以http用户的身份,将pid文件放到chroot里.
 +
 +
{{Note|我不确定pid文件是否需要被放到chroot里.}}
 +
 +
{{hc|/etc/systemd/system/nginx.service|<nowiki>
 +
[Unit]
 +
Description=A high performance web server and a reverse proxy server
 +
After=syslog.target network.target
 +
 +
[Service]
 +
Type=forking
 +
PIDFile=/srv/http/run/nginx.pid
 +
ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;'
 +
ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;'
 +
ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload
 +
ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit
 +
 +
[Install]
 +
WantedBy=multi-user.target</nowiki>}}
 +
 +
{{Note|用pacman升级nginx并不会升级chroot里的nginx。如果你要升级的话可能需要手动重复上面的步骤。不要忘记更新它链接的库.}}
 +
 +
你现在可以安全的写在非chroot环境下的nginx安装.
 +
 +
# pacman -Rsc nginx
 +
 +
如果你不移除非chroot环境的nginx安装,你可能想要确认运行的nginx是不是chroot环境里的, 你可以查询 {{ic|/proc/''PID''/root}} 链接到哪里. 如果链接到 {{ic|/srv/http}}  而不是{{ic|/}}那运行的就是chroot环境下的nginx.
 +
 +
# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done
 +
 +
== 建议和技巧 ==
 +
 +
=== 使用 [[systemd]]无权限运行 ===
 +
 +
[[systemd#Editing_provided_units|编辑 nginx.service]] 并在 {{ic|[Service]}}下设置 {{ic|User}},还可可选的设置 {{ic|Group}} :
 +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
 +
[Service]
 +
User=''user''
 +
Group=''group''
 +
}}
 +
 +
我们可以通过强化服务以防止提升特权:
 +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
 +
[Service]
 +
...
 +
NoNewPrivileges=yes
 +
}}
 +
 +
{{Tip|1=查看 [http://www.freedesktop.org/software/systemd/man/systemd.exec.html#User= systemd.exec(5)] 获取更多限权选项.}}
 +
 +
然后我们需要确保 {{ic|''user''}}(即前面设置的用户)有权限做它需要做的事:
 +
 +
<dl>
 +
<dt>Port</dt>
 +
<dd>
 +
Linux默认不允许非{{ic|root}}用户的进程绑定低于1024的端口,高于1024的端口可以用:
 +
 +
{{hc|/etc/nginx/nginx.conf|
 +
server {
 +
        listen 8080;
 +
}
 +
}}
 +
 +
{{Tip|1=如果你想nginx可以访问80或者443端口,配置你的[[firewall]](防火墙)来重定向从80或者443的请求刀nginx监听的端口.}}
 +
</dd>
 +
<dd>
 +
或者你可以提升nginx进程的CAP_NET_BIND_SERVICE权限,它能允许绑定低于1024的端口:
 +
 +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
 +
[Service]
 +
...
 +
CapabilityBoundingSet=
 +
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
 +
AmbientCapabilities=
 +
AmbientCapabilities=CAP_NET_BIND_SERVICE
 +
}}
 +
</dd>
 +
 +
<dt>PID文件</dt>
 +
<dd>
 +
{{Pkg|nginx}} 默认使用 {{ic|/run/nginx.pid}} . 我们可以创建一个文件夹,''user''有写权限,然后八我们的PID文件放到那里. 一个使用[[systemd#Temporary_files|systemd-tmpfiles]]的例子:
 +
 +
{{hc|/etc/tmpfiles.d/nginx.conf|2=
 +
d /run/nginx 0775 root ''group'' - -
 +
}}
 +
 +
运行配置:
 +
 +
# systemd-tmpfiles --create
 +
 +
基于最初的{{ic|nginx.service}}[[Edit]](编辑)PID的值:
 +
 +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
 +
[Service]
 +
...
 +
PIDFile=/run/nginx/nginx.pid
 +
ExecStart=
 +
ExecStart=/usr/bin/nginx -g 'pid /run/nginx/nginx.pid; error_log stderr;'
 +
ExecReload=
 +
ExecReload=/usr/bin/nginx -s reload -g 'pid /run/nginx/nginx.pid;'
 +
}}
 +
</dd>
 +
 +
<dt>{{ic|/var/lib/nginx/*}}</dt>
 +
<dd>
 +
一些{{ic|/var/lib/nginx}}下面的目录需要被以 {{ic|root}} 运行的nginx的引导. 所以有必要启动整个服务器老这么做, nginx会在一次简单的 [[#配置验证]]之后自己这么做. 所以只要运行这些中的一个就好啦.
 +
</dd>
 +
 +
<dt>日志文件& 目录权限</dt>
 +
<dd>
 +
运行配置测试的步骤会创建一个悬空的{{ic|root}}拥有的日志文件. 移除{{ic|/var/log/nginx}}下面的日志文件开始刷新.
 +
<dd> 
 +
启用nginx服务的用户需要{{ic|/var/log/nginx}}的写权限. 这可能需要 [[File_permissions_and_attributes#Changing_permissions|改变权限]] 和/或 你系统这个目录的所有权.
 +
</dl>
 +
 +
现在一切都好了。开始[[start]](启动) nginx, 然后就可以想用你的非root下的nginx了.
 +
 +
{{Tip|同样的配置可能还要针对你的 [[#FastCGI|FastCGI server]] 再做一次.}}
 +
 +
=== 针对systemd的可选脚本 ===
 +
 +
在纯systemd的情况下,你可以享受chroot + systemd的好处. [http://0pointer.de/blog/projects/changing-roots.html] 基于在下面的文件设置 [http://wiki.nginx.org/CoreModule#user 用户组]一个 pid :
 +
 +
{{hc|/etc/nginx/nginx.conf|2=
 +
user http;
 +
pid /run/nginx.pid;
 +
}}
 +
 +
文件的就绝对路径是{{ic|/srv/http/etc/nginx/nginx.conf}}.
 +
 +
{{hc|/etc/systemd/system/nginx.service|2=
 +
[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默认加载{{ic| -c /etc/nginx/nginx.conf}},二这也是一个好主意.
 +
 +
作为可选项,你可以在chroot环境下'''仅仅'''运行 {{ic|ExecStart}},{{ic|RootDirectoryStartOnly}}参数要调成,查看 {{ic|yes}} [http://www.freedesktop.org/software/systemd/man/systemd.service.html systemd服务手册] 或者在挂载点生效前启用它,或者一个 [http://www.freedesktop.org/software/systemd/man/systemd.path.html systemd路径]也行.
 +
 +
{{hc|/etc/systemd/system/nginx.path|2=
 +
[Unit]
 +
Description=nginx (Chroot) path
 +
[Path]
 +
PathExists=/srv/http/site/Public_html
 +
[Install]
 +
WantedBy=default.target
 +
}}
 +
 +
[[Enable]](启用)创建的{{ic|nginx.path}} 然后把 {{ic|/etc/systemd/system/nginx.service}}里的{{ic|1=WantedBy=default.target}} 变成 {{ic|1=WantedBy=nginx.path}} .
 +
 +
单元文件里的{{ic|PID文件}}允许systemd监控进程(需要绝对路径). 如果这不是你想要的, 你可以改变默认的“一击”类型, 然后删除单元文件里的参照.
 +
 +
=== Nginx Beautifier (nginx美化器)===
 +
 +
{{AUR|nginxbeautifier}} 是一个命令行工具,用来美化和格式化nginx配置文件.
 +
 +
=== 更好的头文件管理 ===
 +
 +
Nginx有一个非常不直观的头文件管理系统,头文件只能在一个上下文中定义, 任何其它的头文件都会被忽略. 为了补救这个,我们可以安装 [https://github.com/openresty/headers-more-nginx-module#more_set_headers headers-more-nginx] 模块.
 +
 +
[[Install]] (安装) {{Pkg|nginx-mod-headers-more}} 包. 这样会把模块安装到{{ic|/usr/lib/nginx/modules}} 目录下.
 +
 +
如果要加载模块把下面加入到你的nginx配置文件的第一行.
 +
 +
{{hc|/etc/nginx/nginx.conf|2=
 +
load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so";
 +
...
 +
}}
 +
 +
== 故障排除 ==
 +
 +
=== 配置验证 ===
 +
 +
# nginx -t
 +
 +
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
 +
nginx: configuration file /etc/nginx/nginx.conf test is successful
 +
 +
=== 接入本地IP重定向到localhost ===
 +
 +
Arch Linux [https://bbs.archlinux.org/viewtopic.php?pid=780561#p780561 论坛]的解决方案.
 +
 +
在 {{ic|/etc/nginx/nginx.conf}}有一行{{ic|server_name localhost}} ,前面没有 {{ic|#}}, 把下面的加入到这一行之下:
 +
 +
server_name_in_redirect off;
 +
 +
默认行为事nginx将任何请求重定向到配置里{{ic|server_name}}给定的值里.
 +
 +
=== Error: The page you are looking for is temporarily unavailable. Please try again later. (502 Bad Gateway) ===
 +
 +
这是因为FastCGI服务器还没有开启,或者套接字有错误的权限.
 +
 +
试一试 [https://stackoverflow.com/questions/4252368/nginx-502-bad-gateway/16497957#16497957 这个回答] 来解决502问题.
 +
 +
在Archlinux, 上面提到的配置文件在 {{ic|/etc/php/php-fpm.conf}}.
 +
 +
=== Error: No input file specified ===
 +
 +
1. 确定{{ic|/etc/php/php.ini}}里的 {{ic|open_basedir}}变量包含正确的路径,要和 {{ic|nginx.conf}} (通常在{{ic|/usr/share/nginx/}})里的{{ic|root}}变量一致. 当使用[http://php-fpm.org/ PHP-FPM] 作为PHP的FastCGI服务器时, 你可以添加 {{ic|<nowiki>fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/"</nowiki>;}} 到{{ic|nginx.conf}}里的 {{ic|location}} 代码块里,它能处理PHP文件.
 +
 +
2. 另一种情况是, {{ic|nginx.conf}}文件里,{{ic|location ~ \.php$}}代码块里的错误的 {{ic|root}} 变量. 确保{{ic|root}} 指向相同服务器里的{{ic|location /}}的同样的目录. 或者你直接把root设为全局,不要把它定义到任何地方
 +
 +
3. 检查权限: 比如. {{ic|http}} 是用户/组,  {{ic|755}} 是目录权限,{{ic|644}} 是文件权限. 记住,到{{ic|html}}目录的路径目录也必须有相同的权限. 可以查看 [[File permissions and attributes#Bulk chmod]] 来批量修改大量文件夹的权限.
 +
 +
4. 你没有让 {{ic|SCRIPT_FILENAME}} 包含你脚本的全部路径. 如果nginx的配置 ({{ic|fastcgi_param SCRIPT_FILENAME}}) 是对的的话, 这种错误意味着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,确保{{ic|/etc/php-fpm/php-fpm.d/www.conf}}里的{{ic|chroot}} 设置正确(或者 {{ic|/etc/php-fpm/php-fpm.conf}} 如果是旧版本的话)
 +
 +
=== Warning: Could not build optimal types_hash ===
 +
 +
当开启 {{ic|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
 +
 +
解决这个警告, 提升{{ic|http}}代码块的下面的键的值 [http://nginx.org/en/docs/http/ngx_http_core_module.html#types_hash_max_size] [http://nginx.org/en/docs/http/server_names.html]:
 +
 +
{{hc|/etc/nginx/nginx.conf|
 +
http {
 +
    types_hash_max_size 4096;
 +
    server_names_hash_bucket_size 128;
 +
    ...
 +
}
 +
}}
 +
 +
=== 不能指定请求地址 ===
 +
 +
来自{{ic|systemctl status nginx.service}}的全部错误是
 +
 +
[emerg] 460#460: bind() to A.B.C.D:443 failed (99: Cannot assign requested address)
 +
 +
尽管,你的单元文件配置为用systemd运行在 {{ic|network.target}} , nginx可能还是企图监听一个配置了但没添加到任何接口的地址. 确保这种情况下手动[[start]] (启动) nginx (从而确保IP地址配置正确). 配置 nginx监听任何地址都会解决这个问题. 如果你的情况是需要监听特定的地址, 一个可能的解决方案是重新配置systemd.
  
'''Step 3: Restart nginx'''<br>
+
在所有网络配置工作和指定IP地址都结束后开启nginx, 在{{ic|nginx.service}}里附加 {{ic|network-online.target}} 到 {{ic|After<nowiki>=</nowiki>}} 然后 [[start/enable]] {{ic|systemd-networkd-wait-online.service}}.
If nginx has been working, run
 
  # /etc/rc.d/nginx restart
 
  
Edit /srv/http/nginx/index.php,
 
  <?php
 
    phpinfo();
 
  ?>
 
view the URL: <nowiki>http://localhost/index.php</nowiki> with your browser, and you'll find PHP has worked well with nginx.
 
  
===安全性配置===
 
* allow/deny based on IP
 
[http://wiki.codemongers.com/NginxHttpAccessModule ngx_http_access_module]
 
* HTTP authentication
 
[http://wiki.codemongers.com/NginxHttpAuthBasicModule ngx_http_auth_basic_module]
 
* HTTPS support
 
[http://wiki.codemongers.com/NginxHttpSslModule ngx_http_ssl_module]
 
  
==Tips==
 
* Tuning the kernel for better speed
 
Add the followings to /etc/sysctl.conf
 
  net.ipv4.tcp_fin_timeout = 30
 
  net.ipv4.tcp_keepalive_time = 300
 
  net.ipv4.tcp_syncookies = 1
 
  net.ipv4.tcp_tw_reuse = 1
 
  net.ipv4.tcp_tw_recycle = 1
 
  net.ipv4.ip_local_port_range = 5000 65000
 
and make it active:
 
  # /sbin/sysctl -p
 
  
* The browser results in: '''the page you are looking for is temporarily unavailable. Please try again later. '''
 
This is because the FastCGI server has not been started.
 
  
* The browser results in: '''no input file specified'''
 
If the configuration of nginx (fastcgi_param  SCRIPT_FILENAME) is all right, this kind of error means php fail to load the requestd script. Usually it's simply an permission issue, you can just run php-cgi as root
 
  # spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi
 
or you should create some group and user to start the php-cgi. For instance :
 
  $ sudo groupadd www
 
  $ sudo useradd -g www www
 
  $ sudo chmod +w /srv/www/nginx/html
 
  $ sudo chown -R www:www /srv/www/nginx/html
 
  $ sudo spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/bin/php-cgi
 
  
Another occasion is that, wrong "root" argument in the "location ~ \.php$" section in nginx.conf, make sure the "root" points to the same directory as it in "location /" in the same server. Or you may just set root as global, don't define it in any location section.
 
  
Also keep in mind that your php script path was defined as /srv/www/nginx/html by default using the variable "open_basedir" in /etc/php/php.ini, you can change them if you need.
+
== 参见 ==
  
==参考==
+
* [[WebDAV#Nginx|WebDAV with nginx]]
*[http://nginx.net/ Nginx Official site]
+
* [https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/ nginx configuration pitfalls]
*[http://wiki.codemongers.com/Main Nginx Wiki]
+
* [https://calomel.org/nginx.html Very good in-depth 2014 look at nginx security and Reverse Proxying]
*[http://calomel.org/nginx.html Nginx Howto]
+
* [http://www.tecmint.com/install-nginx-php-mysql-with-mariadb-engine-and-phpmyadmin-in-arch-linux/ Installing LEMP (nginx, PHP, MySQL with MariaDB engine and PhpMyAdmin) in Arch Linux]
 +
* [[Let’s Encrypt|Using SSL certificates generated with Let's Encrypt]]

Latest revision as of 05:22, 31 July 2019

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

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

安装

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

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

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

对于在基于chroot环境下的额外安全性,可查阅#Installation in a chroot[broken link: invalid section].

启动

Start/enable nginx.service.

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

配置

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

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

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

配置例子

/etc/nginx/nginx.conf
user http;

# May be equal to `grep processor /proc/cpuinfo | wc -l`
worker_processes auto;
worker_cpu_affinity auto;

# PCRE JIT can speed up processing of regular expressions significantly.
pcre_jit on;

events {
    # Should be equal to `ulimit -n`
    worker_connections 1024;

    # Let each process accept multiple connections.
    multi_accept on;

    # Preferred connection method for newer linux versions.
    use epoll;
}

http {
    server_tokens off; # Disables the “Server” response header
    charset utf-8;

    # Sendfile copies data between one FD and other from within the kernel.
    # More efficient than read() + write(), since the requires transferring
    # data to and from the user space.
    sendfile on;

    # Tcp_nopush causes nginx to attempt to send its HTTP response head in one
    # packet, instead of using partial frames. This is useful for prepending
    # headers before calling sendfile, or for throughput optimization.
    tcp_nopush on;

    # Don't buffer data-sends (disable Nagle algorithm). Good for sending
    # frequent small bursts of data in real time.
    #
    tcp_nodelay on;

    # On Linux, AIO can be used starting from kernel version 2.6.22.
    # It is necessary to enable directio, or otherwise reading will be blocking.
    # aio threads;
    # aio_write on;
    # directio 8m;

    # Caches information about open FDs, freqently accessed files.
    # open_file_cache max=200000 inactive=20s;
    # open_file_cache_valid 60s;
    # open_file_cache_min_uses 2;
    # open_file_cache_errors on;

    # http://nginx.org/en/docs/hash.html
    types_hash_max_size 4096;
    include mime.types;
    default_type application/octet-stream;

    # Logging Settings
    access_log off;

    # Gzip Settings
    gzip on;
    gzip_comp_level 6;
    gzip_min_length 500;
    gzip_proxied expired no-cache no-store private auth;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rss+xml
        application/vnd.geo+json
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        font/opentype
        image/bmp
        image/svg+xml
        image/x-icon
        text/cache-manifest
        text/css
        text/plain
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-component
        text/x-cross-domain-policy;

    # index index.php index.html index.htm;
    include sites-enabled/*; # See Server blocks
}

通用配置

进程和连接

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

nginx的最大连接数有下面的公式给出max_clients = worker_processes * worker_connections.

不同用户间的使用

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

/etc/nginx/nginx.conf
user user [group];

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

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

server代码块

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

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

/etc/nginx/nginx.conf
...
server {
        listen 80;
        listen [::]:80;
        server_name domainname1.dom;
        root /usr/share/nginx/domainname1.dom/html;
        location / {
           index index.php index.html index.htm;
        }
}

server {
        listen 80;
        listen [::]:80;
        server_name domainname2.dom;
        root /usr/share/nginx/domainname2.dom/html;
        ...
}
...

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

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

管理服务器入口

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

创建以下文件夹:

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

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

/etc/nginx/sites-available/example
server {
    ..
}

include sites-enabled/*;放到http 模块的末尾:

/etc/nginx/nginx.conf
...
http {
    ...
    include sites-enabled/*;
}
...

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

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

如果要移除一个 server:

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

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

TLS

Tango-edit-clear.pngThis article or section needs language, wiki syntax or style improvements. See Help:Style for reference.Tango-edit-clear.png

Reason: 不要仅仅把 OpenSSL#Certificates的配置搬过来. (Discuss in Talk:Nginx (简体中文)#)

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

Tip:
  • 你可能需要在配置SSL之前阅读 ngx_http_ssl_module 的文档.
  • Let’s Encrypt 时一个免费的、自动的、开放的证书颁发机构.有一个插件可以从茉莉花请求有效的证书并自动配置的.
  • Mozilla有一篇有用的 TLS文章,也有一个自动化工具来创建一个更安全的配置.
  • Cipherli.st 为大多数现代web服务器提供了强大的 TLS 实现例子和教程.
Warning: 如果你打算实现TLS, 你必须知道一些变化和实现 仍然被攻击时很脆弱[1]. 如果想知道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
Note: -days 是可选的并且RSA键大小最低可到2048 (默认值).

如果你需要创建一个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
Note: 如果你需要更多的 openssl选项, 可以阅读它们的 手册 或者读它们的扩展文档.

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

/etc/nginx/nginx.conf
http {
    ...
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ..

    # Redirect to HTTPS
    server {
        listen 80;
        server_name localhost;
        return 301 https://$host$request_uri;
    }

    server {
        #listen 80; # Uncomment to also listen for HTTP requests
        listen 443 ssl http2; # HTTP/2 is only possible when using SSL
        server_name localhost;

        ssl_certificate ssl/server.crt;
        ssl_certificate_key ssl/server.key;

        root /usr/share/nginx/html;
        location / {
            index index.html index.htm;
        }
    }
}

Restart nginx.service 来让配置生效.

分用户目录

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

/etc/nginx/nginx.conf
...
server {
    ...
    # PHP in user directories, e.g. http://example.com/~user/test.php
    location ~ ^/~(.+?)(/.+\.php)$ {
        alias          /home/$1/public_html$2;
        fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        include        fastcgi.conf;
    }

    # User directories, e.g. http://example.com/~user/
    location ~ ^/~(.+?)(/.*)?$ {
        alias     /home/$1/public_html$2;
        index     index.html index.htm;
        autoindex on;
    }
    ...
}
...

查阅 #PHP实现 来阅读更多 nginx的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-fpm 然后确保 PHP 被安装配置正确. PHP-FPM的主要配置文件是 /etc/php/php-fpm.conf. 基础使用的话默认配置就足够了.

最后, enablestart php-fpm.service.

Note:
  • 如果你 用不同的用户运行nginx, 确保PHP-FPM套接字文件能被这个用户访问,或者通过一个TCP套接字.
  • 如果你在chroot的环境下运行nginx (chroot 是 /srv/nginx-jail, web页面在 /srv/nginx-jail/www), 你必须修改文件 /etc/php/php-fpm.conf ,把 chroot /srv/nginx-jaillisten = /srv/nginx-jail/run/php-fpm/php-fpm.sock 两行加入到指令部分 (默认是[www]). 如果缺少的话请为套接字文件创建目录。此外, 动态链接到依赖的模块,你需要将这些依赖复制到(比如. 对于php-imagick, 你需要复制ImageMagic的库复制到chroot, 而不是imagick.so他自己).
nginx 配置
添加到主配置

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

/etc/nginx/sites-available/example
server {
    ...

    root /usr/share/nginx/html;
    location / {
        index index.html index.htm;
    }

    location ~ [^/]\.php(/|$) {
        # Correctly handle request like /test.php/foo/blah.php or /test.php/
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;

        try_files $uri $document_root$fastcgi_script_name =404;

        # Mitigate https://httpoxy.org/ vulnerabilities
        fastcgi_param HTTP_PROXY "";

        fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

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

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

.php 的PHP-FPM扩展处理都需要被直接加入到 /etc/php/php-fpm.d/www.conf:

security.limit_extensions = .php .html .htm
Note: 需要注意下 fastcgi_pass 参数, 因为它被选定的FastCGI服务器在它的配置文件里定义TCP或Unix套接字. 对应php-fpmdefault (Unix) 套接字是 是:
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;

你可能想要使用常见的 TCP 套接字, not default,

fastcgi_pass 127.0.0.1:9000;
Unix 的域名套接字应该会快一点.
PHP配置文件

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

/etc/nginx/php.conf
location ~ \.php$ {
    ...
}

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

/etc/nginx/nginx.conf
server {
    server_name example.com;
    ...
    include php.conf;
}
测试配置

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

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

<?php phpinfo(); ?>

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

CGI 实现

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

fcgiwrap

Install fcgiwrap. 配置文件是 /usr/lib/systemd/system/fcgiwrap.socket. Enable 并且 start fcgiwrap.socket.

多个工人线程

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

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

/etc/systemd/system/fcgiwrap.service
[Unit]
Description=Simple CGI Server
After=nss-user-lookup.target

[Service]
ExecStartPre=/bin/rm -f /run/fcgiwrap.socket
ExecStart=/usr/bin/spawn-fcgi -u http -g http -s /run/fcgiwrap.sock -n -- /usr/bin/multiwatch -f 10 -- /usr/sbin/fcgiwrap
ExecStartPost=/usr/bin/chmod 660 /run/fcgiwrap.sock
PrivateTmp=true
Restart=on-failure

[Install]
WantedBy=multi-user.target

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

Warning: ExecStartPost行是需要的,因为当为spawn-fcgi使用-M 660参数选项时会出现奇怪的情况.这有可能是一个Bug
nginx配置

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

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

如果你一直出现 502 - bad Gateway 错误,你应该检查你的CGI程序是否宣告了下面内容的mime类型。如果是html,他应该是Content-type: text/html.

在chroot环境下安装

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

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

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

创建必要的设备

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

Tip: 确保 /srv/http 挂载了并且没有nodev选项
Tip: 查看 mknod(1)ls -l /dev/{null,random,urandom} 来更好的理解mknod选项.
# 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文档目录在 /srv/http/www.

# 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

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

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

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

/etc/fstab
 tmpfs   /srv/http/run   tmpfs   rw,noexec,relatime,size=1024k   0       0
 tmpfs   /srv/http/tmp   tmpfs   rw,noexec,relatime,size=102400k 0       0

填充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)

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

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

用下面的命令来复制 ld-linux-x86-64.so:

# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib
Note: 不要尝试复制 linux-vdso.so: 他不是真实存在的库,不在/usr/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/group
http:x:33:
nobody:x:99:
$JAIL/etc/passwd
http:x:33:33:http:/:/bin/false
nobody:x:99:99:nobody:/:/bin/false
$JAIL/etc/shadow
http:x:14871::::::
nobody:x:14871::::::
$JAIL/etc/gshadow
http:::
nobody:::
# 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单元文件之前, 把它复制进 /etc/systemd/system/ 因为这里的单元文件的优先级比 /usr/lib/systemd/system/高. 这样更新nginx救护不爱修改你自定义的 .service 文件.

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

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

Note: 我不确定pid文件是否需要被放到chroot里.
/etc/systemd/system/nginx.service
 [Unit]
 Description=A high performance web server and a reverse proxy server
 After=syslog.target network.target
 
 [Service]
 Type=forking
 PIDFile=/srv/http/run/nginx.pid
 ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;'
 ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;'
 ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload
 ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit
 
 [Install]
 WantedBy=multi-user.target
Note: 用pacman升级nginx并不会升级chroot里的nginx。如果你要升级的话可能需要手动重复上面的步骤。不要忘记更新它链接的库.

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

# pacman -Rsc nginx

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

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

建议和技巧

使用 systemd无权限运行

编辑 nginx.service 并在 [Service]下设置 User,还可可选的设置 Group :

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

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

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
NoNewPrivileges=yes
Tip: 查看 systemd.exec(5) 获取更多限权选项.

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

Port
Linux默认不允许非root用户的进程绑定低于1024的端口,高于1024的端口可以用:
/etc/nginx/nginx.conf
server {
        listen 8080;
}
Tip: 如果你想nginx可以访问80或者443端口,配置你的firewall(防火墙)来重定向从80或者443的请求刀nginx监听的端口.
或者你可以提升nginx进程的CAP_NET_BIND_SERVICE权限,它能允许绑定低于1024的端口:
/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
CapabilityBoundingSet=
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=
AmbientCapabilities=CAP_NET_BIND_SERVICE
PID文件
nginx 默认使用 /run/nginx.pid . 我们可以创建一个文件夹,user有写权限,然后八我们的PID文件放到那里. 一个使用systemd-tmpfiles的例子:
/etc/tmpfiles.d/nginx.conf
d /run/nginx 0775 root group - -

运行配置:

# systemd-tmpfiles --create

基于最初的nginx.serviceEdit(编辑)PID的值:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
PIDFile=/run/nginx/nginx.pid
ExecStart=
ExecStart=/usr/bin/nginx -g 'pid /run/nginx/nginx.pid; error_log stderr;' 
ExecReload=
ExecReload=/usr/bin/nginx -s reload -g 'pid /run/nginx/nginx.pid;'
/var/lib/nginx/*
一些/var/lib/nginx下面的目录需要被以 root 运行的nginx的引导. 所以有必要启动整个服务器老这么做, nginx会在一次简单的 #配置验证之后自己这么做. 所以只要运行这些中的一个就好啦.
日志文件& 目录权限
运行配置测试的步骤会创建一个悬空的root拥有的日志文件. 移除/var/log/nginx下面的日志文件开始刷新.
启用nginx服务的用户需要/var/log/nginx的写权限. 这可能需要 改变权限 和/或 你系统这个目录的所有权.

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

Tip: 同样的配置可能还要针对你的 FastCGI server 再做一次.

针对systemd的可选脚本

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

/etc/nginx/nginx.conf
user http;
pid /run/nginx.pid;

文件的就绝对路径是/srv/http/etc/nginx/nginx.conf.

/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默认加载 -c /etc/nginx/nginx.conf,二这也是一个好主意.

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

/etc/systemd/system/nginx.path
[Unit]
Description=nginx (Chroot) path
[Path]
PathExists=/srv/http/site/Public_html
[Install]
WantedBy=default.target

Enable(启用)创建的nginx.path 然后把 /etc/systemd/system/nginx.service里的WantedBy=default.target 变成 WantedBy=nginx.path .

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

Nginx Beautifier (nginx美化器)

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

更好的头文件管理

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

Install (安装) nginx-mod-headers-more 包. 这样会把模块安装到/usr/lib/nginx/modules 目录下.

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

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

故障排除

配置验证

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

接入本地IP重定向到localhost

Arch Linux 论坛的解决方案.

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

server_name_in_redirect off;

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

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

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

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

在Archlinux, 上面提到的配置文件在 /etc/php/php-fpm.conf.

Error: No input file specified

1. 确定/etc/php/php.ini里的 open_basedir变量包含正确的路径,要和 nginx.conf (通常在/usr/share/nginx/)里的root变量一致. 当使用PHP-FPM 作为PHP的FastCGI服务器时, 你可以添加 fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";nginx.conf里的 location 代码块里,它能处理PHP文件.

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

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

4. 你没有让 SCRIPT_FILENAME 包含你脚本的全部路径. 如果nginx的配置 (fastcgi_param SCRIPT_FILENAME) 是对的的话, 这种错误意味着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,确保/etc/php-fpm/php-fpm.d/www.conf里的chroot 设置正确(或者 /etc/php-fpm/php-fpm.conf 如果是旧版本的话)

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代码块的下面的键的值 [4] [5]:

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

不能指定请求地址

来自systemctl status nginx.service的全部错误是

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

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

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




参见