AkurAI Build
Menu

AkurAI-Build

public

Latest change 3875cdb1f0241e71211550cba0c68f423eb55260 - Add safe mail domain retirement operations by Ólafur Búi Ólafsson

set -euo pipefail
action="$1"
domain="${2:-}"

config_paths=(
  /etc/postfix/vmailbox
  /etc/postfix/vmailbox_domains
  /etc/opendkim/key.table
  /etc/opendkim/KeyTable
  /etc/opendkim/signing.table
  /etc/opendkim/SigningTable
  /etc/opendkim/trusted.hosts
  /etc/opendkim/TrustedHosts
  /etc/dovecot/users
)

if [ "$action" = list ]; then
  signed="$(sudo awk '{print $2}' /etc/opendkim/key.table 2>/dev/null | cut -d: -f1 | sort -u | tr '\n' ',' | sed 's/,$//')"
  hosted="$(sudo awk '{print $1}' /etc/postfix/vmailbox_domains 2>/dev/null | sort -u | tr '\n' ',' | sed 's/,$//')"
  printf 'dkim_signed=%s postfix_hosted=%s\n' "${signed:-none}" "${hosted:-none}"
  while IFS= read -r dir; do
    name="${dir##*/}"
    messages="$(sudo find "$dir" -type f \( -path '*/cur/*' -o -path '*/new/*' \) | wc -l)"
    accounts="$(sudo grep -c "@$name:" /etc/dovecot/users 2>/dev/null || true)"
    printf 'domain %s maildirs=%s messages=%s accounts=%s bytes=%s\n' "$name" \
      "$(sudo find "$dir" -mindepth 1 -maxdepth 1 -type d | wc -l)" "$messages" "$accounts" \
      "$(sudo du -sb "$dir" | cut -f1)"
  done < <(sudo find /var/vmail -mindepth 1 -maxdepth 1 -type d | sort)
  exit 0
fi

[ "$action" = retire ]
[[ "$domain" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$ ]]
escaped="${domain//./\\.}"
domain_re="(^|[.@*[:space:]])${escaped}([:[:space:]/]|$)"

present=0
for path in "${config_paths[@]}"; do
  [ -f "$path" ] || continue
  if sudo grep -Eqi -- "$domain_re" "$path"; then present=1; fi
done
[ -d "/var/vmail/$domain" ] && present=1
[ -d "/etc/opendkim/keys/$domain" ] && present=1
if [ "$present" = 0 ]; then
  printf 'retired=%s already_absent=yes changed=0\n' "$domain"
  exit 0
fi

stamp="$(date -u +%Y%m%dT%H%M%SZ)"
changed=0
for path in "${config_paths[@]}"; do
  [ -f "$path" ] || continue
  sudo grep -Eqi -- "$domain_re" "$path" || continue
  sudo cp -a "$path" "$path.retired-$stamp"
  sudo sed -Ei "/$domain_re/Id" "$path"
  changed=$((changed + 1))
  case "$path" in
  /etc/postfix/vmailbox | /etc/postfix/vmailbox_domains) sudo postmap "$path" ;;
  esac
done

maildir=no
if [ -d "/var/vmail/$domain" ]; then
  sudo rm -rf -- "/var/vmail/$domain"
  maildir=yes
fi
keys=no
if [ -d "/etc/opendkim/keys/$domain" ]; then
  sudo rm -rf -- "/etc/opendkim/keys/$domain"
  keys=yes
fi

sudo postfix check
sudo systemctl reload postfix@- 2>/dev/null || sudo systemctl reload postfix
sudo systemctl restart opendkim

remaining=0
for path in "${config_paths[@]}"; do
  [ -f "$path" ] || continue
  if sudo grep -Eqi -- "$domain_re" "$path"; then remaining=$((remaining + 1)); fi
done
[ "$remaining" = 0 ]
[ ! -e "/var/vmail/$domain" ]
[ ! -e "/etc/opendkim/keys/$domain" ]

for unit in postfix@- dovecot opendkim opendmarc; do
  systemctl is-active --quiet "$unit"
done

historical="$(sudo grep -rli -- "$escaped" /etc/postfix /etc/opendkim /etc/dovecot 2>/dev/null |
  grep -cE '\.(bak|db|retired-[0-9TZ]+)' || true)"
printf 'retired=%s changed=%s maildir_removed=%s dkim_keys_removed=%s refs_remaining=0 historical_copies=%s services=active\n' \
  "$domain" "$changed" "$maildir" "$keys" "$historical"