认证策略

本篇理解认证策略和双向TLS认证。

未启用全局双向TLS

带sidecar和不带的都能通讯。

创建三个命名空间foo、bar、legacy,前两者部署带sidecar的httpbin和sleep,后者则不带。

1
2
3
4
5
6
7
8
9
10
11
$ kubectl create ns foo
$ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo

$ kubectl create ns bar
$ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar

$ kubectl create ns legacy
$ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n legacy
$ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
  • 测试:从三个命名空间的sleep的pod中,通过curl请求三个命名空间的httpbin,都返回200。

如下检测sleep.bar到httpbin.foo的可达性。

1
$ kubectl exec $(kubectl get pod -l app=sleep -n bar -o jsonpath={.items..metadata.name}) -c sleep -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"

如下一行命令测试所有可达性

1
2
3
4
5
6
7
8
9
10
11
$ 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 http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done

sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.foo to httpbin.legacy: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.bar to httpbin.legacy: 200
sleep.legacy to httpbin.foo: 200
sleep.legacy to httpbin.bar: 200
sleep.legacy to httpbin.legacy: 200
  • 检测不存在认证策略, 确保试验正确性
1
2
3
4
5
$ kubectl get policies.authentication.istio.io --all-namespaces
No resources found.

$ kubectl get meshpolicies.authentication.istio.io
No resources found.
  • 查看目地路由,不包含相关配置, 确保试验正确性
1
$ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:"

全局启用双向TLS

1
2
3
4
5
6
7
apiVersion: "authentication.istio.io/v1alpha1"
kind: "MeshPolicy"
metadata:
name: "default"
spec:
peers:
- mtls: {}

上面配置使所有workload接受TLS加密。

它的kind是MeshPolicy,name是default,仅能有一个。

此时,仅将接收方配置为使用相互TLS。如果您在Istio服务之间(即那些带有sidecar的服务)运行curl命令,则所有请求都会失败,并显示503错误代码,因为客户端仍在使用纯文本。

1
2
3
4
5
6
$ for from in "foo" "bar"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done

sleep.foo to httpbin.foo: 503
sleep.foo to httpbin.bar: 503
sleep.bar to httpbin.foo: 503
sleep.bar to httpbin.bar: 503

要配置客户端,您需要设置目标规则以使用双向TLS。可以使用多个目标规则,每个适用于一项服务(或名称空间)的规则。但是,使用带有*通配符的规则来匹配所有服务会更方便,这样它就可以与网状网络范围的身份验证策略相提并论。

1
2
3
4
5
6
7
8
9
10
11
12
cat <<EOF | kubectl apply -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
namespace: "default"
spec:
host: "*.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF
  • 主机值* .local只能匹配群集中的服务,而不是外部服务。另请注意,目标规则的名称或名称空间没有限制。
  • 在ISTIO_MUTUAL 模式下,Istio将根据其内部实现设置密钥和证书(例如,客户端证书,私钥和CA证书)的路径。

不要忘记,目的地规则也出于非身份验证的原因(例如设置金丝雀)而使用,但是优先级顺序相同。因此,如果服务出于某种原因(例如,对于配置负载平衡器)需要特定的目标规则,则该规则必须包含具有ISTIO_MUTUAL模式的类似TLS块,否则它将覆盖网状或命名空间范围的TLS设置并禁用TLS。从而导致客户端TLS失效。

从非istio服务到istio服务请求

如sleep.legacy没有sidecar, 所以它不能初始TLS请求到istio服务, 即sleep.legacy到httpbin.foo或httpbin.bar会 失败。由于Envoy拒绝纯文本请求的方式,在这种情况下,您将看到curl退出代码56(接收网络数据失败)。

1
2
3
4
5
6
7
$ for from in "legacy"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done


sleep.legacy to httpbin.foo: 000
command terminated with exit code 56
sleep.legacy to httpbin.bar: 000
command terminated with exit code 56

在全局启用了TLS时,这按预期方式工作,不幸的是,在不降低这些服务的身份验证要求的情况下,没有解决方案。

从istio服务到非istio服务请求

尝试从sleep.foo(或sleep.bar)向httpbin.legacy发送请求。您将看到请求失败,因为Istio按照我们的目标规则中的说明将客户端配置为使用双向TLS,但是httpbin.legacy没有附带功能,因此无法处理。

1
2
3
4
5
$ for from in "foo" "bar"; do for to in "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done


sleep.foo to httpbin.legacy: 503
sleep.bar to httpbin.legacy: 503

要解决此问题,我们可以添加目标规则以覆盖httpbin.legacy的TLS设置。例如:

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: "httpbin-legacy"
spec:
host: "httpbin.legacy.svc.cluster.local"
trafficPolicy:
tls:
mode: DISABLE
EOF

从istio服务到kubernetes API server

Kubernetes API服务器没有附带功能,因此,与将请求发送到任何非Istio服务时相同的问题,来自Istio服务(例如sleep.foo)的请求将失败。

1
2
3
4
5
6
$ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t')
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl https://kubernetes.default/api --header "Authorization: Bearer $TOKEN" --insecure -s -o /dev/null -w "%{http_code}\n"


000
command terminated with exit code 35

同样,我们可以通过覆盖API服务器的目标规则(kubernetes.default)来更正此问题。

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: "api-server"
spec:
host: "kubernetes.default.svc.cluster.local"
trafficPolicy:
tls:
mode: DISABLE
EOF

如果您使用默认的双向TLS选项安装Istio,则此规则以及上述全局身份验证策略和目标规则将在安装过程中注入到系统中。

清除上面配置

1
2
$ kubectl delete meshpolicy default
$ kubectl delete destinationrules default httpbin-legacy api-server

启用命名空间或服务范围的TLS

除了为整个网格指定身份验证策略之外,Istio还允许您为特定的名称空间或服务指定策略。命名空间范围的策略优先于网格范围的策略,而服务特定策略的优先级仍然更高。

命名空间范围策略

以下示例显示了为名称空间foo中的所有服务启用双向TLS的策略。如您所见,它使用kind:“ Policy”而非“ MeshPolicy”,并指定一个名称空间,在这种情况下为foo。如果您未指定名称空间值,则该策略将应用于默认名称空间。

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "default"
namespace: "foo"
spec:
peers:
- mtls: {}
EOF

与网状网络范围的策略类似,整个命名空间范围的策略必须命名为default,并且不限制任何特定服务(“无目标”部分)。

添加相应的目标规则:

1
2
3
4
5
6
7
8
9
10
11
12
cat <<EOF | kubectl apply -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
namespace: "foo"
spec:
host: "*.foo.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF

主机* .foo.svc.cluster.local仅将匹配项限制为foo命名空间中的服务。

由于这些策略和目标规则仅应用于命名空间foo中的服务,因此您应该只能看到从client-without-sidecar(sleep.legacy)到httpbin.foo的请求开始失败。

1
2
3
4
5
6
7
8
9
10
11
12
$ 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 http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done

sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.foo to httpbin.legacy: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.bar to httpbin.legacy: 200
sleep.legacy to httpbin.foo: 000
command terminated with exit code 56
sleep.legacy to httpbin.bar: 200
sleep.legacy to httpbin.legacy: 200

服务范围策略

您还可以为特定服务设置身份验证策略和目标规则。运行此命令以仅为httpbin.bar服务设置另一个策略。

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -n bar -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "httpbin"
spec:
targets:
- name: httpbin
peers:
- mtls: {}
EOF

目标规则:

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -n bar -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "httpbin"
spec:
host: "httpbin.bar.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF

在此示例中,我们没有在元数据中指定名称空间,而是将其放在命令行(-n bar)中,其作用相同。身份验证策略和目标规则名称没有限制。为了简单起见,本示例使用服务本身的名称。

再次,运行探测命令。正如预期的那样,出于相同的原因,从sleep.legacy到httpbin.bar的请求开始失败。

策略优先级

为了说明特定于服务的策略如何优先于名称空间范围的策略,您可以添加一个策略来禁用httpbin.foo的双向TLS,如下所示。请注意,您已经创建了一个整个命名空间范围的策略,该策略为命名空间foo中的所有服务启用了双向TLS,并观察到从sleep.legacy到httpbin.foo的请求都失败了(请参见上文)。

1
2
3
4
5
6
7
8
9
cat <<EOF | kubectl apply -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "overwrite-example"
spec:
targets:
- name: httpbin
EOF

目标规则:

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF | kubectl apply -n foo -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "overwrite-example"
spec:
host: httpbin.foo.svc.cluster.local
trafficPolicy:
tls:
mode: DISABLE
EOF

从sleep.legacy重新运行请求,您应该再次看到成功返回码(200),确认特定于服务的策略将覆盖整个命名空间范围的策略。

1
2
3
4
$ kubectl exec $(kubectl get pod -l app=sleep -n legacy -o jsonpath={.items..metadata.name}) -c sleep -n legacy -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"


200

清除上面配置

1
2
3
4
$ kubectl delete policy default overwrite-example -n foo
$ kubectl delete policy httpbin -n bar
$ kubectl delete destinationrules default overwrite-example -n foo
$ kubectl delete destinationrules httpbin -n bar

用户认证

要试验此功能,您需要一个有效的JWT。 JWT必须与您要用于演示的JWKS端点相对应。在本教程中,我们将使用Istio代码库中的JWT测试和JWKS端点。

另外,为方便起见,请通过ingressgateway公开httpbin.foo(有关更多详细信息,请参见ingress任务)。

参考资料