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单独开启策略。对于公网环境,重要数据流量加密还是有必要的。