動手學 VPS 架站:使用 CentOS 7 + Nginx + PHP-FPM + MariaDB (7) - 網頁伺服器

LNMP

使用 Nginx 做為網頁伺服器,並且透過 PHP-FPM 來處理 PHP 程式。

4.1 啟動 Nginx

# 安裝 Nginx
sudo yum update
sudo yum install epel-release
sudo yum install nginx

# 啟動
systemctl start nginx
# 開機時啟動
systemctl enable nginx
# 查看狀態
systemctl status nginx

假如你有依照 2.1 節的設定,現在打開本機的瀏覽器,連到 http://192.168.8.8 應該可以看到 Nginx 的預設首頁。

Nginx default

 

4.2 Nginx 文件目錄的權限設定

Nginx 預設的文件目錄在 /usr/share/nginx/html。在開始加入自己的網頁之前,要先做幾個動作來確保 Nginx 在讀取網頁時能夠順利。

# 將 nginx 群組加入成為 tony 帳號的次要群組
sudo usermod -aG nginx tony
# 查看 tony 帳號的群組
groups tony
# 結果 tony : tony nginx # <-- 多了一個 nginx 

# 將 /usr/share/nginx 之下的目錄及檔案擁有者改為 nginx:nginx
cd /usr/share/nginx
sudo chown -R nginx:nginx *

# 將 /usr/share/nginx/html 目錄及其下的子目錄加入群組寫入的權限
cd /usr/share/nginx
sudo chmod -R g+w html

# 設定 SELinux 權限
chcon -Rv --type=httpd_sys_content_t /usr/share/nginx
# 檢查 html 及其下檔案及目錄是否有 httpd_sys_content_t 這個權限
ls -Z /usr/share/nginx

我們將自己的帳號加入 nginx 群組,並將群組指定為有寫入的權限,可以避免在新增網頁時出現問題。

CentOS 的 SELinux 有時候會造成 Nginx 沒有讀取的權限,這時當你在瀏覽器開啟網頁時可能會出現 502 Bad Gateway 的錯誤。SELinux 是 Security Enhanced Linux 的縮寫,目的就是對 Linux 做安全強化,對 SELinux 來說,檔案或目錄必須有 httpd_sys_content_t 這個權限,Nginx 才能正常讀取。

SELinux 是針對程式做權限控管而非使用者,所以即使是以 root 身分操作某支程式去執行某個動作,只要該程式沒有對該目錄或行為有權限,就無法執行。例如只要該目錄或檔案沒有 httpd_sys_content_t 的權限,Nginx 程式便無法讀取,這樣可以防止 Nginx 這個程式隨意讀取其他系統上的目錄或檔案,形成保護的機制。

 

4.3 啟動 PHP-FPM

4.3.1 安裝

CentOS 內建的套件庫所提供的 PHP 版本有點舊,只有到第 5 版,我們必須安裝額外的套件庫才能安裝最新版本的 PHP。首先要安裝 EPEL,接著安裝其他有提供最新 PHP 版本的倉庫,例如 WebtaticIUS

# 先安裝 EPEL
sudo yum install epel-release
# 或以 rpm 檔的方式安裝
# CentOS 7
sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# CentOS 6
sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

# 接著選擇一個倉庫來安裝
# 安裝 Webtatic 倉庫
# CentOS 7
sudo yum install https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
# CentOS 6
sudo yum install https://mirror.webtatic.com/yum/el6/latest.rpm

# 或安裝 IUS 倉庫
# CentOS 7
sudo yum install https://centos7.iuscommunity.org/ius-release.rpm
# CentOS 6
sudo yum install https://centos6.iuscommunity.org/ius-release.rpm

安裝完成後,就可以來安裝 php-fpm 了。這裡要說明一下套件名稱,Webtatic 的套件名稱會以 w 結尾,IUS 則是以 u 結尾。

# 列出所有版本的 php-fpm
yum list php*-fpm
# 可能的結果如下

yum list php-fpm

好吧,我們先來移除舊版本的再安裝 IUS 提供的新版本:

# 套件會有關聯性,所以這兩個套件必須同時移除
sudo yum remove php72w-fpm php72w-common 

# 安裝套件
sudo yum install php72u-fpm
# php72u-fpm 套件依賴 php72u-common,所以它也會被要求安裝

# 啟動 php-fpm 伺服器
sudo systemctl start php-fpm
# 開機時啟動
sudo systemctl enable php-fpm

# 都安裝好之後,來查看安裝了哪些 PHP 套件
yum list installed | grep php

其他和 PHP 相關的套件:

mod_php72u:Apache HTTP 伺服器的模組,如果你使用 Apache 的話,必須安裝此套件。

php72u-cli:可以執行 PHP 腳本程式的命令列工具。

php72u-pdo:提供存取 MySQL、PostgreSQL 等資料庫的抽象層介面。

php72u-mysqlnd:提供原生 MySQL 存取驅動程式。

php72u-opcache:快取功能。

php72u-mbstring:支援雙位元的字串。

註:mcrypt 套件在 PHP 7.1.0 版開始被棄用,因為有安全上的問題,建議改用 Sodium (PHP 7.2.0 之後) 或 OpenSSL

你可以一次安裝全部的套件:

sudo yum -y install php72u-fpm php72u-cli php72u-mbstring php72u-mysqlnd php72u-pdo php72u-opcache php72u-sodium

 

4.3.2 關於 cgi.fix_pathinfo 的問題

在較舊版本的 Nginx 和 php-fpm 搭配使用時,會有個 cgi.fix_pathinfo 的漏洞引起安全上的問題,現在可以透過使用新版本加上設定來避免。

Nginx 會有許多以 $ 開頭的環境變數,在它接收到請求時,URI 會被 $fastcgi_script_name 環境變數取出成為 SCRIPT_FILENAMEPATH_TRANSLATED 兩個參數,然後傳遞給 php-fpm 來使用。

PHP 的配置檔中,cgi.fix_pathinfo 選項預設是開啟的 (值為 1) ,它可以用來取出 Nginx 所傳過來的 SCRIPT_FILENAME 中的腳本名稱。

漏洞是這麼發生的,Nginx 將它所解析的 SCRIPT_FILENAME 傳給 php-fpm,而 php-fpm 全然接受 Nginx 所傳過來的結果,但是 Nginx 不知道其實它被騙了,就傳了個有問題的腳本名稱給 php-fpm,而 php-fpm 找到這個腳本執行後,就出問題了。

舉例:當你的網站允許上傳圖檔 (或其他類型的檔案),某人將 bad.php 改名成 bad.jpg 後上傳,於是它可以這樣連入網址 http://myweb.com/bad.jpg/a.php,實際上並沒有 a.php ,是故意亂打的,在 cgi.fix_pathinfo 啟用的情況下,因為找不到 a.php,會往前找 bad.jpg,發現有找到於是執行,如果它是一張正常的圖片就會直接顯示,但是因為它是 bad.php 的偽裝,於是就執行了其中的 php 程式碼。

這是個存在很久的問題,早期的解法是將 cgi.fix_pathinfo 禁用來解決,例如:

# 編輯 php.ini
sudo vim /etc/php.ini
# 搜尋 cgi.fix_pathinfo
/cgi.fix_pathinfo

# 預設是啟用的,將其註解取消並改為 0
# 將  ;cgi.fix_pathinfo=1 
# 改成 cgi.fix_pathinfo=0

現在己經不推薦這個做法,因為它會讓 PHP_SELF 這個全域變數壞掉,有用到此變數的系統就無法正常運作。

Nginx 上有提到正確的做法,請在使用到 PHP 的虛擬主機加上以下設定:

server {
 # ...略

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

        # 如果你的 php-fpm 使用 socket 請使用下面這行
        # fastcgi_pass unix:/var/run/php-fpm.sock;
        # 如果使用 TCP 則使用下面這行,2 選 1
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;

        # 加入 fastcgi_param 檔案的設定
        include fastcgi_params;
        
        # 假如 fastcgi_params 中沒有以下內容,請額外加入
        fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    }
}

/etc/nginx 目錄下可以找到 fastcgi_params 這個檔案。

 

4.3.3 調整 php-fpm 設定

php-fpm 的設定檔在 /etc/php-fpm.d/www.conf 它使用 ; 當註解,我們要針對 Nginx 做一些調整:

sudo vim /etc/php-fpm.d/www.conf

# 程序的使用者及群組
# 23, 24 行,改成
user = nginx
group = nginx

# 如何接受 FastCGI 請求
# 36, 40 行,2 選 1
listen = 127.0.0.1:9000 # TCP
listen = /var/run/php-fpm/php-fpm.sock # Socket
# 這裡的選擇會影響到 Nginx 中虛擬主機的 fastcgi 設定
# 對照設定如下
# fastcgi_pass 127.0.0.1:9000; # TCP
# fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # Socket

# 如果你前面選擇使用 Socket 接受請求的方式,才須做以下設定
# 如果沒有,預設會使用當前使用者的權限
# 設定 socket 的權限
# 50 ~ 52 行
listen.owner = nginx # 設定為 nobody 也可以
listen.group = nginx # 設定為 nobody 也可以
listen.mode = 0660

# 存檔離開

# 重新啟動 PHP-FPM
sudo systemctl restart php-fpm

# 重新啟動 Nginx
sudo systemctl restart nginx

註:在 vim 中顯示行號的方法:esc -> :set nu ,行號可能會略有不同,但是方便你大概知道在文件的哪個位置。

使用 TCP 或 Socket 接受 FastCGI 請求有什麼差別?使用 Socket 必須讓 Nginx 和 php-fpm 處於同一台機器,兩者視為一體,機器上的 CPU 和 RAM 會共享,當要橫向擴充時,也是一起被複製到新的機器上;使用 TCP 則是兩者解耦,一台機器上的 Nginx 可以將資料傳遞給多台機器上的 php-fpm,形成 php-fpm 叢集。


繼續閱讀:動手學 VPS 架站:使用 CentOS 7 + Nginx + PHP-FPM + MariaDB (8) - 使用 Nginx

本文網址:http://blog.tonycube.com/2018/08/vps-centos-7-nginx-php-fpm-mariadb-7-webserver.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。