安全认证
6 分钟阅读
简要概述
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 进程中执行,根据预先配置的策略列表对每个请求进行检查。
可以在以下两个地方配置过滤器:
- Network filter,当鉴权不通过则直接关闭连接;
- 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
action: ALLOW
policies:
"policy-name-1":
{Policy}
"policy-name-2":
{Policy}
属性 | 格式 | 说明 |
---|---|---|
action | string | 可取 [“ALLOW”, “DENY”, “LOG”] 分别表示 “白名单”、“黑名单”、“日志” 模式 |
policies | map<string, Policy> | 键值对,通过定义多个角色,每个角色对应的主体以及包含相关权限 |
策略匹配的动作 (action) 说明:
- 当
action: ALLOW
时:当且仅当存在与请求匹配的策略时才允许该请求,也就是白名单; - 当
action: DENY
时:当且仅当没有与请求匹配的策略时才允许该请求,也就是黑名单; - 当
action: LOG
时:允许所有请求,当请求匹配策略时,更改 “access_log_hint” 为 “true”。
见 基于角色访问控制 示例。
Policy
属性 | 格式 | 说明 |
---|---|---|
permissions | repeated Permission | 权限 |
principals | repeated Principal | 主体 |
策略(Policy)由权限 (Permission) 与主体 (Principal) 组成:
- 权限:指定请求的操作,既做什么,例如 HTTP 请求的方法和路径;
- 主体:指定请求的下游客户端身份,既这是谁,例如 jwt 中的 sub 属性。
Permissions
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
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
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
permissions:
- url_path:
path:
exact: "/guest"
属性 | 格式 | 说明 |
---|---|---|
path | type.matcher.v3.StringMatcher | 匹配请求 url 且不包含参数,如 “/data” 匹配 “/data#fragment?param=value” |
core.v3.CidrRange
permissions:
- destination_ip:
address_prefix: 192.168.0.2
prefix_len: 32
属性 | 格式 | 说明 |
---|---|---|
address_prefix | string | 目标 IPv4 或 IPv6 地址 |
prefix_len | int | 子网掩码 |
type.v3.Int32Range
permissions:
- destination_port_range:
start: 8080
end: 8088
属性 | 格式 | 说明 |
---|---|---|
start | int32 | 端口开始,包含 |
end | int32 | 端口结束,不包含 |
type.matcher.v3.MetadataMatcher
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
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
permissions:
- matcher:
name:
typed_config:
属性 | 格式 | 说明 |
---|---|---|
name | string | - |
typed_config | google.protobuf.Any | - |
type.matcher.v3.ValueMatcher
属性 | 格式 | 说明 |
---|---|---|
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
属性 | 格式 | 说明 |
---|---|---|
string_match | type.matcher.v3.StringMatcher | - |
type.matcher.v3.RegexMatcher
属性 | 格式 | 说明 |
---|---|---|
regex | string | 正则表达式 |