diff --git a/certbot/files/domains.env b/certbot/files/domains.env deleted file mode 100644 index 20fc242..0000000 --- a/certbot/files/domains.env +++ /dev/null @@ -1,2 +0,0 @@ -DOMAINS=example.com www api test\n example.dev www api test -SERVER=kweb\n test diff --git a/docker-compose.yml b/docker-compose.yml index 25b064c..188e375 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,10 @@ services: USER_UID: ${UID_PROXY:-12001} GROUP_UID: ${UID_PROXY:-12001} env_file: - - ./certbot/files/domains.env + # contains DOMAINS and SERVER variable + # DOMAINS: rootA.com www api\nrootB.com www mail + # SERVER: webA webB + - ./files/domains.env restart: always image: proxy container_name: proxy @@ -17,7 +20,7 @@ services: - "80:8080" - "443:8443" networks: - - service_kweb + - service_web - service_certbot volumes: # cert volume @@ -34,7 +37,12 @@ services: USER_UID: ${UID_CERTBOT:-12002} GROUP_UID: ${UID_PROXY:-12002} env_file: - - ./certbot/files/domains.env + # contains DOMAINS and SERVER variable + # DOMAINS: rootA.com www api\nrootB.com www mail + # SERVER (not used by certbot): webA webB + - ./files/domains.env + # cerbot container will be set to testing + # unless PRODUCTION var is set (any value) in the environment - ./production.env # set PRODUCTION= here restart: always image: certbot @@ -53,19 +61,19 @@ services: # TODO: put this in its own docker-compose # as the proxy does not require it - kweb: + web: build: - context: ./kweb + context: ./web args: - USER_UID: ${UID_KWEB:-12003} - GROUP_UID: ${UID_KWEB:-12003} + USER_UID: ${UID_WEB:-12003} + GROUP_UID: ${UID_WEB:-12003} restart: always - image: kweb - container_name: kweb + image: web + container_name: web networks: - - service_kweb + - service_web volumes: - - service_kweb:/service/html:ro + - service_web:/service/html:ro volumes: # proxy volumes @@ -83,11 +91,11 @@ volumes: certbot_logs: name: certbot_logs # service volumes - service_kweb: - name: service_kweb + service_web: + name: service_web networks: - service_kweb: - name: service_kweb + service_web: + name: service_web service_certbot: name: service_certbot \ No newline at end of file diff --git a/files/domains.env b/files/domains.env new file mode 100644 index 0000000..6bc2b67 --- /dev/null +++ b/files/domains.env @@ -0,0 +1,4 @@ +# domains are seperated with \n +DOMAINS=example.com www api test +# server values are seperated with ' ' (space) (one server per domain) +SERVER=web diff --git a/proxy/files/entrypoint.sh b/proxy/files/entrypoint.sh index 26d5df3..5c518b0 100644 --- a/proxy/files/entrypoint.sh +++ b/proxy/files/entrypoint.sh @@ -9,9 +9,11 @@ server_retries=10 log_file="" # leave empty for stdout # variables +config_file="$temp_path/nginx.conf" domain_file="$temp_path/domains.txt" certs_updated="$cert_root_path/.updated" certs_daemon_pid=-1 +server_configs="" #functions function log() @@ -41,6 +43,24 @@ function log() esac } +# this is a helper function as dash does not support arrays +function get_element() +{ + element="$1" + seperator="$2"; shift; shift + array=$@ + old_IFS=$IFS + IFS="$seperator" + set -- $array + while [ "$element" -gt 0 ]; do + shift + element=$(( element - 1 )) + done + result=$1 + IFS=$old_IFS + echo $result +} + function sleep_wait() { # waiting on sleep to be able to handle SIGTERM @@ -85,13 +105,15 @@ function read_file() local func=$1 local file=$2 local result=0 + local count=0 while read -r line || [[ -n "$line" ]]; do + count=$(( count+1 )) # skip empty lines if READ_EMPTY_LINES is not set [ -z ${READ_EMPTY_LINES+x} ] && [ "${#line}" -lt 1 ] && continue - log read_file "LINE: $line" + log read_file "LINE[$count]: $line" log read_file "FUNC START [$func]" - if $func $line; then + if $func $count $line; then log read_file "FUNC END [$func]" else result=1 @@ -102,9 +124,77 @@ function read_file() return $result } +function rng() +{ + rng_n=$(od -An -N2 -i < /dev/urandom) + echo $rng_n +} + +function gen_server_config() +{ + local server=$1; shift + local root_domain=$1; shift + local domain_list="$@" + local domain_count=0 + local domains="$root_domain" + + # no arrays in dash + local subdomain=$(get_element $domain_count " " $domain_list) + while [ ${#subdomain} -gt 0 ]; do + domains="$domains ${subdomain}.$root_domain" + domain_count=$(( domain_count + 1 )) + subdomain=$(get_element $domain_count " " $domain_list) + done + + local rng_number=$(rng) + local server_var=" + server { + listen 8443 ssl; + + # prevent redirecting to 8080 port + # (e.g. when trailing slash is missed) + port_in_redirect off; + + server_name $domains; + + ssl_certificate /service/ssl/$root_domain/fullchain.pem; + ssl_certificate_key /service/ssl/$root_domain/privkey.pem; + + location / { + # allow for upstream to be offline by setting + # docker dns resolver and a variable for the server + resolver 127.0.0.11 valid=30s; + set \$web_$rng_number $server; + proxy_pass http://\$web_$rng_number:8080; + + proxy_redirect off; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host \$server_name; + } + } + + " + echo "$server_var" +} + +function server_config() +{ + local server_number=$1; shift + local server_name=$(get_element $(( server_number-1 )) " " $SERVER) + local server_domain=$@ + if [ ${#server_name} -eq 0 ]; then + log server_config "no name for server name for domains [$server_domain]" + return 1 + fi + log server_config "creating config for [$server_name] with domains [$server_domain]" + server_configs="$server_configs$(gen_server_config $server_name $server_domain)" +} + function cert_exists() { - local domain=$1 + local domain=$2 local cert_path="$cert_root_path/$domain" [ -f "$cert_path/fullchain.pem" ] } @@ -143,6 +233,13 @@ function nginx_handler() # start the daemon (it forks itself) certs_daemon + # generate server config + read_file server_config "$domain_file" + tmp_file="$temp_path/tmp.conf" + echo -e "$server_configs" > $tmp_file + awk 'NR==FNR { a[n++]=$0; next } /#/ { for (i=0;i "$config_file" + # wait for certs to exist before starting nginx # just have nginx crash after n retries (default 10) while ! read_file cert_exists "$domain_file" && [ "$retries" -lt "$server_retries" ]; do @@ -153,7 +250,7 @@ function nginx_handler() # starting nginx in foreground log nginx_handler "Starting nginx in foreground" - exec nginx -g "daemon off;" + exec nginx -c "$config_file" -g "daemon off;" } function main() diff --git a/proxy/files/nginx.conf b/proxy/files/nginx.conf index df961fa..d595866 100644 --- a/proxy/files/nginx.conf +++ b/proxy/files/nginx.conf @@ -38,31 +38,8 @@ http { } } - server { - listen 8443 ssl; + # the following variable is automatically populated via entrypoint.sh + # - # prevent redirecting to 8080 port - # (e.g. when trailing slash is missed) - port_in_redirect off; - - server_name example.com www.example.com localhost; - - ssl_certificate /service/ssl/example.com/fullchain.pem; - ssl_certificate_key /service/ssl/example.com/privkey.pem; - - location / { - # allow for upstream to be offline by setting - # docker dns resolver and a variable for the server - resolver 127.0.0.11 valid=30s; - set $kweb kweb; - proxy_pass http://$kweb:8080; - - proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; - } - } } diff --git a/kweb/Dockerfile b/web/Dockerfile similarity index 100% rename from kweb/Dockerfile rename to web/Dockerfile diff --git a/kweb/files/index.html b/web/files/index.html similarity index 100% rename from kweb/files/index.html rename to web/files/index.html diff --git a/kweb/files/nginx.conf b/web/files/nginx.conf similarity index 100% rename from kweb/files/nginx.conf rename to web/files/nginx.conf