使用 istio ingress/gateway 将 JWT 声明复制到标头

发布于 2025-01-10 07:43:35 字数 4973 浏览 0 评论 0原文

我正在尝试使用未记录的DYNAMIC_METADATA 功能来实现添加 JWT 声明作为请求标头,如 此 github 问题评论 和在此 Google 文档功能提案<中作为“现有解决方案”进行了更详细的解释/a>.顺便问一下,是否有一个地方可以跟踪这些功能提案?我想获得有关该提案状态的更多信息,它在路线图上的任何位置吗?

我正在谈论的具体部分是这样的:

解决方案 3:使用 VirtualService 中未记录的功能
Istio VirtualService 支持使用 DYNAMIC_METADATA 键从动态属性设置标头。这可以与 JWT 元数据结合起来,将声明复制到标头。
请参阅下面的示例

api版本:networking.istio.io/v1alpha3
种类:虚拟服务
元数据:
  名称:评论路线
规格:
  主持人:
  -reviews.prod.svc.cluster.local
  http:
  - 标题:
      要求:
        放:
          # 将组声明复制到 x-istio-jwt-group 标头
          x-istio-jwt-组:
            '%DYNAMIC_METADATA(["istio_authn", "request.auth.claims", "group"])%'

作为 POC,我部署了一个应用程序,该应用程序侦听 /echo-header 并回显 whatever-id 的值它收到的任何请求的标头。该应用程序称为web,并且有一个附带的种类:服务,称为web-svc。我配置了多个AuthorizationPolicy 资源和一个RequestAuthentication 资源,以便只能使用有效的 JWT 访问此服务。当我在没有 JWT 或过期 JWT 的情况下发出请求时,我收到 401 错误,这部分工作正常。 VirtualServiceGateway 资源使服务可用于外部请求。到目前为止,一切都很好。

从现在开始,我将复制 JWT 上存在的 whatever-id 声明的值(在 jwt.io) 到 whatever-id 标头中。
然后,我修改了功能提案中提到的VirtualService,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web
spec:
  hosts:
    - example.com
  gateways:
    - web-gateway
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: web-svc
            port:
              number: 80
      headers:
        request:
          set:
            whatever-id: '%DYNAMIC_METADATA(["istio_authn", "request.auth.claims", "whatever-id"])%'
            # whatever-id: "testing"

但这不起作用。我通过注释/取消注释最后两行来验证标头转换部分,然后从我的 POC 应用程序返回测试字符串。但是,当我将配置与应将声明值复制到标头的行一起使用时,标头未在请求中设置。

当我将日志记录设置为rbac:debug时,我可以在发出请求时看到以下日志:

2022-02-23T14:04:05.708454Z     debug   envoy rbac      checking request: requestedServerName: outbound_.80_._.web-svc.route-echo.svc.cluster.local, sourceIP: 172.16.36.22:50722, directRemoteIP: 172.16.36.22:50722, remoteIP: 172.16.36.35:0,localAddress: 172.16.36.57:80, ssl: uriSanPeerCertificate: spiffe://cluster.local/ns/istio-ingress/sa/istio-ingress, dnsSanPeerCertificate: , subjectPeerCertificate: , headers: ':authority', 'example.com'
':path', '/echo-header'
':method', 'GET'
':scheme', 'https'
... <removed irrelevant headers for brevity>
, dynamicMetadata: filter_metadata {
  key: "envoy.filters.http.jwt_authn"
  value {
    fields {
      key: "<redacted issuer url>"
      value {
        struct_value {
          ... <removed all irrelevant claims for brevity>
          fields {
            key: "whatever-id"
            value {
              string_value: "98a5b3ea-f5e7-4012-8cf5-9cfb5cf9e65f"
            }
          }
        }
      }
    }
  }
}
filter_metadata {
  key: "istio_authn"
  value {
    fields {
      key: "request.auth.audiences"
      value {
        string_value: "account"
      }
    }
    fields {
      key: "request.auth.claims"
      value {
        struct_value {
          ... <removed all irrelevant claims for brevity>
          fields {
            key: "whatever-id"
            value {
              list_value {
                values {
                  string_value: "98a5b3ea-f5e7-4012-8cf5-9cfb5cf9e65f"
                }
              }
            }
          }
        }
      }
    }
    fields {
      key: "request.auth.presenter"
      value {
        string_value: "poc"
      }
    }
    fields {
      key: "request.auth.principal"
      value {
        string_value: "<redacted>"
      }
    }
    fields {
      key: "request.auth.raw_claims"
      value {
        string_value: "<redacted>"
      }
    }
  }
}

查看此内容时我注意到的第一件事是dynamicMetadata (我假设可以使用解决方案中提到的 %DYNAMIC_METADATA()% 语法进行访问)不包含istio_authn 字段,它包含的唯一字段是envoy.filters.http.jwt_auth 字段,其键值等于我的 JWT 的颁发者 URL,在属于该密钥的值下,有一个 whatever-id 字段,其中包含我想将其添加到标头中,因此我尝试将 VirtualService 中的标头值更改为:

whatever-id: '%DYNAMIC_METADATA(["envoy.filters.http.jwt_authn", "<issuer url>", "whatever-id"])%'

遗憾的是,这也不起作用。

我还注意到,解决方案中使用的密钥("istio_authn"、"request.auth.claims")可在 filter_metadata 属性下使用日志(与dynamicMetadata 属性相对)。
那么也许可以使用另一种语法来访问这些filter_metadata 属性?

环境详细信息:

  • Kubernetes 版本 1.22.6
  • 使用 Azure AKS
  • Istio 1.13.0 使用 Helm Chart 部署

I'm trying to implement adding JWT claims as request headers, using the undocumented DYNAMIC_METADATA feature as mentioned in this github issue comment and explained in more detail as an 'existing solution' in this google doc feature proposal. By the way, is there a place where these feature proposals are tracked? I would like to get more information about the state of this proposal, is it anywhere on the roadmap?

The specific part I'm talking about this this:

Solution 3: Use an undocumented feature in VirtualService
Istio VirtualService supports setting headers from dynamic attributes using the DYNAMIC_METADATA key. This can be combined with the JWT metadata to copy claims to headers.
See example below

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews-route
spec:
  hosts:
  - reviews.prod.svc.cluster.local
  http:
  - headers:
      request:
        set:
          # Copy the group claim to the x-istio-jwt-group header
          x-istio-jwt-group:
            '%DYNAMIC_METADATA(["istio_authn", "request.auth.claims", "group"])%'

As a POC, I have deployed an app which listens on /echo-header and will echo back the value of the whatever-id header of any request it receives. The app is called web and has an accompanying kind: Service called web-svc. I have configured several AuthorizationPolicy resources and one RequestAuthentication resource to make it so that this service can only be accessed with a valid JWT. This part works as I get a 401 when I make a request without JWT or with expired JWT. A VirtualService and Gateway resource make the service available for external requests. So far so good.

My from from here on out is to copy the value of the whatever-id claim which exists on my JWT (verified on jwt.io) into the whatever-id header.
I Have then modified the VirtualService as mentioned in the feature proposal like so:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web
spec:
  hosts:
    - example.com
  gateways:
    - web-gateway
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: web-svc
            port:
              number: 80
      headers:
        request:
          set:
            whatever-id: '%DYNAMIC_METADATA(["istio_authn", "request.auth.claims", "whatever-id"])%'
            # whatever-id: "testing"

This however does not work. I verified the header transformation part, by commenting/uncommenting the last 2 lines, after which I did the testing string back from my POC application. But when I use the config with the line that should copy the claim value into the header, the header is not set on the request.

When I set logging to rbac:debug I can see the following log when making a request:

2022-02-23T14:04:05.708454Z     debug   envoy rbac      checking request: requestedServerName: outbound_.80_._.web-svc.route-echo.svc.cluster.local, sourceIP: 172.16.36.22:50722, directRemoteIP: 172.16.36.22:50722, remoteIP: 172.16.36.35:0,localAddress: 172.16.36.57:80, ssl: uriSanPeerCertificate: spiffe://cluster.local/ns/istio-ingress/sa/istio-ingress, dnsSanPeerCertificate: , subjectPeerCertificate: , headers: ':authority', 'example.com'
':path', '/echo-header'
':method', 'GET'
':scheme', 'https'
... <removed irrelevant headers for brevity>
, dynamicMetadata: filter_metadata {
  key: "envoy.filters.http.jwt_authn"
  value {
    fields {
      key: "<redacted issuer url>"
      value {
        struct_value {
          ... <removed all irrelevant claims for brevity>
          fields {
            key: "whatever-id"
            value {
              string_value: "98a5b3ea-f5e7-4012-8cf5-9cfb5cf9e65f"
            }
          }
        }
      }
    }
  }
}
filter_metadata {
  key: "istio_authn"
  value {
    fields {
      key: "request.auth.audiences"
      value {
        string_value: "account"
      }
    }
    fields {
      key: "request.auth.claims"
      value {
        struct_value {
          ... <removed all irrelevant claims for brevity>
          fields {
            key: "whatever-id"
            value {
              list_value {
                values {
                  string_value: "98a5b3ea-f5e7-4012-8cf5-9cfb5cf9e65f"
                }
              }
            }
          }
        }
      }
    }
    fields {
      key: "request.auth.presenter"
      value {
        string_value: "poc"
      }
    }
    fields {
      key: "request.auth.principal"
      value {
        string_value: "<redacted>"
      }
    }
    fields {
      key: "request.auth.raw_claims"
      value {
        string_value: "<redacted>"
      }
    }
  }
}

First thing I noticed when looking at this, is that the dynamicMetadata (which I assumed can be accessed by using the %DYNAMIC_METADATA()% synxtax as mentioned in the solution) does not contain an istio_authn field, the only field it contains is the envoy.filters.http.jwt_auth field, which has the key value equal to the issuer URL of my JWT, and under the value belonging to that key, there is a whatever-id field, which has the value which I want to add to my header, so I tried changing the header value in my VirtualService to:

whatever-id: '%DYNAMIC_METADATA(["envoy.filters.http.jwt_authn", "<issuer url>", "whatever-id"])%'

Sadly this did not work either.

What I also noticed, is that the keys used in the solution ("istio_authn", "request.auth.claims") are available under the filter_metadata property of the log (as opposed to the dynamicMetadata property).
So maybe it is possible to use another syntax to access those filter_metadata properties?

Environment details:

  • Kubernetes version 1.22.6
  • Using Azure AKS
  • Istio 1.13.0 deployed using helm chart

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文