SSL配置#

要配置HTTPS服务器,必须在 server 块的监听套接字上启用 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.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
#...
}

服务器证书是一个公共实体。它会发送给每个连接到服务器的客户端。私钥是一个安全实体,应存储在访问受限的文件中;但它必须对Angie的主进程可读。私钥也可以与证书存储在同一个文件中。

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

在这种情况下,文件的访问权限也应受到限制。即使证书和密钥存储在一个文件中,只有证书会发送给客户端。

指令 ssl_protocolsssl_ciphers 可用于限制连接,仅包含SSL/TLS的强版本和密码。默认情况下,Angie使用:

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

因此,通常不需要显式配置它们。

HTTPS服务器优化#

SSL操作消耗额外的CPU资源。在多处理器系统上,应运行多个 worker processes,数量不能少于可用的CPU核心数。最耗CPU的操作是SSL握手。减少每个客户端的这些操作数量有两种方法:第一种是启用 keepalive 连接,通过一个连接发送多个请求,第二种是重用SSL会话参数,以避免并行和后续连接的SSL握手。会话存储在一个由 ssl_session_cache 指令配置的共享SSL会话缓存中。1MB的缓存大约包含4000个会话。默认缓存超时为5分钟。可以使用 ssl_session_timeout 指令增加。以下是一个针对具有10MB共享会话缓存的多核系统优化的配置示例:

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.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;
    #...

证书链#

某些浏览器可能会对由知名证书颁发机构签署的证书提出警告,而其他浏览器可能不会。这是因为颁发机构使用了一个中间证书来签署服务器证书,而该中间证书并不在某个特定浏览器分发的知名受信任证书机构的证书库中。在这种情况下,颁发机构提供了一组链式证书,这些证书应与签署的服务器证书串联在一起。服务器证书必须在组合文件中位于链式证书之前:

$ 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;
#...
}

如果服务器证书和捆绑证书的顺序连接错误,Angie将无法启动并显示错误信息:

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed

(SSL: error:0B080074:x509 certificate routines: X509_check_private_key:key values mismatch)

因为Angie尝试使用捆绑证书的第一个证书而不是服务器证书来使用私钥。

浏览器通常会存储它们收到的由受信任机构签署的中间证书,因此实际使用的浏览器可能已经拥有所需的中间证书,并且可能不会对未发送链式捆绑的证书提出警告。为了确保服务器发送完整的证书链,可以使用 openssl 命令行工具。

$ openssl s_client -connect www.godaddy.com:443

证书链
 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

小技巧

在使用 SNI 测试配置时,重要的是要指定 -servername 选项,因为 openssl 默认不使用SNI。

在这个例子中,www.GoDaddy.com服务器证书#0的主题("s")由一个颁发者("i")签署,该颁发者本身是证书#1的主题,该证书由一个颁发者签署,该颁发者本身是证书#2的主题,该颁发者由知名颁发者ValiCert, Inc.签署,其证书存储在浏览器的内置证书库中。

如果没有添加证书捆绑,只有服务器证书#0将会显示。

单个HTTP/HTTPS服务器#

可以配置一个同时处理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;
#...
}

基于名称的HTTPS服务器#

配置两个或多个在单个IP地址上监听的HTTPS服务器时,会出现常见问题:

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协议行为造成的。SSL连接在浏览器发送HTTP请求之前建立,而Angie并不知道请求的服务器名称。因此,它只能提供默认服务器的证书。

解决此问题的最古老且最稳健的方法是为每个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;
#...
}

具有多个名称的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;
#...
}

服务器名称指示#

在单个IP地址上运行多个HTTPS服务器的更通用的解决方案是TLS服务器名称指示扩展(SNI,RFC 6066),该扩展允许浏览器在SSL握手期间传递请求的服务器名称,因此,服务器将知道它应该使用哪个证书进行连接。SNI目前被大多数现代浏览器支持,但一些旧的或特殊的客户端可能不会使用。

小技巧

仅可以在SNI中传递域名;然而,一些浏览器可能会错误地将服务器的IP地址作为其名称传递,如果请求包含文字IP地址。对此不应依赖。

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

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

然而,如果启用SNI的Angie动态链接到没有SNI支持的OpenSSL库,Angie将显示警告:

Angie 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