djangoamazon-web-servicesnginxuwsgiflower

nginx, flower, Django, proxy_pass, and rejecting invalid HOSTs


I have a server on AWS running nginx/uWSGI/Django, with RabbitMQ, flower, and Celery.

The problem: use nginx to proxy for flower without opening up a new port, and also reject badly-formed requests that would cause Invalid HTTP_HOST Header errors in Django.

I can do either but not both because I am not super experienced with nginx.

I'm running flower 0.9.4, as I'm aware of the bug in 0.9.5.

In the following config files, if I comment out the "reject_hosts.conf" line, flower works, but I stop rejecting hosts like I should. If I leave it in, the web browser times out making the request for the /flower URL.

Here's the relevant config files:

nginx-app.conf

# the upstream component nginx needs to connect to
upstream django {
    server unix:/home/app.sock; # uwsgi socket
}

include redirect_ssl.conf; #301 SSL redirects

# actual webserver. Note that https is handled by AWS so no listen on 443
server {
    # default_server indicates that this server block is the block to use if no others match server_name
    listen      8080 default_server;
    listen      [::]:8080 default_server;
    charset     utf-8;

    # max upload size
    client_max_body_size 3M;   # adjust to taste
    include django_aliases.conf; # media and static directories

    include reject_hosts.conf; # return 444 if wrong HOSTs header
    include flower.conf; # proxy flower

    include django_root.conf; # django upstream
}

redirect_ssl.conf

## 301 redirect for HTTPS
server {
     listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

flower.conf - this one WORKS if the reject_hosts one is not included. I tried about a thousand variations of this to get one that works properly with all files in Flower.

location /flower/ {
    rewrite ^/flower/(.*)$ /$1 break;
    proxy_pass http://localhost:5555;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_http_version 1.1;
}

reject_hosts.conf

if ($host !~* ^(127.0.0.1|localhost|mydomain.com|myotherdomain.com|my.subdomain.com)$ ) {
    return 444;
}

django_aliases.conf

# Django media
location /media  {
    alias media;  # Media files, actual location removed for paranoia
}

location /static {
    alias static; # Static files, actual location removed for paranoia
}

django_root.conf

location / {
    uwsgi_pass  django;
    include     uwsgi_params; # location removed for paranoia
    uwsgi_read_timeout 600;
    uwsgi_send_timeout 600;
    uwsgi_connect_timeout 60;
    uwsgi_ignore_client_abort on;
}

Finally, Flower is started by supervisord like so:

command = python3 -m celery -A myproj flower --url_prefix=flower --port=5555

Solution

  • You can try to filter the HTTP requests via the server blocks (as suggested by nginx tutorial):

    server {
        listen 80;
        listen [::]:80;
        server_name 127.0.0.1 localhost mydomain.com myotherdomain.com my.subdomain.com;
        return 301 https://$host$request_uri;
    }
    server {
        # handle invalid requests with his one
        listen 80 default_server;
        listen [::]:80 default_server;
        return 444;
    }
    server {
        listen      8080;
        listen      [::]:8080;
        server_name 127.0.0.1 localhost mydomain.com myotherdomain.com my.subdomain.com;
        ... # rest of the configuration
    }
    server {
        # handle invalid requests with his one
        listen      8080 default_server;
        listen      [::]:8080 default_server;
        return 444;
    }
    

    This way you won't need the reject_hosts.conf file at all.