Ограничиваем доступ к S3 средствами Rails-приложения

Добавить хранение файлов в облаке Amazon S3 в приложение на Rails очень просто: устанавливаем гемы paperclip и aws-sdk, и готово. Но что если мы хотим скрыть прямые ссылки на хранилище S3, и даже разграничивать доступ к конкретным файлам в зависимости от роли пользователя? Вот рабочий пример:

Что мы хотим получить в итоге на нашем сайте:
img src="/p/thumb/123/cat.jpg"

– при этом сам файл “cat.jpg” лежит в облаке AWS S3, прямая ссылка на него недоступна, и мы хотим иметь полный контроль над скачиванием: ограничивать количество, разрешать или запрещать доступ к нему и т.д. – силами нашего приложения, индивидуально для каждого пользователя. Итак, поехали:

Этот пример подразумевает, что мы имеем два “виртуалхоста” в nginx: один для фронтенда (SPA), второй для бэкэнда (Rails API). Не пытайтесь бездумно копипастить файлы – в них для краткости приведены лишь ключевые строки. Дополните их до работоспособного кода самостоятельно.

Для начала, создадим хелпер в нашей модели:

Он будет генерировать локальные URL-ы к медиа-ресурсам, которые будут перехватываться в nginx:

Затем они будут направляться в бэкэнд:

(обратитие внимание, что мы используем заголовок ‘X-Project-Private’ чтобы предотвратить прямые запросы к секретному локейшену)

Запросы направляются в роутер Rails-приложения:

И далее в контроллер:

Как это работает? Контроллер парсит параметры запроса, выполняет необходимые проверки, и подготавливает специальный заголовок ‘X-Accel-Redirect’, содержащий путь (не URL) к ресурсу S3 относительно вашего S3-бакета. Далее nginx ловит этот заголовок internally, и посылает подзапрос к локейшену “secret_store”. Пользователю весь этот процесс не виден.

В локейшене “secret_store” мы просто реврайтим URI запроса (извлекая из него path) и делаем обычный proxy_pass на хост Amazon S3 (проверьте, что у вас правильный хост – они разные для разных регионов). Также мы добавляем специальный заголовок ‘S3_SECRET_UA’ для соответствия S3 полиси (см. далее).

Убедитесь, что всё работает на этом шаге! Иначе вы можете сломать ваш сайт.

S3 полиси:

Не копипастите полиси, воспользуйтесь генератором (в настройках своего S3-бакета). После добавления полиси, чтобы убедиться что оно предотвращает запросы без секретного заголовка, попробуйте открыть какой-нибудь URL до вашего контента на S3 – вы должны получить ‘Access denied’.

Если всё работает правильно, теперь вы можете проверять роли и права пользователей в вашем контроллере.