Celery Flower Security in Production
Solution 1:
You can run flower with --auth flag, which will authenticate using a particular google email:
celery flower [email protected]
Edit 1:
New version of Flower requires couple more flags and a registered OAuth2 Client with Google Developer Console:
celery flower \
[email protected] \
--oauth2_key="client_id" \
--oauth2_secret="client_secret" \
--oauth2_redirect_uri="http://example.com:5555/login"
oauth2_redirect_uri
has to be the actual flower login url, and it also has to be added to authorized redirect url's in Google Development Console.
Unfortunately this feature doesn't work properly in current stable version 0.7.2
, but it is now fixed in development version 0.8.0-dev
with this commit.
Edit 2:
You can configure Flower using basic authentication:
celery flower --basic_auth=user1:password1,user2:password2
Then block 5555 port for all but localhost and configure reverse proxy for nginx or for apache:
ProxyRequests off
ProxyPreserveHost On
ProxyPass / http://localhost:5555
Then make sure proxy mod is on:
sudo a2enmod proxy
sudo a2enmod proxy_http
In case you can't set it up on a separate subdomain, ex: flower.example.com
(config above), you can set it up for example.com/flower
:
run flower with url_prefix
:
celery flower --url_prefix=flower --basic_auth=user1:password1,user2:password2
in apache config:
ProxyPass /flower http://localhost:5555
Of course, make sure SSL is configured, otherwise there is no point :)
Solution 2:
I have figured out it using proxy on Django side https://pypi.org/project/django-revproxy/. So Flower is hidden behind Django auth which is more flexible than basic auth. And you don't need rewrite rule in NGINX.
Flower 0.9.5 and higher
URL prefix must be moved into proxy path: https://github.com/mher/flower/pull/766
urls.py
urlpatterns = [
FlowerProxyView.as_url(),
...
]
views.py
class FlowerProxyView(UserPassesTestMixin, ProxyView):
# `flower` is Docker container, you can use `localhost` instead
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())
Flower 0.9.4 and lower
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
Solution 3:
I wanted flower on a subdirectory of my webserver, so my nginx reverse proxy configuration looked like this:
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;
}
Now I can get to flower (password-protected) via www.example.com/flower
Most of this is derived from the Flower documentation page about configuring an nginx reverse proxy:
http://flower.readthedocs.org/en/latest/reverse-proxy.html
Solution 4:
I followed @petr-přikryl's approach using a proxy view. However I couldn't get it to verify authentication (I don't think test_func
is ever called). Instead I chose to embed this in the Django Admin views and use AdminSite.admin_view()
(as described here) to wrap the view with Django Admin authentication.
Specifically, I made the following changes:
# 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)
Lastly, we need to make sure that --url_prefix
is set when running flower, so I set it to run like this in our production and dev environments:
celery flower --app=my_app.celery:app --url_prefix=admin/flower
Solution 5:
Yep there's not auth on flower, since it's just talking to the broker, but if you run it over SSL then basic auth should be good enough.