Istio安全-授权(实操三)
授权HTTP流量
本节展示如何在istio网格中授权HTTP流量。
部署Bookinfo。由于下例在策略中使用了principal和namespace,因此需要启用mutual TLS。
为使用HTTP流量的负载配置访问控制
本任务展示了如何使用istio的授权设置访问控制。首先,使用简单的deny-all
策略拒绝所有到负载的请求,然后增量地授权到负载的访问。
下面使用kubernetes的service account授权istio网格中的HTTP访问
- 在
default
命名空间中创建deny-all
策略。该策略没有selector
字段,将会应用到default命名空间中的所有负载上。sepc:
字段为空{}
,意味着禁止所有的流量。 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: deny-all namespace: default #禁止任何对default命名空间中的负载的访问 spec: {} EOF 刷新Boofinfoproductpage
的页面,可以看到错误RBAC: access denied
,即deny-all
策略已经生效,且istio没有其他规则允许流量访问网格中的负载。 - 执行如下命令创建一个
productpage-viewer
允许使用GET
方法访问productpage
负载。该策略并没有设置from字段,意味着允许所有用户和工作负载进行访问: $ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "AuthorizationPolicy" metadata: name: "productpage-viewer" namespace: default spec: selector: matchLabels: app: productpage #允许对default命名空间中的负载productpage的GET访问 rules: - to: - operation: methods: ["GET"] EOF 再次刷新Boofinfoproductpage
的页面,此时可以看到Bookinfo Sample
页面,但该页面同时也显示了如下错误:Error fetching product details
Error fetching product reviews
这些错误符合预期,因为并没给
productpage
负载授权访问details
和reviews
负载。下面,需要配置一个策略来授权访问这些负载。 - 执行如下命令创建
details-viewer
策略来允许productpage
负载使用cluster.local/ns/default/sa/bookinfo-productpage
service account发起GET
请求访问details
: 在productpage
的deployment中可以看到它使用了service accountbookinfo-productpage
$ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "AuthorizationPolicy" metadata: name: "details-viewer" namespace: default spec: selector: matchLabels: app: details rules: - from: - source: principals: ["cluster.local/ns/default/sa/bookinfo-productpage"] #允许使用default命名空间中的sa bookinfo-productpage访问details to: - operation: methods: ["GET"] EOF - 执行如下命令创建
reviews-viewer
策略来允许productpage
负载使用cluster.local/ns/default/sa/bookinfo-productpage
service account发起GET
请求访问reviews
: $ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "AuthorizationPolicy" metadata: name: "reviews-viewer" namespace: default spec: selector: matchLabels: app: reviews rules: - from: - source: principals: ["cluster.local/ns/default/sa/bookinfo-productpage"] #允许使用default命名空间中的sa bookinfo-productpage访问reviews,信任域为cluster.local to: - operation: methods: ["GET"] EOF 刷新Bookinfoproductpage
,可以在Bookinfo Sample
页面的左边看到Book Details
,并在页面右侧看到Book Reviews
,但在Book Reviews
一栏可以看到错误Ratings service currently unavailable
。 这是因为reviews
负载没有权限访问ratings
负载。为了解决这个问题, 需要授权reviews
负载访问ratings
负载。下面配置一个策略来授权reviews
负载进行访问。 - 运行下面命令创建策略
ratings-viewer
来允许reviews
负载使用cluster.local/ns/default/sa/bookinfo-reviews
service account发起GET
请求访问ratings
: $ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "AuthorizationPolicy" metadata: name: "ratings-viewer" namespace: default spec: selector: matchLabels: app: ratings rules: - from: - source: principals: ["cluster.local/ns/default/sa/bookinfo-reviews"] to: - operation: methods: ["GET"] EOF 刷新Bookinfoproductpage
页面,可以在Book Reviews
一栏中看到黑色和红色的ratings信息。
卸载
$ kubectl delete authorizationpolicy.security.istio.io/deny-all
$ kubectl delete authorizationpolicy.security.istio.io/productpage-viewer
$ kubectl delete authorizationpolicy.security.istio.io/details-viewer
$ kubectl delete authorizationpolicy.security.istio.io/reviews-viewer
$ kubectl delete authorizationpolicy.security.istio.io/ratings-viewer
授权TCP流量
本节展示如何授权istio网格中的TCP流量。
部署
- 在同一个命名空间
foo
中部署两个名为sleep
和tcp-echo
的负载,两个负载都允许了Envoy代理。tcp-echo
负载监听端口9000,9001和9002,并回显收到的所有带有前缀hello的流量。例如,如果发送"world"到tcp-echo
,则会响应"hello world"。tcp-echo
kubernetes service对象仅声明端口9000和9001,并忽略端口9002。pass-through过滤器将处理端口9002的流量。使用如下命令部署命名空间和负载: openshift注意创建NetworkAttachmentDefinition $ kubectl create ns foo $ kubectl apply -f <(istioctl kube-inject -f samples/tcp-echo/tcp-echo.yaml) -n foo $ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo - 使用如下命令校验
sleep
可以通过9000和9001端口连接tcp-echo
# kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9000 connection succeeded # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded - 校验
sleep
可以连接tcp-echo
的9002端口。此时需要通过tcp-echo的pod IP发送流量,这是因为tcp-echo的kubernetes service对象中并没有定义9002端口。获取pod IP并使用如下命令发送请求: # TCP_ECHO_IP=$(kubectl get pod "$(kubectl get pod -l app=tcp-echo -n foo -o jsonpath={.items..metadata.name})" -n foo -o jsonpath="{.status.podIP}") # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c "echo "port 9002" | nc $TCP_ECHO_IP 9002" | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' [root@bastion istio-1.7.0]# kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c "echo "port 9002" | nc $TCP_ECHO_IP 9002" | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9002 connection succeeded 可以看到tcp-echo的k8s service仅暴露了9000和9001端口: # oc get svc -n foo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE sleep ClusterIP 10.84.92.66 <none> 80/TCP 4m31s tcp-echo ClusterIP 10.84.85.246 <none> 9000/TCP,9001/TCP 4m32s
配置TCP负载的访问控制
- 为
foo
命名空间中的tcp-echo
负载创建tcp-policy
授权策略,运行如下命令创建一个授权策略,允许到9000和9001的请求: $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo #对匹配标签的负载允许到端口9000/9001的访问 action: ALLOW rules: - to: - operation: ports: ["9000", "9001"] EOF - 使用如下命令校验允许到9000的访问 此时即使没有上面的策略,到9000/9001的访问都是允许的。因为istio默认使用宽容模式,区别是如果该服务暴露的不止9000/9001端口,那么其他端口是无法访问的。 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9000 connection succeeded
- 使用如下命令校验允许到9001的访问 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded
- 校验到9002的请求是拒绝的。这是由授权策略执行的,该策略也适用于pass through过滤器链,即使
tcp-echo
kubernetes service对象没有明确声明该port。 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c "echo "port 9002" | nc $TCP_ECHO_IP 9002" | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' command terminated with exit code 1 connection rejected - 使用如下命令将策略更新为仅在9000端口上允许HTTP的GET方法 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: ALLOW rules: - to: - operation: methods: ["GET"] ports: ["9000"] EOF
- 校验到9000的端口的请求被拒绝了。这是因为此时规则仅允许HTTP格式的TCP流量。istio会忽略无效的ALLOW规则。最终结果是由于请求不匹配任何ALLOW规则,而被被拒绝。执行如下命令进行校验: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
- 校验到9001的请求被拒绝了。原因同样是因为请求并没有匹配任何ALLOW规则 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
- 使用如下命令将策略更新为DENY,拒绝到9000的请求(当然此时其他端口会走默认的宽容模式) $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: DENY rules: - to: - operation: methods: ["GET"] ports: ["9000"] EOF
- 校验到9000端口的请求被拒绝。它与上面无效的ALLOW规则(istio忽略了整个规则)不同,istio忽略了仅支持HTTP的字段
methods
,但使用了ports
,导致匹配到这个端口的请求被拒绝: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected - 校验到端口9001的请求是允许的,这是因为请求并不匹配DENY策略的
ports
字段 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded
卸载
$ kubectl delete namespace foo
action字段中的默认值为ALLOW,且ALLOW中的规则的关系是
AND
,而DENY中的规则的关系是OR
使用JWT进行授权
本任务展示如何设置istio授权策略来执行基于JSON Web Token(JWT)的访问。Istio授权策略同时支持字符串类型和字符串列表类型的JWT claims。
部署
在foo
命名空间中部署部署两个负载:httpbin
和sleep
。两个负载都运行了Envoy代理。使用如下命令进行部署:
$ 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
校验可以使用sleep
连接httpbin
:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}n"
200
使用有效的JWT和列表类型的claims允许请求
- 使用如下命令在
foo
命名空间中为httpbin
负载创建jwt-example
请求认证策略。该策略会让httpbin
负载接受一个由testing@secure.istio.io
发布的JWT。 $ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "RequestAuthentication" metadata: name: "jwt-example" namespace: foo spec: selector: matchLabels: app: httpbin jwtRules: - issuer: "testing@secure.istio.io" jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.7/security/tools/jwt/samples/jwks.json" EOF jwksUri为开放公钥的接口地址,用于获取公钥,进而对token进行校验 - 校验带无效JWT的请求被拒绝了: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer invalidToken" -w "%{http_code}n" 401
- 校验不带JWT的请求是允许的,因为此时没有配置授权策略 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -w "%{http_code}n" 200
- 下面命令会给
foo
命名空间中的httpbin
负载创建require-jwt
授权策略。该策略会要求所有的请求都必须包含一个有效的JWT(requestPrincipal
为testing@secure.istio.io/testing@secure.istio.io
)。Istio通过将JWT token的iss
和sub
与一个/分隔符组合来构造requestPrincipal
: $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: foo spec: selector: matchLabels: app: httpbin action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] EOF - 获取key为
iss
和sub
,且值(testing@secure.istio.io
)相同的JWT。这会导致istio生成属性requestPrincipal
,对应值为testing@secure.istio.io/testing@secure.istio.io
: # TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.7/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - {"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"} - 校验带有效JWT的请求是允许的: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}n" 200
- 校验带无效JWT的请求被拒绝: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -w "%{http_code}n" 403
- 如下命令会更新
require-jwt
授权策略,要求JWT具有一个名为groups
的claim,且置为group1
: $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: foo spec: selector: matchLabels: app: httpbin action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] when: - key: request.auth.claims[groups] values: ["group1"] EOF - 获取将
groups
claim设置为字符串列表的JWT:group1
和group2
: # TOKEN_GROUP=$(curl https://raw.githubusercontent.com/istio/istio/release-1.7/security/tools/jwt/samples/groups-scope.jwt -s) && echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode - {"exp":3537391104,"groups":["group1","group2"],"iat":1537391104,"iss":"testing@secure.istio.io","scope":["scope1","scope2"],"sub":"testing@secure.istio.io"} - 校验在请求的JWT的
groups
claim中带group1
是允许的: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN_GROUP" -w "%{http_code}n" 200 - 校验请求的JWT中没有
groups
claim是被拒绝的 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}n" 403
卸载
$ kubectl delete namespace foo
总结
RFC 7519.定义了JWT token的格式。Istio的JWT认证流程使用了OAuth 2.0 和OIDC 1.0,可以使用jwks字段或jwksUri
字段标识公钥的提供方。
AuthorizationPolicy
rule 规则中与 JWT 相关的字段包括:
field |
sub field |
JWT claims |
---|---|---|
from.source |
requestPrincipals |
iss/sub |
from.source |
notRequestPrincipals |
iss/sub |
when.key |
request.auth.principal |
iss/sub |
when.key |
request.auth.audiences |
aud |
when.key |
request.auth.presenter |
azp |
when.key |
request.auth.claims[key] |
JWT 全部属性 |
参考
使用deny action的授权策略
本节将展示如何授权istio授权策略来拒绝istio网格中的HTTP流量。
部署
部署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 exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}n"
200
明确拒绝一个请求
- 下面命令会为
foo
命名空间中的httpbin
负载创建deny-method-get
授权策略。策略会对满足rules
字段中的条件的请求执行DENY
action
。下面例子会拒绝使用GET
方法的请求 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: deny-method-get namespace: foo spec: selector: matchLabels: app: httpbin action: DENY rules: - to: - operation: methods: ["GET"] EOF - 校验GET请求被拒绝: # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/get" -X GET -s -o /dev/null -w "%{http_code}n" 403
- 校验允许
POST
请求 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/post" -X POST -s -o /dev/null -w "%{http_code}n" 200 - 更新
deny-method-get
授权策略,只有当HTTP首部x-token
的值不为admin
时才会拒绝GET
请求。下面例子将notValues
字段设置为["admin"]
来拒绝首部字段不为admin
的请求。 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: deny-method-get namespace: foo spec: selector: matchLabels: app: httpbin action: DENY rules: - to: - operation: methods: ["GET"] when: - key: request.headers[x-token] notValues: ["admin"] EOF - 校验当HTTP首部字段为
x-token: admin
时是允许的 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/get" -X GET -H "x-token: admin" -s -o /dev/null -w "%{http_code}n" 200 - 校验当HTTP首部字段为
x-token: guest
时是拒绝的 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/get" -X GET -H "x-token: guest" -s -o /dev/null -w "%{http_code}n" 403 - 下面命令会创建
allow-path-ip
授权策略来允许请求通过/ip
路径访问httpbin
负载。通过在action
字段设置ALLOW
实现 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-path-ip namespace: foo spec: selector: matchLabels: app: httpbin action: ALLOW rules: - to: - operation: paths: ["/ip"] EOF - 校验HTTP首部为
x-token: guest
且路径为/ip
的GET请求仍然被deny-method-get
策略拒绝 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -X GET -H "x-token: guest" -s -o /dev/null -w "%{http_code}n" 403 - 校验HTTP首部为
x-token: admin
且路径为/ip
的GET请求仍然被allow-path-ip
策略允许 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -X GET -H "x-token: admin" -s -o /dev/null -w "%{http_code}n" 200 - 校验HTTP首部为
x-token: admin
且路径为/get
的GET请求仍然被拒绝,因为不匹配allow-path-ip
策略 # kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/get" -X GET -H "x-token: admin" -s -o /dev/null -w "%{http_code}n" 403
总结
本节主要展示了授权中的action
字段的用法:DENY
总是优先于ALLOW
,且ALLOW
中的规则的关系是AND
,而DENY
中的规则的关系是OR
。
卸载
$ kubectl delete namespace foo
授权ingress Gateway
本节展示如何在istio ingress网关上使用授权策略配置访问控制。
istio授权策略支持基于IP的allow/deny列表,以及先前由Mixer策略支持的基于属性的allow/deny列表。Mixer策略已经在1.5版本中被废弃,不建议生产使用。
部署
在foo
命名空间中部署一个httpbin
负载,使用istio ingress网关暴露该服务:
$ 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/httpbin/httpbin-gateway.yaml) -n foo
通过设置externalTrafficPolicy:local
来更新ingress网关,使用以下命令在ingress网关上保留原始客户端的源IP。更多参见 Source IP for Services with Type=NodePort
使用
externalTrafficPolicy:local
时,kube-proxy仅会将请求代理到本地endpoint,不会跨节点。如果没有匹配的endpoint,则该会丢弃该报文,此时会保留报文的原始源IP地址(跨节点会使用SNAT将报文原始源IP地址修改为本节点地址)。
$ kubectl patch svc istio-ingressgateway -n istio-system -p '{"spec":{"externalTrafficPolicy":"Local"}}'
获取INGRESS_HOST
和 INGRESS_PORT
$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
校验可以通过ingress网关访问httbin负载
# curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}n"
200
使用如下命令的输出来保证ingress网关接收到了原始客户端的源IP地址,该地址将会在授权策略中使用:
# CLIENT_IP=$(curl "$INGRESS_HOST":"$INGRESS_PORT"/ip -s | grep "origin" | cut -d'"' -f 4) && echo "$CLIENT_IP"
172.20.127.78
基于IP的allow列表和deny列表
- 下面命令会为istio ingress网关创建授权策略
ingress-policy
。下面策略中的action
字段为ALLOW
,允许ipBlocks字段指定的IP地址访问ingress网关。不在该列表中的IP地址会被拒绝。ipBlocks支持单IP地址和CIDR: $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: ingress-policy namespace: istio-system spec: selector: matchLabels: app: istio-ingressgateway action: ALLOW rules: - from: - source: ipBlocks: ["1.2.3.4", "5.6.7.0/24"] #允许访问网关的源地址IP列表 EOF - 校验到ingress网关的请求被拒绝了 # curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}n" 403
- 更新
ingress-policy
,在ALLOW IP地址列表中包含客户端的IP地址 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: ingress-policy namespace: istio-system spec: selector: matchLabels: app: istio-ingressgateway action: ALLOW rules: - from: - source: ipBlocks: ["1.2.3.4", "5.6.7.0/24", "$CLIENT_IP"] EOF - 校验到ingress网关的请求变为允许 # curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}n" 200
- 更新
ingress-policy
授权策略,将action
设置为DENY
,将客户端地址设置到ipBlocks
字段中,此时对ingress的源地址为$CLIENT_IP
的访问将会被拒绝: $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: ingress-policy namespace: istio-system spec: selector: matchLabels: app: istio-ingressgateway action: DENY rules: - from: - source: ipBlocks: ["$CLIENT_IP"] EOF - 校验到ingress网关的请求被拒绝 # curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}n" 403
卸载
$ kubectl delete namespace foo
$ kubectl delete authorizationpolicy ingress-policy -n istio-system
总结
从上面可以看出,在用法上,对ingress网关的授权策略和对其他istio网关内部的服务的授权策略并没有什么不同。
授权策略信任域迁移
本节展示如何在不修改授权策略的前提下进行信任域的迁移。
在istio 1.4中,引入了一个alpha特性来支持对授权策略的信任域的迁移,即如果一个istio网格需要改变其信任域时,则不需要手动修改授权策略。在istio中,如果一个负载运行在foo
命名空间中,使用的service account为bar
,系统的信任域为my-td
,则负载的身份标识为 spiffe://my-td/ns/foo/sa/bar
。默认情况下,istio网格的信任域为cluster.local
(除非在安装时指定了其他域)。
部署
- 使用用户信任域安装istio,并启用mutual TLS # istioctl install -f cni-annotations.yaml --set values.global.istioNamespace=istio-system --set values.gateways.istio-egressgateway.enabled=true --set meshConfig.accessLogFile="/dev/stdout" --set values.global.trustDomain=old-td
- 在
default
命名空间中部署httpbin,并在default
和sleep-allow
命名空间中部署sleep $ kubectl label namespace default istio-injection=enabled $ kubectl apply -f samples/httpbin/httpbin.yaml $ kubectl apply -f samples/sleep/sleep.yaml $ kubectl create namespace sleep-allow $ kubectl label namespace sleep-allow istio-injection=enabled $ kubectl apply -f samples/sleep/sleep.yaml -n sleep-allow - 配置如下授权策略,拒绝除
sleep-allow
命名空间中的sleep
外的对httpbin
的请求。 $ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: service-httpbin.default.svc.cluster.local namespace: default spec: rules: - from: - source: principals: - old-td/ns/sleep-allow/sa/sleep #只有sleep-allow命名空间中的sleep才能访问httpbin服务 to: - operation: methods: - GET selector: matchLabels: app: httpbin --- EOF
校验default
中的sleep
到httpbin
的请求,该请求被拒绝
# kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n"
403
校验sleep-allow
中的sleep
到httpbin
的请求,该请求被允许
# kubectl exec "$(kubectl -n sleep-allow get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -n sleep-allow -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n"
200
不使用信任域别名迁移信任域
- 使用新的信任域安装istio,现在istio网格的信任域为
new-td
# istioctl install -f cni-annotations.yaml --set values.global.istioNamespace=istio-system --set values.gateways.istio-egressgateway.enabled=true --set meshConfig.accessLogFile="/dev/stdout" --set values.global.trustDomain=new-td - 重新部署httpbin和sleep,使其接收来自新的istio控制面的变更 $ kubectl delete pod --all $ kubectl delete pod --all -n sleep-allow
- 校验
default
和sleep-allow
命名空间的sleep
到httpbin
的请求都被拒绝了 # kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n" 403 # kubectl exec "$(kubectl -n sleep-allow get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -n sleep-allow -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n" 403 这是因为在授权策略中拒绝除使用old-td/ns/sleep-allow/sa/sleep
身份标识的所有请求,即sleep-allow
命名空间中的sleep
应用使用的老标识。当迁移到一个新的信任域new-td
之后,该sleep应用的标识变为了new-td/ns/sleep-allow/sa/sleep
,与授权策略不匹配。因此sleep-allow
命名空间中的sleep
到httpbin
就被拒绝了。在istio 1.4之前需要手动修改授权策略来使之正常工作,现在有了更加方便的方式。 迁移信任域,不使用信任域别名
使用信任域别名迁移信任域
- 使用新的信任域和信任域别名安装istio # cat cni-annotations.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: components: cni: enabled: true namespace: kube-system values: meshConfig: trustDomain: new-td trustDomainAliases: - old-td certificates: - secretName: dns.example1-service-account dnsNames: [example1.istio-system.svc, example1.istio-system] - secretName: dns.example2-service-account dnsNames: [example2.istio-system.svc, example2.istio-system] cni: excludeNamespaces: - istio-system - kube-system chained: false cniBinDir: /var/lib/cni/bin cniConfDir: /etc/cni/multus/net.d cniConfFileName: istio-cni.conf sidecarInjectorWebhook: injectedAnnotations: "k8s.v1.cni.cncf.io/networks": istio-cni
- 不修改任何授权策略,校验到
httpbin
的请求 default命名空间中的sleep到httpbin的请求被拒绝 # kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n" 403 sleep-allow命名空间中的sleep到httpbin的请求被允许 # kubectl exec "$(kubectl -n sleep-allow get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -n sleep-allow -- curl http://httpbin.default:8000/ip -s -o /dev/null -w "%{http_code}n" 200
最佳实践
从istio 1.4开始,当编写授权策略时,需要使用cluster.local
作为策略的信任域,例如cluster.local/ns/sleep-allow/sa/sleep
。注意,上面情况下,cluster.local
并不是istio网格的信任域(信任域为old-td
)。但是在授权策略中,cluster.local
是一个指向当前信任域的指针,即old-td
(或后面的 new-td
)。通过在授权策略中使用cluster.local
,当迁移到一个新的信任域时,istio会探测并将新的信任域与就的信任域一视同仁,而无需使用别名。
按照上面的说法,将创建的授权策略的principals字段修改为
cluster.local/ns/sleep-allow/sa/sleep
,重新测试连通性,可以得到与使用别名相同的结果。
卸载
$ kubectl delete authorizationpolicy service-httpbin.default.svc.cluster.local
$ kubectl delete deploy httpbin; kubectl delete service httpbin; kubectl delete serviceaccount httpbin
$ kubectl delete deploy sleep; kubectl delete service sleep; kubectl delete serviceaccount sleep
$ kubectl delete namespace sleep-allow
$ istioctl manifest generate --set profile=demo -f td-installation.yaml | kubectl delete --ignore-not-found=true -f -
$ rm ./td-installation.yaml
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android编程之光线传感器用法详解
- Android Studio 3.0 新功能全面解析和旧项目适配问题
- Android开发中使用外部应用获取SD卡状态的方法
- Android编程使用光线传感器获取光线强弱的方法【LightSensorManager封装类】
- Android开发中的重力传感器用法实例详解
- 腾讯云TKE-Ingress案例: Nginx-Ingress 实现grpc转发
- 机器人软件中间层 yarp-Yet Another Robot Platforms
- 3分钟短文:Laravel写个命令行,你就是下一个Geek!
- Android开发多年每天Crud不清楚自己的技术?来刷刷大厂的高端技术面试题就知道了
- MySQL案例:count(*)效率优化
- MUI进行APP混合开发实现下拉刷新和上拉加载 原创
- Android 给控件添加边框阴影效果
- 详解Android Selinux 权限及问题
- Android图片采样缩放功能实例代码
- Android开发中使用Intent打开第三方应用及验证可用性的方法详解