启用授权
Alluxio 提供了一个灵活的授权框架,以保护数据访问和管理操作。根据您的安全要求,您可以选择适当的集成:
对于数据访问控制(S3 API 和 Hadoop FS): 与 Apache Ranger 集成,以管理访问文件和目录的用户和应用程序的细粒度权限。
对于管理 API 控制(网关): 与 Open Policy Agent (OPA) 集成,以为通过 Alluxio 网关执行的管理操作强制执行复杂的、基于策略的授权。
本指南提供了配置这些集成的分步说明。
使用 Apache Ranger 进行数据访问授权
Apache Ranger 为数据访问操作提供集中式、细粒度的授权。通过将 Alluxio 与 Ranger 集成,您可以管理 S3 API 和 Hadoop 文件系统客户端的策略,确保用户和应用程序仅具有在 Alluxio 命名空间内读取、写入或管理数据所需的权限。这是控制对数据平面访问的推荐解决方案。
先决条件
在配置 Ranger 与 Alluxio 的集成之前,请确保满足以下要求:
插件 JAR 可用性:确保授权插件 jar(例如
lib/alluxio-authorization-hdfs-AI-3.7-13.0.0.jar
)在所有 Alluxio 节点上都可用。Ranger HDFS 插件:验证您的 Ranger 配置中已启用 HDFS 插件。
HDFS 服务配置:按照 Cloudera 的 HDFS 服务文档 中的说明为 Alluxio 配置一个新的 HDFS 服务。
配置
请按照以下步骤配置集成。
步骤 1:设置配置文件
将以下配置文件复制或创建到 Alluxio 节点上的目录中,例如 /opt/alluxio/conf/ranger/
:
ranger-hdfs-security.xml
ranger-hdfs-audit.xml
以下是这些文件的示例配置。 ranger-hdfs-security.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
<property>
<name>ranger.plugin.hdfs.service.name</name>
<value>alluxio-test</value>
<description>
Name of the Ranger service containing policies for this YARN instance
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.source.impl</name>
<value>org.apache.ranger.admin.client.RangerAdminRESTClient</value>
<description>
Class to retrieve policies from the source
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.rest.url</name>
<value>http://your-ranger-ip:6080</value>
<description>
URL to Ranger Admin
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.pollIntervalMs</name>
<value>30000</value>
<description>
How often to poll for changes in policies?
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.cache.dir</name>
<value>/path/to/policycache</value>
<description>
Directory where Ranger policies are cached after successful retrieval from the source
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.rest.client.connection.timeoutMs</name>
<value>120000</value>
<description>
Hdfs Plugin RangerRestClient Connection Timeout in Milli Seconds
</description>
</property>
<property>
<name>ranger.plugin.hdfs.policy.rest.client.read.timeoutMs</name>
<value>30000</value>
<description>
Hdfs Plugin RangerRestClient read Timeout in Milli Seconds
</description>
</property>
<!-- The following fields are used to customize the audit logging feature -->
<property>
<name>xasecure.auditlog.xasecureAcl.name</name>
<value>ranger-acl</value>
<description>
The module name listed in the auditlog when the permission check is done by RangerACL
</description>
</property>
<property>
<name>xasecure.add-hadoop-authorization</name>
<value>false</value>
<description>
Enable/Disable the default hadoop authorization (based on
rwxrwxrwx permission on the resource) if Ranger Authorization fails.
</description>
</property>
</configuration>
ranger-hdfs-audit.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
<property>
<name>xasecure.audit.is.enabled</name>
<value>false</value>
</property>
</configuration>
步骤 2:更新 Ranger 配置
更新 ranger-hdfs-security.xml
中的配置设置以使用新的 HDFS 服务:
将
ranger.plugin.hdfs.policy.cache.dir
设置为 Alluxio 节点上您希望存储策略缓存的有效目录。将
ranger.plugin.hdfs.service.name
设置为新的 HDFS 服务名称,例如alluxio-test
。验证
ranger.plugin.hdfs.policy.rest.url
指向正确的 Ranger 服务 URL。将
xasecure.add-hadoop-authorization
设置为false
以禁用回退到 Hadoop 授权。
步骤 3:配置 Alluxio 节点
要在 worker 上使用 Ranger 插件对 S3 API 进行授权,请将以下内容添加到 alluxio-cluster.yaml
的 properties
部分。
对于 S3 API:
properties:
alluxio.worker.s3.authorization.enabled: "true"
alluxio.worker.s3.authorizer.classname: "alluxio.s3.auth.ExtendableAuthorizer"
alluxio.security.authorization.plugin.name: "ranger-privacera-4.7"
alluxio.security.authorization.plugin.paths: "/opt/alluxio/conf/ranger/"
# 或您复制 ranger 配置 xml 文件的任何位置
对于 Hadoop 文件系统: 在客户端,通过添加以下内容启用授权:
alluxio.security.authorization.plugin.name=ranger-privacera-4.7
alluxio.security.authorization.plugin.paths=/opt/alluxio/conf/ranger/
# 或您复制 ranger 配置 xml 文件的任何位置
步骤 4:应用配置
重新启动所有 Alluxio 节点以应用新配置,或在启动 Alluxio 集群之前进行配置。您现在可以在 Ranger 中向 Alluxio 服务(例如 alluxio-test
)添加策略,并验证它们在 Alluxio 中生效。
重要说明
路径映射: Ranger 策略中的路径对应于 Alluxio 命名空间中的路径,而不是底层的 S3 对象。
权限层次结构:
读取文件需要对文件本身的
READ
权限。写入或创建文件需要对父目录的
WRITE
权限。列出目录内容需要对该目录的
EXECUTE
权限。
策略优先级: 更具体的策略优先于一般策略。拒绝策略将覆盖同一资源的允许策略。
服务连接: 在 Ranger 中为 Alluxio 配置 HDFS 服务时,连接测试预计会失败,因为 Ranger 无法直接与 Alluxio 的文件系统方案通信。这是正常现象。您可以安全地忽略该错误,保存服务,然后手动创建策略。
故障排除: 如果授权意外失败,请检查:
Ranger 策略缓存刷新状态。
Alluxio 日志中的详细错误消息。
用户身份验证是否正常工作。
使用 Open Policy Agent (OPA) 进行管理 API 授权
为了控制对管理和管理操作的访问,Alluxio 网关与 Open Policy Agent (OPA) 集成。OPA 使您能够为所有网关 API 端点定义和强制执行细粒度的、基于策略的访问控制。这是保护管理平面的推荐解决方案,允许您根据用户身份(角色、组)和正在请求的特定 API 操作创建复杂的规则。
核心概念
在配置之前,了解三个主要组件会很有帮助:
OPA 策略(
.rego
文件): 授权的“逻辑引擎”。此文件包含一组用 Rego 语言编写的规则,这些规则定义了允许或拒绝请求的条件。OPA 数据(
.yaml
文件): 策略使用的“配置”。此文件提供特定数据——例如超级管理员列表、组定义和路径权限——Rego 规则会参考这些数据来做出决策。Kubernetes ConfigMap: 将策略和数据文件提供给 Alluxio 网关 Pod 的标准 Kubernetes 方法。
设置和配置
请按照以下步骤为网关启用和配置 OPA 授权。
步骤 1:准备 OPA 策略和数据文件
首先,在本地创建策略(opa_auth_policy.rego
)和数据(opa_data.yaml
)文件。
1. opa_auth_policy.rego
(策略文件)
这是一个全面的参考策略,您可以将其用作起点或根据您的需求进行自定义。
package opa_auth_policy
import rego.v1
# -----------------------------------------------------------------------------
# --- 默认策略
# -----------------------------------------------------------------------------
# 默认情况下,拒绝所有请求。
default allow := false
# -----------------------------------------------------------------------------
# --- 主要授权规则
# --- 这些是确定最终“允许”决策的顶级规则。
# -----------------------------------------------------------------------------
# 规则 1:超级管理员可以执行任何操作,绕过所有其他检查。
allow if {
user_is_superadmin
}
# 规则 2:对于通过基本检查的用户,允许 GET 访问全局允许的 API。
allow if {
base_checks_pass
method_is_get
api_in_global_allow_list
}
# 规则 3:管理员可以列出资源(即,没有特定路径或 ID 的 GET 请求)。
allow if {
base_checks_pass
user_is_admin
method_is_get
request_is_for_listing
}
# 规则 4:允许任何用户谁通过基本检查 GET 资源,如果所有请求的路径都是允许的。
allow if {
base_checks_pass
method_is_get
all_request_paths_are_allowed
}
# 规则 5:允许管理员执行更新操作,如果所有请求的路径都是允许的。
allow if {
base_checks_pass
user_is_admin
method_is_update
all_request_paths_are_allowed
}
# 规则 6:允许提供 ID 但没有路径的请求。
# 这处理一个特定的更新场景。
allow if {
base_checks_pass
user_is_admin
method_is_update
request_has_id_but_no_path
}
# 规则 7:允许提供 ID 但没有路径的请求。
# 这处理一个特定的 GET 场景。
allow if {
base_checks_pass
method_is_get
request_has_id_but_no_path
}
# -----------------------------------------------------------------------------
# --- 核心逻辑帮助程序
# -----------------------------------------------------------------------------
# 组合非超级管理员用户的常见检查以提高性能。
base_checks_pass if {
not user_is_superadmin
user_is_valid
not api_in_global_deny_list
}
# 检查请求中的所有路径是否都在用户的允许列表和不在拒绝列表中。
all_request_paths_are_allowed if {
count(relevant_paths) > 0
count({x | relevant_paths[x]; path_is_allowed(x)}) == count(relevant_paths)
count({x | relevant_paths[x]; path_is_denied(x)}) == 0
}
# 确定请求是否为“列表”操作(没有 ID 和路径)。
request_is_for_listing if {
not request_has_id
count(relevant_paths) == 0
}
# 确定请求是否包含 ID 但没有路径。
request_has_id_but_no_path if {
request_has_id
count(relevant_paths) == 0
}
# -----------------------------------------------------------------------------
# --- 权限详细信息帮助程序
# -----------------------------------------------------------------------------
# 检查路径是否在用户的允许前缀中。
path_is_allowed(path) if {
some i, j
some group in input_groups
group == data.groups[i].group
clean_path := trim_suffix(path, "/")
clean_prefix := trim_suffix(data.groups[i].allow.pathPrefixes[j].prefix, "/")
strings.any_prefix_match(clean_path, clean_prefix)
is_valid_prefix_match(clean_path, clean_prefix)
api_is_valid_for_path_rule(data.groups[i].allow.pathPrefixes[j])
}
# 检查路径是否在用户的拒绝前缀中。
path_is_denied(path) if {
some i, j
some group in input_groups
group == data.groups[i].group
clean_path := trim_suffix(path, "/")
clean_prefix := trim_suffix(data.groups[i].deny.pathPrefixes[j].prefix, "/")
strings.any_prefix_match(clean_path, clean_prefix)
is_valid_prefix_match(clean_path, clean_prefix)
api_is_valid_for_path_rule(data.groups[i].deny.pathPrefixes[j])
}
# 验证前缀匹配是否合法。
# 如果前缀与路径完全匹配,则此规则为真。
is_valid_prefix_match(path, prefix) if {
strings.any_prefix_match(path, prefix)
suffix := trim_prefix(path, prefix)
suffix == ""
}
# 如果前缀匹配目录边界,则此规则为真。
# 示例:前缀“/a/b”匹配路径“/a/b/c”。
is_valid_prefix_match(path, prefix) if {
strings.any_prefix_match(path, prefix)
suffix := trim_prefix(path, prefix)
startswith(suffix, "/")
}
# 检查当前 API 是否对给定的路径规则有效。
# 规则 1:如果路径规则未指定“apis”列表,则它适用于所有 API。
api_is_valid_for_path_rule(rule) if {
not rule.apis
}
# 规则 2:如果路径规则指定了“apis”列表,则当前 API 必须在该列表中。
api_is_valid_for_path_rule(rule) if {
input_api in rule.apis
}
# -----------------------------------------------------------------------------
# --- 请求解析帮助程序
# -----------------------------------------------------------------------------
# 从请求路径中提取 API 端点。
input_api := split(input.path, "/v1")[1]
# HTTP 方法检查。
method_is_get if input.method == "GET"
method_is_update if input.method != "GET"
# 从请求中的多个可能位置提取路径。
# 使用“contains”关键字逐步构建路径集。
paths_from_request contains path if {
path := input.query.path[0]
}
paths_from_request contains path if {
path := input.parsed_body.path
}
paths_from_request contains path if {
path := input.parsed_body.paths[_]
}
paths_from_request contains path if {
path := input.parsed_body.index
}
# 从请求中收集所有非空路径以进行验证。
relevant_paths := {p | some p in paths_from_request; p != ""}
# 检查请求是否包含 ID。
request_has_id if input.parsed_body.id != ""
request_has_id if input.query.id != ""
# 全局 API 列表检查。
api_in_global_deny_list if input_api in data.denyApis
api_in_global_allow_list if input_api in data.allowApis
# -----------------------------------------------------------------------------
# --- 用户和角色帮助程序
# -----------------------------------------------------------------------------
claims := payload if {
token := input.header.Authorization
count(token) != 0
startswith(token[0], "Bearer ")
bearer_token := substring(token[0], count("Bearer "), -1)
[_, payload, _] := io.jwt.decode(bearer_token)
}
else := user_info if {
token := input.header.Authorization
count(token) != 0
not startswith(token[0], "Bearer ")
base64.is_valid(token[0])
ui = base64.decode(token[0])
json.is_valid(ui)
user_info = json.unmarshal(ui)
}
default input_roles := []
input_roles := claims.roleFieldName if {
claims.roleFieldName != ""
is_array(claims.roleFieldName)
}
else := [claims.roleFieldName] if {
claims.roleFieldName != ""
is_string(claims.roleFieldName)
}
else := claims.role if {
claims.role != ""
is_array(claims.role)
}
else := [claims.role] if {
claims.role != ""
is_string(claims.role)
}
default input_groups := []
input_groups := claims.groupFieldName if {
claims.groupFieldName != ""
is_array(claims.groupFieldName)
}
else := [claims.groupFieldName] if {
claims.groupFieldName != ""
is_string(claims.groupFieldName)
}
else := claims.group if {
claims.group != ""
is_array(claims.group)
}
else := [claims.group] if {
claims.group != ""
is_string(claims.group)
}
user_is_valid if {
count(input_roles) > 0
count(input_groups) > 0
}
user_is_superadmin if {
count(input_roles) > 0
some i
some role in input_roles
role == data.superAdmin[i]
}
user_is_admin if {
some i
some role in input_roles
role == data.groupAdmin[i]
}
2. opa_data.yaml
(数据文件)
在此文件中定义您的角色、API 限制和基于组的路径权限。
# 超级管理员定义
superAdmin: ["SuperAdmin"]
# 管理员用户定义
groupAdmin: ["GroupAdmin"]
# 只能由 superAdmin 访问的 API
denyApis:
- /file_index
- /nodes
- /rebalance
- /cache
- /mount
# 每个人都可以访问的 API
allowApis:
# 针对团队和路径的权限定义
groups:
- group: Search
allow:
pathPrefixes:
- prefix: s3://search-bucket/dir1/dir2
# 如果未定义 apis,则默认允许所有 API
- prefix: s3://search-bucket/dir1/dir3/
apis:
- /load
- group: Recommend
allow:
pathPrefixes:
- prefix: s3://recommend-bucket/dir1/dir2
- prefix: s3://recommend-bucket/dir1/dir3
apis:
- /load
步骤 2:创建 Kubernetes ConfigMap
使用 kubectl
CLI 从您准备的两个文件创建 ConfigMap。
kubectl create configmap opa-gateway-configmap \
--from-file=opa_auth_policy.rego \
--from-file=opa_data.yaml
步骤 3:配置 Alluxio 网关
最后,修改您的 alluxio-cluster.yaml
以启用 OPA 并指示网关使用您创建的 ConfigMap。
spec:
global:
authentication:
enabled: true
type: oidc
oidc:
jwksUri:
nbfCheck: false
roleFieldName:
userFieldName:
groupFieldName:
authorization:
enabled: true
opa:
components:
gateway:
configMapName: opa-gateway-configmap
filenames:
- opa_auth_policy.rego
- opa_data.yaml
query: data.opa_auth_policy.allow
应用此配置后,Alluxio 网关将使用您的 OPA 策略来授权传入的 API 请求。
工作原理
OPA 输入格式
对于它收到的每个 API 请求,网关都会构造一个 JSON 对象,作为 OPA 策略评估的 input
。此对象包含有关请求的所有关键信息。
{
"header": {
"Accept": [
"*/*"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"Authorization": [
"Bearer eyJuYW1lIjoxxxx="
],
"Connection": [
"keep-alive"
],
"Postman-Token": [
"b9844ab1-27b5-41a2-83a5-40f4d9fb74f4"
],
"User-Agent": [
"PostmanRuntime/7.42.0"
],
"X-Request-Id": [
"97be1ace-dc50-40a2-b3a1-e02061ca9504"
]
},
"method": "POST",
"parsed_body": {
"paths": [
"s3://search-bucket/dir1/dir1",
"",
"s3://search-bucket/dir1/dir3"
],
"options": {
"batchSize": 0,
"fileFilterRegx": "",
"replicas": 0,
"skipIfExists": false
}
},
"path": "/api/v1/load"
}
注意: 使用 OIDC 身份验证时,客户端必须提供带有
Bearer
前缀的Authorization
标头。
策略决策流程
参考 OPA 策略遵循清晰的决策流程:
默认拒绝:所有请求都将被拒绝,除非有规则明确允许。
超级管理员覆盖:如果用户具有
SuperAdmin
角色,则始终允许该请求。全局规则:策略检查如果 API 在全局拒绝列表或允许列表中。
基于组和路径的权限:策略检查用户的组是否有权访问请求中指定的资源路径。
特定于操作的规则:最终决定基于操作类型(例如
GET
与POST
/PUT
)和用户角色(例如GroupAdmin
与标准用户)做出。
实施指南和测试
安全默认值:该策略建立在“默认拒绝”原则之上,确保只允许明确允许的操作。
角色权限:
SuperAdmin
具有不受限制的访问权限。GroupAdmin
具有在其授权路径内更新和列出资源的提升权限。测试:通过使用不同的用户令牌发送请求来测试您的策略,以确保它们的行为符合预期。
# 使用用户的 OIDC 令牌进行测试
curl --location 'http://gateway-host:port/api/v1/load' \
--header 'Authorization: Bearer <YOUR_OIDC_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{"paths": ["s3://search-bucket/dir1/dir2/test"], "alias":"loads3a"}'
附录:权限参考
本附录详细介绍了 Alluxio 中各种 S3 API 和 Hadoop 文件系统操作所需的权限。
S3 API 授权
授权分为对象级和存储桶级任务,并为每个操作推荐了 ACL。
对象操作 ACL 矩阵
ListParts
GET
查询中的 uploadId
当前对象
READ
GetObjectTagging
GET
查询中的 tagging
当前对象
READ
GetObject
GET
(其他情况的 GET 方法)
当前对象
READ
PutObjectTagging
PUT
查询中的 tagging
当前对象
WRITE
UploadPartCopy
PUT
查询中的 uploadId 和 x-amz-copy-source
当前对象
READ
目标父目录
WRITE
UploadPart
PUT
查询中的 uploadId
当前对象
WRITE
CopyObject
PUT
x-amz-copy-source 标头
源对象
READ
目标父目录
WRITE
PutObject
PUT
(其他情况的 PUT 方法)
父目录
WRITE
CreateMultipartUpload
POST
查询中的 uploads
父目录
WRITE
CompleteMultipartUpload
POST
查询中的 uploadId
父目录
WRITE
HeadObject
HEAD
-
当前对象
READ
AbortMultipartUpload
DELETE
查询中的 uploadId
当前对象
WRITE
DeleteObjectTagging
DELETE
查询中的 tagging
当前对象
WRITE
DeleteObject
DELETE
(其他情况的 DELETE 方法)
当前对象
WRITE
存储桶操作 ACL 矩阵
ListBuckets
GET
存储桶为空
根路径
EXECUTE
GetBucketTagging
GET
查询中的 tagging
存储桶目录
READ
ListMultipartUploads
GET
查询中的 uploads
存储桶目录
EXECUTE
ListObjects
GET
其他情况的 GET 方法
请求的目录
EXECUTE
PutBucketTagging
PUT
查询中的 tagging
存储桶目录
WRITE
CreateBucket
PUT
(其他情况的 PUT 方法)
根路径
WRITE
DeleteObjects
POST
查询中的 delete
每个指定的对象
WRITE(每个对象)
HeadBucket
HEAD
-
存储桶目录
READ
DeleteBucketTagging
DELETE
查询中的 tagging
存储桶目录
WRITE
DeleteBucket
DELETE
(其他情况的 DELETE 方法)
存储桶目录
WRITE
Hadoop 文件系统授权
Alluxio 的 Hadoop 文件系统集成需要对文件和目录操作进行权限检查。
Hadoop 文件系统函数和权限
append
是
当前路径
WRITE
将数据附加到现有文件。
create
是
父路径
WRITE
创建一个新文件或覆盖一个现有文件。
delete
是
父路径
WRITE
删除文件或目录。
getFileBlockLocations
是
当前路径
READ
返回文件的块位置。
getFileStatus
是
当前路径
READ
返回文件或目录的状态。
setOwner
是
当前路径
WRITE
设置文件或目录的所有者/组。
setPermission
是
当前路径
WRITE
设置文件或目录的权限。
listStatus
是
当前路径
EXECUTE
列出路径中的文件/目录。
mkdirs
是
父路径
WRITE
创建目录和必要的父目录。
open
是
当前路径
READ
打开文件进行读取。
rename
是
源父目录,目标父目录
WRITE, WRITE
重命名文件或目录。
Last updated