From 8801c50867d86f6b60bed4b14a242c2fb7ffecc0 Mon Sep 17 00:00:00 2001 From: Jannis Gebauer Date: Fri, 11 Aug 2017 11:16:15 +0200 Subject: [PATCH] switch to caddy (#1282) --- README.rst | 1 - cookiecutter.json | 1 - docs/project-generation-options.rst | 3 - hooks/post_gen_project.py | 46 ++------- .../compose/caddy/Caddyfile | 14 +++ .../compose/caddy/Dockerfile | 2 + .../compose/nginx/Dockerfile | 12 --- .../compose/nginx/dhparams.example.pem | 3 - .../compose/nginx/nginx-secure.conf | 98 ------------------- .../compose/nginx/nginx.conf | 61 ------------ .../compose/nginx/start.sh | 62 ------------ {{cookiecutter.project_slug}}/env.example | 3 + {{cookiecutter.project_slug}}/production.yml | 29 +----- 13 files changed, 34 insertions(+), 301 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/compose/caddy/Caddyfile create mode 100644 {{cookiecutter.project_slug}}/compose/caddy/Dockerfile delete mode 100644 {{cookiecutter.project_slug}}/compose/nginx/Dockerfile delete mode 100755 {{cookiecutter.project_slug}}/compose/nginx/dhparams.example.pem delete mode 100755 {{cookiecutter.project_slug}}/compose/nginx/nginx-secure.conf delete mode 100644 {{cookiecutter.project_slug}}/compose/nginx/nginx.conf delete mode 100755 {{cookiecutter.project_slug}}/compose/nginx/start.sh diff --git a/README.rst b/README.rst index 9f2af849..3345bfd6 100644 --- a/README.rst +++ b/README.rst @@ -171,7 +171,6 @@ Answer the prompts with your own desired options_. For example:: 2 - Grunt 3 - None Choose from 1, 2, 3, 4 [1]: 1 - use_lets_encrypt [n]: n Select open_source_license: 1 - MIT 2 - BSD diff --git a/cookiecutter.json b/cookiecutter.json index 5b443493..8addeead 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -20,7 +20,6 @@ "use_compressor": "n", "postgresql_version": ["9.6", "9.5", "9.4", "9.3", "9.2"], "js_task_runner": ["Gulp", "Grunt", "None"], - "use_lets_encrypt": "n", "custom_bootstrap_compilation": "n", "open_source_license": ["MIT", "BSD", "GPLv3", "Apache Software License 2.0", "Not open source"] } diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index 8db3077d..5a0cffe6 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -72,9 +72,6 @@ js_task_runner [1] 2. Grunt_ 3. None -use_lets_encrypt [n] - Use `Let's Encrypt`_ as the certificate authority for this project. - custom_bootstrap_compilation [n] If you use Grunt, scaffold out recompiling Bootstrap as as task. (Useful for letting you change Bootstrap variables in real time.) Consult project README for more details. diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 1dc11279..b383e419 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -167,14 +167,6 @@ def remove_packageJSON_file(): PROJECT_DIRECTORY, filename )) -def remove_certbot_files(): - """ - Removes files needed for certbot if it isn't going to be used - """ - nginx_dir_location = os.path.join(PROJECT_DIRECTORY, 'compose/nginx') - for filename in ["nginx-secure.conf", "start.sh", "dhparams.example.pem"]: - file_name = os.path.join(nginx_dir_location, filename) - remove_file(file_name) def remove_copying_files(): """ @@ -229,26 +221,26 @@ def remove_open_source_files(): # dst = os.path.join(target_dir, name) # shutil.copyfile(src, dst) -# 1. Generates and saves random secret key +# Generates and saves random secret key make_secret_key(PROJECT_DIRECTORY) -# 2. Removes the taskapp if celery isn't going to be used +# Removes the taskapp if celery isn't going to be used if '{{ cookiecutter.use_celery }}'.lower() == 'n': remove_task_app(PROJECT_DIRECTORY) -# 3. Removes the .idea directory if PyCharm isn't going to be used +# Removes the .idea directory if PyCharm isn't going to be used if '{{ cookiecutter.use_pycharm }}'.lower() != 'y': remove_pycharm_dir(PROJECT_DIRECTORY) -# 4. Removes all heroku files if it isn't going to be used +# Removes all heroku files if it isn't going to be used if '{{ cookiecutter.use_heroku }}'.lower() != 'y': remove_heroku_files() -# 5. Removes all docker files if it isn't going to be used +# Removes all docker files if it isn't going to be used if '{{ cookiecutter.use_docker }}'.lower() != 'y': remove_docker_files() -# 6. Removes all JS task manager files if it isn't going to be used +# Removes all JS task manager files if it isn't going to be used if '{{ cookiecutter.js_task_runner}}'.lower() == 'gulp': remove_grunt_files() elif '{{ cookiecutter.js_task_runner}}'.lower() == 'grunt': @@ -258,11 +250,7 @@ else: remove_grunt_files() remove_packageJSON_file() -# 7. Removes all certbot/letsencrypt files if it isn't going to be used -if '{{ cookiecutter.use_lets_encrypt }}'.lower() != 'y': - remove_certbot_files() - -# 8. Display a warning if use_docker and use_grunt are selected. Grunt isn't +# Display a warning if use_docker and use_grunt are selected. Grunt isn't # supported by our docker config atm. if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cookiecutter.use_docker }}'.lower() == 'y': print( @@ -271,29 +259,15 @@ if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cook "js task runner service to your docker configuration manually." ) -# 9. Removes the certbot/letsencrypt files and display a warning if use_lets_encrypt is selected and use_docker isn't. -if '{{ cookiecutter.use_lets_encrypt }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() != 'y': - remove_certbot_files() - print( - "You selected to use Let's Encrypt and didn't select to use docker. This is NOT supported out of the box for now. You " - "can continue to use the project like you normally would, but Let's Encrypt files have been included." - ) -# 10. Directs the user to the documentation if certbot and docker are selected. -if '{{ cookiecutter.use_lets_encrypt }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y': - print( - "You selected to use Let's Encrypt, please see the documentation for instructions on how to use this in production. " - "You must generate a dhparams.pem file before running docker-compose in a production environment." - ) - -# 11. Removes files needed for the GPLv3 licence if it isn't going to be used. +# Removes files needed for the GPLv3 licence if it isn't going to be used. if '{{ cookiecutter.open_source_license}}' != 'GPLv3': remove_copying_files() -# 12. Remove Elastic Beanstalk files +# Remove Elastic Beanstalk files if '{{ cookiecutter.use_elasticbeanstalk_experimental }}'.lower() != 'y': remove_elasticbeanstalk() -# 13. Remove files conventional to opensource projects only. +# Remove files conventional to opensource projects only. if '{{ cookiecutter.open_source_license }}' == 'Not open source': remove_open_source_files() diff --git a/{{cookiecutter.project_slug}}/compose/caddy/Caddyfile b/{{cookiecutter.project_slug}}/compose/caddy/Caddyfile new file mode 100644 index 00000000..22ec73f4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/caddy/Caddyfile @@ -0,0 +1,14 @@ +www.{% raw %}{%DOMAIN_NAME%}{% endraw %} { + redir https://{{cookiecutter.domain_name}} +} + +{% raw %}{%DOMAIN_NAME%}{% endraw %} { + proxy / django:5000 { + header_upstream Host {host} + header_upstream X-Real-IP {remote} + header_upstream X-Forwarded-Proto {scheme} + } + log stdout + errors stdout + gzip +} diff --git a/{{cookiecutter.project_slug}}/compose/caddy/Dockerfile b/{{cookiecutter.project_slug}}/compose/caddy/Dockerfile new file mode 100644 index 00000000..774b0bf5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/caddy/Dockerfile @@ -0,0 +1,2 @@ +FROM abiosoft/caddy:0.10.6 +COPY Caddyfile /etc/Caddyfile diff --git a/{{cookiecutter.project_slug}}/compose/nginx/Dockerfile b/{{cookiecutter.project_slug}}/compose/nginx/Dockerfile deleted file mode 100644 index 25b48cd9..00000000 --- a/{{cookiecutter.project_slug}}/compose/nginx/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM nginx:latest -ADD nginx.conf /etc/nginx/nginx.conf - -{% if cookiecutter.use_lets_encrypt == 'y' and cookiecutter.use_docker == 'y' %} -# installs the `ps` command in the nginx image -RUN apt-get update && apt-get install -y procps - -ADD start.sh /start.sh -ADD nginx-secure.conf /etc/nginx/nginx-secure.conf -ADD dhparams.pem /etc/ssl/private/dhparams.pem -CMD /start.sh -{% endif %} diff --git a/{{cookiecutter.project_slug}}/compose/nginx/dhparams.example.pem b/{{cookiecutter.project_slug}}/compose/nginx/dhparams.example.pem deleted file mode 100755 index e88d8123..00000000 --- a/{{cookiecutter.project_slug}}/compose/nginx/dhparams.example.pem +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN DH PARAMETERS----- -EXAMPLE_FILE ------END DH PARAMETERS----- diff --git a/{{cookiecutter.project_slug}}/compose/nginx/nginx-secure.conf b/{{cookiecutter.project_slug}}/compose/nginx/nginx-secure.conf deleted file mode 100755 index c3f46b84..00000000 --- a/{{cookiecutter.project_slug}}/compose/nginx/nginx-secure.conf +++ /dev/null @@ -1,98 +0,0 @@ -user nginx; -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - - -events { - worker_connections 1024; -} - -http { - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - #tcp_nopush on; - - keepalive_timeout 65; - - proxy_headers_hash_bucket_size 52; - - gzip on; - - upstream app { - server django:5000; - } - server { - listen 80; - server_name ___my.example.com___ www.___my.example.com___; - - location /.well-known/acme-challenge { - # Since the certbot container isn't up constantly, need to resolve ip dynamically using docker's dns - resolver ___NAMESERVER___; - set $certbot_addr_port certbot:80; - proxy_pass http://$certbot_addr_port; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location / { - return 301 https://$server_name$request_uri; - } - - } - - server { - listen 443; - server_name ___my.example.com___ www.___my.example.com___; - - ssl on; - ssl_certificate /etc/letsencrypt/live/___my.example.com___/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/___my.example.com___/privkey.pem; - ssl_session_timeout 5m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; - ssl_prefer_server_ciphers on; - - ssl_session_cache shared:SSL:10m; - ssl_dhparam /etc/ssl/private/dhparams.pem; - - location /.well-known/acme-challenge { - resolver ___NAMESERVER___; - set $certbot_addr_port certbot:443; - proxy_pass http://$certbot_addr_port; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto https; - } - - location / { - # checks for static file, if not found proxy to app - try_files $uri @proxy_to_app; - } - - # cookiecutter-django app - location @proxy_to_app { - proxy_set_header X-Forwarded-Proto https; - proxy_set_header X-Url-Scheme $scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_redirect off; - - proxy_pass http://app; - - } - - } - -} diff --git a/{{cookiecutter.project_slug}}/compose/nginx/nginx.conf b/{{cookiecutter.project_slug}}/compose/nginx/nginx.conf deleted file mode 100644 index 9573effe..00000000 --- a/{{cookiecutter.project_slug}}/compose/nginx/nginx.conf +++ /dev/null @@ -1,61 +0,0 @@ -user nginx; -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - #tcp_nopush on; - - keepalive_timeout 65; - - #gzip on; - - upstream app { - server django:5000; - } - - server { - listen 80; - charset utf-8; - - {% if cookiecutter.use_lets_encrypt == 'y' and cookiecutter.use_docker == 'y' %} - server_name ___my.example.com___ ; - - location /.well-known/acme-challenge { - proxy_pass http://certbot:80; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto https; - } - {% endif %} - - location / { - # checks for static file, if not found proxy to app - try_files $uri @proxy_to_app; - } - - # cookiecutter-django app - location @proxy_to_app { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_redirect off; - proxy_pass http://app; - - } - } -} diff --git a/{{cookiecutter.project_slug}}/compose/nginx/start.sh b/{{cookiecutter.project_slug}}/compose/nginx/start.sh deleted file mode 100755 index fa40ed9a..00000000 --- a/{{cookiecutter.project_slug}}/compose/nginx/start.sh +++ /dev/null @@ -1,62 +0,0 @@ -echo sleep 5 -sleep 5 - -echo build starting nginx config - - -echo replacing ___my.example.com___/$MY_DOMAIN_NAME - -# Put your domain name into the nginx reverse proxy config. -sed -i "s/___my.example.com___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf - -cat /etc/nginx/nginx.conf -echo . -echo Firing up nginx in the background. -nginx - -# # Check user has specified domain name -if [ -z "$MY_DOMAIN_NAME" ]; then - echo "Need to set MY_DOMAIN_NAME (to a letsencrypt-registered name)." - exit 1 -fi - -# This bit waits until the letsencrypt container has done its thing. -# We see the changes here bceause there's a docker volume mapped. -echo Waiting for folder /etc/letsencrypt/live/$MY_DOMAIN_NAME to exist -while [ ! -d /etc/letsencrypt/live/$MY_DOMAIN_NAME ] ; -do - sleep 2 -done - -while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/fullchain.pem ] ; -do - echo Waiting for file fullchain.pem to exist - sleep 2 -done - -while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/privkey.pem ] ; -do - echo Waiting for file privkey.pem to exist - sleep 2 -done - -# This is added so that when the certificate is being renewed or is already in place, nginx waits for everything to be good. -sleep 15 - -echo replacing ___my.example.com___/$MY_DOMAIN_NAME - - -# Put your domain name into the nginx reverse proxy config. -sed -i "s/___my.example.com___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx-secure.conf - -# Add the system's nameserver (the docker network dns) so we can resolve container names in nginx -NAMESERVER=`cat /etc/resolv.conf | grep "nameserver" | awk '{print $2}' | tr '\n' ' '` -echo replacing ___NAMESERVER___/$NAMESERVER -sed -i "s/___NAMESERVER___/$NAMESERVER/g" /etc/nginx/nginx-secure.conf - - -#go! -kill $(ps aux | grep 'nginx' | grep -v 'grep' | awk '{print $2}') -cp /etc/nginx/nginx-secure.conf /etc/nginx/nginx.conf - -nginx -g 'daemon off;' diff --git a/{{cookiecutter.project_slug}}/env.example b/{{cookiecutter.project_slug}}/env.example index 138b75fd..c0278efe 100644 --- a/{{cookiecutter.project_slug}}/env.example +++ b/{{cookiecutter.project_slug}}/env.example @@ -3,6 +3,9 @@ POSTGRES_PASSWORD=mysecretpass POSTGRES_USER=postgresuser +# Domain name, used by caddy +DOMAIN_NAME={{ cookiecutter.domain_name }} + # General settings # DJANGO_READ_DOT_ENV_FILE=True DJANGO_ADMIN_URL= diff --git a/{{cookiecutter.project_slug}}/production.yml b/{{cookiecutter.project_slug}}/production.yml index 00df90d5..79e856df 100644 --- a/{{cookiecutter.project_slug}}/production.yml +++ b/{{cookiecutter.project_slug}}/production.yml @@ -3,6 +3,7 @@ version: '2' volumes: postgres_data: {} postgres_backup: {} + caddy: {} services: django: @@ -22,36 +23,16 @@ services: - postgres_backup:/backups env_file: .env - nginx: - build: ./compose/nginx + caddy: + build: ./compose/caddy depends_on: - django -{% if cookiecutter.use_lets_encrypt == 'y' %} - - certbot - environment: - - MY_DOMAIN_NAME={{ cookiecutter.domain_name }} -{% endif %} ports: - "0.0.0.0:80:80" -{% if cookiecutter.use_lets_encrypt == 'y' %} - "0.0.0.0:443:443" volumes: - - /etc/letsencrypt:/etc/letsencrypt - - /var/lib/letsencrypt:/var/lib/letsencrypt - - certbot: - image: quay.io/letsencrypt/letsencrypt - command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} --test --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges http-01" - entrypoint: "" - volumes: - - /etc/letsencrypt:/etc/letsencrypt - - /var/lib/letsencrypt:/var/lib/letsencrypt - ports: - - "80" - - "443" - environment: - - TERM=xterm -{% endif %} + - caddy:/root/.caddy + env_file: .env redis: image: redis:3.0