Let’s encrypt wildcard cert generate via docker.
Step 1 . Prepare folders
Create if not exist dir for let’s encrypt files
Example
mdkir -p /docker-compose/SWARM/certbot/etc/letsencrypt
Step 2 . Create default credfile for cloudflare usage. Get in cloudflare panel.
Example file
cat /docker-compose/SWARM/certbot/etc/letsencrypt/cred_default.ini
Output
# Cloudflare API credentials used by Certbot
dns_cloudflare_email = a.v.galushkotest@gmail.com
dns_cloudflare_api_key = dsdsdsd2dfdhfudshfkdsfhsudkfhuksfs
Step 3. Create script.
cat /docker-compose/SWARM/certbot/etc/letsencrypt/certbot_wildcard.sh
File content
#!/usr/bin/env bash
docker_image="certbot/dns-cloudflare"
echo 'Usage'
echo 'With copy certs to server "./certbot_wildcard.sh --domain="demostand.linux2be.com" --serverip="" --privatekey="/docker-compose/SWARM/certbot/deploy_keys/id_rsa" --admin_email="a.v.galushko86@gmail.com" --CRED_FILE_default="cred_default.ini" --certbot_dir_data="/docker-compose/SWARM/certbot/etc/letsencrypt"'
for i in "$@"
do
case $i in
-d=*|--domain=*)
ARG_DOMAIN="${i#*=}"
shift # past argument=value
;;
-s=*|--serverip=*)
SERVER_IP_SSH="${i#*=}"
shift # past argument=value
;;
-k=*|--privatekey=*)
SSH_PRIVATE_KEY="${i#*=}"
shift # past argument=value
;;
-m=*|--admin_email=*)
ADMIN_EMAIL="${i#*=}"
shift # past argument=value
;;
-c=*|--CRED_FILE_default=*)
CREADFILE_DEF="${i#*=}"
shift # past argument=value
;;
-d=*|--certbot_dir_data=*)
CERTBOT_DIR_DATA="${i#*=}"
shift # past argument=value
;;
-w=*|--certbot_dir_lib=*)
CERTBOT_DIR_LIB="${i#*=}"
shift # past argument=value
;;
--default)
DEFAULT=YES
shift # past argument with no value
;;
*)
# unknown option
;;
esac
done
if [[ -n $1 ]]; then
echo "Last line of file specified as non-opt/last argument:"
tail -1 $1
exit 1
fi
precheck() {
DOMAIN=$ARG_DOMAIN
if [[ ! -f "${CERTBOT_DIR_DATA}/${CREADFILE_DEF}" ]];then
echo "Error.File ${CERTBOT_DIR_DATA}/${CREADFILE_DEF} not exist. Exit"
exit 1
fi
if [[ ! -s "${CERTBOT_DIR_DATA}/${CREADFILE_DEF}" ]];then
echo "Error.File ${CERTBOT_DIR_DATA}/${CREADFILE_DEF} es empty. Exit"
exit 1
fi
{
echo $ARG_DOMAIN | grep -P "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"
} && {
echo "valid FQDN"
} || {
echo "Invalid FQDN Address"
exit 1
}
if [[ "${CERTBOT_DIR_LIB}" == "" ]];then
CERTBOT_DIR_LIB="/docker-compose/SWARM/web/data/var/www/letsencrypt"
fi
if [[ ! -f "${CERTBOT_DIR_LIB}" ]];then
mkdir -p ${CERTBOT_DIR_LIB}
fi
}
run(){
echo "Domain is ${DOMAIN}"
echo "check for file ${CERTBOT_DIR_DATA}/cred_${DOMAIN}.ini"
files_user=$(whoami)
sudo chown -R $files_user:$files_user ${CERTBOT_DIR_DATA}
if [[ -f "${CERTBOT_DIR_DATA}/cred_${DOMAIN}.ini" ]];then
CRED_FILE="cred_${DOMAIN}.ini"
fi
if [[ ! -f "${CERTBOT_DIR_DATA}/cred_${DOMAIN}.ini" ]];then
echo "Notice. File ${CERTBOT_DIR_DATA}/cred_${DOMAIN}.ini not exist. Using ${CERTBOT_DIR_DATA}/${CREADFILE_DEF}"
CRED_FILE="${CREADFILE_DEF}"
fi
if [[ -f "${CERTBOT_DIR_DATA}/${CRED_FILE}" ]];then
chmod 0400 "${CERTBOT_DIR_DATA}/${CRED_FILE}"
fi
if [[ ! -f "${SSH_PRIVATE_KEY}" ]];then
echo "Private key with path ${SSH_PRIVATE_KEY} not exist. Exit"
exit 1;
fi
docker pull ${docker_image}
echo "Cred file is ${CRED_FILE}"
sleep 15;
certbot_get_wildcard
}
certbot_get_wildcard() {
echo "Generate wildcard for ${DOMAIN} with crefile ${CERTBOT_DIR_DATA}/${CRED_FILE}"
docker run -i --rm --name certbot \
-v "${CERTBOT_DIR_DATA}:/etc/letsencrypt:rw" \
-v "${CERTBOT_DIR_LIB}:/var/lib/letsencrypt:rw" \
${docker_image} \
certonly \
--no-eff-email --email ${ADMIN_EMAIL} \
--manual-public-ip-logging-ok \
--agree-tos \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/${CRED_FILE} \
--dns-cloudflare-propagation-seconds 30 \
--server https://acme-v02.api.letsencrypt.org/directory \
--cert-name ${DOMAIN} \
--force-renewal \
-d "*.${DOMAIN}" -d "${DOMAIN}" \
--preferred-challenges="dns"
}
cerbot_help(){
docker run -it --rm --name certbot ${docker_image} certbot -h all
}
copy_cert_to_server() {
for servers_ssh_ip in $(echo "${SERVER_IP_SSH}" | tr "," "\n")
do
if [[ "${servers_ssh_ip}" != "" ]]; then
echo "copy cert ${DOMAIN} to server ${servers_ssh_ip}"
echo "mkdir -p ${CERTBOT_DIR_DATA}/{archive,live}"
ssh-keyscan ${servers_ssh_ip} >> ~/.ssh/known_hosts
ssh -i ${SSH_PRIVATE_KEY} root@${servers_ssh_ip} "mkdir -p ${CERTBOT_DIR_DATA}/archive;mkdir -p ${CERTBOT_DIR_DATA}/live"
files_user=$(whoami)
sudo chown -R $files_user:$files_user ${CERTBOT_DIR_DATA}
sudo chown -R $files_user:$files_user ${SSH_PRIVATE_KEY}
sudo chmod 0600 ${SSH_PRIVATE_KEY}
rsync -rlvzP -e "ssh -i ${SSH_PRIVATE_KEY} -o StrictHostKeyChecking=no" ${CERTBOT_DIR_DATA}/archive/${DOMAIN} root@${servers_ssh_ip}:/${CERTBOT_DIR_DATA}/archive/
rsync -rlvzP -e "ssh -i ${SSH_PRIVATE_KEY} -o StrictHostKeyChecking=no" ${CERTBOT_DIR_DATA}/live/${DOMAIN} root@${servers_ssh_ip}:/${CERTBOT_DIR_DATA}/live/
else
echo "servers_ssh_ip is empty. Skip copy cert ${DOMAIN}"
fi
done
}
reload_nginx_proxy(){
for servers_ssh_ip in $(echo "${SERVER_IP_SSH}" | tr "," "\n")
do
if [[ "${servers_ssh_ip}" != "" ]]; then
echo "Reload nginx"
TARGET_SERVER_NGINX_PROXY_USER="root"
TARGET_SERVER_NGINX_PROXY_IP="${servers_ssh_ip}"
echo "Получаем список контейнеров nginx-proxy на целевом хосте ${TARGET_SERVER_NGINX_PROXY_IP}"
TARGET_NGINX_PROXY_CONTAINER_NAME=$(ssh -i ${SSH_PRIVATE_KEY} ${TARGET_SERVER_NGINX_PROXY_USER}@${TARGET_SERVER_NGINX_PROXY_IP} docker ps | grep 'app' | grep nginx | grep 'front\|proxy' |
grep -ve 'exporter' | awk '{ print $1 }')
echo "Reload nginx container ${TARGET_NGINX_PROXY_CONTAINER_NAME}"
sudo chown -R $files_user:$files_user ${SSH_PRIVATE_KEY}
sudo chmod 0600 ${SSH_PRIVATE_KEY}
echo "ssh -i ${SSH_PRIVATE_KEY} ${TARGET_SERVER_NGINX_PROXY_USER}@${TARGET_SERVER_NGINX_PROXY_IP} \"docker exec -i ${TARGET_NGINX_PROXY_CONTAINER_NAME} sh -c 'nginx -s reload'\""
ssh -i ${SSH_PRIVATE_KEY} ${TARGET_SERVER_NGINX_PROXY_USER}@${TARGET_SERVER_NGINX_PROXY_IP} "docker exec -i ${TARGET_NGINX_PROXY_CONTAINER_NAME} sh -c 'nginx -s reload'"
else
echo "servers_ssh_ip is empty. Skip copy cert ${DOMAIN}"
fi
done
}
main() {
precheck
run
copy_cert_to_server
reload_nginx_proxy
}
main
Step 4. Run script
Make script execute
cd /docker-compose/SWARM/certbot/etc/letsencrypt
chmod +x /docker-compose/SWARM/certbot/etc/letsencrypt/certbot_wildcard.sh
Run script
./certbot_wildcard.sh --domain="demostand.linux2be.com" --serverip="" --privatekey="/docker-compose/SWARM/certbot/deploy_keys/id_rsa" --admin_email="a.v.galushko86@gmail.com" --CRED_FILE_default="cred_default.ini" --certbot_dir_data="/docker-compose/SWARM/certbot/etc/letsencrypt"
args:
- serverip – ip server, on witch we copy generated certs, after script finished. If empty copy will skipped.
- privatekey – key for access remote server to copy generated certs.
After script end we see output:
./certbot_wildcard.sh --domain="demostand.linux2be.com" --serverip="" --privatekey="/docker-compose/SWARM/certbot/deploy_keys/id_rsa" --admin_email="a.v.galushko86@gmail.com" --CRED_FILE_default="cred_default.ini" --certbot_dir_data="/docker-compose/SWARM/certbot/etc/letsencrypt"
Usage
With copy certs to server "./certbot_wildcard.sh --domain="demostand.linux2be.com" --serverip="" --privatekey="/docker-compose/SWARM/certbot/deploy_keys/id_rsa" --admin_email="a.v.galushko86@gmail.com" --CRED_FILE_default="cred_default.ini" --certbot_dir_data="/docker-compose/SWARM/certbot/etc/letsencrypt"
demostand.linux2be.com
valid FQDN
Domain is demostand.linux2be.com
check for file /docker-compose/SWARM/certbot/etc/letsencrypt/cred_demostand.linux2be.com.ini
Notice. File /docker-compose/SWARM/certbot/etc/letsencrypt/cred_demostand.linux2be.com.ini not exist. Using /docker-compose/SWARM/certbot/etc/letsencrypt/cred_default.ini
Using default tag: latest
latest: Pulling from certbot/dns-cloudflare
e6b0cf9c0882: Already exists
da0e9bf0cc60: Pull complete
c9ea274ed700: Pull complete
9cfac6ba3733: Pull complete
f6a933c9cadc: Pull complete
62ab0cf58d48: Pull complete
ebbfe3fe9f2d: Pull complete
de0c8c114499: Pull complete
61432dd3e660: Pull complete
b95b246059ef: Pull complete
9a8ba197d4b1: Pull complete
c48346ce71d6: Pull complete
00466a6c9355: Pull complete
a83383d63105: Pull complete
Digest: sha256:88bda2dbf14850b9861179b773675e09505487b3c1a769295a04fa8d712326f0
Status: Downloaded newer image for certbot/dns-cloudflare:latest
docker.io/certbot/dns-cloudflare:latest
Cred file is cred_default.ini
Generate wildcard for demostand.linux2be.com with crefile /docker-compose/SWARM/certbot/etc/letsencrypt/cred_default.ini
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-cloudflare, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for demostand.linux2be.com
dns-01 challenge for demostand.linux2be.com
Waiting 30 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/demostand.linux2be.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/demostand.linux2be.com/privkey.pem
Your cert will expire on 2020-04-20. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
Walidate certs
openssl x509 -text -noout -in /docker-compose/SWARM/certbot/etc/letsencrypt/live/demostand.linux2be.com/fullchain.pem | head -n 11
Output
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
04:ba:0e:86:3e:ee:5c:eb:1f:af:6d:5c:76:fd:a7:79:b7:66
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
Validity
Not Before: Jan 21 05:02:27 2020 GMT
Not After : Apr 20 05:02:27 2020 GMT
Subject: CN = *.demostand.linux2be.com