请注意本文部分内容经过AI辅助生成,虽然经过笔者检查但是并不保证内容的正确性,请自行判断准确性,本文对相关后果不承担责任
EMR Security Configuration(安全配置)是 Amazon EMR 提供的可复用安全策略模板,用于集中定义集群的加密、认证和授权设置。
- 创建后存储在 EMR 服务端(非集群本身),可被多个集群复用
- 适用于 EMR 4.8.0 及更高版本,并且创建后不可修改,只能删除重建
控制台创建 Security Configuration 时包含四大部分,本文仅涉及In Transit和Kerberos 部分
| 部分 | 说明 |
|---|---|
| Encryption | 静态数据加密(At Rest)和传输中数据加密(In Transit) |
| Authentication | 认证协议:IAM / Kerberos / LDAP |
| Authorization | 授权模式:Instance Profile / Runtime Role / Lake Formation / Ranger |
| IMDS | EC2 实例元数据服务版本控制 |
传输中数据加密(In Transit)
启用后,EMR 自动为支持 TLS 的开源组件配置加密通信(Spark、Tez、MapReduce 等)。

证书提供方式
| 方式 | 说明 |
|---|---|
| PEM | 自行生成 PEM 证书,打包为 zip 上传到 S3 |
| Custom | 自定义 Java 类实现 TLSArtifactsProvider 接口,JAR 上传到 S3 |
PEM 证书 zip 包文件要求
| 文件名 | 是否必须 | 说明 |
|---|---|---|
| privateKey.pem | 必须 | 私钥文件 |
| certificateChain.pem | 必须 | 证书链文件 |
| trustedCertificates.pem | 可选 | 受信任的 CA 证书(建议提供非 Java 默认信任的 CA 证书) |
PEM 文件在 TLS 通信中的角色
EMR 启用 In-transit encryption 后,每个节点都会从 S3 下载同一份证书 zip 包。集群内节点既是 Server 又是 Client(互相通信),所以每个节点同时需要三个文件:
每个 EMR 节点:
├── privateKey.pem ← 当别人连我时,用它签名证明"我确实是证书持有者"
├── certificateChain.pem ← 当别人连我时,把这个发给对方作为身份证明
└── trustedCertificates.pem ← 当我连别人时,用它验证对方的证书是否可信
TLS 握手过程(以 Spark Executor 连接 Driver 为例):
节点 A (Driver/Server) 节点 B (Executor/Client)
┌───────────────────────┐ ┌───────────────────────┐
│ │ ① TCP 连接 │ │
│ │ ◄──────────────── │ │
│ │ │ │
│ certificateChain.pem │ ──────────────► │ trustedCertificates │
│ (公钥证书=身份证) │ ② 发送证书 │ .pem 验证证书可信 ✅ │
│ │ │ │
│ privateKey.pem │ │ │
│ (私钥=证明身份证是我的) │ ◄──────────────── │ 用证书中的公钥加密 │
│ │ ③ 密钥协商 │ 预主密钥发给 A │
│ │ │ │
│ ④ 双方建立加密通道,开始传输数据 │ │
└───────────────────────┘ └───────────────────────┘
各组件使用证书的通信场景
| 通信场景 | Server 端(用 privateKey + certificateChain) | Client 端(用 trustedCertificates 验证) |
|---|---|---|
| Spark shuffle 数据传输 | Executor 的 BlockManager | 另一个 Executor |
| Spark Driver ↔ Executor RPC | Driver | Executor |
| HDFS DataNode 之间块复制 | 源 DataNode | 目标 DataNode |
| HDFS Client 读写数据 | DataNode | NameNode / Client |
| YARN NodeManager ↔ ResourceManager | ResourceManager | NodeManager |
| Tez Task 间 shuffle | Task 所在节点 | 另一个 Task 节点 |
| MapReduce shuffle | Reducer 拉取 Mapper 输出 | Mapper 所在节点 ShuffleHandler |
| HBase RegionServer 间通信 | RegionServer | 另一个 RegionServer |
| Presto/Trino Worker ↔ Coordinator | Coordinator | Worker |
自签名证书与 trustedCertificates.pem
TLS 连接时,Client 需要验证 Server 的证书是否由受信任的 CA 签发。Java 有默认的受信任 CA 列表(cacerts),包含 DigiCert、Let's Encrypt 等公共 CA。自签名证书不在这个列表里,验证会失败。trustedCertificates.pem 告诉 EMR "除了 Java 默认信任的 CA,也信任这个证书"。自签名证书既是证书本身也是签发者,所以直接复制即可。
自签名时:certificateChain.pem == trustedCertificates.pem(自己签自己,自己信任自己)
CA 签发时:trustedCertificates.pem = CA 根证书(如果不在 Java 默认信任列表中才需要提供)
主机名验证(hadoop.ssl.hostname.verifier)
TLS 握手时 Hadoop 默认验证对方证书 CN 是否与实际主机名匹配。使用通配符证书(如 CN=*.cn-north-1.compute.internal)时,节点主机名 ip-10-0-1-23.cn-north-1.compute.internal 能匹配通配符,验证通过。如果证书 CN 是具体主机名,其他节点连接时主机名不匹配,TLS 握手失败。
| 值 | 行为 |
|---|---|
| DEFAULT(默认) | 严格验证 CN/SAN 与主机名匹配 |
| ALLOW_ALL | 跳过主机名验证,只验证证书本身是否可信 |
设置方式(创建集群时通过 classification 传入):
json
[{"Classification": "core-site", "Properties": {"hadoop.ssl.hostname.verifier": "ALLOW_ALL"}}]
集群内所有节点共用同一份证书,通配符确保无论哪个节点出示证书,主机名验证都能通过。如果要用具体主机名的证书(每个节点不同),PEM zip 方式无法实现,需要用 Custom 证书提供者(Java TLSArtifactsProvider 实现),在运行时根据节点主机名动态生成/获取对应证书。
自签名 PEM 证书的要求
- 通配符域名(CN) :证书的 Common Name 必须使用通配符匹配集群所在 VPC 的内部域名
- cn-north-1(北京):
CN=*.cn-north-1.compute.internal - cn-northwest-1(宁夏):
CN=*.cn-northwest-1.compute.internal - 其他区域:
CN=*.<region>.compute.internal
- cn-north-1(北京):
- 密钥算法:推荐 RSA 2048 位或更高
- 有效期:根据需要设置,示例中使用 365 天
- 如果不使用通配符 :必须设置
hadoop.ssl.hostname.verifier=ALLOW_ALL(EMR 7.3.0+ 通过core-siteclassification 配置,低版本直接修改core-site.xml) - 自签名证书仅适合测试,生产环境建议使用内部 CA 签发或自定义证书提供者
生成自签名证书示例
bash
# 生成自签名证书和私钥(以北京区域为例)
openssl req -x509 -newkey rsa:2048 \
-keyout privateKey.pem \
-out certificateChain.pem \
-days 365 -nodes \
-subj '/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=MyDept/CN=*.cn-north-1.compute.internal'
# 自签名证书需要复制为 trustedCertificates.pem
cp certificateChain.pem trustedCertificates.pem
# 打包为 zip(文件名必须严格匹配)
zip -r -X my-certs.zip certificateChain.pem privateKey.pem trustedCertificates.pem
# 上传到 S3
aws s3 cp my-certs.zip s3://my-bucket/emr-certs/my-certs.zip
Kerberos 认证
控制台提供三种认证协议:IAM、Kerberos、LDAP。
IAM(AWS Identity and Access Management)是默认方式,使用 IAM 身份联合或临时安全凭证管理用户访问,无需额外配置。
Kerberos 是一个网络认证协议,通过可信第三方 KDC(Key Distribution Center)验证身份,解决"在不安全网络中证明你是谁"的问题。没有 Kerberos 时,任何人可以通过 HADOOP_USER_NAME=hdfs 伪装成超级用户,HDFS 权限形同虚设。启用 Kerberos 后,每个用户/服务必须先通过 KDC 认证获取 Ticket,才能访问集群资源。
| 概念 | 说明 |
|---|---|
| KDC | 可信第三方,负责验证身份、签发票据 |
| Principal | 身份标识,格式 name/instance@REALM,如 hdfs/ip-10-0-1-1@EC2.INTERNAL |
| TGT (Ticket) | KDC 签发的临时通行证,有时效性(默认 24h),用来向各服务证明身份 |
| Keytab | 存储 Principal 密钥的文件(等同于密码文件),服务进程用它自动获取和续期 TGT |
| Service Ticket | 用 TGT 向 KDC 换取的针对具体服务的票据,访问 HDFS/Hive/YARN 时出示 |

TGT vs Keytab 的区别:
- Keytab = 密码文件(长期有效,存在磁盘上,服务进程用它自动认证)
- TGT = 临时通行证(有时效,过期作废,用 Keytab 或密码换取)
Kerberos 保护了哪些组件
| 组件 | 没有 Kerberos | 有 Kerberos |
|---|---|---|
| HDFS | 任何人可伪装成任何用户读写文件 | 必须持有有效 Ticket,按 Principal 身份做权限检查 |
| YARN | 任何人可提交/杀死作业 | 只能管理自己的作业 |
| Hive/HiveServer2 | 匿名连接,无身份验证 | 必须 Kerberos 认证后才能连接和查询 |
| Spark | 无身份区分 | Driver/Executor 之间通过 Kerberos 互相认证 |
| HBase | 任何人可读写任何表 | 按 Principal 做 ACL 控制 |
| Presto/Trino | 无认证 | 通过 Kerberos 认证用户身份 |
注意:Kerberos 只负责认证(你是谁),授权(你能干什么)由各组件自己的权限系统负责(如 HDFS 文件权限/ACL、Hive 权限、Ranger 策略等)。
两种模式
模式一:Cluster-dedicated KDC
- Master 节点同时运行 KDC + NameNode + HiveServer2 等
- 适用:单集群、测试环境
- 优点:简单,EMR 自动安装配置
- 缺点:主节点挂了 KDC 也没了
模式二:External KDC
- 独立 KDC 服务器,多个 EMR 集群共用
- 适用:多集群共享、生产环境、企业 AD 集成
- 优点:统一身份管理,集群销毁不影响 KDC
- 缺点:需自己维护 KDC,网络需可达
配置分两个地方
Security Configuration(可复用模板)中配置:
- Provider:ClusterDedicatedKdc / ExternalKdc
- Ticket lifetime:默认 24 小时,最大 596523 小时。集群应用和服务会自动续期,SSH 用户需手动执行
kinit续期 - Cross-realm trust(可选):对方的 Realm、Domain、Admin server(端口 749)、KDC server(端口 88)
创建集群时通过 --kerberos-attributes 配置(每个集群不同):
- Realm:Kerberos realm 名称,惯例全大写
- KdcAdminPassword:KDC 管理员密码
bash
aws emr create-cluster \
--security-configuration "MyKerberosSecConfig" \
--kerberos-attributes '{
"Realm": "EC2.INTERNAL",
"KdcAdminPassword": "MyKdcP@ss123"
}' \
...
Realm 命名规则
| 场景 | 能随便写吗 | 建议 |
|---|---|---|
| Cluster-dedicated KDC,独立使用 | 随便写 | 用 EC2.INTERNAL,简单省事 |
| Cluster-dedicated KDC,其他集群要引用 | 随便写,但定了别改 | 取有意义的名字,如 EMR.PROD |
| External KDC | 必须和外部 KDC 一致 | 问 KDC 管理员 |
| Cross-realm trust 对接 AD | 对方 Realm 必须和 AD 一致 | 自己的 Realm 可以随便取 |
集群创建后,所有 Principal 自动带上 Realm 后缀:
hdfs/ip-10-0-1-1.cn-north-1.compute.internal@EC2.INTERNAL
yarn/ip-10-0-1-1.cn-north-1.compute.internal@EC2.INTERNAL
hive/ip-10-0-1-1.cn-north-1.compute.internal@EC2.INTERNAL
kadmin.local vs kadmin
kadmin.local |
kadmin |
|
|---|---|---|
| 连接方式 | 直接读 KDC 本地数据库文件 | 通过网络连接 kadmin 服务(端口 749) |
| 需要认证吗 | 不需要,但需要 root 权限(sudo) | 需要,用 xxx/admin@REALM Principal + 密码 |
| 在哪能用 | 只能在 KDC 所在机器上(即 Master 节点) | 任何能访问 KDC 的机器上 |
| KDC 服务挂了能用吗 | 能用(不依赖服务进程) | 不能 |
EMR 默认不创建 admin Principal ,ACL 规则 */admin *e 允许任何 xxx/admin 格式的 Principal 做管理操作,但需要先用 kadmin.local 手动创建:
bash
# 在 Master 节点上创建管理员 Principal(只需做一次)
sudo kadmin.local -q 'addprinc -pw <你的密码> root/admin@EC2.INTERNAL'
# 之后就可以用 kadmin 了(任何能访问 Master 749 端口的机器上)
kadmin -p root/admin@EC2.INTERNAL -w <你的密码> -q 'listprincs'
# sudo kadmin 会自动推断为 root/admin,不需要 -p
sudo kadmin -w <你的密码> -q 'listprincs'
常用管理命令:
bash
# 以下均可用 kadmin.local(Master 节点)或 kadmin(远程)执行
addprinc -pw user1pass user1@EC2.INTERNAL # 创建用户
cpw -pw newpass user1@EC2.INTERNAL # 修改密码
delprinc user1@EC2.INTERNAL # 删除用户
listprincs # 列出所有 Principal
用户实际使用
- kinit user1 之后,不管你的 Linux 用户是 hadoop、root 还是 ec2-user,HDFS 都认你是 user1。
bash
# 获取 Ticket
kinit user1
Password for user1@EC2.INTERNAL: ****
# 查看 Ticket
klist
Ticket cache: FILE:/tmp/krb5cc_990
Default principal: user1@EC2.INTERNAL
Valid starting Expires Service principal
03/24/26 09:52:43 03/24/26 19:52:43 krbtgt/EC2.INTERNAL@EC2.INTERNAL
renew until 03/25/26 09:52:43
# 以 user1 身份操作 HDFS
hdfs dfs -ls /user/user1 # 有权限
hdfs dfs -ls /user/user2 # Permission denied(取决于 HDFS 权限设置)
# HADOOP_USER_NAME 伪装在 Kerberos 环境下无效。Kerberos 以 Ticket 中的 Principal 为准,忽略环境变量
HADOOP_USER_NAME=hdfs hdfs dfs -touchz /tmp/test.txt
hdfs dfs -ls /tmp/test.txt
# owner 仍然是 user1,不是 hdfs