本文記錄在CentOS7.3中構建LEMP開發環境,並配置SSL證書的過程。其中Web服務器爲Nginx,DBMS爲Percona,開發語言爲PHP,SSL證書由Let’s Encrypt提供。

因是在VPS上操作,故直接使用root用戶進行操作,如果是普通用戶(如lemp),可在文件/etc/sudoers中添加

1
2
3
4
5
6
7
8
# User privilege specification
root ALL=(ALL:ALL) ALL
#需要添加的行
# CentOS中设置
lemp ALL=(ALL) NOPASSWD:ALL
# Debian中设置
#lemp ALL=NOPASSWD:ALL

實現免密碼執行sudo權限。

爲方便使用 普通用戶帳號 登錄的用戶,本文中執行的命令皆添加sudo

: 本文不涉及防火牆規則的設置,請知悉。

Operation System Preparation

操作環境準備

Digital Ocean中創建Droplets,操作系統選擇CentOS 7.2 x64(暫未提供7.3版本),通過SSH遠程登陸進行操作(通過key登陸),登錄命令

1
ssh -C -c blowfish [email protected]

系統信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#內核版本
[email protected]:~# uname -r
3.10.0-514.2.2.el7.x86_64
#發行版信息
[email protected]:~# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

需要進行的操作有

  • 更換resource源
  • 更新系統、重啟後移除舊內核
  • 安裝Vim文本編輯器
  • 更改時區爲Asia/Shanghai(根據所在地區具體設置),設置NTP

本文中Nginx、Percona的官方repo將安裝在目錄/etc/yum.repos.d中。

Digital Ocean默認使用的是其自家提供的鏡像,對於中國大陸地區的用戶,可選擇使用163提供的CentOS鏡像,頁面中有具體的操作說明。

此處仍使用Digital Ocean默認的source,不更改source源。

大致命令如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 更換repo源
[[ ! -d /etc/yum.repos.d/bak ]] && sudo mkdir -p /etc/yum.repos.d/bak
sudo mv /etc/yum.repos.d/CentOS*.repo /etc/yum.repos.d/bak/
curl -s http://mirrors.163.com/.help/CentOS7-Base-163.repo -o /etc/yum.repos.d/CentOS7-Base.repo
# 更新系統
yum clean all
sudo yum update -y
# 安裝vim編輯器
sudo yum install vim -y
# 安裝chrony服務
sudo yum install chrony -y
sudo systemctl start chronyd
sudo systemctl enable chronyd
# 設施同步網路時間
sudo timedatectl set-timezone Asia/Shanghai
sudo timedatectl set-local-rtc 0
sudo timedatectl set-ntp true
# 移除舊內核
sudo yum remove -y $(rpm -qa | grep ^kernel | grep -v `uname -r`)


LEMP Info Introduce

LEMP中各軟件的具體信息

Software Version
Nginx 1.10.2
Percona 5.7.16-10
PHP 5.6.27

Nginx的source配置參考自

Percona的source配置參考自

PHP可通過remi安裝,但該源依賴epel源。

Installing LEMP

安裝LEMP開發套件

Nginx Installation

通常的安裝命令如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#添加Nginx官方Repo
#http://nginx.org/en/linux_packages.html
#https://www.nginx.com/resources/wiki/start/topics/tutorials/install/
sudo tee /etc/yum.repos.d/nginx.repo <<-'EOF'
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
EOF
#安裝Nginx
sudo yum clean all
sudo yum install -y nginx
#啟動Nginx服務
sudo systemctl start nginx
#設置Nginx為開機啟動
sudo systemctl enable nginx
#更改Web Root目錄的owner,group為nginx
chown -R nginx:nginx /usr/share/nginx/html/

重要:但本文需要為Nginx配置SSL證書,故請忽略剛給出的操作命令,按照本人Blog Secure Nginx With Let’s Encrypt Free SSL Certificate on GNU/Linux中的操作進行配置即可。

操作完成後查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#查看版本號
[email protected]:~# nginx -v
nginx version: nginx/1.10.2
#查看版本號,編譯器版本,腳本配置參數
[email protected]:~# nginx -V
nginx version: nginx/1.10.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'
#抓取頁面HTTP Header信息
#使用迴環地址
[[email protected] ~]# curl -I localhost
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 23 Dec 2016 02:25:34 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://lemptest.tech/
#使用外網地址
[[email protected] ~]# curl -I 138.197.80.35
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 23 Dec 2016 02:25:43 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://lemptest.tech/

Percona Installation

根據上文列出的參考文檔

The easiest way to install the Percona Yum repository is to install an RPM that configures yum and installs the Percona GPG key.

安裝方式有2種

方式1: 通過rpm包添加

1
sudo yum localinstall -y http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm

方式2: 手動添加(通過查看percona-release-0.1-3.noarch.rpm文件獲取)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#添加Nginx官方Source
sudo tee /etc/yum.repos.d/percona.repo <<-'EOF'
########################################
# Percona releases and sources, stable #
########################################
[percona-release-$basearch]
name = Percona-Release YUM repository - $basearch
baseurl = http://repo.percona.com/release/$releasever/RPMS/$basearch
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-release-noarch]
name = Percona-Release YUM repository - noarch
baseurl = http://repo.percona.com/release/$releasever/RPMS/noarch
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-release-source]
name = Percona-Release YUM repository - Source packages
baseurl = http://repo.percona.com/release/$releasever/SRPMS
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
####################################################################
# Testing & pre-release packages. You don't need it for production #
####################################################################
[percona-testing-$basearch]
name = Percona-Testing YUM repository - $basearch
baseurl = http://repo.percona.com/testing/$releasever/RPMS/$basearch
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-testing-noarch]
name = Percona-Testing YUM repository - noarch
baseurl = http://repo.percona.com/testing/$releasever/RPMS/noarch
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-testing-source]
name = Percona-Testing YUM repository - Source packages
baseurl = http://repo.percona.com/testing/$releasever/SRPMS
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
############################################
# Experimental packages, use with caution! #
############################################
[percona-experimental-$basearch]
name = Percona-Experimental YUM repository - $basearch
baseurl = http://repo.percona.com/experimental/$releasever/RPMS/$basearch
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-experimental-noarch]
name = Percona-Experimental YUM repository - noarch
baseurl = http://repo.percona.com/experimental/$releasever/RPMS/noarch
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
[percona-experimental-source]
name = Percona-Experimental YUM repository - Source packages
baseurl = http://repo.percona.com/experimental/$releasever/SRPMS
enabled = 0
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
EOF
#安裝Percona的GPG Key
curl -s https://www.percona.com/downloads/RPM-GPG-KEY-percona -o /etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
#此處導入後的名稱為 gpg-pubkey-cd2efd2a-4b26dda1
#列出所有的gpg-pubkey
# rpm -qa gpg-pubkey*
#查詢指定gpg-pubkey的信息
# rpm -qi gpg-pubkey-cd2efd2a-4b26dda1
#移除指定的gpg-pubkey
# rpm -e gpg-pubkey-cd2efd2a-4b26dda1

執行

1
rpm -qi gpg-pubkey-cd2efd2a-4b26dda1 | sed -n '1,/^Description/p'

獲得如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Name : gpg-pubkey
Version : cd2efd2a
Release : 4b26dda1
Architecture: (none)
Install Date: Fri 23 Dec 2016 10:56:56 AM CST
Group : Public Keys
Size : 0
License : pubkey
Signature : (none)
Source RPM : (none)
Build Date : Tue 15 Dec 2009 08:51:45 AM CST
Build Host : localhost
Relocations : (not relocatable)
Packager : Percona MySQL Development Team <[email protected]>
Summary : gpg(Percona MySQL Development Team <[email protected]>)
Description :

兩種方式的操作結果一致,繼續如下操作

1
2
3
4
5
6
7
8
9
10
11
12
13
#清空yum緩存
sudo yum clean all
#移除默認安裝的mariadb5.5
sudo yum erase -y mariadb*
#安裝Percona 5.7
sudo yum install -y Percona-Server-server-57
#啓動MySQL服務並設置爲開機自動啓動
sudo systemctl start mysqld
sudo systemctl enable mysqld

重要:在Debian系統中,安裝過程中會出現提示框,要求爲root用戶輸入密碼。但在CentOS7中不會出現。

安裝完成後出現如下信息

1
2
3
4
5
6
Percona Server is distributed with several useful UDF (User Defined Function) from Percona Toolkit.
Run the following commands to create these functions:
mysql -e "CREATE FUNCTION fnv1a_64 RETURNS INTEGER SONAME 'libfnv1a_udf.so'"
mysql -e "CREATE FUNCTION fnv_64 RETURNS INTEGER SONAME 'libfnv_udf.so'"
mysql -e "CREATE FUNCTION murmur_hash RETURNS INTEGER SONAME 'libmurmur_udf.so'"
See http://www.percona.com/doc/percona-server/5.7/management/udf_percona_toolkit.html for more details

根據自身意願選擇是否執行。

操作完成後查看

1
2
3
4
5
6
7
[email protected]:~# mysql -V
mysql Ver 14.14 Distrib 5.7.16-10, for Linux (x86_64) using 6.2
#提取版本號
[[email protected] ~]# mysql -V | sed -r -n '[email protected]*Distrib (.*),.*@\[email protected]'
5.7.16-10

Percona5.7的配置文件及其讀取順序,可通過如下命令獲取

1
2
mysql --help | awk '$0~/Default options/{getline;print}'
mysqladmin --help | awk '$0~/Default options/{getline;print}'

操作過程

1
2
3
4
5
[[email protected] ~]# mysql --help | awk '$0~/Default options/{getline;print}'
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
[[email protected] ~]# mysqladmin --help | awk '$0~/Default options/{getline;print}'
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf

可以得知,Percona5.7配置文件的讀取順序是
/etc/my.cnf > /etc/mysql/my.cnf > /usr/etc/my.cnf > ~/.my.cnf

PHP Installation

PHP版本選擇5.6,順序安裝epelremi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#安裝yum源
sudo yum clean all
sudo yum install -y epel-release
sudo yum localinstall -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
#啟用/etc/yum.repos.d/remi.repo中[remi-php56]部分中的enabled為1
# vim /etc/yum.repos.d/remi.repo
sudo sed -i -r "[email protected][email protected][email protected];/\[remi-php56\]/,/\[remi-test\]/ [email protected][email protected][email protected]" /etc/yum.repos.d/remi.repo
# 查看與php56相關的包
#yum info php* | awk '$1=="Name" && $NF~/php56/{print $NF}'
sudo yum install -y php56-php \
php56-php-cli \
php56-php-common \
php56-php-fpm \
php56-build \
php56-php-mysqlnd \
php56-php-pdo \
php56-php-bcmath \
php56-php-mbstring \
php56-php-mcrypt \
php56-php-embedded \
php56-php-gd \
php56-php-geos \
php56-php-gmp \
php56-php-imap \
php56-php-intl \
php56-php-ldap \
php56-php-libvirt \
php56-php-magickwand \
php56-php-pear \
php56-php-phurple \
php56-php-pimple \
php56-php-process \
php56-php-pspell \
php56-php-recode \
php56-php-twig \
php56-runtime


Configuration

各軟件默認配置文件路徑

Software Conf Path
Nginx /etc/nginx/nginx.conf/etc/nginx/conf.d/default.conf
Percona /etc/my.cnf.d//etc/percona-server.conf.d//etc/mysql/percona-server.conf.d/mysqld.cnf
PHP /opt/remi/php56/root/etc//opt/remi/php56/root/etc/php.ini
PHP-FPM /opt/remi/php56/root/etc/php-fpm.d/www.conf

LEMP環境的配置具體可參考本人Blog

建議:更改文件之前先進行備份!

以文件/etc/nginx/nginx.conf爲例

1
2
#在同一目錄下備份源文件
sudo cp -pv /etc/nginx/nginx.conf{,.old}

Nginx Configuration

Nginx的配置、優化涉及到內核參數的調整,具體參見

/etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# /etc/nginx/nginx.conf
user nginx;
# worker_processes 1 or N or auto
worker_processes 2;
worker_rlimit_nofile 65536;
pid /var/run/nginx.pid;
events {
worker_connections 65536;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
charset utf-8;
server_tokens off; #關閉版本信息顯示
autoindex off; #禁止顯示目錄下文件,默認off
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# [debug|info|notice|warn|error|crit|alert|emerg]
error_log /var/log/nginx/error.log warn;
access_log /var/log/nginx/access.log combined if=$loggable;
#Conditional Logging
map $status $loggable {
~^[23] 0;
default 1;
}
#log_format name string ...; default combined "...";
log_format cpmpression '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" "$gzip_ratio"';
# Concurrency Connections
# http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn_zone
# One megabyte zone can keep about 32 thousand 32-byte states or about 16 thousand 64-byte states.
limit_conn_zone $binary_remote_addr zone=addr:10m;
#Keep Alive
keepalive_timeout 50;
keepalive_requests 100000;
#Timeouts
client_header_timeout 3m;
client_body_timeout 3m;
send_timeout 60s;
#Buffer Size
client_body_buffer_size 128k;
client_max_body_size 2m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 1 32k;
postpone_output 1460;
#Close connection on Missing Client Response
reset_timedout_connection on;
#Static Asset Serving
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 5;
open_file_cache_errors off;
# gzip compression
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_buffers 16 8k;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/css application/javascript application/x-javascript text/javascript text/plain text/xml application/json application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/xml font/eot font/opentype font/otf image/svg+xml image/vnd.microsoft.icon;
gzip_disable "MSIE [1-6]\.";
gzip_static on;
include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/default.conf

重點部分是對PHP的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# redirect http to https
server {
listen 80;
server_name lemptest.tech;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name lemptest.tech;
#access_log /var/log/nginx/log/host.access.log main;
root /usr/share/nginx/html;
location / {
root /usr/share/nginx/html;
# 添加index.php
index index.php index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# 設置PHP解析
location ~ \.php$ {
try_files $uri = 404;
root /usr/share/nginx/html;
#根據自身情況設置
#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/var/run/php/php5-fpm.sock;
fastcgi_index index.php;
#fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
ssl_certificate /etc/letsencrypt/live/lemptest.tech/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lemptest.tech/privkey.pem;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#https://scotthelme.co.uk/doing-the-chacha-with-nginx/
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH DHE-RSA-CHACHA20-POLY1305 EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4 !SEED !CAMELLIA";
ssl_ecdh_curve secp384r1; # Specifies a curve for ECDHE ciphers.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/ssl/ticket.key; #the list of certificates will not be sent to clients
#OCSP Stapling Configuration
ssl_stapling on;
ssl_stapling_verify on; # Enables verification of OCSP responses by the server
#Let's Encrypt Root and Intermediate Certificates
ssl_trusted_certificate /etc/nginx/ssl/letsencrypt-ca-cert.pem;
# Google DNS, Open DNS, Dyn DNS
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 216.146.35.35 216.146.36.36 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
#https://scotthelme.co.uk/content-security-policy-an-introduction/
add_header Content-Security-Policy 'default-src self';
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Public-Key-Pins 'pin-sha256="iUUgoZuZgkGIbQ9x1lUQbvCJh+87iT1avjyzKKu7K3k=";pin-sha256="F2gQEUpXylr1jmAr6f9WNlFAMxORt597saJMqGCcoks="; max-age=2592000; includeSubDomains';
#if ($scheme = http) {
# return 301 https://$host$request_uri;
#}
#check file exists or not http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files
#location / {
# try_files $uri $uri/ =404;
#}
# Disable unwanted HTTP methods
# 405 A request was made of a resource using a request method not supported by that resource;
if ($request_method !~ ^(GET|HEAD|POST)$ )
{
return 405;
}
# Deny Certain User-Agents or Bots:
if ($http_user_agent ~* LWP::Simple|wget|libwww-perl) {
return 403;
}
if ($http_user_agent ~ (msnbot|Purebot|Baiduspider|Lipperhey|Mail.Ru|scrapbot) ) {
return 403;
}
# Blocking Referral Spam
if ( $http_referer ~* (jewelry|viagra|nude|girl|nudit|casino|poker|porn|sex|teen|babes) ) {
return 403;
}
# Stop Hotlinking 防盜鏈
# location ~ .(gif|png|jpe?g)$ {
# valid_referers none blocked example.com *.example.com;
# if ($invalid_referer) {
# return 403;
# }
# }
# Deny execution of scripts
# deny scripts inside writable directories
# location ~* /(images|cache|media|logs|tmp)/.*.(php|pl|py|jsp|asp|sh|cgi)$ {
# return 403;
# error_page 403 /403_error.html;
# }
# file cache
# location ~* .(woff|eot|ttf|svg|mp4|webm|jpg|jpeg|png|gif|bmp|ico|css|js)$ {
# expires 365d;
# log_not_found off;
# access_log off;
# }
# location ~ ^/favicon\.ico$ {
# root /usr/share/nginx/html;
# }
}

注意: 其中的unix:/var/run/php/php5-fpm.sock會在接下來的php-fpm參數配置中設置,請務必確保兩處的sock路徑一致。

Nginx配置文件修改完成後,使用如下命令

  1. 查看是否配置文件有語法錯誤

    1
    sudo nginx -t
  2. 動態重新載入配置文件

    1
    sudo nginx -s reload

Nginx默認的Web Root路徑是/usr/share/nginx/html,其默認的user、group都是root用戶,此處將其更改爲nginx

1
sudo chown -R nginx:nginx /usr/share/nginx/html

Percona Configuration

參照 MariaDB Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#/etc/percona-server.conf.d/mysqld.cnf
[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/lib/mysql/mysql.sock
port = 3306
datadir = /var/lib/mysql
explicit_defaults_for_timestamp
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address = 127.0.0.1
log-error = /var/log/mysql/error.log
# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_ALL_TABLES
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
character_set_server = utf8
collation-server = utf8_general_ci
default-time_zone = '+8:00'
skip_name_resolve = 1
interactive_timeout = 600 # time seconds
wait_timeout = 600 # time seconds
# InnoDB Variables Setting
default_storage_engine = InnoDB
innodb_file_per_table = 1
innodb_strict_mode = 1
innodb_file_format_check = 1
innodb_buffer_pool_size = 256M # Go up to 80% of your available RAM
#innodb_buffer_pool_instances = 8 # combine with innodb_buffer_pool_size,if its size < 1GB, the default value is 1
innodb_flush_method = O_DIRECT
# innodb_read_io_threads = 16 # If you have a strong I/O system or SSD
# innodb_write_io_threads = 32 # If you have a strong I/O system or SSD
# innodb_io_capacity = 10000 # If you have a strong I/O system or SSD
# innodb_io_capacity_max = 30000 # If you have a strong I/O system or SSD
innodb_flush_log_at_trx_commit = 2 # 1 for durability, 0 or 2 for performance
innodb_log_buffer_size = 16M
innodb_log_file_size = 64M # Bigger means more write throughput but longer recovery time
max_connections = 400 # Values < 1000 are typically good
max_user_connections = 300 # Limit one specific user/application
thread_cache_size = 400 # Up to max_connections makes sense
tmp_table_size = 16M
# Query
query_cache_type = 1
query_cache_size = 8M
max_allowed_packet = 32M
# Memory Table
#max_heap_table_size = 32M
log_timestamps = SYSTEM
# Slow Query Log
slow_query_log = 1
long_query_time = 1.5
slow_query_log_file = /var/log/mysql/percona_slow.log
#log_queries_not_using_indexes=1
#min_examined_row_limit = 100
# Record General Query Log
general_log_file = /var/log/mysql/percona_general.log
general_log = 1
# Error Log
log_error = /var/log/mysql/percona_error.log
log_warnings = 2
# log_error_verbosity = 3
innodb_print_all_deadlocks = 1
# Binary Logging
server_id = 1
log_bin = percona-bin
log_bin_index = percona-bin.index #The index file for binary log file names.
binlog_format=mixed
binlog_cache_size = 1M
binlog_stmt_cache_size = 1M
max_binlog_size = 32M
sync_binlog = 1
expire_logs_days = 15
binlog_row_image = full

注意: 其中的部分參數需根據服務器的實際硬件配置情況設置。

執行如下操作重啓Percona服務

1
sudo systemctl restart mysqld

如果重啟失敗,查找日誌路徑/var/log/mysql/是否存在,如果不存在,執行如下命令

1
2
[[ ! -d /var/log/mysql ]] && sudo mkdir -m 750 -pv /var/log/mysql
sudo chown -R mysql:mysql /var/log/mysql

再次重啟服務

提醒: 日誌是判斷故障原因的有力工具。

重要: MySQL 5.7初次啟動後會生成臨時密碼,存儲在日誌/var/log/mysqld.log,其首次登陸後必須更改用戶密碼才能進行CURD操作。否則會報如下錯誤

ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

具體見官方文檔

可通過如下命令提取root賬戶臨時密碼

1
2
3
grep 'temporary password' /var/log/mysqld.log
#或
awk '$0~/temporary password/{print $NF}' /var/log/mysqld.log

1
2
3
[[email protected] ~]# grep 'temporary password' /var/log/mysqld.log
2016-12-23T06:18:55.304668Z 1 [Note] A temporary password is generated for [email protected]: Skkg3fuin6,a

此處的臨時密碼是Skkg3fuin6,a

執行

1
mysql -uroot -p

根據提示輸入臨時密碼進入MySQL,然後重置密碼,否則無法執行SQL語句。執行如下語句重置密碼

1
2
alter user 'root'@'localhost' identified by '[email protected]';
flush privileges;

執行如下命令進行安全設置

1
mysql_secure_installation

操作過程如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
[[email protected] ~]# mysql_secure_installation
Securing the MySQL server deployment.
Enter password for user root:
The 'validate_password' plugin is installed on the server.
The subsequent steps will run with the existing configuration
of the plugin.
Using existing password for root.
Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) :
... skipping.
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.
Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
- Dropping test database...
Success.
- Removing privileges on test database...
Success.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.
All done!

執行

1
openssl rand -base64 20

生成18位隨機字符串作為密碼

1
2
3
[email protected]:~$ openssl rand -base64 18
Kj/Fea1JT0UtSBa4h7gdqdPV

創建普通用戶,在登入Percona後執行如下操作

1
2
3
create user 'security'@'localhost' identified by 'Kj/Fea1JT0UtSBa4h7gdqdPV';
grant all on *.* to 'security'@'localhost';
flush privileges;

在用戶家目錄下創建文件~/.my.cnf

1
2
3
4
5
6
7
tee ~/.my.cnf <<-'EOF'
[client]
user=security
password=Kj/Fea1JT0UtSBa4h7gdqdPV
EOF
chmod 400 ~/.my.cnf

可實現免密碼登錄 ,直接執行mysql即可。

測試登錄用戶信息

1
2
3
4
5
6
7
[[email protected] ~]# mysql -e "select user();"
+--------------------+
| user() |
+--------------------+
+--------------------+

PHP-FPM Configuration

參照 PHP Configuration

/opt/remi/php56/root/etc/php.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#關閉Nginx文件類型錯誤解析
;cgi.fix_pathinfo=1
cgi.fix_pathinfo=0
#設置時區
;date.timezone =
date.timezone = Asia/Shanghai
#禁止顯示PHP版本信息
;expose_php = On
expose_php = Off
#支持PHP短標籤
;short_open_tag = On
short_open_tag = Off

/opt/remi/php56/root/etc/php-fpm.d/www.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 23,25行
;user = apache
;group = apache
user = nginx
group = nginx
# 39行,此處須與Nginx中一致
;listen = 127.0.0.1:9000
listen = /var/run/php/php5-fpm.sock
#如果不指定爲nginx,無法正常監聽php-fpm.sock
# 49~51行
;listen.owner = www-data
;listen.group = www-data
;listen.mode = 0660
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

默認.pid.sock都在/opt/remi/php56/root/var/run/php-fpm/目錄下

路徑/var/run/php/默認不存在,需手動創建,並將owner, group更改爲nginx,執行如下命令

1
2
[[ ! -d /var/run/php ]] && sudo mkdir -pv /var/run/php
sudo chown -R nginx:nginx /var/run/php

注意: 文件/opt/remi/php56/root/var/run/php-fpm/php-fpm.pid/usr/lib/systemd/system/php56-php-fpm.service使用,故需要修改文件php56-php-fpm.service中的參數PIDFile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#/usr/lib/systemd/system/php56-php-fpm.service
# It's not recommended to modify this file in-place, because it
# will be overwritten during upgrades. If you want to customize,
# the best way is to use the "systemctl edit" command.
[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target
[Service]
Type=notify
PIDFile=/var/run/php/php-fpm.pid
EnvironmentFile=/opt/remi/php56/root/etc/sysconfig/php-fpm
ExecStart=/opt/remi/php56/root/usr/sbin/php-fpm --nodaemonize
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
#添加服務別名
Alias=php-fpm.service

注意: 如果目錄/var/lib/php/session存在,建議執行如下操作

1
[[ -d /var/lib/php/session ]] && chown -R nginx:nginx /var/lib/php/session

重啓php-fpm服務

1
2
3
4
5
6
sudo systemctl daemon-reload
sudo systemctl restart php56-php-fpm
systemctl enable php56-php-fpm
#or
sudo systemctl restart php-fpm

Warning: Unit file of php5-fpm.service changed on disk, ‘systemctl daemon-reload’ recommended.

重要: 請勿忘記重啓Nginx服務

1
sudo systemctl restart nginx

否則PHP文件無法被Nginx解析,會出現文件下載提示信息。

Testing

測試
Nginx默認的Web Root路徑是/usr/share/nginx/html

PHP Probe Testing

PHP探針

1
2
3
4
5
sudo tee /usr/share/nginx/html/index.php <<-'EOF'
<?php
phpinfo();
?>
EOF

PHP Function Testing

1
2
3
4
5
6
sudo tee /usr/share/nginx/html/index.php <<-'EOF'
<?php
echo '<pre>';
print_r($_SERVER);
?>
EOF

Database Connection Testing

數據庫連接測試

Method 1 mysql_connect

根據mysql_connect進行數據庫連接測試

1
2
3
4
5
6
7
8
9
10
11
sudo tee /usr/share/nginx/html/index.php <<-'EOF'
<?php
$link = mysql_connect('localhost', 'security', 'Kj/Fea1JT0UtSBa4h7gdqdPV');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
echo "Connected successfully"."<br>";
echo date('Y-m-d H:i:s');
mysql_close($link);
?>
EOF

Method 2 PDO

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo tee /usr/share/nginx/html/index.php <<-'EOF'
<?php
$db = new PDO('mysql:host=localhost;port=3306','security','Kj/Fea1JT0UtSBa4h7gdqdPV');
$sql = "select user,host from mysql.user";
$stmt = $db->prepare($sql);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '<pre>';
print_r($rows);
echo date('Y-m-d H:i:s');
?>
EOF

Mixed Content

當網站使用HTTPS進行通信時,瀏覽器默認不加載使用HTTP協議傳輸的文件,如css、js等,這種行為稱之為mixed content。css、js加載不了,直接影響是頁面排版變亂。

Mixed Content的具體介紹見https://w3c.github.io/webappsec-mixed-content/

When a user visits a page served over HTTPS, their connection with the web server is encrypted with TLS and is therefore safeguarded from sniffers and man-in-the-middle attacks. If the HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted; the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, so the connection is not safeguarded. When a web page exhibits this behavior, it is called a mixed content page. – https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content

When an HTTPS website references insecure (HTTP) resources, this is called mixed content.

HTTP Header Content-Security-Policy可進行白名單控制,具體使用可參閱 Content Security Policy - An Introduction

References

Content Security Policy


Change Logs

  • 2016.12.23 19:56 Fri Asia/Shanghai
    • 初稿完成