NGINX を Drupal 用に設定する

とりあえず Drupal のインストールは成功したが、処理時間の設定を除き、特に Drupal 用の設定を何も行っていないので、現状では少々問題が残っている。この記事ではより細かい設定を行って行こうと思う。

NGINX 公式の Drupal 用設定例

👉 NGINX 公式の Drupal 用設定例


server {
    server_name example.com;
    root /var/www/drupal8; ## <-- Your only path reference.

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

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

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log)$ {
        allow 192.168.0.0/16;
        deny all;
    }

    location ~ \..*/.*\.php$ {
        return 403;
    }

    location ~ ^/sites/.*/private/ {
        return 403;
    }

    # Block access to scripts in site files directory
    location ~ ^/sites/[^/]+/files/.*\.php$ {
        deny all;
    }

    # Allow "Well-Known URIs" as per RFC 5785
    location ~* ^/.well-known/ {
        allow all;
    }

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
        return 403;
    }

    location / {
        # try_files $uri @rewrite; # For Drupal <= 6
        try_files $uri /index.php?$query_string; # For Drupal >= 7
    }

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;
    }

    # Don't allow direct access to PHP files in the vendor directory.
    location ~ /vendor/.*\.php$ {
        deny all;
        return 404;
    }

    # Protect files and directories from prying eyes.
    location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|composer\.(lock|json)$|web\.config$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
        deny all;
        return 404;
    }

    # In Drupal 8, we must also match new paths where the '.php' appears in
    # the middle, such as update.php/selection. The rule we use is strict,
    # and only allows this pattern with the update.php front controller.
    # This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If
    # you do not have any paths like that, then you might prefer to use a
    # laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL
    # pattern with front controllers other than update.php in a future
    # release.
    location ~ '\.php$|^/update.php' {
        fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
        # Ensure the php file exists. Mitigates CVE-2019-11043
        try_files $fastcgi_script_name =404;
        # Security note: If you're running a version of PHP older than the
        # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.
        # See http://serverfault.com/q/627903/94922 for details.
        include fastcgi_params;
        # Block httpoxy attacks. See https://httpoxy.org/.
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_intercept_errors on;
        # PHP 5 socket location.
        #fastcgi_pass unix:/var/run/php5-fpm.sock;
        # PHP 7 socket location.
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        try_files $uri @rewrite;
        expires max;
        log_not_found off;
    }

    # Fighting with Styles? This little gem is amazing.
    # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
    location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7
        try_files $uri @rewrite;
    }

    # Handle private files through Drupal. Private file's path can come
    # with a language prefix.
    location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7
        try_files $uri /index.php?$query_string;
    }

    # Enforce clean URLs
    # Removes index.php from urls like www.example.com/index.php/my-page --> www.example.com/my-page
    # Could be done with 301 for permanent or other redirect codes.
    if ($request_uri ~* "^(.*/)index\.php/(.*)") {
        return 307 $1$2;
    }
}

ブロック毎に解析していこうと思う。

  1. /favicon.ico をログ対象から外すため
  2. /robots.txt をログ対象から外すため
  3. .log ファイルと .txt ファイルにアクセスできるのを LAN(プライベートアドレス 192.160.0.0/16)からに限定する
  4. 隠しディレクトリ(\..*/.*)下の .php ファイルへのアクセスを 403 禁止
  5. 隠しディレクトリ(^/sites/.*)下の private ディレクトリへのアクセスを 403 禁止
  6. site files ディレクトリ下の .php ファイルへのアクセスを完全ブロック
  7. acme.sh で使ったりするいわゆる "Well-Known URIs" 用のディレクトリへのアクセスは完全許可
  8. 隠しファイルまたは隠しディレクトリへのアクセスを 403 禁止
  9. 【重要】パスに完全一致するファイルがなければ /index.php?$query_string に置換する
  10. @rewrite で処理するように指定されたアドレスは /index.php?q=$1 に置換する
  11. /vender ディレクトリ下の .php ファイルへのアクセスを 404 エラーで全面禁止
  12. これらの一定の種類のファイルを覗き見から 404 エラーで全面的に防止する
  13. 【重要】Drupal で使われる .php ファイルに関する扱い方を規定している
  14. これらの JavaScript や CSS、画像ファイルは @rewrite でアドレスを置換する
  15. site files/styles ディレクトリ下のファイルが無い場合は @rewrite でアドレスを置換する
  16. [a-z\-] をプレフィックスに持つ system/files ディレクトリ下のファイルが無い場合は /index.php?$query_string に置換する
  17. クリーン URLs を 307 エラーを使って強制する。

ほとんどの項目がセキュリティ的なもので、ある意味追加的なものだが、【重要】な 2 つの項目をはじめとするその他いくつかのアドレスを置換する設定は必須度が高い。これらを設定しておかないと、管理画面の各機能にアクセスしてもクリーン URLs のためにページが存在しない扱いとなってしまう現象が発生する。

そういった点などに留意して自分の OpenWrt ルーターでの NGINX の実際の運用に合わせてアレンジした設定を決めることにする。

OpenWrt ルーターでの NGINX の実運用設定への反映

従来の /etc/nginx/nginx.conf の該当する server ブロック

    server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        server_name  localhost;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:DHE+AESGCM:DHE:!RSA!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!CAMELLIA:!SEED";
        ssl_session_tickets off;

        ssl_certificate /etc/nginx/nginx.cer;
        ssl_certificate_key /etc/nginx/nginx.key;

        root /mnt/data/www/default;
        index index.php index.html;

        # next 4-lines are for 504 Gateway Timeout error
        proxy_connect_timeout 600;
        proxy_send_timeout 600;
        proxy_read_timeout 600;
        send_timeout 600;

        location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
            expires 365d;
        }
        include /etc/nginx/conf.d/*.locations;

        include luci_uwsgi.conf;
    }

include /etc/nginx/conf.d/scaredeer.conf;
従来の /etc/nginx/conf.d/scaredeer.conf

server {
    listen 443 ssl;
    server_name scaredeer.com www.scaredeer.com;

    ssl_certificate     /mnt/data/www/scaredeer.com/cert/cert.pem;
    ssl_certificate_key /mnt/data/www/scaredeer.com/cert/cert.key;

    root /mnt/data/www/default;
}
従来の /etc/nginx/conf.d/php.locations

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

    fastcgi_pass unix:/var/run/php7-fpm.sock;
    fastcgi_index index.php;

    # include the fastcgi_param setting
    include fastcgi_params;

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

    # SCRIPT_FILENAME parameter is used for PHP FPM determining
    #  the script name. If it is not set in fastcgi_params file,
    # i.e. /etc/nginx/fastcgi_params or in the parent contexts,
    # please comment off following line:
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_read_timeout 600;
}
/etc/nginx/nginx.conf の該当する server ブロックの修正

-       # next 4-lines are for 504 Gateway Timeout error
-       proxy_connect_timeout 600;
-       proxy_send_timeout 600;
-       proxy_read_timeout 600;
-       send_timeout 600;

        location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
            expires 365d;
        }
-       include /etc/nginx/conf.d/*.locations;

        include luci_uwsgi.conf;
+
+       include /etc/nginx/conf.d/php.locations;

Drupal 関連の記述はサイト別の conf.d/scaredeer.conf 経由で参照する conf.d/drupal.locations に移すことにして、こちらからは削除する

/etc/nginx/conf.d/scaredeer.conf の修正

server {
    listen 443 ssl;
    server_name scaredeer.com www.scaredeer.com;

    ssl_certificate     /mnt/data/www/scaredeer.com/cert/cert.pem;
    ssl_certificate_key /mnt/data/www/scaredeer.com/cert/cert.key;

    root /mnt/data/www/default;
+   index index.php index.html;
+
+   include /etc/nginx/conf.d/drupal.locations;
}

Drupal 用の設定である conf.d/drupal.locations を参照するように include を追加している。

/etc/nginx/conf.d/drupal.locations の新規作成

# next 4 lines are for 504 Gateway Timeout error
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;

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

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

# Very rarely should these ever be accessed outside of your lan
location ~* \.(txt|log)$ {
    allow 192.168.0.0/16;
    deny all;
}

location ~ \..*/.*\.php$ {
    return 403;
}

location ~ ^/sites/.*/private/ {
    return 403;
}

# Block access to scripts in site files directory
location ~ ^/sites/[^/]+/files/.*\.php$ {
    deny all;
}

# Allow "Well-Known URIs" as per RFC 5785
location ~* ^/.well-known/ {
    allow all;
}

# Block access to "hidden" files and directories whose names begin with a
# period. This includes directories used by version control systems such
# as Subversion or Git to store control files.
location ~ (^|/)\. {
    return 403;
}

# This block is important for Drupal
location / {
    try_files $uri /index.php?$query_string;
}

# This block is important for Drupal
location @rewrite {
    rewrite ^/(.*)$ /index.php?q=$1;
}

# Don't allow direct access to PHP files in the vendor directory.
location ~ /vendor/.*\.php$ {
    deny all;
    return 404;
}

# Protect files and directories from prying eyes.
location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|composer\.(lock|json)$|web\.config$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
    deny all;
    return 404;
}

# This line is important for Drupal
include /etc/nginx/conf.d/php.locations;

# This block is important for Drupal
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    try_files $uri @rewrite;
    expires max;
    log_not_found off;
}

# Fighting with Styles? This little gem is amazing.
location ~ ^/sites/.*/files/styles/ {
    try_files $uri @rewrite;
}

# Handle private files through Drupal. Private file's path can come
# with a language prefix.
location ~ ^(/[a-z\-]+)?/system/files/ {
    try_files $uri /index.php?$query_string;
}

# Enforce clean URLs
# Removes index.php from urls like www.example.com/index.php/my-page --> www.example.com/my-page
# Could be done with 301 for permanent or other redirect codes.
if ($request_uri ~* "^(.*/)index\.php/(.*)") {
    return 307 $1$2;
}

基本的に # This block/line is important for Drupal とコメントした部分のみ重要で、まずはそれだけ揃えれば動作確認には十分である。他はセキュリティ上のより細かいこだわりのためのセッティングだと思う。

PHP 関連の設定だけはさらに独立させて conf.d/php.location を参照するようにしている(drupal.locations に統合しても構わない)。

/etc/nginx/conf.d/php.locations の修正

- location ~ [^/]\.php(/|$) {
+ location ~ '\.php$|^/update.php' {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
-   if (!-f $document_root$fastcgi_script_name) {
-       return 404;
-   }
+
+   # Ensure the php file exists. Mitigates CVE-2019-11043
+   try_files $fastcgi_script_name =404;

-   fastcgi_pass unix:/var/run/php7-fpm.sock;
    fastcgi_index index.php;

    # include the fastcgi_param setting
    include fastcgi_params;
-
    # Mitigate https://httpoxy.org/ vulnerabilities
    fastcgi_param HTTP_PROXY "";
-
+   fastcgi_param PATH_INFO $fastcgi_path_info;
    # SCRIPT_FILENAME parameter is used for PHP FPM determining
    #  the script name. If it is not set in fastcgi_params file,
    # i.e. /etc/nginx/fastcgi_params or in the parent contexts,
    # please comment off following line:
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_read_timeout 600;
+   fastcgi_intercept_errors on;
+   fastcgi_pass unix:/var/run/php7-fpm.sock;
}
  • 削除した if ブロックは Mitigates CVE-2019-11043 の try_files と機能はほぼ同じだが、確か NGINX では最近は if を location ブロックで使うのは邪道とされているようなので、こちらの try_files の表記に変えておいた。
  • 基本的には、Drupal には直接関係のない修正がほとんどで、実質的に Drupal と直接関係があるのは、location ~ '\.php$|^/update.php' に変えた部分のみである。
  • fastcgi_param QUERY_STRING $query_string については、こちらの環境では include している fastcgi_params 内に元から含まれていたので、省略した。
  • unix:/var/run/php7-fpm.sock の行を一番末尾に移動したのは単なる趣味の問題。

コメント

このブログの人気の投稿

OpenWrt での Wi-Fi 設定

シークエンスパパとも 本物の霊能力

シークエンスパパともの先見の明