Tech
January 24, 2024

Проблема использования модуля aws_s3 в Ansible при работе с S3 от Yandex.Cloud

Вводные

У Ansible есть модуль amazon.aws.s3_object с помощью которого можно работать с S3 бакетом. Но при попытке попытке загрузить объект в S3 Yandex .Cloud возникают ошибки.

Цель – сделать это с ролью storage.uploader, поскольку этой роли должно быть достаточно.

Но, добавив в Playbook такую задачу

- name: Copy file to s3 bucket
  amazon.aws.s3_object:
    mode: put
    validate_certs: yes
    bucket: "s3_bucket"
    endpoint_url: "s3_endpoint"
    aws_access_key: "s3_aws_access_key"
    aws_secret_key: "s3_aws_secret_key"
    object: "example-content-5"
    content: "example-content-5"

мы не получаем ожидаемого результата. Давайте по-порядку попробуем разобраться с этим.

При отладке шаг за шагом, я получал ошибки.

Шаг 1

Первая наша ошибка: x_amz-_server-_side-_encryption": "only aws: kms value allowed"

Для отладки я запускал Playbook используя флаг -vvv, а также для отладки используем content вместо src. Так вывод немного информативнее. Благодаря этому я получил текст ошибки, и, кажется, тут всё просто, добавляем параметр:

encryption_mode: "aws:kms"

Прочесть об этом параметре можно тут: encryption_mode: "aws:kms"

Шаг 2

Следующая ошибка не такая очевидная: Unable to set object ACL: An error occurred (AccessDenied) when calling the PutObjectAcl operation: Access Denied

Как я понял, после создания объекта в Bucket, этот модуль Ansible пыается выполнить какие-то оперции с ACL. Это конечно можно решить быстро, выдав что-то типа storage.admin сервисному аккаунту. Но нас это не устраивает.

Шаг 3

Может можно добавить storage.editor или storage.configurer? Это работает пока не поменяем content на src. После этого снова получим AccessDenied.

Всё это кажется несправедливым, посколько storage.uploader должно быть достаточно, чтобы загрузить файл в S3. Например при использовании aws-cli c теми же кредами SA, операция выполняется успешно.

Эмпирическим методом выяснил, чтобы прав storage.uploader на загрузку с помощью этого модуля может быть достаточно, давайте еще немного поправим конфиг:

- name: Copy file to s3 bucket
  amazon.aws.s3_object:
  		mode: put
		encrypt: false
        encryption_mode: "aws:kms"
        permission: []
        validate_certs: yes
        bucket: "s3_bucket"
        endpoint_url: "s3_endpoint"
        aws_access_key: "s3_aws_access_key"
        aws_secret_key: "s3_aws_secret_key"
        object: "example-object-5"
    	src: "/path/to/file"

Мы отключили encrypt и явным образом передаем пустой массив permission. Так вроде всё работает.

Но что делать если мы желаем использовать encrypt: true? Тогда используйте роль storage.configurer.

Если честно, я не понял всё это заранее, читая документацию, поэтому обратился в техподдержку Yandex.Cloud. Вероятно для тех, кто досконально знает принцип работы S3, ответ не покажется чем-то новым.

Ответ техподдержки Yandex.Cloud

Изучили ситуацию. Да, кажется что, если вы хотите загрузить объект в бакет с шифрованием, достаточно ролей storage.uploader (для загрузки объекта) и kms.keys.encrypter (для доступа к ключу). Это может привести к ситуации, когда пользователь перезапишет уже существующий зашифрованный объект новым, используя другой ключ шифрования. В итоге пользователи изначального объекта не смогут прочитать его.

Поэтому для более гранулярной настройки доступа в этом месте нужна роль storage.configurer. Так как использование шифрования фактически влияет на конфигурацию бакета. Поэтому это ожидаемое поведение со стороны Object Storage.

В документации мы упоминаем роль storage.editor при использовании шифрования на стороне Object Storage. Она сочетает в себе возможности storage.uploader и storage.configurer. Да, согласны, стоит упомянуть, что роли storage.uploader при загрузке объектов с использованием шифрования не достаточно. Мы передадим информацию коллегам.

Я добавлю от себя, что storage.editor может не подойти для использования на каком-нибудь сервере, откуда вы хотите что-то бэкапировать. Поскольку storage.editor также имеет возможность конфигурировать бакет.