docker-mailserver/target/helper_functions.sh
2020-09-08 19:49:19 +02:00

215 lines
5.8 KiB
Bash

#!/bin/bash
# version 0.1.1
#
# Provides varous helpers.
# ? IP and CIDR -------------------------------------------
function _mask_ip_digit()
{
if [[ ${1} -ge 8 ]]
then
MASK=255
elif [[ ${1} -le 0 ]]
then
MASK=0
else
VALUES=(0 128 192 224 240 248 252 254 255)
MASK=${VALUES[${1}]}
fi
local DVAL=${2}
((DVAL&=MASK))
echo "${DVAL}"
}
# Transforms a specific IP with CIDR suffix
# like 1.2.3.4/16 to subnet with cidr suffix
# like 1.2.0.0/16.
# Assumes correct IP and subnet are provided.
function _sanitize_ipv4_to_subnet_cidr()
{
local DIGIT_PREFIX_LENGTH="${1#*/}"
declare -a MASKED_DIGITS
declare -a DIGITS
IFS='.' ; read -r -a DIGITS < <(echo "${1%%/*}") ; unset IFS
for ((i = 0 ; i < 4 ; i++))
do
MASKED_DIGITS[i]=$(_mask_ip_digit "${DIGIT_PREFIX_LENGTH}" "${DIGITS[i]}")
DIGIT_PREFIX_LENGTH=$((DIGIT_PREFIX_LENGTH - 8))
done
echo "${MASKED_DIGITS[0]}.${MASKED_DIGITS[1]}.${MASKED_DIGITS[2]}.${MASKED_DIGITS[3]}/${1#*/}"
}
export -f _sanitize_ipv4_to_subnet_cidr
# ? ACME certs --------------------------------------------
function _extract_certs_from_acme()
{
local KEY
# shellcheck disable=SC2002
KEY=$(cat /etc/letsencrypt/acme.json | python -c "
import sys,json
acme = json.load(sys.stdin)
for key, value in acme.items():
certs = value['Certificates']
for cert in certs:
if 'domain' in cert and 'key' in cert:
if 'main' in cert['domain'] and cert['domain']['main'] == '${1}' or 'sans' in cert['domain'] and '${1}' in cert['domain']['sans']:
print cert['key']
break
")
local CERT
# shellcheck disable=SC2002
CERT=$(cat /etc/letsencrypt/acme.json | python -c "
import sys,json
acme = json.load(sys.stdin)
for key, value in acme.items():
certs = value['Certificates']
for cert in certs:
if 'domain' in cert and 'certificate' in cert:
if 'main' in cert['domain'] and cert['domain']['main'] == '${1}' or 'sans' in cert['domain'] and '${1}' in cert['domain']['sans']:
print cert['certificate']
break
")
if [[ -n "${KEY}${CERT}" ]]
then
mkdir -p "/etc/letsencrypt/live/${HOSTNAME}/"
echo "${KEY}" | base64 -d >/etc/letsencrypt/live/"${HOSTNAME}"/key.pem || exit 1
echo "${CERT}" | base64 -d >/etc/letsencrypt/live/"${HOSTNAME}"/fullchain.pem || exit 1
echo "Cert found in /etc/letsencrypt/acme.json for ${1}"
return 0
else
return 1
fi
}
export -f _extract_certs_from_acme
# ? Notification ------------------------------------------
declare -A DEFAULT_VARS
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}"
function _notify()
{
c_red="\e[0;31m"
c_green="\e[0;32m"
c_brown="\e[0;33m"
c_blue="\e[0;34m"
c_bold="\033[1m"
c_reset="\e[0m"
notification_type=${1}
notification_msg=${2}
notification_format=${3}
msg=""
case "${notification_type}" in
'taskgrp' ) msg="${c_bold}${notification_msg}${c_reset}" ;;
'task' )
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]
then
msg=" ${notification_msg}${c_reset}"
fi
;;
'inf' )
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]
then
msg="${c_green} * ${notification_msg}${c_reset}"
fi
;;
'started' ) msg="${c_green} ${notification_msg}${c_reset}" ;;
'warn' ) msg="${c_brown} Warning ${notification_msg}${c_reset}" ;;
'err' ) msg="${c_blue} Error ${notification_msg}${c_reset}" ;;
'fatal' ) msg="${c_red} Fatal Error: ${notification_msg}${c_reset}" ;;
* ) msg="" ;;
esac
case "${notification_format}" in
'n' ) options="-ne" ;;
* ) options="-e" ;;
esac
[[ -n "${msg}" ]] && echo "${options}" "${msg}"
}
export -f _notify
# ? Relay Host Map ----------------------------------------
# setup /etc/postfix/relayhost_map
# --
# @domain1.com [smtp.mailgun.org]:587
# @domain2.com [smtp.mailgun.org]:587
# @domain3.com [smtp.mailgun.org]:587
function _populate_relayhost_map()
{
echo -n > /etc/postfix/relayhost_map
chown root:root /etc/postfix/relayhost_map
chmod 0600 /etc/postfix/relayhost_map
if [[ -f /tmp/docker-mailserver/postfix-relaymap.cf ]]
then
_notify 'inf' "Adding relay mappings from postfix-relaymap.cf"
# keep lines which are not a comment *and* have a destination.
sed -n '/^\s*[^#[:space:]]\S*\s\+\S/p' /tmp/docker-mailserver/postfix-relaymap.cf >> /etc/postfix/relayhost_map
fi
{
# note: won't detect domains when lhs has spaces (but who does that?!)
sed -n '/^\s*[^#[:space:]]/ s/^[^@|]*@\([^|]\+\)|.*$/\1/p' /tmp/docker-mailserver/postfix-accounts.cf
[ -f /tmp/docker-mailserver/postfix-virtual.cf ] && sed -n '/^\s*[^#[:space:]]/ s/^\s*[^@[:space:]]*@\(\S\+\)\s.*/\1/p' /tmp/docker-mailserver/postfix-virtual.cf
} | while read -r domain
do
# domain not already present *and* not ignored
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map && ! grep -qs -e "^\s*@${domain}\s*$" /tmp/docker-mailserver/postfix-relaymap.cf
then
_notify 'inf' "Adding relay mapping for ${domain}"
echo "@${domain} [${RELAY_HOST}]:${RELAY_PORT}" >> /etc/postfix/relayhost_map
fi
done
}
export -f _populate_relayhost_map
# ? File checksums ----------------------------------------
# file storing the checksums of the monitored files.
# shellcheck disable=SC2034
CHKSUM_FILE=/tmp/docker-mailserver-config-chksum
# Compute checksums of monitored files.
function _monitored_files_checksums()
{
(
cd /tmp/docker-mailserver || exit 1
exec sha512sum 2>/dev/null -- \
postfix-accounts.cf \
postfix-virtual.cf \
postfix-aliases.cf \
dovecot-quotas.cf \
/etc/letsencrypt/acme.json \
"/etc/letsencrypt/live/${HOSTNAME}/key.pem" \
"/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem"
)
}
export -f _monitored_files_checksums