安全认证

简要概述

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 返回。

角色与策略配置示例如下:

action: ALLOW
policies:
  # 角色名称
  "role-admin":
    # 权限列表
    permissions:
      - any: true
    # 用户主体
    principals:
      - authenticated:
          principal_name:
            exact: "cluster.local/ns/default/sa/admin"
  "role-guest":
    permissions:
    - and_rules:
        rules:
        - url_path:
            path:
              prefix: "/guest"
    principals:
      - any: 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"
        }
    }
}

角色访问控制配置参数

RBAC

数据结构见 envoy/config/rbac/v3/rbac.proto

action: ALLOW
policies:
  "policy-name-1":
    {Policy}
  "policy-name-2":
    {Policy}
属性 格式 说明
action string 可取 [“ALLOW”, “DENY”, “LOG”] 分别表示 “白名单”、“黑名单”、“日志” 模式
policies map<string, Policy> 键值对,通过定义多个角色,每个角色对应的主体以及包含相关权限

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

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

基于角色访问控制 示例。

Policy

数据结构见 envoy/config/rbac/v3/rbac.proto

属性 格式 说明
permissions repeated Permission 权限
principals repeated Principal 主体

策略(Policy)由权限 (Permission) 与主体 (Principal) 组成:

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

Permissions

数据结构见 envoy/config/rbac/v3/rbac.proto

permissions:
- and_rules:
    rules:
    - {Permissions}:
- or_rules:
    rules:
    - {Permissions}:
- any:
- header:
- url_path:
- destination_ip:
- destination_port:
- destination_port_range:
- metadata:
- not_rule:
- requested_server_name:
- matcher:
- uri_template:
属性 格式 说明
and_rules { “rules”: [Permission,…] } 满足所有里面的规则
or_rules { “rules”: [Permission,…] } 满足其中任意一个规则
any bool 任意条件
header route.v3.HeaderMatcher 仅 HTTP 协议下有效,请求头匹配
url_path type.matcher.v3.PathMatcher 仅 HTTP 协议下有效,请求 URL 匹配
destination_ip core.v3.CidrRange 目标 IP 地址段
destination_port uint32 目标端口
destination_port_range type.v3.Int32Range 目标端口范围
metadata type.matcher.v3.MetadataMatcher 自定义元数据,如 jwt 属性
not_rule Permission 不能满足这些规则
requested_server_name type.matcher.v3.StringMatcher 客户端 TLS SNI 匹配值
matcher core.v3.TypedExtensionConfig 自定义匹配扩展
uri_template core.v3.TypedExtensionConfig url 模版路径匹配

包含以上可配置属性,但同一时间仅可配置其中一项,不可同时配置。

Principal

数据结构见 envoy/config/rbac/v3/rbac.proto

principals:
- and_ids:
    ids:
    - {Principal}
- or_ids:
    ids:
    - {Principal}
- any:
- authenticated:
- source_ip:
- direct_remote_ip:
- remote_ip:
- header:
- url_path:
- metadata:
- filter_state:
- not_id:
属性 格式 说明
and_ids { “ids”: [Principal,…] } 一组身份标识,必须全匹配
or_ids { “ids”: [Principal,…] } 一组身份标识,至少匹配其中一项
any bool 开启则允许所有
authenticated { “principal_name”: type.matcher.v3.StringMatcher } 用于匹配 URI SAN 或 DNS SAN
source_ip core.v3.CidrRange 废弃,使用 “remote_ip” 代替
direct_remote_ip core.v3.CidrRange TODO;物理对接 IP 地址
remote_ip core.v3.CidrRange TODO;客户端 IP 地址,一般来自 xff 或者 protocol proxy 区别于 direct_remote_ip
header route.v3.HeaderMatcher 仅 HTTP 协议下有效,请求头匹配
url_path type.matcher.v3.PathMatcher 仅 HTTP 协议下有效,请求 URL 匹配
metadata type.matcher.v3.MetadataMatcher 元数据匹配
filter_state type.matcher.v3.FilterStateMatcher -
not_id Principal -

route.v3.HeaderMatcher

数据结构见 envoy/config/route/v3/route_components.proto

permissions:
- header:
    name: ":method"
    string_match:
      exact: "POST"

principals:
- header:
    name: "x-tr-user"
    string_match:
      exact: "sysadmin"
属性 格式 说明
name string HTTP/2 请求头名称,如 “:authority” 表示为 HTTP/1 下的 “Host”
exact_match string 废弃,使用 “string_match” 代替
safe_regex_match type.matcher.v3.RegexMatcher 废弃,使用 “string_match” 代替
range_match type.v3.Int64Range 求头值必须表示十进制的整数形式,即可包含可选的正负号
present_match bool 为 “true” 将根据请求中是否存在该头来执行匹配;“false” 将根据请求头是否缺失来匹配
prefix_match string 废弃,使用 “string_match” 代替
suffix_match string 废弃,使用 “string_match” 代替
contains_match string 废弃,使用 “string_match” 代替
string_match type.matcher.v3.StringMatcher 根据请求头部值的字符串匹配进行匹配

以上参数除 “name” 外,其他同一时间仅可配置其中一项,不可同时配置。

type.matcher.v3.PathMatcher

数据结构见 envoy/type/matcher/v3/path.proto

permissions:
- url_path:
    path:
      exact: "/guest"
属性 格式 说明
path type.matcher.v3.StringMatcher 匹配请求 url 且不包含参数,如 “/data” 匹配 “/data#fragment?param=value”

core.v3.CidrRange

数据结构见 envoy/config/core/v3/address.proto

permissions:
- destination_ip:
    address_prefix: 192.168.0.2
    prefix_len: 32
属性 格式 说明
address_prefix string 目标 IPv4 或 IPv6 地址
prefix_len int 子网掩码

type.v3.Int32Range

数据结构见 envoy/type/v3/range.proto

permissions:
- destination_port_range:
    start: 8080
    end: 8088
属性 格式 说明
start int32 端口开始,包含
end int32 端口结束,不包含

type.matcher.v3.MetadataMatcher

数据结构见 envoy/type/matcher/v3/metadata.proto

permissions:
- metadata:
    filter: envoy.filters.http.jwt_authn
    path:
      - key: playload
      - key: sub
    value:
      string_match:
        exact: user1
属性 格式 说明
filter string 用于从元数据中检索结构体的过滤器名称
path repeated PathSegment 从结构体中检索值的路径
value type.matcher.v3.ValueMatcher -
invert bool -
  • PathSegment
属性 格式 说明
key string 指向需获取结构体中值的键名

TODO;

type.matcher.v3.StringMatcher

数据结构见 envoy/type/matcher/v3/string.proto

permissions:
- requested_server_name:
    exact: "hello"

pristinals:
  - header:
      name: ":authority"
      string_match:
        safe_regex:
          regex: "([0-9a-z]{6}).mi.test.opsaid.net"
属性 格式 说明
exact string 全匹配,如 “abc” 则必须匹配为 “abc”
prefix string 前缀匹配,如 “abc” 可匹配为 “abc.xyz”
suffix string 后缀匹配,如 “abc” 可匹配为 “xyz.abc”
safe_regex type.matcher.v3.RegexMatcher 正则表达匹配
contains string 包含匹配,如 “abc” 可匹配为 “xyz.abc.def”
custom xds.core.v3.TypedExtensionConfig 自定义匹配

以上参数同一时间仅可配置其中一项,不可同时配置。

core.v3.TypedExtensionConfig

数据结构见 envoy/config/core/v3/extension.proto

permissions:
- matcher:
    name: 
    typed_config: 
属性 格式 说明
name string -
typed_config google.protobuf.Any -

type.matcher.v3.ValueMatcher

数据结构见 envoy/type/matcher/v3/value.proto

属性 格式 说明
null_match string ""
double_match - -
string_match type.matcher.v3.StringMatcher -
bool_match bool -
present_match bool -
list_match type.matcher.v3.ListMatcher -
or_match type.matcher.v3.OrMatcher -

以上参数同一时间仅可配置其中一项,不可同时配置。

type.matcher.v3.FilterStateMatcher

数据结构见 envoy/type/matcher/v3/filter_state.proto

属性 格式 说明
string_match type.matcher.v3.StringMatcher -

type.matcher.v3.RegexMatcher

数据结构见 envoy/type/matcher/v3/regex.proto

属性 格式 说明
regex string 正则表达式



最后修改 2024.05.22: feat: 添加 opa 应用示例 (8418eb5)