-
Notifications
You must be signed in to change notification settings - Fork 48
Exim multi cert #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Exim multi cert #249
Changes from all commits
2a6405d
7ef2c0e
be34dd9
8d25a93
1afbec5
2e1a141
791483b
8a45777
a731174
fe502e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,288 @@ | ||
| #!/bin/bash | ||
| ########### | ||
| # Vexim, mon-certificates | ||
| # by Markus Gschwendt | ||
| # | ||
| # This script will query the vexim database.domains table | ||
| # and try to create/renew certificates for all domains | ||
| # Certbot will listen on the IP resolved by DNS | ||
| # and the ACME server will use the same IP for validating | ||
| ########### | ||
|
|
||
| # whitespace separated list of subdomain prefixes | ||
| # this prefixes will be added to all domains | ||
| # e.g. with "mail smtp", for the domain example.com | ||
| # we will request the certificates mail.example.com | ||
| # and smtp.example.com | ||
| # additionally certificates for all DNS MX records of example.com | ||
| # will be requested automagically | ||
| # no certificate for example.com itself will be requested | ||
| SUBDOMAIN_LIST_DOVECOT="mail" | ||
| SUBDOMAIN_LIST_EXIM="smtp" | ||
| SUBDOMAIN_LIST_WWW="vexim" | ||
|
|
||
| # if you want to use a dedicated DNS resolver put it here | ||
| DNSSERVER="ns1.runout.at" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you really want to leave your own DNS server as example? |
||
| # type of IP address should be used "AAAA" or "A" | ||
| DNSTYPE="AAAA" | ||
|
Comment on lines
+26
to
+27
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This lacks an explanation about what record we are talking about here. |
||
|
|
||
| # If you're behind a nat or you have some kind of port forwarding | ||
| # you can set a global IP for certbot here. | ||
| # certbot will listen on this IP but the ACME server will use the IP from DNS | ||
| DOMAIN_IP_OVERRIDE="" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Options that are optional should probably be commented out? |
||
|
|
||
| # create a fallback certificate for this domain | ||
| # and put all other domains in the SAN field | ||
| # eg. put the main fqdn of your server here | ||
| #FALLBACK_DOMAIN=$(cat /etc/mailname) | ||
| FALLBACK_DOMAIN="mx.example.com" | ||
|
|
||
| # some options for letsencrypt certificates deployed by certbot | ||
| # you will need the certbot package from stretch-backports | ||
| CERTBOT_OPTS="certonly -n --standalone --preferred-challenges http --rsa-key-size 4096" | ||
| #CERTBOT_OPTS+=" -q" | ||
|
|
||
| # enable for certbot debugging | ||
| #CERTBOT_OPTS+=" -v" | ||
| #CERTBOT_TESTCERT="--test-cert" | ||
| #CERTBOT_DRYRUN="--dry-run" | ||
|
|
||
| # configure vexim DB here | ||
| DB_HOST="mysql-server" | ||
| DB_PORT="3306" | ||
| DB_DB="vexim2-db" | ||
| DB_USER="vexim2-db-user" | ||
| DB_PASS="vexim2-db-password" | ||
| DB_OPT="" | ||
|
|
||
| # set this if a config file for dovecot should be created | ||
| # this will also reload dovecot | ||
| # will be used only if the dovecot binary is executable | ||
| DOVECOT_SSL="/etc/dovecot/conf.d/11-ssl-domlist.conf" | ||
| DOVECOT_BIN="/usr/sbin/dovecot" | ||
|
|
||
| # set this if a config file for dovecot should be created | ||
| # this will also reload dovecot | ||
| # will be used only if the dovecot binary is executable | ||
|
Comment on lines
+64
to
+66
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This says "dovecot", but obviously Exim is meant here. |
||
| EXIM_SSL="/etc/exim4/ssl.conf" | ||
| EXIM_BIN="/usr/sbin/exim4" | ||
|
|
||
| # set this if a config file for nginx/vexim should be created | ||
| # this will also reload nginx | ||
| # do not forget to enable this config with a symlink in sites-enabled | ||
| # will be used only if the nginx binary is executable | ||
| #NGINX_SSL="/etc/nginx/sites-available/vexim" | ||
| #NGINX_BIN="/usr/sbin/nginx" | ||
|
|
||
| # settings for the nginx config file | ||
| # where should nginx pass the request. e.g.: | ||
| # proxy_pass http://mail1; | ||
| # uwsgi_pass 0.0.0.0:8000; | ||
| NGINX_PASS="proxy_pass http://mail1;" | ||
| # a file where additional parameters for proxy configurations will be | ||
| NGINX_PROXYPARAMS="include /etc/nginx/proxy_params;" | ||
|
Comment on lines
+81
to
+83
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| # logging | ||
| LOG_FILE="/var/log/letsencrypt/mon-certificates.log" | ||
|
|
||
|
|
||
| ########## | ||
| # end of config | ||
| ########## | ||
|
|
||
| rm -f ${LOG_FILE}.* | ||
| # keep one oldlogfile | ||
| mv ${LOG_FILE} ${LOG_FILE}.1 2>/dev/null | ||
| echo -e "##### ${0} logfile #####\n##### $(date +"%Y%m%d %H:%M:%S") #####" >> "${LOG_FILE}" | ||
|
|
||
| DB_OPT+=" --skip-column-names" | ||
| DB_MYSQL="mysql -u ${DB_USER} -p${DB_PASS} -h ${DB_HOST} -P ${DB_PORT} ${DB_OPT} ${DB_DB}" | ||
| DB_QUERY_DOMAINS="SELECT \`domain\` FROM \`domains\`" | ||
| if [[ ! -z "${DNSSERVER}" ]]; then | ||
| DNSSERVER="@$(dig +short -t ${DNSTYPE} ${DNSSERVER})" | ||
| fi | ||
| echo -e "DNS RECURSOR: ${DNSSERVER}" >> "${LOG_FILE}" | ||
|
|
||
| if [ ! -z "${DOVECOT_SSL}" ] && [ -x "${DOVECOT_BIN}" ]; then | ||
| mv -f ${DOVECOT_SSL} ${DOVECOT_SSL}.1 2>/dev/null | ||
| SUBDOMAIN_LIST_DOVECOT="$(echo -e "${SUBDOMAIN_LIST_DOVECOT}" | sed "s/\s/\n/g" | sed '/^[[:space:]]*$/d')" | ||
| echo -e "DOVECOT config file: ${DOVECOT_SSL}" >> "${LOG_FILE}" | ||
| else # unset DOVECOT_SSL if no executable binary is found | ||
| DOVECOT_SSL= | ||
| SUBDOMAIN_LIST_DOVECOT="" | ||
| fi | ||
|
|
||
| if [ ! -z "${EXIM_SSL}" ] && [ -x "${EXIM_BIN}" ]; then | ||
| mv -f ${EXIM_SSL} ${DOVECOT_SSL}.1 2>/dev/null | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparent copy-paste error here. |
||
| SUBDOMAIN_LIST_EXIM="$(echo -e "${SUBDOMAIN_LIST_EXIM}" | sed "s/\s/\n/g" | sed '/^[[:space:]]*$/d')" | ||
| echo -e "EXIM config file: ${EXIM_SSL}" >> "${LOG_FILE}" | ||
| else # unset EXIM_SSL if no executable binary is found | ||
| EXIM_SSL= | ||
| SUBDOMAIN_LIST_EXIM="" | ||
| fi | ||
|
|
||
| if [ ! -z "${NGINX_SSL}" ] && [ -x "${NGINX_BIN}" ]; then | ||
| mv -f ${NGINX_SSL} ${NGINX_SSL}.1 2>/dev/null | ||
| SUBDOMAIN_LIST_WWW="$(echo -e "${SUBDOMAIN_LIST_WWW}" | sed "s/\s/\n/g" | sed '/^[[:space:]]*$/d')" | ||
| echo -e "NGINX config file: ${NGNX_SSL}" >> "${LOG_FILE}" | ||
| else # unset NGINX_SSL if no executable binary is found | ||
| NGINX_SSL= | ||
| SUBDOMAIN_LIST_WWW="" | ||
| fi | ||
|
|
||
| SUBDOMAIN_LIST="$(echo -e "${SUBDOMAIN_LIST_DOVECOT}\n${SUBDOMAIN_LIST_EXIM}\n${SUBDOMAIN_LIST_WWW}" | sort -u | sed '/^[[:space:]]*$/d')" | ||
| echo -e "##### mail:\n${SUBDOMAIN_LIST_DOVECOT}\n\n${SUBDOMAIN_LIST_EXIM}\n\n##### www:\n${SUBDOMAIN_LIST_WWW}\n\n##### all:\n${SUBDOMAIN_LIST}\n" >> "${LOG_FILE}.SUBDOMAIN.log" | ||
|
|
||
| DOMAINS_PROCESSED="" | ||
| DOMAINS_WRONGIP="" | ||
| DOMAINS_DUPLICATES="" | ||
|
|
||
|
|
||
| ########## | ||
| # retreive domains from mysql | ||
| ########## | ||
|
|
||
| getdomlist () { | ||
| DOMAIN_LIST=$(${DB_MYSQL} <<< ${DB_QUERY_DOMAINS} | sed '/^[[:space:]]*$/d') | ||
| #echo -e "${DOMAIN_LIST}" | ||
| } | ||
|
|
||
| # get the IP for a domain from DNS or set it to the defined override | ||
| getdomip () { | ||
| DOMAIN_=${1} | ||
| if [[ -z "${DOMAIN_IP_OVERRIDE}" ]]; then | ||
| echo "$(dig +short -t ${DNSTYPE} ${DOMAIN_} ${DNSSERVER} | tail -n1)" | ||
| else | ||
| echo "${DOMAIN_IP_OVERRIDE}" | ||
| fi | ||
| } | ||
|
|
||
| ########## | ||
| # check certificate | ||
| ########## | ||
|
|
||
| check_cert () { | ||
| DOMAIN_=${1} | ||
| # if we did not already process this domain | ||
| if [[ -z "$(echo -e "${DOMAINS_PROCESSED}" | grep "${DOMAIN_}")" ]]; then | ||
| DOMAIN_IP_="$(getdomip ${1})" | ||
|
|
||
| # get cert only if there is a AAAA/A record in dns | ||
| # and this IP is in our network config | ||
| if [[ ! -z ${DOMAIN_IP_} ]] && \ | ||
| [[ ! -z "$(ip -6 addr show | grep "${DOMAIN_IP_}")" ]]; then | ||
| DOMAINS_PROCESSED+="$(echo -e "${DOMAIN_} ${DOMAIN_IP_}")\n" | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n${DOMAIN_} ${DOMAIN_IP_}" >> "${LOG_FILE}" | ||
| certbot ${CERTBOT_OPTS} ${CERTBOT_TESTCERT} ${CERTBOT_DRYRUN} -d ${DOMAIN_} --http-01-address ${DOMAIN_IP_} >> "${LOG_FILE}" 2>&1 | ||
| if [ -e /etc/letsencrypt/live/${DOMAIN_}/fullchain.pem ] && \ | ||
| [ -e /etc/letsencrypt/live/${DOMAIN_}/privkey.pem ]; then | ||
|
|
||
| # DOVECOT | ||
| if [[ ! -z "${DOVECOT_SSL}" ]] && \ | ||
| [[ ! -z "$(echo -e "${SUBDOMAIN_LIST_DOVECOT}" | grep -e "^$(echo "${DOMAIN_}" | sed 's/\..*//')$")" ]]; then | ||
| # write to dovecot config file | ||
| echo -e "local_name ${DOMAIN_} {" >> "${DOVECOT_SSL}.tmp2" | ||
| echo -e " ssl_cert = </etc/letsencrypt/live/${DOMAIN_}/fullchain.pem" >> "${DOVECOT_SSL}.tmp2" | ||
| echo -e " ssl_key = </etc/letsencrypt/live/${DOMAIN_}/privkey.pem" >> "${DOVECOT_SSL}.tmp2" | ||
| echo -e "}\n" >> "${DOVECOT_SSL}.tmp2" | ||
| fi | ||
|
|
||
| # NGINX | ||
| if [[ ! -z "${NGINX_SSL}" ]] && \ | ||
| [[ ! -z "$(echo -e "${SUBDOMAIN_LIST_WEB}" | grep -e "^$(echo "${DOMAIN_}" | sed 's/\..*//')$")" ]]; then | ||
| if [[ -z "$(echo -e "${DOMAIN_IP_}" | grep ':')" ]]; then | ||
| DOMAIN_IP6_="::" | ||
| else | ||
| DOMAIN_IP6_="${DOMAIN_IP_}" | ||
| fi | ||
| # write to nginx config file | ||
| echo -e "server {" >> "${NGINX_SSL}" | ||
| echo -e " listen 443 ssl http2;" >> "${NGINX_SSL}" | ||
| echo -e " listen [${DOMAIN_IP6_}]:443 ssl http2;" >> "${NGINX_SSL}" | ||
| echo -e " server_name ${DOMAIN_};" >> "${NGINX_SSL}" | ||
| echo -e " ssl_certificate /etc/letsencrypt/live/${DOMAIN_}/fullchain.pem;" >> "${NGINX_SSL}" | ||
| echo -e " ssl_certificate_key /etc/letsencrypt/live/${DOMAIN_}/privkey.pem;" >> "${NGINX_SSL}" | ||
| echo -e " location / {" >> "${NGINX_SSL}" | ||
| echo -e " ${NGINX_PASS}" >> "${NGINX_SSL}" | ||
| echo -e " ${NGINX_PROXYPARAMS}" >> "${NGINX_SSL}" | ||
| echo -e " }" >> "${NGINX_SSL}" | ||
| echo -e "}\n" >> "${NGINX_SSL}" | ||
| fi | ||
|
|
||
| fi | ||
| else | ||
| DOMAINS_WRONGIP+="$(echo -e "${DOMAIN_} ${DOMAIN_IP_}")\n" | ||
| fi | ||
|
|
||
| else | ||
| DOMAINS_DUPLICATES+="$(echo -e "${DOMAIN_} ${DOMAIN_IP_}")\n" | ||
| fi | ||
| } | ||
|
|
||
|
|
||
| getdomlist | ||
|
|
||
| # DOMAINS_PROCESSED is used at the end to create a certificate with all domains in the SAN field as a fallback | ||
| # putting the FALLBACK_DOMAIN in here it will be skipped for now | ||
| FALLBACK_IP="$(getdomip ${FALLBACK_DOMAIN})" | ||
|
|
||
| DOMAINS_PROCESSED="${FALLBACK_DOMAIN} ${FALLBACK_IP}\n" | ||
|
|
||
| while read DOMAIN REST; do | ||
|
|
||
| while read SUBDOMAIN REST; do | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n${DOMAIN} SUBDOMAIN: ${SUBDOMAIN}.${DOMAIN%.}" >> "${LOG_FILE}.SUBDOMAIN.log" | ||
| check_cert ${SUBDOMAIN}.${DOMAIN%.} | ||
| done <<< ${SUBDOMAIN_LIST} | ||
|
|
||
| while read DOMAIN_MX REST; do | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n${DOMAIN} MX: ${DOMAIN_MX%.}" >> "${LOG_FILE}.MX.log" | ||
| check_cert ${DOMAIN_MX%.} | ||
| done <<< "$(dig +short -t mx ${DOMAIN} ${DNSSERVER} | cut -d " " -f2 | sed '/^[[:space:]]*\.*$/d')" | ||
|
|
||
| done <<< ${DOMAIN_LIST} | ||
|
|
||
| # create certificate with all domains in the SAN field as fallback | ||
| DOMAIN_SAN="$(echo -e ${DOMAINS_PROCESSED} | cut -d " " -f 1 | sed "/^$/d" | sed "s/^/-d /" | tr '\n' ' ')" | ||
| #echo -e "${DOMAIN_SAN}" | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n${DOMAIN_SAN} ${FALLBACK_IP}" >> "${LOG_FILE}" | ||
| certbot ${CERTBOT_OPTS} ${CERTBOT_TESTCERT} ${CERTBOT_DRYRUN} --cert-name ${FALLBACK_DOMAIN} ${DOMAIN_SAN} --http-01-address ${FALLBACK_IP} >> "${LOG_FILE}" 2>&1 | ||
|
|
||
|
|
||
| # write to dovecot config file and reload dovecot | ||
| if [[ ! -z "${DOVECOT_SSL}" ]]; then | ||
| if [ -e /etc/letsencrypt/live/${FALLBACK_DOMAIN}/fullchain.pem ] && \ | ||
| [ -e /etc/letsencrypt/live/${FALLBACK_DOMAIN}/privkey.pem ]; then | ||
| echo -e "ssl_cert = </etc/letsencrypt/live/${FALLBACK_DOMAIN}/fullchain.pem" >> "${DOVECOT_SSL}.tmp1" | ||
| echo -e "ssl_key = </etc/letsencrypt/live/${FALLBACK_DOMAIN}/privkey.pem\n" >> "${DOVECOT_SSL}.tmp1" | ||
| else | ||
| echo -e "\n##### WARNING #####\n" >> "${LOG_FILE}" | ||
| echo -e " No fallback certificate for Dovecot found!\n" >> "${LOG_FILE}" | ||
| echo -e " /etc/letsencrypt/live/${FALLBACK_DOMAIN}/fullchain.pem\n" >> "${LOG_FILE}" | ||
| fi | ||
| cat "${DOVECOT_SSL}.tmp1" "${DOVECOT_SSL}.tmp2" > "${DOVECOT_SSL}" | ||
| rm -f "${DOVECOT_SSL}.tmp1" | ||
| rm -f "${DOVECOT_SSL}.tmp2" | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n" >> "${LOG_FILE}" | ||
| service dovecot reload > /dev/null 2>&1 | ||
| echo -e "service dovecot reload" >> "${LOG_FILE}" | ||
| fi | ||
|
|
||
| # reload nginx | ||
| if [[ ! -z "${NGINX_SSL}" ]]; then | ||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n" >> "${LOG_FILE}" | ||
| service nginx reload | ||
| echo -e "service nginx reload" >> "${LOG_FILE}" | ||
| fi | ||
|
|
||
| echo -e "\n##### $(date +"%Y%m%d %H:%M:%S") #####\n" >> "${LOG_FILE}" | ||
|
|
||
| # some more logging | ||
| DOMAINS_PROCESSED="$(echo -e "${DOMAINS_PROCESSED}" | sort -u | sed '/^[[:space:]]*$/d')" | ||
| DOMAINS_WRONGIP="$(echo -e "${DOMAINS_WRONGIP}" | sort -u | sed '/^[[:space:]]*$/d')" | ||
| DOMAINS_DUPLICATES="$(echo -e "${DOMAINS_DUPLICATES}" | sort -u | sed '/^[[:space:]]*$/d')" | ||
| echo -e "processed:\n${DOMAINS_PROCESSED}\n" >> "${LOG_FILE}" | ||
| echo -e "no/unconfigured IP:\n${DOMAINS_WRONGIP}\n" >> "${LOG_FILE}" | ||
| echo -e "already processed before:\n${DOMAINS_DUPLICATES}\n" >> "${LOG_FILE}" | ||
|
|
||
| exit 0 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/bin/shwould be more portable.