I'm using django channel for my Django application. On top of Django, I add nginx as layer for http request. All http request is working nicely, but when I tried to create a websocket connection, it was getting 302 HTTP Code.
Nginx Configuration
# Enable upgrading of connection (and websocket proxying) depending on the
# presence of the upgrade field in the client request header
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Define connection details for connecting to django running in
# a docker container.
upstream uwsgi {
server uwsgi:8080;
}
server {
# OTF gzip compression
gzip on;
gzip_min_length 860;
gzip_comp_level 5;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json;
gzip_disable “MSIE [1-6].(?!.*SV1)”;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# the port your site will be served on
listen 8080;
# the domain name it will serve for
#server_name *;
charset utf-8;
error_page 500 502 /500.html;
location = /500.html {
root /html;
internal;
}
# max upload size, adjust to taste
client_max_body_size 15M;
# Django media
location /media {
# your Django project's media files - amend as required
alias /home/web/media;
expires 21d; # cache for 71 days
}
location /static {
# your Django project's static files - amend as required
alias /home/web/static;
expires 21d; # cache for 21 days
}
location /archive {
proxy_set_header Host $http_host;
autoindex on;
# your Django project's static files - amend as required
alias /home/web/archive;
expires 21d; # cache for 6h
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass uwsgi;
# the uwsgi_params file you installed needs to be passed with each
# request.
# the uwsgi_params need to be passed with each uwsgi request
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
if (!-f $request_filename) {
proxy_pass http://uwsgi;
break;
}
# Require http version 1.1 to allow for upgrade requests
proxy_http_version 1.1;
# We want proxy_buffering off for proxying to websockets.
proxy_buffering off;
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if you use HTTPS:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client for the sake of redirects
proxy_set_header Host $http_host;
# We've set the Host header, so we don't need Nginx to muddle
# about with redirects
proxy_redirect off;
# Depending on the request value, set the Upgrade and
# connection headers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
Routing.py
from channels.routing import route
from consumers import ws_add, ws_message, ws_disconnect
channel_routing = [
route("websocket.connect", ws_add),
route("websocket.receive", ws_message),
route("websocket.disconnect", ws_disconnect),
]
Consumers. py
from channels import Channel, Group
from channels.sessions import channel_session
from channels.auth import channel_session_user, channel_session_user_from_http
# Connected to websocket.connect
#channel_session_user_from_http
def ws_add(message):
# Accept the connection
message.reply_channel.send({"accept": True})
# Add to the group
Group("progress-%s" % message.user.username).add(message.reply_channel)
#channel_session_user
def ws_message(message):
Group("progress-%s" % message.user.username).send({
"text": message['text'],
})
# Connected to websocket.disconnect
#channel_session_user
def ws_disconnect(message):
Group("progress-%s" % message.user.username).discard(message.reply_channel)
If i remove the nginx layer it is working nicely. Is there any configuration that I miss?
Related
Is it secure to set SECURE_SSL_REDIRECT=False if I have nginx setup as a reverse proxy serving the site over https?
I can access the site over SSL if this is set this to False, where as if it is True, I receive too many redirects response.
NOTE: nginx and django run from within docker containers.
My nginx.conf looks like:
upstream config {
server web:8000;
}
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name _;
ssl_certificate /etc/ssl/certs/cert.com.chained.crt;
ssl_certificate_key /etc/ssl/certs/cert.com.key;
location / {
proxy_pass http://config;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
alias /home/app/web/staticfiles/;
}
}
EDIT: Added http to https redirect in nginx.conf.
You need to add the following to your "location /" block:
location / {
...
proxy_set_header X-Forwarded-Proto $scheme;
...
}
I know it's not appropriate place to ask question about nginx but I stack with some issue for a few days and still have no idea how to solve a problem.
I would like to use nginx to redirect user from domain.com:3001 to sub.domain.com. Application on port 3001 is running in docker container, I didn't add any files in directory sites-available/sites-enabled. I have added two server blocks (vhosts) in my conf.d directory. In server block I set $upstream and resolver according to record in my /etc/resolv.conf file. The problem is that when I test in browser sub.domain.com every time I receive information that IP address could not be connected with any server (DNS_PROBE_FINISHED_NXDOMAIN) or 50x errors.
However, when I run curl sub.domain.com from the server I receive 200 with index.html response, this doesn't work when I run the same command from my local PC. Server domain is in private network. Have you any idea what my configuration files lack of?? Maybe there is some issue with the listen port when app is running in docker or maybe there is something wrong with the version of nginx? When I installed nginx there was empty conf.d directory, with no default.conf. I am lost...
Any help will be highly appreciated.
Here is my configuration files:
server.conf:
server
{
listen 80;
listen 443 ssl;
server_name sub.domain.net;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
ssl_certificate /etc/nginx/ssl/cer.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
#set_real_ip_from 127.0.0.1;
#real_ip_header X-Real-IP;
#real_ip_recursive on;
# location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
# }
location / {
resolver 10.257.10.4;
set $upstream https://127.0.0.1:3000;
proxy_pass $upstream;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;`enter code here`
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
nginx.conf
#user nginx;
worker_processes 1;
#error_log /var/log/nginx/error.log;
#error_log /var/log/nginx/error.log notice;
#error_log /var/log/nginx/error.log info;
#pid /var/run/nginx.pid;
include /etc/nginx/modules.conf.d/*.conf;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local]
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#tcp_nodelay on;
#gzip on;
#gzip_disable "MSIE [1-6]\.(?!.*SV1)";
server_tokens off;
include /etc/nginx/conf.d/*.conf;
}
# override global parameters e.g. worker_rlimit_nofile
include /etc/nginx/*global_params;
I'm working through https://serversforhackers.com/video/letsencrypt-for-free-easy-ssl-certificates and https://certbot.eff.org/docs/intro.html , trying to add an ssl certificate to my site (django 1.8 on nginx on ubuntu 16.04). I have been able to do this before a few months ago using the standalone option (Certbot cannot reach nginx webroot running django), but this time I want to get the certbot-auto script working so I can run it on a chron job. I tried:
deploy#server:/opt/certbot$ sudo ./certbot-auto certonly --webroot -w /var/www/html -d example.org -d www.example.org
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for example.org
http-01 challenge for www.example.org
Using the webroot path /var/www/html for all unmatched domains.
Waiting for verification...
Cleaning up challenges
Failed authorization procedure. www.example.org (http-01): urn:acme:erruthorized :: The client lacks sufficient authorization :: Invalid response http://www.example.org/.well-known/acme-challenge/6j3QzM4LGMRWaLYZXYTR98: "
If I paste http://www.example.org/.well-known/acme-challenge/6j3QzM4LGMRWaLYZXYTR98: " into the browser I get a 404 like in the screenshot. Is it possible to set django to allow the challenge to 'pass through ' the routing without generating a django error?
EDIT:
please note I am NOT running from root but rather a normal user (deploy)
output of nginx -T:
# configuration file /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
# configuration file /etc/nginx/sites-enabled/example3:
server {
#listen 80;
listen 80 ;
listen [::]:80 ;
listen 443 ssl http2 ;
listen [::]:443 ssl http2 ;
server_name example.org www.example.org;
include snippets/ssl-example.org.conf;
include snippets/ssl-params.conf;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/deploy/example3;
}
location / {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/example3.sock;
}
location ~* (?:^|/)\. {
allow all;
}
}
# configuration file /etc/nginx/snippets/ssl-example.org.conf:
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
# configuration file /etc/nginx/snippets/ssl-params.conf:
# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable preloading HSTS for now. You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
# configuration file /etc/nginx/uwsgi_params:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
I am having trouble establishing a websocket in my Flask web application.
On the client side, I am emitting a "ping" websocket event every second to the server. In the browser console, I see the following error each second
POST https://example.com/socket.io/?EIO=3&transport=polling&t=LOkVYzQ&sid=88b5202cf38f40879ddfc6ce36322233 400 (BAD REQUEST)
GET https://example.com/socket.io/?EIO=3&transport=polling&t=LOkVZLN&sid=5a355bbccb6f4f05bd46379066876955 400 (BAD REQUEST)
WebSocket connection to 'wss://example.com/socket.io/?EIO=3&transport=websocket&sid=5a355bbccb6f4f05bd46379066876955' failed: WebSocket is closed before the connection is established.
I have the following nginx.conf
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
upstream app_server {
# for UNIX domain socket setups
server unix:/pathtowebapp/gunicorn.sock fail_timeout=0;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
keepalive_timeout 5;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
charset utf-8;
client_max_body_size 30M;
location / {
try_files $uri #proxy_to_app;
}
location /socket.io {
proxy_pass http://app_server;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Upgrade websocket;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
proxy_buffering off;
proxy_headers_hash_max_size 1024;
}
location /static {
alias /pathtowebapp/webapp/static;
}
location #proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
#proxy_buffering off;
proxy_pass http://app_server;
}
}
I have been looking all over for examples of a websocket working with https using nginx in front of gunicorn.
My webpage loads, although the websocket connection is not successful.
The client side websocket is established using the following javascript:
var socket = io.connect('https://' + document.domain + ':' + location.port + namespace);
Here is my gunicorn.conf
import multiprocessing
bind = 'unix:/pathtowebapp/gunicorn.sock'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'eventlet'
[EDIT] if I configure nginx the way it is in the Flask-IO documentation and just run (env)$ python deploy_app.py then it works. But I was under the impression that this was not as production-ideal as the setup I previously mentioned
The problem is that you are running multiple workers on gunicorn. This is not a configuration that is currently supported, due to the very limited load balancer in gunicorn that does not support sticky sessions. Documentation reference: https://flask-socketio.readthedocs.io/en/latest/#gunicorn-web-server.
Instead, run several gunicorn instances, each with one worker, and then set up nginx to do the load balancing, using the ip_hash method so that sessions are sticky.
Also, in case you are not aware, if you run multiple servers you need to also run a message queue, so that the processes can coordinate. This is also covered in the documentation link above.
I have an app running on Server A. I have a Server B that I want to use as a reverse proxy for accessing the app on Server A. Server A is running nginx 1.4.5 and Django (Python 2.7.6 with fastcgi), Server B is running nginx 1.4.5 as well. I also want to add on SSL that way.
The proxy is kinda working. The issue is that requests don't see to get passed along correctly. When going to https://servera.org/ I only get a 404 error instead of the log-in page I'm expecting.
This is the error message I am seeing in the browser (it's an error message, so I know the request is reaching Server A):
Page not found (404)
Request Method: GET
Request URL: http://nova.iguw.tuwien.ac.at/index.html
Using the URLconf defined in TUBadges.urls, Django tried these URL patterns, in this order:
1. ^admin/doc/
2. ^admin/
3. ^$
4. ^badges/?$
5. ^badges/(?P<uid>\d+)/?$
6. ^presets/$
7. ^svg$
8. ^bgsvg$
9.
This is my config for the reverse proxy:
upstream server_a {
server servera.org:8080 fail_timeout=0;
}
server {
listen 443 ssl;
listen 80;
server_name subdomain1.serverb.org;
server_name subdomain2.serverb.org;
keepalive_timeout 70;
ssl_certificate /etc/certificates/server_b.pem;
ssl_certificate_key /etc/certificates/server_b.key;
error_log /var/log/nginx/aurora.ssl.error.log error;
access_log off;
client_max_body_size 50M;
location ~ ^/(.+)$ {
proxy_intercept_errors off;
proxy_buffering off;
proxy_connect_timeout 5;
proxy_send_timeout 5;
proxy_read_timeout 5;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-Fo $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_log off;
error_log /var/log/nginx/tubadges.error.log debug;
proxy_pass http://server_a;
proxy_redirect off;
}
}
And that is the config I'm using on Server A to run the app:
server {
listen 8080; ## listen for ipv4
server_name localhost;
server_name servera.org;
client_max_body_size 5M;
error_log /var/log/nginx/app1.error.log;
access_log /var/log/nginx/app1.access.log;
location /static {
root /srv/django/projects/app1;
}
location /media {
root /srv/django/projects/app1;
}
location / {
# host and port to fastcgi server
fastcgi_pass unix:/srv/django/run/app1.socket;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_pass_header Authorization;
fastcgi_intercept_errors off;
}
}
I'm assuming that it's got something to do with my Django App config or the config on Server A.
Can you spot an error? Do you need more information?
Is there, maybe, an answer on here that I have just missed?
Thanks in advance!
P.S.: This is my first time asking a question on StackOverflow, so if there's a way that I can improve my question to get better answers, and you see something that really bugs, please don't hesitate to point out to me how I can improve this question. :)
Actually, it looks like Nginx is correctly passing the request through - the error page comes from Django (so the request is being serviced by Apache) and it states that the url:
http://nova.iguw.tuwien.ac.at/index.html
cannot be resolved to any of the urls defined in TUBadges.urls
You need to hit a url such as http://nova.iguw.tuwien.ac.at/admin/login or something like that.