Skip to content

Instantly share code, notes, and snippets.

@peteristhegreat
Last active January 23, 2022 05:27
Show Gist options
  • Save peteristhegreat/9720be9f14c0125cc3adced5d61ca838 to your computer and use it in GitHub Desktop.
Save peteristhegreat/9720be9f14c0125cc3adced5d61ca838 to your computer and use it in GitHub Desktop.
SSL TLS Gunicorn Flask Lets Encrypt Nginx Cron Docker

Production Flask Server SSL

Lots of little steps for making a nice secure site without paying for a dedicated load balancer, or pricey certificates.

Step Zero, buy a domain, or get a free subdomain somewhere

namescheap.com

Setting up Let's Encrypt

https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https

Diffie-Hellman dhparam.pem

Run this on a beefy machine and it should take < 1 minute. Run it on a single cpu and it may take a while. Copy the result onto your destination hardware.

openssl dhparam -out /path/to/dhparam.pem 4096

then in nginx.conf file put

ssl_dhparam /path/to/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

ProxyFix

https://flask.palletsprojects.com/en/2.0.x/deploying/wsgi-standalone/

Under the server in nginx.conf

    location / {
        proxy_pass         http://127.0.0.1:8000/;
        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_set_header   X-Forwarded-Proto    $scheme;
    }

Add a proxy fix in python

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

Setting up nginx + let's encrypt + cron

https://uobis.com/blog/ssltls-certificates-flask-application-lets-encrypt-nginx/

cron example

$ sudo crontab –e

30 2 * * 1 /opt/letsencrypt/letsencrypt-auto renew >> /var/log/le-renew.log
35 2 * * 1 /etc/init.d/nginx reload

Setting up nginx + gunicorn + let's encrypt

https://serverfault.com/questions/746297/how-to-run-gunicorn-upstream-with-an-nginx-ssl-configuration

Make Nginx log files easier to look at

https://serverfault.com/questions/643254/how-to-share-nginx-logs-without-sudo-to-another-user

chmod 755 /var/log/nginx && chmod 644 /var/log/nginx/*.log && chmod 644 /var/log/nginx/*.gz

TODO try

$ sudo apt install python-certbot-nginx
$ sudo certbot --nginx
# install with
# ln -s /etc/nginx/conf.d/mywebsite.conf /path/to/nginx.conf
# mostly copied from https://serverfault.com/a/746403
set $domainname example.com # yourdomain.here.com
set $localhost 127.0.0.1 # website
upstream website {
ip_hash; # for sticky sessions, more below
server $localhost:8000 max_fails=1 fail_timeout=10s;
}
server {
# only listen to https here
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name $domainname;
access_log /var/log/nginx/$domainname.access.log;
error_log /var/log/nginx/$domainname.error.log;
ssl on;
ssl_certificate /etc/letsencrypt/live/$domainname/fullchain.pem; # ca-cert.chained.crt
ssl_certificate_key /etc/letsencrypt/live/$domainname/privkey.pem; # cert.key
ssl_session_cache shared:SSL:5m;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/certs/dhparams.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_buffer_size 8k;
location / {
proxy_pass http://127.0.0.1:8000/;
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_set_header X-Forwarded-Proto $scheme;
# proxy_pass http://127.0.0.1:8000;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_http_version 1.1;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto http;
# proxy_redirect http:// $scheme://;
}
}
# redirect http to https here
server {
listen 80;
listen [::]:80;
server_name $domainname;
return 301 https://$server_name/;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment