2017-11-24 23:51:31 +01:00
#!/bin/bash
set -e
# Get the first domain of a comma separated list.
function get_base_domain {
2020-07-04 02:39:32 +02:00
awk -F ',' '{print $1}' <<< " ${ 1 : ? } " | tr -d ' ' | sed 's/\.$//'
2017-11-24 23:51:31 +01:00
}
export -f get_base_domain
2018-08-02 22:58:14 +02:00
2021-04-04 22:58:22 +02:00
# Run a acme-companion container
2017-11-24 23:51:31 +01:00
function run_le_container {
local image = " ${ 1 : ? } "
local name = " ${ 2 : ? } "
2022-02-25 19:36:03 +01:00
shift 2
2020-10-23 23:55:50 +02:00
local -a cli_args_arr
2022-02-25 19:36:03 +01:00
while [ [ $# -gt 0 ] ] ; do
local flag = " $1 "
case $flag in
-c| --cli-args) #only one value per flag. Multiple args = use flag multiple times
local cli_args_arr_tmp
IFS = ' ' read -r -a cli_args_arr_tmp <<< " ${ 2 : ? } "
cli_args_arr += ( " ${ cli_args_arr_tmp [0] } " ) #Head
#shellcheck disable=SC2116
cli_args_arr += ( " $( echo " ${ cli_args_arr_tmp [@] : 1 } " ) " ) #Tail
shift 2
; ;
*) #Legacy Option
local cli_args_str = " ${ 1 : ? } "
for arg in $cli_args_str ; do
cli_args_arr += ( " $arg " )
done
shift
; ;
esac
2020-10-23 23:55:50 +02:00
done
2018-06-01 10:33:12 +02:00
if [ [ " $SETUP " = = '3containers' ] ] ; then
2020-10-23 23:55:50 +02:00
cli_args_arr += ( --env " NGINX_DOCKER_GEN_CONTAINER= $DOCKER_GEN_CONTAINER_NAME " )
2018-06-01 10:33:12 +02:00
fi
2020-12-27 22:50:12 +01:00
if [ [ " $ACME_CA " = = 'boulder' ] ] ; then
cli_args_arr += ( --env "ACME_CA_URI=http://boulder:4001/directory" )
cli_args_arr += ( --network boulder_bluenet)
elif [ [ " $ACME_CA " = = 'pebble' ] ] ; then
cli_args_arr += ( --env "ACME_CA_URI=https://pebble:14000/dir" )
cli_args_arr += ( --env "CA_BUNDLE=/pebble.minica.pem" )
cli_args_arr += ( --network acme_net)
2022-02-25 19:36:03 +01:00
cli_args_arr += ( --volume " ${ GITHUB_WORKSPACE } /pebble.minica.pem:/pebble.minica.pem " )
2020-12-27 22:50:12 +01:00
else
return 1
fi
2020-10-22 01:09:18 +02:00
if docker run -d \
2017-11-24 23:51:31 +01:00
--name " $name " \
2020-10-09 13:04:10 +02:00
--volumes-from " $NGINX_CONTAINER_NAME " \
2017-11-24 23:51:31 +01:00
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
2020-10-23 23:55:50 +02:00
" ${ cli_args_arr [@] } " \
2020-12-07 00:57:22 +01:00
--env "DOCKER_GEN_WAIT=500ms:2s" \
2020-11-27 18:16:35 +01:00
--env "TEST_MODE=true" \
2020-11-08 22:30:33 +01:00
--env "DEBUG=1" \
2017-12-29 00:28:15 +01:00
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
2020-10-22 01:09:18 +02:00
" $image " > /dev/null; \
then
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Started letsencrypt container for test ${ name %%_2* } "
else
echo " Could not start letsencrypt container for test ${ name %%_2* } "
return 1
fi
return 0
2017-11-24 23:51:31 +01:00
}
export -f run_le_container
2020-10-22 01:09:18 +02:00
# Run an nginx container
function run_nginx_container {
2020-12-27 22:50:12 +01:00
local -a cli_args_arr
2020-12-28 11:26:52 +01:00
while [ [ $# -gt 0 ] ] ; do
local flag = " $1 "
case $flag in
-h| --hosts)
local le_host = " ${ 2 : ? } "
local virtual_host = " ${ le_host // / } " ; virtual_host = " ${ virtual_host //.,/, } " ; virtual_host = " ${ virtual_host %, } "
shift 2
; ;
-n| --name)
local container_name = " ${ 2 : ? } "
shift 2
; ;
-c| --cli-args)
local cli_args_str = " ${ 2 : ? } "
for arg in $cli_args_str ; do
cli_args_arr += ( " $arg " )
done
shift 2
; ;
*) #Unknown option
shift
; ;
esac
done
2020-12-27 22:50:12 +01:00
if [ [ " $ACME_CA " = = 'boulder' ] ] ; then
cli_args_arr += ( --network boulder_bluenet)
elif [ [ " $ACME_CA " = = 'pebble' ] ] ; then
cli_args_arr += ( --network acme_net)
else
return 1
fi
2020-12-28 11:26:52 +01:00
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Starting $container_name nginx container, with VIRTUAL_HOST= $virtual_host , LETSENCRYPT_HOST= $le_host and the following cli arguments : ${ cli_args_arr [*] } . "
2020-10-22 01:09:18 +02:00
if docker run --rm -d \
2020-12-28 11:26:52 +01:00
--name " ${ container_name :- $virtual_host } " \
2020-10-22 01:09:18 +02:00
-e " VIRTUAL_HOST= $virtual_host " \
-e " LETSENCRYPT_HOST= $le_host " \
2020-12-28 16:28:18 +01:00
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
2020-12-27 22:50:12 +01:00
" ${ cli_args_arr [@] } " \
2020-10-22 01:09:18 +02:00
nginx:alpine > /dev/null ; \
then
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Started $container_name nginx container. "
else
2020-12-28 11:26:52 +01:00
echo " Failed to start $container_name nginx container, with VIRTUAL_HOST= $virtual_host , LETSENCRYPT_HOST= $le_host and the following cli arguments : ${ cli_args_arr [*] } . "
2020-10-22 01:09:18 +02:00
return 1
fi
return 0
}
export -f run_nginx_container
2018-08-02 22:58:14 +02:00
2020-07-05 12:40:24 +02:00
# Wait for the /etc/nginx/conf.d/standalone-cert-$1.conf file to exist inside container $2
function wait_for_standalone_conf {
local domain = " ${ 1 : ? } "
local name = " ${ 2 : ? } "
2020-11-23 03:13:51 +01:00
local timeout
timeout = " $( date +%s) "
2021-04-05 14:32:04 +02:00
timeout = " $(( timeout + 120 )) "
2020-07-05 12:40:24 +02:00
local target
until docker exec " $name " [ -f " /etc/nginx/conf.d/standalone-cert- $domain .conf " ] ; do
2020-11-23 03:13:51 +01:00
if [ [ " $( date +%s) " -gt " $timeout " ] ] ; then
2020-07-05 12:40:24 +02:00
echo " Standalone configuration file for $domain was not generated under one minute, timing out. "
return 1
fi
sleep 0.1
done
}
export -f wait_for_standalone_conf
2018-03-18 18:50:47 +01:00
# Wait for the /etc/nginx/certs/$1.crt symlink to exist inside container $2
function wait_for_symlink {
2017-11-24 23:51:31 +01:00
local domain = " ${ 1 : ? } "
local name = " ${ 2 : ? } "
2020-10-22 01:09:18 +02:00
local expected_target = " ${ 3 :- } "
2020-11-23 03:13:51 +01:00
local timeout
timeout = " $( date +%s) "
2021-04-05 14:32:04 +02:00
timeout = " $(( timeout + 120 )) "
2018-03-18 18:50:47 +01:00
local target
until docker exec " $name " [ -L " /etc/nginx/certs/ $domain .crt " ] ; do
2020-11-23 03:13:51 +01:00
if [ [ " $( date +%s) " -gt " $timeout " ] ] ; then
2018-11-23 03:17:31 +01:00
echo " Symlink for $domain certificate was not generated under one minute, timing out. "
2017-11-24 23:51:31 +01:00
return 1
fi
2020-11-23 03:13:51 +01:00
sleep 0.1
2017-11-24 23:51:31 +01:00
done
2020-10-22 01:09:18 +02:00
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Symlink to $domain certificate has been generated. "
if [ [ -n " $expected_target " ] ] ; then
target = " $( docker exec " $name " readlink " /etc/nginx/certs/ $domain .crt " ) "
if [ [ " $target " != " $expected_target " ] ] ; then
echo " The symlink to the $domain certificate is expected to point to $expected_target but point to $target instead. "
return 1
elif [ [ " ${ DRY_RUN :- } " = = 1 ] ] ; then
echo " The symlink is pointing to the file $target "
fi
fi
return 0
2017-11-24 23:51:31 +01:00
}
2018-03-18 18:50:47 +01:00
export -f wait_for_symlink
2018-08-02 22:58:14 +02:00
2018-11-23 03:00:30 +01:00
# Wait for the /etc/nginx/certs/$1.crt symlink to be removed inside container $2
2018-03-18 18:50:47 +01:00
function wait_for_symlink_rm {
local domain = " ${ 1 : ? } "
local name = " ${ 2 : ? } "
2020-11-23 03:13:51 +01:00
local timeout
timeout = " $( date +%s) "
2021-04-05 14:32:04 +02:00
timeout = " $(( timeout + 120 )) "
2018-11-23 03:00:30 +01:00
until docker exec " $name " [ ! -L " /etc/nginx/certs/ $domain .crt " ] ; do
2020-11-23 03:13:51 +01:00
if [ [ " $( date +%s) " -gt " $timeout " ] ] ; then
2018-11-23 03:17:31 +01:00
echo " Certificate symlink for $domain was not removed under one minute, timing out. "
2018-03-18 18:50:47 +01:00
return 1
fi
2020-11-23 03:13:51 +01:00
sleep 0.1
2018-03-18 18:50:47 +01:00
done
2020-10-22 01:09:18 +02:00
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Symlink to $domain certificate has been removed. "
return 0
2018-03-18 18:50:47 +01:00
}
export -f wait_for_symlink_rm
2017-11-24 23:51:31 +01:00
2018-08-02 22:58:14 +02:00
# Attempt to grab the certificate from domain passed with -d/--domain
# then check if the subject either match or doesn't match the pattern
# passed with either -m/--match or -nm/--no-match
# If domain can't be reached return 1
function check_cert_subj {
while [ [ $# -gt 0 ] ] ; do
local flag = " $1 "
case $flag in
-d| --domain)
local domain = " ${ 2 : ? } "
shift
shift
; ;
-m| --match)
local re = " ${ 2 : ? } "
local match_rc = 0
local no_match_rc = 1
shift
shift
; ;
-n| --no-match)
local re = " ${ 2 : ? } "
local match_rc = 1
local no_match_rc = 0
shift
shift
; ;
*) #Unknown option
shift
; ;
esac
done
2020-10-22 01:09:18 +02:00
if curl -k https://" $domain " & > /dev/null; then
2018-08-02 22:58:14 +02:00
local cert_subject
cert_subject = " $( echo \
| openssl s_client -showcerts -servername " $domain " -connect " $domain :443 " 2>/dev/null \
| openssl x509 -subject -noout) "
else
return 1
fi
if [ [ " $cert_subject " = ~ $re ] ] ; then
return $match_rc
else
return $no_match_rc
fi
}
export -f check_cert_subj
# Wait for a successful https connection to domain passed with -d/--domain then wait
# - until the served certificate isn't the default one (default behavior)
# - until the served certificate is the default one (--default-cert)
# - until the served certificate subject match a string (--subject-match)
2017-11-24 23:51:31 +01:00
function wait_for_conn {
2018-08-02 22:58:14 +02:00
local action
local domain
local string
while [ [ $# -gt 0 ] ] ; do
local flag = " $1 "
case $flag in
-d| --domain)
domain = " ${ 2 : ? } "
shift
shift
; ;
--default-cert)
action = '--match'
shift
; ;
--subject-match)
action = '--match'
string = " $2 "
shift
shift
; ;
*) #Unknown option
shift
; ;
esac
done
2020-11-23 03:13:51 +01:00
local timeout
timeout = " $( date +%s) "
2021-04-05 14:32:04 +02:00
timeout = " $(( timeout + 120 )) "
2018-08-02 22:58:14 +02:00
action = " ${ action :- --no-match } "
string = " ${ string :- letsencrypt -nginx-proxy-companion } "
until check_cert_subj --domain " $domain " " $action " " $string " ; do
2020-11-23 03:13:51 +01:00
if [ [ " $( date +%s) " -gt " $timeout " ] ] ; then
2017-11-24 23:51:31 +01:00
echo " Could not connect to $domain using https under two minutes, timing out. "
return 1
fi
2020-11-23 03:13:51 +01:00
sleep 0.1
2017-11-24 23:51:31 +01:00
done
2020-10-22 01:09:18 +02:00
[ [ " ${ DRY_RUN :- } " = = 1 ] ] && echo " Connection to $domain using https was successful. "
return 0
2017-11-24 23:51:31 +01:00
}
export -f wait_for_conn
2018-06-16 19:50:46 +02:00
2018-08-02 22:58:14 +02:00
2018-06-16 19:50:46 +02:00
# Get the expiration date in unix epoch of domain $1 inside container $2
function get_cert_expiration_epoch {
local domain = " ${ 1 : ? } "
local name = " ${ 2 : ? } "
local cert_expiration
cert_expiration = " $( docker exec " $name " openssl x509 -noout -enddate -in " /etc/nginx/certs/ $domain .crt " ) "
cert_expiration = " $( echo " $cert_expiration " | cut -d "=" -f 2) "
if [ [ " $( uname) " = = 'Darwin' ] ] ; then
cert_expiration = " $( date -j -f "%b %d %T %Y %Z" " $cert_expiration " "+%s" ) "
else
cert_expiration = " $( date -d " $cert_expiration " "+%s" ) "
fi
echo " $cert_expiration "
}
export -f get_cert_expiration_epoch