1922 lines
44 KiB
Bash
Executable File
1922 lines
44 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# ----------
|
|
# Selective SRS deactivation
|
|
#
|
|
# Restores the Postfix configuration state recorded before selective SRS
|
|
# was activated.
|
|
#
|
|
# The script is intentionally conservative:
|
|
#
|
|
# - DISABLED -> no changes, rc=0
|
|
# - ENABLED -> deactivate after explicit confirmation
|
|
# - INCONSISTENT -> abort without changes
|
|
#
|
|
# Persistent pre-SRS state is read from:
|
|
#
|
|
# /etc/postfix-srs/state.conf
|
|
#
|
|
# The state file and installed SRS components are intentionally retained.
|
|
# ----------
|
|
|
|
|
|
# ----------
|
|
# Script setup
|
|
# ----------
|
|
|
|
working_dir="$(dirname "$(realpath "$0")")"
|
|
|
|
# shellcheck source=helpers/srs-common.sh
|
|
source "${working_dir}/helpers/srs-common.sh"
|
|
|
|
trap 'handle_signal' SIGHUP SIGINT SIGTERM
|
|
|
|
init_srs_runtime
|
|
|
|
|
|
# ----------
|
|
# Deactivation runtime state
|
|
# ----------
|
|
|
|
DEACTIVATION_CHANGES_STARTED=false
|
|
DEACTIVATION_BACKUP_DIR=""
|
|
|
|
POSTFIX_SRS_DROPIN_WAS_PRESENT=false
|
|
SRS_DELIVERY_DROPIN_WAS_PRESENT=false
|
|
|
|
declare -A SERVICE_WAS_ACTIVE
|
|
declare -A SERVICE_WAS_ENABLED
|
|
|
|
|
|
# ----------
|
|
# Local helper functions
|
|
# ----------
|
|
|
|
main_cf_explicit_value() {
|
|
local parameter="$1"
|
|
|
|
awk -v parameter="$parameter" '
|
|
/^[[:space:]]*#/ { next }
|
|
|
|
$0 ~ "^[[:space:]]*" parameter "[[:space:]]*=" {
|
|
line=$0
|
|
}
|
|
|
|
END {
|
|
if (line != "") {
|
|
sub("^[[:space:]]*" parameter "[[:space:]]*=[[:space:]]*", "", line)
|
|
print line
|
|
}
|
|
}
|
|
' "$POSTFIX_MAIN_CF"
|
|
}
|
|
|
|
|
|
main_cf_parameter_is_set() {
|
|
local parameter="$1"
|
|
|
|
grep -Eq \
|
|
"^[[:space:]]*${parameter}[[:space:]]*=" \
|
|
"$POSTFIX_MAIN_CF"
|
|
}
|
|
|
|
|
|
master_service_option_value() {
|
|
local service="$1"
|
|
local option="$2"
|
|
|
|
awk -v service="$service" -v option="$option" '
|
|
/^[[:space:]]*#/ { next }
|
|
|
|
$1 == service && $2 == "inet" {
|
|
in_service=1
|
|
next
|
|
}
|
|
|
|
in_service && $0 !~ /^[[:space:]]/ {
|
|
exit
|
|
}
|
|
|
|
in_service && $1 == "-o" {
|
|
prefix=option "="
|
|
|
|
if (index($2, prefix) == 1) {
|
|
value=$2
|
|
sub("^" option "=", "", value)
|
|
print value
|
|
exit
|
|
}
|
|
}
|
|
' "$POSTFIX_MASTER_CF"
|
|
}
|
|
|
|
|
|
master_srs_forward_present() {
|
|
awk '
|
|
/^[[:space:]]*#/ { next }
|
|
|
|
$1 == "srs-forward" && $2 == "unix" {
|
|
found_service=1
|
|
next
|
|
}
|
|
|
|
found_service && $0 !~ /^[[:space:]]/ {
|
|
exit
|
|
}
|
|
|
|
found_service && $0 ~ /argv=\/usr\/local\/sbin\/srs-forward\.py/ {
|
|
found_argv=1
|
|
}
|
|
|
|
END {
|
|
if (found_service && found_argv)
|
|
exit 0
|
|
exit 1
|
|
}
|
|
' "$POSTFIX_MASTER_CF"
|
|
}
|
|
|
|
|
|
port_is_listening() {
|
|
local host="$1"
|
|
local port="$2"
|
|
|
|
ss -ltnH 2>/dev/null |
|
|
awk -v host="$host" -v port="$port" '
|
|
{
|
|
local_address=$4
|
|
|
|
if (local_address == host ":" port ||
|
|
local_address == "[" host "]:" port) {
|
|
found=1
|
|
exit
|
|
}
|
|
}
|
|
|
|
END {
|
|
exit(found ? 0 : 1)
|
|
}
|
|
'
|
|
}
|
|
|
|
|
|
service_exists() {
|
|
systemctl cat "$1" > /dev/null 2>&1
|
|
}
|
|
|
|
|
|
service_active() {
|
|
systemctl is-active --quiet "$1"
|
|
}
|
|
|
|
|
|
service_enabled_state() {
|
|
systemctl is-enabled "$1" 2>/dev/null || true
|
|
}
|
|
|
|
|
|
service_enabled() {
|
|
case "$(service_enabled_state "$1")" in
|
|
enabled|enabled-runtime)
|
|
return 0
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
postsrsd_srs_domain() {
|
|
awk '
|
|
/^[[:space:]]*#/ { next }
|
|
|
|
/^[[:space:]]*srs-domain[[:space:]]*=/ {
|
|
line=$0
|
|
sub(/^[^=]*=[[:space:]]*/, "", line)
|
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
|
|
gsub(/^"|"$/, "", line)
|
|
print line
|
|
exit
|
|
}
|
|
' "$POSTSRSD_CONF_FILE"
|
|
}
|
|
|
|
|
|
srs_alias_tool() {
|
|
local action="$1"
|
|
python3 "$SRS_ALIAS_HELPER" "$action" \
|
|
--main-cf "$POSTFIX_MAIN_CF" \
|
|
--domain "$_SRS_DOMAIN" \
|
|
--reverse-map "socketmap:inet:${SRS_REVERSE_SOCKET_HOST}:${SRS_REVERSE_SOCKET_PORT}:reverse"
|
|
}
|
|
|
|
state_main_cf_matches() {
|
|
local parameter="$1"
|
|
local was_set="$2"
|
|
local expected_value="$3"
|
|
local current_value=""
|
|
|
|
if "$was_set" ; then
|
|
if ! main_cf_parameter_is_set "$parameter" ; then
|
|
return 1
|
|
fi
|
|
|
|
current_value="$(main_cf_explicit_value "$parameter")"
|
|
[[ "$current_value" == "$expected_value" ]]
|
|
return
|
|
fi
|
|
|
|
! main_cf_parameter_is_set "$parameter"
|
|
}
|
|
|
|
|
|
state_master_option_matches() {
|
|
local service="$1"
|
|
local option="$2"
|
|
local was_set="$3"
|
|
local expected_value="$4"
|
|
local current_value=""
|
|
|
|
current_value="$(master_service_option_value "$service" "$option")"
|
|
|
|
if "$was_set" ; then
|
|
[[ "$current_value" == "$expected_value" ]]
|
|
return
|
|
fi
|
|
|
|
[[ -z "$current_value" ]]
|
|
}
|
|
|
|
|
|
csv_add_value() {
|
|
local current="$1"
|
|
local add="$2"
|
|
local item
|
|
local result=""
|
|
local found=false
|
|
|
|
IFS=',' read -r -a _items <<< "$current"
|
|
|
|
for item in "${_items[@]}" ; do
|
|
item="${item#"${item%%[![:space:]]*}"}"
|
|
item="${item%"${item##*[![:space:]]}"}"
|
|
|
|
[[ -z "$item" ]] && continue
|
|
|
|
if [[ "$item" == "$add" ]]; then
|
|
found=true
|
|
fi
|
|
|
|
if [[ -z "$result" ]]; then
|
|
result="$item"
|
|
else
|
|
result="${result},${item}"
|
|
fi
|
|
done
|
|
|
|
if ! $found ; then
|
|
if [[ -z "$result" ]]; then
|
|
result="$add"
|
|
else
|
|
result="${result},${add}"
|
|
fi
|
|
fi
|
|
|
|
printf '%s\n' "$result"
|
|
}
|
|
|
|
|
|
csv_remove_value() {
|
|
local current="$1"
|
|
local remove="$2"
|
|
local item
|
|
local result=""
|
|
|
|
IFS=',' read -r -a _items <<< "$current"
|
|
|
|
for item in "${_items[@]}" ; do
|
|
item="${item#"${item%%[![:space:]]*}"}"
|
|
item="${item%"${item##*[![:space:]]}"}"
|
|
|
|
[[ -z "$item" ]] && continue
|
|
[[ "$item" == "$remove" ]] && continue
|
|
|
|
if [[ -z "$result" ]]; then
|
|
result="$item"
|
|
else
|
|
result="${result},${item}"
|
|
fi
|
|
done
|
|
|
|
printf '%s\n' "$result"
|
|
}
|
|
|
|
|
|
update_master_service_option() {
|
|
local service="$1"
|
|
local option="$2"
|
|
local value="$3"
|
|
|
|
python3 - \
|
|
"$POSTFIX_MASTER_CF" \
|
|
"$service" \
|
|
"$option" \
|
|
"$value" <<'PY_EOF'
|
|
import os
|
|
import re
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
path, service, option, value = sys.argv[1:5]
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
service_index = None
|
|
|
|
for idx, line in enumerate(lines):
|
|
if not line.strip() or line.lstrip().startswith("#"):
|
|
continue
|
|
|
|
fields = line.split()
|
|
|
|
if len(fields) >= 2 and fields[0] == service and fields[1] == "inet":
|
|
service_index = idx
|
|
break
|
|
|
|
if service_index is None:
|
|
raise SystemExit(2)
|
|
|
|
end_index = len(lines)
|
|
|
|
for idx in range(service_index + 1, len(lines)):
|
|
line = lines[idx]
|
|
|
|
if line.strip() == "":
|
|
end_index = idx
|
|
break
|
|
|
|
if line[0].isspace():
|
|
continue
|
|
|
|
if line.lstrip().startswith("#"):
|
|
end_index = idx
|
|
break
|
|
|
|
end_index = idx
|
|
break
|
|
|
|
option_re = re.compile(
|
|
r"^(?P<indent>\s*)-o\s+" + re.escape(option) + r"=.*(?:\n)?$"
|
|
)
|
|
|
|
option_index = None
|
|
option_indent = " "
|
|
|
|
for idx in range(service_index + 1, end_index):
|
|
match = option_re.match(lines[idx])
|
|
|
|
if match:
|
|
option_index = idx
|
|
option_indent = match.group("indent") or " "
|
|
break
|
|
|
|
if value:
|
|
new_line = f"{option_indent}-o {option}={value}\n"
|
|
|
|
if option_index is not None:
|
|
lines[option_index] = new_line
|
|
else:
|
|
lines.insert(end_index, new_line)
|
|
else:
|
|
if option_index is not None:
|
|
del lines[option_index]
|
|
|
|
st = os.stat(path)
|
|
directory = os.path.dirname(path)
|
|
|
|
fd, tmp_path = tempfile.mkstemp(prefix=".master.cf.srs.", dir=directory)
|
|
|
|
try:
|
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
f.writelines(lines)
|
|
|
|
os.chmod(tmp_path, stat.S_IMODE(st.st_mode))
|
|
os.chown(tmp_path, st.st_uid, st.st_gid)
|
|
os.replace(tmp_path, path)
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.unlink(tmp_path)
|
|
PY_EOF
|
|
}
|
|
|
|
|
|
remove_managed_block() {
|
|
local file="$1"
|
|
local begin_marker="$2"
|
|
local end_marker="$3"
|
|
|
|
python3 - \
|
|
"$file" \
|
|
"$begin_marker" \
|
|
"$end_marker" <<'PY_EOF'
|
|
import os
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
path, begin_marker, end_marker = sys.argv[1:4]
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
begin_matches = [
|
|
idx for idx, line in enumerate(lines)
|
|
if line.rstrip("\n") == begin_marker
|
|
]
|
|
|
|
end_matches = [
|
|
idx for idx, line in enumerate(lines)
|
|
if line.rstrip("\n") == end_marker
|
|
]
|
|
|
|
if len(begin_matches) != 1 or len(end_matches) != 1:
|
|
raise SystemExit(2)
|
|
|
|
begin_idx = begin_matches[0]
|
|
end_idx = end_matches[0]
|
|
|
|
if begin_idx >= end_idx:
|
|
raise SystemExit(3)
|
|
|
|
new_lines = lines[:begin_idx] + lines[end_idx + 1:]
|
|
|
|
# Remove one excessive blank line left at the join point.
|
|
while (
|
|
begin_idx > 0
|
|
and begin_idx < len(new_lines)
|
|
and new_lines[begin_idx - 1].strip() == ""
|
|
and new_lines[begin_idx].strip() == ""
|
|
):
|
|
del new_lines[begin_idx]
|
|
|
|
st = os.stat(path)
|
|
directory = os.path.dirname(path)
|
|
|
|
fd, tmp_path = tempfile.mkstemp(prefix=".srs-managed.", dir=directory)
|
|
|
|
try:
|
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
f.writelines(new_lines)
|
|
|
|
os.chmod(tmp_path, stat.S_IMODE(st.st_mode))
|
|
os.chown(tmp_path, st.st_uid, st.st_gid)
|
|
os.replace(tmp_path, path)
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.unlink(tmp_path)
|
|
PY_EOF
|
|
}
|
|
|
|
|
|
restore_main_cf_parameter() {
|
|
local parameter="$1"
|
|
local was_set="$2"
|
|
local value="$3"
|
|
|
|
python3 - \
|
|
"$POSTFIX_MAIN_CF" \
|
|
"$parameter" \
|
|
"$was_set" \
|
|
"$value" <<'PY_EOF'
|
|
import os
|
|
import re
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
path, parameter, was_set, value = sys.argv[1:5]
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
pattern = re.compile(
|
|
r"^[ \t]*" + re.escape(parameter) + r"[ \t]*="
|
|
)
|
|
|
|
new_lines = []
|
|
skip_continuation = False
|
|
|
|
for line in lines:
|
|
if skip_continuation:
|
|
if line.startswith((" ", "\t")) and not line.lstrip().startswith("#"):
|
|
continue
|
|
skip_continuation = False
|
|
|
|
if line.lstrip().startswith("#"):
|
|
new_lines.append(line)
|
|
continue
|
|
|
|
if pattern.match(line):
|
|
skip_continuation = True
|
|
continue
|
|
|
|
new_lines.append(line)
|
|
|
|
if was_set == "true":
|
|
if new_lines and new_lines[-1].strip() != "":
|
|
new_lines.append("\n")
|
|
|
|
new_lines.append(f"{parameter} = {value}\n")
|
|
|
|
st = os.stat(path)
|
|
directory = os.path.dirname(path)
|
|
|
|
fd, tmp_path = tempfile.mkstemp(prefix=".main.cf.srs.", dir=directory)
|
|
|
|
try:
|
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
f.writelines(new_lines)
|
|
|
|
os.chmod(tmp_path, stat.S_IMODE(st.st_mode))
|
|
os.chown(tmp_path, st.st_uid, st.st_gid)
|
|
os.replace(tmp_path, path)
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.unlink(tmp_path)
|
|
PY_EOF
|
|
}
|
|
|
|
|
|
remove_legacy_srs_forward_service() {
|
|
|
|
python3 - "$POSTFIX_MASTER_CF" <<'PY_EOF'
|
|
import os
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
path = sys.argv[1]
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
start = None
|
|
|
|
for idx, line in enumerate(lines):
|
|
if not line.strip() or line.lstrip().startswith("#"):
|
|
continue
|
|
|
|
fields = line.split()
|
|
|
|
if len(fields) >= 2 and fields[0] == "srs-forward" and fields[1] == "unix":
|
|
if start is not None:
|
|
raise SystemExit(2)
|
|
start = idx
|
|
|
|
if start is None:
|
|
raise SystemExit(3)
|
|
|
|
end = len(lines)
|
|
|
|
for idx in range(start + 1, len(lines)):
|
|
line = lines[idx]
|
|
|
|
if line.strip() == "":
|
|
end = idx
|
|
break
|
|
|
|
if line[0].isspace():
|
|
continue
|
|
|
|
if line.lstrip().startswith("#"):
|
|
end = idx
|
|
break
|
|
|
|
end = idx
|
|
break
|
|
|
|
block = "".join(lines[start:end])
|
|
|
|
required = [
|
|
"flags=Rq null_sender= user=nobody",
|
|
"argv=/usr/local/sbin/srs-forward.py",
|
|
"${sender}",
|
|
"${recipient}",
|
|
"${original_recipient}",
|
|
"${queue_id}",
|
|
"envid=${envid}",
|
|
]
|
|
|
|
for token in required:
|
|
if token not in block:
|
|
raise SystemExit(4)
|
|
|
|
new_lines = lines[:start] + lines[end:]
|
|
|
|
while (
|
|
start > 0
|
|
and start < len(new_lines)
|
|
and new_lines[start - 1].strip() == ""
|
|
and new_lines[start].strip() == ""
|
|
):
|
|
del new_lines[start]
|
|
|
|
st = os.stat(path)
|
|
directory = os.path.dirname(path)
|
|
|
|
fd, tmp_path = tempfile.mkstemp(prefix=".master.cf.srs.", dir=directory)
|
|
|
|
try:
|
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
f.writelines(new_lines)
|
|
|
|
os.chmod(tmp_path, stat.S_IMODE(st.st_mode))
|
|
os.chown(tmp_path, st.st_uid, st.st_gid)
|
|
os.replace(tmp_path, path)
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.unlink(tmp_path)
|
|
PY_EOF
|
|
}
|
|
|
|
|
|
systemd_dropins_match_expected() {
|
|
|
|
[[ -r "$POSTFIX_SRS_DROPIN_FILE" ]] || return 1
|
|
[[ -r "$SRS_DELIVERY_DROPIN_FILE" ]] || return 1
|
|
|
|
grep -Fxq \
|
|
"Wants=${SRS_REVERSE_MAP_SERVICE} ${SRS_DELIVERY_SERVICE}" \
|
|
"$POSTFIX_SRS_DROPIN_FILE" || return 1
|
|
|
|
grep -Fxq \
|
|
"After=${SRS_REVERSE_MAP_SERVICE} ${SRS_DELIVERY_SERVICE}" \
|
|
"$POSTFIX_SRS_DROPIN_FILE" || return 1
|
|
|
|
grep -Fxq \
|
|
"Wants=${POSTSRSD_SERVICE}" \
|
|
"$SRS_DELIVERY_DROPIN_FILE" || return 1
|
|
|
|
grep -Fxq \
|
|
"After=${POSTSRSD_SERVICE}" \
|
|
"$SRS_DELIVERY_DROPIN_FILE" || return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
snapshot_systemd_dropins() {
|
|
|
|
if [[ -f "$POSTFIX_SRS_DROPIN_FILE" ]]; then
|
|
POSTFIX_SRS_DROPIN_WAS_PRESENT=true
|
|
cp -a "$POSTFIX_SRS_DROPIN_FILE" \
|
|
"${DEACTIVATION_BACKUP_DIR}/postfix.service-srs.conf" || return 1
|
|
fi
|
|
|
|
if [[ -f "$SRS_DELIVERY_DROPIN_FILE" ]]; then
|
|
SRS_DELIVERY_DROPIN_WAS_PRESENT=true
|
|
cp -a "$SRS_DELIVERY_DROPIN_FILE" \
|
|
"${DEACTIVATION_BACKUP_DIR}/postfix@srs-delivery.service-srs.conf" || return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
remove_srs_systemd_dropins() {
|
|
|
|
rm -f \
|
|
"$POSTFIX_SRS_DROPIN_FILE" \
|
|
"$SRS_DELIVERY_DROPIN_FILE" || return 1
|
|
|
|
systemctl daemon-reload || return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
restore_systemd_dropins() {
|
|
local restore_failed=false
|
|
|
|
if $POSTFIX_SRS_DROPIN_WAS_PRESENT ; then
|
|
mkdir -p "$POSTFIX_SRS_DROPIN_DIR" || restore_failed=true
|
|
|
|
cp -a \
|
|
"${DEACTIVATION_BACKUP_DIR}/postfix.service-srs.conf" \
|
|
"$POSTFIX_SRS_DROPIN_FILE" || restore_failed=true
|
|
else
|
|
rm -f "$POSTFIX_SRS_DROPIN_FILE" || restore_failed=true
|
|
fi
|
|
|
|
if $SRS_DELIVERY_DROPIN_WAS_PRESENT ; then
|
|
mkdir -p "$SRS_DELIVERY_DROPIN_DIR" || restore_failed=true
|
|
|
|
cp -a \
|
|
"${DEACTIVATION_BACKUP_DIR}/postfix@srs-delivery.service-srs.conf" \
|
|
"$SRS_DELIVERY_DROPIN_FILE" || restore_failed=true
|
|
else
|
|
rm -f "$SRS_DELIVERY_DROPIN_FILE" || restore_failed=true
|
|
fi
|
|
|
|
systemctl daemon-reload || restore_failed=true
|
|
|
|
! $restore_failed
|
|
}
|
|
|
|
|
|
snapshot_service_states() {
|
|
local service
|
|
|
|
for service in \
|
|
"$POSTSRSD_SERVICE" \
|
|
"$SRS_REVERSE_MAP_SERVICE" \
|
|
"$SRS_DELIVERY_SERVICE"
|
|
do
|
|
if service_active "$service" ; then
|
|
SERVICE_WAS_ACTIVE["$service"]="true"
|
|
else
|
|
SERVICE_WAS_ACTIVE["$service"]="false"
|
|
fi
|
|
|
|
SERVICE_WAS_ENABLED["$service"]="$(service_enabled_state "$service")"
|
|
done
|
|
}
|
|
|
|
|
|
restore_service_states() {
|
|
local service
|
|
local restore_failed=false
|
|
local enabled_state
|
|
|
|
for service in \
|
|
"$POSTSRSD_SERVICE" \
|
|
"$SRS_REVERSE_MAP_SERVICE" \
|
|
"$SRS_DELIVERY_SERVICE"
|
|
do
|
|
enabled_state="${SERVICE_WAS_ENABLED[$service]:-}"
|
|
|
|
case "$enabled_state" in
|
|
enabled|enabled-runtime)
|
|
systemctl enable "$service" > /dev/null 2>> "$log_file" || restore_failed=true
|
|
;;
|
|
disabled)
|
|
systemctl disable "$service" > /dev/null 2>> "$log_file" || restore_failed=true
|
|
;;
|
|
esac
|
|
|
|
if [[ "${SERVICE_WAS_ACTIVE[$service]:-false}" == "true" ]]; then
|
|
systemctl start "$service" > /dev/null 2>> "$log_file" || restore_failed=true
|
|
else
|
|
systemctl stop "$service" > /dev/null 2>> "$log_file" || restore_failed=true
|
|
fi
|
|
done
|
|
|
|
! $restore_failed
|
|
}
|
|
|
|
|
|
srs_delivery_queue_is_empty() {
|
|
local queue_data
|
|
|
|
queue_data="$(
|
|
postqueue -c "$POSTFIX_SRS_DELIVERY_DIR" -j 2>> "$log_file"
|
|
)" || return 2
|
|
|
|
[[ -z "${queue_data//[[:space:]]/}" ]]
|
|
}
|
|
|
|
|
|
rollback_deactivation() {
|
|
local rollback_failed=false
|
|
|
|
if ! $DEACTIVATION_CHANGES_STARTED ; then
|
|
return 0
|
|
fi
|
|
|
|
blank_line
|
|
warn "SRS deactivation failed. Trying to restore the previous state."
|
|
|
|
echononl "Restore '${POSTFIX_MAIN_CF}'.."
|
|
if cp -a "${DEACTIVATION_BACKUP_DIR}/main.cf" "$POSTFIX_MAIN_CF" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
|
|
echononl "Restore '${POSTFIX_MASTER_CF}'.."
|
|
if cp -a "${DEACTIVATION_BACKUP_DIR}/master.cf" "$POSTFIX_MASTER_CF" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
|
|
echononl "Restore previous systemd SRS drop-ins.."
|
|
if restore_systemd_dropins ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
|
|
echononl "Restore previous SRS service states.."
|
|
if restore_service_states ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
|
|
echononl "Check restored Postfix configuration.."
|
|
if postfix check > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
|
|
if systemctl is-active --quiet "$POSTFIX_SERVICE" ; then
|
|
echononl "Reload restored Postfix configuration.."
|
|
|
|
if systemctl reload "$POSTFIX_SERVICE" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
rollback_failed=true
|
|
fi
|
|
fi
|
|
|
|
if $rollback_failed ; then
|
|
error "Automatic rollback could not fully restore the previous state. Manual intervention is required."
|
|
return 1
|
|
fi
|
|
|
|
info "Previous enabled SRS state was successfully restored."
|
|
return 0
|
|
}
|
|
|
|
|
|
fatal_with_deactivation_rollback() {
|
|
local message="$1"
|
|
|
|
if $DEACTIVATION_CHANGES_STARTED ; then
|
|
if rollback_deactivation ; then
|
|
DEACTIVATION_CHANGES_STARTED=false
|
|
fatal "${message} The previous state was successfully restored."
|
|
else
|
|
fatal "${message} Automatic rollback could not fully restore the previous state."
|
|
fi
|
|
fi
|
|
|
|
fatal "$message"
|
|
}
|
|
|
|
|
|
handle_signal() {
|
|
trap - SIGHUP SIGINT SIGTERM
|
|
|
|
warn "Deactivation interrupted by signal."
|
|
|
|
if $DEACTIVATION_CHANGES_STARTED ; then
|
|
rollback_deactivation
|
|
fi
|
|
|
|
clean_up 1
|
|
}
|
|
|
|
|
|
# ----------
|
|
# Header
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[1m----------\033[m"
|
|
echo -e "\033[32m\033[1mRunning script \033[m\033[1m${script_name}\033[32m .. \033[m"
|
|
echo -e "\033[1m----------\033[m"
|
|
blank_line
|
|
|
|
|
|
# ----------
|
|
# Basic checks
|
|
# ----------
|
|
|
|
check_root
|
|
|
|
check_required_commands \
|
|
awk \
|
|
cp \
|
|
cut \
|
|
grep \
|
|
hostname \
|
|
mkdir \
|
|
mktemp \
|
|
mv \
|
|
postfix \
|
|
postqueue \
|
|
python3 \
|
|
realpath \
|
|
rm \
|
|
sed \
|
|
ss \
|
|
stat \
|
|
systemctl
|
|
|
|
load_srs_config
|
|
load_srs_state
|
|
[[ -f "$SRS_ALIAS_HELPER" && ! -L "$SRS_ALIAS_HELPER" ]] ||
|
|
fatal "Missing regular SRS alias helper: ${SRS_ALIAS_HELPER}"
|
|
|
|
|
|
# ----------
|
|
# Expected active master.cf values
|
|
# ----------
|
|
|
|
smtp_pre_value=""
|
|
amavis_pre_value=""
|
|
|
|
if $_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS_SET ; then
|
|
smtp_pre_value="$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS"
|
|
fi
|
|
|
|
if $_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS_SET ; then
|
|
amavis_pre_value="$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS"
|
|
fi
|
|
|
|
smtp_active_value="$(csv_add_value "$smtp_pre_value" "no_address_mappings")"
|
|
amavis_active_value="$(csv_remove_value "$amavis_pre_value" "no_address_mappings")"
|
|
|
|
|
|
# ----------
|
|
# Check installation
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mCheck selective SRS installation..\033[m"
|
|
blank_line
|
|
|
|
installation_ok=true
|
|
|
|
for _file in \
|
|
"$POSTFIX_MAIN_CF" \
|
|
"$POSTFIX_MASTER_CF" \
|
|
"$POSTSRSD_CONF_FILE" \
|
|
"$POSTSRSD_SECRET_FILE" \
|
|
"${POSTFIX_SRS_DELIVERY_DIR}/main.cf" \
|
|
"${POSTFIX_SRS_DELIVERY_DIR}/master.cf" \
|
|
"${POSTFIX_SRS_DELIVERY_DIR}/transport" \
|
|
"${POSTFIX_SRS_DELIVERY_DIR}/transport.db"
|
|
do
|
|
echononl "Check file '${_file}'.."
|
|
|
|
if [[ -r "$_file" ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
installation_ok=false
|
|
fi
|
|
done
|
|
|
|
for _script in \
|
|
"$SRS_ALIAS_DECISION_SCRIPT" \
|
|
"$SRS_FORWARD_SCRIPT" \
|
|
"$SRS_REVERSE_MAP_SCRIPT" \
|
|
"$SRS_REVERSE_POLICY_SCRIPT"
|
|
do
|
|
echononl "Check executable '${_script}'.."
|
|
|
|
if [[ -x "$_script" ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
installation_ok=false
|
|
fi
|
|
done
|
|
|
|
echononl "Check configured PostSRSd SRS domain.."
|
|
if [[ "$(postsrsd_srs_domain)" == "$_SRS_DOMAIN" ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
installation_ok=false
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Check services and listeners
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mCheck selective SRS runtime..\033[m"
|
|
blank_line
|
|
|
|
services_exist=true
|
|
services_active=true
|
|
services_enabled=true
|
|
|
|
for _service in \
|
|
"$POSTSRSD_SERVICE" \
|
|
"$SRS_REVERSE_MAP_SERVICE" \
|
|
"$SRS_DELIVERY_SERVICE"
|
|
do
|
|
echononl "Check systemd service '${_service}' exists.."
|
|
|
|
if service_exists "$_service" ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
services_exist=false
|
|
services_active=false
|
|
services_enabled=false
|
|
continue
|
|
fi
|
|
|
|
echononl "Check systemd service '${_service}' is active.."
|
|
if service_active "$_service" ; then
|
|
echo_ok
|
|
else
|
|
echo_inactive
|
|
services_active=false
|
|
fi
|
|
|
|
if service_enabled "$_service" ; then
|
|
echononl "Check systemd service '${_service}' enable state (enabled).."
|
|
echo_ok
|
|
else
|
|
echononl "Check systemd service '${_service}' enable state (disabled).."
|
|
echo_ok
|
|
services_enabled=false
|
|
fi
|
|
done
|
|
|
|
listeners_active=true
|
|
|
|
while IFS='|' read -r _label _host _port ; do
|
|
if port_is_listening "$_host" "$_port" ; then
|
|
echononl "Check ${_label} state on ${_host}:${_port} (listening).."
|
|
echo_ok
|
|
else
|
|
echononl "Check ${_label} state on ${_host}:${_port} (inactive).."
|
|
echo_ok
|
|
listeners_active=false
|
|
fi
|
|
done <<EOF
|
|
PostSRSd socketmap|${POSTSRSD_SOCKET_HOST}|${POSTSRSD_SOCKET_PORT}
|
|
SRS reverse socketmap|${SRS_REVERSE_SOCKET_HOST}|${SRS_REVERSE_SOCKET_PORT}
|
|
SRS reinjection listener|${SRS_REINJECT_HOST}|${SRS_REINJECT_PORT}
|
|
SRS reverse listener|${SRS_REVERSE_HOST}|${SRS_REVERSE_PORT}
|
|
EOF
|
|
|
|
if systemd_dropins_match_expected ; then
|
|
echononl "Check systemd SRS dependency drop-ins state (present).."
|
|
echo_ok
|
|
systemd_dropins_active=true
|
|
else
|
|
echononl "Check systemd SRS dependency drop-ins state (absent).."
|
|
echo_ok
|
|
systemd_dropins_active=false
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Check Postfix integration
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mCheck Postfix SRS integration..\033[m"
|
|
blank_line
|
|
|
|
main_block_status="invalid"
|
|
master_block_status="invalid"
|
|
|
|
check_managed_block_markers \
|
|
"$POSTFIX_MAIN_CF" \
|
|
"$SRS_MAIN_CF_BEGIN_MARKER" \
|
|
"$SRS_MAIN_CF_END_MARKER"
|
|
|
|
case "$?" in
|
|
0) main_block_status="present" ;;
|
|
1) main_block_status="absent" ;;
|
|
*) main_block_status="invalid" ;;
|
|
esac
|
|
|
|
echononl "Validate managed SRS block state in '${POSTFIX_MAIN_CF}'.."
|
|
case "$main_block_status" in
|
|
present) echo_ok ;;
|
|
absent) echo_ok ;;
|
|
*) echo_failed ;;
|
|
esac
|
|
|
|
check_managed_block_markers \
|
|
"$POSTFIX_MASTER_CF" \
|
|
"$SRS_MASTER_CF_BEGIN_MARKER" \
|
|
"$SRS_MASTER_CF_END_MARKER"
|
|
|
|
case "$?" in
|
|
0) master_block_status="present" ;;
|
|
1) master_block_status="absent" ;;
|
|
*) master_block_status="invalid" ;;
|
|
esac
|
|
|
|
echononl "Validate managed SRS block state in '${POSTFIX_MASTER_CF}'.."
|
|
case "$master_block_status" in
|
|
present) echo_ok ;;
|
|
absent) echo_ok ;;
|
|
*) echo_failed ;;
|
|
esac
|
|
|
|
main_srs_values_ok=true
|
|
|
|
echononl "Validate 'default_transport' state.."
|
|
if [[ "$(main_cf_explicit_value default_transport)" == "srs-forward" ]]; then
|
|
echo_ok
|
|
elif state_main_cf_matches \
|
|
default_transport \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT_SET" \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT"
|
|
then
|
|
echo_ok
|
|
main_srs_values_ok=false
|
|
else
|
|
echo_differs
|
|
main_srs_values_ok=false
|
|
fi
|
|
|
|
echononl "Validate 'srs-forward_destination_recipient_limit' state.."
|
|
if [[ "$(main_cf_explicit_value srs-forward_destination_recipient_limit)" == "1" ]]; then
|
|
echo_ok
|
|
elif state_main_cf_matches \
|
|
srs-forward_destination_recipient_limit \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT_SET" \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT"
|
|
then
|
|
echo_ok
|
|
main_srs_values_ok=false
|
|
else
|
|
echo_differs
|
|
main_srs_values_ok=false
|
|
fi
|
|
|
|
echononl "Validate 'recipient_canonical_maps' state.."
|
|
if [[ "$(main_cf_explicit_value recipient_canonical_maps)" == \
|
|
"socketmap:inet:${SRS_REVERSE_SOCKET_HOST}:${SRS_REVERSE_SOCKET_PORT}:reverse" ]]
|
|
then
|
|
echo_ok
|
|
elif state_main_cf_matches \
|
|
recipient_canonical_maps \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS_SET" \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS"
|
|
then
|
|
echo_ok
|
|
main_srs_values_ok=false
|
|
else
|
|
echo_differs
|
|
main_srs_values_ok=false
|
|
fi
|
|
|
|
|
|
master_srs_forward_ok=false
|
|
smtp_override_ok=false
|
|
amavis_override_ok=false
|
|
|
|
echononl "Validate 'srs-forward' pipe service state.."
|
|
if master_srs_forward_present ; then
|
|
echo_ok
|
|
master_srs_forward_ok=true
|
|
else
|
|
# Absent is valid in the stored pre-SRS state.
|
|
echo_ok
|
|
fi
|
|
|
|
echononl "Validate port 25 receive_override_options state.."
|
|
if [[ "$(master_service_option_value smtp receive_override_options)" == "$smtp_active_value" ]]; then
|
|
echo_ok
|
|
smtp_override_ok=true
|
|
elif state_master_option_matches \
|
|
smtp \
|
|
receive_override_options \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS"
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_differs
|
|
fi
|
|
|
|
echononl "Validate AmaViS reinjection receive_override_options state.."
|
|
if [[ "$(master_service_option_value localhost:10025 receive_override_options)" == "$amavis_active_value" ]]
|
|
then
|
|
echo_ok
|
|
amavis_override_ok=true
|
|
elif state_master_option_matches \
|
|
localhost:10025 \
|
|
receive_override_options \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS"
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_differs
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Validate against active and pre-SRS states
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mValidate current Postfix configuration against active/pre-SRS states..\033[m"
|
|
blank_line
|
|
|
|
pre_srs_main_ok=true
|
|
pre_srs_master_ok=true
|
|
|
|
echononl "Validate 'default_transport' transition.."
|
|
if state_main_cf_matches \
|
|
default_transport \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT_SET" \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT"
|
|
then
|
|
echo_ok
|
|
elif [[ "$(main_cf_explicit_value default_transport)" == "srs-forward" ]]; then
|
|
echo_ok
|
|
pre_srs_main_ok=false
|
|
else
|
|
echo_differs
|
|
pre_srs_main_ok=false
|
|
fi
|
|
|
|
echononl "Validate 'recipient_canonical_maps' transition.."
|
|
if state_main_cf_matches \
|
|
recipient_canonical_maps \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS_SET" \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS"
|
|
then
|
|
echo_ok
|
|
elif [[ "$(main_cf_explicit_value recipient_canonical_maps)" == \
|
|
"socketmap:inet:${SRS_REVERSE_SOCKET_HOST}:${SRS_REVERSE_SOCKET_PORT}:reverse" ]]
|
|
then
|
|
echo_ok
|
|
pre_srs_main_ok=false
|
|
else
|
|
echo_differs
|
|
pre_srs_main_ok=false
|
|
fi
|
|
|
|
echononl "Validate recipient limit transition.."
|
|
if state_main_cf_matches \
|
|
srs-forward_destination_recipient_limit \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT_SET" \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT"
|
|
then
|
|
echo_ok
|
|
elif [[ "$(main_cf_explicit_value srs-forward_destination_recipient_limit)" == "1" ]]; then
|
|
echo_ok
|
|
pre_srs_main_ok=false
|
|
else
|
|
echo_differs
|
|
pre_srs_main_ok=false
|
|
fi
|
|
|
|
echononl "Validate port 25 receive_override_options transition.."
|
|
if state_master_option_matches \
|
|
smtp \
|
|
receive_override_options \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS"
|
|
then
|
|
echo_ok
|
|
elif [[ "$(master_service_option_value smtp receive_override_options)" == "$smtp_active_value" ]]; then
|
|
echo_ok
|
|
pre_srs_master_ok=false
|
|
else
|
|
echo_differs
|
|
pre_srs_master_ok=false
|
|
fi
|
|
|
|
echononl "Validate AmaViS receive_override_options transition.."
|
|
if state_master_option_matches \
|
|
localhost:10025 \
|
|
receive_override_options \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS"
|
|
then
|
|
echo_ok
|
|
elif [[ "$(master_service_option_value localhost:10025 receive_override_options)" == "$amavis_active_value" ]]
|
|
then
|
|
echo_ok
|
|
pre_srs_master_ok=false
|
|
else
|
|
echo_differs
|
|
pre_srs_master_ok=false
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Determine state
|
|
# ----------
|
|
|
|
srs_integration_enabled=false
|
|
srs_integration_disabled=false
|
|
runtime_enabled=false
|
|
runtime_disabled=false
|
|
srs_state="inconsistent"
|
|
|
|
if [[ "$main_block_status" == "present" ]] &&
|
|
[[ "$master_block_status" != "invalid" ]] &&
|
|
$main_srs_values_ok &&
|
|
$master_srs_forward_ok &&
|
|
$smtp_override_ok &&
|
|
$amavis_override_ok
|
|
then
|
|
srs_integration_enabled=true
|
|
fi
|
|
|
|
if [[ "$main_block_status" == "absent" ]] &&
|
|
[[ "$master_block_status" == "absent" ]] &&
|
|
$pre_srs_main_ok &&
|
|
$pre_srs_master_ok &&
|
|
! $master_srs_forward_ok
|
|
then
|
|
srs_integration_disabled=true
|
|
fi
|
|
|
|
if $services_active &&
|
|
$services_enabled &&
|
|
$listeners_active &&
|
|
$systemd_dropins_active
|
|
then
|
|
runtime_enabled=true
|
|
fi
|
|
|
|
if ! $services_active &&
|
|
! $services_enabled &&
|
|
! $listeners_active &&
|
|
! $systemd_dropins_active
|
|
then
|
|
runtime_disabled=true
|
|
fi
|
|
|
|
if $srs_integration_enabled && $runtime_enabled ; then
|
|
srs_state="enabled"
|
|
elif $srs_integration_disabled && $runtime_disabled ; then
|
|
srs_state="disabled"
|
|
else
|
|
srs_state="inconsistent"
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Summary
|
|
# ----------
|
|
|
|
master_block_summary="$master_block_status"
|
|
|
|
if [[ "$master_block_status" == "absent" && "$srs_state" == "enabled" ]]; then
|
|
master_block_summary="absent (legacy accepted)"
|
|
fi
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mSelective SRS summary:\033[m"
|
|
blank_line
|
|
|
|
echo " SRS domain.....................................: ${_SRS_DOMAIN}"
|
|
echo " Persistent state..............................: ${SRS_STATE_FILE}"
|
|
echo " State origin..................................: ${_STATE_ORIGIN}"
|
|
echo ""
|
|
echo " Managed main.cf block.........................: ${main_block_status}"
|
|
echo " Managed master.cf block.......................: ${master_block_summary}"
|
|
echo " SRS main.cf values............................: ${main_srs_values_ok}"
|
|
echo " srs-forward master.cf service.................: ${master_srs_forward_ok}"
|
|
echo " Port 25 SRS receive_override_options..........: ${smtp_override_ok}"
|
|
echo " AmaViS SRS receive_override_options...........: ${amavis_override_ok}"
|
|
echo ""
|
|
echo " SRS services active...........................: ${services_active}"
|
|
echo " SRS services enabled..........................: ${services_enabled}"
|
|
echo " SRS listeners active..........................: ${listeners_active}"
|
|
echo " systemd SRS dependency drop-ins...............: ${systemd_dropins_active}"
|
|
echo ""
|
|
echo " Current main.cf matches stored pre-SRS state..: ${pre_srs_main_ok}"
|
|
echo " Current master.cf matches stored pre-SRS state: ${pre_srs_master_ok}"
|
|
echo ""
|
|
|
|
case "$srs_state" in
|
|
enabled)
|
|
echo -e " Current selective SRS state....................: \033[32m\033[1mENABLED\033[m"
|
|
;;
|
|
disabled)
|
|
echo -e " Current selective SRS state....................: \033[33m\033[1mDISABLED\033[m"
|
|
;;
|
|
*)
|
|
echo -e " Current selective SRS state....................: \033[31m\033[1mINCONSISTENT\033[m"
|
|
;;
|
|
esac
|
|
|
|
blank_line
|
|
|
|
|
|
# ----------
|
|
# Handle detected state
|
|
# ----------
|
|
|
|
srs_alias_status="$(srs_alias_tool status 2>"$log_file")" ||
|
|
fatal "Unsafe virtual alias configuration: $(cat "$log_file")"
|
|
|
|
if [[ "$srs_state" == "disabled" ]]; then
|
|
[[ "$srs_alias_status" == 'absent' ]] ||
|
|
fatal "SRS marked disabled but virtual alias items remain (${srs_alias_status})."
|
|
info "Selective SRS is already disabled. Nothing to do."
|
|
clean_up 0
|
|
fi
|
|
|
|
if [[ "$srs_state" == "inconsistent" ]]; then
|
|
error "Selective SRS configuration is inconsistent. No changes were made."
|
|
clean_up 1
|
|
fi
|
|
|
|
if ! $installation_ok || ! $services_exist ; then
|
|
fatal "Selective SRS is enabled, but the installation is incomplete. Deactivation was not attempted."
|
|
fi
|
|
if [[ "$srs_alias_status" == 'legacy' ]]; then
|
|
fatal "SRS virtual alias settings are not tracked. Run the revised activate-srs.sh once to adopt them first."
|
|
fi
|
|
[[ "$srs_alias_status" == 'managed' || "$srs_alias_status" == 'absent' ]] ||
|
|
fatal "Unsafe SRS alias status: ${srs_alias_status}"
|
|
|
|
|
|
# ----------
|
|
# Check SRS delivery queue
|
|
# ----------
|
|
|
|
echononl "Check SRS delivery queue is empty.."
|
|
srs_delivery_queue_is_empty
|
|
_queue_rc=$?
|
|
|
|
case "$_queue_rc" in
|
|
0)
|
|
echo_ok
|
|
;;
|
|
1)
|
|
echo_failed
|
|
fatal_with_queue_help \
|
|
"SRS delivery queue is not empty. Deactivation was not attempted." \
|
|
"${POSTFIX_SRS_DELIVERY_DIR}"
|
|
;;
|
|
*)
|
|
echo_failed
|
|
fatal "Could not inspect SRS delivery queue. Deactivation was not attempted. $(cat "$log_file")"
|
|
;;
|
|
esac
|
|
|
|
|
|
# ----------
|
|
# Confirmation
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mSelective SRS deactivation settings:\033[m"
|
|
blank_line
|
|
|
|
echo " Current state.................................: ENABLED"
|
|
echo " Target state..................................: DISABLED"
|
|
echo " Restore state from............................: ${SRS_STATE_FILE}"
|
|
echo ""
|
|
echo " main.cf.......................................: ${POSTFIX_MAIN_CF}"
|
|
echo " master.cf.....................................: ${POSTFIX_MASTER_CF}"
|
|
echo ""
|
|
echo " PRE-SRS default_transport set.................: ${_PRE_SRS_DEFAULT_TRANSPORT_SET}"
|
|
echo " PRE-SRS recipient_canonical_maps set..........: ${_PRE_SRS_RECIPIENT_CANONICAL_MAPS_SET}"
|
|
echo " PRE-SRS recipient limit set...................: ${_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT_SET}"
|
|
echo " PRE-SRS port 25 receive_override_options......: ${_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS:-<not set>}"
|
|
echo " PRE-SRS AmaViS receive_override_options.......: ${_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS:-<not set>}"
|
|
echo ""
|
|
echo " SRS configuration and binaries will remain installed."
|
|
echo " ${SRS_STATE_FILE} will NOT be deleted."
|
|
echo ""
|
|
|
|
if ! ask_yes_no "einverstanden" "no" ; then
|
|
warn "Deactivation cancelled by user. Nothing was changed."
|
|
clean_up 0
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Prepare rollback
|
|
# ----------
|
|
|
|
snapshot_service_states
|
|
|
|
DEACTIVATION_BACKUP_DIR="/root/postfix-srs-deactivation-${backup_date}"
|
|
|
|
echononl "Create deactivation backup directory '${DEACTIVATION_BACKUP_DIR}'.."
|
|
if mkdir -m 0700 "$DEACTIVATION_BACKUP_DIR" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal "$(cat "$log_file")"
|
|
fi
|
|
|
|
echononl "Backup '${POSTFIX_MAIN_CF}'.."
|
|
if cp -a "$POSTFIX_MAIN_CF" "${DEACTIVATION_BACKUP_DIR}/main.cf" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal "$(cat "$log_file")"
|
|
fi
|
|
|
|
echononl "Backup '${POSTFIX_MASTER_CF}'.."
|
|
if cp -a "$POSTFIX_MASTER_CF" "${DEACTIVATION_BACKUP_DIR}/master.cf" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal "$(cat "$log_file")"
|
|
fi
|
|
|
|
echononl "Backup systemd SRS drop-ins.."
|
|
if snapshot_systemd_dropins > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal "$(cat "$log_file")"
|
|
fi
|
|
|
|
DEACTIVATION_CHANGES_STARTED=true
|
|
|
|
|
|
# ----------
|
|
# Restore main.cf
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mRestore pre-SRS Postfix configuration..\033[m"
|
|
blank_line
|
|
|
|
echononl "Remove managed SRS block from '${POSTFIX_MAIN_CF}'.."
|
|
if remove_managed_block \
|
|
"$POSTFIX_MAIN_CF" \
|
|
"$SRS_MAIN_CF_BEGIN_MARKER" \
|
|
"$SRS_MAIN_CF_END_MARKER" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not remove managed SRS block from '${POSTFIX_MAIN_CF}'."
|
|
fi
|
|
|
|
echononl "Restore pre-SRS 'default_transport'.."
|
|
if restore_main_cf_parameter \
|
|
default_transport \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT_SET" \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not restore pre-SRS 'default_transport'."
|
|
fi
|
|
|
|
echononl "Restore pre-SRS 'recipient_canonical_maps'.."
|
|
if restore_main_cf_parameter \
|
|
recipient_canonical_maps \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS_SET" \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not restore pre-SRS 'recipient_canonical_maps'."
|
|
fi
|
|
|
|
echononl "Restore pre-SRS 'srs-forward_destination_recipient_limit'.."
|
|
if restore_main_cf_parameter \
|
|
srs-forward_destination_recipient_limit \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT_SET" \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not restore pre-SRS recipient limit."
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Remove only SRS-owned virtual alias list items; preserve later admin edits
|
|
# ----------
|
|
if [[ "$srs_alias_status" == 'managed' ]]; then
|
|
echononl "Remove tracked SRS virtual alias items.."
|
|
if srs_alias_tool remove > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Cannot safely remove SRS alias items: $(cat "$log_file")"
|
|
fi
|
|
fi
|
|
|
|
# ----------
|
|
# Restore master.cf
|
|
# ----------
|
|
|
|
if [[ "$master_block_status" == "present" ]]; then
|
|
echononl "Remove managed SRS block from '${POSTFIX_MASTER_CF}'.."
|
|
|
|
if remove_managed_block \
|
|
"$POSTFIX_MASTER_CF" \
|
|
"$SRS_MASTER_CF_BEGIN_MARKER" \
|
|
"$SRS_MASTER_CF_END_MARKER" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not remove managed SRS block from '${POSTFIX_MASTER_CF}'."
|
|
fi
|
|
else
|
|
echononl "Remove legacy unmarked 'srs-forward' service.."
|
|
|
|
if remove_legacy_srs_forward_service > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not safely remove legacy unmarked 'srs-forward' service."
|
|
fi
|
|
fi
|
|
|
|
echononl "Restore pre-SRS port 25 receive_override_options.."
|
|
if update_master_service_option \
|
|
smtp \
|
|
receive_override_options \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS" > "$log_file" 2>&1
|
|
then
|
|
if ! $_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS_SET ; then
|
|
# An empty value means remove the option.
|
|
update_master_service_option smtp receive_override_options "" > "$log_file" 2>&1 || {
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not remove port 25 receive_override_options."
|
|
}
|
|
fi
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not restore port 25 receive_override_options."
|
|
fi
|
|
|
|
echononl "Restore pre-SRS AmaViS receive_override_options.."
|
|
if $_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS_SET ; then
|
|
_amavis_restore="$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS"
|
|
else
|
|
_amavis_restore=""
|
|
fi
|
|
|
|
if update_master_service_option \
|
|
localhost:10025 \
|
|
receive_override_options \
|
|
"$_amavis_restore" > "$log_file" 2>&1
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not restore AmaViS receive_override_options."
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Remove systemd SRS dependencies
|
|
# ----------
|
|
|
|
echononl "Remove systemd SRS dependency drop-ins.."
|
|
if remove_srs_systemd_dropins > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not remove systemd SRS dependency drop-ins. $(cat "$log_file")"
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Validate and reload main Postfix
|
|
# ----------
|
|
|
|
echononl "Check restored Postfix configuration.."
|
|
if postfix check > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Postfix configuration check failed. $(cat "$log_file")"
|
|
fi
|
|
|
|
echononl "Reload main Postfix service '${POSTFIX_SERVICE}'.."
|
|
if systemctl reload "$POSTFIX_SERVICE" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not reload '${POSTFIX_SERVICE}'. $(cat "$log_file")"
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Stop and disable SRS services
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mStop selective SRS runtime services..\033[m"
|
|
blank_line
|
|
|
|
for _service in \
|
|
"$SRS_DELIVERY_SERVICE" \
|
|
"$SRS_REVERSE_MAP_SERVICE" \
|
|
"$POSTSRSD_SERVICE"
|
|
do
|
|
echononl "Stop systemd service '${_service}'.."
|
|
|
|
if systemctl stop "$_service" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not stop systemd service '${_service}'. $(cat "$log_file")"
|
|
fi
|
|
|
|
echononl "Disable systemd service '${_service}'.."
|
|
|
|
if systemctl disable "$_service" > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
fatal_with_deactivation_rollback "Could not disable systemd service '${_service}'. $(cat "$log_file")"
|
|
fi
|
|
done
|
|
|
|
|
|
# ----------
|
|
# Final verification
|
|
# ----------
|
|
|
|
blank_line
|
|
echo -e "\033[37m\033[1mVerify disabled selective SRS configuration..\033[m"
|
|
blank_line
|
|
|
|
verification_ok=true
|
|
|
|
echononl "Verify managed main.cf block is absent.."
|
|
check_managed_block_markers \
|
|
"$POSTFIX_MAIN_CF" \
|
|
"$SRS_MAIN_CF_BEGIN_MARKER" \
|
|
"$SRS_MAIN_CF_END_MARKER"
|
|
_marker_rc=$?
|
|
|
|
if [[ "$_marker_rc" -eq 1 ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify managed master.cf block is absent.."
|
|
check_managed_block_markers \
|
|
"$POSTFIX_MASTER_CF" \
|
|
"$SRS_MASTER_CF_BEGIN_MARKER" \
|
|
"$SRS_MASTER_CF_END_MARKER"
|
|
_marker_rc=$?
|
|
|
|
if [[ "$_marker_rc" -eq 1 ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify 'srs-forward' service is absent.."
|
|
if ! master_srs_forward_present ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify pre-SRS main.cf state.."
|
|
if state_main_cf_matches \
|
|
default_transport \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT_SET" \
|
|
"$_PRE_SRS_DEFAULT_TRANSPORT" &&
|
|
state_main_cf_matches \
|
|
recipient_canonical_maps \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS_SET" \
|
|
"$_PRE_SRS_RECIPIENT_CANONICAL_MAPS" &&
|
|
state_main_cf_matches \
|
|
srs-forward_destination_recipient_limit \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT_SET" \
|
|
"$_PRE_SRS_SRS_FORWARD_DESTINATION_RECIPIENT_LIMIT"
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify SRS virtual alias items are absent.."
|
|
if [[ "$(srs_alias_tool status 2>"$log_file")" == 'absent' ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify pre-SRS master.cf state.."
|
|
if state_master_option_matches \
|
|
smtp \
|
|
receive_override_options \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_SMTP_RECEIVE_OVERRIDE_OPTIONS" &&
|
|
state_master_option_matches \
|
|
localhost:10025 \
|
|
receive_override_options \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS_SET" \
|
|
"$_PRE_SRS_AMAVIS_RECEIVE_OVERRIDE_OPTIONS"
|
|
then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify systemd SRS dependency drop-ins are absent.."
|
|
if [[ ! -e "$POSTFIX_SRS_DROPIN_FILE" && ! -e "$SRS_DELIVERY_DROPIN_FILE" ]]; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
for _service in \
|
|
"$POSTSRSD_SERVICE" \
|
|
"$SRS_REVERSE_MAP_SERVICE" \
|
|
"$SRS_DELIVERY_SERVICE"
|
|
do
|
|
echononl "Verify systemd service '${_service}' is inactive.."
|
|
|
|
if ! service_active "$_service" ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
echononl "Verify systemd service '${_service}' is disabled.."
|
|
|
|
if ! service_enabled "$_service" ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
done
|
|
|
|
for _endpoint in \
|
|
"PostSRSd socketmap|${POSTSRSD_SOCKET_HOST}|${POSTSRSD_SOCKET_PORT}" \
|
|
"SRS reverse socketmap|${SRS_REVERSE_SOCKET_HOST}|${SRS_REVERSE_SOCKET_PORT}" \
|
|
"SRS reinjection listener|${SRS_REINJECT_HOST}|${SRS_REINJECT_PORT}" \
|
|
"SRS reverse listener|${SRS_REVERSE_HOST}|${SRS_REVERSE_PORT}"
|
|
do
|
|
IFS='|' read -r _label _host _port <<< "$_endpoint"
|
|
|
|
echononl "Verify ${_label} is inactive.."
|
|
|
|
if ! port_is_listening "$_host" "$_port" ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
done
|
|
|
|
echononl "Final Postfix configuration check.."
|
|
if postfix check > "$log_file" 2>&1 ; then
|
|
echo_ok
|
|
else
|
|
echo_failed
|
|
verification_ok=false
|
|
fi
|
|
|
|
if ! $verification_ok ; then
|
|
fatal_with_deactivation_rollback "Final selective SRS deactivation verification failed."
|
|
fi
|
|
|
|
|
|
# ----------
|
|
# Success
|
|
# ----------
|
|
|
|
DEACTIVATION_CHANGES_STARTED=false
|
|
|
|
blank_line
|
|
info "Selective SRS was successfully deactivated."
|
|
|
|
echo " Current selective SRS state....................: DISABLED"
|
|
echo " Persistent state retained......................: ${SRS_STATE_FILE}"
|
|
echo " Deactivation backup............................: ${DEACTIVATION_BACKUP_DIR}"
|
|
echo ""
|
|
|
|
clean_up 0
|