- PVSM.RU - https://www.pvsm.ru -

Проксируем файлы из AWS S3 средствами nginx

Казалось бы, задача реализации фронтенда для AWS на nginx звучит как типовой кейс для StackOverflow — ведь проблем с проксированием файлов из S3 быть не может? На деле выяснилось, что готовое решение не так-то просто найти, и данная статья должна исправить эту ситуацию.

Проксируем файлы из AWS S3 средствами nginx - 1

Зачем это вообще может понадобиться?

  1. Контроль доступа к файлам средствами nginx — актуально для концепции IaC (инфраструктура как код). Все изменения, связанные с доступом, будут вноситься только в конфигах, которые лежат в проекте.
  2. Если отдавать файлы через свой nginx, появляется возможность их кэшировать и сэкономить тем самым на запросах к S3.
  3. Подобный прокси поможет абстрагироваться от типа хранилища файлов для разных инсталляций приложения (ведь помимо S3 существуют и другие решения).

Сформулируем рамки

  • Исходный bucket должен быть приватным — нельзя разрешать анонимным пользователям качать файлы напрямую из S3. Если в вашем случае это ограничение не работает, то просто используйте proxy_pass и дальше можете не читать.
  • Настройка со стороны AWS должна быть одноразовой по принципу «настроил и забыл», дабы упростить эксплуатацию.

Ищем решение в лоб

Если ваш оригинальный bucket публичный, то никакие сложности вам не грозят, проксируйте запросы на S3 и всё будет работать. Если же он приватный, то придётся как-то аутентифицироваться в S3. Что нам предлагают коллеги из интернета:

  1. Есть примеры [1] реализации протокола аутентификации средствами nginx. Решение хорошее, но к сожалению, оно рассчитано на устаревший протокол аутентификации (Signature v2 [2]), который не работает в некоторых ЦОД Amazon [3]. Если вы попытаетесь воспользоваться этим решением, например, во Франкфурте, то получите ошибку «The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256». Более свежая версия протокола (Signature v4 [4]) гораздо сложнее в реализации, а готовых решений для nginx с ней нет.
  2. Есть сторонний модуль для nginx — ngx_aws_auth [5]. Если судить по исходникам, он поддерживает Signature v4. Однако проект выглядит заброшенным: более года отсутствуют изменения в кодовой базе, а также имеется проблема совместимости [6] с другими модулями, на которую не реагирует разработчик. К тому же, добавление дополнительных модулей в nginx — это само по себе зачастую болезненный шаг.
  3. Можно воспользоваться отдельным s3-прокси, коих написано достаточно много. Лично мне понравилось решение на Go — aws-s3-proxy [7]: у него есть готовый и достаточно популярный образ на DockerHub. Но в этом случае приложение обрастёт ещё одним компонентом со своими потенциальными проблемами.

Применяем AWS Bucket Policy

AWS, как правило, пугает новых пользователей своей сложностью и объёмом документации. Но если разобраться, то понимаешь, что он устроен очень логично и гибко. В Amazon'е нашлось решение и для нашей задачи — S3 Bucket Policy [8]. Этот механизм позволяет строить гибкие правила авторизации для bucket'а на основе разных параметров клиента или запроса.

Проксируем файлы из AWS S3 средствами nginx - 2
Интерфейс генератора политик — AWS Policy Generator [9]

Вот некоторые интересные параметры, к которым можно привязаться:

  • IP (aws:SourceIp),
  • заголовок Referer (aws:Referer),
  • заголовок User-Agent (aws:UserAgent),
  • остальные — описаны в документации [10].

Привязка к IP — хороший вариант только при условии, что у приложения есть определённое место жительства, а в наше время это редкость. Соответственно, нужно привязываться к чему-то ещё. В качестве решения я предлагаю сгенерировать секретный User-Agent или Referer и отдавать файлы только тем пользователям, которые знают секретный заголовок. Вот как выглядит подобная политика:

{
    "Version": "2012-10-17",
    "Id": "http custom auth secret",
    "Statement": [
        {
            "Sid": "Allow requests with my secret.",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::example-bucket-for-habr/*",
            "Condition": {
                "StringLike": {
                    "aws:UserAgent": [
                        "xxxyyyzzz"
                    ]
                }
            }
        }
    ]
}

Немного пояснений:

  • "Version": "2012-10-17" — это внутренняя кухня AWS, которую править не надо;
  • Principal — кого касается данное правило. Можно указать, что оно работает только для определённой AWS-учётки, но в нашем случае стоит "*" — это означает, что правило работает вообще для всех, в том числе и анонимных пользователей;
  • Resource — ARN (Amazon Resource Name) bucket'а и шаблон для файлов внутри bucket'а. В нашем случае политика касается всех файлов, которые лежат в bucket'е example-bucket-for-habr;
  • Condition — здесь указываются условия, которые должны сойтись, чтобы политика заработала. В нашем случае мы сравниваем предустановленный заголовок User-Agent со строкой xxxyyyzzz.

А вот как выглядит работа этого правила с точки зрения анонимного пользователя:

$ curl -I https://s3.eu-central-1.amazonaws.com/example-bucket-for-habr/hello.txt
HTTP/1.1 403 Forbidden
$ curl -I https://s3.eu-central-1.amazonaws.com/example-bucket-for-habr/hello.txt -H 'User-Agent: xxxyyyzzz'
HTTP/1.1 200 OK

Осталось настроить nginx для проксирования:

  location /s3-media/ {
      limit_except GET {
          deny all;
      }

      set $aws_bucket "example-bucket-for-habr";
      set $aws_endpoint "s3.eu-central-1.amazonaws.com:443";
      set $aws_custom_secret "xxxyyyzzz";

      proxy_set_header User-Agent $aws_custom_secret;

      rewrite ^/s3-media/(.*)$ /$aws_bucket/$1 break;
      proxy_buffering off;
      proxy_pass https://$aws_endpoint;
  }

Заключение

Итого, единожды написав простую политику для bucket'а, мы получили возможность безопасно проксировать файлы с помощью nginx. При этом мы не привязаны по IP и не зависим от дополнительного ПО.

P.S.

Читайте также в нашем блоге:

Автор: Андрей Половов

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/nginx/296142

Ссылки в тексте:

[1] примеры: https://gist.github.com/skddc/c7a982226d08acd4e041/c38f32013fa8e22acffad87a63ab5f74717a3c64

[2] Signature v2: http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html

[3] некоторых ЦОД Amazon: https://docs.aws.amazon.com/en_us/general/latest/gr/signature-version-2.html

[4] Signature v4: https://docs.aws.amazon.com/en_us/AmazonS3/latest/API/sig-v4-header-based-auth.html

[5] ngx_aws_auth: https://github.com/anomalizer/ngx_aws_auth

[6] проблема совместимости: https://github.com/anomalizer/ngx_aws_auth/issues/47#issuecomment-341960554

[7] aws-s3-proxy: https://github.com/pottava/aws-s3-proxy

[8] S3 Bucket Policy: https://docs.aws.amazon.com/en_us/AmazonS3/latest/dev/example-bucket-policies.html

[9] AWS Policy Generator: https://awspolicygen.s3.amazonaws.com/policygen.html

[10] документации: https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/reference_policies_condition-keys.html

[11] Awless — мощная альтернативная CLI-утилита для работы с сервисами AWS: https://habr.com/company/flant/blog/330398/

[12] Rook — «самообслуживаемое» хранилище данных для Kubernetes: https://habr.com/company/flant/blog/348044/

[13] Теория и практика бэкапов с Borg: https://habr.com/company/flant/blog/420055/

[14] Источник: https://habr.com/post/426739/?utm_source=habrahabr&utm_medium=rss&utm_campaign=426739