Commands to setup a secure django website on ubuntu
TLDR: How to setup a secure django website on ubuntu.
Add limited user accounts
[9]: Linode - set up and secure / #add-a-limited-user-account
When logged in as root add an appuser and a dbuser.
adduser appuser
adduser appuser sudo
sudo chown appuser:appuser -R /home/appuser/
Continue logged in as the newly created appuser.
sudo apt update
sudo apt upgrade
sudo apt install python3-pip python3-dev python3-venv
sudo apt install postgresql postgresql-contrib libpq-dev
sudo apt install nginx
sudo su - postgres
createdb devdb
createuser -P dbuser
psql devdb
ALTER ROLE dbuser SET client_encoding TO 'utf8';
ALTER ROLE dbuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE dbuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE devdb TO dbuser;
GRANT ALL PRIVILEGES ON SCHEMA public TO dbuser;
\q
exit
Security check.
For maximum security, ... this is an important security consideration.
sudo nano /etc/postgresql/18/main/pg_hba.conf
local all postgres peer
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
To access PostgreSQL from a remote location, consider using SSH to connect to the database machine and then using a local connection to the database from there.
Virtual python environment
[3]: How to run django with mod_wsgi and apache with a virtualenv python environment on a debian vps
mkdir ~/devsite
cd ~/devsite
python3 -V
python3 -m venv env
source env/bin/activate
pip install --upgrade pip
Install and configure django
[1]: How to install and configure django with postgres, nginx and gunicorn
cd ~/devsite
source env/bin/activate
pip install django gunicorn uvicorn[standard] uvicorn_worker psycopg[binary]
django-admin startproject devsite .
deactivate
nano ~/devsite/devsite/settings.py
SECRET_KEY = ''
with open('/etc/secret_key.txt') as f:
SECRET_KEY = f.read().strip()
with open('/etc/database_name.txt') as f:
db_name = f.read().strip()
with open('/etc/database_user.txt') as f:
db_user = f.read().strip()
with open('/etc/database_password.txt') as f:
db_password = f.read().strip()
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': db_name,
'USER': db_user,
'PASSWORD': db_password,
'HOST': 'localhost',
'PORT': '', # Set to empty string for default.
}
}
sudo nano /etc/secret_key.txt
sudo nano /etc/database_name.txt
sudo nano /etc/database_user.txt
sudo nano /etc/database_password.txt
nano ~/devsite/devsite/settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media/'
Creating systemd Socket and Service Files for Gunicorn
[6]: How to set up django with postgres, nginx and gunicorn on ubuntu 20.04
sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn ASGI service for Django
Requires=gunicorn.socket
After=network.target
[Service]
User=appuser
Group=appuser
WorkingDirectory=/home/appuser/devsite
ExecStart=/home/appuser/devsite/env/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
-k uvicorn_worker.UvicornWorker \
devsite.asgi:application
[Install]
WantedBy=multi-user.target
sudo systemctl start gunicorn.socket
sudo systemctl start gunicorn.service
sudo systemctl enable gunicorn.socket
sudo systemctl status gunicorn.socket
sudo systemctl status gunicorn.service
Configure Nginx to Proxy Pass to Gunicorn
[6]: How to setup django with postgres nginx and gunicorn on ubuntu 20.04
sudo nano /etc/nginx/sites-available/devsite
server {
server_name devsite.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
alias /home/appuser/devsite/static/;
add_header Cache-Control "no-cache";
}
location /media/ {
alias /home/appuser/devsite/media/;
add_header Cache-Control "no-cache";
}
location /robots.txt {
alias /home/appuser/devsite/robots.txt;
}
location / {
proxy_pass http://unix:/run/gunicorn.sock;
proxy_set_header Host $http_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;
proxy_set_header X-Forwarded-Port $server_port;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
HTTPS: SSL with lets-encrypt
sudo ln -s /etc/nginx/sites-available/devsite /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl daemon-reload
sudo apt-get update
sudo apt-get remove certbot
sudo apt-get update
sudo snap install --classic certbot
sudo certbot --nginx -d devsite.com -d www.devsite.com
sudo certbot renew --dry-run
nano ~/devsite/devsite/settings.py
debug = False
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# SECURE_SSL_REDIRECT = True ?
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
# SECURE_HSTS_SECONDS = 60 ?
SECURE_BROWSER_XSS_FILTER = True
Final checking
source env/bin/activate
python manage.py check --deploy
python manage.py migrate
python manage.py collectstatic
deactivate
sudo systemctl restart gunicorn.socket gunicorn.service nginx
sudo systemctl status gunicorn.socket
sudo systemctl status gunicorn.service