安全认证

简要概述

TODO;

证书验证

https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/transport_socket/transport_socket

配合上游使用

config.cluster.v3.Cluster 中的 transport_socket 配置使用,支持以下类型:

名称 类型
envoy.transport_sockets.tls extensions.transport_sockets.tls.v3.UpstreamTlsContext

如在 clusters[_].transport_socket 中配置使用 “/tmp/http-ca.crt” 作为 ca 验证上游服务端:

static_resources:

  ......

  clusters:
  - name: oneops-syncds-v1
    type: STATIC

    ......

    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        # 通用 tls 配置,则不做校验仅做通讯加密
        common_tls_context:
          # 这说明需使用 "/tmp/http-ca.crt" 去验证上游集群
          validation_context:
            trusted_ca:
              filename: /tmp/http-ca.crt

配合下游使用

config.listener.v3.FilterChain 中的 transport_socket 配置:

支持这几种类型:

名称 类型
envoy.transport_sockets.alts x
envoy.transport_sockets.raw_buffer x
envoy.transport_sockets.starttls x
envoy.transport_sockets.tap x
envoy.transport_sockets.tcp_stats x
envoy.transport_sockets.tls extensions.transport_sockets.tls.v3.DownstreamTlsContext
static_resources:
  listeners:
  - name: listener_0
  
    ......

    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
 
        ......

      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain:
                filename: "./tls/example.pem"
              private_key:
                filename: "./tls/example.key"
......

内置鉴权模式

基于角色访问控制

基于角色的访问控制过滤器 (RBAC Filter) 负责验证传入请求的授权状态。该过滤器在 Envoy 进程中执行,根据预先配置的策略列表对每个请求进行检查。

可以在以下两个地方配置过滤器:

  1. Network filter,当鉴权不通过则直接关闭连接;
  2. HTTP filter,当鉴权不通过则以 403 返回。

策略(policy)由权限(permission)与主体(principal)组成:

  1. 权限:指定请求的操作,既做什么,例如 HTTP 请求的方法和路径;
  2. 主体:指定请求的下游客户端身份,既这是谁,例如 jwt 中的 sub 属性。

角色与策略配置示例如下,详细接口文档参考 config.rbac.v3.RBAC

action: ALLOW
policies:
  # 角色名称
  "service-admin":
    # 权限列表
    permissions:
      - any: true
    # 用户主体
    principals:
      - authenticated:
          principal_name:
            exact: "cluster.local/ns/default/sa/admin"
      - authenticated:
          principal_name:
            exact: "cluster.local/ns/default/sa/superuser"
  "role-sal":
    permissions:
    - and_rules:
        rules:
        - header: { name: ":method", exact_match: "GET" }
        - header: { name: "User", exact_match: "sal" }
        - url_path:
            path: { prefix: "/" }
    principals:
    - metadata:
        filter: envoy.filters.http.jwt_authn
        path:
          - key: my_payload
          - key: sub
        value:
          string_match:
            exact:  "admin@example.com"
  "role-guest":
    permissions:
    - and_rules:
        rules:
        - url_path:
            path:
              prefix: "/guest"
    principals:
      - any: true

策略匹配的动作 (action) 说明:

  1. action: ALLOW 时:当且仅当存在与请求匹配的策略时才允许该请求,也就是白名单;
  2. action: DENY 时:当且仅当没有与请求匹配的策略时才允许该请求,也就是黑名单;
  3. action: LOG 时:允许所有请求,当请求匹配策略时,更改 “access_log_hint” 为 “true”。

Network filter 配置示例

拒绝来自 “192.168.0.1” 且访问 “80” 端口的客户端,其他来源均允许:

static_resources:
  listeners:
  - name: listener_0
  
    ......

    filter_chains:
    - filters:
      - name: envoy.filters.network.rbac
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC
          stat_prefix: network_filter
          rules:
            action: DENY
            policies:
              "guest":
                permissions:
                  - and_rules:
                      rules:
                        - destination_port: 80
                principals:
                  - remote_ip:
                      address_prefix: 192.168.0.1
                      prefix_len: 32
    ......

如果开启了 prometheus 则会产生相关 性能指标 项。

HTTP filter 配置示例

static_resources:
  listeners:
  - name: listener_0
  
    ......

    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

          http_filters:
          - name: envoy.filters.http.rbac
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
              rules:
                action: ALLOW
                policies:
                  "admin":
                    permissions:
                    - any: true
                    principals:
                    - metadata:
                        filter: envoy.filters.http.jwt_authn
                        path:
                          - key: my_payload
                          - key: sub
                        value:
                          string_match:
                            exact:  "admin@example.com"
                  "guest":
                    permissions:
                    - and_rules:
                        rules:
                        - url_path:
                            path:
                              prefix: "/guest"
                    principals:
                      - any: true
    ......

外部鉴权模式

基于 OPA 的访问控制

外部鉴权模式内置鉴权模式 一样,也支持 “Network filter” 与 “HTTP filter” 两种过滤器。它的流程是当请求发生至 Envoy 时,由 Envoy 把请求的元信息(HTTP 请求头、源 IP 与端口、目标 IP 与端口等)传递给下游 OPA 服务端,由它判断鉴权是否通过。

这里 OPA 服务端由 opa-envoy-plugin 实现,启动与配置示例:

./main run \
	--server \
	--addr=0.0.0.0:8181 \
	--log-level=debug \
	--diagnostic-addr=0.0.0.0:8282 \
   	--ignore=.* \
	--config-file=config.yaml \
	authz/policy.rego
  • 配置 “config.yaml” 内容
plugins:
  envoy_ext_authz_grpc:
    addr: :9191
    path: envoy/authz/allow
    dry-run: false
    enable-reflection: true
    grpc-max-recv-msg-size: 40194304
    grpc-max-send-msg-size: 2147483647
    skip-request-body-parse: false
    enable-performance-metrics: false

decision_logs:
  console: true
  • 鉴权文件 “authz/policy.rego” 内容
package envoy.authz

import rego.v1

default allow := false

allow if {
  input.attributes.source.address.socketAddress.address == "192.168.0.1"
}

默认不允许,因为 “path: envoy/authz/allow” 指向的 “authz/policy.rego” 文件中 “allow” 变量默认值为 false,当且仅当源 IP 为 “192.168.0.1” 时可访问。这个相比内置鉴权模式,存在更高的灵活性,能实现更加细粒度的控制,通过声明式编写策略文件即可。

Network filter 配置示例

用于实现源IP、源端口、目标IP、目标端口的限制,在 envoy 上配置使用外部验证服务地址:

static_resources:
  listeners:
  - name: listener_0
  
    ......

    filter_chains:
    - filters:
      - name: envoy.filters.network.ext_authz
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.ext_authz.v3.ExtAuthz
          transport_api_version: V3
          stat_prefix: ext_authz
          grpc_service:
            envoy_grpc:
              cluster_name: opa-envoy
          include_peer_certificate: true
    ......

  clusters:
  - name: opa-envoy
    connect_timeout: 1.25s
    type: STATIC
    http2_protocol_options: {}
    load_assignment:
      cluster_name: opa-envoy
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 192.168.0.1
                port_value: 9191
    ......

opa-envoy-plugin 作为服务端接收到 envoy 数据,并转换为 opa 使用的 input 变量

{
    "input": {
        "attributes": {
            "destination": {
                "address": {
                    "socketAddress": {
                        "address": "192.168.0.2",
                        "portValue": 80
                    }
                }
            },
            "source": {
                "address": {
                    "socketAddress": {
                        "address": "192.168.0.1",
                        "portValue": 56695
                    }
                }
            }
        },
        "parsed_body": null,
        "parsed_path": [
            ""
        ],
        "parsed_query": {},
        "truncated_body": false,
        "version": {
            "encoding": "protojson",
            "ext_authz": "v3"
        }
    }
}

HTTP filter 配置示例

除IP、端口外,还可以实现对 HTTP 协议细节进行限制,在 envoy 上配置使用外部验证服务地址

static_resources:
  listeners:
  - name: listener_0
  
    ......

    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

          http_filters:
          - name: envoy.ext_authz
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
              transport_api_version: V3
              failure_mode_allow: false
              grpc_service:
                envoy_grpc:
                  cluster_name: opa-envoy
              with_request_body:
                allow_partial_message: true
                max_request_bytes: 1024
                pack_as_bytes: true

          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

    ......

opa-envoy-plugin 作为服务端接收到 envoy 数据,并转换为 opa 使用的 input 变量

{
    "input": {
        "attributes": {
            "destination": {
                "address": {
                    "socketAddress": {
                        "address": "192.168.0.2",
                        "portValue": 80
                    }
                }
            },
            "metadataContext": {},
            "request": {
                "http": {
                    "headers": {
                        ":authority": "hello.example.com",
                        ":method": "GET",
                        ":path": "/vw0tha/hello/test.jpg?arg1=test1&arg2=test2",
                        ":scheme": "http",
                        "accept": "*/*",
                        "user-agent": "curl/8.4.0",
                        "x-forwarded-proto": "http",
                        "x-request-id": "ca5f65a7-e8e1-420a-adf6-466f04c623dd"
                    },
                    "host": "hello.example.com",
                    "id": "5149459733649565588",
                    "method": "GET",
                    "path": "/vw0tha/hello/test.jpg?arg1=test1&arg2=test2",
                    "protocol": "HTTP/1.1",
                    "scheme": "http"
                },
                "time": "2024-03-19T03:39:00.986466Z"
            },
            "source": {
                "address": {
                    "socketAddress": {
                        "address": "192.168.0.1",
                        "portValue": 57372
                    }
                }
            }
        },
        "parsed_body": null,
        "parsed_path": [
            "vw0tha",
            "hello",
            "test.jpg"
        ],
        "parsed_query": {
            "arg1": [
                "test1"
            ],
            "arg2": [
                "test2"
            ]
        },
        "truncated_body": false,
        "version": {
            "encoding": "protojson",
            "ext_authz": "v3"
        }
    }
}



最后修改 2024.03.20: docs: envoy ext_authz (3843b4d)