From da0a683f2f92f8369178ed0f7d940e28128093aa Mon Sep 17 00:00:00 2001 From: James Williams Date: Wed, 23 Sep 2020 12:24:59 +0100 Subject: [PATCH 1/7] configure traefik http security headers --- .gitignore | 1 + cookiecutter.json | 1 + .../compose/production/traefik/traefik.yml | 31 +++++++++++++++++++ .../config/settings/production.py | 21 ++++++++----- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index f753fec911..c4a37a436e 100644 --- a/.gitignore +++ b/.gitignore @@ -215,3 +215,4 @@ tags .idea/ .pytest_cache/ +.vscode/ diff --git a/cookiecutter.json b/cookiecutter.json index 4a580036d7..9829c0ca44 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,6 +1,7 @@ { "project_name": "My Awesome Project", "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_')|replace('.', '_')|trim() }}", + "_extensions": ["cookiecutter.extensions.RandomStringExtension"], "description": "Behold My Awesome Project!", "author_name": "Daniel Roy Greenfeld", "domain_name": "example.com", diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml index 7b56063f30..a1e4f6be37 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml @@ -1,3 +1,4 @@ +--- log: level: INFO @@ -30,6 +31,20 @@ certificatesResolvers: httpChallenge: entryPoint: web +tls: + # https://docs.traefik.io/https/tls/#tls-options + options: + default: + # see https://caniuse.com/tls1-2 for browsers that support TLS 1.2 + minVersion: VersionTLS12 + # see https://caniuse.com/sni for browsers that support SNI + sniStrict: true + # https://wiki.mozilla.org/Security/Server_Side_TLS + cipherSuites: # use only elliptic curve (the strongest) ciphers + - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + http: routers: web-secure-router: @@ -42,6 +57,7 @@ http: - web-secure middlewares: - csrf + - security-headers service: django tls: # https://docs.traefik.io/master/routing/routers/#certresolver @@ -52,6 +68,8 @@ http: rule: "Host(`{{ cookiecutter.domain_name }}`)" entryPoints: - flower + middlewares: + - security-headers service: flower tls: # https://docs.traefik.io/master/routing/routers/#certresolver @@ -64,6 +82,19 @@ http: # https://docs.djangoproject.com/en/dev/ref/csrf/#ajax headers: hostsProxyHeaders: ["X-CSRFToken"] + security-headers: + # https://docs.traefik.io/middlewares/headers/#general + headers: + # TODO increase stsSeconds to *at least* 31536000 (1 year) once HTTPS works + stsSeconds: 60 + stsIncludeSubdomains: true + stsPreload: true + frameDeny: true + contentTypeNosniff: true + browserXssFilter: true + customResponseHeaders: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server + server: {{ random_ascii_string(10) }} # hide server version services: django: diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index bd0acfbd47..485a419a95 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -43,17 +43,14 @@ # SECURITY # ------------------------------------------------------------------------------ -# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header -SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") +{%- if cookiecutter.use_docker -%} +# NOTE headers are managed by the security-headers middleware in traefik.yml +{%- else -%} +# TODO set security headers in your load balancer if possible and remove these # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True) -# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure -SESSION_COOKIE_SECURE = True -# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure -CSRF_COOKIE_SECURE = True -# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds -# TODO: set this to 60 seconds first and then to 518400 once you prove the former works +# TODO increase this to *at least* 31536000 (1 year) once HTTPS works SECURE_HSTS_SECONDS = 60 # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( @@ -65,6 +62,14 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool( "DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True ) +{%- endif -%} +# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure +SESSION_COOKIE_SECURE = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure +CSRF_COOKIE_SECURE = True {% if cookiecutter.cloud_provider != 'None' -%} # STORAGES From 837c869965cb9c08ecf0aba3569f3796305f224a Mon Sep 17 00:00:00 2001 From: James Williams Date: Wed, 23 Sep 2020 12:25:19 +0100 Subject: [PATCH 2/7] run traefik as non-root user --- .../compose/production/traefik/Dockerfile | 26 +++++++++++++++---- .../compose/production/traefik/traefik.yml | 4 +-- {{cookiecutter.project_slug}}/production.yml | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile index aa879052b7..e07f4b2d8a 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile @@ -1,5 +1,21 @@ -FROM traefik:v2.2.11 -RUN mkdir -p /etc/traefik/acme \ - && touch /etc/traefik/acme/acme.json \ - && chmod 600 /etc/traefik/acme/acme.json -COPY ./compose/production/traefik/traefik.yml /etc/traefik +FROM traefik:2.3 + +WORKDIR /etc/traefik/ + +RUN addgroup --system traefik \ + && adduser \ + --disabled-password \ + --gecos '' \ + --no-create-home \ + --ingroup traefik \ + traefik \ + && mkdir acme/ \ + && touch acme/acme.json \ + && chmod 0600 acme/acme.json \ + && chown -R traefik:traefik . + +EXPOSE 8080/tcp 8443/tcp + +VOLUME /etc/traefik/acme/ + +USER traefik diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml index a1e4f6be37..07552bb943 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml @@ -5,7 +5,7 @@ log: entryPoints: web: # http - address: ":80" + address: ":8080" http: # https://docs.traefik.io/routing/entrypoints/#entrypoint redirections: @@ -14,7 +14,7 @@ entryPoints: web-secure: # https - address: ":443" + address: ":8443" {%- if cookiecutter.use_celery == 'y' %} flower: diff --git a/{{cookiecutter.project_slug}}/production.yml b/{{cookiecutter.project_slug}}/production.yml index 93b61b1343..b981e5ee85 100644 --- a/{{cookiecutter.project_slug}}/production.yml +++ b/{{cookiecutter.project_slug}}/production.yml @@ -39,6 +39,7 @@ services: - django volumes: - production_traefik:/etc/traefik/acme:z + - ./compose/production/traefik/traefik.yml:/etc/traefik/traefik.yml:ro ports: - "0.0.0.0:80:80" - "0.0.0.0:443:443" From 6442fb57f9c56929910939d98a583f7fefcca6ed Mon Sep 17 00:00:00 2001 From: James Williams Date: Wed, 23 Sep 2020 18:06:47 +0100 Subject: [PATCH 3/7] fix production.py templating --- {{cookiecutter.project_slug}}/config/settings/production.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index 485a419a95..8c246206bc 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -43,9 +43,9 @@ # SECURITY # ------------------------------------------------------------------------------ -{%- if cookiecutter.use_docker -%} +{% if cookiecutter.use_docker -%} # NOTE headers are managed by the security-headers middleware in traefik.yml -{%- else -%} +{% else -%} # TODO set security headers in your load balancer if possible and remove these # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True) @@ -62,7 +62,7 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool( "DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True ) -{%- endif -%} +{% endif -%} # https://docs.djangoproject.com/en/dev/topics/security/#ssl-https # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From ff97a20014da4ec4790b0af17a2d72954742200e Mon Sep 17 00:00:00 2001 From: James Williams Date: Wed, 23 Sep 2020 20:37:00 +0100 Subject: [PATCH 4/7] add security documentation --- docs/index.rst | 1 + docs/security.rst | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 docs/security.rst diff --git a/docs/index.rst b/docs/index.rst index f62184643b..a1cb3a938a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ Contents: deployment-with-docker docker-postgres-backups websocket + security faq troubleshooting diff --git a/docs/security.rst b/docs/security.rst new file mode 100644 index 0000000000..8b05bb5673 --- /dev/null +++ b/docs/security.rst @@ -0,0 +1,52 @@ +Security +======== + +.. index:: Security, Configuration, TLS, SSL, Encrpytion, Compatibility + +Overview +-------- + +There is a reasonable level of security for most use cases configured as standard in cookiecutter-django, as much as is possible without knowing the features of the website you are building. That being said, your security requirements may differ greatly; before any deployment **you should ensure the security of your website is appropriate for your use case**. As a starting point, you should check any security settings used in cookiecutter-django and see if they are right for you, also actioning any TODOs. A good place to start is `config/settings/production.py`_, as well as `compose/production/traefik/traefik.yml`_ if you are using Docker. Naturally if you collect sensitive data, or have reason to believe your site is at all likely be targeted by hackers, a complete security review before allowing any user access is a must. + +.. _`config/settings/production.py`: https://github.com/pydanny/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/config/settings/production.py +.. _`compose/production/traefik/traefik.yml`: https://github.com/pydanny/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/compose/production/traefik/traefik.yml + + + +Security Checklist +--------------------------------- +This list is by no means comprehensive, but is a good starting point for those less well-versed in website security: + +- Run ``python manage.py check --deploy`` and ensure any issues listed are fixed (make sure you are checking the production settings by setting ``DJANGO_SETTINGS_MODULE=config.settings.production`` in the environment). Note: if you are using Docker, some settings flagged as missing by Django may be managed by Traefik's config. +- Test your SSL security `using SSL Labs`_, a full score may be overkill and will likely cause compatibility issues with older browsers, but try to action anything flagged in red. **Note**: full marks here does not necessarily make your website 'secure', this tool merely tests the strength of your TLS encryption settings. +- Check how your site stacks up against Mozilla's `Web Security Cheat Sheet`_. Again, completing everything listed here may be overkill but it is good as a reference. +- Consider creating a `Content Security Policy`_ for your site. In short, this is a HTTP header that instructs client browsers where they should load resources from (scripts, stylesheets etc.), and what web features should be enabled (inline HTML script and style tags, allowed domains for JavaScript AJAX fetches etc.). An ideal policy is to disable everything by default then just enable the features you use on your site. + +.. _`using SSL Labs`: https://www.ssllabs.com/ssltest/ +.. _`Web Security Cheat Sheet`: https://infosec.mozilla.org/guidelines/web_security.html#web-security-cheat-sheet +.. _`Content Security Policy`: https://content-security-policy.com/ + +Compatibility Issues +-------------------- +By default, cookiecutter-django uses security settings that may break compatibility with very old browsers. Before changing your settings to allow for these older browsers, consider asking your clients to update. There is a trade-off between security and compatibility, and weakening your security to support a minority of users can make things less safe for those who are up to date. + +Some settings of note when using Docker (in `compose/production/traefik/traefik.yml`_): + +- ``tls.options.default.minVersion: VersionTLS12``: `TLS 1.2 supported browsers`_ +- ``tls.options.default.sniStrict: true``: `SNI supported browsers`_ +- ``tls.options.default.cipherSuites``: only ECDHE ciphers are used, this will not work in old browsers. Internet Explorer <11 does not work and IE11 seems to only work on Windows 10. Edge is fine though, and is a better alternative for Windows users. + +.. _`TLS 1.2 supported browsers`: https://caniuse.com/tls1-2 +.. _`SNI supported browsers`: https://caniuse.com/sni + +Useful Links +------------ +- `Django Deployment Checklist`_ +- `SSL and TLS Deployment Best Practices`_ +- `Content Security Policy (CSP) Quick Reference Guide`_ +- `Mozilla Web Security Cheat Sheet`_ + +.. _`Django Deployment Checklist`: https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ +.. _`SSL and TLS Deployment Best Practices`: https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices +.. _`Content Security Policy (CSP) Quick Reference Guide`: https://content-security-policy.com/ +.. _`Mozilla Web Security Cheat Sheet`: https://infosec.mozilla.org/guidelines/web_security.html#web-security-cheat-sheet From 0c7cc387ba20fe40a8b13d1ff1e3f1e576ddbd53 Mon Sep 17 00:00:00 2001 From: James Williams Date: Sat, 28 Nov 2020 22:18:23 +0000 Subject: [PATCH 5/7] change django 3.1 docs to dev --- docs/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security.rst b/docs/security.rst index 8b05bb5673..f7d0bb7771 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -46,7 +46,7 @@ Useful Links - `Content Security Policy (CSP) Quick Reference Guide`_ - `Mozilla Web Security Cheat Sheet`_ -.. _`Django Deployment Checklist`: https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ +.. _`Django Deployment Checklist`: https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ .. _`SSL and TLS Deployment Best Practices`: https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices .. _`Content Security Policy (CSP) Quick Reference Guide`: https://content-security-policy.com/ .. _`Mozilla Web Security Cheat Sheet`: https://infosec.mozilla.org/guidelines/web_security.html#web-security-cheat-sheet From c9d67c38e59056f39c0c0199902b9af431ef05ab Mon Sep 17 00:00:00 2001 From: James Williams Date: Sat, 28 Nov 2020 22:23:51 +0000 Subject: [PATCH 6/7] add commented out security settings if using docker --- .../config/settings/production.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index 8c246206bc..453976ad2b 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -45,6 +45,22 @@ # ------------------------------------------------------------------------------ {% if cookiecutter.use_docker -%} # NOTE headers are managed by the security-headers middleware in traefik.yml +# Uncomment the following if you are not using Traefik +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect +# SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True) +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds +# TODO increase this to *at least* 31536000 (1 year) once HTTPS works +# SECURE_HSTS_SECONDS = 60 +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains +# SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( +# "DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True +# ) +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload +# SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True) +# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff +# SECURE_CONTENT_TYPE_NOSNIFF = env.bool( +# "DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True +# ) {% else -%} # TODO set security headers in your load balancer if possible and remove these # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect From 88ce1351a281d28ab5e3ecf28d0f76fd0a153c69 Mon Sep 17 00:00:00 2001 From: James Williams Date: Sat, 3 Apr 2021 16:48:56 +0100 Subject: [PATCH 7/7] bump traefik to v2.4 --- .../compose/production/traefik/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile index e07f4b2d8a..803a4e284b 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile @@ -1,4 +1,4 @@ -FROM traefik:2.3 +FROM traefik:2.4 WORKDIR /etc/traefik/