| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 | #!/bin/bash# Return values / exit codes# 0 = cert created successfully# 1 = cert renewed successfully# 2 = cert not due for renewal# * = errorssource /srv/functions.shCERT_DOMAINS=(${DOMAINS[@]})CERT_DOMAIN=${CERT_DOMAINS[0]}ACME_BASE=/var/lib/acmeTYPE=${1}PREFIX=""# only support rsa certificates for nowif [[ "${TYPE}" != "rsa" ]]; then  log_f "Unknown certificate type '${TYPE}' requested"  exit 5fiDOMAINS_FILE=${ACME_BASE}/${CERT_DOMAIN}/domainsCERT=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}cert.pemSHARED_KEY=${ACME_BASE}/acme/${PREFIX}key.pem  # must already existKEY=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}key.pemCSR=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}acme.csrif [[ -z ${CERT_DOMAINS[*]} ]]; then  log_f "Missing CERT_DOMAINS to obtain a certificate"  exit 3fiif [[ "${LE_STAGING}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then  if [[ ! -z "${DIRECTORY_URL}" ]]; then    log_f "Cannot use DIRECTORY_URL with LE_STAGING=y - ignoring DIRECTORY_URL"  fi  log_f "Using Let's Encrypt staging servers"  DIRECTORY_URL='--directory-url https://acme-staging-v02.api.letsencrypt.org/directory'elif [[ ! -z "${DIRECTORY_URL}" ]]; then  log_f "Using custom directory URL ${DIRECTORY_URL}"  DIRECTORY_URL="--directory-url ${DIRECTORY_URL}"fiif [[ -f ${DOMAINS_FILE} && "$(cat ${DOMAINS_FILE})" ==  "${CERT_DOMAINS[*]}" ]]; then  if [[ ! -f ${CERT} || ! -f "${KEY}" || -f "${ACME_BASE}/force_renew" ]]; then    log_f "Certificate ${CERT} doesn't exist yet or forced renewal - start obtaining"  # Certificate exists and did not change but could be due for renewal (30 days)  elif ! openssl x509 -checkend 2592000 -noout -in ${CERT} > /dev/null; then    log_f "Certificate ${CERT} is due for renewal (< 30 days) - start renewing"  else    log_f "Certificate ${CERT} validation done, neither changed nor due for renewal."    exit 2  fielse  log_f "Certificate ${CERT} missing or changed domains '${CERT_DOMAINS[*]}' - start obtaining"fi# Make backupif [[ -f ${CERT} ]]; then  DATE=$(date +%Y-%m-%d_%H_%M_%S)  BACKUP_DIR=${ACME_BASE}/backups/${CERT_DOMAIN}/${PREFIX}${DATE}  log_f "Creating backups in ${BACKUP_DIR} ..."  mkdir -p ${BACKUP_DIR}/  [[ -f ${DOMAINS_FILE} ]] && cp ${DOMAINS_FILE} ${BACKUP_DIR}/  [[ -f ${CERT} ]] && cp ${CERT} ${BACKUP_DIR}/  [[ -f ${KEY} ]] && cp ${KEY} ${BACKUP_DIR}/  [[ -f ${CSR} ]] && cp ${CSR} ${BACKUP_DIR}/fimkdir -p ${ACME_BASE}/${CERT_DOMAIN}if [[ ! -f ${KEY} ]]; then  log_f "Copying shared private key for this certificate..."  cp ${SHARED_KEY} ${KEY}  chmod 600 ${KEY}fi# Generating CSRprintf "[SAN]\nsubjectAltName=" > /tmp/_SANprintf "DNS:%s," "${CERT_DOMAINS[@]}" >> /tmp/_SANsed -i '$s/,$//' /tmp/_SANopenssl req -new -sha256 -key ${KEY} -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf /tmp/_SAN) > ${CSR}# acme-tiny writes info to stderr and ceritifcate to stdout# The redirects will do the following:# - redirect stdout to temp certificate file# - redirect acme-tiny stderr to stdout (logs to variable ACME_RESPONSE)# - tee stderr to get live output and log to dockerdlog_f "Checking resolver..."until dig letsencrypt.org +time=3 +tries=1 @unbound > /dev/null; do  sleep 2donelog_f "Resolver OK"log_f "Using command acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} --account-key ${ACME_BASE}/acme/account.pem --disable-check --csr ${CSR} --acme-dir /var/www/acme/"ACME_RESPONSE=$(acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} \  --account-key ${ACME_BASE}/acme/account.pem \  --disable-check \  --csr ${CSR} \  --acme-dir /var/www/acme/ 2>&1 > /tmp/_cert.pem | tee /dev/fd/5; exit ${PIPESTATUS[0]})SUCCESS="$?"ACME_RESPONSE_B64=$(echo "${ACME_RESPONSE}" | openssl enc -e -A -base64)log_f "${ACME_RESPONSE_B64}" redis_only b64case "$SUCCESS" in  0) # cert requested    log_f "Deploying certificate ${CERT}..."    # Deploy the new certificate and key    # Moving temp cert to {domain} folder    if verify_hash_match /tmp/_cert.pem ${KEY}; then      RETURN=0  # certificate created      if [[ -f ${CERT} ]]; then        RETURN=1  # certificate renewed      fi      mv -f /tmp/_cert.pem ${CERT}      echo -n ${CERT_DOMAINS[*]} > ${DOMAINS_FILE}      rm /var/www/acme/* 2> /dev/null      log_f "Certificate successfully obtained"      exit ${RETURN}    else      log_f "Certificate was successfully requested, but key and certificate have non-matching hashes, ignoring certificate"      exit 4    fi    ;;  *) # non-zero is non-fun    log_f "Failed to obtain certificate ${CERT} for domains '${CERT_DOMAINS[*]}'"    redis-cli -h redis SET ACME_FAIL_TIME "$(date +%s)"    exit 100${SUCCESS}    ;;esac
 |