Deploy SwampDragon with Nginx, Supervisor and Django - python

I have deployed my application in Linode. But for testing SD I used a pc as server in my local network where I have running my application. I'd like to share my Nginx and Supervisor config if you could give me some advice if it is right or I've made a mistake.
I modified my hosts file to access with www.mydomain.com.
In my settings.py file I have:
# SWAMPDRAGON
SWAMP_DRAGON_CONNECTION = ('swampdragon_auth.socketconnection.HttpDataConnection', '/data')
DRAGON_URL = 'http://127.0.0.1:9999'
I took as example what I did for celery and reuse that to do something similar with SD:
I created a command file: 'swampdragon_start'
#!bin/bash
NAME="swampdragon"
DJANGODIR=/opt/virtualenvs/myproject/pysrc/myproject/myproject
# Enable virtualenv
cd $DJANGODIR
source ../../../bin/activate
export C_FORCE_ROOT="true"
# Execute SD
exec python manage.py runsd 0.0.0.0:9999
Then I created a file for supervisor: 'swampdragon.conf'
[program:swampdragon]
command = /opt/virtualenvs/myproject/bin/swampdragon_start
user = root
stdout = /path/to/file.log
stderr = /path/to/file.log
autostart = true
autorestart = true
Finally, I made swampdragon_start executable, and add swampdragon to supervisor.
My Nginx config is as follow:
upstream swampdragon{
server 127.0.0.1:9999;
}
proxy_next_upstream off;
server {
listen 80;
server_name www.mydomain.com;
access_log /path/to/log
error_log /path/to/log
location /media/ {
alias /path/to/media
}
location /static/ {
alias /path/to/statics
}
location / {
... # django config
}
}
server {
# this is taken from http://swampdragon.net/blog/deploying-swampdragon/
listen 80;
server_name sd.mydomain.com;
# Websocket
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_pass http://swampdragon;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
That's how I've made it work, but I think Nginx upstream is not working really.
I am sure there's a better way to deploy SD, any suggestion?
Thanks for your time.

Related

How do I get the client IP address of a websocket connection in Django Channels?

I need to get the client IP address of a websocket connection for some extra functionality I would like to implement. I have an existing deployed Django server running an Nginx-Gunicorn-Uvicorn Worker-Redis configuration. As one might expect, during development, whilst running a local server, everything works as expected. However, when deployed, I receive the error NoneType object is not subscriptable when attempting to access the client IP address of the websocket via self.scope["client"][0].
Here are the configurations and code:
NGINX Config:
upstream uvicorn {
server unix:/run/gunicorn.sock;
}
server {
listen 80;
server_name <ip address> <hostname>;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://uvicorn;
proxy_headers_hash_max_size 512;
proxy_headers_hash_bucket_size 128;
}
location /ws/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass http://uvicorn;
}
location /static/ {
root /var/www/serverfiles/;
autoindex off;
}
location /media {
alias /mnt/apps;
}
}
Gunicorn Config:
NOTE: ExecStart has been formatted for readability, it is one line in the actual config
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=django
Group=www-data
WorkingDirectory=/srv/server
Environment=DJANGO_SECRET_KEY=
Environment=GITEA_SECRET_KEY=
Environment=MSSQL_DATABASE_PASSWORD=
ExecStart=/bin/bash -c "
source venv/bin/activate;
exec /srv/server/venv/bin/gunicorn
--workers 3
--bind unix:/run/gunicorn.sock
--timeout 300
--error-logfile /var/log/gunicorn/error.log
--access-logfile /var/log/gunicorn/access.log
--log-level debug
--capture-output
-k uvicorn.workers.UvicornWorker
src.server.asgi:application
"
[Install]
WantedBy=multi-user.target
Code throwing the error:
#database_sync_to_async
def _set_online_if_model(self, set_online: bool) -> None:
model: MyModel
for model in MyModel.objects.all():
if self.scope["client"][0] == model.ip:
model.online = set_online
model.save()
This server has been running phenomenally in its current configuration before my need to access connect client IP addresses. It handles other websocket connections just fine without any issues.
I've already looked into trying to configure my own custom UvicornWorker according to the docs. I'm not at all an expert in this, so I might have misunderstood what I was supposed to do: https://www.uvicorn.org/deployment/#running-behind-nginx
from uvicorn.workers import UvicornWorker
class ServerUvicornWorker(UvicornWorker):
def __init__(self, *args, **kwargs) -> None:
self.CONFIG_KWARGS.update({"proxy_headers": True, "forwarded_allow_ips": "*"})
super().__init__(*args, **kwargs)
I also looked at https://github.com/django/channels/issues/546 which mentioned a --proxy-headers config for Daphne, however, I am not running Daphne. https://github.com/django/channels/issues/385 mentioned that HTTP headers are passed to the connect method of a consumer, however, that post is quite old and no longer relavent as far as I can tell. I do not get any additional **kwargs to my connect method.
Client IP has nothing to do with channels
self.scope["client"][0] is undefined because when you receive data from the front end at the backend there is no data with the name client. so try to send it from the frontend. you can send a manual, static value at first to verify and then find techniques to read the IP address and then send it.

How to redirect HTTP to HTTPS in default.conf file with Nginx dockerized

I have a Django project already using AWS SSL certificate from the Certificate Manager service. My application is accessible via HTTPS, however, it isn't redirecting automatically when accessing via HTTP.
My Nginx default.conf file before redirect (works like a charm!):
upstream django {
server my_app:8000;
}
server {
location / {
proxy_pass http://django;
}
}
After setting up the redirect:
upstream django {
server my_app:8000;
}
server {
listen 80;
if ($http_x_forwarded_proto = 'http'){
return 301 https://$host$request_uri;
}
location / {
proxy_pass http://django;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
And here is my Django settings.py for this:
.
.
.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 340505040
SECURE_SSL_REDIRECT = True
.
.
.
Then I'm getting http 400 (this is the Load Balancer Health Checker):
Edit 1
With this new setup, I'm getting http 301:
upstream django {
server my_app:8000;
}
server {
listen 80;
location / {
proxy_pass http://django/;
if ($http_x_forwarded_proto != 'https') {
rewrite ^ https://$host$request_uri? permanent;
}
}
}
I've been looking around and didn't find any example that helps me. What can I try next?
On NGINX config put all the sites on SSL only
site on SSL
nginx/sites-available/sitex only listens to port 443
server {
# SSL configuration
#
listen 443 ssl ;
listen [::]:443 ssl ;
ssl_certificate /etc/letsencrypt/live/www.sitex.nl/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.sitex.cops.nl/privkey.pem; # managed by Certbot
server_name www.sitex.com; # managed by Certbot
access_log /var/log/nginx/sitex_access.log;
error_log /var/log/nginx/sitex_error.log;
location / {
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-Proto $scheme;
add_header Access-Control-Allow-Origin *;
proxy_pass http://127.0.0.1:8004;
}
}
All SSL/TLS requests to www.sitex.com are forwarded to localhost:8004.
And the SiteX Docker Image is picking up on that port.
nginx.conf
In the nginx.conf file the Virtual Hosts section is as follows
##
# Virtual Host Configs
##
include /etc/nginx/all_http_to_https.conf;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
all_http_to_https.conf
This file does the trick
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}

Nginx config does not work for two subdomains

I'm trying to get the following setup to work with gunicorn and nginx. Everything works until I add the second server config...
upstream app_server_djangoapp {
server localhost:8002 fail_timeout=0;
}
server {
listen 80;
server_name api.domain.tld;
access_log /var/log/nginx/guni-access.log;
error_log /var/log/nginx/guni-error.log info;
keepalive_timeout 5;
# Size in megabytes to allow for uploads.
client_max_body_size 20M;
# path for static files
root /home/username/webapps/guni/static;
location /docs/ {
autoindex on;
alias /srv/site/docs/buildHTML/html/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://app_server_djangoapp;
break;
}
}
}
server {
listen 80;
server_name flower.domain.tld;
location / {
proxy_pass http://localhost:5555;
}
What I'm I doing wrong? I need to have two subdomains one mapped to my django app and other mapped to my monitoring software on 5555 (flower)
log files states:
2014/11/21 12:03:27 [emerg] 962#0: unexpected end of file, expecting
"}" in /etc/nginx/sites-enabled/default:47
Your code is missing a closing "}" at the very end:
server {
listen 80;
server_name flower.domain.tld;
location / {
proxy_pass http://localhost:5555;
}
}
For future reference:
You can run nginx -t (with sudo if needed) to test the configuration before reloading nginx - this will give you a quite good description of any errors you might have in your configuration file(s).

Removing the port number from URL

I'm new to anything related to servers and am trying to deploy a django application. Today I bought a domain name for the app and am having trouble configuring it so that the base URL does not need the port number at the end of it. I have to type www.trackthecharts.com:8001 to see the website when I only want to use www.trackethecharts.com. I think the problem is somewhere in my nginx, gunicorn or supervisor configuration.
gunicorn_config.py
command = '/opt/myenv/bin/gunicorn'
pythonpath = '/opt/myenv/top-chart-app/'
bind = '162.243.76.202:8001'
workers = 3
root#django-app:~#
nginx config
server {
server_name 162.243.76.202;
access_log off;
location /static/ {
alias /opt/myenv/static/;
}
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
}
}
supervisor config
[program:top_chart_gunicorn]
command=/opt/myenv/bin/gunicorn -c /opt/myenv/gunicorn_config.py djangoTopChartApp.wsgi
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor_gunicorn.err.log
stdout_logfile=/var/log/supervisor_gunicorn.out.log
Thanks for taking a look.
You should bind to port 80, the default http port. Then make sure in /etc/nginx/sites-enabled/, your are listening on port 80.
By binding to port 80, you will not need to explicitly specify one in your url.

Django SSL Redirect snippet modification, not working as expected

I'm using Nginx as webserver, with a reverse proxy to a gunicorn django server.
I tried using the SSLRedirect snippet from here:
http://djangosnippets.org/snippets/85/
Because this snippet would always return false from is_secure() with my setup, resulting in a redirect loop, I had to make some changes.
SSL works, but when I access http://domain.net/main it doesn't redirect to https://domain.net/main. Isn't it supposed to do that?
Below outlines the modification I made:
if 'HTTP_X_FORWARDED_PROTOCOL' in request.META:
return True
And in my nginx conf (I only need SSL, http not required):
server {
listen 8888;
server_name domain.net;
ssl on;
ssl_certificate /path/to/domain.pem;
ssl_certificate_key /path/to/domain.key;
# serve directly - analogous for static/staticfiles
location /media/ {
root /path/to/root;
}
location /static/ {
root /path/to/root;
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 10;
proxy_pass http://127.0.0.1:8881/;
# note this line
proxy_set_header X-Forwarded-Protocol https;
}
}
Just do it entirely with nginx. No need to involve Django at all:
server {
listen 80;
rewrite ^(.*) https://$host$1 permanent;
}
server {
listen 443;
# The rest of your original server config here
}

Categories

Resources