docker-entrypoint.sh 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/bin/bash
  2. ACME_BASE=/var/lib/acme
  3. SSL_EXAMPLE=/var/lib/ssl-example
  4. mkdir -p ${ACME_BASE}/acme/private
  5. restart_containers(){
  6. for container in $*; do
  7. curl -X POST \
  8. --unix-socket /var/run/docker.sock \
  9. "http/containers/${container}/restart"
  10. done
  11. }
  12. if [[ -f ${ACME_BASE}/cert.pem ]]; then
  13. ISSUER=$(openssl x509 -in ${ACME_BASE}/cert.pem -noout -issuer)
  14. if [[ ${ISSUER} != *"Let's Encrypt"* && ${ISSUER} != *"mailcow"* ]]; then
  15. echo "Found certificate with issuer other than mailcow snake-oil CA and Let's Encrypt, skipping ACME client..."
  16. exit 0
  17. else
  18. declare -a SAN_ARRAY_NOW
  19. SAN_NAMES=$(openssl x509 -noout -text -in ${ACME_BASE}/cert.pem | awk '/X509v3 Subject Alternative Name/ {getline;gsub(/ /, "", $0); print}' | tr -d "DNS:")
  20. if [[ ! -z ${SAN_NAMES} ]]; then
  21. IFS=',' read -a SAN_ARRAY_NOW <<< ${SAN_NAMES}
  22. echo "Found Let's Encrypt or mailcow snake-oil CA issued certificate with SANs: ${SAN_ARRAY_NOW[*]}"
  23. fi
  24. fi
  25. else
  26. ISSUER="mailcow"
  27. if [[ -f ${ACME_BASE}/acme/fullchain.pem ]] && [[ -f ${ACME_BASE}/acme/private/privkey.pem ]]; then
  28. cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
  29. cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
  30. else
  31. cp ${SSL_EXAMPLE}/cert.pem ${ACME_BASE}/cert.pem
  32. cp ${SSL_EXAMPLE}/key.pem ${ACME_BASE}/key.pem
  33. fi
  34. fi
  35. [[ ! -f ${ACME_BASE}/dhparams.pem ]] && cp ${SSL_EXAMPLE}/dhparams.pem ${ACME_BASE}/dhparams.pem
  36. while true; do
  37. if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
  38. echo "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..."
  39. exit 0
  40. fi
  41. declare -a SQL_DOMAIN_ARR
  42. declare -a VALIDATED_CONFIG_DOMAINS
  43. declare -a ADDITIONAL_VALIDATED_SAN
  44. IFS=',' read -r -a ADDITIONAL_SAN_ARR <<< "${ADDITIONAL_SAN}"
  45. IPV4=$(curl -4s https://mailcow.email/ip.php)
  46. while read line; do
  47. SQL_DOMAIN_ARR+=("${line}")
  48. done < <(mysql -h mysql-mailcow -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs)
  49. for SQL_DOMAIN in "${SQL_DOMAIN_ARR[@]}"; do
  50. A_CONFIG=$(dig A autoconfig.${SQL_DOMAIN} +short | tail -n 1)
  51. if [[ ! -z ${A_CONFIG} ]]; then
  52. echo "Found A record for autoconfig.${SQL_DOMAIN}: ${A_CONFIG}"
  53. if [[ ${IPV4} == ${A_CONFIG} ]]; then
  54. echo "Confirmed A record autoconfig.${SQL_DOMAIN}"
  55. VALIDATED_CONFIG_DOMAINS+=("autoconfig.${SQL_DOMAIN}")
  56. else
  57. echo "Cannot match Your IP against hostname autoconfig.${SQL_DOMAIN}"
  58. fi
  59. else
  60. echo "No A record for autoconfig.${SQL_DOMAIN} found"
  61. fi
  62. A_DISCOVER=$(dig A autodiscover.${SQL_DOMAIN} +short | tail -n 1)
  63. if [[ ! -z ${A_DISCOVER} ]]; then
  64. echo "Found A record for autodiscover.${SQL_DOMAIN}: ${A_CONFIG}"
  65. if [[ ${IPV4} == ${A_DISCOVER} ]]; then
  66. echo "Confirmed A record autodiscover.${SQL_DOMAIN}"
  67. VALIDATED_CONFIG_DOMAINS+=("autodiscover.${SQL_DOMAIN}")
  68. else
  69. echo "Cannot match Your IP against hostname autodiscover.${SQL_DOMAIN}"
  70. fi
  71. else
  72. echo "No A record for autodiscover.${SQL_DOMAIN} found"
  73. fi
  74. done
  75. for SAN in "${ADDITIONAL_SAN_ARR[@]}"; do
  76. A_SAN=$(dig A ${SAN} +short | tail -n 1)
  77. if [[ ! -z ${A_SAN} ]]; then
  78. echo "Found A record for ${SAN}: ${A_SAN}"
  79. if [[ ${IPV4} == ${A_SAN} ]]; then
  80. echo "Confirmed A record ${SAN}"
  81. ADDITIONAL_VALIDATED_SAN+=("${SAN}")
  82. else
  83. echo "Cannot match Your IP against hostname ${SAN}"
  84. fi
  85. else
  86. echo "No A record for ${SAN} found"
  87. fi
  88. done
  89. ORPHANED_SAN=($(echo ${SAN_ARRAY_NOW[*]} ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} ${MAILCOW_HOSTNAME} | tr ' ' '\n' | sort | uniq -u ))
  90. if [[ ! -z ${ORPHANED_SAN[*]} ]]; then
  91. DATE=$(date +%Y-%m-%d_%H_%M_%S)
  92. echo "Found orphaned SAN(s) ${ORPHANED_SAN[*]} in certificate, moving old files to ${ACME_BASE}/acme/private/${DATE}.bak/"
  93. mkdir -p ${ACME_BASE}/acme/private/${DATE}.bak/
  94. [[ -f ${ACME_BASE}/acme/private/account.key ]] && mv ${ACME_BASE}/acme/private/account.key ${ACME_BASE}/acme/private/${DATE}.bak/
  95. mv ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/acme/private/${DATE}.bak/
  96. mv ${ACME_BASE}/acme/cert.pem ${ACME_BASE}/acme/private/${DATE}.bak/
  97. cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/acme/private/${DATE}.bak/ # Keep key for TLSA 3 1 1 records
  98. fi
  99. acme-client \
  100. -v -e -b -N -n \
  101. -f ${ACME_BASE}/acme/private/account.key \
  102. -k ${ACME_BASE}/acme/private/privkey.pem \
  103. -c ${ACME_BASE}/acme \
  104. ${MAILCOW_HOSTNAME} ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]}
  105. case "$?" in
  106. 0) # new certs
  107. # cp the new certificates and keys
  108. cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
  109. cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
  110. # restart docker containers
  111. restart_containers ${CONTAINERS_RESTART}
  112. ;;
  113. 1) # failure
  114. [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ]] && cp ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ${ACME_BASE}/cert.pem
  115. [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ]] && cp ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ${ACME_BASE}/key.pem
  116. exit 1;;
  117. 2) # no change
  118. ;;
  119. *) # unspecified
  120. [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ]] && cp ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ${ACME_BASE}/cert.pem
  121. [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ]] && cp ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ${ACME_BASE}/key.pem
  122. exit 1;;
  123. esac
  124. echo "ACME certificate validation done. Sleeping for another day."
  125. sleep 86400
  126. done