共计 4556 个字符,预计需要花费 12 分钟才能阅读完成。
Istio 的安全功能主要包括以下几个部分的实现:
- 双向 TLS 支持。
- 基于黑白名单的访问控制。
- 基于角色的访问控制。
本文主要和大家聊一聊 istio 的双向 tls。双向 tls 支持主要针对通信方面,将明文传输的服务通信,转换为 Envoy 之间的加密通信。这一安全设置可以在全局、Namespace 或者单个服务的范围内生效。
认证策略是对服务收到的请求生效,要在双向 tls 中指定客户端认证策略,需要在 DetinationRule 中设置 TLSSettings,每个认证策略需要和目的地规则共同生效。
全局 tls
apiVersion: "authentication.istio.io/v1alpha1"
kind: "MeshPolicy"
metadata:
name: "default"
spec:
peers:
-mtls: {}
---
apiVersion:"networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
spec:
host: "*.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
- 网格范围内的认证策略名称必须是 default;所有其它名字的策略都会被拒绝和忽视。CRD 类型是 MeshPolicy,它不同于命名空间范围内或服务范围内的策略类型 (Policy)。
- 另一方面,目的地规则可以是任意名字,也可以存在于任意命名空间。
- 目的地规则中形如 *.local 的宿主名称只匹配网格中以 local 结尾的服务。
- 当处于 ISTIO_MUTUAL TLS 模式,Istio 会依据内部实现机制设置密钥和证书的路径(例如 clientCertificate、privateKey 和 caCertificates)。
trafficPolicy.
tls.mode
DISABLE
Do not setup a TLS connection to the upstream endpoint.
SIMPLE
Originate a TLS connection to the upstream endpoint.
MUTUAL
Secure connections to the upstream using mutual TLS by presenting client certificates for authentication.
ISTIO_MUTUAL
Secure connections to the upstream using mutual TLS by presenting client certificates for authentication.
Compared to Mutual mode, this mode uses certificates generated automatically by Istio for mTLS
authentication. When this mode is used, all other fields in TLSSettings should be empty.
peers.
mtls
STRICT
Client cert must be presented, connection is in TLS.
PERMISSIVE
Connection can be either plaintext or TLS, and client cert can be omitted.
我们首先创建 3 个 namespace,分别为 test1, test2, test3。其中 test1,test2 自动注入 sidecar,test3 不注入,启动 sample 的 httpbin 和 sleep pod,如下图:
当启用策略以后:
这些认证策略和目的地规则有效地配置了所有服务的 sidecars,使服务在双向 tls 模式下分别进行接收和发送请求。但这对于没有 sidecar 的服务并不适用,例如上文中创建的 httpbin.test3 和 sleep.test3。从 sleep.test3 到 httpbin.test1 和 httpbin.test2 的请求开始出现失败现象,这是由于虽然在服务端启用了双向 tls 认证,但 sleep.test3 并没有 sidecar 来支持认证。类似的,从 sleep.test1 (或 sleep.test2) 到 httpbin.test3 的请求也会失败。
带 sidecar 客户端到不带 sidecar 服务端
下面为 test3 的 httpbin 开启单独的策略:
apiVersion:”networking.istio.io/v1alpha3″
kind: “DestinationRule”
metadata:
name: “httpbin”
namespace: “test3”
spec:
host: “httpbin.test3.svc.cluster.local”
trafficPolicy:
tls:
mode: DISABLE
可以看到,发给 test3 的请求都正常。
不带 sidecar 客户端到带 sidecar 服务端
从不带 sidecar 的客户端到带有 sidecar 的服务端 (工作在双向 TLS 模式) 的连接,唯一的选择是从双向 TLS 模式切换到 PERMISSIVE 模式。该模式允许服务端接收 HTTP 或(双向)TLS 流量。显然,这种模式会降低安全等级,因此建议只在迁移过程中使用。
apiVersion:"authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "httpbin"
namespace: "test1"
spec:
targets:
-name: "httpbin"
peers:
-mtls:
mode: PERMISSIVE
应用完成后从 test3 的 sleep 发给 test1 的请求成功了,但是发给 test2 的请求还是失败。因为我们只针对 test1 开启策略:
从这里也可看出,policy 的策略生效机制,局部作用域覆盖全局作用域。
服务 tls
这次以 productpage 为例,开启之前的访问如下图:
通过 Policy crd 开启 tls:
apiVersion:"authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "productpage-tls"
spec:
targets:
-name: productpage
peers:
-mtls:
其中 target 是可选项,如果去掉的话,作用域将扩展到整个 Namespace。
证书
将规则应用到 produtpage 服务,http 方式无法访问,https 提示需客户端证书
查看开启 tls 以后证书安装情况:
将自动生成的证书拷贝出来,查看下有效期和 SAN:
带证书去 curl,可以看到成功了:
服务端口的 tls
对于一个服务存在多个端口,也可以通过 Destination Rule 只开启某个端口的服务:
apiVersion:"networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "productpage-2"
spec:
host:productpage.bookinfo.svc.cluster.local
trafficPolicy:
tls:
mode: DISABLE
portLevelSettings:
-port:
number: 9080
tls:
mode: ISTIO_MUTUAL
这个也很容易理解,这一规则用于指派对该地址的访问方式:
tls.mode = DISABLE,这个服务缺省是不开启 tls 支持的,如果取值 ISTIO_MUTUAL,则代表这个地址(服务)的所有端口都开启 TLS。
port.tls.mode = ISTIO_MUTUAL,只针对这一个端口启用 mTLS 支持。
从不带 sidecar 的客户端到带有 sidecar 的服务端 (工作在双向 TLS 模式) 的连接,唯一的选择是从双向 tls 模式切换到 permissive 模式,该模式允许服务端接收 http 或(双向)tls 流量。显然,这种模式会降低安全等级,推荐只在迁移过程中使用。
认证策略是对服务收到的请求生效的,要在双向 tls 中指定客户端认证策略,需要在 Detination Rule 中设置 TLS Settings,每个认证策略需要和目的地规则共同生效。另外 istio 也提供了外部 CA 的支持,可以使用已有的证书体系来替换网格内的自签发体系。
双向 tls 与 https
当 Istio sidecar 使用 https 服务部署时,代理将自动从 L7 降至 L4(无论是否启用了双向 TLS),这就意味着它不会终止原来的 https 通信。
环境准备:生成证书,Nginx 示例使用的 secret,configmap
openssl req -x509 -nodes -days 365 -newkeyrsa:2048 -keyout /tmp/nginx.key -out /tmp/nginx.crt -subj"/CN=my-nginx/O=my-nginx"
kubectl create secret tls nginxsecret --key/tmp/nginx.key --cert /tmp/nginx.crt
kubectl create configmap nginxconfigmap--from-file=samples/https/default.conf
kubectl apply -fsamples/https/nginx-app.yaml -n ngtest
kubectl apply -f <(bin/istioctlkube-inject -f samples/sleep/sleep.yaml) -n ngtest
服务端无 sidecar
上述可以看出,服务是可用的。
服务端有 sidecar,未开启双向 tls
删掉以前的 Nginx,使用 sidecar 部署
确保运行。
从原容器访问 nginx:
可用访问成功。
从 sidecar 访问 nginx:
访问成功。
服务端有 sidecar,开启双向 tls
在上一步的基础上启用网格内部的双向 tls 策略
从原容器访问 nginx:
访问成功,因为工作流”sleep –> sleep-proxy –> nginx-proxy –> nginx”,整个过程是 7 层流量,在 sleep-proxy 和 nginx-proxy 之间有一个 L4 双向 TLS 加密。在这种情况下,一切都很好。
从 sidecar 访问 nginx:
访问失败,对于工作流”sleep-proxy–> nginx-proxy –> nginx”,nginx-proxy 可以从 sleep-proxy 中获得双向的 TLS 流量。在上面的命令中,sleep-proxy 不提供客户端证书,因此它不会起作用。
对于纯私有环境用户,双向 tls 看起来意义并不大,如果攻击者有权限能攻破内网,应该不会仅仅局限于只是抓明文报文。且如果开启的话,对于有不带 sidecar 的客户端访问,要么需要带证书,要么服务端需开启。
permissive 模式运行。使用场景的局限性较大。且开启以后,如果 pod 有访问 apiserver 的需求还需针对 apiserver 单独开启策略。对于公网环境,重要数据流量加密还是有必要的。