istio 安全 身份验证策略
对等身份验证和请求身份验证
1、两种身份验证方式
1.1、对等身份验证
用于服务到服务身份验证,以验证建立连接的客户端。Istio 提供双向 TLS作为传输身份验证的全栈解决方案,无需更改服务代码即可启用。
1.2、请求身份验证
用于最终用户身份验证,以验证附加到请求的凭据。Istio 使用 JSON Web Token (JWT) 验证实现请求级身份验证,并使用自定义身份验证提供程序或任何 OpenID Connect 提供程序简化开发人员体验。
2、互相tls认证
istio使用envoy代理作为客户端和服务端的pep(策略执行点),在服务之间创建了一种安全的隧道化通信机制。当一个负载向另一个负载通过互相tls认证发起请求时,请求会有如下处理方式:
-
Istio 将客户端的出站流量重新路由到客户端本地的 sidecar Envoy。
-
客户端 Envoy 开始与服务器端 Envoy 进行双向 TLS 握手。在握手过程中,客户端 Envoy 还会进行 安全命名 检查,以验证服务器证书中提供的服务帐户是否有权运行目标服务。
-
客户端 Envoy 和服务端 Envoy 建立相互 TLS 连接,Istio 将流量从客户端 Envoy 转发到服务端 Envoy。
-
服务端 Envoy 授权请求。如果获得授权,它会通过本地 TCP 连接将流量转发到后端服务。
2.1、宽容模式
Istio 双向 TLS 具有宽容模式,允许服务同时接受纯文本流量和双向 TLS 流量。
2.2、安全命名
服务器身份编码在证书中,但服务名称通过发现服务或DNS检索。安全命名信息将服务器身份映射到服务名称。身份A到服务名称B的映射意味着“A被授权运行服务B”。控制平面监视apiserver,生成安全命名映射,并将它们安全地分发给PEP。
请注意,对于非 HTTP/HTTPS 流量,安全命名无法防止 DNS 欺骗,在这种情况下,攻击者会修改服务的目标 IP。由于 TCP 流量不包含Host信息,并且 Envoy 只能依赖目标 IP 进行路由,因此 Envoy 可能会将流量路由到被劫持 IP 上的服务。这种 DNS 欺骗甚至可能在客户端 Envoy 收到流量之前就发生了。
3、身份验证策略
3.1、策略存储
Istio 将网格范围策略存储在 根命名空间 中。这些策略具有一个空的选择器,适用于网格中的所有工作负载。具有命名空间范围的策略存储在相应的命名空间中。它们仅适用于其命名空间内的工作负载。如果您配置字段selector,则身份验证策略仅适用于符合您配置的条件的工作负载。
对等身份验证策略:PeerAuthentication
请求身份验证策略:RequestAuthentication
3.2、选择器字段
对等身份验证策略 和 请求身份验证策略 使用selector字段来指定要应用该策略的工作负载的标签。
如果您不为该字段提供值selector,Istio 会将策略与策略存储范围内的所有工作负载进行匹配。
selector字段可帮助指定策略的范围:
-
网格范围策略:为根命名空间指定的策略,没有或带有空selector字段。
-
命名空间范围的策略:为没有或带有空选择器字段的非根名称空间指定的策略。
-
工作负载特定策略:在常规命名空间中定义的策略,具有非空的选择器字段。
每个命名空间只能有一个网格范围的 对等身份验证策略 ,并且每个命名空间只能有一个命名空间范围的 对等身份验证策略 。当您为同一个网格或命名空间配置多个网格范围或命名空间范围的 对等身份验证策略 时,Istio 会 忽略较新的策略 。当有多个特定于工作负载的对等身份验证策略匹配时,Istio 会 选择最旧的策略 。
工作复制最窄匹配策略:
-
特定于工作负载
-
命名空间范围
-
网格范围
Istio 可以组合所有匹配的 请求身份验证策略 ,就像它们来自单个请求身份验证策略一样。因此,您可以在网格或命名空间中拥有多个网格范围或命名空间范围的策略。但是,避免 拥有多个网格范围或命名空间范围的请求身份验证策略仍然是一种很好的做法。
3.3、对等身份验证
对等身份验证策略指定 Istio 在目标工作负载上强制实施的双向 TLS 模式。支持以下模式:
-
PERMISSIVE:工作负载接受相互的TLS和纯文本流量。当没有sidecar的工作负载无法使用互TLS时,此模式在迁移期间最有用。一旦使用sidecar注入迁移了工作负载,就应该将模式切换为STRICT。
-
STRICT:工作负载只接受相互的TLS流量。
-
DISABLE:禁用互TLS。从安全角度来看,除非您提供自己的安全解决方案,否则不应该使用此模式。
当模式未设置时,将继承父范围的模式。未设置模式的网格范围对等身份验证策略默认使用PERMISSIVE模式。
3.3.1、官网例子
以下对等身份验证策略要求命名空间foo中的所有工作负载使用相互TLS:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: "example-policy"
namespace: "foo"
spec:
mtls:
mode: STRICT
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: "example-workload-policy"
namespace: "foo"
spec:
selector:
matchLabels:
app: example-app
portLevelMtls:
80:
mode: DISABLE
3.4、请求身份验证
请求身份验证策略指定验证 JSON Web Token (JWT) 所需的值。
3.5、principal
当您使用对等身份验证策略和相互TLS时,Istio将从对等身份验证中提取身份到source.principal中。类似地,当您使用请求身份验证策略时,Istio将JWT中的标识分配给request.auth.principal。使用这些主体设置授权策略和作为遥测输出。
3.6、对等身份验证策略测试
3.6.1、基础配置
kubectl create ns bar
kubectl create ns foo
kubectl create ns legacy
kubectl label namespace foo istio-injection=enabled
kubectl label namespace bar istio-injection=enabled
kubectl apply -f samples/sleep/sleep.yaml -n bar
kubectl apply -f samples/sleep/sleep.yaml -n foo
kubectl apply -f samples/sleep/sleep.yaml -n legacy
# 可以参考前面几篇文章的,都是一样的。
kubectl apply -f service-a.yml -n bar
kubectl apply -f service-a.yml -n foo
kubectl apply -f service-a.yml -n legacy
此时是接受相互的TLS和纯文本流量。
for from in "foo" "bar" "legacy"; do
for to in "foo" "bar" "legacy"; do
kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl -s "http://service-a.${to}:8000/api/v1/testA/get_headers/" -s -o /dev/null -w "sleep.${from} to service-a.${to}: %{http_code}\n";
done;
done
sleep.foo to service-a.foo: 200
sleep.foo to service-a.bar: 200
sleep.foo to service-a.legacy: 200
sleep.bar to service-a.foo: 200
sleep.bar to service-a.bar: 200
sleep.bar to service-a.legacy: 200
sleep.legacy to service-a.foo: 200
sleep.legacy to service-a.bar: 200
sleep.legacy to service-a.legacy: 200
3.6.2、宽容模式测试
具有代理的工作负载之间的所有流量都使用双向 TLS,而无需您执行任何操作。
当服务器没有 sidecar 时,header中就没有X-Forwarded-Client-Cert,这意味着请求是纯文本。
执行结果
sidecar -> sidecar
```shell
{"host":"service-a.foo:8000","user-agent":"curl/8.10.1","accept":"*/*","x-forwarded-proto":"http","x-request-id":"aeb4d7ae-e49d-4ff1-98a7-dfaaccf40566","x-envoy-attempt-count":"1","x-forwarded-client-cert":"By=spiffe://cluster.local/ns/foo/sa/default;Hash=f5e88b5f6633eab21fa2f7a392f6dfdd63f3cdf6c46a99332b0fe1c90e3eb5d9;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"}
sleep.foo to service-a.foo: 200
```
sidecar -> non-sidecar
```shell
{"host":"service-a.legacy:8000","user-agent":"curl/8.10.1","accept":"*/*","x-forwarded-proto":"http","x-request-id":"0b09707d-c862-47d4-ae87-a9fc97d17f99","x-envoy-decorator-operation":"service-a.legacy.svc.cluster.local:8000/*","x-envoy-peer-metadata-id":"sidecar~10.1.4.122~sleep-5577c64d7c-ptnp6.foo~foo.svc.cluster.local","x-envoy-peer-metadata":"ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwp5CgZMQUJFTFMSbyptCg4KA2FwcBIHGgVzbGVlcAoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAogCgROQU1FEhgaFnNsZWVwLTU1NzdjNjRkN2MtcHRucDYKEgoJTkFNRVNQQUNFEgUaA2ZvbwpFCgVPV05FUhI8GjprdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvZm9vL2RlcGxveW1lbnRzL3NsZWVwChgKDVdPUktMT0FEX05BTUUSBxoFc2xlZXA=","x-envoy-attempt-count":"1"}
sleep.foo to service-a.legacy: 200
```
non-sidecar -> sidecar
```shell
{"host":"service-a.bar:8000","user-agent":"curl/8.10.1","accept":"*/*","x-forwarded-proto":"http","x-request-id":"fdd4ef65-c566-4361-9dc5-5fd4c42970bc"}
sleep.legacy to service-a.bar: 200
```
non-sidecar -> non-sidecar
```shell
{"host":"service-a.legacy:8000","user-agent":"curl/8.10.1","accept":"*/*"}
sleep.legacy to service-a.legacy: 200
```
3.6.3、严格模式测试
为了防止整个mesh的非互TLS流量,可以配置全mesh的对端认证策略,互TLS模式设置为STRICT。网状范围的对等身份验证策略不应该有选择器,并且必须在 根命名空间 中应用。
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: "default"
namespace: "istio-system"
spec:
mtls:
mode: STRICT
EOF
警告
该示例假设istio-system是根命名空间。如果您在安装过程中使用了不同的值,请将其替换istio-system为您使用的值。
结果
sidecar -> sidecar
```shell
{"host":"service-a.foo:8000","user-agent":"curl/8.10.1","accept":"*/*","x-forwarded-proto":"http","x-request-id":"2c05f546-2a3a-4894-96f9-d0613a6a15dd","x-envoy-attempt-count":"1","x-forwarded-client-cert":"By=spiffe://cluster.local/ns/foo/sa/default;Hash=f5e88b5f6633eab21fa2f7a392f6dfdd63f3cdf6c46a99332b0fe1c90e3eb5d9;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"}
sleep.foo to service-a.foo: 200
```
sidecar -> non-sidecar
```shell
{"host":"service-a.legacy:8000","user-agent":"curl/8.10.1","accept":"*/*","x-forwarded-proto":"http","x-request-id":"117b6d2a-ad57-46a0-9a1d-2abc5936f1d7","x-envoy-decorator-operation":"service-a.legacy.svc.cluster.local:8000/*","x-envoy-peer-metadata-id":"sidecar~10.1.4.121~sleep-5577c64d7c-pxqmk.bar~bar.svc.cluster.local","x-envoy-peer-metadata":"ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwp5CgZMQUJFTFMSbyptCg4KA2FwcBIHGgVzbGVlcAoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAogCgROQU1FEhgaFnNsZWVwLTU1NzdjNjRkN2MtcHhxbWsKEgoJTkFNRVNQQUNFEgUaA2JhcgpFCgVPV05FUhI8GjprdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvYmFyL2RlcGxveW1lbnRzL3NsZWVwChgKDVdPUktMT0FEX05BTUUSBxoFc2xlZXA=","x-envoy-attempt-count":"1"}
sleep.bar to service-a.legacy: 200
```
non-sidecar -> sidecar
```shell
sleep.legacy to service-a.foo: 000
command terminated with exit code 56
```
non-sidecar -> non-sidecar
```shell
{"host":"service-a.legacy:8000","user-agent":"curl/8.10.1","accept":"*/*"}
sleep.legacy to service-a.legacy: 200
```
3.6.4、策略优先级
特定于工作负载的对等身份验证策略优先于命名空间范围的策略。
3.7、请求身份验证测试
3.7.1、配置策略
基于已有的ingressgateway和服务。
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: "jwt-example"
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
jwtRules:
- issuer: "testing@secure.istio.io"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/jwks.json"
EOF
curl "service-a.localserver.com/api/v1/testA/get_headers/" -s -o /dev/null -w "%{http_code}\n"
200
curl --header "Authorization: Bearer deadbeef" "service-a.localserver.com/api/v1/testA/get_headers/" -s -o /dev/null -w "%{http_code}\n"
401
TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/demo.jwt -s)
curl --header "Authorization: Bearer $TOKEN" "service-a.localserver.com/api/v1/testA/get_headers/" -s -o /dev/null -w "%{http_code}\n"
200