TOC
本任务涵盖了启用、配置和使用Istio authentication policies时可能需要执行的主要活动。在身份验证概述中了解有关底层概念的更多信息。
开始之前
- 了解Istio authentication policy和相关的mutual TLS authentication概念。
- 使用默认配置文件在Kubernetes集群上安装Istio,如安装步骤所述。
$ istioctl install --set profile=default
开始
我们的示例用到两个命名空间 foo 和 bar,以及两个服务 httpbin 和 sleep,这两个服务都带有 Envoy sidecar proxy 一起运行。我们也会用到两个运行在 legacy 命名空间下不带 sidecar 的 httpbin 和 sleep 实例。如果您想要使用相同的示例尝试任务,执行如下命令
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
您可以通过从名称空间foo、bar或legacy中的任何sleep pod发送一个带有curl的HTTP请求到httpbin来验证设置。httpbin.foo, httpbin.bar或者httpbin.legacy。使用HTTP代码200,所有请求都应该成功。 例如,下面是sleep.bar请求httpbin.foo的测试验证:
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"
这个一行命令方便地遍历所有可达性组合:
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://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
使用如下命令验证系统中没有peer authentication policy:
$ kubectl get peerauthentication --all-namespaces
No resources found
最后(但并非最不重要),确认没有应用于示例服务的destination rules。您可以通过检查现有destination rules的host: value来做到这一点,并确保它们不匹配。例如:
$ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:"
根据Istio的版本,您可能会看到其他主机的destination rules。但是,在foo、bar和legacy名称空间中应该没有host,也不应该有匹配所有的通配符
*
自动mTLS
默认情况下,Istio关联迁移到Istio代理的服务工作负载,并在配置client proxy自动向这些工作负载发送mTLS通信流,并在没有sidecars的情况下向工作负载发送纯文本(plain text)通信流。
因此,具有代理的工作负载之间的所有通信都使用相互TLS,而无需您做任何事情
。例如,从请求到httpbin/header的响应。当使用相互TLS时,代理将X-Forwarded-Client-Cert头注入到向后端发送的上游请求。报头的存在就是使用了mTLS的证据。例如:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl -s http://httpbin.foo:8000/headers -s | grep X-Forwarded-Client-Cert | sed 's/Hash=[a-z0-9]*;/Hash=<redacted>;/'
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"
当服务没有sidecar时,X-Forwarded-Client-Cert头不在那里,这意味着请求是纯文本的。
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.legacy:8000/headers -s | grep X-Forwarded-Client-Cert
在STRICT模式下全局启用Istio mutual TLS
虽然Istio自动将代理和工作负载之间的所有通信升级为mTLS,但工作负载仍然可以接收纯文本通信。为了防止整个mesh的非双向TLS通信,可以设置全mesh-wide对端认证策略,mutual TLS模式设置为STRICT。mesh-wide peer authentication policy不应该有selecter,必须应用在根命名空间中,例如:
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
namespace: "istio-system"
spec:
mtls:
mode: STRICT
EOF
该示例假定istio-system是根名称空间。如果在安装过程中使用了不同的值,请使用所使用的值替换istio-system。
此peer authentication policy将工作负载配置为只接受使用TLS加密的请求。因为它没有为selector字段指定值,所以该策略应用于mesh中的所有工作负载,例如:
$ 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: 000 # 失败
command terminated with exit code 56
sleep.legacy to httpbin.legacy: 200
您会看到请求仍然成功,除了那些来自没有sidecar(sleep)的客户机的请求。sleep.legacy请求到有sidecar代理的服务httpbin。foo或httpbin.bar。这是预期的,因为现在严格要求相互TLS,但没有sidecar的工作负载无法遵守。
早在Istio1.1时,此请求无解。后面版本可通过tls.mode: DISABLE实现访问。
清除 Part1
删除会话中添加的全局peer authentication policy和destination rules:
$ kubectl delete peerauthentication -n istio-system default
对每个namespace或工作负载启用mTLS
namespace范围的policy
要为特定名称空间内的所有工作负载更改mTLS,请使用名称空间范围的策略。策略的规范与mesh-wide策略的规范相同,但是要在元数据下指定它应用的名称空间。例如,以下对等身份验证策略为foo命名空间启用严格的mTLS:
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
namespace: "foo"
spec:
mtls:
mode: STRICT
EOF
由于此策略仅应用于名称空间foo中的工作负载,因此您应该只看到没有sidecar的服务(sleep.legacy)到httpbin.foo的请求开始失败。
$ 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
每个工作负载启用mTLS
要为特定工作负载设置peer authentication policy,您必须配置selector部分,并指定匹配所需工作负载的label。但是,Istio不能将出站mTLS通信流的工作负载级策略聚合到服务。配置destination rules来管理该行为。 例如,下面的为httpbin.bar负载启用peer authentication policy和destination rules:
$ cat <<EOF | kubectl apply -n bar -f -
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "httpbin"
namespace: "bar"
spec:
selector:
matchLabels:
app: httpbin
mtls:
mode: STRICT
EOF
---
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
再次运行探测命令。不出所料,请求从 sleep.legacy到httpbin.bar失败也是因为同样的原因。
$ 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: 000
command terminated with exit code 56
sleep.legacy to httpbin.legacy: 200
...
sleep.legacy to httpbin.bar: 000
command terminated with exit code 56
要细化每个端口的相互TLS设置,您必须配置portLevelMtls部分。例如,以下对端认证策略要求在除80端口外的所有端口上相互TLS:
$ cat <<EOF | kubectl apply -n bar -f -
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "httpbin"
namespace: "bar"
spec:
selector:
matchLabels:
app: httpbin
mtls:
mode: STRICT
portLevelMtls:
80:
mode: DISABLE
EOF
和前面一样,你还需要一个目标规则:
$ 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
portLevelSettings:
- port:
number: 8000
tls:
mode: DISABLE
EOF
- PeerAuthentication中的端口值是container的端口。DestinationRule的值是service的端口。
- 如果端口绑定到服务则只能使用portLevelMtls。否则Istio会忽略它.
$ 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的mTLS,您可以测试此行为。例如Foo工作负载。注意,您已经创建了一个名称空间范围的策略,该策略为名称空间foo中的所有服务启用相互TLS,并观察来自sleep.legacy请求httpbin.foo 正在失败(见上文)。
$ cat <<EOF | kubectl apply -n foo -f -
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "overwrite-example"
namespace: "foo"
spec:
selector:
matchLabels:
app: httpbin
mtls:
mode: DISABLE
EOF
---
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) ,确认特定于服务的策略重写了名称空间范围的策略。
$ $ 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
清除Part2
删除上面步骤中创建的策略和目标规则:
$ kubectl delete peerauthentication default overwrite-example -n foo
$ kubectl delete peerauthentication httpbin -n bar
$ kubectl delete destinationrules overwrite-example -n foo
$ kubectl delete destinationrules httpbin -n bar
最终用户身份验证
要试验这个特性,您需要一个有效的JWT。JWT必须与您希望用于演示的JWKS端点相对应。本教程使用Istio代码库中的测试令牌JWT测试和JWKS端点。此外,为了方便起见,请将httpbin.foo设置ingressgateway(有关更多详细信息,请参阅ingressgateway任务)。
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
namespace: foo
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
EOF
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
namespace: foo
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- route:
- destination:
port:
number: 8000
host: httpbin.foo.svc.cluster.local
EOF
按照确定入口IP和端口的说明定义INGRESS_HOST
和INGRESS_PORT
环境变量。然后运行一个测试查询:
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200
现在,添加一个请求身份验证策略,它要求终端用户JWT作为ingressgateway。
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: "jwt-example"
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
jwtRules:
- issuer: "[email protected]"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/jwks.json"
EOF
将策略应用于它选择的工作负载的名称空间,在本例中是ingressgateway。您需要指定的名称空间是isio-system。如果您在授权头中提供了一个令牌(隐式默认位置),Istio将使用公钥集验证令牌,并在持牌人令牌无效时拒绝请求。但是,没有令牌的请求将被接受。要观察这种行为,请在不使用令牌、使用错误令牌和使用有效令牌的情况下重试请求:
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200
$ curl --header "Authorization: Bearer deadbeef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
401
$ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/demo.jwt -s)
curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200
要观察JWT验证的其他方面,可以使用gen-jwt.py脚本生成新的令牌,以使用不同的发行者、受众、到期日期等进行测试。该脚本可以从Istio存储库下载:
$ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/gen-jwt.py
你还需要钥匙。pem文件:
$ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/key.pem
如果您还没有在系统上安装jwcrypto库,请下载它。 JWT认证具有60秒的时钟偏差,这意味着JWT令牌将比其配置的nbf早60秒生效,并在其配置的exp后60秒仍然有效。 例如,下面的命令创建一个在5秒内过期的令牌。如您所见,Istio首先成功地使用该令牌验证请求,但在65秒后拒绝它们:
$ TOKEN=$(python3 ./gen-jwt.py ./key.pem --expire 5)
$ for i in $(seq 1 10); do curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"; sleep 10; done
200
200
200
200
200
200
200
401
401
401
也可以将JWT策略添加到入口网关(例如service istio-ingressgateway.istio-system.svc.cluster.local)。这通常用于为绑定到网关的所有服务(而不是单个服务)定义JWT策略。
需要有一个效令牌
要拒绝没有有效令牌的请求,请添加一个授权策略,其中包含一个规则,该规则为没有请求主体的请求指定拒绝操作,在下面的示例中显示为notRequestPrincipals:[“*”]。请求主体仅在提供有效的JWT令牌时可用。因此,该规则拒绝没有有效令牌的请求。
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "frontend-ingress"
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
action: DENY
rules:
- from:
- source:
notRequestPrincipals: ["*"]
EOF
在不使用令牌的情况下重试请求。请求现在失败,错误代码为403:
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
403
要求每个路径都有有效的令牌
要用每个主机、路径或方法的令牌要求来细化授权,请将授权策略更改为只需要在/headers上使用JWT。当此授权规则生效时,请求$INGRESS_HOST:$INGRESS_PORT/headers失败,错误码为403。对所有其他路径的请求都成功,例如$INGRESS_HOST:$INGRESS_PORT/ip。
kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "frontend-ingress"
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
action: DENY
rules:
- from:
- source:
notRequestPrincipals: ["*"]
to:
- operation:
paths: ["/headers"]
EOF
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
403
$ curl "$INGRESS_HOST:$INGRESS_PORT/ip" -s -o /dev/null -w "%{http_code}\n"
200
清除Part3
删除authentication policy:
$ kubectl -n istio-system delete requestauthentication jwt-example
删除authorization policy:
$ kubectl -n istio-system delete authorizationpolicy frontend-ingress
删除令牌生成器脚本和密钥文件:
$ rm -f ./gen-jwt.py ./key.pem
如果您不打算研究任何后续任务,您可以通过删除测试名称空间来删除所有资源:
$ kubectl delete ns foo bar legacy
本文来至官方:https://istio.io/latest/docs/tasks/security/authentication/authn-policy/#end-user-authentication
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付

comments powered by Disqus