Cloudflare 代理自签名 HTTPS 证书的源站

源站使用 Cloudflare 的 CDN 服务非常方便,只要将 DNS 托管在 Cloudflare 平台,然后设置 DNS 记录时,代理状态勾选「已代理」就行了。不过这一步操作无法选择源站使用的是 HTTP 协议还是 HTTPS 协议,依稀记得有个地方是可以设置的。翻了一遍操作菜单,在左侧导航菜单的「STL/TLS > 概述」里找到了,加密模式有如下四个选项:

  1. 关闭(不安全)
    未应用加密。关闭 SSL 将禁用 HTTPS,浏览器同时会显示警告,指出您的网站不安全。
  2. 灵活
    仅在访问者与 Cloudflare 之间启用加密。这可以避免浏览器发出安全警告,但 Cloudflare 与您的源服务器之间的所有连接均通过 HTTP 建立。
  3. 完全
    启用加密端到端。当您的源服务器支持 SSL 认证但未使用有效的公开可信的证书时,使用此模式。
  4. 完全(严格)
    (推荐模式)启用端到端加密,对源服务器证书强制执行验证。使用 Cloudflare 的源服务器 CA 为您的源服务器生成证书。

不过这个设置是全局的,更改后会对当前站点下所有的 DNS 记录生效。Cloudflare 不支持针对每条 DNS 记录设置单独的加密模式,也不支持 HTTPS -> HTTP 的 Fallback 策略。

此前该站点下的所有二级域名对应的源站都是仅 HTTPS 协议的,这次要添加的二级域名源站是仅 HTTP 协议的。如果将加密模式设置为「灵活」的话,势必要启用所有源站的 HTTP 端口。最好的解决办法就是为这次添加的源站开启 HTTPS 协议。只要将加密方式设置为“完全”,就可以使用自签名证书的 HTTPS 服务。

源站建立在一台 CentOS 7 系统的小型 VPS 上,使用 Nginx 提供 HTTP 服务。打开 Nginx 默认配置文件 /etc/nginx/nginx.conf,去掉下面配置的注释(如果没有则添加):

nginxserver {
    listen       443 ssl http2;
    listen       [::]:443 ssl http2;
    server_name  _;
    root         /usr/share/nginx/html;

    ssl_certificate "/etc/pki/nginx/server.crt";
    ssl_certificate_key "/etc/pki/nginx/private/server.key";
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

使用 OpenSSL 创建站点证书:

sh# 不对外服务,密钥长度选择 1024 足够了
openssl genrsa -out server.key 1024
# 下行命令需要填写一些信息
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -signkey server.key -out server.crt
# 如果 /etc/pki/nginx 目录不存在则手动创建
mv server.key /etc/pki/nginx/private/server.key
mv server.crt /etc/pki/nginx/server.crt

完成配置后使用 systemctl reload nginx 命令使 Nginx 配置生效。

问题一

如果发生以下错误:

[emerg] cannot load certificate "/etc/pki/nginx/server.crt": BIO_new_file() failed (SSL: error:0200100D:system library:fopen:Permission denied:fopen('/etc/pki/nginx/server.crt','r') error:2006D002:BIO routines:BIO_new_file:system lib)

错误原因多半是因为开启了 SELinux,使用 sestatus 命令,如果返回如下信息则表示 SELinux 已启用:

SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed

使用 ls --scontext /etc/pki/nginx/server.crt 命令查看文件的 security context:

unconfined_u:object_r:admin_home_t:s0 /etc/pki/nginx/server.crt

由于证书之前是在 /root 目录下创建的,因此证书类型是 admin_home_t。现在只要重置文件 scontext 类型就可以了:

shrestorecon -v -R /etc/pki/nginx/server.crt
ls --scontext /etc/pki/nginx/server.crt

显示信息如下则表示重置成功:

unconfined_u:object_r:cert_t:s0  /etc/pki/nginx/server.crt

/etc/pki/nginx/private/server.key 进行同样操作。最后重启 Nginx 服务即可。

问题二

当站点配置为 proxy_pass http://127.0.0.1:8000; 时,访问服务器出现 502 网关错误:

connect() to 127.0.0.1:8000 failed (13: Permission denied) while connecting to upstream

同样是因为开启了 SELinux 导致的。运行如下命令允许 Nginx 访问网络:

shsetsebool -P httpd_can_network_connect 1