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 chmod 755 -R /home/appuser
adduser dbuser
Continue logged in as the newly created appuser.
sudo apt update
sudo apt upgrade
sudo add-apt-repository universe
sudo add-apt-repository restricted
sudo add-apt-repository multiverse
sudo apt install python3-pip python3-dev libpq-dev python3-virtualenv
sudo apt install postgresql postgresql-contrib
sudo apt install nginx
sudo su - postgres
createdb devdb
createuser -P dbuser
psql
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/14/main/pg_hba.conf
local all postgres peer
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
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
pip install --upgrade pip
pip install virtualenv
mkdir ~/devsite
cd ~/devsite
python3 -V
# python3.8 -m venv env
virtualenv env
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 psycopg2-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
LANGUAGE_CODE = 'sv'
TIME_ZONE = 'Europe/Stockholm'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'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'
source env/bin/activate
python manage.py check --deploy
python manage.py migrate
python manage.py collectstatic
python manage.py check --deploy
deactivate
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 daemon
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 \
devsite.wsgi:application
[Install]
WantedBy=multi-user.target
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
sudo systemctl status gunicorn.socket
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 {
listen 80;
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 / {
#include proxy_params;
proxy_pass_header Server;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
sudo ln -s /etc/nginx/sites-available/devsite /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl restart gunicorn.socket gunicorn.service
sudo systemctl daemon-reload
HTTPS: SSL with lets-encrypt
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
source env/bin/activate
python manage.py check --deploy
python manage.py migrate
python manage.py collectstatic
python manage.py check --deploy
deactivate
sudo systemctl restart gunicorn.socket gunicorn.service
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl daemon-reload