企业级Nginx Web服务优化-Nginx基本安全优化
1.1 调整参数隐藏Nginx软件版本号信息
- 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的。因此,我们应尽量隐藏或消除Web服务对访问用户显示各类敏感信息(例如Web软件名称及版本号等信息),这样恶意的用户就很难猜到他攻击的服务器所用的是否有特定漏洞的软件,或者是否有对应漏洞的某一特定版本,从而加强Web服务的安全性。这在武侠小说里,就相当于隐身术,你隐身了,对手就很难打着你了。
- 想要隐身,首先要了解所使用软件的版本号,对于Linux客户端,可通过命令行查看Nginx版本号,最简单的方法就是在Linux客户端系统命令行执行如下curl命令:
[root@LNMP html]# curl -I 192.168.0.220
HTTP/1.1 200 OK
Server: nginx/1.6.2 #这里清晰的暴露了Web版本号(1.6.2)及软件名称(nginx)
Date: Wed, 23 Aug 2017 10:45:47 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.3.28
Link: <http://192.168.0.220/wp-json/>; rel="https://api.w.org/"
在Windows客户端上,通过浏览器访问Web服务时,若找不到页面,默认报错的信息如下图所示:
以上虽然是不同的客户端,但是都获得了Nginx软件名称,而且查到了Nginx的版本号,这就使得Nginx Web服务的安全存在一定的风险,因此,应隐藏掉这些敏感信息或用一个其他的名字将其替代。例如,下面是百度搜索引擎网站Web软件的更名做法:
[root@LNMP html]# curl -I baidu.com
HTTP/1.1 200 OK
Date: Fri, 25 Aug 2017 12:22:29 GMT
Server: Apache #将Web服务软件更名为了Apache,并且版本号也去掉了
[root@LNMP html]# curl -I -s www.baidu.com
HTTP/1.1 200 OK
Server: bfe/1.0.8.18 #将Web服务软件更名为了bfe,并且版本号改为1.0.8.18(闭源软件名称和版本就无所谓了)
门户网站尚且如此,我们也学着隐藏或改掉应用服务软件名和版本号把!事实上,还可以通过配置文件加参数来隐藏Nginx版本号。编辑nginx.conf配置文件增加参数,实现隐藏Nginx版本号的方式如下:
#在Nginx配置文件nginx.conf中的http标签段内加入“server_tokens off”
http
{
...............
server_tokens off;
...............
}
此参数放置在http标签内,作用是控制http response header内的Web服务版本信息的显示,以及错误信息中Web服务版本信息的显示。
server_tokens参数的官方说明如下:
syntax: server_tokens on|off; #此行为参数语法,on为开启状态,off为关闭状态
default: server_tokens on; #此行意思是不配置该参数,软件默认情况的结果
context: http,server,location #此行为server_tokens参数可以放置的位置参数作用:激活或禁止Nginx的版本信息显示在报错信息和Server的响应首部位置中。
官方资料地址:http://nginx.org/en/docs/http/ngx_http_core_module.html
配置完毕后保存,重新加载配置文件,再次通过curl查看,结果如下:
[root@LNMP nginx]# curl -I 192.168.0.220
HTTP/1.1 200 OK
Server: nginx #版本号已经消失
Date: Wed, 23 Aug 2017 11:22:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.3.28
Link: <http://192.168.0.220/wp-json/>; rel="https://api.w.org/"
此时,浏览器的报错提示中没有了版本号,如下图所示,修改成功。
1.2 更改源码隐藏Nginx软件名及版本号
隐藏了Nginx版本号后,更进一步,可以通过一些手段把Web服务软件的名称也隐藏起来,或者更改为其他Web服务软件名以迷惑黑客。但软件名字的隐藏修改,一般情况下不会有配置参数和入口,Nginx也不例外,这可能是由于商业及品牌展示等原因,软件提供商不希望使用者把软件名字隐藏起来。因此,此处需要更改Nginx源代码,具体的解决方法如下:
1.2.1 第一步:依次修改3个Nginx源码文件。
修改的第一个文件为nginx-1.6.3/src/core/nginx.h,如下:
[root@LNMP ~]# cd /usr/src/nginx-1.6.2/src/core/
[root@LNMP core]# ls -l nginx.h
-rw-r--r--. 1 1001 1001 351 Sep 16 2014 nginx.h
[root@LNMP core]# sed -n '13,17p' nginx.h
#define NGINX_VERSION "1.6.2" #修改为想要显示的版本号
#define NGINX_VER "nginx/" NGINX_VERSION
#将nginx修改为想要修改的软件名称。
#define NGINX_VAR "NGINX" #将nginx修改为想要修改的软件名称
#define NGX_OLDPID_EXT ".oldbin"
修改后的结果如下:
[root@LNMP core]# sed -n '13,17p' nginx.h
#define NGINX_VERSION "0.0.0.0"
#define NGINX_VER "yunjisuan/" NGINX_VERSION
#define NGINX_VAR "YUNJISUAN"
#define NGX_OLDPID_EXT ".oldbin"
修改的第二个文件是nginx-1.6.3/src/http/ngx_http_header_filter_module.c的第49行,需要修改的字符串内容如下:
ls -l /usr/src/nginx-1.6.2/src/http/ngx_http_header_filter_module.c
-rw-r--r--. 1 1001 1001 19321 Sep 16 2014 /usr/src/nginx-1.6.2/src/http/ngx_http_header_filter_module.c
[root@LNMP http]# grep -n 'Server: nginx' ngx_http_header_filter_module.c
49:static char ngx_http_server_string[] = "Server: nginx" CRLF; #修改本行结尾的nginx
通过sed替换修改,后如下:
[root@LNMP http]# grep -n 'Server: nginx' ngx_http_header_filter_module.c
49:static char ngx_http_server_string[] = "Server: nginx" CRLF;
[root@LNMP http]# sed -i 's#Server: nginx#Server: yunjisuan#g' ngx_http_header_filter_module.c
[root@LNMP http]# grep -n 'Server: yunjisuan' ngx_http_header_filter_module.c
49:static char ngx_http_server_string[] = "Server: yunjisuan" CRLF;
修改的第三个文件是nginx-1.6.3/src/http/nginx_http_special_response.c,对面页面报错时,它会控制是否展开敏感信息。这里输出修改前的信息ngx_http_special_response.c中的第21~30行,如下:
[root@LNMP http]# sed -n '21,30p' ngx_http_special_response.c
static u_char ngx_http_error_full_tail[] =
"<hr><center>" NGINX_VER "</center>" CRLF #此行需要修改
"</body>" CRLF
"</html>" CRLF
;
static u_char ngx_http_error_tail[] =
"<hr><center>nginx</center>" CRLF #此行需要修改
"</body>" CRLF
修改后的结果如下:
[root@LNMP nginx-1.6.2]# sed -n '21,32p' src/http/ngx_http_special_response.c
static u_char ngx_http_error_full_tail[] =
"<hr><center>" NGINX_VER " (Mr.chen 2018-08-26)</center>" CRLF #此行是定义对外展示的内容
"</body>" CRLF
"</html>" CRLF
;
static u_char ngx_http_error_tail[] =
"<hr><center>yunjisuan</center>" CRLF #此行将对外展示的Nginx名字更改为yunjisuan
"</body>" CRLF
"</html>" CRLF
;
1.2.2 第二步是修改后编辑软件,使其生效
修改后再编译安装软件,如果是已经安装好的服务,需要重新编译Nginx,配好配置,启动服务。
再次使浏览器出现404错误,然后看访问结果,如下图所示:
如上面所示:Nginx的软件和版本名都被改掉了,并且加上了本人的大名。
再看看Linux curl命令响应头部信息,如下:
[root@LNMP conf]# curl -I localhost/xxx/
HTTP/1.1 404 Not Found
Server: yunjisuan/0.0.0.0 #也更改了
Date: Wed, 23 Aug 2017 15:33:54 GMT
Content-Type: text/html
Content-Length: 196
Connection: keep-alive
1.3 更改Nginx服务的默认用户
- 为了让Web服务更安全,要尽可能地改掉软件默认的所有配置,包括端口,用户等。
- 下面就来更改Nginx服务的默认用户
- 首先,查看Nginx服务对应的默认用户。一般情况下,Nginx服务启动后,默认使用的用户是nobody,查看默认的配置文件,如下:
[root@LNMP conf]# cd /usr/local/nginx/conf/
[root@LNMP conf]# grep "#user" nginx.conf.default
#user nobody;
为了防止黑客猜到这个Web服务的用户,我们需要更改成特殊的用户名,例如nginx或特殊点的inca,但是这个用户必须是系统里事先存在的,下面以nginx用户为例进行说明。
(1)为Nginx服务建立新用户
useradd nginx -s /sbin/nologin -M
#不需要有系统登录权限,应当禁止登陆。
(2)配置Nginx服务,让其使用刚建立的nginx用户
更改Nginx服务默认使用用户,方法有二:
第一种:直接更改配置文件参数,将默认的#user nobody;改为如下内容:
user nginx nginx;
如果注释或不设置上述参数,默认为nobody用户,不推荐使用nobody用户名,最好采用一个普通用户,此处用大家习惯的,前面建立好的nginx用户。
第二种:直接在编译nginx软件时指定编译的用户和组,命令如下:
./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module
#提示:
#前文在编译Nginx服务时,就是这样带着参数的,因此无论配置文件中是否加参数,默认都是nginx用户。
(3)检查更改用户的效果
重新加载配置后,检查Nginx服务进程的对应用户,如下:
[root@LNMP conf]# ps -ef | grep nginx | grep -v grep
root 52023 1 0 11:30 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx 52024 52023 0 11:30 ? 00:00:00 nginx: worker process
通过查看上述更改后的Nginx进程,可以看到worker processes进程对应的用户都变成了nginx。所以,我们有理由得出结论,上述的两种方法都可设置Nginx的worker进程运行的用户。当然,Nginx的主进程还是以root身份运行的。
二,根据参数优化Nginx服务性能
2.1 优化Nginx服务的worker进程个数
- 在高并发,高访问量的Web服务场景,需要事先启动好更多的Nginx进程,以保证快速响应并处理大量并发用户的请求。
- 这类似于开饭店,在营业前,需要事先招聘一定数量的服务员准备接待顾客,但这里就有一个问题,如果饭店对客流没有正确预估,就会导致一些问题发生,例如:服务员人数招聘多了,但是客流很少,那么服务员就可能很闲,没事干,饭店的成本也高了;如果客流很大,而服务员人数少了,可能就接待不过来顾客,导致顾客吃饭体验差。因此,饭店要根据客户的流量及并发量来调整接待的服务人员数量,然后根据检测顾客量变化及时调整到最佳配置。
- Nginx服务就相当于饭店,网站用户就相当于顾客,Nginx的进程就相当于服务员,下面就来优化Nginx进程的个数。
2.1.1 优化Nginx进程对应的配置
#优化Nginx进程对应Nginx服务的配置参数如下:
worker_processes 1; #指定了Nginx要开启的进程数,结尾数字就是进程个数
上述参数调整的是Nginx服务的worker进程数,Nginx有Master进程和worker进程之分,Master为管理进程,真正接待“顾客”的是worker进程。
2.1.2 优化Nginx进程个数的策略
- 前面已经讲解过,worker_processes参数大小的设置最好和网站的用户数量相关联,可如果是新配置,不知道网站的用户数量该怎么办呢?
- 搭建服务器时,worker进程数最开始的设置可以等于CPU的核数,且worker进程数要多一些,这样起始提供服务时就不会出现因为访问量快速增加而临时启动新进程提供服务的问题,缩短了系统的瞬时开销和提供服务的时间,提升了服务用户的速度。高流量高并发场合也可以考虑将进程数提高至CPU核数*2,具体情况要根据实际的业务来选择,因为这个参数除了要和CPU核数匹配外,也和硬盘存储的数据及系统的负载有关,设置为CPU的核数是一个好的起始配置,这也是官方的建议。
2.1.3 查看Web服务器CPU硬件资源信息
下面介绍查看Linux服务器CPU总核数的方法:
(1)通过/proc/cpuinfo可查看CPU个数及总核数。查看CPU总核数的示例如下:
[root@LNMP ~]# grep processor /proc/cpuinfo
processor : 0
processor : 1
processor : 2
processor : 3
[root@LNMP ~]# grep processor /proc/cpuinfo | wc -l
4 #表示为1颗CPU四核
[root@LNMP ~]# grep -c processor /proc/cpuinfo
4 #表示为1颗CPU四核
#查看CPU总颗数示例如下:
[root@LNMP ~]# grep "physical id" /proc/cpuinfo
physical id : 0 #物理ID一致,同一颗CPU
physical id : 0 #物理ID一致,同一颗CPU
physical id : 0 #物理ID一致,同一颗CPU
physical id : 0 #物理ID一致,同一颗CPU
[root@LNMP ~]# grep "physical id" /proc/cpuinfo | sort | uniq | wc -l
1 #去重复,表示1颗CPU
(2)通过执行top命令,然后按数字1,即可显示所有的CPU核数,如下:
2.1.4 实践修改Nginx配置
假设服务器的CPU颗数为1颗,核数为4核,则初始的配置可通过查看默认的nginx.conf里的worker_processes数来了解,命令如下:
[root@LNMP ~]# grep worker_processes /usr/local/nginx/conf/nginx.conf
worker_processes 1;
[root@LNMP ~]# sed -i 's#worker_processes 1#worker_processes 4#' /usr/local/nginx/conf/nginx.conf
[root@LNMP ~]# grep worker_processes /usr/local/nginx/conf/nginx.conf
worker_processes 4; #提示可以通过vi修改
#优雅重启Nginx,使修改生效,如下:
[root@LNMP ~]# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@LNMP ~]# /usr/local/nginx/sbin/nginx -s reload
#现在检查修改后的worker进程数量,如下:
[root@LNMP ~]# ps -ef | grep "nginx" | grep -v grep
root 1110 1 0 11:12 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx 1429 1110 0 11:33 ? 00:00:00 nginx: worker process
nginx 1430 1110 0 11:33 ? 00:00:00 nginx: worker process
nginx 1431 1110 0 11:33 ? 00:00:00 nginx: worker process
nginx 1432 1110 0 11:33 ? 00:00:00 nginx: worker process
从“worker_processes 4”可知,worker进程数为4个。Nginx Master主进程不包含在这个参数内,Nginx Master的主进程为管理进程,负责调度和管理worker进程。
有关worker_processes参数的官方说明如下:
syntax: worker_processes number; #此行为参数语法,number为数量
default: worker_processes 1; #此行意思是不匹配该参数,软件默认情况数量为1
context: main; #此行为worker_processes参数可以放置的位置
worker_processes为定义worker进程数的数量,建议设置为CPU的核数或CPU核数*2,具体情况要根据实际的业务来选择,因为这个参数,除了要和CPU核数匹配外,和硬盘存储的数据以系统的负载也有关,设置为CPU的个数或核数是一个好的起始配置。From:http://nginx.org/en/docs/ngx_core_module.html
2.2 优化绑定不同的Nginx进程到不同的CPU上
- 默认情况下,Nginx的多个进程有可能跑在某一个CPU或CPU的某一核上,导致Nginx进程使用硬件的资源不均,本节的优化是尽可能地分配不同的Nginx进程给不同的CPU处理,达到充分有效利用硬件的多CPU多核资源的目的。
- 在优化不同的Nginx进程对应不同的CPU配置时,四核CPU服务器的参数配置参考如下:
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
#worker_cpu_affinity就是配置Nginx进程与CPU亲和力的参数,即把不同的进程分给不同的CPU处理。这里0001 0010 0100 1000是掩码,分别代表第1,2,3,4核CPU,由于worker_processes进程数为4,因此,上述配置会把每个进程分配一核CPU处理,默认情况下进程不会绑定任何CPU,参数位置为main段。
四核和八核CPU服务器的参数配置参考如下:
#八核掩码
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
#四核掩码
worker_cpu_affinity 0001 0010 0100 1000;
worker_cpu_affinity 的作用是绑定不同的worker进程数到一组CPU上。通过设置bitmask控制进程允许使用的CPU,默认worker进程不会绑定到任何CPU(自动平均分配。)
2.2.1 实验环境准备
主机名 | IP地址 | 备注 |
---|---|---|
Nginx | 192.168.0.220 | nginxWeb |
测试机1 | 192.168.0.240 | Webbench压力测试 |
测试机2 | 192.168.0.245 | Webbench压力测试 |
#安装webbench
tar xf webbench-1.5.tar.gz
cd webbench-1.5
mkdir /usr/local/man
make install clean
which webbench.
虚拟机开启4核心
2.2.2 第一步:不绑定worker进程进行压力测试
#配置文件如下:(未绑定worker进程)
[root@LNMP nginx]# cat conf/nginx.conf
worker_processes 4;
#worker_cpu_affinity 0001 0010 0100 1000;
events {
worker_connections 10240;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name bbs.yunjisuan.com;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
#在NginxWeb上执行如下命令:
[root@LNMP nginx]# top -u nginx
Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 1004412k total, 911632k used, 92780k free, 6952k buffers
Swap: 2031608k total, 0k used, 2031608k free, 749976k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1454 nginx 20 0 49240 5640 468 S 0.0 0.6 0:00.00 nginx
1455 nginx 20 0 49240 5672 500 S 0.0 0.6 0:00.00 nginx
1456 nginx 20 0 49240 5672 500 S 0.0 0.6 0:00.00 nginx
1457 nginx 20 0 49240 5672 500 S 0.0 0.6 0:00.00 nginx
#在以上界面时按键盘的数值1键,出现如下界面:
top - 14:44:46 up 36 min, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie
Cpu0 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu1 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu2 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu3 : 0.0%us,