merge @mjsisley #576

This commit is contained in:
Daniel Roy Greenfeld 2016-06-04 10:21:44 -07:00
commit 94cb04b389
11 changed files with 313 additions and 21 deletions

View File

@ -97,6 +97,7 @@ Listed in alphabetical order.
Matt Linares Matt Linares
Matt Menzenski `@menzenski`_ Matt Menzenski `@menzenski`_
Matt Warren `@mfwarren`_ Matt Warren `@mfwarren`_
Matthew Sisley `@mjsisley`_
Meghan Heintz `@dot2dotseurat`_ Meghan Heintz `@dot2dotseurat`_
mjsisley `@mjsisley`_ mjsisley `@mjsisley`_
mozillazg `@mozillazg`_ mozillazg `@mozillazg`_
@ -183,6 +184,7 @@ Listed in alphabetical order.
.. _@yunti: https://github.com/yunti .. _@yunti: https://github.com/yunti
.. _@zcho: https://github.com/zcho .. _@zcho: https://github.com/zcho
.. _@noisy: https://github.com/noisy .. _@noisy: https://github.com/noisy
.. _@mjsisley: https://github.com/mjsisley
Special Thanks Special Thanks
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~

View File

@ -21,5 +21,6 @@
"use_docker": "y", "use_docker": "y",
"use_heroku": "n", "use_heroku": "n",
"js_task_runner": ["Gulp", "Grunt", "None"], "js_task_runner": ["Gulp", "Grunt", "None"],
"use_certbot": "n",
"open_source_license": ["MIT", "BSD", "Apache Software License 2.0", "Not open source"] "open_source_license": ["MIT", "BSD", "Apache Software License 2.0", "Not open source"]
} }

View File

@ -110,3 +110,47 @@ To get the status, run::
If you have errors, you can always check your stack with `docker-compose`. Switch to your projects root directory and run:: If you have errors, you can always check your stack with `docker-compose`. Switch to your projects root directory and run::
docker-compose ps docker-compose ps
If you are using certbot for https, you must do the following before running anything with docker-compose:
Replace dhparam.pem.example with a generated dhparams.pem file before running anything with docker-compose. You can generate this on ubuntu or OS X by running the following in the project root:
::
$ openssl dhparam -out /path/to/project/compose/nginx/dhparams.pem 2048
If you would like to add additional subdomains to your certificate, you must add additional parameters to the certbot command in the `docker-compose.yml` file:
Replace:
::
command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} --text --agree-tos --email mjsisley@relawgo.com --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
With:
::
command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} -d www.{{ cookiecutter.domain_name }} -d etc.{{ cookiecutter.domain_name }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
Please be cognizant of Certbot/Letsencrypt certificate requests limits when getting this set up. The provide a test server that does not count against the limit while you are getting set up.
The certbot certificates expire after 3 months.
If you would like to set up autorenewal of your certificates, the following commands can be put into a bash script:
::
#!/bin/bash
cd <project directory>
docker-compose run certbot bash -c "sleep 6 && certbot certonly --standalone -d {{ cookiecutter.domain_name }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
docker exec pearl_nginx_1 nginx -s reload
And then set a cronjob by running `crontab -e` and placing in it (period can be adjusted as desired):
0 4 * * 1 /path/to/bashscript/renew_certbot.sh

View File

@ -160,6 +160,7 @@ Then you may need to run the following for it to work as desired:
$ docker-compose run -f dev.yml --service-ports django $ docker-compose run -f dev.yml --service-ports django
django-debug-toolbar django-debug-toolbar
"""""""""""""""""""" """"""""""""""""""""

View File

@ -157,6 +157,16 @@ def remove_packageJSON_file():
PROJECT_DIRECTORY, filename 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"]:
os.remove(os.path.join(
nginx_dir_location, filename
))
# IN PROGRESS # IN PROGRESS
# def copy_doc_files(project_directory): # def copy_doc_files(project_directory):
# cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir'] # cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir']
@ -204,9 +214,12 @@ else:
remove_grunt_files() remove_grunt_files()
remove_packageJSON_file() remove_packageJSON_file()
# 7. Removes all certbot/letsencrypt files if it isn't going to be used
if '{{ cookiecutter.use_certbot }}'.lower() != 'y':
remove_certbot_files()
# 7. Display a warning if use_docker and js task runner are selected. Grunt isn't supported by our # 8. Display a warning if use_docker and use_grunt are selected. Grunt isn't
# docker config atm. # supported by our docker config atm.
if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cookiecutter.use_docker }}'.lower() == 'y': if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cookiecutter.use_docker }}'.lower() == 'y':
print( print(
"You selected to use docker and a JS task runner. This is NOT supported out of the box for now. You " "You selected to use docker and a JS task runner. This is NOT supported out of the box for now. You "
@ -214,13 +227,19 @@ if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cook
"js task runner service to your docker configuration manually." "js task runner service to your docker configuration manually."
) )
# 7. Display a warning if use_docker and use_mailhog are selected. Mailhog isn't supported by our # 9. Removes the certbot/letsencrypt files and display a warning if use_certbot is selected and use_docker isn't.
# docker config atm. if '{{ cookiecutter.use_certbot }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() != 'y':
if '{{ cookiecutter.use_mailhog }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y': remove_certbot_files()
print( print(
"You selected to use docker and mailhog. This is NOT supported out of the box for now. You" "You selected to use certbot(letsencrypt) 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 you will need to add a " "can continue to use the project like you normally would, but you will no certbot files have been included"
" mailhog service to your docker configuration manually." )
# 10. Directs the user to the documentation if certbot and docker are selected.
if '{{ cookiecutter.use_certbot }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y':
print(
"You selected to use certbot(letsencrypt), 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."
) )
# 4. Copy files from /docs/ to {{ cookiecutter.project_slug }}/docs/ # 4. Copy files from /docs/ to {{ cookiecutter.project_slug }}/docs/

View File

@ -1,2 +1,8 @@
FROM nginx:latest FROM nginx:latest
ADD nginx.conf /etc/nginx/nginx.conf ADD nginx.conf /etc/nginx/nginx.conf
{% if cookiecutter.use_certbot == 'y' and cookiecutter.use_docker == 'y' %}
ADD start.sh /start.sh
ADD nginx-secure.conf /etc/nginx/nginx-secure.conf
ADD dhparams.pem /etc/ssl/private/dhparams.pem
{% endif %}

View File

@ -0,0 +1,3 @@
-----BEGIN DH PARAMETERS-----
EXAMPLE_FILE
-----END DH PARAMETERS-----

View File

@ -0,0 +1,92 @@
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 {
proxy_pass http://___LETSENCRYPT_IP___:___LETSENCRYPT_PORT___;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
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 {
proxy_pass http://___LETSENCRYPT_HTTPS_IP___:___LETSENCRYPT_HTTPS_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-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
}
}
}

View File

@ -9,9 +9,9 @@ events {
worker_connections 1024; worker_connections 1024;
} }
http { http {
include /etc/nginx/mime.types;
include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' log_format main '$remote_addr - $remote_user [$time_local] "$request" '
@ -31,23 +31,40 @@ http {
server django:5000; server django:5000;
} }
server { server {
listen 80; listen 80;
charset utf-8; charset utf-8;
{% if cookiecutter.use_certbot == 'y' and cookiecutter.use_docker == 'y' %}
server_name ___my.example.com___ ;
location / { location /.well-known/acme-challenge {
proxy_pass http://___LETSENCRYPT_IP___:___LETSENCRYPT_PORT___;
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 # checks for static file, if not found proxy to app
try_files $uri @proxy_to_app; try_files $uri @proxy_to_app;
} }
location @proxy_to_app {
# cookiecutter-django app
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_redirect off; proxy_redirect off;
proxy_pass http://app; proxy_pass http://app;
}
} }
}
} }

View File

@ -0,0 +1,81 @@
echo sleep 5
sleep 5
echo build starting nginx config
echo replacing ___my.example.com___/$MY_DOMAIN_NAME
echo replacing ___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR
echo replacing ___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT
echo replacing ___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR
echo replacing ___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT
# Put your domain name into the nginx reverse proxy config.
sed -i "s/___my.example.com___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf
# Add your app's container IP and port into config
sed -i "s/___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR/g" /etc/nginx/nginx.conf
sed -i "s/___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT/g" /etc/nginx/nginx.conf
sed -i "s/___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR/g" /etc/nginx/nginx.conf
sed -i "s/___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT/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
echo replacing ___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR
echo replacing ___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT
echo replacing ___LETSENCRYPT_HTTPS_IP___/$LETSENCRYPT_PORT_443_TCP_ADDR
echo replacing ___LETSENCRYPT_HTTPS_PORT___/$LETSENCRYPT_PORT_443_TCP_PORT
echo replacing ___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR
echo replacing ___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT
# 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 LE container IP and port into config
sed -i "s/___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_HTTPS_IP___/$LETSENCRYPT_PORT_443_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_HTTPS_PORT___/$LETSENCRYPT_PORT_443_TCP_PORT/g" /etc/nginx/nginx-secure.conf
# Add your app's container IP and port into config
sed -i "s/___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT/g" /etc/nginx/nginx-secure.conf
#go!
kill $(ps aux | grep 'nginx' | awk '{print $2}')
cp /etc/nginx/nginx-secure.conf /etc/nginx/nginx.conf
nginx -g 'daemon off;'

View File

@ -27,12 +27,38 @@ services:
build: ./compose/nginx build: ./compose/nginx
depends_on: depends_on:
- django - django
{% if cookiecutter.use_certbot == 'y' %}
- certbot
{% endif %}
ports: ports:
- "0.0.0.0:80:80" - "0.0.0.0:80:80"
{% if cookiecutter.use_certbot == 'y' %}
environment:
- MY_DOMAIN_NAME={{ cookiecutter.domain_name }}
ports:
- "0.0.0.0:80:80"
- "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 }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
entrypoint: ""
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- /var/lib/letsencrypt:/var/lib/letsencrypt
ports:
- "80"
- "443"
environment:
- TERM=xterm
{% endif %}
redis: redis:
image: redis:3.0 image: redis:3.0
{% if cookiecutter.use_celery == 'y' %} {% if cookiecutter.use_celery == 'y' %}
celeryworker: celeryworker:
build: build:
context: . context: .
@ -54,4 +80,4 @@ services:
- postgres - postgres
- redis - redis
command: celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO command: celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO
{% endif %} {% endif %}