Istio安全-授权(实操三)

Istio安全-授权

授权HTTP流量

本节展示如何在istio网格中授权HTTP流量。

部署Bookinfo。由于下例在策略中使用了principal和namespace,因此需要启用mutual TLS。

为使用HTTP流量的负载配置访问控制

本任务展示了如何使用istio的授权设置访问控制。首先,使用简单的deny-all策略拒绝所有到负载的请求,然后增量地授权到负载的访问。

下面使用kubernetes的service account授权istio网格中的HTTP访问

  1. 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
    

    刷新Boofinfo productpage的页面,可以看到错误RBAC: access denied,即deny-all策略已经生效,且istio没有其他规则允许流量访问网格中的负载。

  2. 执行如下命令创建一个 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
    

    再次刷新Boofinfo productpage的页面,此时可以看到Bookinfo Sample页面,但该页面同时也显示了如下错误:

    • Error fetching product details
    • Error fetching product reviews

    这些错误符合预期,因为并没给productpage负载授权访问detailsreviews负载。下面,需要配置一个策略来授权访问这些负载。

  3. 执行如下命令创建details-viewer策略来允许productpage负载使用 cluster.local/ns/default/sa/bookinfo-productpage service account发起GET请求访问details

    productpage的deployment中可以看到它使用了service account bookinfo-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
    
  4. 执行如下命令创建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
    

    刷新Bookinfo productpage,可以在Bookinfo Sample页面的左边看到Book Details,并在页面右侧看到Book Reviews,但在Book Reviews一栏可以看到错误Ratings service currently unavailable

    这是因为reviews负载没有权限访问ratings负载。为了解决这个问题, 需要授权reviews负载访问ratings负载。下面配置一个策略来授权reviews负载进行访问。

  5. 运行下面命令创建策略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
    

    刷新Bookinfo productpage页面,可以在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中部署两个名为sleeptcp-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负载的访问控制

  1. 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
    
  2. 使用如下命令校验允许到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
    
  3. 使用如下命令校验允许到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
    
  4. 校验到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
    
  5. 使用如下命令将策略更新为仅在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
    
  6. 校验到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
    
  7. 校验到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
    
  8. 使用如下命令将策略更新为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
    
  9. 校验到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
    
  10. 校验到端口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命名空间中部署部署两个负载:httpbinsleep。两个负载都运行了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允许请求

  1. 使用如下命令在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进行校验

  2. 校验带无效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
    
  3. 校验不带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
    
  4. 下面命令会给foo命名空间中的httpbin负载创建require-jwt授权策略。该策略会要求所有的请求都必须包含一个有效的JWT(requestPrincipaltesting@secure.istio.io/testing@secure.istio.io)。Istio通过将JWT token的isssub与一个/分隔符组合来构造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
    
  5. 获取key为isssub,且值(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"}
    
  6. 校验带有效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
    
  7. 校验带无效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
    
  8. 如下命令会更新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
    
  9. 获取将 groups claim设置为字符串列表的JWT: group1group2:

    # 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,"scope":["scope1","scope2"],"sub":"testing@secure.istio.io"}
    
  10. 校验在请求的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
    
  11. 校验请求的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.0OIDC 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 全部属性

参考

  1. 基于OIDC实现istio来源身份验证
  2. JWT 授权
  3. JWT Claims

使用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

明确拒绝一个请求

  1. 下面命令会为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
    
  2. 校验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
    
  3. 校验允许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
    
  4. 更新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
    
  5. 校验当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
    
  6. 校验当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
    
  7. 下面命令会创建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
    
  8. 校验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
    
  9. 校验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
    
  10. 校验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_HOSTINGRESS_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列表

  1. 下面命令会为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
    
  2. 校验到ingress网关的请求被拒绝了

    # curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
    403
    
  3. 更新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
    
  4. 校验到ingress网关的请求变为允许

    # curl "$INGRESS_HOST":"$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
    200
    
  5. 更新 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
    
  6. 校验到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(除非在安装时指定了其他域)。

部署

  1. 使用用户信任域安装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
    
  2. default命名空间中部署httpbin,并在defaultsleep-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
    
  3. 配置如下授权策略,拒绝除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中的sleephttpbin的请求,该请求被拒绝

# 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中的sleephttpbin的请求,该请求被允许

# 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

不使用信任域别名迁移信任域

  1. 使用新的信任域安装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
    
  2. 重新部署httpbin和sleep,使其接收来自新的istio控制面的变更

    $ kubectl delete pod --all
    $ kubectl delete pod --all -n sleep-allow
    
  3. 校验defaultsleep-allow命名空间的sleephttpbin的请求都被拒绝了

    # 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命名空间中的sleephttpbin就被拒绝了。在istio 1.4之前需要手动修改授权策略来使之正常工作,现在有了更加方便的方式。

    迁移信任域,不使用信任域别名

使用信任域别名迁移信任域

  1. 使用新的信任域和信任域别名安装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
    
  2. 不修改任何授权策略,校验到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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


istio的授权功能,也称为基于角色的访问控制(RBAC),它为istio服务网格中的服务提供命名空间级别、服务级别和方法级别的访问控制。基于角色的访问控制具有简单易用、灵活和高性能等特性。本文介绍如何在服务网格中为服务进行授权控制。·前置条件·•安装istio的k8s集群,启用认证功能、
Errorfromserver(Forbidden):errorwhencreating"oot/istio.yaml":configmaps"istio-galley-configuration"isforbidden:unabletocreatenewcontentinnamespaceistio-systembecauseitisbeingterminatedErrorfromserver(Forbid
3.1Istio的核心组件及其功能Istio总体分两部分:控制面和数据面。数据面(sidecar):sidecar通过注入的方式和业务容器共存于一个pod,会劫持业务容器的流量,并接受控制面组件的控制,同时会向控制面输出日志、跟踪以及监控数据。控制面:Istio的核心,管理Istio的所有功能。
在Istio中,双向TLS是传输身份验证的完整堆栈解决方案,它为每个服务提供可跨集群的强大身份、保护服务到服务通信和最终用户到服务通信,以及提供密钥管理系统。本文阐述如何在不中断通信的情况下,把现存Istio服务的流量从明文升级为双向TLS。使用场景 在部署了Istio的集群中,使用人员
在之前的最佳实践中,已经带大家通过一系列的实践任务领略了Istio的无穷魅力。今天,将向大家介绍如何用Istio实现流量熔断。熔断机制是创建弹性微服务应用程序的重要模式。熔断可以帮助您自由控制故障影响的范围、网络延迟的峰值以及抵御其他一些来自外部的恶意***等场景。在接下来
流量镜像流量镜像,也称为影子流量,流量镜像提供一种尽可能低的风险为生产带来变化的强大功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。在非生产或者测试环境中,尝试访问一个服务所有可能的测试用例组合是个非常不现实的任务。在某些情况下
一、负载均衡算法原理与实战负载均衡算法(loadbalancingalgorithm),定义了几种基本的流量分发方式,在Istio中一共有4种标准负载均衡算法。•Round_Robin:轮询算法,顾名思义请求将会依次发给每一个实例,来共同分担所有的请求。•Random:随机算法,将所有的请求随机分发给健康的实例•
本文整理自华为CloudBU技术专家在K8S技术社上关于Istio调用链的分享。前言大家好,我是idouba,来自华为CloudBU,当前在做Istio服务网格在华为云容器服务的产品化工作。今天跟大家分享的主题是Istio调用链相关内容。通过剖析Istio的架构机制与Istio中调用链的工作原理来解答一个大
今天,我们就来谈谈Istio主打功能---保护服务。那么,便引出3个问题:Istio凭什么保护服务?Istio具体如何保护服务?如何告诉Istio发挥保护能力?Istio凭什么保护服务?将单体应用程序分解为一个个服务,为大型软件系统的开发和维护带来了诸多好处,比如更好的灵活性、可伸缩性和可复用性
istio-opentracing链路追踪方案istio-opentracing链路追踪主要是由sidecar(envoy)支持的,istio只是在上层进行配置的修改。envoy链路追踪envoy主要用三个功能来支撑系统范围内的跟踪生成RequestID:envoy会在需要的时候生成UUID,并操作名为[x-request-id]的HTTPHeader。应用可
在前面的文章中,大家都已经熟悉了Istio的故障注入和流量迁移。这两个方面的功能都是Istio流量治理的一部分。今天将继续带大家了解Istio的另一项功能,关于请求超时的管理。首先我们可以通过一个简单的Bookinfo的微服务应用程序来动手实践一下Istio是如何实现请求超时的管理。看过ido
调用链原理和场景正如ServiceMesh的诞生是为了解决大规模分布式服务访问的治理问题,调用链的出现也是为了对应于大规模的复杂的分布式系统运行中碰到的故障定位定界问题。大量的服务调用、跨进程、跨服务器,可能还会跨多个物理机房。无论是服务自身问题还是网络环境的问题导致调用
在Istio中,双向TLS是传输身份验证的完整堆栈解决方案,它为每个服务提供可跨集群的强大身份、保护服务到服务通信和最终用户到服务通信,以及提供密钥管理系统。本文阐述如何在不中断通信的情况下,把现存Istio服务的流量从明文升级为双向TLS。使用场景在部署了Istio的集群中,使用人员刚
前言在Istio的世界里,如果想把外部的请求流量引入网格,你需要认识并会学会配置IstioIngressGateway什么是IngressGateway由于KubernetesIngressAPI只能支持最基本的HTTP路由,使用KubernetesIngress资源来配置外部流量的方式不能满足需求。因此Istiov1alpha3routingAPI引
Istio是什么?使用云平台可以为组织提供丰富的好处。然而,不可否认的是,采用云可能会给DevOps团队带来压力。开发人员必须使用微服务已满足应用的可移植性,同时运营商管理了极其庞大的混合和多云部署。Istio允许您连接、保护、控制和观测服务。在较高的层次上,Istio有助于降低这些
Istio利用k8s的探针对service进行流量健康检查,有两种探针可供选择,分别是liveness和readiness:liveness探针用来侦测什么时候需要重启容器。比如说当liveness探针捕获到程序运行时出现的一个死锁,这种情况下重启容器可以让程序更容易可用。readiness探针用来使容器准备好接收流量。
摘要使用Istio可以很方便地实现微服务间的访问控制。本文演示了使用Denier适配器实现拒绝访问,和Listchecker适配器实现黑白名单两种方法。使用场景有时需要对微服务间的相互访问进行控制,比如使满足某些条件(比如版本)的微服务能够(或不能)调用特定的微服务。访问控制属于策略
导读目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究竟解决了什么问题,为什么它们能够一鸣惊人。随着微服务理念
注:以下讲述的按理环境场景是基于Kubernetes环境基础上部署的Istio环境。涉及到Envoy概念介绍请参考深度解析Istio系列之流量控制篇。本文重点针对Envoy初始化场景进行拆解。Istio-proxy(Envoy)作为Istio数据平面的重要组件,基于sidecar方式与业务应用混合部署到同一pod,为应用提
使用云平台可以为组织提供丰富的好处。然而,不可否认的是,采用云可能会给DevOps团队带来压力。开发人员必须使用微服务以满足应用的可移植性,同时运营商管理了极其庞大的混合和多云部署。Istio允许您连接、保护、控制和观测服务。在较高的层次上,Istio有助于降低这些部署的复杂性,