赞
踩
在介绍Istio的认证机制前,先了解一些安全领域的理论知识。传统网络安全架构是基于边界的安全架构,企业构建网络安全体系时,先寻找安全边界,把网络划分为外网、内网、DMZ(DeMilitarized Zone)等不同的区域,然后在边界上部署防火墙、入侵检测等产品。传统的认证是以网络为中心,采用内网信任、边界防护、静态访问控制的机制。
随着云计算、大数据、物联网等新技术与业务的融合,在安全方面提出了新的概念零信任架构(ZTA Zero Trust Architecture),零信任的核心原则是:从不信任、始终验证。安全架构的核心有如下几点
零信任架构三大技术SIM包括
软件定义边界(SDP,Software Defined Perimeter)
SDP旨在使应用程序能在需要时部署安全边界,以便将服务于不安全的网络隔离开。
身份识别与访问管理(IAM,Identity and Access Management)
微隔离(MSG,Micro Segmentation)
微隔离本质上是一种网络安全隔离技术,相比较传统的网络安全隔离,微隔离的区别在于将网络边界分割到尽可能小,这样能缓解传统边界安全理念下的边界过度信任带来的安全风险。
Istio 中的安全性涉及多个组件:
用于密钥和证书管理的证书颁发机构(CA)
配置 API 服务器分发给代理:认证策略/授权策略
安全命名信息
Sidecar和边缘代理作为Policy Enforcement Points(PEPs) 以保护客户端和服务器间的通信安全。
一组Envoy代理扩展,用于管理遥测和审计。Istio PKI 使用 X.509 证书为每个工作负载都提供强大的身份标识。伴随着每个 Envoy 代理的 istio-agent(istio-agent是指sidecar容器中的pilot-agent进程)和 istiod 一起协作来大规模进行自动化密钥和证书轮换。如下图所示完成完成身份的供应。
Istio提供两种类型的认证:
Peer authentication:用于服务到服务的认证,以验证进行连接的客户端。Istio提供双向TLS 作为传输认证的全栈解决方案,无需更改服务代码就可以启用它。
Request authentication:用于最终用户认证,以验证附加到请求的凭据。 Istio使用JSON Web Token(JWT)验证启用请求级认证,并使用自定义认证实现或任何 OpenID Connect 的认证实现来简化的开发人员体验。
安全命名:服务器身份(Server identities)被编码在证书里,但服务名称(service names)通过服务发现或 DNS 被检索。安全命名信息将服务器身份映射到服务名称。身份 A 到服务名称 B 的映射表示“授权 A 运行服务 B“。控制平面监视 apiserver,生成安全命名映射,并将其安全地分发到 PEPs。
上面介绍了概念,接下来通过实际例子演示Istio如何完成认证的。创建三个namespace foo,bar,lagacy,其中foo,bar注入Istio sidecar,且三个namespace下都部署httpbin和sleep服务,这两个服务都来自Istio官网的sample目录下。创建对象完成后,查看bar下面的应用,可以看到是2/2,说明已经注入了Sidecar。接着尝试通过命令让bar namespace下的sleep应用通过curl命令访问httpbin应用,可以看到成功返回了200.
接着在三个namespace下让sleep服务访问httpbin服务。访问命令如下,也全部访问成功
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
接着查看是否存在peerauthentication对象和destinationRules,都不存在。查看httpbin/headers,可以看到header里面有Client-Cert信息。
接着直接在主机上通过httpbin pod的IP地址访问headers信息,可以看到无Cert信息。
通过上面的例子可以看到对于注入了Istio sidecard的应用来说,自动启动了mTLS的双向认证机制。接下来创建一个PeerAuthentication对象,模式是STRICT模式,PeerAuthentication的模式有三种,三种模式的区别如下所示:
PERMISSIVE:工作负载接受双向 TLS 和纯文本流量。此模式在迁移因为没有 sidecar 而无法使用双向 TLS 的工作负载的过程中非常有用。一旦工作负载完成 sidecar 注入的迁移,应将模式切换为 STRICT。
STRICT: 工作负载仅接收双向 TLS 流量。
DISABLE:禁用双向 TLS。 从安全角度来看,除非您提供自己的安全解决方案,否则请勿使用此模式。
- kubectl apply -f - <<EOF
- apiVersion: security.istio.io/v1beta1
- kind: PeerAuthentication
- metadata:
- name: "default"
- namespace: "istio-system"
- spec:
- mtls:
- mode: STRICT
- EOF
因为上述PeerAuthentication对象是创建在istio-system namespaces下面,即Istio的根namespace下,所以是全局生效。生效后再次用三个namespace下的sleep应用访问httpbin服务。可以看到legacy下的sleep服务访问foo,bar下的httpbin服务都失败了,因为legacy没有注入Istio sidecar,所以是纯文本流量,现在采用STRICT模式,所以会失败。Istio默认是采用PERMISSIVE。
接着在foo namespace下创建新的PeerAuthentication,对于访问httpbin的流量DISABLE,即禁用双向TLS。
- 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
再次查看服务之间相互访问情况,可以看到Legacy下面的sleep应用访问foo下面的httpbin应用重新通了,只有bar下面的不同。因为上面的PA是创建在foo namespace。
通过上面的例子演示,可以看到可以通过配置PeerAuthentication来精细化控制服务间的隔离,即实现微隔离,也符合SDP原则,即应用自身来确定部署的安全边界。
上面介绍了PeerAuthentication(主要用户客户端和服务端都受Istio管理的场景),接着来看看Request Authentication(用户客户端不受Istio管理,无法使用mTLS的场景).。
Request 认证策略指定验证 JSON Web Token(JWT)所需的值。 这些值包括:
token 在请求中的位置
请求的 issuer
公共 JSON Web Key Set(JWKS)
Istio会根据request认证策略中的规则检查提供的令牌(如果已提供),并拒绝令牌无效的请求。当请求不带有令牌时,默认情况下将接受它们。接着通过实际例子来看看Request Authentication如何工作。首先通过gateway把httpbin的服务暴露出来。
- 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
接着创建RequestAuthentication对象。
- 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: "testing@secure.istio.io"
- jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.14/security/tools/jwt/samples/jwks.json"
- EOF
在Istio的官网上提供了生成token的python代码,可以通过这个脚本生成有效的token。通过ingress svc的IP地址访问服务,如果不带token,默认是接收请求。
可以看到不带token的情况下,访问成功。
通过Istio官网提供的脚本生成有效的token,生成有效token官网提供了两种方式。
- 方式一
- TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/demo.jwt -s)
- curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
-
- 方式二
- pip install jwcrypto
- TOKEN=$(./gen-jwt.py key.pem --expire=300 --iss "new-issuer@secure.istio.io")
带着有效token访问ingress可以看到返回了200.如果把token替换成任意的字符串,返回了401,说明认证不通过。
上面的例子中如果访问服务不带token则默认接受,这里通过生成AuthoriztionPolicy对象来控制如果不带token则拒绝请求。
- 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
AuthorizationPolicy对象生效后,不带token访问则refused,带上有效token访问,仍然能成功访问成功。关于AuthorizationPolicy的使用会在下一篇博客中详细介绍。
通过上面的例子演示 AuthenticationRequest的工作过程。可以看到如果不受Istio管控的服务,通过AuthenticationRequest可以完成认证管控,保证应用安全。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。