org.apache.hadoop.security.AccessControlException: в доступе отказано при попытке доступа к корзине S3 через URI s3n с помощью API-интерфейсов Hadoop Java на EC2

сценарий

Я создаю роль AWS IAM под названием "my-role", определяющую 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/*"
      ]
    }
  ]
}

Я запускаю экземпляр EC2 (Amazon Linux 2014.09.1) из командной строки, используя AWS CLI, указывая "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". (CLI-интерфейс AWS устанавливается и настраивается по умолчанию при запуске этого AMI. Экземпляр EC2 и корзина S3 находятся в одной учетной записи AWS)

Я запускаю / устанавливаю сервер и контейнер Tomcat7 в таком экземпляре, на котором я без проблем разворачиваю сервлет J2EE 1.7.

Такой сервлет должен загружать в локальную файловую систему файл из корзины S3, в частности из s3://my-bucket/custom-path/file.tar.gz используя Hadoop Java API. (Пожалуйста, обратите внимание, что я попробовал артефакт hadoop-common 2.4.x, 2.5.x, 2.6.x без каких-либо положительных результатов. Я опубликую ниже исключения, которое получаю при использовании 2.5.x)

В сервлете я получаю свежие учетные данные из 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: в доступе отказано: s3n://my-bucket/custom-path/file.tar.gz at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystem44torej.jpg) в org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.processException(Jets3tNativeFileSystemStore.java:427) в org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.handle.Ex.jj.Slay.HaseFid fs.s3native. DelegatingMethodAccessorImpl.java:43) в java.lang.reflect.Method.invoke(Method.java:606) в org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.joopgap.ap): 7).io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102) в org.apache.hadoop.fs.s3native.$Proxy12.retrieveMetadata(Неизвестный источник) в org.apache.hadoop.fs.s3native.NativeS3FileSystem.getFileStatus:4). в 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.ap. 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 и секретный ключ доступа должны быть указаны как имя пользователя или пароль (соответственно) URL-адреса s3n или путем установки свойств fs.s3n.awsAccess KeyId или fs.s3n.awsSecretAccess Key (соответственно).

Финальные заметки

  • Если я пытаюсь загрузить файл с того же URI (но s3 вместо s3n), используя команды CLI AWS из экземпляра, у меня НЕТ ПРОБЛЕМ.
  • Если я попытаюсь загрузить дистрибутив Hadoop (например, 2.4.1 с https://archive.apache.org/dist/hadoop/core/hadoop-2.4.1/), разархивируйте его, извлеките временные учетные данные AWS из метаданных экземпляра. URL и попробуйте запустить <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(PathD68.java) в 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)

Извините за длинный пост, я просто старался быть как можно более подробным. Спасибо за любую возможную помощь здесь.

1 ответ

Вы используете STS/ временные учетные данные AWS; в настоящее время они не поддерживаются s3 или же s3nFileSystem реализации в Hadoop.

AWS STS/ временные учетные данные включают в себя не только (ключ доступа, секретный ключ), но и дополнительно токен сеанса. Хадуп s3 а также s3nFileSystem (s) пока не поддерживают включение токена сеанса (т.е. ваша конфигурация fs.s3n.awsSessionToken не поддерживается и игнорируется s3nFileSystem,

Из AmazonS3 - Hadoop Wiki...
(Обратите внимание, что нет упоминания о 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 из apache / hadoop на github.com, вы заметите, что понятие токена сеанса полностью отсутствует в представлении учетных данных S3.

Был представлен патч для устранения этого ограничения (подробно здесь); однако, это не было интегрировано.


Если вы используете роли экземпляра AWS IAM, вы можете изучить новые s3aFileSystem это было добавлено в Hadoop 2.6.0. В нем утверждается, что он поддерживает аутентификацию на основе ролей IAM (то есть вам вообще не нужно явно указывать ключи).

Билет Hadoop JIRA описывает, как настроить s3aFileSystem:

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

fs.s3a.access.key - Ваш идентификатор ключа доступа AWS (не указан для аутентификации роли)
fs.s3a.secret.key - Ваш секретный ключ AWS (не указан для аутентификации роли)

Другие вопросы по тегам