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

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

# 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登陸),登錄命令

ssh -C -c blowfish root@138.197.80.35

系統信息

#內核版本
root@lempstacker:~# uname -r
3.10.0-514.2.2.el7.x86_64

#發行版信息
root@lempstacker:~# 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"
root@lempstacker:~#

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

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

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

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

大致命令如下

# 更換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配置參考自 * nginx: Linux packages * Install

Percona的source配置參考自 * Installing Percona Server on Red Hat Enterprise Linux and CentOS

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

Installing LEMP

安裝LEMP開發套件

Nginx Installation

通常的安裝命令如下

#添加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中的操作進行配置即可。

操作完成後查看

#查看版本號
root@lempstacker:~# nginx -v
nginx version: nginx/1.10.2

#查看版本號,編譯器版本,腳本配置參數
root@lempstacker:~# 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'
root@lempstacker:~#

#抓取頁面HTTP Header信息
#使用迴環地址
[root@lempstacker ~]# 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/

#使用外網地址
[root@lempstacker ~]# 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包添加

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文件獲取)

#添加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

執行

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

獲得如下信息

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 <mysql-dev@percona.com>
Summary     : gpg(Percona MySQL Development Team <mysql-dev@percona.com>)
Description :

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

#清空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中不會出現。

安裝完成後出現如下信息

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

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

操作完成後查看

root@lempstacker:~# mysql -V
mysql  Ver 14.14 Distrib 5.7.16-10, for Linux (x86_64) using  6.2

#提取版本號
[root@lempstacker ~]# mysql -V | sed -r -n 's@.*Distrib (.*),.*@\1@p'
5.7.16-10
[root@lempstacker ~]#

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

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

操作過程

[root@lempstacker ~]# mysql --help | awk '$0~/Default options/{getline;print}'
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
[root@lempstacker ~]# mysqladmin --help | awk '$0~/Default options/{getline;print}'
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
[root@lempstacker ~]#

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

PHP Installation

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

#安裝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 "s@enabled=1@enabled=0@g;/\[remi-php56\]/,/\[remi-test\]/ s@enabled=0@enabled=1@" /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 * LEMP Installation and Nginx Optimization * Compiling And Installing Zabbix3.2 Based On LEMP On CentOS6.8 * Nginx Simple Usage on CentOS 7

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

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

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

Nginx Configuration

Nginx的配置、優化涉及到內核參數的調整,具體參見 * LEMP Installation and Nginx Optimization * Secure Nginx With Let’s Encrypt Free SSL Certificate on GNU/Linux

/etc/nginx/nginx.conf

# /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的解析

# 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. 查看是否配置文件有語法錯誤

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

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

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

Percona Configuration

參照 MariaDB Configuration

#/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服務

sudo systemctl restart mysqld

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

[[ ! -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.

具體見官方文檔 * A Quick Guide to Using the MySQL Yum RepositoryStarting the MySQL Server部分; * 2.5.1 Installing MySQL on Linux Using the MySQL Yum RepositoryStarting the MySQL Serve部分;

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

grep 'temporary password' /var/log/mysqld.log
#或
awk '$0~/temporary password/{print $NF}' /var/log/mysqld.log
[root@lempstacker ~]# grep 'temporary password' /var/log/mysqld.log
2016-12-23T06:18:55.304668Z 1 [Note] A temporary password is generated for root@localhost: Skkg3fuin6,a
[root@lempstacker ~]#

此處的臨時密碼是Skkg3fuin6,a

執行

mysql -uroot -p

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

alter user 'root'@'localhost' identified by 'percona@Security2017';
flush privileges;

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

mysql_secure_installation

操作過程如下

[root@lempstacker ~]# 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!
[root@lempstacker ~]#

執行

openssl rand -base64 20

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

flying@lempstacker:~$ openssl rand -base64 18
Kj/Fea1JT0UtSBa4h7gdqdPV
flying@lempstacker:~$

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

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

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

tee ~/.my.cnf <<-'EOF'
[client]
user=security
password=Kj/Fea1JT0UtSBa4h7gdqdPV
EOF

chmod 400 ~/.my.cnf

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

測試登錄用戶信息

[root@lempstacker ~]# mysql -e "select user();"
+--------------------+
| user()             |
+--------------------+
| security@localhost |
+--------------------+
[root@lempstacker ~]#

PHP-FPM Configuration

參照 PHP Configuration

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

#關閉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

# 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,執行如下命令

[[ ! -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

#/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存在,建議執行如下操作

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

重啓php-fpm服務

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服務

sudo systemctl restart nginx

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

Testing

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

PHP Probe Testing

PHP探針

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

PHP Function Testing

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進行數據庫連接測試

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

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
    • 初稿完成