如何在 Envoy 中提取 jwt

0xb*_*eny 0 jwt envoyproxy

如何在envoy中提取jwt ,并将提取的值放入标头

我需要在 http_filters 下面添加一些额外的属性但我对此一无所知,并且我研究了jwtProviderjwtHeader,它们都出现在 envoy 文档中

这是我的envoy.yaml文件,此配置有什么问题:

admin: 
  access_log_path: "/dev/null"
  address: 
    socket_address:
      address: 0.0.0.0
      port_value: 8001
static_resources:
  listeners:
  - name: main
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 1337
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite_literal: 0.0.0.0
                  cluster: web_service
          http_filters:
          - name: config.filter.http.jwt_authn.v2alpha.JwtHeader
            from_params:
            - jwt_token
          - name: envoy.filters.http.lua
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
              inline_code: |
                function envoy_on_request(request_handle)
                  local meta = request_handle:streamInfo():dynamicMetadata()
                  for key, value in pairs(meta) do
                    request_handle:logInfo("extract dynamicMetadata key: "..key)
                    request_handle:logInfo("extract dynamicMetadata value: "..value.jwt_payload.usr)
                  end
                end
          - name: envoy.filters.http.router
  clusters:
  - name: web_service
    connect_timeout: 5s
    type: STRICT_DNS  # static
    load_assignment:
      cluster_name: web_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 0.0.0.0
                port_value: 3000`enter code here`
Run Code Online (Sandbox Code Playgroud)

nor*_*bjd 6

如果我理解正确的话,你想要的是:

  1. Envoy 从标头中提取并验证 JWT Authorization( Authorization: Bearer ...)
  2. 如果 JWT 通过验证,Envoy 会将请求转发到您的集群
  3. 在该转发请求中,Envoy 添加一个或多个与解码的 JWT 有效负载项相对应的标头

您可以通过更改http_filters部分配置来实现此目的。我使用的是 envoy v1.21.0。为了方便起见,我在这里使用本地 JWKS配置(HS256算法,带有 Secret helloworld= aGVsbG93b3JsZA==in base64)。但请注意,您还可以定义远程 JWKS 配置

          http_filters:
          - name: envoy.filters.http.jwt_authn
            typed_config:
              "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication"
              providers:
                jwt_auth:
                  local_jwks:
                    inline_string: '{"keys":[{"typ": "JWT", "kty":"oct","alg":"HS256","kid":"df","k":"aGVsbG93b3JsZA=="}]}'
                  from_headers:
                  - name: Authorization
                    value_prefix: "Bearer "
                  payload_in_metadata: jwt_payload
              rules:
              - match:
                  prefix: "/"
                requires:
                  provider_name: jwt_auth
          - name: envoy.filters.http.lua
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
              inline_code: |
                function envoy_on_request(request_handle)
                  local meta = request_handle:streamInfo():dynamicMetadata()
                  for key, value in pairs(meta) do
                    request_handle:headers():add("jwt-extracted-user", value.jwt_payload.usr)
                  end
                end
          - name: envoy.filters.http.router
Run Code Online (Sandbox Code Playgroud)

第一个过滤器 ( envoy.filters.http.jwt_authn)

第一个过滤器 ( envoy.filters.http.jwt_authn) 配置为:

  • 使用一些本地 JWKS 配置:请参阅local_jwks.inline_string
  • 从标头获取 JWT Authorization(在该Bearer 部分之后):参见from_headers属性
  • 将经过验证的 JWT 有效负载写入StreamInfo DynamicMetadatavalue jwt_payload:请参阅payload_in_metadata
  • 定义一些规则来将匹配与上面定义的提供者链接起来 ( jwt_auth) :参见rules

第二个过滤器 ( envoy.filters.http.lua)

配置第一个过滤器后,您现在可以处理StreamInfo DynamicMetadata添加新标头。这是由第二个过滤器 ( envoy.filters.http.lua) 完成的。内联代码:

function envoy_on_request(request_handle)
  local meta = request_handle:streamInfo():dynamicMetadata()
  for key, value in pairs(meta) do
    request_handle:headers():add("jwt-extracted-user", value.jwt_payload.usr)
  end
end
Run Code Online (Sandbox Code Playgroud)
  • 迭代StreamInfo DynamicMetadata
  • usr从 JWT 有效负载中提取字段(名为jwt_payload,如第一个过滤器的payload_in_metadata属性中所配置)
  • value.jwt_payload.usr在名为的新标头中添加该信息 ( )jwt-extracted-user

测试

通过我的答案http_filters替换您的配置中的答案,您可以启动一个执行您想要的操作的特使,并使用包含以下有效负载的有效 JWT对其进行测试:

{
  "usr": "John Doe"
}
Run Code Online (Sandbox Code Playgroud)

赶紧跑 :

curl \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJKb2huIERvZSJ9.-9p297JIIXkHomuuvAFD7pghEtDhdKKAe8V0SqeDqU4' \
  http://localhost:1337
Run Code Online (Sandbox Code Playgroud)

JWT 由特使验证,并web_service使用名为 的新标头转发给您jwt-extracted-user。为了证明这一点,我为您使用了一个简单的 Flask Python 应用程序web_service,仅打印标题:

import sys
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route("/")
def hello_world():
    print(request.headers, file=sys.stderr)
    return "Hello, world!"


if __name__ == '__main__':
    app.run(host='0.0.0.0')
Run Code Online (Sandbox Code Playgroud)

在 Python 应用程序的日志中,您应该看到类似以下内容:

Host: localhost:1337
User-Agent: curl/7.81.0
Accept: */*
X-Forwarded-Proto: http
X-Request-Id: 3a45f721-ccdd-4225-91ab-8ff4307c97de
Jwt-Extracted-User: John Doe
X-Envoy-Expected-Rq-Timeout-Ms: 15000
Run Code Online (Sandbox Code Playgroud)

请注意 的存在Jwt-Extracted-User,包含usrJWT 有效负载的字段。

制作人员

https://github.com/envoyproxy/envoy/issues/9716#issuecomment-577261856