通过这个task,你将学会如何:
- 使用认证策略建立相互TLS
- 使用认证策略进行最终用户的认证
Before you begin
- 理解istio authentication policy 和相关的 mutual TLS authentication 概念。
- 有一个安装Istio的k8s集群,没开启全局相互TLS(如使用
install/kubernetes/istio-demo.yaml
,或者使用 Helm 设置global.mtls.enabled
为false)。 - 对于演示,创建两个命名空间
foo
和bar
,并部署带有sidecar的 httpbin 和 sleep 。同时,运行一个没有sidecar的sleep 应用(为了保持分离,在命名空间legacy
上运行)
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/sleep/sleep.yaml -n legacy
- 通过发送http请求(使用curl命令)从任何sleep pod(命名空间
foo
,bar
orlegacy
的那些)到httpbin.foo
或httpbin.bar
来验证设置。所有请求都应该成功返回HTTP code 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"
200
方便的是,这行命令遍历所有组合:
for from in "foo" "bar" "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.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.legacy to httpbin.foo: 200
sleep.legacy to httpbin.bar: 200
- 同时确认系统中没有认证策略
kubectl get policies.authentication.istio.io --all-namespaces
No resources found.
Enable mutual TLS for all services in a namespace
运行这个命令来为命名空间foo
设置命名空间级策略:
cat <<EOF | istioctl create -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "example-1"
namespace: "foo"
spec:
peers:
- mtls:
EOF
验证策略已添加:
kubectl get policies.authentication.istio.io -n foo
NAME AGE
example-1 1m
添加这个目标规则来配置客户端使用相互TLS:
cat <<EOF | istioctl create -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "example-1"
namespace: "foo"
spec:
host: "*.foo.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF
注意: 这个规则假定系统中没有任何其他目标规则。如果不是这种情况,你需要相应修改存在规则的流量策略。 *.foo.svc.cluster.local
匹配所有在命名空间foo
的服务。 在Istio相互TLS模式下,Istio将根据其内部实现为密钥和证书(如clientCertificate
, privateKey
and caCertificates
)来设置路径。
运行上面相同的测试命令。你应该看到从 sleep.legacy
到httpbin.foo
的请求开始失败,这是 httpbin.foo
启动相互TLS但是sleep.legacy
没有sidecar支持的结果。另一方面,对于使用sidecar(sleep.foo
and sleep.bar
)的客户端,Istio自动将它们配置为在与 http.foo
交流时使用互相TLS,以便它们继续工作。此外,对 httpbin.bar
的请求没有影响,因为策略仅在命名空间 foo
上有效。
for from in "foo" "bar" "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.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.legacy to httpbin.foo: 000
command terminated with exit code 56
sleep.legacy to httpbin.bar: 200
如果上面的目标规则没有创建,所有到 httpbin.foo
的请求都将失败,因为客户端没有正确配置切换到使用互相TLS。
Enable mutual TLS for single service httpbin.bar
运行这个命令仅为httpbin.bar
服务设置另一个策略。注意这个例子中,我们没有指定命名空间,但是在命令行中使用了 (-n bar
)。效果相同
cat <<EOF | istioctl create -n bar -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "example-2"
spec:
targets:
- name: httpbin
peers:
- mtls:
EOF
目标规则:
cat <<EOF | istioctl create -n bar -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "example-2"
spec:
host: "httpbin.bar.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF
再次运行探测命令。正如所料,从 sleep.legacy
到 httpbin.bar
的请求由于相同原因开始失败。
...
sleep.legacy to httpbin.bar: 000
command terminated with exit code 56
如果我们在命名空间 bar
中有更多服务,我们应该观察出到它们的流量未受影响。我们没有增加更多服务来演示这种行为,而是稍微修改了这个策略。
cat <<EOF | istioctl replace -n bar -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "example-2"
spec:
targets:
- name: httpbin
ports:
- number: 1234
peers:
- mtls:
EOF
对应目标规则的改动
cat <<EOF | istioctl replace -n bar -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "example-2"
spec:
host: httpbin.bar.svc.cluster.local
trafficPolicy:
tls:
mode: DISABLE
portLevelSettings:
- port:
number: 1234
tls:
mode: ISTIO_MUTUAL
EOF
新策略仅对于 httpbin
服务的1234
端口生效。结果, 8000
端口的互相TLS(再次)失效,从 sleep.legacy
来的请求将再次工作。
kubectl exec $(kubectl get pod -l app=sleep -n legacy -o jsonpath={.items..metadata.name}) -c sleep -n legacy -- curl http://httpbin.bar:8000/ip -s -o /dev/null -w "%{http_code}\n"
200
Having both namespace-level and service-level policies
假设我们已经添加了命名空间级的策略,它为命名空间中foo
的所有服务开启互相TLS,并观察从 sleep.legacy
到httpbin.foo
的请求失败(见上文)。现在添加另一个策略,专门为httpbin
服务禁用相互TLS(peers部分为空)
cat <<EOF | istioctl create -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "example-3"
spec:
targets:
- name: httpbin
EOF
目标规则
cat <<EOF | istioctl create -n foo -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "example-3"
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
Setup end-user authentication
你将需要一个有效的JWT(对应于你想要用于演示的JWKS端点)。请遵循指示 here 创建一个。你同样可以在demo中使用你自己的JWT/JWKS 端点。一旦你拥有了,导出了一些环境变量。
export SVC_ACCOUNT="example@my-project.iam.gserviceaccount.com"
export JWKS=https://www.googleapis.com/service_accounts/v1/jwk/${SVC_ACCOUNT}
export TOKEN=<YOUR-TOKEN>
同样,为了便利,通过ingress暴露 httpbin.foo
(更多细节,查看 ingress task)
cat <<EOF | kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: httpbin-ingress
namespace: foo
annotations:
kubernetes.io/ingress.class: istio
spec:
rules:
- http:
paths:
- path: /headers
backend:
serviceName: httpbin
servicePort: 8000
EOF
获得ingress IP
export INGRESS_HOST=$(kubectl get ing -n foo -o=jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
运行一个测试查询
curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
200
现在,让我们为 httpbin.foo
添加一个需要最终用户JWT的策略。下一个命令假定名称为“httpbin”的策略已经存在(如果你按照前面的部分照做了就应该是这样)。你可以运行 kubectl get policies.authentication.istio.io -n foo
来确认,如果找不到资源,则使用istio create
(而不是istio replace
)。同样注意在这个策略中,peer认证(相互TLS)也被设置,尽管可以在不影响原始验证设置的情况下将其删除。
cat <<EOF | istioctl replace -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "example-3"
spec:
targets:
- name: httpbin
peers:
- mtls:
origins:
- jwt:
issuer: $SVC_ACCOUNT
jwksUri: $JWKS
principalBinding: USE_ORIGIN
EOF
之前同样的curl命令将返回401错误码,原因是服务器期待JWT,但没有提供:
curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
401
附加上面生成的有效标记返回成功:
curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
200
你或许想要尝试修改token或策略(如修改发行人,听众,到期日等)来观察JWT验证其他方面。
Cleanup
移除所有资源
kubectl delete ns foo bar legacy