Nginx【有与无】【NP-AG5-1】安全控制

说明

  • NP: NGINX Plus
  • AG: Admin Guide
  • 会话: session
  • 上游:  upstream
  • 流量:traffic
  • 后端:backend
  • 区域:zone
  • 切片:slices
  • 位置:location
  • 根:root
  • 终端:termination
  • 端点:endpoint

目录

1.NGINX SSL终端

1.1.设置HTTPS服务器

1.2.OCSP验证客户证书

1.3.HTTPS服务器优化

1.4.SSL证书链

1.5.单个HTTP / HTTPS服务器

1.6.基于名称的HTTPS服务器

1.7.具有多个名称的SSL证书

1.8.服务器名称指示

1.9.兼容性说明

2.TCP上游服务器的SSL终端

2.1.什么是SSL终端?

2.2.先决条件

2.3.获取SSL证书

2.4.配置NGINX Plus

2.5.启用SSL

2.6.添加SSL证书

2.7.加快安全的TCP连接

2.8.优化SSL会话缓存

2.9.会话票证(Session Tickets)

2.10.完整的例子

3.使用HTTP基本身份验证限制访问

3.1.介绍

3.2.先决条件

3.3.创建密码文件

3.4.配置NGINX和NGINX Plus以进行HTTP基本身份验证

3.5.将基本身份验证与IP地址访问限制相结合

3.6.完整的例子

4.基于子请求结果的身份验证

4.1.介绍

4.2.先决条件

4.3.配置NGINX和NGINX Plus

4.4.完整的例子

5.设置JWT身份验证

5.1.介绍

5.2.先决条件

5.3.配置NGINX以验证API

5.4.NGINX Plus如何验证JWT

5.5.创建一个JSON Web密钥文件

5.6.向客户发行JWT

5.7.配置NGINX Plus以接受来自查询字符串的JWT

5.8.从Subrequest获取JWK

5.9.也可以看看

6.限制对代理HTTP资源的访问

6.1.介绍

6.2.限制连接数

6.3.限制请求率

6.4.测试请求速率限制

6.5.处理过多的请求

6.6.延迟过多的请求

6.7.同步许多共享内存区域的内容

6.8.限制带宽

6.9.动态带宽控制

6.10.也可以看看

7.限制对代理TCP资源的访问

7.1.通过IP地址限制访问

7.2.限制TCP连接数

7.3.限制带宽

8.通过地理位置限制访问

8.1.介绍

8.2.先决条件

8.3.获取数据库

8.4.了解数据库结构

8.5.在NGINX Plus中配置GeoIP2

8.6.方案:选择最近的服务器

8.7.例子

8.8.更多信息

9.保护到上游服务器的HTTP流量

9.1.先决条件

9.2.获取SSL服务器证书

9.3.获取SSL客户端证书

9.4.配置NGINX

9.5.配置上游服务器

9.6.完整的例子

10.保护到上游服务器的TCP流量

10.1.先决条件

10.2.获取SSL服务器证书

10.3.获取SSL客户端证书

10.4.配置NGINX

10.5.完整的例子

11.动态拒绝IP地址

11.1.总览

11.2.先决条件

11.3.配置

11.4.管理键值数据库

11.5.完整的例子

11.6.也可以看看


1.NGINX SSL终端

本节介绍如何在NGINX和NGINX Plus上配置HTTPS服务器。

1.1.设置HTTPS服务器

要设置HTTPS服务器,请在你的nginx.conf文件中在server块中的listen指令中包含ssl参数,然后指定服务器证书和私钥文件的位置:

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    #...
}

服务器证书是公共实体。 它被发送到连接到NGINX或NGINX Plus服务器的每个客户端。 私钥是一个安全实体,应存储在访问受限的文件中。 但是,NGINX主进程必须能够读取该文件。 另外,私钥可以与证书存储在同一文件中:

ssl_certificate     www.example.com.cert;
ssl_certificate_key www.example.com.cert;

在这种情况下,限制对文件的访问非常重要。 请注意,尽管在这种情况下证书和密钥存储在一个文件中,但是只有证书被发送到客户端。

ssl_protocolsssl_ciphers指令可用于要求客户端在建立连接时仅使用SSL/TLS的强壮的版本和密码(ciphers)。

从1.9.1版开始,NGINX使用以下默认值:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;

有时在较旧的密码设计中会发现漏洞,我们建议在现代NGINX配置中将其禁用(遗憾的是,由于现有NGINX部署的向后兼容性,默认配置无法轻易更改)。请注意,CBC模式密码可能容易受到多种攻击(尤其是BEAST攻击,如CVE-2011-3389中所述),并且由于POODLE攻击,我们建议不要使用SSLv3,除非你需要支持旧版客户端 。

1.2.OCSP验证客户证书

NGINX可以配置为使用在线证书状态协议(OCSP)来检查X.509客户端证书的有效性。 客户端证书状态的OCSP请求被发送到OCSP响应器,该响应器检查证书的有效性并返回带有证书状态的响应:

  • Good - 证书未被吊销
  • Revoked - 证书被吊销
  • Unknown - 没有有关客户端证书的信息

要启用SSL客户端证书的OCSP验证,请指定ssl_ocsp指令以及ssl_verify_client指令,以启用证书验证:

server {
    listen 443 ssl;

    ssl_certificate     /etc/ssl/foo.example.com.crt;
    ssl_certificate_key /etc/ssl/foo.example.com.key;

    ssl_verify_client       on;
    ssl_trusted_certificate /etc/ssl/cachain.pem;
    ssl_ocsp                on; # Enable OCSP validation

    #...
}

除非使用ssl_ocsp_responder 指令定义了不同的URI,否则NGINX会将OCSP请求发送到客户端证书中嵌入的OCSP URI。 仅支持http:// OCSP 响应者:

#...
ssl_ocsp_responder http://ocsp.example.com/;
#...

要在所有工作进程共享的单个内存区域中缓存OCSP响应,请指定ssl_ocsp_cache指令以定义该区域的名称和大小。 除非OCSP响应中的nextUpdatevalue指定不同的值,否则响应将被缓存1小时。

#...
ssl_ocsp_cache shared:one:10m;
#...

$ssl_client_verify变量中提供了客户端证书验证的结果,包括OCSP失败的原因。

1.3.HTTPS服务器优化

SSL操作会消耗额外的CPU资源。 最耗CPU的操作是SSL握手。 有两种方法可以最大程度地减少每个客户端执行这些操作的次数:

  • 启用保持连接以通过一个连接发送多个请求
  • 重用SSL会话参数以避免并行和后续连接的SSL握手

会话存储在工作进程之间共享的SSL会话缓存中,并由ssl_session_cache 指令进行配置。 一兆字节的缓存包含大约4000个会话。 默认的缓存超时为5分钟。 可以使用ssl_session_timeout 指令来增加此超时时间。 以下是针对具有10 MB共享会话缓存的多核系统进行了优化的示例配置:

worker_processes auto;

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    server {
        listen              443 ssl;
        server_name         www.example.com;
        keepalive_timeout   70;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        #...
    }
}

1.4.SSL证书链

一些浏览器可能会抱怨由知名证书颁发机构签署的证书,而其他浏览器可能会毫无问题地接受该证书。 发生这种情况是因为颁发机构已使用中间证书签署了服务器证书,该中间证书在特定浏览器中分布的知名可信证书颁发机构的基础中不存在。 在这种情况下,授权机构提供应链接到已签名服务器证书的捆绑证书链。 服务器证书必须出现在组合文件中的链接证书之前:

$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt

生成的文件应在ssl_certificate 指令中使用:

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.chained.crt;
    ssl_certificate_key www.example.com.key;
    #...
}

如果服务器证书和分发包的连接顺序错误,NGINX将无法启动,并显示以下错误消息:

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
   (SSL: error:0B080074:x509 certificate routines:
    X509_check_private_key:key values mismatch)

发生错误是因为NGINX尝试将私有密钥与捆绑包的第一个证书一起使用,而不是服务器证书。

浏览器通常存储接收到的中间证书,并由受信任的权威机构签名。 因此,经常使用的浏览器可能已经具有必需的中间证书,并且可能不会抱怨没有链式捆绑包发送的证书。 为了确保服务器发送完整的证书链,可以使用openssl命令行实用程序:

$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain
 0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
     /1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
     /OU=MIS Department/CN=www.GoDaddy.com
     /serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
   i:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc.
     /OU=ValiCert Class 2 Policy Validation Authority
     /CN=http://www.valicert.com//emailAddress=info@valicert.com
...

在此示例中,www.GoDaddy.com 服务器证书#0的主题(“s”)由本身是证书#1的主题的颁发者(“i”)签名。 证书#1由本身是证书#2主题的发行者签名。 但是,该证书由著名的发行商ValiCert签署,其证书存储在浏览器中。

如果尚未添加证书捆绑包,则仅显示服务器证书(#0)。

1.5.单个HTTP / HTTPS服务器

通过将一个带有ssl参数的监听指令和一个不包含在同一虚拟服务器中的listen指令放置在一起,可以配置一台同时处理HTTP和HTTPS请求的服务器:

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    #...
}

在NGINX 0.7.13及更早版本中,无法为各监听套接字选择性地启用SSL,如上所述。 仅可以使用ssl指令为整个服务器启用SSL,从而无法设置单个HTTP/HTTPS服务器。 将ssl 参数添加到listen指令以解决此问题。 因此,在版本0.7.14和更高版本中不建议使用ssl指令。

1.6.基于名称的HTTPS服务器

当两个或多个HTTPS服务器配置为侦听单个IP地址时,会出现一个常见问题:

server {
    listen          443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    #...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    #...
}

使用此配置,浏览器将接收默认服务器的证书。 在这种情况下,无论请求的服务器名称是什么,它都是 www.example.com。 这是由SSL协议本身的行为引起的。 在浏览器发送HTTP请求之前,已建立SSL连接,NGINX不知道所请求服务器的名称。 因此,它可能仅提供默认服务器的证书。

解决此问题的最佳方法是为每个HTTPS服务器分配一个单独的IP地址:

server {
    listen          192.168.1.1:443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    #...
}

server {
    listen          192.168.1.2:443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    #...
}

请注意,还有一些特定的HTTPS上游代理设置(proxy_ssl_ciphersproxy_ssl_protocolsproxy_ssl_session_reuse),可用于在NGINX和上游服务器之间微调SSL。 你可以在HTTP代理模块文档中阅读有关这些内容的更多信息

1.7.具有多个名称的SSL证书

还有其他几种方法可以在多个HTTPS服务器之间共享一个IP地址。 但是,它们都有缺点。 一种方法是在SubjectAltName证书字段中使用具有多个名称的证书,例如,www.example.comwww.example.org。 但是,SubjectAltName字段的长度是有限的。

另一种方法是使用带有通配符名称的证书,例如*.example.org。 通配符证书可保护指定域的所有子域,但只能在一个级别上。 该证书与www.example.org匹配,但与example.orgwww.sub.example.org不匹配。 这两种方法也可以结合使用。 证书在SubjectAltName 字段中可能包含确切的名称和通配符名称。 例如,example.org*.example.org

最好将带有多个名称的证书文件及其私钥文件放置在配置的http级别,以便它们继承所有服务器上的单个内存复本:

ssl_certificate     common.crt;
ssl_certificate_key common.key;

server {
    listen          443 ssl;
    server_name     www.example.com;
    #...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    #...
}

1.8.服务器名称指示

TLS服务器名称指示(SNI)扩展(RFC 6066)是在单个IP地址上运行多个HTTPS服务器的更通用的解决方案,它允许浏览器在SSL握手期间传递请求的服务器名称。 使用此解决方案,服务器将知道它应使用哪个证书进行连接。 但是,SNI的浏览器支持有限。 当前从以下浏览器版本开始受支持:

  • Opera 8.0
  • MSIE 7.0 (但仅在Windows Vista或更高版本上)
  • Firefox 2.0和其他使用Mozilla Platform rv:1.8.1的浏览器
  • Safari 3.2.1 (Windows版本在Vista或更高版本上支持SNI)
  • Chrome (Windows版本也支持Vista或更高版本上的SNI)

只能在SNI中传递域名。 但是,如果请求中包含文字IP地址,则某些浏览器会将服务器的IP地址作为其名称。 最好不要依赖于此。

为了在NGINX中使用SNI,必须在构建NGINX二进制文件的OpenSSL库以及在运行时动态链接的库中都支持SNI。 如果OpenSSL使用配置option --enable-tlsext构建,则从0.9.8f版本开始支持SNI。 从OpenSSL 0.9.8j版本开始,默认情况下启用此选项。 如果NGINX是使用SNI支持构建的,则当使用-V开关运行时,NGINX将显示以下内容:

$ nginx -V
...
TLS SNI support enabled
...

但是,如果启用了SNI的NGINX动态链接到不支持SNI的OpenSSL库,则NGINX将显示警告:

NGINX was built with SNI support, however, now it is linked
dynamically to an OpenSSL library which has no tlsext support,
therefore SNI is not available

1.9.兼容性说明

  • 自0.8.21和0.7.62版本以来,-V开关已显示SNI支持状态。
  • 自0.7.14版本以来,已支持listen 指令的ssl参数。 在版本0.8.21之前,只能与默认参数一起指定。
  • 从0.5.23版本开始支持SNI。
  • 从0.5.6版本开始,支持共享SSL会话缓存。
  • 版本1.9.1和更高版本:默认的SSL协议是TLSv1TLSv1.1TLSv1.2(如果OpenSSL库支持)。
  • 从0.7.65和0.8.19及更高版本开始,默认的SSL协议为SSLv3TLSv1TLSv1.1TLSv1.2(如果OpenSSL库支持)。
  • 在0.7.64和0.8.18及更低版本中,默认的SSL协议为SSLv2SSLv3, TLSv1.。
  • 在1.0.5及更高版本中,默认的SSL密码(ciphers)为HIGH:!aNULL:!MD5
  • 在0.7.65和0.8.20及更高版本中,默认的SSL密码(ciphers)为HIGH:!ADH:!MD5
  • 从0.8.19版本开始,默认的SSL密码(ciphers)为ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM
  • 从版本0.7.64、0.8.18和更早版本开始,默认SSL密码(ciphers)为ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP

 

2.TCP上游服务器的SSL终端

介绍了如何为NGINX Plus和接受TCP连接的负载均衡的服务器组设置SSL终端。

2.1.什么是SSL终端?

SSL终端意味着NGINX Plus充当与客户端连接的服务器端SSL端点:它执行对请求的解密和对响应的加密,而后端服务器则必须这样做。 该操作称为终端,因为NGINX Plus关闭了客户端连接,并通过新创建的未加密连接将客户端数据转发到上游组中的服务器。 在R6和更高版本中,NGINX Plus对TCP连接以及HTTP连接执行SSL终端。

2.2.先决条件

  • NGINX Plus R6 NGINX Plus或更高版本
  • 具有多个TCP服务器的负载均衡的上游组 
  • SSL证书和私钥(获取或自行生成)

2.3.获取SSL证书

首先,你将需要获取服务器证书和私钥并将其放在服务器上。 可以从可信证书颁发机构(CA)获取证书,也可以使用SSL库(例如OpenSSL)生成证书。

2.4.配置NGINX Plus

要配置SSL终端,请在NGINX Plus配置中添加以下指令:

2.5.启用SSL

要启用SSL,请为将连接传递到上游服务器组的TCP服务器指定listen 指令的ssl参数:

stream {

    server {
        listen     12345 ssl;
        proxy_pass backend;
        #...
    }
}

2.6.添加SSL证书

要添加SSL证书,请使用ssl_certificate 指令指定证书的路径(必须采用PEM格式),并在ssl_certificate_key 指令中指定私钥的路径:

server {
    #...
    ssl_certificate        /etc/ssl/certs/server.crt;
    ssl_certificate_key    /etc/ssl/certs/server.key;
}

此外,ssl_protocols 和ssl_ciphers 指令可用于限制连接,并且仅包括SSL/TLS的强壮的版本和密码:

server {
    #...
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers    HIGH:!aNULL:!MD5;
}

ssl_ciphers指令告诉NGINX通知SSL库它喜欢哪种密码。

2.7.加快安全的TCP连接

实施SSL/TLS可能会严重影响服务器性能,因为SSL握手操作(客户端和服务器交换一系列消息以验证连接是否可信)会占用大量CPU。 SSL握手的默认超时为60秒,可以使用ssl_handshake_timeout 指令对其进行重新定义。 我们不建议将此值设置得太低或太高,因为这可能导致握手失败或等待握手完成的时间较长:

server {
    #...
    ssl_handshake_timeout 10s;
}

2.8.优化SSL会话缓存

创建适用于每个SSL/TLS连接的会话参数的缓存可以减少握手次数,从而可以显着提高性能。 缓存是通过ssl_session_cache 指令设置的:

ssl_session_cache;

默认情况下,NGINX Plus使用会话缓存的内置(built-in)类型,这意味着你的SSL库中内置了缓存。 这不是最佳选择,因为这样的缓存只能由一个工作进程使用,并且可能导致内存碎片。 将ssl_session_cache 指令设置为shared,以在所有工作进程之间共享高速缓存,这可以加快以后的连接速度,因为连接设置信息已为人所知:

ssl_session_cache shared:SSL:1m;

作为参考,一个1 MB的共享缓存可以容纳大约4,000个会话。

默认情况下,NGINX Plus将缓存的会话参数保留五分钟。 将ssl_session_timeout 的值增加到几个小时可以提高性能,因为重新使用缓存的会话参数可以减少耗时的握手次数。 当你增加超时时,缓存需要更大,以容纳产生的更多缓存参数。 对于以下示例中的4小时超时,适合使用20 MB的缓存:

ssl_session_timeout 4h;

如果超时长度增加,则需要更大的缓存来存储会话,例如20 MB:

server {
    #...
    ssl_session_cache   shared:SSL:20m;
    ssl_session_timeout 4h;
}

这些行创建了一个20 MB的内存中高速缓存以存储会话信息,并指示NGINX Plus在添加会话参数后的4小时内重新使用高速缓存中的会话参数。

2.9.会话票证(Session Tickets)

会话票证是会话缓存的替代方法。 会话信息存储在客户端,无需服务器端缓存即可存储会话信息。 当客户端恢复与后端服务器的交互时,它将显示会话票证,并且不需要重新协商。 将ssl_session_tickets 指令设置为on

server {
    #...
    ssl_session_tickets on;
}

将会话票证用于上游组时,必须使用相同的会话密钥初始化每个上游服务器。 最佳做法是经常更改会话密钥,我们建议你实施一种机制,以在所有上游服务器之间轮换共享密钥:

server {
    #...
    ssl_session_tickets on;
    ssl_session_ticket_key /etc/ssl/session_ticket_keys/current.key;
    ssl_session_ticket_key /etc/ssl/session_ticket_keys/previous.key;
}

2.10.完整的例子

stream {
    upstream stream_backend {
         server backend1.example.com:12345;
         server backend2.example.com:12345;
         server backend3.example.com:12345;
    }

    server {
        listen                12345 ssl;
        proxy_pass            stream_backend;

        ssl_certificate       /etc/ssl/certs/server.crt;
        ssl_certificate_key   /etc/ssl/certs/server.key;
        ssl_protocols         SSLv3 TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers           HIGH:!aNULL:!MD5;
        ssl_session_cache     shared:SSL:20m;
        ssl_session_timeout   4h;
        ssl_handshake_timeout 30s;
        #...
     }
}

在此示例中,server 块中的指令指示NGINX Plus终端并解密来自客户端的安全TCP流量,并将未加密的流量传递给上游组stream_backend ,该组由三个服务器组成。

listen 指令的ssl参数指示NGINX Plus接受SSL连接。 当客户端请求安全的TCP连接时,NGINX Plus将开始握手过程,该过程将使用ssl_certificate 指令指定的PEM格式证书,ssl_certificate_key 指令指定的证书私钥以及ssl_protocols 和ssl_ciphers 指令列出的协议和密码。

一旦建立了安全的TCP连接,NGINX Plus就会根据ssl_session_cache 指令缓存会话参数。 在此示例中,会话缓存在所有工作进程之间共享(shared 参数),大小为20 MB(参数20m),并将每个SSL会话保留4小时以供重用(ssl_session_timeout 指令)。

要了解有关NGINX Plus的更多信息,请参阅我们的商业订阅的描述

3.使用HTTP基本身份验证限制访问

3.1.介绍

你可以通过实现用户名/密码身份验证来限制对网站或网站某些部分的访问。 用户名和密码取自由密码文件创建工具(例如apache2-utils)创建并填充的文件。

HTTP基本身份验证也可以与其他访问限制方法结合使用,例如通过IP地址地理位置限制访问。

3.2.先决条件

  • NGINX Plus或NGINX开源代码
  • 密码文件创建实用程序,例如 apache2-utils(Debian,Ubuntu)或httpd-tools(RHEL/CentOS/Oracle Linux)。

3.3.创建密码文件

要创建用户名-密码对,请使用密码文件创建实用程序,例如apache2-utilshttpd-tools

1.确认已安装apache2-utils(Debian,Ubuntu)或httpd-tools(RHEL/CentOS/Oracle Linux)。

2.创建密码文件和第一个用户。 运行带有-c标志的htpasswd 实用程序(以创建一个新文件),文件路径名作为第一个参数,用户名作为第二个参数:

$ sudo htpasswd -c /etc/apache2/.htpasswd user1

按Enter,然后在提示时键入user1的密码。

3.创建其他用户密码对。 省略-c标志,因为该文件已经存在:

$ sudo htpasswd /etc/apache2/.htpasswd user2

4.你可以确认该文件包含成对的用户名和加密的密码:

$ cat /etc/apache2/.htpasswd
user1:$apr1$/woC1jnP$KAh0SsVn5qeSMjTtn0E9Q0
user2:$apr1$QdR8fNLT$vbCEEzDj7LyqCMyNpSoBh/
user3:$apr1$Mr5A0e.U$0j39Hp5FfxRkneklXaMrr/

3.4.配置NGINX和NGINX Plus以进行HTTP基本身份验证

1.在要保护的位置内,指定auth_basic指令,并为受密码保护的区域命名。 询问凭据时,该区域的名称将显示在用户名/密码对话框窗口中:

location /api {
    auth_basic “Administrator’s Area”;
    #...
}

2.指定auth_basic_user_file指令以及包含user/password对的.htpasswd文件的路径:

location /api {
    auth_basic           “Administrator’s Area”;
    auth_basic_user_file /etc/apache2/.htpasswd; 
}

另外,你可以使用基本身份验证来限制对整个网站的访问,但仍将某些网站区域设为公开。 在这种情况下,请指定auth_basic 指令的off 参数,该参数取消从较高配置级别的继承:

server {
    ...
    auth_basic           "Administrator’s Area";
    auth_basic_user_file conf/htpasswd;

    location /public/ {
        auth_basic off;
    }
}

3.5.将基本身份验证与IP地址访问限制相结合

HTTP基本身份验证可以有效地结合IP地址的访问限制。 你可以至少实现两种方案:

  • 用户必须经过身份验证并具有有效的IP地址
  • 用户必须经过身份验证或具有有效的IP地址

1.使用allowdeny 指令允许或拒绝来自特定IP地址的访问:

location /api {
    #...
    deny  192.168.1.2;
    allow 192.168.1.1/24;
    allow 127.0.0.1;
    deny  all;
}

仅对92.168.1.1/24网络(不包括192.168.1.2地址)授予访问权限。 请注意,allow 和deny 指令将按其定义顺序应用。

2.将IP和HTTP身份验证的限制与satisfy指令结合使用。 如果将指令设置为all,则如果客户端同时满足这两个条件,则将授予访问权限。 如果将指令设置为any,则如果客户端满足至少一个条件,则将授予访问权限:

location /api {
    #...
    satisfy all;    

    deny  192.168.1.2;
    allow 192.168.1.1/24;
    allow 127.0.0.1;
    deny  all;

    auth_basic           "Administrator’s Area";
    auth_basic_user_file conf/htpasswd;
}

3.6.完整的例子

该示例显示了如何通过简单身份验证以及IP地址访问限制来保护你的状态区域:

http {
    server {
        listen 192.168.1.23:8080;
        root   /usr/share/nginx/html;

        location /api {
            api;
            satisfy all;

            deny  192.168.1.2;
            allow 192.168.1.1/24;
            allow 127.0.0.1;
            deny  all;

            auth_basic           "Administrator’s Area";
            auth_basic_user_file /etc/apache2/.htpasswd; 
        }
    }
}

当你访问状态页面时,系统会提示你登录:

如果提供的名称和密码与密码文件不匹配,则会出现401 (Authorization Required)错误。

4.基于子请求结果的身份验证

4.1.介绍

NGINX和NGINX Plus可以使用外部服务器或服务验证对你网站的每个请求。 为了执行身份验证,NGINX向外部服务器发出HTTP子请求,在该服务器上验证了子请求。 如果子请求返回2xx响应代码,则允许访问,如果子请求返回401或403,则拒绝访问。 这种身份验证类型允许实现各种身份验证方案,例如多因素身份验证,或者允许实现LDAP或OAuth身份验证。

4.2.先决条件

  • NGINX Plus或NGINX开源代码
  • 外部认证服务器或服务

4.3.配置NGINX和NGINX Plus

1.确保with-http_auth_request_module配置选项编译了NGINX开源。 运行此命令并验证输出中是否包含--with-http_auth_request_module

$ nginx -V 2>&1 | grep -- 'http_auth_request_module'

由于NGINX Plus已经包含auth_request模块,因此可以跳过此步骤。

2.在需要请求身份验证的位置,指定auth_request 指令,在其中指定将授权子请求转发到的内部位置:

location /private/ {
    auth_request /auth;
    #...
}

在此,对于每个对/private的请求,将对内部/auth位置进行子请求。

3.指定一个内部位置,并在该位置内指定proxy_pass指令,以将身份验证子请求代理到身份验证服务器或服务:

location = /auth {
    internal;
    proxy_pass http://auth-server;
    #...
}

4.由于请求主体被丢弃以进行身份验证子请求,因此你需要将proxy_pass_request_body 指令设置为off,并将Content-Length标头设置为空字符串:

location = /auth {
    internal;
    proxy_pass              http://auth-server;
    proxy_pass_request_body off;
    proxy_set_header        Content-Length "";
    #...
}

5.使用带有proxy_set_header 指令的参数传递完整的原始请求URI:

location = /auth {
    internal;
    proxy_pass              http://auth-server;
    proxy_pass_request_body off;
    proxy_set_header        Content-Length "";
    proxy_set_header        X-Original-URI $request_uri;
}

6.作为选择,你可以使用auth_request_set 指令基于子请求的结果设置变量值:

location /private/ {
    auth_request        /auth;
    auth_request_set $auth_status $upstream_status;
}

4.4.完整的例子

本示例将前面的步骤总结为一个配置:

http {
    #...
    server {
    #...
        location /private/ {
            auth_request     /auth;
            auth_request_set $auth_status $upstream_status;
        }

        location = /auth {
            internal;
            proxy_pass              http://auth-server;
            proxy_pass_request_body off;
            proxy_set_header        Content-Length "";
            proxy_set_header        X-Original-URI $request_uri;
        }
    }
}

5.设置JWT身份验证

本文介绍了如何使用JWT身份验证来控制Web资源的身份验证。

5.1.介绍

使用NGINX Plus,可以使用JWT身份验证来控制对资源的访问。 JWT是OpenID Connect标准中用于用户信息的数据格式,该标准是OAuth 2.0协议之上的标准标识层。 API和微服务的部署者也因为其简单性和灵活性而转向JWT标准。 通过JWT身份验证,客户端将提供JSON Web令牌,并且将根据本地密钥文件或远程服务来验证令牌。

5.2.先决条件

5.3.配置NGINX以验证API

假设NGINX Plus充当了通往许多API服务器(upstream {}块)的网关(proxy_pass http://api_server):

upstream api_server {
    server 10.0.0.1;
    server 10.0.0.2;
}

server {
    listen 80;

    location /products/ {
        proxy_pass http://api_server;
        #...
    }
}

传递给API服务器的请求应经过身份验证。 要实现JWT进行身份验证,请指定auth_jwt指令,该指令启用JWT身份验证并定义身份验证区域(在示例中为“realm”,“API”):

server {
    listen 80;

    location /products/ {
        proxy_pass http://api_server;
        auth_jwt   "API";
        #...
    }
}

指定将针对其验证JWT签名的JWT密钥文件的路径。 这可以通过auth_jwt_key_file 指令来完成。 它指示将用于验证令牌签名的JSON Web密钥(功能类似于SSL / TLS加密中的公共密钥):

server {
    listen 80;

    location /products/ {
        proxy_pass        http://api_server;
        auth_jwt          "API";
        auth_jwt_key_file conf/key.jwk;
    }
}

5.4.NGINX Plus如何验证JWT

当满足以下条件时,JWT被认为是有效的:

  • 可以使用auth_jwt_key_file中找到的密钥来验证签名(如果存在,则与kid标头字段匹配)。
  • 当由nbf(“不早于”)和exp(“过期”)声明之一或两者定义时,将在有效期内显示JWT。

5.5.创建一个JSON Web密钥文件

为了使用密钥验证签名,应创建一个JSON Web密钥(key.jwk)。文件格式由JSON Web密钥规范定义

{"keys":
    [{
        "k":"ZmFudGFzdGljand0",
        "kty":"oct",
        "kid":"0001"
    }]
}
  • k字段是基于密钥(在示例中为fantasticjwt)生成的对称密钥(base64url编码)。 可以使用以下命令生成密码:
$ echo -n fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
ZmFudGFzdGljand0
  • kty字段将密钥类型定义为对称密钥(八位字节序列)
  • kid(密钥ID)字段定义此JSON Web密钥的序列号

创建密钥后,可以将JWT发行给客户端。

5.6.向客户发行JWT

首先,有必要为客户端创建一个JWT并将NGINX Plus配置为接受JWT。 你可以使用身份提供者(IdP)或自己的服务来创建JWT。 为了进行测试,你可以创建自己的JWT,有关详细信息,请参见使用JWT和NGINX Plus博客文章身份验证API客户端

5.7.配置NGINX Plus以接受来自查询字符串的JWT

NGINX Plus可以从查询字符串参数获取JWT。 要配置它,请在auth_jwt 指令中包括 token=参数:

server {
    listen 80;

    location /products/ {
        proxy_pass        http://api_server;
        auth_jwt          "API" token=$arg_apijwt;
        auth_jwt_key_file conf/key.jwk;
    }
}
#...

通过此配置,可以使用以下命令来验证JWT:

$ curl http://192.168.1.1/products/widget1?apijwt=`cat token.jwt`

5.8.从Subrequest获取JWK

可以将NGINX Plus配置为从远程位置(通常是身份提供者)获取JSON Web密钥,尤其是在使用OpenID Connect时。 将使用auth_jwt_key_request指令配置将子请求发送到的IdP URI:

http {
    #...

    server {
        listen 80;
            #...

        location / {
            auth_jwt "closed site";
            auth_jwt_key_request /_jwks_uri; # Keys will be fetched by subrequest

            proxy_pass http://my_backend;
        }
    }
}

URI可能引用内部位置(_jwks_uri),以便可以缓存JSON Web密钥集(proxy_cache 和proxy_cache_path指令),以避免验证开销:

http {
    proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:1m max_size=10m;
    #...

    server {
        listen 80;
            #...

        location = /_jwks_uri {
            internal;
            proxy_method GET;
            proxy_cache  jwk; # Cache responses
            proxy_pass   https://idp.example.com/oauth2/keys; # Obtain keys from here
        }
    }
}

从子请求获取JWK的完整示例:

#
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:1m max_size=10m;

server {
    listen 80; # Use SSL/TLS in production

    location / {
        auth_jwt "closed site";
        auth_jwt_key_request /_jwks_uri; # Keys will be fetched by subrequest

        proxy_pass http://my_backend;
    }

    location = /_jwks_uri {
        internal;
        proxy_method GET;
        proxy_cache  jwk; # Cache responses
        proxy_pass   https://idp.example.com/oauth2/keys; # Obtain keys from here
    }
}

5.9.也可以看看

6.限制对代理HTTP资源的访问

介绍了如何设置连接请求的最大数量,或从服务器下载内容的最大速率。

6.1.介绍

使用NGINX和NGINX Plus,可以限制:

  • 每个键值的连接数(例如,每个IP地址)
  • 每个键值的请求速率(在一秒钟或一分钟内允许处理的请求数)
  • 连接的下载速度

请注意,可以在NAT设备后面共享IP地址,因此应谨慎使用IP地址限制。

6.2.限制连接数

限制连接数:

1.使用limit_conn_zone指令定义密钥并设置共享内存区域的参数(工作进程将使用该区域共享密钥值的计数器)。 作为第一个参数,指定评估为键的表达式。 在第二个参数zone中,指定区域的名称及其大小:

limit_conn_zone $binary_remote_addr zone=addr:10m;

2.使用limit_conn 指令将限制应用于location {}server {}, http {} 上下文中。 将共享内存区域的名称指定为第一个参数,并将每个键的允许连接数指定为第二个参数:

location /download/ {
     limit_conn addr 1;
}

3.由于$binary_remote_addr变量用作键,因此连接数受IP地址限制。

限制给定服务器连接数的另一种方法是使用$server_name变量:

http {
    limit_conn_zone $server_name zone=servers:10m;

    server {
        limit_conn servers 1000;
    }
}

6.3.限制请求率

速率限制可用于防止DDoS攻击,或防止上游服务器同时被太多请求淹没。 该方法基于泄漏存储桶(leaky bucket)算法:请求以各种速率到达存储桶,并以固定速率离开存储桶。

在使用速率限制之前,你需要配置“泄漏桶(leaky bucket)”的全局参数:

  • 键 - 用于区分一个客户与另一个客户的参数,通常是一个变量
  • 共享内存区域-保留这些键的状态的区域的名称和大小(“泄漏存储桶”)
  • rate-以每秒请求数(r/s)或每分钟请求数(r/m)指定的请求速率限制(“漏斗排放”)。 每分钟请求数用于指定小于每秒一个请求的速率。

这些参数是使用limit_req_zone 指令设置的。 该指令在http {}级别上定义-这种方法允许将不同的区域应用于不同的环境,并向不同的上下文请求溢出参数:

http {
    #...
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
}

使用此配置,将创建大小为10 MB的共享内存区域1。 该区域保留使用$binary_remote_addr变量设置的客户端IP地址的状态。 请注意,与$remote_addr相比,$remote_addr也持有客户端的IP地址,而$binary_remote_addr则持有较短的IP地址的二进制表示形式。

可以使用以下数据来计算共享内存区域的最佳大小:$binary_remote_addr值的大小对于IPv4地址是4个字节,在64位平台上,存储状态占用128个字节。 因此,大约16,000个IP地址的状态信息占据该区域的1兆字节。

如果NGINX需要添加新条目时存储空间耗尽,它将删除最旧的条目。 如果释放的空间仍然不足以容纳新记录,则NGINX返回状态代码503 Service Unavailable。 可以使用limit_req_status 指令重新定义状态代码。

设置区域后,你可以在NGINX配置中的任何地方使用限制请求,这些请求具有为server {}location {}, or http {}上下文指定的limit_req :

http {
    #...

    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        #...

        location /search/ {
            limit_req zone=one;
        }
    }
}

使用此配置,NGINX每秒将在/search/位置处理不超过1个请求。 这些请求的处理以总速率不超过指定的速率被延迟。 如果请求数量超过指定的速率,NGINX将延迟处理此类请求,直到“存储桶”(共享存储区one)已满。 对于到达完整存储桶的请求,NGINX将响应503 Service Unavailable错误(如果未使用limit_req_status重新定义)。

6.4.测试请求速率限制

在配置实际速率限制之前,你可以尝试不限制请求处理速率的“空运行(dry run)”模式。 但是,此类过多的请求仍在共享内存区域中进行处理并记录。 可以使用limit_req_dry_run指令启用“空运行(dry run)”模式:

http {
    #...

    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        #...

        location /search/ {
            limit_req zone=one;
            limit_req_dry_run on;
        }
    }
}

每个超出定义的速率限制的请求都将记录“空运行(dry run)”标记:

2019/09/03 10:28:45 [error] 142#142: *13246 limiting requests, dry run, excess: 1.000 by zone "one", client: 172.19.0.1, server: www.example.com, request: "GET / HTTP/1.0", host: "www.example.com:80"

6.5.处理过多的请求

限制请求以符合limit_req_zone指令中定义的速率。 如果请求数超过了指定的速率,并且共享内存区已满,NGINX将响应并返回错误。 由于流量趋向于突发性,因此在流量突发期间响应客户端请求返回错误不是最好的情况。

NGINX中的此类过多请求可以被缓冲和处理。 limit_req指令的burst参数设置等待以指定速率处理的过多请求的最大数量:

http {
    #...

    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        #...

        location /search/ {
            limit_req zone=one burst=5;
        }
    }
}

使用此配置,如果请求速率超过每秒1个请求,则超出该速率的请求将被放入区域one。 当区域已满时,过多的请求将排队(burst),此队列的大小为5个请求。 队列中的请求处理以总速率不大于指定的速率的方式延迟。 超出突发(burst)限制的请求将被拒绝,并显示503错误。

如果在流量突发(burst)期间不希望延迟请求,请添加nodelay参数:

http {
    #...

    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        #...

        location /search/ {
            limit_req zone=one burst=5 nodelay;
        }
    }
}

使用此配置,无论指定速率(rate)如何,都将立即处理突发(burst)限制内的过多请求,超过突发限制的请求将被拒绝,并显示503错误

6.6.延迟过多的请求

处理过多请求的另一种方法是不延迟地处理一定数量的请求,然后应用速率限制直到过多请求将被拒绝为止。

这可以通过延迟(delay)和突发(burst)参数来实现。 delay参数定义了延迟过多请求以符合定义的速率限制的时间点:

http {
    #...

    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        #...

        location /search/ {
            limit_req zone=one burst=5 delay=3;
        }
    }
}

使用此配置,前三个请求(delay)将无延迟地传递,接下来的两个请求(burst - delay)将以总速率不大于指定速率的方式被延迟,进一步的请求将被拒绝,因为总突发大小已超过,以后的请求将被延迟。

6.7.同步许多共享内存区域的内容

如果你的计算机群集具有多个NGINX实例,并且这些实例使用limit_req方法,则可以在以下条件下同步其共享内存区域的内容:

http {
    #...
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s sync;
}

有关详细信息,请参见群集中的运行时状态共享

6.8.限制带宽

要限制每个连接的带宽,请使用limit_rate指令:

location /download/ {
    limit_rate 50k;
}

使用此设置,客户端将能够通过单个连接以最大50 kb/s的最大速度下载内容。 但是,客户端可以打开多个连接。 因此,如果目标是防止下载速度大于指定值,则连接数量也应受到限制。 例如,每个IP地址一个连接(如果使用了上面指定的共享内存区域):

location /download/ {
    limit_conn addr 1;
    limit_rate 50k;
}

要仅在客户端下载一定数量的数据后才施加限制,请使用limit_rate_after指令。 允许客户快速下载一定数量的数据(例如,文件标头-电影索引)并限制下载其余数据的速率(使用户查看电影而不是下载)是合理的。

limit_rate_after 500k;
limit_rate       20k;

以下示例显示了用于限制连接数和带宽的组合配置。 每个客户端地址允许的最大连接数设置为5个连接,这适合大多数常见情况,因为现代浏览器通常一次最多打开3个连接。 同时,提供下载的位置仅允许一个连接:

http {
    limit_conn_zone $binary_remote_address zone=addr:10m

    server {
        root /www/data;
        limit_conn addr 5;

        location / {
        }

        location /download/ {
            limit_conn       addr 1;
            limit_rate_after 1m;
            limit_rate       50k;
        }
    }
}

6.9.动态带宽控制

limit_rate值也可以指定为变量-这启用了动态带宽用例,例如,允许现代浏览器使用更高的带宽限制:

map $ssl_protocol $response_rate {
    "TLSv1.1" 10k;
    "TLSv1.2" 100k;
    "TLSv1.3" 1000k;
}

server {
    listen 443 ssl;
    ssl_protocols       TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;

    location / {
        limit_rate       $response_rate; # Limit bandwidth based on TLS version
        limit_rate_after 512;      # Apply limit after headers have been sent
        proxy_pass       http://my_backend;
    }
}

6.10.也可以看看

7.限制对代理TCP资源的访问

提供了用于限制对通过TCP进行通信的数据库或介质服务器的访问的方案。 访问可能受到IP地址,同时连接数或带宽的限制。

7.1.通过IP地址限制访问

NGINX可以基于客户端计算机的特定IP地址或IP地址范围来允许或拒绝访问。 要允许或拒绝访问,请在stream上下文或server块内使用allowdeny指令:
 

stream {
    #...
    server {
        listen 12345;
        deny   192.168.1.2;
        allow  192.168.1.1/24;
        allow  2001:0db8::/32;
        deny   all;
    }
}

规则从上到下依次处理:如果序列中的第一个指令deny all(拒绝全部),则所有其他allow(允许)指令均无效。 在此示例中,允许访问192.168.1.1/24子网,但192.168.1.2除外。 还允许使用IPv6地址的2001:0db8::/32范围,并且拒绝访问任何其他IP地址。

7.2.限制TCP连接数

你可以限制来自一个IP地址的同时TCP连接的数量。 这对于防止拒绝服务(DoS(denial-of-service))攻击很有用。

首先,让我们定义一个区域(zone),该区域将存储与一台服务器的最大TCP连接数,以及一个用于识别连接的密钥(key)。 这可以通过在stream上下文中使用limit_conn_zone指令来完成:

stream {
    #...
    limit_conn_zone $binary_remote_addr zone=ip_addr:10m;
    #...
}

标识连接的密钥(key)定义为$binary_remote_addr,它以二进制格式表示客户端的IP地址。 共享内存区域的名称为ip_addr,区域(zone)大小为10 MB。

定义区域后,使用limit_conn指令限制连接。 它的第一个参数指定先前由limit_conn_zone定义的共享存储区的名称。 作为第二个参数,在stream上下文或server块中为每个IP地址指定允许的最大连接数(如本示例中,该示例还显示了前提条件limit_conn_zone指令):

stream {
    #...
    limit_conn_zone $binary_remote_addr zone=ip_addr:10m;

    server {
        #...
        limit_conn ip_addr 1;
    }
}

当限制每个IP地址的连接数时,请注意,网络地址转换(NAT)设备后面的多个主机共享相同的IP地址。

7.3.限制带宽

你可以配置TCP连接的最大下载或上传速度。 分别包括proxy_download_rateproxy_upload_rate指令:

server {
    #...
    proxy_download_rate 100k;
    proxy_upload_rate   50k;
}

通过这些设置,客户端可以通过单个连接以最大100 kb/s的最大速度下载数据,并通过单个连接以最大50 kb/s的最大速度上传数据。 但是,客户端可以打开多个连接。 因此,如果目标是限制每个客户端的整体加载速度,则连接数也必须限制为1,如上一节所述。

stream {
    #...
    limit_conn_zone $binary_remote_addr zone=ip_addr:10m;

    server {
        #...
        limit_conn ip_addr  1;
        proxy_download_rate 100k;
        proxy_upload_rate   50k;
    }
}

8.通过地理位置限制访问

8.1.介绍

NGINX Plus可以根据用户的地理位置来区分用户。 例如,你可以为不同国家/地区提供不同的网站内容,或者可以将内容分发限制为特定国家或城市。

NGINX Plus使用第三方MaxMind数据库来匹配用户的IP地址及其位置。 知道地理位置后,便可以在地图(map)或split_clients模块中使用基于Geoip的变量。

注意:MaxMind GeoLite旧版数据库目前已停产,应改用MaxMind GeoIP2或GeoLite2数据库和NGINX Plus GeoIP2模块。

地理位置限制适用于HTTP和TCP/UDP协议。

8.2.先决条件

8.3.获取数据库

可以从MaxMind下载页面获得GeoIP2或GeoLite2数据库。 在此示例中,使用了GeoLite2免费可下载数据库。

要获取和解压缩GeoLite2 Country数据库:

$ wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
$ gunzip GeoLite2-Country.mmdb.gz

要获取和解压缩GeoLite2 City数据库:

$ wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz
$ gunzip GeoLite2-City.mmdb.gz

8.4.了解数据库结构

要查看可用的地理数据(geodata),可以使用mmdblookup实用工具查询GeoLite2-Country和GeoLite2-City数据库。 地理数据(geodata)表示为JSON树。

安装libmaxminddb数据库实用程序:

  • 对于Amazon Linux,CentOS,Oracle Linux和RHEL:
$ yum install libmaxminddb-devel
  • 对于Debian和Ubuntu:
$ apt-get install libmaxminddb-dev
  • 对于SLES:
$ zypper install libmaxminddb-devel

对数据库的查询可以以下格式发送:

mmdblookup –file [FILE PATH] –ip [IP ADDRESS] [DATA PATH]

例如,要获取8.8.8.8 IP地址的所有可用地理数据,请发送以下命令:

$ mmdblookup --file /usr/local/etc/geoip2/GeoLite2-Country.mmdb --ip 8.8.8.8

输出将是:

{
    "continent":
      {
        "code":
          "NA" <utf8_string>
        "geoname_id":
          6255149 <uint32>
        "names":
          {
            "de":
              "Nordamerika" <utf8_string>
            "en":
              "North America" <utf8_string>
            "es":
              "Norteamérica" <utf8_string>
            "fr":
              "Amérique du Nord" <utf8_string>
            "ja":
              "北アメリカ" <utf8_string>
            "pt-BR":
              "América do Norte" <utf8_string>
            "ru":
              "Северная Америка" <utf8_string>
            "zh-CN":
              "北美洲" <utf8_string>
          }
      }
    "country":
      {
        "geoname_id":
          6252001 <uint32>
        "iso_code":
          "US" <utf8_string>
        "names":
          {
            "de":
              "USA" <utf8_string>
            "en":
              "United States" <utf8_string>
            "es":
              "Estados Unidos" <utf8_string>
            "fr":
              "États-Unis" <utf8_string>
            "ja":
              "アメリカ合衆国" <utf8_string>
            "pt-BR":
              "Estados Unidos" <utf8_string>
            "ru":
              "США" <utf8_string>
            "zh-CN":
              "美国" <utf8_string>
          }
      }
    "registered_country":
      {
        "geoname_id":
          6252001 <uint32>
        "iso_code":
          "US" <utf8_string>
        "names":
          {
            "de":
              "USA" <utf8_string>
            "en":
              "United States" <utf8_string>
            "es":
              "Estados Unidos" <utf8_string>
            "fr":
              "États-Unis" <utf8_string>
            "ja":
              "アメリカ合衆国" <utf8_string>
            "pt-BR":
              "Estados Unidos" <utf8_string>
            "ru":
              "США" <utf8_string>
            "zh-CN":
              "美国" <utf8_string>
          }
      }
  }

例如,要获取特定的地理数据,例如仅获取特定国家/地区的ISO代码,将country iso_code参数添加到命令的末尾:

$ mmdblookup --file /usr/local/etc/geoip2/GeoLite2-Country.mmdb --ip 8.8.8.8 country iso_code

在用于NGINX的GeoIP2模块中创建变量时,也会使用这些参数。

8.5.在NGINX Plus中配置GeoIP2

1.为NGINX Plus安装GeoIP2动态模块:

对于Amazon Linux,CentOS,Oracle Linux和RHEL:

$ yum install nginx-plus-module-geoip2

对于Debian和Ubuntu:

$ apt-get install nginx-plus-module-geoip2

对于SLES:

$ zypper install nginx-plus-module-geoip2

2.使用在main配置级别中指定的load_module指令在NGINX Plus配置文件中启用GeoIP2动态模块:

load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_stream_geoip2_module.so;

http {
    # ...
}

3.使用http {}stream {}或两者的geoip2 {}块将国家和城市数据库的路径添加到NGINX配置中:

http {
    #...
    geoip2 GeoIP2/GeoLite2-Country.mmdb {
        #...
    }

    geoip2 GeoIP2/GeoLite2-City.mmdb {
        #...
    }
}

stream {
    #...
    geoip2 GeoIP2/GeoLite2-Country.mmdb {
        #...
    }

    geoip2 GeoIP2/GeoLite2-City.mmdb {
        #...
    }
}

4.根据GeoIP数据库结构,创建自定义变量,该变量将保留GeoIP2数据库中的数据,然后将数据传递到mapsplit_clients指令(可在 http {} 和stream {} 上下文中应用):

geoip2 GeoIP2/GeoLite2-City.mmdb {
    $geoip2_data_city_name   city names en;
    $geoip2_data_postal_code postal code;
    $geoip2_data_latitude    location latitude;
    $geoip2_data_longitude   location longitude;
    $geoip2_data_state_name  subdivisions 0 names en;
    $geoip2_data_state_code  subdivisions 0 iso_code;
}

geoip2 GeoIP2/GeoLite2-Country.mmdb {
    $geoip2_data_continent_code   continent code;
    $geoip2_data_country_iso_code country iso_code;
}

#...

8.6.方案:选择最近的服务器

使用来自创建的变量的地理位置数据,可以将客户端连接重定向到最近的服务器,从而减少网络延迟并提高连接速度。

这可以通过在变量中使用来自GeoIP2数据库的大洲代码和地图(map)模块来实现,该模块将创建另一个变量,其值将是基于大洲位置的最接近的服务器。 基于此值,NGINX将请求传递给相应的上游服务器组。

1.确保为每个洲配置了服务器或上游服务器组,例如,eu 对于欧洲,na对于北美,针对IP地址无法与GeoIP数据库匹配的所有(all)情况,都进行了配置:

upstream all {
    server all1.example.com:12345;
    server all2.example.com:12345;
}

upstream eu {
    server eu1.example.com:12345;
    server eu2.example.com:12345;
}

upstream na {
    server na1.example.com:12345;
    server na2.example.com:12345;
}

2.添加带有任何名称变量的geoip2 {}块(例如,$geoip2_data_continent_code),以获取GeoIP2数据库的洲代码:

geoip2 GeoIP2/GeoLite2-Country.mmdb {
    $geoip2_data_continent_code continent code;
}

#...

3.创建将创建$nearest_server变量的map

#...
map $geoip2_data_continent_code $nearest_server {
    default all;
    EU      eu;
    NA      na;
    AS      as;
    AF      af;
}
#...

4.创建server {} 块,该块将根据$nearest_server变量中传递的值将请求传递给上游服务器组之一:

server {
    listen 12346;
    proxy_pass http://$nearest_server;
}

如果该大陆是Europe,则$nearest_server的值将为eu,并且连接将通过proxy_pass指令传递给eu上游:

#...
server {
    listen 12346;
    proxy_pass http://$nearest_server;
}

upstream all {
    server all1.example.com:12345;
    server all2.example.com:12345;

upstream eu {
    server eu1.example.com:12345;
    server eu2.example.com:12345;
}
upstream na {
    server na1.example.com:12345;
    server na2.example.com:12345;
}
#...

8.7.例子

此示例可以在httpstream上下文中应用。

# can be either "http {}" or "stream {}"
#...
geoip2 GeoIP2/GeoLite2-Country.mmdb {
    $geoip2_data_continent_code continent code;
}

map $geoip2_data_continent_code $nearest_server {
    default all;
    EU      eu;
    NA      na;
    AS      as;
    AF      af;
}

server {
    listen 12346;
    proxy_pass http://$nearest_server;
    }

upstream all {
    server all1.example.com:12345;
    server all2.example.com:12345;
}

upstream eu {
    server eu1.example.com:12345;
    server eu2.example.com:12345;
}

upstream na {
    server na1.example.com:12345;
    server na2.example.com:12345;
}

在此示例中,将在GeoLite2-Country.mmdb数据库中检查IP地址,结果将写入$geoip2_data_continent_code变量。 NGINX Plus会将变量的值与map指令中的值进行匹配,并将结果写入自定义变量中,在我们的示例中为$nearest_server。 基于$nearest_server的值,proxy_pass指令将选择一个相应的上游服务器。

8.8.更多信息

9.保护到上游服务器的HTTP流量

介绍了如何加密NGINX与上游组或代理服务器之间的HTTP通信。

9.1.先决条件

9.2.获取SSL服务器证书

你可以从受信任的证书提交机构(CA)购买服务器证书,也可以使用OpenSSL库创建自己的内部CA并生成自己的证书。服务器证书以及私钥应放在每个上游服务器上。

9.3.获取SSL客户端证书

NGINX将使用SSL客户端证书向上游服务器标识自己。 此客户端证书必须由受信任的CA签名,并在NGINX上与相应的私钥一起配置

你还需要配置上游服务器,以要求所有传入的SSL连接都需要客户端证书,并信任颁发NGINX客户端证书的CA。 然后,当NGINX连接到上游时,它将提供其客户端证书,而上游服务器将接受它。

9.4.配置NGINX

首先,将URL更改为上游组以支持SSL连接。 在NGINX配置文件中,在proxy_pass指令中为代理服务器或上游组指定“https”协议:

location /upstream {
    proxy_pass https://backend.example.com;
}

使用proxy_ssl_certificateproxy_ssl_certificate_key指令添加客户端证书和用于对每个上游服务器上的NGINX进行身份验证的密钥:

location /upstream {
    proxy_pass                https://backend.example.com;
    proxy_ssl_certificate     /etc/nginx/client.pem;
    proxy_ssl_certificate_key /etc/nginx/client.key;
}

如果你对上游或自己的CA使用自签名证书,则还应包含proxy_ssl_trusted_certificate。 该文件必须为PEM格式。 (可选)包括proxy_ssl_verifyproxy_ssl_verfiy_depth指令,以使NGINX检查安全证书的有效性:

location /upstream {
    #...
    proxy_ssl_trusted_certificate /etc/nginx/trusted_ca_cert.crt;
    proxy_ssl_verify       on;
    proxy_ssl_verify_depth 2;
    #...
}

每个新的SSL连接都需要在客户端和服务器之间进行完整的SSL握手,这会占用大量CPU。 要让NGINX代理先前协商过连接参数并使用所谓的缩写握手,请包含proxy_ssl_session_reuse指令:

location /upstream {
    #...
    proxy_ssl_session_reuse on;
    #...
}

(可选)你可以指定使用哪些SSL协议和密码:

location /upstream {
        #...
        proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        proxy_ssl_ciphers   HIGH:!aNULL:!MD5;
}

9.5.配置上游服务器

每个上游服务器应配置为接受HTTPS连接。 对于每个上游服务器,请使用ssl_certificatessl_certificate_key指令指定服务器证书和私钥的路径:

server {
    listen              443 ssl;
    server_name         backend1.example.com;

    ssl_certificate     /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/certs/server.key;
    #...
    location /yourapp {
        proxy_pass http://url_to_app.com;
        #...
    }
}

使用ssl_client_certificate指令指定客户端证书的路径:

server {
    #...
    ssl_client_certificate /etc/ssl/certs/ca.crt;
    ssl_verify_client      optional;
    #...
}

9.6.完整的例子

http {
    #...
    upstream backend.example.com {
        server backend1.example.com:443;
        server backend2.example.com:443;
   }

    server {
        listen      80;
        server_name www.example.com;
        #...

        location /upstream {
            proxy_pass                    https://backend.example.com;
            proxy_ssl_certificate         /etc/nginx/client.pem;
            proxy_ssl_certificate_key     /etc/nginx/client.key;
            proxy_ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
            proxy_ssl_ciphers             HIGH:!aNULL:!MD5;
            proxy_ssl_trusted_certificate /etc/nginx/trusted_ca_cert.crt;

            proxy_ssl_verify        on;
            proxy_ssl_verify_depth  2;
            proxy_ssl_session_reuse on;
        }
    }

    server {
        listen      443 ssl;
        server_name backend1.example.com;

        ssl_certificate        /etc/ssl/certs/server.crt;
        ssl_certificate_key    /etc/ssl/certs/server.key;
        ssl_client_certificate /etc/ssl/certs/ca.crt;
        ssl_verify_client      optional;

        location /yourapp {
            proxy_pass http://url_to_app.com;
        #...
        }

    server {
        listen      443 ssl;
        server_name backend2.example.com;

        ssl_certificate        /etc/ssl/certs/server.crt;
        ssl_certificate_key    /etc/ssl/certs/server.key;
        ssl_client_certificate /etc/ssl/certs/ca.crt;
        ssl_verify_client      optional;

        location /yourapp {
            proxy_pass http://url_to_app.com;
        #...
        }
    }
}

在此示例中,proxy_pass指令中的“https”协议指定由NGINX转发至上游服务器的流量得到保护。

当安全连接首次从NGINX传递到上游服务器时,将执行完整的握手过程。 proxy_ssl_certificate指令定义上游服务器所需的PEM格式证书的位置,proxy_ssl_certificate_key指令定义证书的私钥的位置,proxy_ssl_protocolsproxy_ssl_ciphers指令控制使用哪些协议和密码。

下次NGINX将连接传递到上游服务器时,由于proxy_ssl_session_reuse指令,会话参数将被重用,并且安全连接的建立速度更快。

proxy_ssl_trusted_certificate指令命名的文件中的受信任CA证书用于在上游验证证书。proxy_ssl_verify_depth指令指定检查证书链中的两个证书,并且proxy_ssl_verify指令验证证书的有效性。

10.保护到上游服务器的TCP流量

介绍了如何保护NGINX和TCP上游服务器或TCP服务器上游组之间的TCP通信。

10.1.先决条件

  • 使用--with-streamwith-stream_ssl_module配置参数编译的NGINX Plus R6和更高版本或最新的NGINX开源代码
  • 代理的TCP服务器或TCP服务器的上游组
  • SSL证书和私钥

10.2.获取SSL服务器证书

首先,你将需要获取服务器证书和私钥,并将它们放在上游服务器或上游组中的每台服务器上。 可以从可信证书颁发机构(CA)获取证书,也可以使用SSL库(例如OpenSSL)生成证书。

当需要加密NGINX与上游服务器之间的连接时,将使用自签名服务器证书。 但是,这些连接很容易受到中间人攻击:冒名顶替者可以冒充上游服务器,而NGINX不会知道它正在与假服务器通信。 如果你获得由受信任的CA签名的服务器证书(可以使用OpenSSL创建自己的内部CA),则可以将NGINX配置为仅信任由该CA签名的证书。 这使攻击者更难模仿上游服务器。

10.3.获取SSL客户端证书

NGINX可以使用SSL客户端证书向上游服务器标识自己。 此客户端证书必须由受信任的CA签名,并与相应的私钥一起存储在NGINX上。

你将需要配置上游服务器,以要求所有传入的SSL连接都需要客户端证书,并信任将客户端证书颁发给NGINX的CA。 然后,当NGINX连接到上游时,它将提供其客户端证书,而上游服务器将接受它。

10.4.配置NGINX

在NGINX配置文件中,在stream级别的server块中包含proxy_ssl指令:

stream {
    server {
        ...
        proxy_pass backend;
        proxy_ssl  on;
    }
}

然后指定上游服务器所需的SSL客户端证书的路径以及证书的私钥:

server {
        ...
        proxy_ssl_certificate     /etc/ssl/certs/backend.crt;
        proxy_ssl_certificate_key /etc/ssl/certs/backend.key;
}

(可选)你可以指定使用哪些SSL协议和密码(ciphers):

server {
        ...
        proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        proxy_ssl_ciphers   HIGH:!aNULL:!MD5;
}

如果你使用由CA颁发的证书,则还应包含proxy_ssl_trusted_certificate指令来命名包含用于验证上游安全证书的受信任CA证书的文件。 该文件必须为PEM格式。 (可选)包括proxy_ssl_verifyproxy_ssl_verfiy_depth指令,以使NGINX检查安全证书的有效性:

server {
    ...
    proxy_ssl_trusted_certificate /etc/ssl/certs/trusted_ca_cert.crt;
    proxy_ssl_verify       on;
    proxy_ssl_verify_depth 2;
}

每个新的SSL连接都需要在客户端和服务器之间进行完整的SSL握手,这会占用大量CPU。 要让NGINX代理先前协商过连接参数并使用所谓的缩写握手,请包含proxy_ssl_session_reuse指令:

proxy_ssl_session_reuse on;

10.5.完整的例子

stream {

    upstream backend {
        server backend1.example.com:12345;
        server backend2.example.com:12345;
        server backend3.example.com:12345;
   }

    server {
        listen     12345;
        proxy_pass backend;
        proxy_ssl  on;

        proxy_ssl_certificate         /etc/ssl/certs/backend.crt;
        proxy_ssl_certificate_key     /etc/ssl/certs/backend.key;
        proxy_ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
        proxy_ssl_ciphers             HIGH:!aNULL:!MD5;
        proxy_ssl_trusted_certificate /etc/ssl/certs/trusted_ca_cert.crt;

        proxy_ssl_verify        on;
        proxy_ssl_verify_depth  2;
        proxy_ssl_session_reuse on;
    }
}

在此示例中,proxy_ssl指令指定由NGINX转发到上游服务器的TCP通信是受保护的。

当安全的TCP连接第一次从NGINX传递到上游服务器时,将执行完整的握手过程。 上游服务器要求NGINX提交proxy_ssl_certificate指令中指定的安全证书。 proxy_ssl_protocolsproxy_ssl_ciphers指令控制使用哪些协议和密码。

下次NGINX将连接传递到上游时,由于proxy_ssl_session_reuse指令,会话参数将被重用,并且安全的TCP连接建立得更快。

proxy_ssl_trusted_certificate指令命名的文件中的受信任CA证书用于验证上游服务器上的证书。 proxy_ssl_verify_depth指令指定检查证书链中的两个证书,并且proxy_ssl_verify指令验证证书的有效性。

要了解有关NGINX Plus的更多信息,请参阅我们的商业订阅

11.动态拒绝IP地址

介绍如何创建拒绝列表或允许列表

11.1.总览

在NGINX Plus Release 13(R13)和更高版本中,你可以拒绝列出某些IP地址,以及创建和维护被拒绝列出的IP地址的数据库。 你也可以明确地允许列出其他IP地址。 IP地址数据库由NGINX Plus APIkeyval模块管理。

NGINX Plus Release 19 (R19)通过将IP地址与子网或网络范围内的任何地址进行匹配来扩展此功能。

11.2.先决条件

NGINX Plus Release 13及更高版本,NGINX Plus Release 19及更高版本,以支持网络范围。

11.3.配置

首先,启用数据库以存储拒绝列出和允许列出的IP地址列表。

1.在NGINX Plus配置文件中,在http上下文中包括keyval_zone指令,以创建用于存储键和值的存储区。 此示例指令创建一个称为one的1‑MB区域。

http {
    # ...
    keyval_zone zone=one:1m;
 }

要对子网进行IP地址匹配(例如192.168.13.0/24),请指定keyval_zone指令的type=ip参数:

http {
    # ...
    keyval_zone zone=one:1m type=ip;
 }

请注意,还应该增加keyval_zone的大小,因为type=ip参数还会启用在该区域中存储的额外索引。

1.你可以选择包括state参数来创建一个文件,该文件存储了键值数据库,因此可以在NGINX Plus重新加载和重新启动后持久保存; 在此示例中,one.keyval

keyval_zone zone=one:1m state=one.keyval;

2.使用api指令以读写模式启用NGINX Plus API:

# ...
 server {
     listen 80;
     server_name www.example.com;

     location /api {
         api write=on;
     }
 }

我们强烈建议限制对此位置的访问,例如,仅允许从localhost (127.0.0.1)访问,并使用HTTP基本身份验证将PATCHPOST, DELETE方法的使用限制为指定的用户集:

# ...
 server {
     listen 80;
     server_name www.example.com;

     location /api {
         api   write=on;

         allow 127.0.0.1;
         deny  all;
         
         limit_except GET {
             auth_basic "NGINX Plus API";
             auth_basic_user_file /path/to/passwd/file;
         }
     }
 }

3.使用API的POST方法填充键值数据库,并以JSON格式提供数据。 你可以使用以下示例中的curl命令。 如果区域为空,则可以一次输入多个键值对; 否则,必须一次添加一对。

$ curl -X POST -d '{
   "10.0.0.1": "1",
   "10.0.0.2": "1",
   "10.0.0.3": "0",
   "10.0.0.4": "0"
 }' -s http://www.example.com/api/6/http/keyvals/one

如果已指定IP地址与网络范围的匹配(使用keyval_zone指令的type=ip参数),请发送POST命令,并使用CIDR表示法指定的网络范围:

$ curl -X POST -d '{
   "192.168.13.0/24": "1"
 }' -s http://www.example.com/api/6/http/keyvals/one

4.通过在http上下文中包含keyval指令,定义如何根据键值数据库评估客户端IP地址。

该指令利用标准的NGINX和NGINX Plus变量$remote_addr,该变量会为每个请求自动设置为客户端IP地址。

在处理每个请求时,NGINX Plus:

  • 在由 zone= 参数指定的键值数据库(此处为one)中查找第一个参数(此处为$remote_addr,预设为客户端的IP地址)。
  • 如果数据库中的键与$remote_addr完全匹配,则将第二个参数(此处为$target)设置为与该键对应的值。 在我们的示例中,对于拒绝列出的地址,值为1;对于允许列出的地址,值为0。
http {
     # ...
     keyval_zone zone=one:1m type=ip state=one.keyval;
     keyval $remote_addr $target zone=one; # Client address is the key, 
                                           # $target is the value;
 }

5.使用if指令创建一个规则,该规则根据客户端IP地址允许或拒绝访问。 使用此规则,当$target为0时允许访问,而当其为1时拒绝访问:

if ($target) {
    return 403;
}

11.4.管理键值数据库

你可以使用API方法动态更新键值数据库,而无需重新加载NGINX Plus。

以下所有示例均在one区域中进行操作,可从http://www.example.com/api/6/http/keyvals/one访问该区域。

  • 要获取区域的所有数据库条目的列表:
$ curl -X GET 'http://www.example.com/api/6/http/keyvals/one'
  • 要更新现有条目的值(在本示例中,将IP地址10.0.0.4的访问状态从允许列表更改为拒绝列表):
$ curl -X PATCH -d '{"10.0.0.4": "1"}' -s 'http://www.example.com/api/6/http/keyvals/one'
  • 要将条目添加到填充区域:
$ curl -X POST -d '{"10.0.0.5": "1"}' -s 'http://www.example.com/api/6/http/keyvals/one'
  • 删除条目:
$ curl -X PATCH -d '{"10.0.0.4":null}' -s 'http://www.example.com/api/6/http/keyvals/one'

11.5.完整的例子

完整的NGINX Plus配置:

http {
    # ...
    keyval_zone zone=one:1m type=ip state=one.keyval;
    keyval $remote_addr $target zone=one;

    server {
        listen 80;
        server_name www.example.com;

        location /api {
            api   write=on;

            allow 127.0.0.1;
            deny  all;
        
            limit_except GET {
                auth_basic "NGINX Plus API";
                auth_basic_user_file /path/to/passwd/file;
            }
        }

        if ($target) {
            return 403;
        }
    }
}

此配置:

  • 创建一个1 MB的keyval区域one,该区域接受网络范围,并创建文件one.keyval,以使键-值对数据库在NGINX Plus的重新加载和重新启动后持续存在。
  • 在写入模式下启用NGINX Plus API,以便可以使用IP地址填充区域。
  • 允许在键值数据库中查找IP地址$remote_addr作为键,并将找到的键的值放入$target变量中。
  • 启用一条简单的规则来检查结果值:如果$target的值是1(拒绝列出地址),则将403 (Forbidden)返回给客户端。

以下curl命令使用拒绝列表(值为1)或允许列表(值为0)的IP地址填充一个空的keyval区域one

$ curl -X POST -d '{
     "10.0.0.1": "1",
     "192.168.13.0/24": "1",
     "10.0.0.3": "0",
     "10.0.0.4": "0"
}' -s 'http://www.example.com/api/6/http/keyvals/one'

11.6.也可以看看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值