[Project] Initial Commit
This commit is contained in:
commit
45aff8bc5b
|
@ -0,0 +1,40 @@
|
|||
# nginx-lets-encrypt-proxy
|
||||
|
||||
This is a docker container as a nginx proxy with Let's Encrypt and virtual hosts support.
|
||||
|
||||
Inspirations:
|
||||
|
||||
* https://github.com/gilyes/docker-nginx-letsencrypt-sample
|
||||
* https://ilhicas.com/2019/03/02/Nginx-Letsencrypt-Docker.html
|
||||
* https://github.com/Ilhicas/nginx-letsencrypt
|
||||
|
||||
|
||||
Create necessary directories and files:
|
||||
|
||||
mkdir ssl
|
||||
mkdir nginx/modules-enabled
|
||||
mkdir letsencrypt/
|
||||
touch letsencrypt/configured-domains
|
||||
|
||||
|
||||
Create the dhparams:
|
||||
|
||||
openssl dhparam -out ssl/dhparam.pem 2048
|
||||
|
||||
|
||||
And then add your virtual hosts in `nginx/sites-enabled`, but make sure to use the following certificates:
|
||||
|
||||
ssl_certificate /etc/letsencrypt/snake-oil/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/snake-oil/private-key.pem;
|
||||
|
||||
The internal scripts will then replace this and reload nginx when necessary.
|
||||
|
||||
Now add your domains as separate lines in `letsencrypt/configured-domains`.
|
||||
|
||||
Now build the image:
|
||||
|
||||
docker-compose build --pull
|
||||
|
||||
And then start:
|
||||
|
||||
docker-compose up
|
|
@ -0,0 +1 @@
|
|||
Dockerfile
|
|
@ -0,0 +1,12 @@
|
|||
FROM nginx:alpine
|
||||
RUN apk add bash inotify-tools certbot openssl ca-certificates
|
||||
RUN mkdir -p /var/www/certbot/
|
||||
RUN mkdir -p /etc/ssl/
|
||||
WORKDIR /opt
|
||||
COPY certbot-obtain.sh certbot-obtain.sh
|
||||
COPY certbot-renewal.sh certbot-renewal.sh
|
||||
COPY nginx-reload.sh nginx-reload.sh
|
||||
COPY entrypoint.sh entrypoint.sh
|
||||
COPY default.conf /etc/nginx/nginx.conf
|
||||
RUN chmod +x certbot-obtain.sh && chmod +x certbot-renewal.sh && chmod +x nginx-reload.sh && chmod +x entrypoint.sh
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
|
@ -0,0 +1,68 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$#" != "3" ]] ; then
|
||||
echo -e "ERROR: Usage: $0 <watch_file> <letsencrypt_dir> <certbot_dir>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WATCH_FILE="$1"
|
||||
LETSENCRYPT_DIR="$2"
|
||||
CERTBOT_WWW_DIR="$3"
|
||||
|
||||
WATCH_FILE_NAME=$(basename $(realpath ${WATCH_FILE}))
|
||||
WATCH_DIR=$(dirname $(realpath ${WATCH_FILE}))
|
||||
|
||||
sleep 5s
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Watching directory ${WATCH_DIR} for changes to file ${WATCH_FILE_NAME}..."
|
||||
|
||||
function parseFile() {
|
||||
file="$1"
|
||||
cat ${file} | while read line ; do
|
||||
line=${line##*( )}
|
||||
line=${line%%*( )}
|
||||
[[ "$line" == "" ]] && continue
|
||||
[[ $line =~ ^#.* ]] && continue
|
||||
|
||||
obtainCertificate ${line}
|
||||
done
|
||||
}
|
||||
|
||||
function obtainCertificate() {
|
||||
domain="$1"
|
||||
|
||||
nginx_file="/etc/nginx/sites-enabled/${domain}"
|
||||
letsencrypt_file="${LETSENCRYPT_DIR}/live/${domain}"
|
||||
|
||||
[[ ! -f ${nginx_file} ]] && continue
|
||||
[[ -d ${letsencrypt_file} ]] && continue
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Obtaining certificate for new domain ${domain}"
|
||||
if ! echo certbot certonly \
|
||||
--test-cert \
|
||||
--dry-run \
|
||||
--config-dir ${LETSENCRYPT_DIR} \
|
||||
--agree-tos \
|
||||
--domain "${domain}" \
|
||||
--email "${LETS_ENCRYPT_EMAIL}" \
|
||||
--expand \
|
||||
--noninteractive \
|
||||
--webroot \
|
||||
--webroot-path ${CERTBOT_WWW_DIR} ; then
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') ERROR: Failed to obtain certificate for $domain"
|
||||
echo nginx -s quit
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Initially obtaining certificates..."
|
||||
parseFile ${WATCH_FILE}
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Done."
|
||||
|
||||
inotifywait -m -e CLOSE_WRITE "${WATCH_DIR}" | while read filename eventlist eventfile ; do
|
||||
[[ $eventfile != ${WATCH_FILE_NAME} ]] && continue
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Obtaining certificates as changes detected in file $filename $eventlist $eventfile"
|
||||
parseFile ${WATCH_FILE}
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
sleep 30s
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Renewing certificates every 12h."
|
||||
while :
|
||||
do
|
||||
sleep "12h"
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Renewing certificates..."
|
||||
|
||||
if ! certbot -q renew --post-hook nginx -s reload ; then
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') ERROR: Failed to renew certificates stopping nginx!"
|
||||
nginx -s stop
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,63 @@
|
|||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
##
|
||||
# Basic Settings
|
||||
##
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
# server_tokens off;
|
||||
|
||||
# server_names_hash_bucket_size 64;
|
||||
# server_name_in_redirect off;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
##
|
||||
# SSL Settings
|
||||
##
|
||||
#ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
|
||||
#ssl_prefer_server_ciphers on;
|
||||
|
||||
##
|
||||
# Logging Settings
|
||||
##
|
||||
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;
|
||||
|
||||
##
|
||||
# Gzip Settings
|
||||
##
|
||||
gzip on;
|
||||
|
||||
# gzip_vary on;
|
||||
# gzip_proxied any;
|
||||
# gzip_comp_level 6;
|
||||
# gzip_buffers 16 8k;
|
||||
# gzip_http_version 1.1;
|
||||
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
##
|
||||
# Virtual Host Configs
|
||||
##
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#!/bin/bash
|
||||
# Create a self signed default certificate, so Nginx can start before we have
|
||||
# any real certificates.
|
||||
|
||||
#Ensure we have folders available
|
||||
|
||||
WATCH_FILE="/etc/letsencrypt/configured-domains"
|
||||
LETSENCRYPT_DIR="/etc/letsencrypt"
|
||||
CERTBOT_WWW_DIR="/var/www/certbot"
|
||||
|
||||
if [[ ! -f "${WATCH_FILE}" ]] ; then
|
||||
echo -e "E$(date +'%Y-%m-%d %H:%M:%S') RROR: Failed to read configured-domains file: ${WATCH_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "${CERTBOT_WWW_DIR}" ]] ; then
|
||||
echo -e "E$(date +'%Y-%m-%d %H:%M:%S') RROR: Certbot directory does not exist at ${CERTBOT_WWW_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LETS_ENCRYPT_EMAIL=eitch@eitchnet.ch
|
||||
NGINX_HOST=proxy.eitchnet.ch
|
||||
NGINX_PORT=80
|
||||
|
||||
if [[ ! -f /etc/letsencrypt/snake-oil/fullchain.pem ]] ; then
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Creating snake-oil certificate..."
|
||||
mkdir -p /etc/letsencrypt/snake-oil
|
||||
openssl genrsa -out /etc/letsencrypt/snake-oil/private-key.pem 4096
|
||||
openssl req -new -key /etc/letsencrypt/snake-oil/private-key.pem -out /etc/letsencrypt/snake-oil/snake-oil.csr -nodes -subj \
|
||||
"/C=CH/ST=Solothurn/L=Solothurn/O=Home/OU=Lab/CN=${NGINX_HOST}"
|
||||
openssl x509 -req -days 365 -in /etc/letsencrypt/snake-oil/snake-oil.csr -signkey /etc/letsencrypt/snake-oil/private-key.pem -out /etc/letsencrypt/snake-oil/fullchain.pem
|
||||
fi
|
||||
|
||||
trap "echo $(date +'%Y-%m-%d %H:%M:%S') exiting due to signal ; kill $(jobs -p)" SIGHUP SIGINT SIGTERM SIGQUIT SIGTRAP SIGABRT SIGSTOP
|
||||
trap "echo $(date +'%Y-%m-%d %H:%M:%S') exiting due to error! ; exit" ERR
|
||||
|
||||
nginx -g "daemon off;" &
|
||||
nginx_sid=$!
|
||||
|
||||
/opt/certbot-obtain.sh ${WATCH_FILE} ${LETSENCRYPT_DIR} ${CERTBOT_WWW_DIR} &
|
||||
/opt/certbot-renewal.sh "12h" &
|
||||
/opt/nginx-reload.sh &
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Sleeping indefinitely..."
|
||||
wait ${nginx_sid}
|
||||
echo -e "INFO: nginx has exited, exiting script!"
|
||||
kill $(jobs -p)
|
||||
exit 0
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
sleep 5s
|
||||
|
||||
WATCH_DIR="/etc/nginx/sites-enabled"
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Watching directory ${WATCH_DIR} for changes to file ${WATCH_FILE_NAME}..."
|
||||
|
||||
lastReload=0
|
||||
|
||||
inotifywait -m -e CREATE -e CLOSE_WRITE "${WATCH_DIR}" | while read filename eventlist eventfile ; do
|
||||
[[ $eventfile =~ ^\..* ]] && continue
|
||||
now=$(date +%s)
|
||||
diff=$((now-lastReload))
|
||||
if [[ ${diff} -lt 10 ]] ; then
|
||||
echo -e "INFO: Ignoring duplicate action to $eventfile"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo -e "$(date +'%Y-%m-%d %H:%M:%S') INFO: Reloading nginx as changes detected in file $filename $eventlist $eventfile"
|
||||
nginx -s reload
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,30 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
proxy:
|
||||
restart: unless-stopped
|
||||
image: eitch-proxy
|
||||
build: ./build
|
||||
container_name: eitch-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- "./letsencrypt:/etc/letsencrypt"
|
||||
- "./ssl/dhparam.pem:/etc/ssl/dhparam.pem:ro"
|
||||
- "./nginx/conf.d:/etc/nginx/conf.d"
|
||||
- "./nginx/modules-enabled:/etc/nginx/modules-enabled"
|
||||
- "./nginx/sites-enabled:/etc/nginx/sites-enabled"
|
||||
- "./nginx/cache:/var/cache/nginx"
|
||||
- "./nginx/pid:/var/run"
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
- LETS_ENCRYPT_EMAIL=eitch@eitchnet.ch
|
||||
- NGINX_HOST=proxy.eitchnet.ch
|
||||
- NGINX_PORT=80
|
||||
networks:
|
||||
proxy-network:
|
||||
ipv4_address: 10.254.0.2
|
||||
networks:
|
||||
proxy-network:
|
||||
external: true
|
|
@ -0,0 +1,8 @@
|
|||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_timeout 5m;
|
||||
ssl_dhparam /etc/ssl/dhparam.pem;
|
||||
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
|
|
@ -0,0 +1,18 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name localhost;
|
||||
ssl_certificate /etc/letsencrypt/snake-oil/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/snake-oil/private-key.pem;
|
||||
#include /etc/nginx/ssl.conf;
|
||||
}
|
Loading…
Reference in New Issue