postfixLabel="[Postfix]" # Generates a self-signed TLS certificate for Postfix if one does not already exist. function cmdPostfixPreparation() { if [[ ! -f "$postfixTlsCrtFile" || ! -f "$postfixTlsKeyFile" ]]; then local crtPath; crtPath=$(dirname "$postfixTlsCrtFile") local tlsCnfFile="$crtPath/openssl.cnf" local cn="${postfixHost:-$postfixDomain}" local sanList="DNS:${cn}" [[ -n "$postfixDomain" ]] && sanList="${sanList},DNS:${postfixDomain}" mkdir -p "$crtPath" || { printDanger "Failed to create folder for certificate"; return 1; } cat >"$tlsCnfFile" < "$varsFile" < "$postfixYaml" \ || { printDanger "$postfixLabel Render failed"; return 1; } printSuccess "$postfixLabel Render completed" } # Initializes Postfix after install: generates DKIM for postfixHost and sets sasldb2 ownership. function cmdPostfixInit() { postfixDkimAdd "$postfixHost" || return 1 local error if ! runError error postfixExec chown postfix:postfix /config/sasldb2; then printDanger "$postfixLabel | $error" fi } # Installs Postfix into Kubernetes. function cmdPostfixInstall() { cmdPostfixPreparation || return 1 cmdPostfixYamlRender || return 1 cmdK3sYamlApplyEx "$postfixYaml" || return 1 } # Uninstalls Postfix Kubernetes resources. function cmdPostfixUninstall() { "$k3sCmd" kubectl delete -f "$postfixYaml" } # Prints the Postfix mail version. function cmdPostfixInfo() { local output error podList ver pid if ! run podList error k3sPodListStatus "$postfixKube"; then printDanger "Get pods: $error" elif [[ -z "$podList" ]]; then printDanger "Pods not found" else while IFS='|' read -r pod phase; do printSection "$pod" if [[ "$phase" != "Running" ]]; then printDot 60 "Phase" "$fontRed$phase$fontReset" else printDot 60 "Phase" "$fontGreen$phase$fontReset" if ! run output error postfixPodExec "$pod" postfix status; then printDot 60 "Postfix status" "$fontRed$labelFail$fontReset" printDanger "$error" else output="${output:-$error}" if [[ $output == *"is running"* ]]; then pid=${output##*PID: } pid=${pid%%[^0-9]*} printDot 60 "Postfix status" "$labelRunning" printDot 60 "pid" "$pid" else printDot 60 "Postfix status" "$fontRed$output$fontReset" fi fi if ! run output error postfixPodExec "$pod" postconf mail_version; then printDot 60 "Postfix mail version" "$fontRed$labelFail$fontReset" printDanger "$error" else ver=$(printf '%s' "$output" | cut -d '=' -f2 | tr -d '\r\n') printDot 60 "Postfix mail version" "$ver" fi fi done < <(awk 'NF' <<< "$podList") fi printRow } # Sets a virtual alias forward-to address for a domain and saves it to site config. # $1 (domain): site domain name. # $2 (mail): forward-to email address. function cmdPostfixVirtualAliasSetEx() { local domain="$1" [[ -n "$domain" ]] || { printDanger "$postfixLabel Domain not specified"; return 1; } local mail="$2" [[ -n "$mail" ]] || { printDanger "$postfixLabel Mail not specified"; return 1; } local domainList output error if ! run domainList error postfixDomainList; then printDanger "$postfixLabel postfixDomainList: $error" return 1 elif ! listContains "$domain" "$domainList"; then printDanger "$postfixLabel Domain not found in postfixDomainList" return 1 elif ! run output error siteConfigSet "$domain" "postfixForwardTo" "$mail"; then printDanger "$postfixLabel siteConfigSet: $error" elif ! run output error postfixVirtualAliasSet "@$domain" "$mail"; then printDanger "$postfixLabel postfixVirtualAliasSet: $error" fi } # Checks MTA host DNS records (A, SPF, PTR, DKIM) against configured values. function cmdPostfixMtaDnsCheck() { local label output error text label="$postfixLabel A record: $postfixHost" if ! run output error dig +short A "$postfixHost" "$postfixResolver"; then printDanger "$label" else text=$(printf '%s' "$output" | paste -sd, - | sed 's/,/ \/ /g') if grep -Fxq -- "$postfixIp" <<<"$output"; then printSuccess "$label | $text" else printWarning "$label | $text" fi fi label="$postfixLabel SPF record: $postfixHost" if ! run output error dig +short TXT "$postfixHost" "$postfixResolver"; then printDanger "$label" elif grep -Eq '^"?v=spf1([[:space:]]|")' <<<"$output"; then printSuccess "$label | $output" else printWarning "$label | $output" fi label="$postfixLabel PTR record: $postfixHost" if ! run output error dig -x "$postfixIp" +short "$postfixResolver"; then printDanger "$label" else text=$(printf '%s' "$output" | paste -sd, - | sed 's/,/ \/ /g') if grep -Fxq -- "$postfixHost." <<<"$output"; then printSuccess "$label | $text" else printWarning "$label | $text" fi fi label="$postfixLabel DKIM record: $postfixHost" if ! run output error dig +short TXT "$postfixDkimSelector._domainkey.$postfixHost"; then printDanger "$label | $error" else local dkimDnsNorm dkimFileRaw dkimFileNorm dkimDnsNorm=$( printf '%s\n' "$output" | tr -d '\n' | sed -e 's/"//g' -e 's/[[:space:]]//g' | sed -n 's/.*p=\([^;]*\).*/\1/p' ) dkimFileRaw=$(postfixDkimGet "$postfixHost") dkimFileNorm=$( printf '%s\n' "$dkimFileRaw" | tr -d '\n' | sed -e 's/[()"]//g' -e 's/[[:space:]]//g' | sed -n 's/.*p=\([^;]*\).*/\1/p' ) if [[ "$dkimDnsNorm" == "$dkimFileNorm" ]]; then printSuccess "$label | OK" else printWarning "$label | DNS : $dkimDnsNorm" printWarning "$label | FILE: $dkimFileNorm" fi fi } # Prints domain/alias/sender/SASL lists; accepts domain/alias/senders/sasl as filter. # [$1] (filter): domain|alias|senders|sasl; omit to print all. function cmdPostfixList() { case "$1" in 'domain') postfixDomainList ;; 'alias') postfixAliasList ;; 'senders') postfixSendersList ;; 'sasl') postfixSaslUserList ;; *) printInfo "$postfixLabel Domains" postfixDomainList printInfo "$postfixLabel Aliases" postfixAliasList printInfo "$postfixLabel Senders" postfixSendersList printInfo "$postfixLabel SASL users" postfixSaslUserList ;; esac }