org.apache.hadoop.security.AccessControlException:尝试在 EC2 上使用 Hadoop Java API 通过 s3n URI 访问 S3 存储桶时权限被拒绝

2024-04-17

Scenario

我创建一个名为“my-role”的 AWS IAM 角色,将 EC2 指定为可信实体,即使用信任关系策略文档:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

该角色具有以下策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:GetBucketAcl",
        "s3:GetBucketCORS",
        "s3:GetBucketLocation",
        "s3:GetBucketLogging",
        "s3:GetBucketNotification",
        "s3:GetBucketPolicy",
        "s3:GetBucketRequestPayment",
        "s3:GetBucketTagging",
        "s3:GetBucketVersioning",
        "s3:GetBucketWebsite",
        "s3:GetLifecycleConfiguration",
        "s3:GetObject",
        "s3:GetObjectAcl",
        "s3:GetObjectTorrent",
        "s3:GetObjectVersion",
        "s3:GetObjectVersionAcl",
        "s3:GetObjectVersionTorrent",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:ListBucketVersions",
        "s3:ListMultipartUploadParts",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:PutObjectVersionAcl",
        "s3:RestoreObject"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

我使用 AWS CLI 从命令行启动 EC2 实例 (Amazon Linux 2014.09.1),指定“my-role”作为实例配置文件,一切正常。我通过运行以下命令来验证该实例是否有效地承担了“我的角色”:

  • curl http://169.254.169.254/latest/meta-data/iam/security-credentials/查询实例元数据,从中我得到响应my-role;
  • curl http://169.254.169.254/latest/meta-data/iam/security-credentials/my-role我从中获得与“我的角色”相关的临时凭证。

此类凭证检索响应的示例如下:

{
  "Code" : "Success",
  "LastUpdated" : "2015-01-19T10:37:35Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "an-access-key-id",
  "SecretAccessKey" : "a-secret-access-key",
  "Token" : "a-token",
  "Expiration" : "2015-01-19T16:47:09Z"
}
  • aws s3 ls s3://my-bucket/我从中正确地获得了包含“my-bucket”下第一个子目录的列表。(启动此 AMI 时默认安装并配置 AWS CLI。EC2 实例和 S3 存储桶位于同一 AWS 账户内)

我在这样的实例上运行/安装 Tomcat7 服务器和容器,在其上部署 J2EE 1.7 servlet 没有任何问题。

此类 servlet 应从 S3 存储桶(特别是从s3://my-bucket/custom-path/file.tar.gz using Hadoop Java API. (请注意,我尝试了 hadoop-common 工件 2.4.x、2.5.x、2.6.x,但没有得到任何积极结果。我将在使用 2.5.x 时遇到的异常情况下发布)

在 servlet 中,我从上面提到的实例元数据 URL 检索新凭据,并使用它们来配置我的 Hadoop Java API 实例:

... 
Path path = new Path("s3n://my-bucket/");
Configuration conf = new Configuration();
conf.set("fs.defaultFS", path.toString());
conf.set("fs.s3n.awsAccessKeyId", myAwsAccessKeyId);
conf.set("fs.s3n.awsSecretAccessKey", myAwsSecretAccessKey);
conf.set("fs.s3n.awsSessionToken", mySessionToken);
...

明显地,myAwsAccessKeyId, myAwsSecretAccessKey, and mySessionToken是我之前用实际值设置的 Java 变量。 然后,我使用以下命令有效地获取了一个 FileSystem 实例:

FileSystem fs = path.getFileSystem(conf);

我能够检索与文件系统 (fs.getconf().get(key-name)) 相关的所有配置并验证所有内容均按假设配置。

Problem

我无法下载s3://my-bucket/custom-path/file.tar.gz using:

...
fs.copyToLocalFile(false, new Path(path.toString()+"custom-path/file.tar.gz"), outputLocalPath);
...

如果我使用 hadoop-common 2.5.x 我得到IOException:

org.apache.hadoop.security.AccessControlException:权限被拒绝: s3n://my-bucket/custom-path/file.tar.gz 位于 org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:449) 在 org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:427) 在 org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.handleException(Jets3tNativeFileSystemStore.java:411) 在 org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrieveMetadata(Jets3tNativeFileSystemStore.java:181) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 处 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:606) 处 org.apache.hadoop.io.retry.RetryInvocalHandler.invokeMethod(RetryInvocalHandler.java:187) 在 org.apache.hadoop.io.retry.RetryInvocalHandler.invoke(RetryInvocalHandler.java:102) 在 org.apache.hadoop.fs.s3native.$Proxy12.retrieveMetadata(未知 来源)于 org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:467) 在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) 处 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) 在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968) 在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937) ...

如果我使用 hadoop-common 2.4.x,我会得到一个NullPointerException:

java.lang.NullPointerException 在 org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:433) 在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) 在 org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) 在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968) 在 org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937) ...

仅供记录,如果不设置任何 aws 凭证,我会得到:

必须将 AWS 访问密钥 ID 和秘密访问密钥指定为 s3n URL 的用户名或密码(分别),或者通过设置 fs.s3n.awsAccessKeyId 或 fs.s3n.awsSecretAccessKey 属性 (分别)。

最后的笔记

  • 如果我尝试使用实例中的 AWS CLI 命令从完全相同的 URI(但使用 s3 代替 s3n)下载文件,则完全没有问题。
  • 如果我尝试下载 Hadoop 发行版(例如 2.4.1)https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/ https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/),解压它,从实例元数据 URL 检索临时 AWS 凭证并尝试运行<hadoop-dir>/bin/hadoop fs -cp s3n://<aws-access-key-id>:<aws-secret-access-key>@my-bucket/custom-path/file.tar.gz .我再次得到 NPE:

致命内部错误 java.lang.NullPointerException 在org.apache.hadoop.fs.s3native.NativeS3FileSystem.listStatus(NativeS3FileSystem.java:479) 在 org.apache.hadoop.fs.shell.PathData.getDirectoryContents(PathData.java:268) 在 org.apache.hadoop.fs.shell.Command.recursePath(Command.java:347) 在 org.apache.hadoop.fs.shell.Ls.processPathArgument(Ls.java:96) 在org.apache.hadoop.fs.shell.Command.processArgument(Command.java:260) 在 org.apache.hadoop.fs.shell.Command.processArguments(Command.java:244) 在 org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:190) 在 org.apache.hadoop.fs.shell.Command.run(Command.java:154) 在 org.apache.hadoop.fs.FsShell.run(FsShell.java:255) 在 org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70) 在 org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84) 在 org.apache.hadoop.fs.FsShell.main(FsShell.java:308)

很抱歉这篇文章很长,我只是尽力尽可能详细。感谢您在这里提供的任何最终帮助。


您正在使用 STS/临时 AWS 凭证;这些目前似乎不受支持s3 or s3n FileSystemhadoop 中的实现。

AWS STS/临时凭证不仅包括(访问密钥、秘密密钥),还包括会话令牌。哈杜普s3 and s3n FileSystem(s) 尚不支持包含会话令牌(即您的配置fs.s3n.awsSessionToken不被支持并被忽略s3n FileSystem.

From AmazonS3 - Hadoop 维基 https://wiki.apache.org/hadoop/AmazonS3...
(注意这里没有提到fs.s3.awsSessionToken):

配置使用 s3/s3n 文件系统

编辑你的core-site.xml文件包含您的 S3 密钥

   <property>
     <name>fs.s3.awsAccessKeyId</name>
     <value>ID</value>
   </property>

   <property>
     <name>fs.s3.awsSecretAccessKey</name>
     <value>SECRET</value>
   </property>


如果你看一下S3Credentials.java https://github.com/apache/hadoop/blob/trunk/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3/S3Credentials.java从 github.com 上的 apache/hadoop 中,您会注意到 S3 凭证的表示中完全缺少会话令牌的概念。

已提交一个补丁来解决此限制(详细信息here https://issues.apache.org/jira/browse/HADOOP-9680);然而,它还没有被整合。


If you are using AWS IAM Instance Roles, you may want to explore using the new s3a FileSystem that was added in Hadoop 2.6.0. It claims to have support for IAM role-based authentication (i.e. you wouldn't have to explicitly specify the keys at all).

Hadoop JIRA 票证描述了如何配置s3a FileSystem:

From https://issues.apache.org/jira/browse/HADOOP-10400 https://issues.apache.org/jira/browse/HADOOP-10400 :

fs.s3a.access.key- 您的 AWS 访问密钥 ID(角色身份验证时省略)
fs.s3a.secret.key- 您的 AWS 密钥(角色身份验证时省略)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

org.apache.hadoop.security.AccessControlException:尝试在 EC2 上使用 Hadoop Java API 通过 s3n URI 访问 S3 存储桶时权限被拒绝 的相关文章

随机推荐