在之前的 nextcloud-fpm + frp + nginx + ssl 一文中,公网服务器的Nginx与家中服务器的Nginx通信是是使用自签名证书的。这样做主要的原因是家中的服务器没有公网IP,无法直接获取公共CA签名的证书;而定时从服务器复制证书下来的操作非常不方便,不值得。但是自签名证书容易被中间人攻击是广为人知的,而为了解决这个问题,今天无意间看到client authentication。大概意思是,传统的HTTPS是客户端验证服务器证书是否有效,而client authentication顾名思义就是加多了服务器验证客户端证书是否有效这一方向,所以也称为双向验证。理论上来说这可以提高安全性,不管怎么说,折腾一下总没错的。
本文会介绍:
- 自签名CA及自签名证书
- 双向证书验证(Nginx)
1. 自签名CA及自签名证书
生成CA及自签名证书需要分开来做。
1.1 CA
生成自己的CA的Key和Cert,会提示设置私钥密码:
1
2
# 生成10年有效的rsa 2048位CA证书(rootCA.crt)与私钥(rootCA.key)
openssl req -x509 -sha256 -days 3650 -newkey rsa:2048 -keyout rootCA.key -out rootCA.crt
1.2 Server证书与Client证书
先生成证书私钥和CSR。生成CSR的过程中会提示输入相关信息,其中的Common Name通常是填完整域名——比如买了 somedomain.com
,如果是给子域名 blog.somedomain.com
发证书,Common Name写的就是 blog.somedomain.com
而不是 somedomain.com
。
1
2
3
4
5
6
7
# 把example.domain.com替换成你自己的域名
# 生成私钥
openssl genrsa -out example.domain.com.server.key 2048
openssl genrsa -out example.domain.com.client.key 2048
# 生成CSR
openssl req -new -key example.domain.com.server.key -out example.domain.com.server.csr
openssl req -new -key example.domain.com.client.key -out example.domain.com.client.csr
然后要创建一个域名附加信息文件domain.ext,内容如下:
1
2
3
4
5
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.domain.com
然后就可以用CA签发证书了:
1
2
openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in example.domain.com.server.csr -out example.domain.com.server.crt -days 3650 -CAcreateserial -extfile domain.ext
openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in example.domain.com.client.csr -out example.domain.com.client.crt -days 3650 -CAcreateserial -extfile domain.ext
此时手上应有3对共6个文件:
- Server的Cert和Key
- Client的Cert和Key
- CA的Cert和Key
接下来就要分发了。
2. 设置Nginx
2.1 公网服务器用Client套
把Client的一套( example.domain.com.client.crt
和 example.domain.com.client.key
)以及CA的Cert( rootCA.crt
)上传到公网服务器的Nginx配置文件夹里,比如 /etc/nginx/ssl
。然后修改nextcloud的站点配置文件,主要是把 location /
的proxy指令们增改一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server {
# 其他配置......
location / {
# 因为使用了相同的域名,所以这里把server_name配置成相同的值
proxy_ssl_server_name on;
proxy_ssl_name $host;
# 转发到frp的监听端口
proxy_pass https://127.0.0.1:8002/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 把nextcloud官方建议的secure header的add_header改成这里的proxy_set_header
proxy_set_header Referrer-Policy "no-referrer";
proxy_set_header X-Content-Type-Options "nosniff";
proxy_set_header X-Download-Options "noopen";
proxy_set_header X-Frame-Options "SAMEORIGIN";
proxy_set_header X-Permitted-Cross-Domain-Policies "none";
proxy_set_header X-Robots-Tag "noindex, nofollow";
proxy_set_header X-XSS-Protection "1; mode=block";
proxy_set_header Host $http_host;
proxy_set_header cookie $http_cookie;
proxy_set_header Proxy-Connection "";
# 设置client证书
proxy_ssl_certificate /etc/nginx/ssl/example.domain.com.client.crt;
proxy_ssl_certificate_key /etc/nginx/ssl/example.domain.com.client.key;
# 这个指令不再是trust Server证书了,而是指定ca证书
#proxy_ssl_trusted_certificate /etc/nginx/ssl/nextcloud.crt;
proxy_ssl_trusted_certificate /etc/nginx/ssl/rootCA.crt;
proxy_http_version 1.1;
}
}
2.2 家里服务器用Server套
把Server的一套( example.domain.com.server.crt
和 example.domain.com.server.key
)以及Client的Cert( example.domain.com.client.crt
)、CA的Cert( rootCA.crt
)上传到家里服务器的Nginx配置文件夹里,比如 /etc/nginx/ssl
。然后修改nextcloud的站点配置文件,主要是把server blog的ssl指令们增改一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
#其他配置...
ssl_certificate /etc/nginx/ssl/example.domain.com.server.crt;
ssl_certificate_key /etc/nginx/ssl/example.domain.com.server.key;
ssl_client_certificate /etc/nginx/ssl/example.domain.com.client.key;
# 指定用于验证客户端证书的CA证书
ssl_trusted_certificate /etc/nginx/ssl/rootCA.crt;
# 打开客户端验证
ssl_verify_client on;
# 其他配置...
}
注意 ssl_client_certificate
指令是指定client的证书的,就是前面放到公网服务器上用于proxy_ssl_certificate指令的证书。
此时对公网服务器和家里服务器都reload一下Nginx,就应该可以正常访问的了。