jina verze
This commit is contained in:
@@ -0,0 +1,391 @@
|
||||
# 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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Rebuilds lmdb maps for domains, aliases, and senders.
|
||||
function postfixPostmapRebuild() {
|
||||
touch "$postfixConfigPath/$postfixDomainsFile" || return 1
|
||||
touch "$postfixConfigPath/$postfixAliasesFile" || return 1
|
||||
touch "$postfixConfigPath/$postfixSendersFile" || return 1
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
# Sets or removes a key/value in a postfix map file.
|
||||
# $1 (file): path to the map file.
|
||||
# $2 (key): map key.
|
||||
# [$3] (value): map value; omit to delete the key.
|
||||
function postfixConfigSet() {
|
||||
local file="$1" key="$2" value="$3"
|
||||
[[ -n "$file" ]] || { appError "File not specified"; return 1; }
|
||||
[[ -n "$key" ]] || { appError "Key not specified"; return 1; }
|
||||
|
||||
value="${value//$'\r'/ }"
|
||||
value="${value//$'\n'/ }"
|
||||
local output error
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
mkdir -p "$(dirname -- "$file")" || { appError "Failed to create folder"; return 1; }
|
||||
install -o root -g root -m 644 /dev/null "$file" || { appError "Failed to create file"; return 1; }
|
||||
else
|
||||
runError error sed -i -E "/^[[:space:]]*${key}[[:space:]]+/d" "$file" || { appError "Error writing to a file: $error"; return 1; }
|
||||
fi
|
||||
|
||||
if [[ -n "$value" ]]; then
|
||||
runShell output error printf "%s\n" "$key $value" ">>" "$file" || { appError "Error writing to a file: $error"; return 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
# Sets or removes a domain entry in the virtual domains map.
|
||||
# $1 (domain): site domain name.
|
||||
# [$2] (value): map value; omit to delete.
|
||||
function postfixVirtualDomainSet() {
|
||||
local domain
|
||||
domain=$(domainPrepare "$1")
|
||||
postfixConfigSet "$postfixConfigPath/$postfixDomainsFile" "$domain" "$2"
|
||||
}
|
||||
|
||||
# Sets or removes an entry in the virtual aliases map.
|
||||
function postfixVirtualAliasSet() {
|
||||
postfixConfigSet "$postfixConfigPath/$postfixAliasesFile" "$@"
|
||||
}
|
||||
|
||||
# Sets or removes a domain entry in the virtual domains map (lmdb-safe variant).
|
||||
# $1 (key): map key.
|
||||
# [$2] (value): map value; omit to delete.
|
||||
function postfixVirtualDomainSet2() {
|
||||
local key="$1"
|
||||
[[ -n "$key" ]] || { appError "Key not specified"; return 1; }
|
||||
local value="$2"
|
||||
local file="$postfixConfigPath/$postfixDomainsFile"
|
||||
|
||||
local escaped
|
||||
escaped=$(printf '%s\n' "$key" | sed 's/[.[\*^$()+?{}|\\/]/\\&/g')
|
||||
sed -i "/^${escaped}[[:space:]]/d" "$file" || { appError "Failed to clean old key in $file"; return 1; }
|
||||
|
||||
if [[ -n "$value" ]]; then
|
||||
printf '%s %s\n' "$key" "$value" >> "$file" || { appError "Failed to append to $file"; return 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
# Adds or removes a (sender, login) pair from the owners file and rebuilds senders map.
|
||||
# $1 (sender): sender address.
|
||||
# $2 (login): SASL login.
|
||||
# [$3] (save): non-empty to add; omit to remove.
|
||||
function postfixSenderOwnerSet() {
|
||||
local sender="$1" login="$2" save="$3"
|
||||
[[ -n "$sender" ]] || { appError "Sender not set"; return 1; }
|
||||
[[ -n "$login" ]] || { appError "Login not set"; return 1; }
|
||||
local ownersFile="$postfixConfigPath/$postfixSendersFile.owners"
|
||||
touch "$ownersFile" || return 1
|
||||
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
awk -v s="$sender" -v l="$login" '!(NF>=2 && $1==s && $2==l)' "$ownersFile" >"$tmp"
|
||||
if [[ -n "$save" ]]; then
|
||||
printf '%s %s\n' "$sender" "$login" >>"$tmp"
|
||||
fi
|
||||
mv -f "$tmp" "$ownersFile"
|
||||
|
||||
postfixSendersRebuild
|
||||
}
|
||||
|
||||
# Sets or removes an entry in the virtual aliases map (lmdb-safe variant).
|
||||
# $1 (key): map key.
|
||||
# [$2] (value): map value; omit to delete.
|
||||
function postfixVirtualAliasSet2() {
|
||||
local key="$1"
|
||||
[[ -n "$key" ]] || { appError "Key not specified"; return 1; }
|
||||
local value="$2"
|
||||
local file="$postfixConfigPath/$postfixAliasesFile"
|
||||
|
||||
local escaped
|
||||
escaped=$(printf '%s\n' "$key" | sed 's/[.[\*^$()+?{}|\\/]/\\&/g')
|
||||
sed -i "/^${escaped}[[:space:]]/d" "$file" || { appError "Failed to clean old key in $file"; return 1; }
|
||||
|
||||
if [[ -n "$value" ]]; then
|
||||
printf '%s %s\n' "$key" "$value" >> "$file" || { appError "Failed to append to $file"; return 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
# Rebuilds the senders map from the .owners file, deduplicating per sender.
|
||||
function postfixSendersRebuild() {
|
||||
local ownersFile="$postfixConfigPath/$postfixSendersFile.owners"
|
||||
local outFile="$postfixConfigPath/$postfixSendersFile"
|
||||
|
||||
touch "$ownersFile" || return 1
|
||||
|
||||
local tmpNorm tmpOut
|
||||
tmpNorm=$(mktemp)
|
||||
tmpOut=$(mktemp)
|
||||
|
||||
awk 'NF>=2 && $1 !~ /^#/ { print $1, $2 }' "$ownersFile" >"$tmpNorm"
|
||||
|
||||
awk '
|
||||
{ s=$1; o=$2; k=s SUBSEP o;
|
||||
if (!seen[k]++) {
|
||||
if (list[s]=="") list[s]=o; else list[s]=list[s] "," o;
|
||||
if (!sseen[s]++) order[++n]=s;
|
||||
}
|
||||
}
|
||||
END { for (i=1; i<=n; i++) { s=order[i]; print s " " list[s]; } }
|
||||
' "$tmpNorm" >"$tmpOut"
|
||||
|
||||
mv -f "$tmpOut" "$outFile"
|
||||
rm -f "$tmpNorm" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Creates, updates, or deletes a Postfix SASL user.
|
||||
# $1 (login): SASL username.
|
||||
# [$2] (password): password; omit to delete the user.
|
||||
function postfixSaslUserSet() {
|
||||
local login="$1" password="$2"
|
||||
[[ -n "$login" ]] || { appError "Login not specified"; return 1; }
|
||||
|
||||
local error
|
||||
if [[ -z "$password" ]]; then
|
||||
runError error postfixExec saslpasswd2 -d -f "/config/sasldb2" -u "$postfixDefaultRealm" "$login" || {
|
||||
appError "Failed to delete SASL user $login"
|
||||
return 1
|
||||
}
|
||||
else
|
||||
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
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# Lists all domains from the virtual domains map file.
|
||||
function postfixDomainList() {
|
||||
local file="$postfixConfigPath/$postfixDomainsFile"
|
||||
[[ -f "$file" ]] || { appError "Domains file not found: $file"; return 1; }
|
||||
|
||||
awk '
|
||||
/^[[:space:]]*$/ { next }
|
||||
/^[[:space:]]*#/ { next }
|
||||
{ print $1 }
|
||||
' "$file" | sort -u
|
||||
}
|
||||
|
||||
# Registers a domain in Postfix: creates SASL user, sets sender ownership, optionally adds alias.
|
||||
# $1 (domain): site domain name.
|
||||
function postfixDomainAdd() {
|
||||
local domain="$1"
|
||||
[[ -n "$domain" ]] || { appError "Domain not specified"; return 1; }
|
||||
|
||||
local pfxId
|
||||
pfxId=$(postfixDomain2id "$domain")
|
||||
local postfixUser postfixPass
|
||||
postfixUser=$(siteConfigGetOrSet "$domain" "postfixUser" "$pfxId")
|
||||
postfixPass=$(siteConfigGetOrCreate "$domain" "postfixPass")
|
||||
|
||||
postfixSenderOwnerSet "$postfixPostmaster" "$postfixUser@$postfixDefaultRealm" "SAVE" || return 1
|
||||
postfixSaslUserSet "$postfixUser" "$postfixPass" || return 1
|
||||
|
||||
local postfixForwardTo
|
||||
postfixForwardTo=$(siteConfigGet "$domain" "postfixForwardTo")
|
||||
if [[ -n "$postfixForwardTo" ]]; then
|
||||
postfixVirtualAliasSet "@$domain" "$postfixForwardTo"
|
||||
postfixVirtualDomainSet "$domain" "OK" || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Removes a domain from Postfix: clears SASL user, sender ownership, alias, and domain entry.
|
||||
# $1 (domain): site domain name.
|
||||
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; }
|
||||
|
||||
postfixVirtualDomainSet "$domain"
|
||||
postfixSenderOwnerSet "$postfixPostmaster" "$postfixUser@$postfixDefaultRealm"
|
||||
postfixSaslUserSet "$postfixUser"
|
||||
postfixVirtualAliasSet "@$domain"
|
||||
}
|
||||
|
||||
# Registers a domain in Postfix and rebuilds lmdb maps.
|
||||
# $1 (domain): site domain name.
|
||||
function postfixConfigRebuild() {
|
||||
local domain error
|
||||
domain=$(domainPrepare "$1")
|
||||
runError error domainCheck "$domain" || { appError "$error"; return 1; }
|
||||
runError error postfixDomainAdd "$domain" || { appError "$error"; return 1; }
|
||||
postfixPostmapRebuild
|
||||
}
|
||||
|
||||
# Reloads the opendkim daemon.
|
||||
function postfixDkimReload() {
|
||||
postfixExec sh -lc "pkill -HUP -f '/usr/sbin/opendkim' 2>/dev/null"
|
||||
}
|
||||
|
||||
# Lists domains that have DKIM key material present.
|
||||
function postfixDkimList() {
|
||||
ls -1 "$postfixDkimPath"/keys/*.txt 2>/dev/null | xargs -n1 basename | sed "s/\.txt$//" || true
|
||||
}
|
||||
|
||||
# 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 ! test -s "$postfixDkimPath/keys/$domain.private" || ! test -s "$postfixDkimPath/keys/$domain.txt"; then
|
||||
postfixExec sh -lc "
|
||||
set -e
|
||||
|
||||
mkdir -p /etc/opendkim/keys
|
||||
touch /etc/opendkim/SigningTable /etc/opendkim/KeyTable
|
||||
|
||||
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 add DKIM for $domain"; return 1; }
|
||||
fi
|
||||
|
||||
sed -i "/^\*@$domain[[:space:]]/d" "$postfixDkimPath/SigningTable"
|
||||
sed -i "/^$postfixDkimSelector\._domainkey\.$domain[[:space:]]/d" "$postfixDkimPath/KeyTable"
|
||||
|
||||
echo "*@$domain $postfixDkimSelector._domainkey.$domain" >> "$postfixDkimPath/SigningTable"
|
||||
echo "$postfixDkimSelector._domainkey.$domain $domain:$postfixDkimSelector:/etc/opendkim/keys/$domain.private" >> "$postfixDkimPath/KeyTable"
|
||||
|
||||
postfixDkimReload
|
||||
}
|
||||
|
||||
# Autocreates DKIM keys if missing and returns the TXT record, or lists available domains.
|
||||
# [$1] (domain): domain name; omit to list all domains.
|
||||
function postfixDkimGet() {
|
||||
local domain="$1"
|
||||
if [[ -z "$domain" ]]; then
|
||||
postfixDkimList
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ ! "$domain" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$ ]]; then
|
||||
appError "Invalid domain: $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
postfixDkimAdd "$domain" || { appError "Failed to create DKIM for $domain"; return 1; }
|
||||
cat "$postfixDkimPath/keys/$domain.txt" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Removes DKIM key material for a domain and reloads opendkim.
|
||||
# $1 (domain): domain name.
|
||||
function postfixDkimRemove() {
|
||||
local domain="$1"
|
||||
[[ -n "$domain" ]] || { appError "Domain not specified"; return 1; }
|
||||
|
||||
sed -i "/^\*@$domain[[:space:]]/d" "$postfixDkimPath/SigningTable"
|
||||
sed -i "/^$postfixDkimSelector\._domainkey\.$domain[[:space:]]/d" "$postfixDkimPath/KeyTable"
|
||||
|
||||
postfixDkimReload
|
||||
}
|
||||
|
||||
# Sends mail through Postfix from a raw RFC822 message file.
|
||||
# $1 (mailFrom): envelope sender address.
|
||||
# $2 (msgFile): path to the RFC822 message file.
|
||||
function postfixMailSendFromFile() {
|
||||
local mailFrom="$1" msgFile="$2"
|
||||
[[ -n "$mailFrom" ]] || { appError "Mail not specified"; return 1; }
|
||||
[[ -n "$msgFile" ]] || { appError "File not specified"; return 1; }
|
||||
|
||||
local output error
|
||||
runShell output error postfixExec sh -lc "sendmail -v -oi -t -f '$mailFrom'" "<" "$msgFile" || {
|
||||
[[ -n "$error" ]] && appError "$error"
|
||||
return 1
|
||||
}
|
||||
printf '%s' "$output"
|
||||
}
|
||||
|
||||
# Lists aliases from the virtual aliases map file.
|
||||
function postfixAliasList() {
|
||||
local file="$postfixConfigPath/$postfixAliasesFile"
|
||||
[[ -f "$file" ]] || { appError "Aliases file not found: $file"; return 1; }
|
||||
|
||||
awk '
|
||||
/^[[:space:]]*$/ { next }
|
||||
/^[[:space:]]*#/ { next }
|
||||
{ print $1 " -> " $2 }
|
||||
' "$file" | sort -u
|
||||
}
|
||||
|
||||
# Lists senders from the senders owners file.
|
||||
function postfixSendersList() {
|
||||
local file="$postfixConfigPath/$postfixSendersFile.owners"
|
||||
[[ -f "$file" ]] || { appError "Senders file not found: $file"; return 1; }
|
||||
|
||||
awk '
|
||||
/^[[:space:]]*$/ { next }
|
||||
/^[[:space:]]*#/ { next }
|
||||
{ print $1 " -> " $2 }
|
||||
' "$file" | sort -u
|
||||
}
|
||||
|
||||
# Lists all SASL users from the sasldb2 database.
|
||||
function postfixSaslUserList() {
|
||||
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"
|
||||
}
|
||||
|
||||
# Returns the value for a key in a postfix map file; exits non-zero if not found.
|
||||
# $1 (file): path to the map file.
|
||||
# $2 (key): map key to look up.
|
||||
function postfixMapHas() {
|
||||
local file="$1" key="$2"
|
||||
[[ -n "$file" ]] || { appError "File not specified"; return 1; }
|
||||
[[ -f "$file" ]] || { appError "File not found"; return 1; }
|
||||
[[ -n "$key" ]] || { appError "Key not specified"; return 1; }
|
||||
|
||||
awk -v k="$key" 'NF>=2 && $1==k {print $2; found=1} END {exit !found}' "$file"
|
||||
}
|
||||
|
||||
# Checks whether (postmaster, login) pair exists in the senders owners file.
|
||||
# $1 (login): SASL login to look up.
|
||||
function postfixSenderOwnerHas() {
|
||||
local login="$1"
|
||||
[[ -n "$login" ]] || { appError "Login not specified"; return 1; }
|
||||
local file="$postfixConfigPath/$postfixSendersFile.owners"
|
||||
[[ -f "$file" ]] || { appError "File not found"; return 1; }
|
||||
|
||||
awk -v s="$postfixPostmaster" -v l="$login" 'NF>=2 && $1==s && $2==l {found=1} END{exit !found}' "$file"
|
||||
}
|
||||
Reference in New Issue
Block a user