「Nginx」の版間の差分

提供: ArchWiki
ナビゲーションに移動 検索に移動
(改行を追加)
(→‎トラブルシューティング: 'Tips and tricks'項目を追加)
471行目: 471行目:
 
If you do not remove the non-chrooted nginx installation, you may want to make sure that the running nginx process is in fact the chrooted one. You can do so by checking where {{ic|/proc/{PID}/root}} symmlinks to. If should link to {{ic|/srv/http}} instead of {{ic|/}}.
 
If you do not remove the non-chrooted nginx installation, you may want to make sure that the running nginx process is in fact the chrooted one. You can do so by checking where {{ic|/proc/{PID}/root}} symmlinks to. If should link to {{ic|/srv/http}} instead of {{ic|/}}.
 
# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done
 
# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done
  +
  +
== Tips and tricks ==
  +
  +
=== Running unprivileged using [[systemd]] ===
  +
  +
[[systemd#Editing_provided_units|Edit nginx.service]] and set the {{ic|User}} and optionally {{ic|Group}} options under {{ic|[Service]}}:
  +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
  +
[Service]
  +
User=''user''
  +
Group=''group''
  +
}}
  +
  +
We can harden the service against ever elevating privileges:
  +
{{hc|/etc/systemd/system/nginx.service.d/user.conf|2=
  +
[Service]
  +
...
  +
NoNewPrivileges=yes
  +
}}
  +
  +
{{Tip|1=See [http://www.freedesktop.org/software/systemd/man/systemd.exec.html#User= systemd.exec(5)] for more options of confinement.}}
  +
  +
Then we need to ensure that {{ic|''user''}} has access to everything it needs:
  +
  +
<dl>
  +
<dt>Port</dt>
  +
<dd>
  +
Linux does not permit non-{{ic|root}} processes to bind to ports below 1024 by default. A port above 1024 can be used:
  +
  +
{{hc|/etc/nginx/nginx.conf|
  +
server {
  +
listen 8080;
  +
}
  +
}}
  +
  +
{{Tip|1=If you want nginx accessible on port 80 or 443, configure your [[firewall]] to redirect requests from 80 or 443 to the ports nginx listens to.}}
  +
</dd>
  +
<dd>
  +
Or you may grant the nginx process the CAP_NET_BIND_SERVICE capability which will allow it to bind to ports below 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 file</dt>
  +
<dd>
  +
{{Pkg|nginx}} uses {{ic|/run/nginx.pid}} by default. We can create a directory that ''user'' has write access to and place our PID file in there. An example using [[systemd#Temporary_files|systemd-tmpfiles]]:
  +
  +
{{hc|/etc/tmpfiles.d/nginx.conf|2=
  +
d /run/nginx 0775 root ''group'' - -
  +
}}
  +
  +
Run the configuration:
  +
  +
# systemd-tmpfiles --create
  +
  +
[[Edit]] the PID values based on the original {{ic|nginx.service}}:
  +
  +
{{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>
  +
Some directories under {{ic|/var/lib/nginx}} need to be bootstrapped by nginx running as {{ic|root}}. It is not necessary to start the whole server to do that, nginx will do it on a simple [[#Configuration_validation|configuration test]]. So just run one of those and you're good to go.
  +
</dd>
  +
  +
<dt>Log file & Directory Permissions</dt>
  +
<dd>
  +
The step of running a configuration test will create a dangling {{ic|root}}-owned log. Remove logs in {{ic|/var/log/nginx}} to start fresh.
  +
<dd>
  +
The nginx service user needs write permission to {{ic|/var/log/nginx}}. This may require [[File_permissions_and_attributes#Changing_permissions|changing permission]] and/or ownership of this directory on your system.
  +
</dl>
  +
  +
Now we should be good to go. Go ahead and [[start]] nginx, and enjoy your completely rootless nginx.
  +
  +
{{Tip|The same setup may be desirable for your [[#FastCGI|FastCGI server]] as well.}}
  +
  +
=== Alternative script for systemd ===
  +
  +
On pure systemd you can get advantages of chroot + systemd. [http://0pointer.de/blog/projects/changing-roots.html] Based on set [http://wiki.nginx.org/CoreModule#user user group] an pid on:
  +
  +
{{hc|/etc/nginx/nginx.conf|2=
  +
user http;
  +
pid /run/nginx.pid;
  +
}}
  +
  +
the absolute path of file is {{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
  +
}}
  +
  +
It is not necesary to set the default location, nginx loads at default {{ic| -c /etc/nginx/nginx.conf}}, but it is a good idea though.
  +
  +
Alternatively you can run '''only''' {{ic|ExecStart}} as chroot with parameter {{ic|RootDirectoryStartOnly}} set as {{ic|yes}} [http://www.freedesktop.org/software/systemd/man/systemd.service.html man systemd service] or start it before mount point as effective or a [http://www.freedesktop.org/software/systemd/man/systemd.path.html systemd path] is available.
  +
  +
{{hc|/etc/systemd/system/nginx.path|2=
  +
[Unit]
  +
Description=nginx (Chroot) path
  +
[Path]
  +
PathExists=/srv/http/site/Public_html
  +
[Install]
  +
WantedBy=default.target
  +
}}
  +
  +
[[Enable]] the created {{ic|nginx.path}} and change the {{ic|1=WantedBy=default.target}} to {{ic|1=WantedBy=nginx.path}} in {{ic|/etc/systemd/system/nginx.service}}.
  +
  +
The {{ic|PIDFile}} in unit file allows systemd to monitor process (absolute path required). If it is undesired, you can change to default one-shot type, and delete the reference from the unit file.
  +
  +
=== Nginx Beautifier ===
  +
  +
{{AUR|nginxbeautifier}} is a commandline tool used to beautify and format nginx configuration files.
  +
  +
=== Better headers management ===
  +
  +
Nginx has a rather unintuitive header management system where headers can only be defined in one context, any other headers are ignored. To remedy this we can install the [https://github.com/openresty/headers-more-nginx-module#more_set_headers headers-more-nginx] module.
  +
  +
[[Install]] the package {{Pkg|nginx-mod-headers-more}} package. This will install the module to {{ic|/usr/lib/nginx/modules}} directory.
  +
  +
To load the module add the following to the top of your main nginx configuration file.
  +
  +
{{hc|/etc/nginx/nginx.conf|2=
  +
load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so";
  +
...
  +
}}
  +
   
 
== トラブルシューティング ==
 
== トラブルシューティング ==

2019年4月12日 (金) 13:33時点における版

nginx ("エンジンエックス"と発音します)は2005年から Igor Sysoev(ロシア)によって開発されている、フリーでオープンソースかつハイパフォーマンスな HTTP サーバーかつリバースプロクシで、IMAP/POP3 プロクシサーバーとしても機能します。

Nginx は主に、その安定性・多機能・単純な設定・低リソース消費によって知られています。

インストール

公式リポジトリから nginx-mainline (最新の機能、アップデート、バグ修正が適用された最新版)か nginx (重要な修正のみを適応した安定版) をインストールできます。

ノート:

基本的には最新版の利用が推奨されていますが、

  • サードパーティ製のモジュールとの互換性
  • 新規に追加された機能の未知のバグ

などによる影響が懸念される場合は、安定版を使用することを検討してください。

Ruby on Rails のためにインストールするには、Ruby on Rails#Rails パーフェクトセットアップを参照してください。

セキュリティを向上させるために chroot によるインストールをするには、#chroot でインストール を参照してください。

サービスの開始

Nginx サービスを走らせるには:

# systemctl start nginx

スタートアップ時に Nginx サービスを起動するには:

# systemctl enable nginx

http://127.0.0.1 にデフォルトで表示されるページは:

/usr/share/nginx/html/index.html

設定

nginx における最初の手順は 公式のビギナーズガイド で説明されています。/etc/nginx/ にあるファイルを編集することで Nginx の設定ができます。メインの設定ファイルは /etc/nginx/nginx.conf です。

より詳しい解説はこちら: 公式の ドキュメント にある Nginx Configuration Examples

一般設定

worker_processes の適当な値を探って下さい。この設定は最終的に nginx が受け入れる接続の数と利用できるプロセスの数を定義します。通常、システムのハードウェアのスレッド数にすると良いでしょう。また、バージョン 1.3.8 と 1.2.5 から worker_processes には auto と設定することができ、最適な値を自動的に検出します (ソース)。

nginx が受け入れる最大接続数は max_clients = worker_processes * worker_connections で計算できます。

サーバーブロック

server ブロックを使うことで複数のドメインを利用することができます。"バーチャルホスト"とも呼ばれますが、これは Apache の用語です。server ブロックの利用方法は Apache とは異なっています。

以下の例では2つのドメインの接続をサーバーが待機します: domainname1.domdomainname2.dom:

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

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

nginx.service再起動して変更を適用します。

クライアントからの接続時にこれらのドメイン名を解決するには、BINDdnsmasq などのDNSサーバーを設定する必要があります。

ローカルマシンでバーチャルホストをテストするには、/etc/hosts ファイルにバーチャルネームを追加してください:

127.0.0.1 domainname1.dom 
127.0.0.1 domainname2.dom

TLS/SSL

SSL を使用するには、openssl をインストールする必要があります。

ヒント:
  • SSL を設定する前に ngx_http_ssl_module のドキュメントを読むことを推奨します。
  • Let’s Encrypt は無料の、自動化されたオープンな証明局です。letsencrypt-nginx[リンク切れ: 置換パッケージ: certbot-nginx] をインストールすることで nginx プラグインを利用してコマンドラインから直接 SSL 証明書を作成して自動で設定することができます。

自己署名証明書を作成してください (キーのサイズや効力の日数は変更できます):

# mkdir /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out cert.key
# chmod 600 cert.key
# openssl req -new -key cert.key -out cert.csr
# openssl x509 -req -days 365 -in cert.csr -signkey cert.key -out cert.crt
ノート: その他の openssl オプションに関しては、 OpenSSL の man pageextensive documentation を参照してください。
警告: If you plan on implementing SSL/TLS, know that some variations and implementations are still vulnerable to attack. For details on these current vulnerabilities within SSL/TLS and how to apply appropriate changes to nginx, visit http://disablessl3.com/ and https://weakdh.org/sysadmin.html

TLS/SSL を用いる場合の nginx.conf 設定サンプル:

/etc/nginx/nginx.conf
http {
        ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8 8.8.4.4 valid=300s; # Google DNS Servers
        resolver_timeout 5s;
}

server {
        #listen 80; # Uncomment to also listen for HTTP requests
        listen 443 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;
        }
}
ヒント:
  • Mozilla は有用な SSL/TLS の記事 を用意しており、nginx の設定ガイドラインや、セキュアな設定を助ける 自動化ツール が載っています。
  • Cipherli.st には大半のモダンなサーバーに対応する強力な SSL の実装例とチュートリアルが載っています。

nginx.service を再起動して変更を適用します。

FastCGI

FastCGI、または FCGI はウェブサーバーでインタラクティブなプログラムを動作させるためのプロトコルです。FastCGI は Common Gateway Interface (CGI) の変種で、ウェブサーバーと CGI プログラムのオーバーヘッドを減らすよう設計されていて、サーバーはより多くのウェブページのリクエストを一度に捌くことができます。

Nginx には FastCGI が組み込まれており多くの外部ツールが動きます、例えば Perl、PHPPython など。これらを使うためにはまず FastCGI サーバーを動かす必要があります。

PHP を動かす

PHP のために FastCGI サーバーを動かす方法は複数あります。ここでは推奨方法として php-fpm を使う方法を記載しています。

手順1: PHP の設定

php をインストールしてください。/etc/php/php.ini の中にある open_basedir に PHP ファイルが含まれているベースディレクトリを (/srv/http//usr/share/webapps/ のような感じで)指定しなくてはなりません:

open_basedir = /usr/share/webapps/:/srv/http/:/home/:/tmp/:/usr/share/pear/

そうしたら必要なモジュールを設定しましょう。例えば sqlite3 を使うなら php-sqlite をインストールして、/etc/php/php.ini の次の行をアンコメントして有効にします:

extension=sqlite3.so
ノート: chroot 環境で nginx を動かしている場合 (chroot は /srv/nginx-jail で、ウェブページは /srv/nginx-jail/www) です。/etc/php/php-fpm.conf ファイルを編集して chroot /srv/nginx-jaillisten = /srv/nginx-jail/run/php-fpm/php-fpm.sock ディレクティブを pool セクション (デフォルトは [www]) の中に記述する必要があります。ソケットファイルのディレクトリが存在しない場合は作成してください。
MariaDB

MariaDB で説明されているようにして MySQL/MariaDB を設定してください。

/etc/php/php.ini の以下の行の 少なくともどれか一つ をアンコメント:

extension=pdo_mysql.so
extension=mysqli.so
警告: PHP 5.5 から、mysql.so廃止 されログファイルにエラーが表示されます。

ウェブスクリプトのために最小権限の MySQL ユーザーを追加することができます。また、/etc/mysql/my.cnf を編集して skip-networking 行をアンコメントすると MySQL サーバーはローカルホストからしかアクセスできなくなります。変更を適用するには MySQL を再起動する必要があります。

ヒント: データベースを利用するために phpMyAdmin, Adminer, mysql-workbench などのツールをインストールしても良いでしょう。
手順2: php-fpm

php-fpm をインストールします:

# pacman -S php-fpm

設定ファイルは /etc/php/php-fpm.conf です。

サービスを動かします:

# systemctl start php-fpm

php-fpm をスタートアップ時に有効にします:

# systemctl enable php-fpm.service
手順3: Nginx の設定

それぞれの server ブロックの中の location ブロックに PHP アプリケーションを次のように記述します:

location ~ \.php$ {
     fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
     fastcgi_index  index.php;
     include        fastcgi.conf;
}

/etc/nginx/php.conf を作って設定をそこに書く場合、このファイルを server ブロックに入れて下さい。

server = {
    ...
    include  php.conf;
    ...
}

.html や .htm ファイルを PHP として処理したい場合は、以下のようにしてください:

location ~ \.(php|html|htm)$ {
     fastcgi_pass  unix:/run/php-fpm/php-fpm.sock;
     fastcgi_index  index.php;
     include        fastcgi.conf;
}

拡張子が .php でないファイルを php-fpm で動かすことを /etc/php/php-fpm.conf で有効にする必要があります:

security.limit_extensions = .php .html .htm

設定を変えた後は php-fpm デーモンを再起動してください。

# systemctl restart php-fpm
ノート: fastcgi_pass 引数に注意を払って下さい、FastCGI サーバーの設定ファイルで定義された TCP や Unix ソケットでなくてはなりません。デフォルトの php-fpm (Unix) ソケットは
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;

デフォルトの代わりに、通常の TCP ソケットを使うこともできます

fastcgi_pass 127.0.0.1:9000;
ただし Unix ドメインソケットの方が高速です。

Nginx のための FastCGI 設定がある fastcgi.conffastcgi_params が含まれていますが、後者は使われなくなりました。これらのファイルは Nginx をインストールしたときに作られます。

最後に、Nginx が動作している場合は再起動してください:

# systemctl restart nginx

FastCGI をテストしたい場合は、/usr/share/nginx/html/index.php を次の内容で作成して

<?php
  phpinfo();
?> 

ブラウザで http://127.0.0.1/index.php を開いて下さい。

以下は実際の設定例です。例の中では root パスは server の下に直接指定されており (デフォルト設定のように) location の中には置いていません。

server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    location / {
        index index.html index.htm index.php;
    }

    location ~ \.php$ {
        #fastcgi_pass 127.0.0.1:9000; (depending on your php-fpm socket configuration)
        fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
    }
}

CGI を動かす

この実装は CGI アプリケーションに必要です。

手順1: fcgiwrap

fcgiwrap をインストールしてください。 設定ファイルは /usr/lib/systemd/system/fcgiwrap.socket です。 systemdfcgiwrap.socket を有効化・起動します。

この systemd ユニットファイルは現在 ArchLinux のタスクページで議論されています。思い通りに動くか確認するためにユニットファイルを自分で検査すると良いでしょう。

マルチワーカースレッド

複数のワーカースレッドを生成したい場合は、multiwatchAUR を使用するのが推奨されています。これはクラッシュした子スレッドの再起動の面倒をみてくれます。multiwatch は systemd が作成したソケットを管理できないため、unix ソケットを作成するには spawn-fcgi を使う必要がありますが、ユニットファイルから直接実行した場合は fcgiwrap には問題が生じません。

/usr/lib/systemd/system/fcgiwrap.service から /etc/systemd/system/fcgiwrap.service にユニットファイルをコピーして (存在する場合は fcgiwrap.socket ユニットも)、ExecStart 行を必要に応じて修正してください。以下は multiwatchAUR を使用するユニットファイルです。fcgiwrap.socket が実行中だったり有効になっていないことを確認してください、このユニットと衝突してしまうからです:

/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 は生成する子スレッドの数に変更してください。

警告: The ExecStartPost line is required because of strange behaviour I'm seeing when I use the -M 660 option for spawn-fcgi. The wrong mode is set. This may be a bug?
手順2: Nginx の設定

CGI ウェブアプリケーションを置くそれぞれの server ブロックには以下のような location ブロックを記述します:

location ~ \.cgi$ {
     root           /path/to/server/cgi-bin;
     fastcgi_pass   unix:/run/fcgiwrap.sock;
     include        fastcgi.conf;
}

fcgiwrapデフォルト (Unix) ソケットは /run/fcgiwrap.sock です。

chroot でインストール

chroot に Nginx をインストールすることでセキュリティレイヤーを追加することができます。セキュリティを最大限に高めるために、Nginx サーバーを動かすのに必要なファイルだけを入れて、全てのファイルは出来る限り最小限の権限にします。例えば、なるべく root が所有するようにして、/usr/bin などのディレクトリは読み書きできないようにするなど。

Arch はデフォルトでサーバーを実行するための http ユーザーとグループが設定されています。chroot は /srv/http で動作させます。

この牢獄を作るための perl スクリプトが jail.pl gist にあります。このスクリプトを使用するか、またはこの記事の指示に従って作成するか選ぶことができます。スクリプトは root で実行してください。変更を適用する前に行をアンコメントする必要があります。

必要なデバイスを作成

Nginx には /dev/null, /dev/random, /dev/urandom が必要です。これらを chroot にインストールするために /dev/ フォルダを作成してデバイスを mknod で追加します。chroot が危険になったときでも、攻撃者が /dev/sda1 などの重要なデバイスにアクセスするには chroot から脱出しなくてはならないようにするために、/dev/ を全てマウントはしません。

ヒント: mknod の引数については man mknodls -l /dev/{null,random,urandom} を見て下さい。
# 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 
ノート: 64ビットのカーネルを使っている場合 usr/liblib64usr/lib64 のシンボリックリンクを作成する必要があります: cd $JAIL; ln -s usr/lib lib64cd $JAIL/usr; ln -s lib lib64

そして $JAIL/tmp$JAIL/run を tmpfs でマウントします。攻撃者が RAM を全て喰いつくせないようにサイズを制限すると良いでしょう。

# 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)
# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib

/usr/lib の中にあるファイルについては次のワンライナーが使えます:

# cp $(ldd /usr/bin/nginx | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
ノート: linux-vdso.so をコピーしないでください – 実際のライブラリではなく /usr/lib には存在しません。64ビット環境の場合 ld-linux-x86-64.so/lib64 にあるように表示されます。

必要なライブラリとシステムファイル以外のファイルをコピーします。

# 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

Create restricted user/group files for the chroot. This way only the users needed for the chroot to function exist as far as the chroot knows, and none of the system users/groups are leaked to attackers should they gain access to the 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

Finally make set very restrictive permissions. As much as possible should 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 sudo chmod -rw
# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod +x
# find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs sudo chmod -x
# find $JAIL/usr/bin -type f -print | xargs sudo chmod ug+rx
# find $JAIL/ -group http -user http -print | xargs sudo chmod o-rwx
# chmod +rw $JAIL/tmp
# chmod +rw $JAIL/run

If your server will bind port 80 (or any port 0-1024), give the chrooted executable permission to bind these ports without root.

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

nginx.service を変更して chroot を起動

Before modifying the nginx.service unit file, it may be a good idea to copy it to /etc/systemd/system/ since the unit files there take priority over those in /usr/lib/systemd/system/. This means upgrading nginx would not modify your custom .service file.

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

The systemd unit must be changed to start up Nginx in the chroot, as the http user, and store the pid file in the chroot

ノート: I'm not sure if the pid file needs to be stored in the chroot jail.
/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
ノート: Upgrading nginx with pacman will not upgrade the chrooted nginx installation. You have to take care of the updates manually by repeating some of the steps above. Do not forget to also update the libraries it links against.

You can now safely get rid of the non-chrooted nginx installation.

# pacman -Rsc nginx

If you do not remove the non-chrooted nginx installation, you may want to make sure that the running nginx process is in fact the chrooted one. You can do so by checking where /proc/{PID}/root symmlinks to. If should link to /srv/http instead of /.

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

Tips and tricks

Running unprivileged using systemd

Edit nginx.service and set the User and optionally Group options under [Service]:

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

We can harden the service against ever elevating privileges:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
NoNewPrivileges=yes
ヒント: See systemd.exec(5) for more options of confinement.

Then we need to ensure that user has access to everything it needs:

Port
Linux does not permit non-root processes to bind to ports below 1024 by default. A port above 1024 can be used:
/etc/nginx/nginx.conf
server {
        listen 8080;
}
ヒント: If you want nginx accessible on port 80 or 443, configure your firewall to redirect requests from 80 or 443 to the ports nginx listens to.
Or you may grant the nginx process the CAP_NET_BIND_SERVICE capability which will allow it to bind to ports below 1024:
/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
CapabilityBoundingSet=
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=
AmbientCapabilities=CAP_NET_BIND_SERVICE
PID file
nginx uses /run/nginx.pid by default. We can create a directory that user has write access to and place our PID file in there. An example using systemd-tmpfiles:
/etc/tmpfiles.d/nginx.conf
d /run/nginx 0775 root group - -

Run the configuration:

# systemd-tmpfiles --create

Edit the PID values based on the original nginx.service:

/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/*
Some directories under /var/lib/nginx need to be bootstrapped by nginx running as root. It is not necessary to start the whole server to do that, nginx will do it on a simple configuration test. So just run one of those and you're good to go.
Log file & Directory Permissions
The step of running a configuration test will create a dangling root-owned log. Remove logs in /var/log/nginx to start fresh.
The nginx service user needs write permission to /var/log/nginx. This may require changing permission and/or ownership of this directory on your system.

Now we should be good to go. Go ahead and start nginx, and enjoy your completely rootless nginx.

ヒント: The same setup may be desirable for your FastCGI server as well.

Alternative script for systemd

On pure systemd you can get advantages of chroot + systemd. [1] Based on set user group an pid on:

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

the absolute path of file is /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

It is not necesary to set the default location, nginx loads at default -c /etc/nginx/nginx.conf, but it is a good idea though.

Alternatively you can run only ExecStart as chroot with parameter RootDirectoryStartOnly set as yes man systemd service or start it before mount point as effective or a systemd path is available.

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

Enable the created nginx.path and change the WantedBy=default.target to WantedBy=nginx.path in /etc/systemd/system/nginx.service.

The PIDFile in unit file allows systemd to monitor process (absolute path required). If it is undesired, you can change to default one-shot type, and delete the reference from the unit file.

Nginx Beautifier

nginxbeautifierAUR is a commandline tool used to beautify and format nginx configuration files.

Better headers management

Nginx has a rather unintuitive header management system where headers can only be defined in one context, any other headers are ignored. To remedy this we can install the headers-more-nginx module.

Install the package nginx-mod-headers-more package. This will install the module to /usr/lib/nginx/modules directory.

To load the module add the following to the top of your main nginx configuration file.

/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: 403 (Permission error)

This is most likely a permission error. Are you sure whatever user configured in the Nginx configuration is able to read the correct files?

If the files are located within a home directory, (e.g. /home/arch/public/webapp) and you are sure the user running Nginx has the right permissions (you can temporarily chmod all the files to 777 in order to determine this), /home/arch might be chmod 750, simply chmod it to 751, and it should work.

If you have changed your document root

If you are sure that permissions are as they should be, make sure that your document root directory is not empty. Try creating index.html in there.

Error: 404 (Pathinfo error)

いくつかのフレームワーク (thinkphp、cakephp など)や CMS では pathinfo が必要です。

1. /etc/php/php.ini ファイルを開き、次のように付け加えます

cgi.fix_pathinfo=1

2. /etc/nginx/nginx.conf を開き、次の部分をコメントアウトします

location ~ \.php$ {
...
}

#location ~ \.php$ {
#...
#}

そして以下を加えます

location ~ ^(.+\.php)(.*)$ {
  root   /srv/http/nginx;
  fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock; 	
  #fastcgi_pass   127.0.0.1:9000; #Un-comment this and comment "fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;" if you are not using php-fpm.
  fastcgi_index  index.php;
  set $document_root2 $document_root;
  if ($document_root2 ~ "^(.*\\\\).*?[\\\\|\/]\.\.\/(.*)$") { set $document_root2 $1$2; }
  if ($document_root2 ~ "^(.*\\\\).*?[\\\\|\/]\.\.\/(.*)$") {	set $document_root2 $1$2; }
  if ($document_root2 ~ "^(.*\\\\).*?[\\\\|\/]\.\.\/(.*)$") {	set $document_root2 $1$2; }
  if ($document_root2 ~ "^(.*\\\\).*?[\\\\|\/]\.\.\/(.*)$") {	set $document_root2 $1$2; }
  if ($document_root2 ~ "^(.*\\\\).*?[\\\\|\/]\.\.\/(.*)$") {	set $document_root2 $1$2; }
  fastcgi_split_path_info ^(.+\.php)(.*)$;
  fastcgi_param	SCRIPT_FILENAME	$document_root2$fastcgi_script_name;
  fastcgi_param	PATH_INFO	$fastcgi_path_info;
  fastcgi_param	PATH_TRANSLATED	$document_root2$fastcgi_path_info;
  include	fastcgi_params;
  fastcgi_param  DOCUMENT_ROOT      $document_root2;
}

try_files を使う方法もあります:

server {
..
   root /usr/share/nginx/html;
   index index.php;
   location / {
     try_files $uri $uri/ /index.php?$args;
   }
}

Error: The page you are looking for is temporarily unavailable. Please try again later.

FastCGI サーバーが動作していないか、ソケットが間違ったパーミッションに設定されています。

Error: No input file specified

おそらくスクリプトのフルパスを含んだ SCRIPT_FILENAME がありません。 nginx の設定 (fastcgi_param SCRIPT_FILENAME) が完全でも、このエラーは php が requestd スクリプトをロードできないことを意味しています。これは単純にパーミッションの問題であることが普通で、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

他の可能性としては、nginx.conf 内の "location ~ \.php$" セクションの "root" 引数が間違っていることがありえます。"root" が同じサーバーの "location /" と同じディレクトリを示しているか確認してください。もしくは root をグローバルに設定するには、location セクションで定義しないでください。

Also keep in mind that your php script path was defined as /srv/http by default using the variable "open_basedir" in /etc/php/php.ini; you can change them if you need.

Also notice that not only php script should have read permission, but also the entire directory structure should have execute permission so that PHP user can traverse the path.

Error: FastCGI で空のページが表示される

access.log にエラーが表示されず、コード200が返されるのに以下のような空のページが表示される場合:

 <html>
  <head></head>
  <body></body>
 </html>

Thomas Beutel のブログ に書かれた解決方法:

/etc/nginx/nginx.conf を編集して php の location セクションに fastcgi_param という行を追加:

    location ~ \.php$ {
         ...
         include fastcgi_params;
         fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
         ...
    }

php_fpm では PHP ファイルのパスを認識するために以上のように設定する必要があります。

参照