# Executes a command inside the postfix container. function postfixExec() { k3sRun exec -it deploy/"$postfixKube" -c "$postfixKube" -- "$@" } # Executes a command inside a specific Postfix pod. # $1 (pod): pod name. # $@ (...): command and arguments to execute. function postfixPodExec() { local pod="$1" [[ -n "$pod" ]] || { appError "Pod not specified"; return 1; } shift || true k3sRun exec pod/"$pod" -c "$postfixKube" -- "$@" } # Converts a domain name to a random 64-char postfix ID. function postfixDomain2id() { domainToRandom "$1" 64 } # Reloads the Postfix daemon. function postfixReload() { postfixExec postfix reload } # Creates or updates a Postfix SASL user. # $1 (login): SASL username. # $2 (password): password. function postfixUserSet() { local login="$1" password="$2" [[ -n "$login" ]] || { appError "Login not specified"; return 1; } [[ -n "$password" ]] || { appError "Password not specified"; return 1; } local error runError error postfixExec \ env SASL_LOGIN="$login" SASL_PWD="$password" SASL_DB="/config/sasldb2" SASL_REALM="$postfixDefaultRealm" \ sh -lc 'printf "%s\n" "$SASL_PWD" | saslpasswd2 -p -c -f "$SASL_DB" -u "$SASL_REALM" "$SASL_LOGIN"' || { appError "Failed to create/update SASL user $login: $error" return 1 } } # Deletes a Postfix SASL user. # $1 (login): SASL username. function postfixUserRemove() { local login="$1" [[ -n "$login" ]] || { appError "Login not specified"; return 1; } local error runError error postfixExec saslpasswd2 -d -f "/config/sasldb2" -u "$postfixDefaultRealm" "$login" || { appError "Failed to delete SASL user $login" return 1 } } # Lists all SASL users from the sasldb2 database. function postfixUserList() { local output error run output error postfixExec sasldblistusers2 -f /config/sasldb2 || { appError "$error"; return 1; } awk -F':' ' /^[[:space:]]*$/ { next } { gsub(/[[:space:]]+$/, "", $1); print $1 } ' <<<"$output" } # Rebuilds the senders map from the current SASL user list. function postfixSendersRebuild() { local outFile="$postfixConfigPath/$postfixSendersFile" local saslList error run saslList error postfixUserList || { appError "Failed to get SASL list: $error"; return 1; } local logins logins=$(awk 'NF' <<<"$saslList" | paste -sd ',' -) local tmpOut tmpOut=$(mktemp) [[ -n "$logins" ]] && printf '%s %s\n' "$postfixPostmaster" "$logins" > "$tmpOut" mv -f "$tmpOut" "$outFile" } function postfixConfigRebuild() { local domain="$1" if [[ -n "$domain" ]]; then postfixDomainSet "$domain" fi local error # runError error postfixExec postmap "lmdb:/config/$postfixDomainsFile" || { appError "Failed rebuild $postfixDomainsFile: $error"; return 1; } # runError error postfixExec postmap "lmdb:/config/$postfixAliasesFile" || { appError "Failed rebuild $postfixAliasesFile: $error"; return 1; } runError error postfixExec postmap "lmdb:/config/$postfixSendersFile" || { appError "Failed rebuild $postfixSendersFile: $error"; return 1; } } function postfixDomainSet() { local domain="$1" [[ -n "$domain" ]] || { appError "Domain not specified"; return 1; } domainCheck "$domain" || return 1 local pfxId pfxId=$(postfixDomain2id "$domain") local postfixUser postfixPass postfixUser=$(siteConfigGetOrSet "$domain" "postfixUser" "$pfxId") postfixPass=$(siteConfigGetOrCreate "$domain" "postfixPass") postfixUserSet "$postfixUser" "$postfixPass" || return 1 postfixSendersRebuild postfixConfigRebuild } function postfixDomainRemove() { local domain="$1" [[ -n "$domain" ]] || { appError "Domain not specified"; return 1; } local postfixUser postfixUser=$(siteConfigGet "$domain" "postfixUser") [[ -n "$postfixUser" ]] || { appError "postfixUser not found"; return 1; } postfixUserRemove "$postfixUser" postfixSendersRebuild postfixConfigRebuild } # Reloads the opendkim daemon. function postfixDkimReload() { postfixExec sh -lc "pkill -HUP -f '/usr/sbin/opendkim' 2>/dev/null" || true } # Lists domains that have DKIM key material present. function postfixDkimList() { local f for f in "$postfixDkimPath/keys/"*.txt; do [[ -f "$f" ]] || continue basename -- "$f" .txt done } # Generates a DKIM keypair for a domain and updates SigningTable/KeyTable. # $1 (domain): domain name. function postfixDkimAdd() { local domain="$1" [[ -n "$domain" ]] || { appError "Domain not specified"; return 1; } if [[ ! -s "$postfixDkimPath/keys/$domain.private" || ! -s "$postfixDkimPath/keys/$domain.txt" ]]; then postfixExec sh -lc " set -e mkdir -p /etc/opendkim/keys opendkim-genkey -b 2048 -r -D '/etc/opendkim/keys' -d '$domain' -s '$postfixDkimSelector' mv -f \"/etc/opendkim/keys/$postfixDkimSelector.private\" \"/etc/opendkim/keys/$domain.private\" mv -f \"/etc/opendkim/keys/$postfixDkimSelector.txt\" \"/etc/opendkim/keys/$domain.txt\" chown opendkim:opendkim \"/etc/opendkim/keys/$domain.private\" \"/etc/opendkim/keys/$domain.txt\" || true chmod 0600 \"/etc/opendkim/keys/$domain.private\" chmod 0644 \"/etc/opendkim/keys/$domain.txt\" " || { appError "Failed to generate DKIM keys for $domain"; return 1; } fi local domainEsc="${domain//./\\.}" local selectorEsc="${postfixDkimSelector//./\\.}" sed -i "/^\*@${domainEsc}[[:space:]]/d" "$postfixDkimPath/SigningTable" sed -i "/^${selectorEsc}\._domainkey\.${domainEsc}[[:space:]]/d" "$postfixDkimPath/KeyTable" printf '*@%s %s._domainkey.%s\n' "$domain" "$postfixDkimSelector" "$domain" >> "$postfixDkimPath/SigningTable" printf '%s._domainkey.%s %s:%s:/etc/opendkim/keys/%s.private\n' \ "$postfixDkimSelector" "$domain" "$domain" "$postfixDkimSelector" "$domain" >> "$postfixDkimPath/KeyTable" postfixDkimReload } # Ensures DKIM keys exist for a domain and returns the TXT record. # $1 (domain): domain name. function postfixDkimGet() { local domain="$1" [[ -n "$domain" ]] || { appError "Domain not specified"; return 1; } domainCheck "$domain" || return 1 postfixDkimAdd "$domain" || return 1 cat "$postfixDkimPath/keys/$domain.txt" 2>/dev/null || true } # Removes DKIM table entries for a domain and reloads opendkim. # $1 (domain): domain name. function postfixDkimRemove() { local domain="$1" [[ -n "$domain" ]] || { appError "Domain not specified"; return 1; } local domainEsc="${domain//./\\.}" local selectorEsc="${postfixDkimSelector//./\\.}" sed -i "/^\*@${domainEsc}[[:space:]]/d" "$postfixDkimPath/SigningTable" sed -i "/^${selectorEsc}\._domainkey\.${domainEsc}[[:space:]]/d" "$postfixDkimPath/KeyTable" postfixDkimReload }