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

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

HTTPS 服务器优化#

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

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

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

小技巧

在使用 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。自 0.9.8f 版本以来,OpenSSL 支持 SNI,如果是用配置选项 ‑‑enable‑tlsext 构建的。自 OpenSSL 0.9.8j 以来,该选项默认启用。如果 Angie 是用 SNI 支持构建的,那么运行时使用 "-V" 开关会显示这一点:

$ 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