基于 JWT 声明的路由

本任务将向您展示如何使用 Istio 入口网关上的请求身份验证和虚拟服务,根据 JWT 声明路由请求。

注意:此功能仅支持 Istio 入口网关,需要同时使用请求身份验证和虚拟服务才能正确验证和根据 JWT 声明进行路由。

开始之前

  • 了解 Istio 的 身份验证策略虚拟服务 概念。

  • 使用 Istio 安装指南 安装 Istio。

  • 部署一个工作负载(例如 httpbin)到一个命名空间(例如 foo),并使用以下命令通过 Istio 入口网关将其暴露出来

    压缩压缩
    $ kubectl create ns foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
    $ kubectl apply -f @samples/httpbin/httpbin-gateway.yaml@ -n foo
    
  • 按照 确定入口 IP 和端口 中的说明定义 INGRESS_HOSTINGRESS_PORT 环境变量。

  • 使用以下命令验证 httpbin 工作负载和入口网关是否按预期工作

    $ curl "$INGRESS_HOST:$INGRESS_PORT"/headers -s -o /dev/null -w "%{http_code}\n"
    200
    

基于 JWT 声明配置入口路由

Istio 入口网关支持基于经过身份验证的 JWT 进行路由,这对于根据最终用户身份进行路由很有用,并且与使用未经身份验证的 HTTP 属性(例如路径或标头)相比更安全。

  1. 为了根据 JWT 声明进行路由,首先创建请求身份验证以启用 JWT 验证

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: RequestAuthentication
    metadata:
      name: ingress-jwt
      namespace: istio-system
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      jwtRules:
      - issuer: "[email protected]"
        jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/jwks.json"
    EOF
    

    请求身份验证在 Istio 入口网关上启用 JWT 验证,以便随后在虚拟服务中使用经过验证的 JWT 声明进行路由。

    请求身份验证应用于入口网关,因为基于 JWT 声明的路由仅在入口网关上受支持。

    注意:请求身份验证仅在请求中存在 JWT 时才会检查 JWT。要使 JWT 成为必需项,并在未包含 JWT 时拒绝请求,请应用 任务 中指定的授权策略。

  2. 更新虚拟服务以根据经过验证的 JWT 声明进行路由

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: httpbin
      namespace: foo
    spec:
      hosts:
      - "*"
      gateways:
      - httpbin-gateway
      http:
      - match:
        - uri:
            prefix: /headers
          headers:
            "@request.auth.claims.groups":
              exact: group1
        route:
        - destination:
            port:
              number: 8000
            host: httpbin
    EOF
    

    虚拟服务使用保留的标头 "@request.auth.claims.groups" 与 JWT 声明 groups 进行匹配。前缀 @ 表示它与从 JWT 验证中获取的元数据匹配,而不是与 HTTP 标头匹配。

    支持字符串、字符串列表和嵌套声明类型的声明。使用 .[] 作为嵌套声明名称的分隔符。例如,"@request.auth.claims.name.givenName""@request.auth.claims[name][givenName]" 匹配嵌套声明 namegivenName,它们在这里是等效的。当声明名称包含 . 时,只能使用 [] 作为分隔符。

基于 JWT 声明验证入口路由

  1. 验证入口网关在没有 JWT 时返回 HTTP 代码 404

    $ curl -s -I "http://$INGRESS_HOST:$INGRESS_PORT/headers"
    HTTP/1.1 404 Not Found
    ...
    

    您也可以创建授权策略,在 JWT 丢失时明确拒绝请求,返回 HTTP 代码 403。

  2. 验证入口网关在 JWT 无效时返回 HTTP 代码 401

    $ curl -s -I "http://$INGRESS_HOST:$INGRESS_PORT/headers" -H "Authorization: Bearer some.invalid.token"
    HTTP/1.1 401 Unauthorized
    ...
    

    由于 JWT 验证失败,请求身份验证返回 401。

  3. 验证入口网关是否使用包含声明 groups: group1 的有效 JWT 令牌路由请求

    $ TOKEN_GROUP=$(curl https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/groups-scope.jwt -s) && echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode
    {"exp":3537391104,"groups":["group1","group2"],"iat":1537391104,"iss":"[email protected]","scope":["scope1","scope2"],"sub":"[email protected]"}
    
    $ curl -s -I "http://$INGRESS_HOST:$INGRESS_PORT/headers" -H "Authorization: Bearer $TOKEN_GROUP"
    HTTP/1.1 200 OK
    ...
    
  4. 验证入口网关在使用有效 JWT 但不包含声明 groups: group1 时是否返回 HTTP 代码 404

    $ TOKEN_NO_GROUP=$(curl https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN_NO_GROUP" | cut -d '.' -f2 - | base64 --decode
    {"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"[email protected]","sub":"[email protected]"}
    
    $ curl -s -I "http://$INGRESS_HOST:$INGRESS_PORT/headers" -H "Authorization: Bearer $TOKEN_NO_GROUP"
    HTTP/1.1 404 Not Found
    ...
    

清理

  • 删除命名空间 foo

    $ kubectl delete namespace foo
    
  • 删除请求身份验证

    $ kubectl delete requestauthentication ingress-jwt -n istio-system
    
这些信息对您有用吗?
您有什么改进建议吗?

感谢您的反馈!