对象存储审计

S3A 连接器为审计 S3 请求提供了一个扩展点。审计可以在每个 FS 操作的入口点进行,也可以在 AWS S3 SDK 中进行,即在执行请求之前。

完整架构在 审计架构 中进行了介绍;本文档介绍了其用法。

重要提示:默认情况下,审计处于禁用状态

由于使用 ThreadLocal 字段导致内存泄漏,此审计功能在创建和删除 S3A 文件系统实例时会泄漏内存。这会在不重复使用文件系统实例或尝试删除属于特定用户的全部实例的长生命周期进程中造成问题。请参阅 HADOOP-18091 S3A 审计通过 ThreadLocal 引用泄漏内存

为了避免这些内存泄漏,在 hadoop 3.3.2 版本中默认禁用了审计。

由于这些内存泄漏现已修复,因此已重新启用审计。

要禁用它,请将 fs.s3a.audit.enabled 设置为 false

审计工作流

  1. 可以为每个 S3A FileSystem 实例实例化一个 审计器服务,该服务在 FS 初始化期间创建,并在 FS 实例关闭时关闭。
  2. S3A FS 将为每个 Hadoop FileSystem API 调用从审计服务请求一个审计跨度
  3. 审计跨度将在 API 调用执行期间调用的每个 S3 操作期间调用回调,在 AWS SDK 中
  4. 这允许审计服务记录所做的请求并与用户和操作关联起来。
  5. 和/或拒绝操作。
  6. 捆绑的“日志审计”记录操作并将有关调用的信息附加到 HTTP Referrer 标头。
  7. 因此有助于调试与性能、存储桶负载、S3 成本等相关的各种问题。

因此:可以插入审计服务以提供(尽力而为)审计以及暗示允许/拒绝安全性。

  • 为什么尽力而为:覆盖范围不完整。请参见下面的限制。
  • 为什么是“暗示”安全性?在 JVM 中运行的任何自定义代码都可以检索 AWS 凭证链,从而绕过此审计机制。

限制

这不是控制对 S3 资源的访问的一种手段。这是尽力而为地尝试支持 FileSystem 操作 API 调用的日志记录,特别是将 S3 多个对象请求与单个 FS API 调用关联起来,理想情况下甚至可以识别生成负载的进程/作业。

  • 仅供内部使用而使用公共 S3 方法的底层代码可能不会创建跨度。
  • 请求 AWS S3 客户端的代码可能会绕过跨度创建。
  • 应用程序代码还可以创建一个新的 S3 客户端(重新使用任何现有凭证),从而可以未经审计地访问 S3。
  • 与 OpenTelemetry 没有关联。
  • 通过 TransferManager 执行的上传和复制操作不会拾取活动跨度,因为工作是在 S3A 代码无法更新的线程中执行的。
  • http referer 标头的长度有限;对长路径执行的操作可能无法完全记录。

使用审计

默认情况下禁用审计。启用审计时,日志审计将通过对 S3 发出的请求中的自定义 HTTP Referrer 标头对 S3 日志进行注释。可以使用其他审计器类。

审计器选项

选项 含义 默认值
fs.s3a.audit.enabled 是否启用审计? true
fs.s3a.audit.service.classname 审计器类名 org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor
fs.s3a.audit.request.handlers 要包含在处理程序链中的 AWS SDK RequestHandler2 的额外子类的列表 ""
fs.s3a.audit.referrer.enabled 日志审计在 HTTP Referrer 标头中发布审计信息 true
fs.s3a.audit.referrer.filter 要过滤的审计字段列表 ""
fs.s3a.audit.reject.out.of.span.operations 审计器拒绝“跨度之外”的操作 false

禁用审计。

在此 Hadoop 版本中,审计已禁用。

这可以针对全局或特定存储桶显式设置

<property>
  <name>fs.s3a.audit.enabled</name>
  <value>false</value>
</property>

即使在全局启用审计时,也可以禁用特定存储桶的审计。

<property>
  <name>fs.s3a.bucket.landsat-pds.audit.enabled</name>
  <value>false</value>
  <description>Do not audit landsat bucket operations</description>
</property>

使用日志审计进行审计

“日志审计器”是默认审计器。它提供两种形式的日志记录

  1. 通过活动 SLF4J 实现来记录客户端中的操作。
  2. 动态生成 S3 请求的 HTTP Referrer 头。

通过在选项 fs.s3a.audit.service.classname 中提供其类名来启用日志审计器。

<property>
  <name>fs.s3a.audit.enabled</name>
  <value>true</value>
</property>

<property>
  <name>fs.s3a.audit.service.classname</name>
  <value>org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor</value>
</property>

要在本地客户端日志中打印审计事件,请将关联的 Log4J 日志设置为在调试中记录

# Auditing
log4j.logger.org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor=DEBUG

与 S3 服务器访问日志记录集成

可以将 AWS S3 存储桶配置为将对存储桶的所有 HTTP 请求的日志存储到另一个 S3 存储桶中,S3 服务器访问日志记录在日志审计器中,每个 AWS S3 请求的 HTTP referer 字段都构建到一个 URL 中,该 URL 提供上下文和跨度信息。由于此字段保存在 S3 日志中,因此如果启用了 S3 存储桶日志记录,则日志将能够将 S3 客户端的访问与正在进行的实际操作相关联。

注意:此日志记录被描述为“尽力而为”。无法保证日志何时到达。

拒绝跨度外的操作

可以将日志审计器配置为在通过 S3AFileSystem 实例(创建了审计器的实例)与 S3 交互的线程没有任何跨度处于激活状态时,对 S3 发出请求时引发异常。

这主要是为了开发,因为它可用于保证通过公共 API 调用输入跨度。

<property>
  <name>fs.s3a.audit.reject.out.of.span.operations</name>
  <value>true</value>
</property>

此拒绝过程对某些 AWS S3 请求类禁用,这些类在 AWS SDK 中创建,作为更大操作的一部分,并且无法附加跨度。

始终允许的 AWS 请求 原因
GetBucketLocationRequest 在 AWS SDK 中用于确定 S3 终端节点
CopyPartRequest 在 AWS SDK 中用于复制操作
CompleteMultipartUploadRequest 在 AWS SDK 中用于完成复制操作

发起复制/多部分上传的请求始终会接受审计,因此审计过程确实涵盖了重命名和多部分 IO。但是,AWS S3 日志不会在关联的复制/完成调用的引荐人标头中包含完整的跟踪信息。

审计和 HTTP Referrer 标头

HTTP 引用标头由日志记录审核员附加。如果 S3 存储桶配置为将请求记录到另一个存储桶,则这些日志条目将包含审核信息作为引用

可以解析此信息(查阅 AWS 文档以获取正则表达式)并提取 HTTP 引用标头。

https://audit.example.org/hadoop/1/op_rename/3c0d9b7e-2a63-43d9-a220-3c574d768ef3-3/
    ?op=op_rename
    &p1=s3a://alice-london/path1
    &pr=alice
    &p2=s3a://alice-london/path2
    &ps=235865a0-d399-4696-9978-64568db1b51c
    &ks=5
    &id=3c0d9b7e-2a63-43d9-a220-3c574d768ef3-3
    &t0=12
    &fs=af5943a9-b6f6-4eec-9c58-008982fc492a
    &t1=12
    &ts=1617116985923

以下是请求中可能找到的字段。如果任何字段值为 null,则省略该字段。

名称 含义 示例
cm 命令 S3GuardTool$BucketInfo
fs 文件系统 ID af5943a9-b6f6-4eec-9c58-008982fc492a
id 跨度 ID 3c0d9b7e-2a63-43d9-a220-3c574d768ef3-3
ji 作业 ID(S3A 提交者) (由查询引擎生成)
op 文件系统 API 调用 op_rename
p1 操作的路径 1 s3a://alice-london/path1
p2 操作的路径 2 s3a://alice-london/path2
pr 主体 alice
ps 唯一的进程 UUID 235865a0-d399-4696-9978-64568db1b51c
rg GET 请求范围 100-200
ta 任务尝试 ID(S3A 提交者)
t0 线程 0:创建线程跨度 100
t1 线程 1:执行此操作的线程 200
ts 时间戳(UTC 纪元毫秒) 1617116985923
ks 作为给定请求一部分要删除的文件(数量)的关键大小(适用于删除和重命名操作) 5

备注

  • 线程 ID 来自 JVM 中的当前线程,因此可以与 Log4J 日志中的线程 ID 进行比较。它们绝不会是唯一的。
  • 任务尝试/作业 ID 仅在涉及 S3A 提交者的操作中设置,特别是提交者执行的所有操作。在与提交者实例化相同的线程中执行的操作可能报告 ID,即使它们与实际任务无关。认为它们“尽力而为”。
Long.toString(Thread.currentThread().getId())

t0t1 不同时,这意味着该范围已移交给另一个线程,以代表原始操作进行工作。这可以与客户端上的日志条目关联,以将工作隔离到特定线程。

HTTP Referrer 标头的限制

标头长度存在大小限制;对长路径的操作可能会超过该限制。在这种情况下,审计日志不完整。

这就是将 span ID 始终作为 URL 的一部分传递的原因,而不仅仅是 HTTP 查询参数:即使标头被截断,span ID 也会始终存在。

HTTP Referrer 审计的隐私影响

当 S3A 客户端向 S3 存储桶发出请求时,审计员会将 span 信息添加到标头中,然后将其存储在日志中

如果 S3 存储桶归客户端所属的同一组织所有,则此 span 信息是组织内部的。

如果 S3 存储桶归不同的实体所有/管理,则该实体收集的任何 S3 存储桶日志中都可以看到 span 信息。这包括主体名称和通过 Tools 或服务启动器 API 启动应用程序时执行的命令。

可以通过过滤特定标头或完全明确禁用引荐标头生成来禁用共享此信息。

注意:即使通过或主体过滤禁用 HTTP Referrer,AWS S3 日志也会包含发出请求的用户或 IAM 角色的 ARN。

过滤 Referrer 标头

可以从引荐标头中过滤特定字段,因此不会包含在 S3A 日志中。

<property>
  <name>fs.s3a.audit.referrer.filter</name>
  <value>pr, cm</value>
  <description>Strip out principal and command from referrer headers</description>
</property>

禁用 Referrer 标头

可以通过将选项 fs.s3a.audit.referrer.enabled 设置为 false(全局或针对特定存储桶)来配置日志审计器,使其不添加引荐标头

<property>
  <name>fs.s3a.audit.referrer.enabled</name>
  <value>false</value>
  <description>Disable referrer for all buckets</description>
</property>

<property>
  <name>fs.s3a.bucket.landsat-pds.audit.referrer.enabled</name>
  <value>false</value>
  <description>Do not add the referrer header to landsat operations</description>
</property>

收集 AWS S3 日志以进行分析

必须为 S3 存储桶设置 服务器访问日志记录

这将告知 AWS S3 收集所有 HTTP 请求的访问日志,并将它们存储在同一区域中的另一个存储桶中。日志会以文件形式到达,其中包含几秒钟的日志数据,存储在配置的路径下。

启用日志记录:源存储桶

  1. 如果尚未创建,请在同一区域中为日志创建一个单独的存储桶。
  2. 在 S3 控制台中,找到要作为日志源的存储桶,然后转到“属性”。
  3. 向下滚动到“服务器访问日志记录”
  4. 选择“编辑”,然后启用日志记录,在附近的存储桶中输入日志路径。(提示:为了方便将多个存储桶记录到同一个日志存储桶,请使用类似于 logs/$BUCKET/log- 的前缀来隔离不同存储桶的日志。例如,来自 dev data london 的路径日志数据可以是 s3://london-log-bucket/logs/dev-data-lon/log-
  5. 保存此项。

在 S3 请求发出和日志出现之间大约有一个小时的延迟;如果在设置期间事情似乎不起作用,请不要担心。启用日志,通过“hadoop fs”命令行使用存储桶,等待一小时,然后去日志存储桶中查找条目。日志文件名包括这些日志开始的时间

通过删除旧日志来降低成本。

由于日志存储在 S3 存储桶中,因此它们也会产生费用。通过在一段时间后删除日志来降低成本,和/或设置一个工作流来加载和合并日志条目到压缩格式和更大的文件中。

设置一个规则来自动删除旧日志文件非常简单。

  1. 在 S3 控制台中,打开作为日志目标的存储桶,例如 london-log-bucket
  2. 转到“管理”选项卡。
  3. 添加一个生命周期规则(与你应该已经有的“中止待处理上传”规则并列)。
  4. 添加规则名称“删除旧日志文件”。
  5. 选择“限制范围”。
  6. 添加前缀 logs/ 以删除所有存储桶的所有日志。重要提示:你不得有任何前导“/”,例如 /logs/ - 将没有匹配项,规则将不起作用。
  7. 在“生命周期规则操作”中,选择“使当前版本过期”。这将删除日志条目。
  8. 在“使对象的当前版本过期”中,设置保留日志条目的天数。
  9. 最后,按“创建规则”按钮

密切关注存储桶以确保删除操作正常运行;前缀中很容易出错,并且由于日志会无限创建,因此成本会不断增加。

解析 AWS S3 日志以提取引荐标头

AWS S3 文档涵盖了日志格式,并包含一个用于处理日志格式的 Hive 外部表声明。

hadoop-aws 测试套件中用于提取标头的 Java 模式正则表达式定义为

(?<owner>[^ ]*) (?<bucket>[^ ]*) (?<timestamp>\[(.*?)\]) (?<remoteip>[^ ]*) (?<requester>[^ ]*) (?<requestid>[^ ]*) (?<operation>[^ ]*) (?<key>[^ ]*) (?<requesturi>(-|"[^"]*")) (?<http>(-|[0-9]*)) (?<awserrorcode>[^ ]*) (?<bytessent>[^ ]*) (?<objectsize>[^ ]*) (?<totaltime>[^ ]*) (?<turnaroundtime>[^ ]*) (?<referrer>(-|"[^"]*")) (?<useragent>(-|"[^"]*")) (?<version>[^ ]*) (?<hostid>[^ ]*) (?<sigv>[^ ]*) (?<cypher>[^ ]*) (?<auth>[^ ]*) (?<endpoint>[^ ]*) (?<tls>[^ ]*)*$

org.apache.hadoop.fs.s3a.audit.S3LogParser 提供了此模式以及每个组的常量。它被声明为 Public/Unstable

调试

org.apache.hadoop.fs.s3a.audit 日志上下文包含实现审计的不同组件的日志。

可以通过将该日志设置为调试来启用使用 LoggingAuditService 审计的请求的日志记录。

# Log before a request is made to S3
log4j.logger.org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor=DEBUG

这会为每个请求添加一行日志,并提供对 S3A 客户端和 AWS S3 之间通信的一些见解。

对于审计系统的低级别调试(例如,当进入和退出跨度时),将日志设置为 TRACE

# log request creation, span lifecycle and other low-level details
log4j.logger.org.apache.hadoop.fs.s3a.audit=TRACE

这会产生很多噪音,不建议在正常操作中使用。

与 S3A 提交者的集成

通过 S3A 提交者提交的工作将具有与针对该线程中所有 S3A 文件系统执行的 S3 操作关联的作业(查询)ID。

要使此操作有用,任务中执行的工作必须与在提交者上调用 jobSetup()taskSetup() 的同一线程中。