Skip to content

Commit 0c18ba7

Browse files
add_whitelists.sh
1 parent ee14a1a commit 0c18ba7

File tree

1 file changed

+181
-71
lines changed

1 file changed

+181
-71
lines changed

add_whitelists.sh

Lines changed: 181 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,188 @@
1-
#!/bin/bash
2-
3-
# Скрипт для добавления доменов и IP в Postfix и Postgrey whitelist
4-
# Использование: ./add_whitelists.sh whitelist.txt
5-
6-
# Цвета
7-
RED="\e[31m"
8-
GREEN="\e[32m"
9-
YELLOW="\e[33m"
10-
CYAN="\e[36m"
11-
RESET="\e[0m"
12-
13-
# Проверка аргумента
14-
if [ -z "$1" ]; then
15-
echo -e "${RED}Укажите файл с доменами и IP. Пример: ./add_whitelists.sh whitelist.txt${RESET}"
16-
exit 1
1+
#!/usr/bin/env bash
2+
# add_whitelists.sh — add one or many domains/IPs to Postfix + Postgrey
3+
# Usage:
4+
# ./add_whitelists.sh example.com
5+
# ./add_whitelists.sh -f whitelists.txt
6+
# ./add_whitelists.sh -n -f whitelists.txt # dry-run
7+
8+
set -Eeuo pipefail
9+
10+
POSTFIX_FILE="/etc/postfix/client_whitelist"
11+
POSTGREY_FILE="/etc/postgrey/whitelist_clients.local"
12+
BACKUP_DATE="$(date +%F_%H%M%S)"
13+
14+
usage() {
15+
cat <<'EOF'
16+
Usage:
17+
add_whitelists.sh [-n] <domain-or-ip>
18+
add_whitelists.sh [-n] -f <file_with_entries>
19+
20+
Options:
21+
-f FILE File with entries (one per line; empty lines and #comments ignored)
22+
-n Dry-run (no changes applied)
23+
-h Show this help
24+
EOF
25+
exit 1
26+
}
27+
28+
# --- light coloring (TTY only) ---
29+
if [ -t 1 ]; then
30+
C_GREEN=$(tput setaf 2 || true); C_CYAN=$(tput setaf 6 || true)
31+
C_YELL=$(tput setaf 3 || true); C_RED=$(tput setaf 1 || true)
32+
C_BOLD=$(tput bold || true); C_RESET=$(tput sgr0 || true)
33+
else
34+
C_GREEN=""; C_CYAN=""; C_YELL=""; C_RED=""; C_BOLD=""; C_RESET=""
1735
fi
1836

19-
LIST_FILE="$1"
20-
DRY_RUN=0
21-
22-
echo -e "🛠 ${CYAN}Dry-run:${RESET} $DRY_RUN"
23-
24-
# Пути к файлам
25-
PF_FILE="/etc/postfix/client_whitelist"
26-
PG_FILE="/etc/postgrey/whitelist_clients.local"
27-
28-
# Создаём файлы, если нет
29-
echo -e "📄 ${YELLOW}Creating file:${RESET} $PF_FILE"
30-
touch "$PF_FILE"
31-
32-
echo -e "📄 ${YELLOW}Creating file:${RESET} $PG_FILE"
33-
touch "$PG_FILE"
34-
35-
# Резервные копии
36-
PF_BACKUP="${PF_FILE}.bak_$(date +%F_%H%M%S)"
37-
PG_BACKUP="${PG_FILE}.bak_$(date +%F_%H%M%S)"
38-
39-
cp "$PF_FILE" "$PF_BACKUP"
40-
echo -e "📦 ${CYAN}Backup:${RESET} $PF_BACKUP"
41-
42-
cp "$PG_FILE" "$PG_BACKUP"
43-
echo -e "📦 ${CYAN}Backup:${RESET} $PG_BACKUP"
44-
45-
# Добавляем в Postfix
46-
grep -vE '^\s*#|^\s*$' "$LIST_FILE" | while read -r entry; do
47-
echo "$entry OK" >> "$PF_FILE"
37+
msg() { printf '%b\n' "$*"; }
38+
die() { printf '%sERROR:%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; exit 1; }
39+
40+
DRY=0
41+
LIST_FILE=""
42+
while getopts ":f:nh" opt; do
43+
case "$opt" in
44+
f) LIST_FILE="$OPTARG" ;;
45+
n) DRY=1 ;;
46+
h) usage ;;
47+
*) usage ;;
48+
esac
4849
done
49-
echo -e "${GREEN}Adding to Postfix:${RESET} $LIST_FILE OK"
50-
51-
# Добавляем в Postgrey (только домены, без IP)
52-
grep -vE '^\s*#|^\s*$' "$LIST_FILE" | grep -vE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' >> "$PG_FILE"
53-
echo -e "${GREEN}Adding to Postgrey:${RESET} $LIST_FILE"
54-
55-
# Перегенерация и рестарт сервисов
56-
postmap "$PF_FILE"
57-
echo -e "🔄 ${YELLOW}Restarting Postfix${RESET}"
58-
systemctl restart postfix
59-
60-
echo -e "🔄 ${YELLOW}Restarting Postgrey${RESET}"
61-
systemctl restart postgrey
62-
63-
# Итог
64-
POSTFIX_COUNT=$(grep -vE '^\s*#|^\s*$' "$LIST_FILE" | wc -l)
65-
POSTGREY_COUNT=$(grep -vE '^\s*#|^\s*$' "$LIST_FILE" | grep -vE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | wc -l)
66-
67-
echo -e "${GREEN}Done.${RESET} Changes: Postfix=${CYAN}$POSTFIX_COUNT${RESET}, Postgrey=${CYAN}$POSTGREY_COUNT${RESET}, Errors=${CYAN}0${RESET}"
50+
shift $((OPTIND - 1))
51+
SINGLE_TARGET="${1:-}"
52+
53+
[ -z "$SINGLE_TARGET" ] && [ -z "$LIST_FILE" ] && usage
54+
55+
require_root() {
56+
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
57+
die "Run as root or with sudo."
58+
fi
59+
}
60+
61+
ensure_file() {
62+
# create parent dir and file if missing
63+
local path="$1" dir
64+
dir="$(dirname "$path")"
65+
if [ ! -d "$dir" ]; then
66+
msg "📁 ${C_YELL}Creating dir:${C_RESET} $dir"
67+
[ "$DRY" -eq 0 ] && mkdir -p "$dir"
68+
fi
69+
if [ ! -f "$path" ]; then
70+
msg "📝 ${C_YELL}Creating file:${C_RESET} $path"
71+
[ "$DRY" -eq 0 ] && touch "$path" && chmod 644 "$path"
72+
fi
73+
}
74+
75+
backup_if_exists() {
76+
local path="$1"
77+
if [ -f "$path" ]; then
78+
msg "🗂 ${C_CYAN}Backup:${C_RESET} ${path}.bak_${BACKUP_DATE}"
79+
[ "$DRY" -eq 0 ] && cp -a "$path" "${path}.bak_${BACKUP_DATE}"
80+
fi
81+
}
82+
83+
is_domain() { [[ "$1" =~ ^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,}$ ]]; }
84+
is_ipv4() { [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; }
85+
is_cidr() { [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]]; }
86+
87+
already_in_file() {
88+
# match full token at start or before whitespace
89+
local needle="$1" file="$2"
90+
grep -qE -- "^${needle//./\\.}([[:space:]]|$)" "$file"
91+
}
92+
93+
add_postfix() {
94+
local v="$1"
95+
if already_in_file "$v" "$POSTFIX_FILE"; then
96+
return 1
97+
fi
98+
[ "$DRY" -eq 0 ] && printf '%s OK\n' "$v" >> "$POSTFIX_FILE"
99+
return 0
100+
}
101+
102+
add_postgrey() {
103+
local v="$1"
104+
if already_in_file "$v" "$POSTGREY_FILE"; then
105+
return 1
106+
fi
107+
[ "$DRY" -eq 0 ] && printf '%s\n' "$v" >> "$POSTGREY_FILE"
108+
return 0
109+
}
110+
111+
# collectors
112+
ADDED_ALL=()
113+
ADDED_PF=0
114+
ADDED_PG=0
115+
ERRORS=0
116+
117+
process_entry() {
118+
local raw="$1" entry
119+
# trim + lower
120+
entry="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]' | xargs)"
121+
[ -z "$entry" ] && return 0
122+
[[ "$entry" =~ ^# ]] && return 0
123+
124+
if is_cidr "$entry"; then
125+
msg "⚠️ ${C_YELL}CIDR not supported in hash map:${C_RESET} $entry"
126+
return 0
127+
elif is_ipv4 "$entry"; then
128+
if add_postfix "$entry"; then
129+
ADDED_PF=$((ADDED_PF+1)); ADDED_ALL+=( "$entry" )
130+
fi
131+
elif is_domain "$entry"; then
132+
local touched=0
133+
if add_postfix "$entry"; then ADDED_PF=$((ADDED_PF+1)); touched=1; fi
134+
if add_postgrey "$entry"; then ADDED_PG=$((ADDED_PG+1)); touched=1; fi
135+
[ "$touched" -eq 1 ] && ADDED_ALL+=( "$entry" )
136+
else
137+
msg "${C_RED}Invalid entry:${C_RESET} $entry"
138+
ERRORS=$((ERRORS+1))
139+
return 1
140+
fi
141+
}
142+
143+
# ------------ main ------------
144+
require_root
145+
msg "🔧 Dry-run: $DRY"
146+
147+
ensure_file "$POSTFIX_FILE"
148+
ensure_file "$POSTGREY_FILE"
149+
backup_if_exists "$POSTFIX_FILE"
150+
backup_if_exists "$POSTGREY_FILE"
151+
152+
if [ -n "$LIST_FILE" ]; then
153+
[ -f "$LIST_FILE" ] || die "File not found: $LIST_FILE"
154+
while IFS= read -r line || [ -n "$line" ]; do
155+
process_entry "$line" || true
156+
done < "$LIST_FILE"
157+
else
158+
process_entry "$SINGLE_TARGET" || true
159+
fi
68160

69-
echo -e "\n📊 ${YELLOW}Всего добавлено в белый список ($POSTFIX_COUNT записей):${RESET}"
161+
if [ "$DRY" -eq 0 ]; then
162+
if [ "$ADDED_PF" -gt 0 ]; then
163+
msg "🧰 postmap $POSTFIX_FILE"
164+
postmap "$POSTFIX_FILE"
165+
msg "🔄 Restarting Postfix"; systemctl restart postfix
166+
fi
167+
if [ "$ADDED_PG" -gt 0 ]; then
168+
msg "🔄 Restarting Postgrey"; systemctl restart postgrey || true
169+
fi
170+
msg "${C_GREEN}Done.${C_RESET} Changes: Postfix=${C_CYAN}${ADDED_PF}${C_RESET}, Postgrey=${C_CYAN}${ADDED_PG}${C_RESET}, Errors=${C_CYAN}${ERRORS}${C_RESET}"
171+
else
172+
msg "🔎 Dry-run complete. Would change: Postfix=${ADDED_PF}, Postgrey=${ADDED_PG}, Errors=${ERRORS}"
173+
fi
70174

71-
# Красивый цветной вывод — IP одним цветом, домены другим
72-
grep -vE '^\s*#|^\s*$' "$LIST_FILE" | while read -r entry; do
73-
if [[ "$entry" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
74-
echo -e " 🌐 ${CYAN}$entry${RESET}" # IP — голубой
175+
# Summary list of actually added items (deduplicated by logic above)
176+
if [ "${#ADDED_ALL[@]}" -gt 0 ]; then
177+
msg ""
178+
msg "📊 ${C_BOLD}Added to whitelist (${#ADDED_ALL[@]} items):${C_RESET}"
179+
for item in "${ADDED_ALL[@]}"; do
180+
if is_ipv4 "$item"; then
181+
printf ' 🌐 %s%s%s\n' "$C_CYAN" "$item" "$C_RESET"
75182
else
76-
echo -e " 🏷 ${GREEN}$entry${RESET}" # Домен — зелёный
183+
printf ' 🏷 %s%s%s\n' "$C_GREEN" "$item" "$C_RESET"
77184
fi
78-
done
185+
done
186+
else
187+
msg "ℹ️ No new entries were added."
188+
fi

0 commit comments

Comments
 (0)