CredentialProvider API 是一种 SPI 框架,用于插入可扩展凭据提供程序。凭据提供程序用于将敏感令牌、密钥和密码的使用与存储和管理的详细信息分开。选择用于保护这些凭据的不同存储机制的能力使我们能够将此类敏感资产置于明文之外,远离窥探,并可能由第三方解决方案管理。
本文档旨在描述 CredentialProvider API 的设计、开箱即用的实现、它们的使用位置以及如何采用其使用。
让我们快速概述一下凭据提供程序框架在 Hadoop 中保护密码或其他敏感令牌的用法。
某些部署对如何存储和管理群集内的密码等敏感令牌非常敏感。例如,可能存在要求此类内容永远不以明文形式存储的安全最佳实践和策略。企业部署可能需要使用首选的凭据管理解决方案,我们需要一种方法为其插入集成。
Hadoop 项目和生态系统中有许多地方现在可以利用凭据提供程序 API,并且数量还在不断增加。通常,用法模式包含相同的要求和流程。
hadoop.security.credential.provider.path
是一个或多个凭据提供程序 URI 的逗号分隔列表,在尝试解析凭据别名时会遍历该列表。org.apache.hadoop.conf.Configuration
类进行配置或对凭据提供程序有其他内部用途的功能或组件可以选择使用 CredentialProvider
API 本身。可以在 Configuration.getPassword 及其单元测试中找到其使用示例。示例:ssl.server.keystore.password
hadoop credential create ssl.server.keystore.password -value 123 \ -provider localjceks://file/home/lmccay/aws.jceks
别名与用于从 Configuration.get()
方法获取凭据的配置属性相同。
现在,我们需要确保此已配置的凭据存储在运行时由 Configuration.getPassword 方法识别。如果没有凭据提供程序路径配置,则 Configuration.getPassword()
将跳过凭据提供程序 API 查询。因此,务必在 core-site.xml
或组件的等效文件中配置以下内容。
<property> <name>hadoop.security.credential.provider.path</name> <value>localjceks://file/home/lmccay/aws.jceks</value> <description>Path to interrogate for protected credentials.</description> </property>
关于提供程序路径的另外几件事项
localjceks
提供程序不依赖于 Hadoop FileSystem API。有时需要它来避免递归依赖。由 jceks
表示的另一个提供程序确实使用 Hadoop FileSystem API,并且可以支持在 HDFS 或其他兼容文件系统中配置的关键存储。第三种提供程序类型是 user
类型。此提供程序可以管理为进程存储在凭据文件中的凭据。Configuration.getPassword
方法将按顺序查询每个提供程序,直到解析别名或用尽列表。根据对凭据的运行时需求,我们可能需要配置要检查的提供程序链。总之,首先将凭据配置到提供程序中,然后配置提供程序以供功能或组件使用,并且通常只需通过使用 Configuration.getPassword 方法来选取它。
功能\组件 | 说明 | 链接 |
---|---|---|
LDAPGroupsMapping | LDAPGroupsMapping 用于在 LDAP 中查找给定用户的组。CredentialProvider API 用于保护 LDAP 绑定密码和 SSL 所需的密码。 | LDAP 组映射 |
SSL 密码 | FileBasedKeyStoresFactory 利用凭据提供程序 API 来解析 SSL 相关密码。 | 待办事项 |
HDFS | DFSUtil 使用 Configuration.getPassword() 使用凭据提供程序 API 和/或回退到存储在 ssl-server.xml 中的明文值。基于 Zookeeper 的联合状态存储和故障转移控制器使用 Configuration.getPassword 来获取 Zookeeper 身份验证信息,并回退到明文身份验证信息。 |
待办事项 |
YARN | WebAppUtils 通过 Configuration 上称为 getPassword 的新方法吸收了凭据提供程序 API 的使用。这提供了一种替代方法,即在 ssl-server.xml 文件中以明文存储密码,同时保持向后兼容性。基于 Zookeeper 的资源管理器状态存储使用 Configuration.getPassword 来获取 Zookeeper 身份验证信息,并回退到明文身份验证信息。 | 待办事项 |
KMS | 使用 HttpServer2.loadSSLConfiguration,它利用 Configuration.getPassword 来读取 SSL 相关凭据。它们可以通过凭据提供程序和/或在允许时从配置中的明文解析。 | KMS |
HttpFS | 使用 HttpServer2.loadSSLConfiguration,它利用 Configuration.getPassword 来读取 SSL 相关凭据。它们可以通过凭据提供程序和/或在允许时从配置中的明文解析。 | HttpFS 服务器设置 |
AWS S3A |
使用 Configuration.getPassword 获取 S3 凭据。它们可以通过凭据提供程序 API 或从配置中解析以实现向后兼容性。 |
AWS S3/S3A 使用 |
Azure WASB |
使用 Configuration.getPassword 获取 WASB 凭据。它们可以通过凭据提供程序 API 或从配置中解析以实现向后兼容性。 |
Azure WASB 使用 |
Azure ADLS |
使用 Configuration.getPassword 获取 ADLS 凭据。它们可以通过凭据提供程序 API 或从配置中解析以实现向后兼容性。 |
Azure ADLS 使用 |
Apache Accumulo |
Tracer 使用 trace.password 属性来对 Accumulo 进行身份验证并在跟踪表中保留跟踪信息。凭据提供程序 API 用于从提供程序或配置中获取 trace.password 以实现向后兼容性。 | 待办事项 |
Apache Slider |
Slider 已添加了一项功能,可提示用户输入所需的密码,并使用 CredentialProvider 存储这些密码,以便稍后由应用检索。 | 待办事项 |
Apache Hive |
通过使用 Credential Provider API,已添加了对元存储密码、SSL 相关密码和 JDO 字符串密码的保护 | 待办事项 |
Apache HBase |
HBase RESTServer 正在使用新的 Configuration.getPassword 方法,以便首先检查凭证提供程序 API,然后在允许时回退到明文。 | 待办事项 |
Apache Oozie |
使用凭证提供程序 API 保护 SSL、电子邮件和 JDBC 密码。 | 待办事项 |
Apache Ranger |
使用凭证提供程序 API 保护数据库、信任和密钥库密码。 | 待办事项 |
hadoop credential
命令用法:hadoop credential <subcommand> [options]
请参阅 命令手册 中的命令选项详细信息
凭证命令可用于向特定凭证存储提供程序配置密码或密钥。为了明确指示要使用的提供程序存储,应使用 -provider
选项。
示例:hadoop credential create ssl.server.keystore.password -provider jceks://file/tmp/test.jceks
为了指示特定的提供程序类型和位置,用户必须在 core-site.xml 中提供 hadoop.security.credential.provider.path
配置元素,或在每个凭证管理命令上使用命令行选项 -provider
。此提供程序路径是一个逗号分隔的 URL 列表,指示应咨询的提供程序列表的类型和位置。例如,以下路径:user:///,jceks://file/tmp/test.jceks,jceks://[email protected]/my/path/test.jceks
指示应通过用户提供程序咨询当前用户的凭证文件,位于 /tmp/test.jceks
的本地文件是 Java 密钥库提供程序,而位于 HDFS 中的文件 nn1.example.com/my/path/test.jceks
也是 Java 密钥库提供程序的存储。
UserProvider
(由提供程序 URI user:///
表示)用于从用户的凭证文件中检索凭证。此文件用于存储执行作业和应用程序所需的各种令牌、密钥和密码。JavaKeyStoreProvider
由提供程序 URI jceks://SCHEME/path-to-keystore
表示,用于从文件系统 <SCHEME>
中的 Java 密钥库文件中检索凭据。Hadoop 文件系统 API 的底层使用允许将凭据存储在本地文件系统中或群集存储中。LocalJavaKeyStoreProvider
由提供程序 URI localjceks://file/path-to-keystore
表示,用于从必须存储在本地文件系统中的 Java 密钥库中访问凭据。对于会导致对访问 HDFS 产生递归依赖关系的凭据,这是必需的。任何时候,如果需要凭据才能访问 HDFS,我们都不能依赖从 HDFS 中获取凭据来执行此操作。BouncyCastleFIPSKeyStoreProvider
由提供程序 URI bcfks://SCHEME/path-to-keystore
表示,用于从文件系统 <SCHEME>
中的 Bouncy Castle FIPS 密钥库文件中检索凭据。Hadoop 文件系统 API 的底层使用允许将凭据存储在本地文件系统中或群集存储中。LocalBcouncyCastleFIPSKeyStoreProvider
由提供程序 URI localbcfks://file/path-to-keystore
表示,用于从必须存储在本地文件系统中的 Bouncy Castle FIPS 密钥库中访问凭据。对于会导致对访问 HDFS 产生递归依赖关系的凭据,这是必需的。任何时候,如果需要凭据才能访问 HDFS,我们都不能依赖从 HDFS 中获取凭据来执行此操作。当凭据存储在文件系统中时,将应用以下规则
存储在本地 localjceks://
或 localbcfks://
文件中的凭据在读取配置的过程中加载。对于在 YARN 应用程序中使用,这意味着它们必须在整个群集中可见,在主机的本地文件系统中。
使用 jceks://
或 bcfks://
提供程序存储的凭据可以存储在群集文件系统中,因此在整个群集中可见,但不能存储在需要特定凭据才能访问的文件系统中。
要使用 jceks
URI 包装文件系统 URI,请按照以下步骤操作。Bouncy Castle FIPS 提供程序遵循类似的步骤,即用 bcfks
替换 jceks
,同时配置 OS/JDK 级别 FIPS 提供程序。
hdfs://namenode:9001/users/alice/secrets.jceks
jceks://
:jceks://hdfs://namenode:9001/users/alice/secrets.jceks
@
符号替换第二个 ://
字符串:jceks://hdfs@namenode:9001/users/alice/secrets.jceks
示例
对于本地文件系统,例如 file:///tmp/secrets.jceks
的路径将变为:jceks://file/tmp/secrets.jceks
路径 URI | jceks URI |
---|---|
hdfs://namenode.example.org:9001/user/alice/secret.jceks |
jceks://[email protected]:9001/user/alice/secret.jceks |
file:///tmp/secrets.jceks |
jceks://file/tmp/secret.jceks |
s3a://container1/secrets/secret.jceks |
jceks://s3a@container1/secrets/secret.jceks |
wasb://account@container/secret.jceks |
jceks://wasb@account@container/secret.jceks |
abfs://account@container/secret.jceks |
jceks://abfs@account@container/secret.jceks |
https://user:pass@service/secret.jceks?token=aia |
jceks://https@user:pass@service/secret.jceks?token=aia |
请注意,为了避免无限递归,诸如 abfs
、s3a
、adls
和 wasb
之类的文件系统明确排除了存储在其自己的文件系统方案中的路径上的密钥库,即使它们存储在使用与正在查找的凭据不同的凭据集的容器中。
例如,您不能使用存储在 s3a://shared/secrets/secret.jceks
中的凭据来读取容器 s3a://private/
的凭据。
Java 中的密钥库通常受密码保护。基于密钥库的凭据提供程序的主要保护方法是操作系统级别的文件权限和针对目标文件系统可能存在的任何其他基于策略的访问保护。虽然密码不是主要保护来源,但了解管理这些密码所需的机制和可用选项非常重要。了解所有需要访问用于保护密钥库的密码的各方也很重要,以便在运行时使用它们。
选项 | 说明 | 备注 |
---|---|---|
默认密码 | 这是一个硬编码密码“none”。 | 这是开源项目中的硬编码密码,因此具有明显的缺点。但是,机制部分将显示它更简单,因此几乎与其他更复杂选项一样安全。 |
环境变量 | HADOOP_CREDSTORE_PASSWORD |
此选项使用环境变量来传递在查询配置在 hadoop.security.credential.provider.path 配置属性中的所有密钥库时应使用的密码。路径中基于所有密钥库的提供程序都必须受同一密码保护。 |
密码文件 | hadoop.security.credstore.java-keystore-provider.password-file |
此选项使用“辅助文件”,其位置配置在 hadoop.security.credstore.java-keystore-provider.password-file 配置属性中,以传递在查询配置在 hadoop.security.credential.provider.path 配置属性中的所有密钥库时应使用的密码。 |
非常重要的是要考虑,受保护凭据的所有运行时使用者(mapreduce 作业/应用程序)都需要能够访问用于保护密钥库提供程序的密码。可以通过多种方式传递此密码,这些方式在上面的“选项”部分中进行了描述。
密钥库密码 | 说明 | 需要同步 | 明文 | 文件权限 |
---|---|---|---|---|
默认密码 | 硬编码密码是默认设置。本质上,当对所有基于密钥库的凭据存储使用默认密码时,我们利用文件权限来保护凭据存储,而密钥库密码只是持久化密钥库的一种形式。 | 否 | 是 | 否(已记录) |
环境变量 | HADOOP_CREDSTORE_PASSWORD 环境变量必须设置为所有密钥库的自定义密码,这些密钥库可能在需要从基于密钥库的凭据提供程序访问凭据的任何进程的提供程序路径中进行配置。对于整个逗号分隔提供程序路径,只有一个环境变量。很难知道每个密钥库所需的密码,建议对所有基于密钥库的凭据提供程序使用相同的密码以避免此问题。设置环境变量可能需要从脚本或其他明文存储机制进行设置。可以通过各种 Unix 命令获得用于运行进程的环境变量。 |
是 | 是 | 否 |
密码文件 | hadoop.security.credstore.java-keystore-provider.password-file 配置属性必须设置为包含提供程序路径中可能配置的所有密钥库的自定义密码的“辅助文件”的位置。任何需要从基于密钥库的凭据提供程序访问凭据的进程都需要将此配置属性设置为适当的文件位置。对于整个逗号分隔的提供程序路径,只有一个密码文件。很难知道每个密钥库所需的密码,因此建议对所有基于密钥库的凭据提供程序使用相同的密码以避免此问题。密码文件是需要管理的其他文件,以明文存储密码,并且需要设置文件权限,以便只有需要访问权限的人员才能访问。如果文件权限设置不当,则以明文形式提供访问密钥库的密码。 |
是 | 是 | 是 |
使用默认密码意味着无需与运行时使用者进行额外的通信/同步。默认密码是已知的,但文件权限是密钥库的主要保护措施。
当文件权限受阻时,与“辅助文件”不同,即使知道密码,也没有标准工具可以公开受保护的凭据。Keytool 需要一个六个字符或更多字符的密码,并且不知道如何从密钥库中检索常规机密。它还仅限于 PKI 密钥对。编辑器不会显示存储在密钥库中的机密,cat
、more
或任何其他标准工具也不会显示。这就是为什么密钥库提供程序比“辅助文件”凭据存储更好的原因。
也就是说,对于某人来说,使用 API 编写代码来访问存储在基于密钥库的凭据提供程序中的凭据非常简单。同样,在使用默认密码时,密码仅仅是持久化密钥库的一种形式。唯一的保护措施是文件权限和操作系统级别的访问策略。
用户可以决定使用密码“辅助文件”来存储密钥库本身的密码,并且支持此操作。只是非常重要的是要了解此正确性级别所需的机制。
如果不存在凭据提供程序或找不到密钥,Credentials.getPassword()
操作会回退到使用配置文件 XML 文件中的条目。
可以通过将配置选项 hadoop.security.credential.clear-text-fallback
从 true
更改为 false
来禁用此操作
<property> <name>hadoop.security.credential.clear-text-fallback</name> <value>false</value> <description> true or false to indicate whether or not to fall back to storing credential password as clear text. The default value is true. This property only works when the password can't not be found from credential providers. </description> </property>
一旦设置,通过 getPassword()
API 查找的所有配置选项都必须通过凭据提供程序提供。