Software engineering notes

Nginx

Install

sudo apt-get update
sudo apt-get install nginx

啟動

sudo service nginx start

到 browser 看是否安裝成功 ex: http://192.168.56.1

nginx www default directory : /usr/share/nginx/html/

如果啟動 nginx 不成功/沒反應

先判斷設定檔語法是否都正確

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

查看 /var/log/nginx/error.log 是否有噴錯

2017/08/07 08:51:59 [emerg] 2032#0: bind() to 0.0.0.0:80 failed (98: Address already in use)
2017/08/07 08:51:59 [emerg] 2032#0: bind() to [::]:80 failed (98: Address already in use)

重新安裝 nginx (Ubuntu)

sudo apt-get purge nginx nginx-common nginx-full
sudo apt-get install nginx

nginx.conf

/etc/nginx/nginx.conf :

user  www-data;
worker_processes  1;                                # CPU 數量, 可以避免 connection lock

events {
    worker_connections  1024;                       # concurrent connection
    use epoll;
}

403 Forbidden

建議設定 error_log 才能知道問題出在哪裡, 有可能是網站根目錄的權限不對

Rails config

/etc/nginx/sites-available/default :

http {
    passenger_root /home/test/.rvm/gems/ruby-2.1.3/gems/passenger-4.0.50;
    passenger_ruby /home/test/.rvm/gems/ruby-2.1.3/wrappers/ruby;

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

    sendfile        on;

    keepalive_timeout  65;

    error_log /var/www/pumpkin/log/nginx_error.log;
    access_log /var/www/pumpkin/log/nginx_access.log;
    server {
        listen       80;
        server_name  a.example.com;
        root /var/www/pumpkin/public;
        passenger_enabled on;
        passenger_set_cgi_param SECRET_KEY_BASE "cf2d4472039660a31a002b21cd3ded0cf7cc2c5a0d82f24dcdf5097b79c1900241f97eb85542f8e4a349f32fac37b618bc663b21f16de2706bb897885d6cc3f0";
        client_max_body_size       50M;     # 最大上傳 50MB

        error_page   500 502 503 504  /50x.html;

        location = /50x.html {
            root   html;
        }
    }

}

php (php5-fpm) config

第一次使用 nginx 執行 php 請先參考本文下方 php5-fpm

/etc/nginx/sites-available/default :

http {
    server {
        listen       80;
        root  /var/www/php;
        index index.php index.html index.htm;
        server_name  php.example.com;

        # 上傳大小
        client_max_body_size 50M;
        client_body_buffer_size 128k;

        # log
        access_log /var/log/nginx/test-access.log;
        error_log /var/log/nginx/test-error.log;

        # error page
        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;

        location / {
            try_files $uri $uri/ /index.php;
        }

        location = /50x.html {
            root /usr/share/nginx/www;
        }

        # pass the PHP scripts to FastCGI server listening on the php-fpm socket
        location ~ \.php$ {
            # fastcgi_pass   127.0.0.1:9000;
            fastcgi_pass unix:/var/run/php5-fpm.sock;                           # 一定要指定, 要跟 php-fpm config 設定的一樣
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;

            # Server variable
            fastcgi_param DEVELOPER 'test';
        }
    }

}

修改完重啟

sudo service php5-fpm restart
sudo service nginx restart

Multi sites 新增在 sites-enabled 下

有些方法安裝 nginx 完沒有 sites-enabled 資料夾, 必須自行新增資料夾再引入

/opt/nginx/conf/nginx.conf :

http {

    ...略...

    server {

        ...略...

    }
    include /opt/nginx/conf/sites-enabled/*;
}

/opt/nginx/conf/sites-enabled/php-nginx.conf :

server {
    ...略...
}

nginx 預設 user 是 nobody

不同 site 的網站根目錄的權限建議設定為 myUser:www-data,

讓 myUser 開發及 www-data 存取目錄或檔案時不會發生權限問題

如果本身己存在 sites-available 及 sites-enabled

建立新的 sites 就不用那麼麻煩了, 使用 symblic link

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/www
sudo ln -s /etc/nginx/sites-available/www /etc/nginx/sites-enabled/www

並重新啟動 nginx

nginx 預設看似沒有像 apache 的 a2ensite 及 a2dissite 指令, 所以手動操作

Remove nginx

sudo apt-get remove nginx       # Removes all but config files.
sudo apt-get purge nginx        # Removes everything.
sudo apt-get autoremove         # After using any of the above commands, use this in order to remove dependencies used by nginx which are no longer required.
rm -rf /etc/nginx               # remove the conf files too.

其他設定

IPv6 + IPv4

listen [::]:80;         # 前面的是 IPv6

設定 SSL

到 Godaddy 買好 domain, SSL, domain 不要買 private 的

根據網路上找到很多資料都說 domain 不能 private, 但我仍然在 domain 是 private 的情況下完成 ssl 的設定 at 20150822, 所以以下操作可以等到輸入 CSR 後的 SSL 認證信收不到後再考慮是不是要拿掉 domain private

先確定 whois 可以查到 domain 的 email, 如果沒有查到代表 domain 被設定為 private 了, 可操作以下步驟解除

Godaddy 設定頁 點 DOMAINS -> 欄位 Associated Products 的 Domains By Proxy

(你也會看到 Registration Type 為 Private, 如果沒有保護的話會顯示 Public)

取消流程可參考此網頁, 流程大約是

在 Account 的 Domain 下點 Domains By Proxy 然候選擇 忘記密碼, 因為那個網站你還沒有帳密, 接著輸入網址後它就會寄信給你了, 收信取得 customer number, 登入帳號為 customer number 密碼為 godaddy 的密碼, 進入 dashboard 點 Cancel private registration

設定 dns

example.com 及 www.example.com 指到主機 IP

A (Host)
Host    Points To       TTL
---------------------------
@       139.162.11.41   1H
www     139.162.11.41   1H

Create the SSL Certificate

sudo mkdir /opt/nginx/ssl
cd /opt/nginx/ssl
openssl req -new -newkey rsa:2048 -nodes -keyout example.key -out example.csr             # 如果申請 example.com, 輸入 example.key, example.csr

接下來會問你幾個問題, 至少填第一個, 否則 Certificate 可能建立不起來. If you enter '.', the field will be left blank.

Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:Taipei
Organization Name (eg, company) [Internet Widgits Pty Ltd]:example
Organizational Unit Name (eg, section) []: (不用輸入)
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:test@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: (不用輸入)
An optional company name []: (不用輸入)

Godaddy 設定頁 點 SSL CERTIFICATES -> manage -> CSR 貼上 .csr 的內容

-----BEGIN CERTIFICATE-----
MIID8zCCAtugAwIBAgIJAOT8G1V0fwvRMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
VQQGEwJUVzEPMA0GA1UECAwGVGFpd2FuMQ8wDQYDVQQHDAZUYWlwZWkxGzAZBgNV
BAoMEm1lZXR0aGV0cmFuc2xhdG9yczEfMB0GA1UEAwwWbWVldHRoZXRyYW5zbGF0
b3JzLmNvbTEgMB4GCSqGSIb3DQEJARYRanh4eGxpbkBnbWFpbC5jb20wHhcNMTUw
ODIxMTY1OTI2WhcNMTYwODIwMTY1OTI2WjCBjzELMAkGA1UEBhMCVFcxDzANBgNV
BAgMBlRhaXdhbjEPMA0GA1UEBwwGVGFpcGVpMRswGQYDVQQKDBJtZWV0dGhldHJh
bnNsYXRvcnMxHzAdBgNVBAMMFm1lZXR0aGV0cmFuc2xhdG9ycy5jb20xIDAeBgkq
hkiG9w0BCQEWEWp4eHhsaW5AZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAvu8s2aL1Tq4UfaCcMoO0jZKzmOeuYDSlCn4CrJjZmctFqFeS
+TVY7YObvbTI7XFLzaru55mOMpVI4QurYFCnHKJsbPsOWIN2PBkPYxv5KuHmZRPj
d5YGBzqgFaEgIoPftfYmNII9NYxgRCW/hGkx4j9BquZB/CaGMe0Im26Hj/BS3yyZ
oA+LaxiCz6FqK2E0gS0vVce/7ECvuR7DxQr5GNRQ8gmNrgvly0nvgPjyEzKndfMh
gv99MRQ5r6JjaozLD6KlNQ8npoNoeiIAaA3Q/uGuYpgKVxqpZIxmC5kMMeGfQHYh
XZ2tsjKALk+EOOErV/Zwa5y6BrTAUhIznQLZ8QIDAQABo1AwTjAdBgNVHQ4EFgQU
tETSHyBIKhOzMcJMfLBEcIjJOfwwHwYDVR0jBBgwFoAUtETSHyBIKhOzMcJMfLBE
cIjJOfwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAtNokFSCUtwsK
o5nmhB8pS20sILKWjRWg32HUrPU+uaBwDfAASq5ZRGjUoLQMDrxDuo0HWtR+vUCw
OLqCrp7iWSUs7UW4jK2OzNKDyEqUfEPnGd1ZcW309b0oix45aUPXwlp4UzvvBTU+
hZ2UdGsb+Tu/0uPGCjCqJudbZvtESqGSYd5e5bJWnPpz95pibvtCiSsD8ta+S3++
XscKN4Qpb8bjJ877y7NPm9aud9qJ0RQ41tUa4pmWE3uDVcRSpJzcMRK1oyMuh9YN
ftUJMSIawf49PCiSqAwGmQfs9Ou/ZtIwqjTnnWJ2lbHCxd2Zd8TaGeyz/TIB6vIH
OwFSjQxxhQ==
-----END CERTIFICATE-----

去 Email 收信 (當初申請這個 domain 的 email)

Download Certificate 下載會選擇 HTTP Server 的型態, 沒有 nignx 選 other,

下載完是一個 zip 檔, 解壓縮出來是 b38412d0dd76c4f3.crt (伺服器 SSL 憑證) 及 gd_bundle-g2-g1.crt (中繼憑證 (Intermediate Certificate))

nginx 只需要兩個參數,將這兩個檔案搬到 /opt/nginx/ssl 下, 並且合併成同一個檔案 :

cat b38412d0dd76c4f3.crt gd_bundle-g2-g1.crt > example_combined.crt

設定 nginx.conf, 填上 SSL 設定

強制將 non-www 及 http 導到 https://www

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server {

    listen 443 ssl;
    ssl_certificate /opt/nginx/ssl/example_combined.crt;
    ssl_certificate_key /opt/nginx/ssl/example.key;

    listen       80;
    server_name  example.com;

    # non-www redirect to www
    if ($host = $server_name) {
        return 301 https://www.$server_name$request_uri;
    }

    # 將 http 導到 https
    if ($scheme = http) {
        return 301 https://www.$server_name$request_uri;
    }

    ...(略)...

}

完成!

https://www.example.com

[問題] 如果 Https 不是綠色的而是灰色的

可能出現此訊息

這個網頁含有其他不安全的資源

看一下 developer tool 的 console, 顯示什麼提示訊息,

例如網頁上如果用 <img> 來源是別人的圖片, 如果不是 https 就有可能出現此訊息

判斷您的 SSL 是否安全

disable SSL 3 to mitigate 問題

POODLE 是 protocol 層級的弱點, 很難修復, 它可以透過兩個方面去防止這個攻擊

第一個就是 user 將瀏覽器的 SSLv3 關掉, 第二個就是網站工程師要做的, 在 Server 端將 SSLv3 關掉

在 nginx.conf 裡指定 ssl protocols

http {

    ...(略)...

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ...(略)...

    server {
        ...(略)...
    }
}

可以在 http 層指定 或每個 server 層指定

也因為修正這個漏洞原本被 QUALYS SSL LABS 評為 C 而升到 B 了

TODO 後記: 停用 SSLv2 + SSLv3 較安全

讓 nginx 執行 php (php5-fpm)

sudo apt-get install php5 php5-cli php5-fpm php5-mcrypt php5-mysql

啟動 php5-fpm

sudo service php5-fpm

將 fpm 與 nginx 連接起來

原理

當 request 進來, nginx 接到後會往 php-fpm 裡送, 透過它的 fastcgi 去執行 php

修改 nginx 設定檔

為了讓 nginx 可以執行 php 的 code, 到 /etc/nginx/sites-available/www

找到這行 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

除了這行 fastcgi_pass 127.0.0.1:9000; 將其他被註解的打開

因為 fpm 預設會使用 unix socket (php-fpm 設定裡預設用 listen = /run/php/php7.0-fpm.sock), 所以在本機跑會比較快, 如果要交由一台效能較好的

server 去處理 php 可以在 /etc/php5/fpm/pool.d/www.conf 設定改用 tcp socket (listen = 127.0.0.1:9000) 而 fpm 就會用 tcp socket 來運作了

如果要改 php 的執行 user 及 group 也在 /etc/php5/fpm/pool.d/www.conf 裡面改

修改 root path : 網站根目錄路徑

index.php 也設為預設的首頁

index index.html index.htm index.php;

在網站根目錄新增 index.php 看結果是否成功

fpm 設定檔 : /etc/php5/fpm/pool.d/www.conf, 要改 http server 執行的 user, group 在這改

If php5-fpm not working

  1. 檢查 /etc/php5/fpm/pool.d/www.conf

    listen = /var/run/php5-fpm.sock user = www-data group = www-data

  2. 確認 /var/run/php5-fpm.sock 權限

    srw-rw—- 1 www-data www-data 0 May 6 17:29 /var/run/php5-fpm.sock

  3. 確認 nginx 的執行權限

    user www-data;

  4. 檔案的執行權限

    -rw-rw-r– 1 www-data www-data 81 May 5 08:04 index.php

Config 效能調校

ref : http://blog.didibird.com/2016/05/24/the-way-of-turning-the-best-performance-for-nginx/

安裝 ngx_pagespeed (重新安裝整個 nginx)

無法直接加入 ngx_pagespeed module, 必須把原本的 nginx 刪掉再重編

照著安裝 : https://developers.google.com/speed/pagespeed/module/build_ngx_pagespeed_from_source?hl=zh-TW

Verify :

/usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.6.2
built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
configure arguments: --add-module=/home/test/ngx_pagespeed-release-1.9.32.1-beta

Command

git clone https://github.com/Fleshgrinder/nginx-sysvinit-script.git
sudo cp nginx-sysvinit-script/nginx /etc/init.d/nginx
sudo chmod 0755 /etc/init.d/nginx
sudo chown root:root /etc/init.d/nginx
sudo service nginx start

Troubleshootings

reponse 被截斷

回 json 但 response 只收到一半, 直接被截斷

如果以 FastCGI backend (such as PHP-FPM) 為底層, 會 buffer response 在記憶體, 預設 nginx buffer 是 32kb, 如果超過這個大小的話就會被截斷

設定不限制大小

fastcgi_buffering off;

[補充] 預設為

fastcgi_buffers 8 4k|8k;        # 8 等分, 後面是取決於平台, Ubuntu 就是 4k

ref : https://gist.github.com/magnetikonline/11312172