mTLS
mTLS
是 Mutual TLS(双向TLS认证)
的缩写
让我们使用 curl
演示 mTLS 过程
首先, 我们需要一个 client
和 server
都信任的 certificate authority(CA)
CA
就是一个 public key
和 private key
其中 public key
包含在自签名的 X.509证书
中.
使用下面的命令创建 CA
openssl req \
-new \
-x509 \
-nodes \
-days 365 \
-subj '/CN=my-ca' \
-keyout ca.key \
-out ca.crt
上述命令会产出2个文件: ca.key
和 ca.crt
, 使用 PEM格式. 其中
-
ca.key
是private key(私钥)
-
ca.crt
是X.509证书
, 其中包含public key
使用 openssl x509 -in ca.crt -text -noout
查看证书内容
通过查看证书内容,我们可以确认以下几点:
-
Subject
和Issuer
都是CN = my-ca
, 这表明该证书是自签名的 -
Validity
表明证书有效期是一年 -
X509v3 Basic Constraints
的值CA:TRUE
表明该证书可以用做CA
, 即可以用来签署证书
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
[OBMITTED]
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = my-ca
Validity
Not Before: Mar 7 00:00:19 2021 GMT
Not After : Mar 7 00:00:19 2022 GMT
Subject: CN = my-ca
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
[OBMITTED]
Exponent: [OBMITTED]
X509v3 extensions:
X509v3 Subject Key Identifier:
[OBMITTED]
X509v3 Authority Key Identifier:
keyid:[OBMITTED]
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
[OBMITTED]
然后,创建Server端的private key和证书
首先创建 server端
的 private key
执行下面的命令, 该命令会产生1个文件: server.key
, 即 server端
的 private key
openssl genrsa \
-out server.key 2048
然后创建 server端证书
(1) 先创建 Certificate Signing Request (CSR)
, 下面的命令会产生1个文件: server.csr
, 该文件用于第2步生成 server端证书
CN(Common Name)
填写 server的hostname
, 本例中为 localhost
# 先创建 CSR
openssl req \
-new \
-key server.key \
-out server.csr
(2) 创建 server端证书
, 下面的命令会产生1个文件: server.crt
, 即 server端证书
, 使用 PEM格式
openssl x509 \
-req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-days 365 \
-out server.crt
使用 openssl x509 -in ca.crt -text -noout
查看证书内容
通过查看证书内容,我们可以确认以下几点:
-
Issuer
是CN = my-ca
, 这表明证书是由my-ca证书颁发机构
签名的 -
Subject
是CN = localhost
, 这表明可以将该证书提供给客户端,以验证服务器是否可以信任DNS名称localhost的内容 -
Validity
表明证书有效期是一年
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
[OBMITTED]
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = my-ca
Validity
Not Before: Jun 13 00:50:18 2020 GMT
Not After : Jun 13 00:50:18 2021 GMT
Subject: CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
[OBMITTED]
Exponent: [OBMITTED]
Signature Algorithm: sha256WithRSAEncryption
[OBMITTED]
然后,创建Client端的private key和证书
过程同上
然后,使用curl携带客户端证书访问服务器
curl \
--cacert ca.crt \
--key client.key \
--cert client.crt \
https://localhost:3000
除了 --cert参数
之外,其他参数还要简单说明一下:
-
--cacert
是为了验证server端证书
-
--key
是为了发送请求内容时进行私钥加密
TLS Termination
TLS Termination
主要作用是,作为一个 前置代理服务器 接收外部到达的加密TLS流量 , 然后将其 解密为HTTP明文 , 最后再将流量 转发到内部的某个服务
TLS Origination
TLS Origination
主要作用是 作为一个 代理服务器, 接收内部服务的HTTP明文流量 ,然后将其 加密 ,最后 转发到一个HTTPS服务 上,该服务既可以是内部,也可以是外部的,但看起来就像是一个内部的服务。
作为与边界入口网关对立的存在,出口网关也通常放置在网络的边界。
所有的出口流量都被它接管,在这个节点上我们可以 统一实施一些访问控制策略 ,或监控,或日志等,这和 Ingress
的功能其实是一样的,最大的不同在于 将明文流量加密再转发 。
我们对向外主动请求的流量常常是不设防的,这样的严进宽出在安全要求较高的场合是不合适的。
了解了 Egress
这个概念后,不仅仅是在安全上对我们的业务有帮助,对于业务架构的设计其实也是有所裨益的。
考虑一个基于微信的服务,可能会有公众号,小程序,还可能会有多个小程序和公众号,更复杂一些,设计成微服务,不同的服务可能会调用同一公众号的微信接口。
那么问题来了, API access token
的存取就出现了竞争,我们可以做一个独立的服务,将各种token的获取,刷新和缓存封装起来,所有的服务需要用到token时直接通过 token-server
获取即可。
这种办法是可以的,虽然每次微信请求接口调用都产生了一次额外的内部rpc调用,性能上其实可以忽略不计,
麻烦的是代码可能不会简洁,更重要的是 对这些外发接口调用的监控 ,日志没有一个统一的地方处理。
我们还可以做得更好,简单的架构如下:
在内部服务中,所有调用微信API的协议均使用HTTP,然后这些请求都会经过 wx-egress-server
,它会给请求附加合适的 access token
,通过 HTTPS 协议发往外部的微信服务器。
除此以外,我们还可以 增加日志便于调试 ,监控请求的成功率,甚至发起重试等等,这样相对原来的方案要更灵活。