描述
单向认证指浏览器与服务器交互需要对服务器证书进行认证,而双向认证就是服务端对客户端也要进行一次认证,认证的主要条件为:客户端需要有一张客户端证书,而这张客户端证书必须是由服务端指定的 CA
根证书签发的(中间 CA 个人没验证过)。
常规操作
服务端的 Nginx 配置:
server {
listen 443 ssl;
server_name example.com;
server_tokens off;
access_log /etc/nginx/access.log;
error_log /etc/nginx/example.log;
ssl_certificate /etc/nginx/certs/example.pem;
ssl_certificate_key /etc/nginx/certs/example.key;
ssl_client_certificate /etc/nginx/certs/example_server_ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
ssl_prefer_server_ciphers on;
ssl_session_tickets off;
ssl_session_cache off;
location / {
proxy_http_version 1.1;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Vary;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;
proxy_pass http://127.0.0.1:serverPort;
add_header Access-Control-Allow-Origin https://server.example.com;
add_header Access-Control-Allow-Credentials true;
}
}
配置说明
基础部分
- listen:为监听的端口
- server_name:指定访问该服务的域名
- server_tokens:在错误页和“服务器”响应头字段中启用或禁用nginx版本(有道翻译)点此查看具体细节
- access_log:服务日志路径
- error_log:错误日志路径
证书及验证部分
- ssl_certificate:服务器证书
- ssl_certificate_key:服务器证书私钥
- ssl_client_certificate:服务器验证客户端证书的 CA 证书
- ssl_verify_client:是否开启客户端验证
- ssl_verify_depth:验证深度(如果是中间证书颁发的客户端证书深度至少要写2)
- ssl_prefer_server_ciphers:指定在使用SSLv3和TLS协议时,服务器密码应该优于客户端密码(有道翻译)点此查看具体细节
- ssl_session_tickets:是否可以通过 ssl_session_tickets 恢复 session 会话(有道翻译)点此查看具体细节
- ssl_session_cache:设置存储会话参数的缓存的类型和大小(有道翻译)点此查看具体细节
代理部分
- proxy_http_version:代理 http 协议版本
- proxy_ignore_headers:禁止从代理服务器处理某些响应标头字段(有道翻译)点此查看具体细节
- proxy_hide_header:设置不传递的非默认不传递的字段点此查看具体细节
- proxy_set_header:设置请求头
- proxy_pass:设置代理服务器的协议和地址
跨域设置
- add_header:添加头部信息
- Access-Control-Allow-Origin:访问控制允许来源
- Access-Control-Allow-Credentials:响应头告诉浏览器是否将响应公开给前端JavaScript代码
扩展
Nginx 反向代理作为客户端请求服务端进行双向认证
服务端配置:
server {
listen 443 ssl;
server_name server.example.com;
access_log /etc/nginx/access.log;
error_log /etc/nginx/error.log;
ssl_certificate /etc/nginx/conf.d/ssl/cert.pem;
ssl_certificate_key /etc/nginx/conf.d/ssl/key.pem;
location / {
proxy_http_version 1.1;
proxy_set_header Host example.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_certificate /etc/nginx/conf.d/ssl/clientcert.pem;
proxy_ssl_certificate_key /etc/nginx/conf.d/ssl/clientkey.pem;
proxy_ssl_server_name on;
proxy_pass http://127.0.0.1:serverPort;
}
}
坑位总结
- 配置问题:代理作为客户端配置客户端证书的字段是
proxy_ssl_certificate
和proxy_ssl_certificate_key
而不是ssl_certificate
和ssl_certificate_key
,前者是服务器证书而不是作为代理的客户端证书。 proxy_ssl_server_name
这个字段默认是off
,这里要改为on
,后端服务需要代理发送SNI
才能正常工作,如果代理服务器不发送SNI
,会返回 502 错误。即无法正常和后端通信。- 头部信息设置时需要将
Host
改为与proxy_pass
一致,不然会无法响应,一直502
,个人理解:SNI
中的server_name
拿取的是proxy_set_header
中的Host
所以如果Host
默认为请求当前服务器的Host
那么代理到example.com
的服务器server_name
就会有问题,找不到对应的路由,当然这只是个人踩坑的解决方案,原理有问题的话,大家可以自动屏蔽。