Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Attempt to automate initial bootstrap without need to run init-letsencrypt.sh manually #94

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env-sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Set to 1 if you're testing your setup to avoid hitting request limits
staging=0
domains=example.org
# Adding a valid address is strongly recommended
email=""
rsa_key_size=4096
nginx_api_user=foo
nginx_api_password=bar
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/data/certbot
!/data/certbot/scripts
/.env
!/.env*sample
/TMP
12 changes: 12 additions & 0 deletions certbot/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM certbot/certbot

RUN set -x \
&& apk add --no-cache \
curl \
bash \
&& rm -rf /var/cache/apk/* \
/tmp/* \
/var/tmp/*

COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
67 changes: 67 additions & 0 deletions certbot/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# vim:sw=2:ts=2:et

set -ueo pipefail
# DEBUG
# set -x

# convert space-delimited string from the ENV to array
domains=(${domains:-example.org})

domain=${domains[0]}

data_path="/etc/letsencrypt"
path="$data_path/live/$domain"

rsa_key_size=${rsa_key_size:-4096}

trap exit TERM

echo "### Let's nginx bootstrap"
sleep 10s

# Select appropriate email arg
case "$email" in
"") email_arg="--register-unsafely-without-email" ;;
*) email_arg="--email $email" ;;
esac

if [ ! -f "$path/privkey.pem" ]; then
echo "### Requesting Let's Encrypt certificate for $domains ..."

# join $domains to -d args
domain_args=""
for domain in "${domains[@]}"; do
domain_args="$domain_args -d $domain"
done

# Enable staging mode if needed
if [ $staging != "0" ]; then
staging_arg="--staging"
else
staging_arg=""
fi

certbot certonly \
--webroot -w /var/www/certbot \
$staging_arg \
$email_arg \
$domain_args \
--rsa-key-size $rsa_key_size \
--agree-tos \
--force-renewal

echo "### Reloading nginx ..."
curl --fail --silent --user ${nginx_api_user}:${nginx_api_password} http://nginx/nginx/reload
fi

while :; do
certbot renew \
--webroot -w /var/www/certbot \
$email_arg \
--rsa-key-size $rsa_key_size \
--agree-tos

curl --fail --silent --user ${nginx_api_user}:${nginx_api_password} http://nginx/nginx/reload
sleep 12h & wait ${!}
done
31 changes: 0 additions & 31 deletions data/nginx/app.conf

This file was deleted.

35 changes: 25 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
version: '3'
volumes:
ssl: {}

services:
nginx:
image: nginx:1.15-alpine
build:
context: nginx
dockerfile: Dockerfile
restart: unless-stopped
volumes:
- ./data/nginx:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
healthcheck:
test: ["CMD", "curl", "--silent", "--fail", "http://localhost"]
interval: 45s
timeout: 5s
retries: 3
ports:
- "80:80"
- "443:443"
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
env_file: .env
volumes:
- ./nginx/conf-templates:/etc/nginx/templates
- ssl:/etc/letsencrypt
- ssl:/var/www/certbot

certbot:
image: certbot/certbot
build:
context: certbot
dockerfile: Dockerfile
restart: unless-stopped
depends_on:
nginx:
condition: service_healthy
env_file: .env
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
- ssl:/etc/letsencrypt
- ssl:/var/www/certbot
80 changes: 0 additions & 80 deletions init-letsencrypt.sh

This file was deleted.

23 changes: 23 additions & 0 deletions nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM nginx:1.19-alpine

RUN set -x \
&& apk add --no-cache \
apache2-utils \
curl \
bash \
netcat-openbsd \
openssl \
&& mkdir -p /etc/nginx/letsencrypt \
&& curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "/etc/nginx/letsencrypt/options-ssl-nginx.conf" \
&& curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "/etc/nginx/letsencrypt/ssl-dhparams.pem" \
&& chown -R nginx /etc/nginx/letsencrypt \
&& rm -rf /var/cache/apk/* \
/tmp/* \
/var/tmp/*

RUN curl -s https://raw.githubusercontent.com/nginxinc/docker-nginx/master/mainline/alpine/docker-entrypoint.sh > "/nginx-entrypoint.sh" \
&& chmod +x /nginx-entrypoint.sh
COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]

CMD ["nginx", "-g", "daemon off;"]
39 changes: 39 additions & 0 deletions nginx/conf-templates/app.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# vim:sw=4:ts=4:et:ft=nginx

server {
listen 80;
server_name ${nginx_domain};
server_tokens off;

location /nginx/reload {
auth_basic 'Access restriction';
auth_basic_user_file /tmp/.htpasswd;
proxy_pass http://localhost:9000;
}

location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
return 301 https://$host$request_uri;
}
}

server {
listen 443 ssl;
server_name ${nginx_domain};
server_tokens off;

ssl_certificate /etc/letsencrypt/live/${nginx_domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${nginx_domain}/privkey.pem;
include /etc/nginx/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/nginx/letsencrypt/ssl-dhparams.pem;

location / {
proxy_pass http://${nginx_domain};
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
63 changes: 63 additions & 0 deletions nginx/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# vim:sw=2:ts=2:et

set -ueo pipefail
# DEBUG
# set -x

# convert space-delimited string from the ENV to array
domains=(${domains:-example.org})
domain="${domains[0]}"
export nginx_domain="${domain}"

data_path="/etc/letsencrypt"
path="$data_path/live/$domain"

rsa_key_size=${rsa_key_size:-4096}

wait_certbot(){
(
echo "### Waiting for certbot container"

retries="${1:-180}"

set +e
until ping -c 1 certbot > /dev/null 2>&1 || [ "$retries" -eq 0 ]; do
: $((retries--))
echo "### certbot is not up yet!"
sleep 1s
done
set -e

[ "${retries}" -ne 0 ] || (echo "### certbot service did not get up"; exit 1)

echo "### Removing self-signed SSL from $path"
rm -rf "$path"
) &
}

if [ ! -f "$path/privkey.pem" ]; then
sleep 5
echo "### Creating dummy certificate for $domain ..."

mkdir -p "$path"

openssl req -x509 -nodes -newkey rsa:1024 -days 1 \
-keyout "$path/privkey.pem" \
-out "$path/fullchain.pem" \
-subj '/CN=localhost'

wait_certbot
fi

# API service that reloads nginx on request
htpasswd -bc /tmp/.htpasswd "${nginx_api_user}" "${nginx_api_password}" > /dev/null 2>&1
(
while true
do
{ echo -e "HTTP/1.1 200 OK\n\nNGINX reload requested at: $(date)"; nginx -s reload & } | nc -l -p 9000 -q 1
done
) &

# original entrypoint for nginx
exec /nginx-entrypoint.sh "$@"