Сельдерей Цветочная Безопасность в Производстве

Я собираюсь использовать Flower ( https://github.com/mher/flower) для мониторинга моих задач в Celery вместо django-admin, как рекомендовано в их документах ( http://docs.celeryproject.org/en/latest/userguide/monitoring.html). Однако, поскольку я новичок в этом, меня немного смущает то, как страница Flower основана только на HTTP, а не на HTTPS. Как я могу включить защиту для своих задач в Celery, чтобы любой старый пользователь не мог просто зайти на сайт, не требующий входа в систему, http://flowerserver.com:5555/ и что-то изменить?

Я рассмотрел собственную документацию Celery по этому вопросу, но, к сожалению, там нет упоминаний о том, как защитить API или веб-интерфейс Flower. Все это говорит: [Need more text here]

Спасибо!

Обновление: мой вопрос частично дублирует здесь: Как мне добавить аутентификацию и конечную точку для Django Celery Flower Monitoring?

Однако я проясняю его вопрос здесь, спрашивая, как запустить его, используя среду, включающую nginx, gunicorn и celery на одной и той же удаленной машине. Я тоже задаюсь вопросом о том, как настроить внешний доступный URL-адрес Flower, но также предпочел бы что-то вроде https, а не http, если это возможно (или какой-то способ защиты webui и удаленного доступа к нему). Мне также нужно знать, является ли оставление работающего Flower значительным риском для безопасности для любого, кто может получить доступ к внутреннему API-интерфейсу Flower, и каким может быть наилучший способ обеспечить это, или же его следует просто отключить и использовать только для некоторых целей. нужна основа.

8 ответов

Решение

Вы можете запустить flower с флагом --auth, который будет аутентифицироваться с использованием определенного адреса электронной почты Google:

celery flower --auth=your.email@gmail.com

Изменить 1:

Новая версия Flower требует еще нескольких флагов и зарегистрированного клиента OAuth2 с Google Developer Console:

celery flower --auth=your.email@gmail.com --oauth2_key="client_id" --oauth2_secret="client_secret" --oauth2_redirect_uri="http://example.com:5555/login"

oauth2_redirect_uri должен быть фактическим URL-адресом для входа в систему, а также должен быть добавлен к авторизованным URL-адресам перенаправления в консоли разработки Google.

К сожалению, эта функция не работает должным образом в текущей стабильной версии 0.7.2, но теперь это исправлено в версии для разработчиков 0.8.0-dev с этим коммитом.

Изменить 2:

Вы можете настроить Flower с помощью обычной аутентификации:

celery flower --basic_auth=user1:password1,user2:password2

Затем заблокируйте порт 5555 для всех, кроме localhost, и настройте обратный прокси для nginx или для apache:

ProxyRequests off
ProxyPreserveHost On
ProxyPass / http://localhost:5555

Затем убедитесь, что мод прокси включен:

sudo a2enmod proxy
sudo a2enmod proxy_http

Если вы не можете установить его на отдельный поддомен, например: flower.example.com (конфиг выше), вы можете настроить его для example.com/flower:

запустить цветок с url_prefix:

celery flower --url_prefix=flower --basic_auth=user1:password1,user2:password2

в конфиге apache:

ProxyPass /flower http://localhost:5555

Конечно, убедитесь, что SSL настроен, иначе нет смысла:)

Я понял это, используя прокси на стороне Django https://pypi.org/project/django-revproxy/. Таким образом, Flower скрыт за Django auth, который более гибок, чем базовый auth. И вам не нужно правило перезаписи в NGINX.

urls.py

urlpatterns = [
    re_path(r'^flower/?(?P<path>.*)$', FlowerProxyView.as_view()),
    ...
]

views.py

from django.contrib.auth.mixins import UserPassesTestMixin
from revproxy.views import ProxyView


class FlowerProxyView(UserPassesTestMixin, ProxyView):
    # `flower` is Docker container, you can use `localhost` instead
    upstream = 'http://flower:5555'

    def test_func(self):
        return self.request.user.is_superuser

Кредиты https://www.webnach.ru/django-auth-for-celery-flower.html

Flower 0.9.5 и выше

Префикс URL-адреса необходимо переместить в путь прокси: https://github.com/mher/flower/pull/766

urls.py

urlpatterns = [
    FlowerProxyView.as_url(),
    ...
]

views.py

class FlowerProxyView(UserPassesTestMixin, ProxyView):
    upstream = 'http://{}:{}'.format('flower', 5555)
    url_prefix = 'flower'
    rewrite = (
        (r'^/{}$'.format(url_prefix), r'/{}/'.format(url_prefix)),
     )

    def test_func(self):
        return self.request.user.is_superuser

    @classmethod
    def as_url(cls):
        return re_path(r'^(?P<path>{}.*)$'.format(cls.url_prefix), cls.as_view())

Я хотел цветок в подкаталоге моего веб-сервера, поэтому моя конфигурация обратного прокси nginx выглядела так:

location /flower/ {
    proxy_pass http://localhost:5555/;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_http_version 1.1;

    auth_basic  "Restricted";
    auth_basic_user_file  /etc/nginx/.htpasswd;
}

Теперь я могу добраться до цветка (защищенного паролем) через www.example.com/flower.

Большая часть этого получена на странице документации Flower о настройке обратного прокси-сервера nginx:

http://flower.readthedocs.org/en/latest/reverse-proxy.html

Я последовал подходу @petr-přikryl, используя прокси-представление. Однако я не смог заставить его проверить аутентификацию (я не думаю, что test_funcкогда-либо называется). Вместо этого я решил встроить это в представления администратора Django и использовать AdminSite.admin_view()(как описано здесь ), чтобы обернуть представление аутентификацией администратора Django.

В частности, я внес следующие изменения:

      # Pipfile
[packages]
...
django-revproxy="*"
      # admin.py
class MyAdminSite(admin.AdminSite):
    # ...
    def get_urls(self):
        from django.urls import re_path

        # Because this is hosted in the root `urls.py` under `/admin` this 
        # makes the total prefix /admin/flower
        urls = super().get_urls()
        urls += [
            re_path(
                r"^(?P<path>flower.*)$",
                self.admin_view(FlowerProxyView.as_view()),
            )
        ]
        return urls
      # views.py
from __future__ import annotations

from django.urls import re_path

from revproxy.views import ProxyView


class FlowerProxyView(ProxyView):
    # Need `/admin/` here because the embedded view in the admin app drops the
    # `/admin` prefix before sending the URL to the ProxyView
    upstream = "http://{}:{}/admin/".format("localhost", 5555)

Наконец, нам нужно убедиться, что --url_prefix устанавливается при запуске цветка, поэтому я настроил его на запуск в нашей производственной среде и среде разработки:

      celery flower --app=my_app.celery:app --url_prefix=admin/flower

Это ответ на сообщение Петра Пршикрыла. django-revproxy не работает в моем проекте Django 4.1.x. Я сталкиваюсь с ошибкойAttributeError: 'HttpResponse' object has no attribute '_headers'. Многие другие сталкиваются с той же проблемой. brianmay в ветке выпуска утверждает: «Я думаю, что этот проект практически мертв, извините».

Я пошел с другой библиотекой, чтобы служить обходным путем.

Установить джанго-прокси

Вот как выглядит мой код.

      # urls.py

from django.urls import re_path
from myapp.views import flower


urlpatterns = [
    re_path("flower/(?P<path>.*)", flower),
]
      # views.py

from django.views.decorators.csrf import csrf_exempt
from proxy.views import proxy_view

@csrf_exempt
def flower(request, path):
    extra_requests_args = {}
    remoteurl = f"http://localhost:5555/flower/" + path
    return proxy_view(request, remoteurl, extra_requests_args)

Затем запустите сельдерей с

      $ celery --app myproject flower --loglevel INFO --url_prefix=flower

Затем вы можете просмотреть его в своем браузере через Django по адресу http://localhost:8000/flower/.

Дополнительные замечания:

--url_prefix= важен, потому что это позволит прокси-серверу обслуживать статические файлы, которые запрашивает цветок.

Если вы используете docker compose, вам, вероятно, потребуется изменить имя хоста вremoteurlстрока в функции, чтобы отразить то же самое службы. Например, моя служба правильно называетсяflowerв моемdocker-compose.yamlфайл. Поэтому я бы изменил строку сf"http://localhost:5555/flower/"кf"http://flower:5555/flower/"

Чтобы разгрузить приложение django, я предлагаю вам использовать заголовок, чтобы использовать nginx для прокси-сервера Flower server. Это выглядит следующим образом:

  1. пользователь запрашивает цветочную дорожку (например, /task)
  2. nginx proxy_pass запрос к вашему приложению, как обычно
  3. ваше приложение django решает принять или отклонить запрос (например, на основе аутентификации)
  4. если ваше приложение принимает запрос, оно возвращает ответ с HTTP-заголовком вместе со строкой внутреннего местоположения , то есть путем, к которому пользователь не может получить доступ напрямую
  5. nginx перехватывает ответ вместо того, чтобы пересылать его пользователю, и использует его как новый путь с возможностью на этот раз доступа к внутренним местоположениям, в нашем случае к серверу цветов.

Если запрос отклонен, просто не используйте X-Accel-Redirect и обработайте это дело, как и любой другой отклоненный запрос, который вы бы реализовали.

nginx.conf:

      upstream celery_server {
    server /var/run/celery/flower.sock;
}

upstream app_server {
    server /var/run/gunicorn/asgi.sock;
}

server {
    listen 80;

    location /protected/task {
        internal;  # returns 404 if accessed directly
        proxy_http_version 1.1;
        proxy_redirect off;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;

        proxy_pass http://celery_server/task;
    }

    location / {
        proxy_http_version 1.1;
        proxy_redirect off;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $server_name;

        proxy_pass http://app_server;
    }
}

views.py:

      from django.contrib.admin.views.decorators import staff_member_required
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse


class XAccelRedirectResponse(HttpResponse):
    def __init__(self, path, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self['X-Accel-Redirect'] = '/protected' + path
        del self['Content-Type']  # necessary


# I chose to only allow staff members, i.e. whose who can access the admin panel
@staff_member_required
@csrf_exempt
def task_app(request, path):
    query_str = request.META['QUERY_STRING']  # you must keep the query string
    return XAccelRedirectResponse(f'/task/{path}?{query_str}')

urls.py:

      from django.urls import re_path
from app import views


urlpatterns = [
    re_path('task/(?P<path>.*)', views.task_app, name='task'),
]

Цветок

Важно изменить url-prefix цветов:

      celery flower --unix-socket="/var/run/celery/flower.sock" --url-prefix="task"

Да, на цветке нет аутентификации, поскольку он просто общается с брокером, но если вы запускаете его через SSL, тогда базовая аутентификация должна быть достаточно хорошей.

Как HTTP и HTTPS повлияют на безопасность Celery? На какие логины вы ссылаетесь?

Цветочные мониторы к очереди сельдерея, прикрепляясь к рабочим. При настройке Flower вам необходимо указать строку подключения [broker]://[user_name]:[password]@[database_address]:[port]/[instance]. Имя пользователя и пароль - это учетные данные для входа в базу данных по вашему выбору.

Если вы имеете в виду этот логин, не будет достаточно просто отключить / удалить их логины?

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