对一家典型的《财富》1000 强公司而言,数据可访问性提高 10% ,就能带来超过 6500 万美元的新增净收入。
——Richard Joyce,Forrester¹
在本书前文中,我们已经讨论过数据的重要性,以及如何借助 Amazon Redshift 访问不同类型的结构化或半结构化数据——无论这些数据是本地装载的,还是外部源查询得到的。不过,与“以成本可控、性能优良的方式访问并转换数据”的能力同样重要的,是安全地做到这一点:确保只有正确的人能够访问他们应当访问的数据。许多组织在让所有数据对用户可访问方面举步维艰。在数据量不断膨胀、数据访问需求极高的当下,如何在可访问性与安全性之间取得平衡,既关键又困难。
本章将讨论用户在 Amazon Redshift 中进行安全管理的不同方式,包括“对象级访问控制(Object-Level Access Controls) ”与“数据库角色(Database Roles) ”。我们会探讨需要细粒度访问控制的用户群体场景,以及如何通过“行级安全(Row-Level Security) ”与“动态数据脱敏(Dynamic Data Masking) ”来实现。最后我们将讨论 Amazon Redshift 如何通过“外部数据访问控制(External Data Access Control) ”来管理安全。
对象级访问控制(Object-Level Access Controls)
Amazon Redshift 以对象层级进行组织,每个对象都受一组权限的管控。如第 3 章“设置数据模型与数据摄取(Setting Up Your Data Models and Ingesting Data)”所述:一个 Redshift 集群可包含多个数据库,每个数据库可包含多个模式(schema) ,而每个模式可包含多种对象,如表、视图、函数、过程。除了这些对象,Redshift 还包含适用于所有数据库的管理对象,如用户(users) 、组(groups)与角色(roles) 。
- 用户(user) :登录到数据库的主体。
- 数据库组(group) :用户的集合,一个用户可属于多个组。
- 数据库角色(role) :与组类似,但具有额外功能(本章后文详述)。
可通过 GRANT/REVOKE 语句为用户、组或角色授予对象权限。表 8-1 列出了权限及其适用的对象。更多 GRANT 细节参见在线文档。注意:你可以对列级授予 UPDATE 与 SELECT,这对限制访问 PII 十分有用。
表 8-1. 对象权限(Object privileges)
| 权限 | 对象 |
|---|---|
| INSERT, DELETE, DROP, REFERENCES | TABLE |
| UPDATE | TABLE, COLUMN |
| SELECT | TABLE, VIEW, COLUMN |
| EXECUTE | FUNCTION, PROCEDURE |
| CREATE | DATABASE, SCHEMA |
| TEMP | DATABASE |
| USAGE | LANGUAGE, SCHEMA |
| CREATE MODEL, EXECUTE | MODEL |
| ALTER, SHARE | DATASHARE |
最佳实践:将权限授予数据库组或数据库角色,而非直接授予用户。若要进一步提升权限管理的可扩展性,考虑通过单点登录(SSO)从企业 IdP 传递用户/组/角色信息,按组成员关系授予权限,并在中心统一管理。连接相关内容可回看第 2 章“Amazon Redshift 入门”。
对象所有权(Object Ownership)
除了通过 GRANT 显式授予的权限之外,对象所有者(owner)隐式拥有该对象的全部权限。并且,所有者还拥有对象的管理权限;例如,所有者可以执行 ALTER TABLE 或 REFRESH MATERIALIZED VIEW(见示例 8-1)。在某些情形下你可能需要变更对象所有者,可由当前所有者执行 ALTER。
示例 8-1. 对象所有权
ALTER TABLE table1 OWNER TO user1;
如果某用户仍拥有对象,则不能 DROP 该用户。最佳实践是限制用户可创建对象的位置,便于识别其名下的对象。例如:只给用户对应的 schema 授予 CREATE,而给共享 schema 授予 SELECT。
创建 Redshift 仓库时,你会指定一个具备超级用户(superuser)权限的 admin 用户。超级用户拥有与对象所有者相近的所有权限,不受对象级访问控制限制。超级用户还可创建其它超级用户,或通过 CREATEUSER 属性将普通用户提升为超级用户(示例 8-2)。
示例 8-2. 超级用户权限
CREATE USER adminuser CREATEUSER PASSWORD '1234Admin';
ALTER USER adminuser CREATEUSER;
最佳实践:不要用超级用户处理日常工作。应把相应权限授予数据库组/角色,以清晰表达哪些用户群拥有哪些权限。
默认权限(Default Privileges)
为简化 GRANT 管理,你可以设置表 8-2 所示的默认权限:使得任何由某用户创建或在某 schema 中创建的对象,自动把相应权限授予另一个用户/组/角色。更多默认权限的 GRANT/REVOKE 细节见在线文档。
表 8-2. 默认权限(Default privileges)
| 权限 | 对象 |
|---|---|
| INSERT, UPDATE, DELETE, DROP, REFERENCES | TABLE |
| SELECT | TABLE, VIEW |
| EXECUTE | FUNCTION, PROCEDURE |
Public 模式与搜索路径(Public Schema and Search Path)
当 Redshift 仓库启动时,每个数据库都会创建一个 public 模式,并且每个用户都是 PUBLIC 组成员。默认情况下,PUBLIC 组对各数据库的 public 模式拥有读/写权限。这种策略便于用户在公共空间协作,但任何新增 schema 都需要显式授权。
此外,新建用户的默认 search_path 为 $user,public。这意味着当引用对象时未指明数据库或 schema,Redshift 会先在与用户同名的 schema中查找,再查找 public。这种策略让用户优先在自有 schema中处理本地数据,再访问 public 中的共享数据。正如第 3 章所述,仍建议按主题域将数据模型组织为专用 schema,以便元数据管理与授权;并可将这些 schema 加入用户的 search_path,以简化共享数据的访问。
访问控制实战(Access Controls in Action)
以第 3 章的学生信息系统数据集为例,采用以 public 模式存放共享数据的访问模型。public 对所有用户可读,但仅 etluser 可写。
首先,禁用 PUBLIC 组对 public 模式的写权限,避免误改共享表。示例 8-3 会移除现有写权限,并确保新对象的默认权限同样撤销写权限。注意:该语句不会撤销 SELECT,因此 PUBLIC 组成员仍可读取 public 模式的数据。
示例 8-3. 撤销 PUBLIC 在 schema 上的写入
REVOKE INSERT,UPDATE,DELETE,DROP ON ALL TABLES
IN SCHEMA public FROM GROUP PUBLIC;
REVOKE CREATE ON SCHEMA public FROM GROUP PUBLIC;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public REVOKE INSERT,UPDATE,DELETE ON TABLES FROM PUBLIC;
然后,给 etluser 授予 public 模式上的全部权限,并确保新对象默认同样授予(示例 8-4)。
示例 8-4. 授权用户在 schema 上写入
GRANT ALL ON ALL TABLES IN SCHEMA public TO etluser;
GRANT ALL ON SCHEMA public TO etluser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON TABLES TO USER etluser;
最后,创建用户 faculty_bob,其将继承对 public 的 SELECT 权限;同时创建与用户名同名的 schema,允许其拷贝并改造数据(示例 8-5)。
示例 8-5. 创建用户 schema 并授予 ALL
CREATE USER faculty_bob PASSWORD '<your password>';
CREATE SCHEMA faculty_bob;
GRANT ALL ON SCHEMA faculty_bob TO USER faculty_bob;
在上述权限就位后,假定 etluser 已装载好 public。示例 8-6 的查询会按 semester_id 与 lecture_duration 返回 student_cnt,且无需显式写出 schema 名——无论以 faculty_bob 还是 etluser 运行,因 search_path 中用户 schema 不存在这些表,Redshift 会在 public 中解析。
示例 8-6. 利用 search_path 查询共享表
SELECT
co.semester_id,
cs.lecture_duration,
count(distinct co.student_id) student_cnt,
FROM course_registration cr
JOIN course_outcome co
ON cr.student_id = co.student_id AND
cr.course_id = co.course_id AND
cr.semester_id = co.semester_id
JOIN course_schedule cs
ON cr.course_id = cs.course_id AND
cr.semester_id = cs.semester_id
GROUP BY 1,2;
现在,faculty_bob 需要将 course_schedule 扩展一个字段 lecture_duration_band(按时长分段)。他可以在自有 schema中复制该表并新增字段。注意,因为 search_path,他仍无需写出 schema 名;新表会在 faculty_bob 的 schema 下创建(示例 8-7)。
示例 8-7. 利用 search_path 创建用户表
CREATE TABLE course_schedule AS SELECT * FROM course_schedule;
ALTER TABLE course_schedule ADD column lecture_duration_band varchar(100);
UPDATE TABLE course_schedule SET lecture_duration_band = CASE
WHEN lecture_duration BETWEEN 0 AND 60 THEN '0-1 Hour'
WHEN lecture_duration BETWEEN 61 AND 120 THEN '1-2 Hour'
WHEN lecture_duration > 120 THEN '2+ Hour' END;
为测试结果,faculty_bob 可执行修改后的查询,引用新字段(示例 8-8)。
示例 8-8. 利用 search_path 查询用户表
SELECT
co.semester_id,
cs.lecture_duration_band, -- 从 lecture_duration 改为分段
count(distinct co.student_id) student_cnt,
FROM course_registration cr
JOIN course_outcome co
ON cr.student_id = co.student_id AND
cr.course_id = co.course_id AND
cr.semester_id = co.semester_id
JOIN course_schedule cs
ON cr.course_id = cs.course_id AND
cr.semester_id = cs.semester_id
GROUP BY 1,2;
若结果满足目标,faculty_bob 可将转换规则交给 etluser,由其修改 public 模式。因为 search_path,etluser 同样无需写 schema 名;其 search_path 中的 course_schedule 仅存在于 public,且 etluser 对其拥有写权限(示例 8-9)。
示例 8-9. 修改共享表
ALTER TABLE course_schedule ADD column lecture_duration_band varchar(100);
UPDATE TABLE course_schedule SET lecture_duration_band = CASE
WHEN lecture_duration BETWEEN 0 AND 60 THEN '0-1 Hour'
WHEN lecture_duration BETWEEN 61 AND 120 THEN '1-2 Hour'
WHEN lecture_duration > 120 THEN '2+ Hour' END;
最后,faculty_bob 可以删除自己的用户表,并运行修改后的 SQL,此时将引用已更新的共享表(示例 8-10)。
示例 8-10. 删除用户表并查询已修改的共享表
DROP TABLE course_schedule;
SELECT
co.semester_id,
cs.lecture_duration_band, -- 从 lecture_duration 改为分段
count(distinct co.student_id) student_cnt,
FROM course_registration cr
JOIN course_outcome co
ON cr.student_id = co.student_id AND
cr.course_id = co.course_id AND
cr.semester_id = co.semester_id
JOIN course_schedule cs
ON cr.course_id = cs.course_id AND
cr.semester_id = cs.semester_id
GROUP BY 1,2;
数据库角色(Database Roles)
数据库角色与数据库组类似,但在管理 Amazon Redshift 安全性时,角色提供了两项额外能力,使之更灵活:
- 委派系统权限
数据库角色允许把某些过去仅授予对象所有者或超级用户的系统权限(如 ALTER/DROP 表、刷新物化视图、管理用户等)委派出去。系统权限的完整清单请参见在线文档。 - 角色嵌套与权限传递
数据库角色支持角色嵌套,Redshift 会在每一次角色授权时传递相应的权限。图 8-1 的示例中,将角色 R1 授给 R2,再将 R2 授给 R3,则 R3 拥有 R1、R2、R3 的全部权限。因此,只要把 R3 授给某用户,该用户就继承这三者的全部权限。
图 8-1. 角色层级(Role hierarchy)
Redshift 不允许创建循环授权,因此不能把 R3 再授给 R1,否则会形成环。
为便于上手,Redshift 提供了 4 个系统内置角色(表 8-3),你也可以按需创建更细粒度的自定义角色。内置角色名称带前缀 sys: ,自建角色不能使用该前缀。
表 8-3. 系统内置角色
| 角色名 | 权限说明 |
|---|---|
| sys:operator | 可访问目录/系统表,执行 ANALYZE、VACUUM,或取消查询。 |
| sys:dba | 可创建/删除 schema、table、view、procedure、function、external function,以及 TRUNCATE 表;并继承 sys:operator 的全部权限。 |
| sys:superuser | 与 Redshift 超级用户等同的权限。 |
| sys:secadmin | 可创建/修改/删除用户,创建/删除/授予角色。仅当显式授予此角色对象权限时,才可访问用户表数据。 |
数据库角色实战(Database Roles in Action)
利用系统内置角色,你可以提升用户权限。假设有一位 etluser 需要管理数据库对象、监控装载进程,但不管理安全对象(用户/组/角色)。示例 8-11 展示如何授予这些权限:
示例 8-11. 向用户授予数据库角色
GRANT ROLE sys:dba TO etluser;
GRANT ROLE sys:operator TO etluser;
同样,你也可以通过角色来限制系统权限。设想某用户需要对象管理权限,但不应拥有用户管理权限。示例 8-12 中,先将 etluser 提升为超级用户,再创建一个角色 revoke_secadmin,在该角色上撤销用户管理相关权限,并把该角色授给 etluser:
示例 8-12. 通过角色收回用户管理权限
ALTER USER etluser CREATEUSER;
CREATE ROLE revoke_secadmin;
REVOKE CREATE USER, DROP USER, ALTER USER,
CREATE ROLE, GRANT ROLE
FROM ROLE revoke_secadmin;
GRANT ROLE revoke_secadmin TO etluser;
更多基于角色的权限管理示例,参见博文 “Simplify Management of Database Privileges in Amazon Redshift Using Role-Based Access Control” 。
行级安全(Row-Level Security, RLS)
Redshift 的 RLS 提供细粒度访问控制:判断当前登录用户是否可访问表中的某些记录,并仅返回被允许访问的记录。
- RLS 策略通过引用被查询表中的零个或多个列,并将其与静态值或动态值(如
current_user或会话配置变量)比较来定义。如何定义策略详见CREATE RLS POLICY文档;设置会话变量详见SET_CONFIG文档。 - RLS 策略随后附加(attach)到表,并且必须关联到用户、数据库角色或 PUBLIC 组。详见
ATTACH RLS POLICY文档。 - 博文 “Achieve Fine-Grained Data Security with Row-Level Access Control in Amazon Redshift” 展示了制定 RLS 策略的多种选项。
RLS 实战(Row-Level Security in Action)
继续使用第 3 章定义的学生信息系统数据模型:目标是让学生只能查看自己的数据。做法是创建一个检查 current_user 的策略。此处假设当前用户名与表中的 student_id 一致,且所有学生登录后都隶属于 student 角色(示例 8-13)。
示例 8-13. 使用 current_user 的行级安全
CREATE RLS POLICY student
WITH (student_id int)
USING (student_id = current_user);
GRANT SELECT ON
student,
course_outcome,
course_registration,
degree_plan
TO RLS POLICY student;
ATTACH RLS POLICY student ON student TO ROLE student;
ATTACH RLS POLICY student ON course_outcome TO ROLE student;
ATTACH RLS POLICY student ON course_registration TO ROLE student;
ATTACH RLS POLICY student ON degree_plan TO ROLE student;
配置生效后,凡附加了该策略的表,只会返回与当前用户相关的数据。例如执行 SELECT * FROM course_outcome; 时,仅返回该学生的记录。
Redshift 支持同一表附加多个 RLS 策略。当用户(直接或经角色)在某表上命中多条策略时,Redshift 会以 AND 逻辑同时应用。
若某大学有成千上万学生,无法为每名学生都建数据库用户,而是由应用使用共享数据库账号(如 application_user)访问数据,则上述策略将失效(student_id 不再等于 current_user)。此时,如果应用知道具体的学生身份,可设置会话配置变量,并在 RLS 策略中使用它。假设 application_user 也隶属 student 角色(示例 8-14)。
示例 8-14. 使用会话配置变量的行级安全
DROP RLS POLICY student CASCADE;
CREATE RLS POLICY student
WITH (student_id int)
USING (student_id = current_setting('app.student_id', FALSE));
GRANT SELECT ON
student,
course_outcome,
course_registration,
degree_plan
TO RLS POLICY student;
ATTACH RLS POLICY student ON student TO ROLE student;
ATTACH RLS POLICY student ON course_outcome TO ROLE student;
ATTACH RLS POLICY student ON course_registration TO ROLE student;
ATTACH RLS POLICY student ON degree_plan TO ROLE student;
最后,只要应用在查询这些表之前执行 set_config,RLS 策略即可生效(示例 8-15)。
示例 8-15. 使用 set_config
SELECT set_config('app.student_id', '<VALUE>', FALSE);
再来看第 7 章“通过数据共享开展协作”的场景:多个租户可访问某个 datashare,并在消费端数据仓库应用 RLS。你已在 school 表中新增了 consumer_namespace 字段。若在消费端设置示例 8-16 的 RLS 策略,即可确保只有被授权的消费方可以查询被授权学校的数据。
示例 8-16. 基于消费端命名空间的行级安全
CREATE RLS POLICY consumer
WITH (school_id int)
USING (school_id = (
SELECT school_id
FROM school
WHERE consumer_namespace = current_namespace));
GRANT SELECT ON
student,
course_outcome,
course_registration,
degree_plan
TO RLS POLICY consumer;
ATTACH RLS POLICY consumer ON student TO ROLE school;
ATTACH RLS POLICY consumer ON course_outcome TO ROLE school;
ATTACH RLS POLICY consumer ON course_registration TO ROLE school;
ATTACH RLS POLICY consumer ON degree_plan TO ROLE school;
RLS 注意事项(Considerations)
引入 RLS 后,可能会关心依赖对象(视图、物化视图、数据共享对象、数据湖外部表、联邦表)如何受影响。表 8-4 总结了其行为与替代方案。
表 8-4. RLS 依赖对象行为
| 依赖对象 | 行为 | 替代/变通 |
|---|---|---|
| 视图(View) | 不能对视图附加 RLS 策略。 | 视图继承其底层表的策略,通常无需对视图单独附加。 |
| 物化视图(MV) | 不能对 MV 附加 RLS;且不能在被 MV 引用的表上创建 RLS 策略。 | 将 MV 逻辑改为存储过程维护的物理表,再对该表应用 RLS。 |
| 数据共享(Datashare) | 可在提供方为表附加 RLS;但消费方不受提供方策略约束。 | 消费方可在其侧对 datashare 对象自定义策略。 |
| 数据湖外部表 | 不能对来自数据湖的外部表附加 RLS。 | 可使用 Lake Formation 的单元格级过滤,并赋予在外部 schema 中使用的 IAM 角色。 |
| 联邦表(Federated) | 不能对来自联邦源的外部表附加 RLS。 | 采用受限数据库视图策略,并将其授予外部 schema 定义中的联邦用户。 |
更多 RLS 使用注意事项,请参见在线文档。
动态数据脱敏(Dynamic Data Masking, DDM)
DDM 的作用与场景
DDM 用于对数据进行脱敏显示,常见于合规/监管要求或内部隐私标准。它会在查询时基于用户权限动态处理敏感数据的展示。相较于在静态数据上做混淆并改造装载与转换流程,DDM 更易扩展。你通过掩码(masking)策略来控制访问,这些策略附加到表与列上,并关联到某个用户、数据库角色或 PUBLIC 组。借助 DDM,你可以在不修改转换代码、底层数据或应用 SQL 的情况下,快速响应不断演进的隐私需求。
正如“对象级访问控制”所述,列级安全(CLS)可通过 REVOKE 应用。但使用 CLS 时,用户执行 SELECT * 这类查询会报错。相比之下,应用 DDM 策略不会报错,而是对数据进行模糊化或返回 NULL。
定义掩码策略
掩码策略通过引用用于判断如何脱敏的列,并指定评估机制来决定脱敏后的输出值。多数情况下,仅对被脱敏的单列定义策略就足够;复杂场景下,可额外引用该表中的其他列以满足规则。
支持的脱敏机制包括:静态值、内联 SQL、标量 SQL UDF 或 Python UDF;它们都可引用 current_user 或会话配置变量等动态值。如何定义 DDM 策略,详见在线文档。
定义好策略后,需要将其附加到表上(并声明策略用到的输入列),同时将策略关联到用户、数据库角色或 PUBLIC。如何附加策略,详见在线文档。
当同一用户因隶属多个角色而匹配多条策略时,使用 PRIORITY 优先级来决定生效策略。
鉴于 DDM 策略的灵活性,可选方案很多。可参考博文 “How Dynamic Data Masking Support in Amazon Redshift Helps Achieve Data Privacy and Compliance” 了解更多实操思路。
动态数据脱敏实战
延续第 3 章“学生信息系统与数据摄取”的模型,演示如何对数据实施 DDM。假设要实现如下 PII 保护规则:
- 默认情况下,PII 显示为 NULL。
birth_date与email_address被视为 PII。- 学生本人应能看到未脱敏的数据。
- 教师可以看到生日的月/日,但不能看到年份。
- 教师应看到脱敏后的邮箱。
1) 创建默认策略(按数据类型定义策略)。示例 8-17 为 varchar 与 date 类型返回 NULL 的策略:
-- 示例 8-17. 定义默认 DDM 策略
CREATE MASKING POLICY null_varchar
WITH (i_varchar varchar) USING (NULL);
CREATE MASKING POLICY null_date
WITH (i_date date) USING (NULL);
2) 附加默认策略到 student.email_address 与 student.birth_date,并赋予 PUBLIC(所有用户都是其成员)。注意优先级设为 99(示例 8-18)。稍后会添加更低优先级数字(代表更高优先级)的策略以覆盖默认策略:
-- 示例 8-18. 附加默认策略
ATTACH MASKING POLICY null_varchar
ON student(email_address) TO PUBLIC PRIORITY 99;
ATTACH MASKING POLICY null_date
ON student(birth_date) TO PUBLIC PRIORITY 99;
备注:可省略
TO PUBLIC,若未指定用户或角色,默认即为 PUBLIC。
3) 为“学生本人可见原文”创建策略。示例 8-19 中策略接受两个输入:被脱敏列与辅助列(如 student_id)。当 student_id = current_user 时返回原值,否则返回 NULL:
-- 示例 8-19. 直通(passthrough)策略
CREATE MASKING POLICY passthrough_varchar_student_id
WITH (i_varchar varchar, student_id int) USING (
CASE student_id WHEN current_user THEN i_varchar ELSE NULL END
);
CREATE MASKING POLICY passthrough_date_student_id
WITH (i_date date, student_id int) USING (
CASE student_id WHEN current_user THEN i_date ELSE NULL END
);
4) 将直通策略附加到学生角色,优先级设为 25(高于 99),以满足规则 3(示例 8-20):
-- 示例 8-20. 给 student 角色附加直通策略
ATTACH MASKING POLICY passthrough_varchar_student_id
ON student(email_address)
USING (email_address, student_id) TO ROLE student PRIORITY 25;
ATTACH MASKING POLICY passthrough_date_student_id
ON student(birth_date)
USING (birth_date, student_id) TO ROLE student PRIORITY 25;
5) 为教师定义“仅遮蔽出生年份”的策略。示例 8-21 通过 SQL 函数将年份替换为 1900:
-- 示例 8-21. 日期不含年份的策略
CREATE MASKING POLICY date_noyear
WITH (i_date date) USING (
(extract('mon' FROM i_date)||'/'||date_part('day', i_date)||'/1900')::date
);
6) 为教师定义“邮箱局部脱敏”的策略。示例 8-22 使用 Python UDF 将邮箱前 4 个字符替换为 ####,并处理 @ 存在与本地部分长度不足等情况:
-- 示例 8-22. 邮箱脱敏策略
CREATE OR REPLACE FUNCTION redact_email (i_email TEXT)
RETURNS TEXT IMMUTABLE
AS $$
md=i_email.find('@')
ln=len(i_email)
IF md>0 and ln>0:
RETURN '####'+i_email[0:md][4:]+i_email[md:]
ELSE:
RETURN 'invalid email'
$$ LANGUAGE plpythonu;
CREATE MASKING POLICY email_redact
WITH (i_email varchar) USING (redact_email(i_email));
7) 将教师策略附加到相应列并赋予 faculty 角色,优先级设为 50(示例 8-23)。当用户同时属于 faculty 与 student 两个角色时,由于 25 < 50,学生策略优先:
-- 示例 8-23. 给 faculty 角色附加日期与邮箱脱敏策略
ATTACH MASKING POLICY date_noyear
ON openlearn.student(birth_date) TO ROLE faculty PRIORITY 50;
ATTACH MASKING POLICY email_redact
ON openlearn.student(email_address) TO ROLE faculty PRIORITY 50;
DDM 注意事项
与 RLS 相似,你可能关心依赖对象(视图、物化视图、数据共享对象、数据湖外表、联邦表)如何受影响。见表 8-5:
表 8-5. DDM 依赖对象行为
| 依赖对象 | 行为 | 变通方案 |
|---|---|---|
| 视图(View) | 不能对视图附加 DDM 策略。 | 视图继承底层表的策略,一般无需单独附加。 |
| 物化视图(MV) | 不能对 MV 附加 DDM;且不能在被 MV 引用的表上创建 DDM 策略。 | 将 MV 逻辑改为存储过程维护物理表,再对该表应用 DDM。 |
| 数据共享(Datashare) | 可在提供方为表附加 DDM;但消费方不受提供方策略约束。 | 消费方可在其侧对 datashare 对象自定义策略。 |
| 数据湖外部表 | 不能对来自数据湖的外部表附加 DDM。 | 在静态数据上做混淆/脱敏/令牌化;在 Redshift 中用 UDF 解密(按需)。 |
| 联邦表(Federated) | 不能对来自联邦源的外部表附加 DDM。 | 在源侧对静态数据做混淆/令牌化;在 Redshift 用 UDF 解密(按需)。 |
更多关于 DDM 的使用考量,请参见在线文档。
外部数据访问控制(External Data Access Control)
正如第 1 章《AWS for Data》所述,现代数据架构常常需要让用户在数据产生之处直接访问数据,即使这些数据不在 Amazon Redshift 内部。第 4 章《数据转换策略》中介绍了用户如何在使用 Amazon Redshift 进行数据转换时直接访问外部数据源。为治理这些外部数据源中的数据访问,Amazon Redshift 借助 IAM 角色(IAM roles) 。
- 对于 COPY、UNLOAD、EXTERNAL FUNCTIONS、CREATE MODEL 等场景,执行查询的用户需要在语句中显式引用 IAM 角色,并且该用户需要被授权**可假设(assume)**该 IAM 角色。
- 对于业务库(operational data) 、Amazon S3 数据与流式写入等场景,则通过使用 IAM 角色来创建 external schema。
下面分别说明如何将 IAM 角色关联到 Redshift、如何授权可假设角色的权限,以及如何用角色建立 external schema。最后将介绍如何借助 Lake Formation 对 S3 数据湖实施细粒度访问控制。
关联(Associate)IAM 角色
可在启动数据仓库时或事后通过控制台或 API 将 IAM 角色关联到 Redshift。但只有当该 IAM 角色的**信任策略(trust relationship)**允许 Redshift 服务(redshift.amazonaws.com)或 Redshift Serverless(redshift-serverless.amazonaws.com)执行 sts:AssumeRole 时,才能将其附加至数据仓库。示例 8-24 展示了使用 AWS CLI 为某个集群附加名为 myRedshiftRole 的 IAM 角色:
示例 8-24. 为集群添加 IAM 角色
aws redshift modify-cluster-iam-roles \
--cluster-identifier mycluster \
--add-iam-roles arn:aws:iam::1234567890:role/myRedshiftRole
本书前文多次示范通过默认 IAM 角色(default)访问外部服务(如 S3),以便用户书写 SQL 时无需关心 ARN 细节或选择哪个角色。示例 8-25 通过 CLI 将 myRedshiftRole 设为默认角色:
示例 8-25. 设置默认 IAM 角色
aws redshift modify-cluster-iam-roles \
--cluster-identifier mycluster \
--default-iam-role-arn arn:aws:iam::1234567890:role/myRedshiftRole
示例 8-26(第 3 章) 使用具有读取 s3://openlearn-redshift/assessments 权限的默认 IAM 角色将数据装载至 Redshift:
COPY "openlearn"."assessments"
FROM 's3://openlearn-redshift/assessments'
IAM_ROLE default
DELIMITER ',' COMPUPDATE ON REGION 'us-east-1'
REMOVEQUOTES IGNOREHEADER 1;
示例 8-27(第 7 章) 使用默认 IAM 角色将 Redshift 数据导出到 s3://openlearn/studentactivities(该角色需具备写入权限):
UNLOAD ('select * from mv_student_lmsactivities_and_score')
TO 's3://openlearn/studentactivities'
IAM_ROLE default PARQUET PARTITION BY (school_id);
示例 8-28(第 4 章) 创建指向 AWS Lambda 函数的标量 UDF,IAM 角色需具备执行 f-kms-encrypt 的权限:
CREATE OR REPLACE EXTERNAL FUNCTION
f_kms_encrypt (key varchar, value varchar)
RETURNS varchar(max) STABLE
LAMBDA 'f-kms-encrypt'
IAM_ROLE default;
示例 8-29(第 6 章) 通过 SQL 创建、训练并运行 ML 模型推理;IAM 角色需具备访问 SageMaker 与写临时 S3 桶 bucketname 的权限:
CREATE MODEL demo_ml.customer_churn_model
FROM (SELECT state,
area_code,
total_charge/account_length AS average_daily_spend,
cust_serv_calls/account_length AS average_daily_cases,
churn
FROM demo_ml.customer_activity
WHERE record_date < '2020-01-01')
TARGET churn
FUNCTION predict_customer_churn
IAM_ROLE default
SETTINGS (S3_BUCKET 'bucketname');
如需访问跨账户资源,可采用**角色链(role chaining)**策略:在两个账户的 IAM 角色之间建立互信。详见在线文档。
授权可假设角色(Authorize Assume Role)权限
当你将 IAM 角色关联到 Redshift 数据仓库后,默认PUBLIC 组中的任何成员都可以 ASSUMEROLE。在拥有多类型用户的大型部署中,通常需要进一步限制谁可以假设哪些角色。可先执行 REVOKE 移除默认行为(示例 8-30):
示例 8-30. 撤销公共可假设 IAM 角色
REVOKE ASSUMEROLE on ALL from PUBLIC for ALL;
随后,通过 GRANT 将特定用户/数据库角色/数据库组与可假设的角色精确绑定。例如,赋予 faculty 数据库角色假设具有调用 f_kms_decrypt Lambda 权限的 IAM 角色(示例 8-31):
示例 8-31. 授权假设可用于 Lambda 的 IAM 角色
GRANT ASSUMEROLE ON 'arn:aws:iam::1234567890:role/myLambdaRole'
TO ROLE faculty FOR EXTERNAL FUNCTION
再如,授予 etluser 数据库角色假设具备读取 S3 原始数据权限的 IAM 角色(示例 8-32):
示例 8-32. 授权假设可用于 S3 的 IAM 角色
GRANT ASSUMEROLE ON 'arn:aws:iam::1234567890:role/myETLRole'
TO ROLE etluser FOR COPY, UNLOAD
注意:一个 Redshift 数据仓库最多可关联 50 个 IAM 角色。设计角色策略时请考虑此上限。
建立 External Schemas
要实时查询外部源(如 S3 上的开格式文件、RDS MySQL/PostgreSQL 业务库、Kinesis 或托管 Kafka 的流式数据),必须先建立 external schema。你还可以通过对该 schema 执行 GRANT/REVOKE USAGE,限制哪些用户/组/角色可查询这些外部源。
示例 8-33(第 4 章) :在 Redshift 中将 schema federatedschema 映射到 Amazon RDS 的 PostgreSQL 业务库:
CREATE EXTERNAL SCHEMA IF NOT EXISTS federatedschema
FROM POSTGRES DATABASE 'db1' SCHEMA 'pgschema'
URI '<rdsname>.<hashkey>.<region>.rds.amazonaws.com'
SECRET_ARN 'arn:aws:secretsmanager:us-east-1:123456789012:secret:pgsecret'
IAM_ROLE default;
使用联邦查询访问业务库时,将建立到源库的网络连接,并使用 AWS Secrets Manager 中的凭证。用于 external schema 的 IAM 角色需要对该 Secret 具有读取权限;Secret 中的数据库用户还需在源库具备执行查询的权限;同时 Redshift 与源库之间需有可达的 TCP/IP 网络路径。详见联邦查询在线文档。
示例 8-34(第 3 章) :在 Redshift 中将 schema kds 映射到 Kinesis 数据流:
CREATE EXTERNAL SCHEMA kds
FROM KINESIS
IAM_ROLE default;
此处 IAM 角色需具备对目标流的读取权限(kinesis:Get*、kinesis:List*、kinesis:Describe*),并可按需限定到具体流。流式写入细节见在线文档。
示例 8-35(第 4 章) :在 Redshift 中将 EXTERNAL SCHEMA 映射到 AWS Glue Data Catalog 的某个数据库:
CREATE EXTERNAL SCHEMA IF NOT EXISTS externalschema
FROM data catalog DATABASE 'externaldb'
IAM_ROLE default;
在示例 8-35 中,授权由默认 IAM 角色决定,即只有一个身份主体来界定可访问的数据。若你有多类用户需要对 S3 数据实施不同粒度的访问控制,一种做法是为不同 IAM 角色建立不同的 external schema,并通过前述“授权可假设角色”来控制谁可用哪个 schema。
然而,若你对 Redshift 采用联合访问(federated access) ,可配置成让每个用户组以不同 IAM 角色登录 Redshift,并在 external schema 定义中使用 SESSION 关键字透传当前会话的 IAM 角色(示例 8-36)。同时提供 CATALOG_ID(与你的 AWS 账号 ID 一致)以便实现跨账户 S3 访问。此策略可避免为多类用户创建多个 external schema,亦无需在 Redshift 上附加大量 IAM 角色。详见在线文档。
示例 8-36. 使用 SESSION 授权的 S3 external schema
CREATE EXTERNAL SCHEMA IF NOT EXISTS externalschema
FROM data catalog DATABASE 'externaldb'
IAM_ROLE 'SESSION'
CATALOG_ID '123456789012';
Glue Data Catalog 除供 Redshift 使用外,也被 Amazon Athena、AWS Glue ETL、Amazon EMR 共享。用于 external schema 的 IAM 角色需具备对 Glue Catalog 的读取权限,以及对 Catalog 中对象(S3 上的实际数据)的读取权限。可选地,赋予对 Glue Catalog 的写入权限,以便在 Redshift 中使用 CREATE EXTERNAL TABLE 直接注册外部表。若启用了 Lake Formation,对象权限将由 Lake Formation 管理(下一节详述)。
如未启用 Lake Formation,则对象权限由 IAM 策略决定;需在 IAM 策略中包含合适的 s3:Get* 与 s3:List*,并通过资源范围限制对 S3 对象的访问。如何查询 S3 数据详见在线文档。
使用 Lake Formation 实现细粒度访问控制
当数据湖规模与用户群体增大,仅凭 IAM 策略管理访问控制会变得繁琐,尤其当多个用户组的权限存在重叠时。并且,IAM 策略对 S3 的授权粒度主要基于桶/前缀,通常只能做到整表或分区级授权,很难做到更细粒度。
要开始使用 AWS Lake Formation,你的 external schema 无需变更,因为 Lake Formation 与 Redshift 共享同一个 Glue Data Catalog。从 IAM 权限迁移到 Lake Formation 的过程是:保持原有数据结构不变,先启用 Lake Formation 来接管访问控制,然后把各 IAM 角色上的对象级 S3 访问策略移除,转而在 Lake Formation 中为这些 IAM 角色授予数据库/表/列等层面的权限(以及行/单元格级等更高级策略)。完成后,访问管理将集中于 Lake Formation 控制台,你可以使用更贴近数据抽象的权限模型来治理。
为简化对 Lake Formation 权限的管理,你也可以在 Redshift 的 SQL 界面中直接执行 GRANT。例如对外部表 externalschema.transactions 实施列级安全:为财务角色授予所有列的选择权限,而对分析师角色去掉 customer_id(示例 8-37):
示例 8-37. 在 Redshift 中授予 Lake Formation 外表权限
GRANT SELECT (
returnflag,linestatus,zip,quantity,extendedprice,
discount,tax,customer_id,year,month)
ON EXTERNAL TABLE externalschema.transactions
TO IAM_ROLE 'arn:aws:iam::1234567890:role/myFinanceRole';
GRANT SELECT (returnflag,linestatus,zip,quantity,
extendedprice,discount,tax,year,month)
ON EXTERNAL TABLE externalschema.transactions
TO IAM_ROLE 'arn:aws:iam::1234567890:role/myAnalystRole';
更多内容可参考博文: “Centralize Governance for Your Data Lake Using AWS Lake Formation While Enabling a Modern Data Architecture with Amazon Redshift Spectrum” 。
小结
本章详细阐述了对Redshift 内部数据以及Redshift 可访问的外部数据的多种访问控制方式:如何在保证数据安全的同时,为用户提供足够灵活的分析能力。
下一章将介绍从现有分析环境迁移至 Amazon Redshift 的考量与策略,包括工具与服务、示例迁移流程,以及如何借助 Redshift 加速你的迁移进程。