java - org.apache.hadoop.security.AccessControlException : Permission denied when trying to access S3 bucket through s3n URI using Hadoop Java APIs on EC2

标签 java hadoop amazon-web-services amazon-s3 tomcat7

场景

我创建了一个名为“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),将“我的角色”指定为实例配置文件,一切正常。我通过运行验证该实例是否有效地承担了“我的角色”:

  • 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/从中我正确地得到了一个包含“我的桶”下的第一个子目录的列表。 (启动此 AMI 时默认安装和配置 AWS CLI。EC2 实例和 S3 存储桶在同一个 AWS 账户中)

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

这样的 servlet 应该从 S3 存储桶下载一个文件到本地文件系统,特别是从 s3://my-bucket/custom-path/file.tar.gz使用 Hadoop Java API(请注意,我尝试了 hadoop-common artifact 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 , 和 mySessionToken是我之前用实际值设置的 Java 变量。 然后,我有效地获得了一个 FileSystem 实例,使用:

FileSystem fs = path.getFileSystem(conf);

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

问题

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

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

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

org.apache.hadoop.security.AccessControlException: Permission denied: s3n://my-bucket/custom-path/file.tar.gz at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:449) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:427) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.handleException(Jets3tNativeFileSystemStore.java:411) at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrieveMetadata(Jets3tNativeFileSystemStore.java:181) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:187) at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102) at org.apache.hadoop.fs.s3native.$Proxy12.retrieveMetadata(Unknown Source) at org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus(NativeS3FileSystem.java:467) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:337) at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:289) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1968) at org.apache.hadoop.fs.FileSystem.copyToLocalFile(FileSystem.java:1937) ...

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

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

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

AWS Access Key ID and Secret Access Key must be specified as the username or password (respectively) of a s3n URL, or by setting the fs.s3n.awsAccessKeyId or fs.s3n.awsSecretAccessKey properties (respectively).

最后的笔记

  • 如果我尝试使用来自实例的 AWS CLI 命令从完全相同的 URI(但 s3 代替 s3n)下载文件,我完全没有问题。
  • 如果我尝试下载 Hadoop 发行版(例如来自 https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/ 的 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:

Fatal internal error java.lang.NullPointerException at org.apache.hadoop.fs.s3native.NativeS3FileSystem.listStatus(NativeS3FileSystem.java:479) at org.apache.hadoop.fs.shell.PathData.getDirectoryContents(PathData.java:268) at org.apache.hadoop.fs.shell.Command.recursePath(Command.java:347) at org.apache.hadoop.fs.shell.Ls.processPathArgument(Ls.java:96) at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:260) at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:244) at org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:190) at org.apache.hadoop.fs.shell.Command.run(Command.java:154) at org.apache.hadoop.fs.FsShell.run(FsShell.java:255) at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70) at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84) at org.apache.hadoop.fs.FsShell.main(FsShell.java:308)

抱歉这篇文章太长了,我只是尽量写得详细一点。感谢您在这里提供的任何最终帮助。

最佳答案

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

AWS STS/临时凭证不仅包括(访问 key 、 secret key ),还包括 session token 。 hadoop s3s3n FileSystem(s) 尚不支持包含 session token (即您的 fs.s3n 配置.awsSessionToken 不受 s3n FileSystem 支持和忽略。

来自 AmazonS3 - Hadoop Wiki ...
(注意没有提到fs.s3.awsSessionToken):

Configuring to use s3/ s3n filesystems

Edit your core-site.xml file to include your S3 keys

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

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


如果你看一下S3Credentials.java从 github.com 上的 apache/hadoop,您会注意到 session token 的概念在 S3 凭证的表示中完全缺失。

提交了一个补丁来解决这个限制(详细here);但是,它尚未集成。


如果您使用的是 AWS IAM 实例角色,您可能想要探索使用 Hadoop 2.6.0 中添加的新 s3a FileSystem。它声称支持基于 IAM 角色的身份验证(即您根本不必明确指定 key )。

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

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

fs.s3a.access.key - Your AWS access key ID (omit for role authentication)
fs.s3a.secret.key - Your AWS secret key (omit for role authentication)

关于java - org.apache.hadoop.security.AccessControlException : Permission denied when trying to access S3 bucket through s3n URI using Hadoop Java APIs on EC2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28024570/

相关文章:

hadoop - hadoop-将驱动器添加到现有集群

java - 以编程方式创建的 AWS EC2 实例未显示在浏览器中

java - 导入所有 Java 库

java - 设置 Eclipse 以始终包含 hadoop jar 文件

Java 8 函数 - "wrapper"函数在执行给定的 lambda 之前执行某些操作?

hadoop - 在 Pig 中读取非字符串分区的 Hive 表

amazon-web-services - 自动伸缩组实例未在 ALB 上注册

amazon-web-services - 如何等待 lambda 函数完成 SQS 队列的处理?

java - 导出 Jenkins 工作的构建历史

java - Maven更新项目和依赖项