NGINX的工作原理
NGINX建立在非阻塞的事件驱动架构上。它有一个主进程、若干个工作进程,以及两个缓存管理进程。要查看这一点,您只需要使用以下命令运行ps command
来查看进程树:
ps -ef --forest | grep nginx
主进程以root权限运行,并执行需要提升权限的操作,比如:
- 读取配置文件;
- 打开端口;
- 创建新进程。
工作进程承担所有工作:
- 处理网络连接;
- 读写数据到磁盘;
- 与后端服务器通信。
通常情况下,工作进程的数量等于CPU核心数。这样可以最大程度地高效利用系统资源。
工作进程监听两种类型的套接字:
- 监听器(listener) - 用于新的客户端事件(连接请求);
- 连接器(connection) - 用于需要处理的事件。
工作进程使用两种类型的API来等待事件:epoll或kqueue。当在监听器套接字上收到新的客户端事件时,工作进程会创建一个新的套接字连接。
在连接套接字上收到事件后,工作进程执行此请求。处理完事件后,它不会被阻塞,而是进入待机模式,从而最小化操作系统的上下文切换次数。
有了上述架构,更新NGINX配置就像向主进程发送SIGHUP信号一样简单。在NGINX适当配置的情况下,每个工作进程能够同时处理成千上万个连接。
在此之后,工作进程将停止接受新的连接,当前连接在处理完事件后将被关闭(不等待保持活动状态)。一旦所有连接都关闭,工作进程也会结束。这允许您在不丢失连接、闲置资源或中断客户端服务的情况下更新服务器配置。
小贴士与技巧
1. 在应用配置之前检查配置的正确性
在更改或创建配置后,您需要检查语法和写入配置文件的正确性。Nginx允许您使用以下命令检查配置的正确性:
nginx -t
2. 在不重启服务的情况下重新加载Nginx
在不重启服务的情况下应用设置是一个很好的做法。这不会终止当前的连接,并消除了加载服务时的等待时间,即没有停机时间:
nginx -s reload
或者
/etc/init.d/nginx reload
或者
service nginx reload
3. 禁用Nginx的server_tokens
默认情况下,Nginx中的server_tokens指令会显示Nginx的版本号。它在所有自动生成的错误页面和所有HTTP响应的服务器头中直接可见。
这可能导致信息泄露 - 未经授权的用户可以了解您的Nginx版本。您应该通过在Nginx配置文件中设置以下内容来禁用server_tokens
指令:
server_tokens off;
4. 禁用旧版SSL/TLS协议
ssl_protocols TLSv1.2 TLSv1.3;
5. 禁用任何不需要的HTTP方法
禁用不会被使用且不需要在Web服务器上实现的任何HTTP方法。
如果在Nginx虚拟主机配置文件的location块中添加以下条件,服务器将仅允许GET,HEAD
和POST
方法,并过滤掉诸如DELETE
和TRACE
之类的方法:
location / {
limit_except GET HEAD POST { deny all; }
}
另一种方法是在server部分(或server块)中添加以下条件。这可能更通用,但在location上下文中使用if语句时必须小心:
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
6. 设置和配置Nginx的访问和错误日志
默认情况下,Nginx启用了访问和错误日志,并分别位于logs/error.log
和logs/access.log
中。
如果您想更改位置,可以在Nginx配置文件中使用error_log指令。您还可以使用此指令根据其严重性级别来指定要记录的日志。
例如,关键严重性将导致Nginx记录关键问题以及所有比关键问题严重性级别更高的问题。要将严重性级别设置为关键,请将error_log指令设置如下:
error_log logs/error.log crit;
您可以在官方Nginx文档中找到完整的error_log严重性级别列表。
您还可以更改Nginx配置文件中的access_log指令,以指定访问日志的非标准位置。
最后,您可以使用log_format指令自定义记录消息的格式,如Nginx文档中所述。
7. Nginx的工作进程连接数
我们配置的一个关键设置是Nginx配置文件/etc/nginx/nginx.conf
中的工作进程数量和工作连接数量。
我们将逐步调整工作流程和工作连接的值,以处理DDoS攻击:
events {
worker_connections 20000;
}
此设置允许每个工作进程处理高达20000个连接。
8. 请求速率限制
在防止DDoS攻击的众多有价值的策略中,最简单且最有效的之一是限制传入流量的速率。
例如,您可以指定NGINX接受从特定客户端IP地址到您的服务的平均速率的传入请求速率。
我们在Nginx配置文件中设置limit_req_zone指令来限制请求数:
limit_req_zone $binary_remote_addr zone=req_per_ip:1m rate=30r/m;
server {
location /login {
limit_req zone=req_per_ip;
}
}
此示例创建一个名为"req_per_ip"的存储区,可容纳多达16,000(1m)个唯一IP地址,30r/m意味着每分钟只允许30个请求。
然后,我们使用limit_req指令来限制到特定位置或文件的连接速度,此处为登录。
9. 限制连接数
您可以限制单个客户端IP地址可以打开的连接数,在此设置中,我们将limit_conn_zone和limit_conn指令设置为限制每个IP地址的连接数:
limit_conn_zone $binary_remote_addr zone=conn_per_ip:1m;
server {
location / {
limit_conn conn_per_ip 10;
}
}
此示例创建了一个名为"conn_per_ip"的存储区,用于存储指定键的请求,此处为客户端的IP地址$binary_remote_addr。然后,limit_conn指令设置每个客户端IP地址的连接数为十个。
10. 超时选项
连接缓慢可能表示试图将连接保持打开的尝试。结果,服务器无法接受新的连接:
server {
client_body_timeout 5s;
client_header_timeout 5s;
}
在此示例中,client_body_timeout指令指定Nginx在客户端正文输入之间等待的时间,而client_header_timeout指令指定Nginx在客户端头输入之间等待的时间。两者都设置为5秒。
11. 限制请求的大小
类似地,大缓冲区值或大的HTTP请求大小会使DDoS攻击变得更容易。因此,我们在Nginx配置文件中限制以下缓冲区值以减小DDoS攻击:
client_body_buffer_size 200K;
client_header_buffer_size 2k;
client_max_body_size 200k;
large_client_header_buffers 3 1k;
其中,
client_body_buffer_size 1k
—(默认为8k或16k)该指令指定客户端请求正文缓冲区的大小;client_header_buffer_size 1k
— 该指令设置来自客户端的请求头的头部缓冲区大小。1Kb的缓冲区大小对于绝大多数请求来说足够了。如果客户端发送自定义标头或巨大的cookie(如wap客户端),可以增加此值;client_max_body_size 1k
— 该指令设置客户端请求正文的最大允许大小,由请求头中的Content-Length行指定。如果大小大于指定值,客户端将收到“请求实体过大”错误(413)。当使用POST方法上传文件时,增加此值;large_client_header_buffers 2 1k
— 该指令为从客户端请求中读取大型头部分配了最大数量和大小的缓冲区。默认情况下,一个缓冲区的大小等于页面大小,具体取决于平台,可以是4K或8K。如果连接进入保持活动状态并在工作请求结束时,这些缓冲区将被释放。例如,2x1k将接受一个2KB的数据URI。它还有助于抵御恶意机器人和DoS攻击。
12. IP黑名单
如果您可以识别用于攻击的客户端IP地址,您可以使用deny指令将其列入黑名单,以便NGINX和NGINX Plus不会接受其连接或请求:
location / {
deny 111.111.111.4;
deny 111.111.111.0/24;
}
在此示例中,第一个Deny阻止特定IP地址,第二个Deny阻止整个IP地址范围。