Skip to content

Commit 25302a1

Browse files
committed
Add whitelist mode for geo-blocking (#29)
New GEOBLOCK_MODE setting: blacklist (default, block listed countries) or whitelist (allow only listed countries, drop everything else). Toggle via TUI Geo-Blocking > [4] or CLI. Port changes clean up old-port geoblock rules before applying on new port.
1 parent 5b0543d commit 25302a1

1 file changed

Lines changed: 77 additions & 15 deletions

File tree

mtproxymax.sh

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ CUSTOM_IP=""
106106
FAKE_CERT_LEN=2048
107107
PROXY_PROTOCOL="false"
108108
AD_TAG=""
109+
GEOBLOCK_MODE="blacklist"
109110
BLOCKLIST_COUNTRIES=""
110111
MASKING_ENABLED="true"
111112
MASKING_HOST=""
@@ -573,6 +574,7 @@ PROXY_PROTOCOL='${PROXY_PROTOCOL}'
573574
AD_TAG='${AD_TAG}'
574575
575576
# Geo-Blocking
577+
GEOBLOCK_MODE='${GEOBLOCK_MODE}'
576578
BLOCKLIST_COUNTRIES='${BLOCKLIST_COUNTRIES}'
577579
578580
# Traffic Masking
@@ -619,7 +621,7 @@ load_settings() {
619621
# Whitelist of allowed keys
620622
case "$key" in
621623
PROXY_PORT|PROXY_METRICS_PORT|PROXY_DOMAIN|PROXY_CONCURRENCY|\
622-
PROXY_CPUS|PROXY_MEMORY|CUSTOM_IP|FAKE_CERT_LEN|PROXY_PROTOCOL|AD_TAG|BLOCKLIST_COUNTRIES|\
624+
PROXY_CPUS|PROXY_MEMORY|CUSTOM_IP|FAKE_CERT_LEN|PROXY_PROTOCOL|AD_TAG|GEOBLOCK_MODE|BLOCKLIST_COUNTRIES|\
623625
MASKING_ENABLED|MASKING_HOST|MASKING_PORT|\
624626
TELEGRAM_ENABLED|TELEGRAM_BOT_TOKEN|TELEGRAM_CHAT_ID|\
625627
TELEGRAM_INTERVAL|TELEGRAM_ALERTS_ENABLED|TELEGRAM_SERVER_LABEL|\
@@ -636,6 +638,7 @@ load_settings() {
636638
[[ "$FAKE_CERT_LEN" =~ ^[0-9]+$ ]] && [ "$FAKE_CERT_LEN" -ge 512 ] || FAKE_CERT_LEN=2048
637639
[[ "$PROXY_CONCURRENCY" =~ ^[0-9]+$ ]] || PROXY_CONCURRENCY=8192
638640
[[ "$PROXY_PROTOCOL" == "true" ]] || PROXY_PROTOCOL="false"
641+
[[ "$GEOBLOCK_MODE" == "whitelist" ]] || GEOBLOCK_MODE="blacklist"
639642
[[ "$TELEGRAM_INTERVAL" =~ ^[0-9]+$ ]] || TELEGRAM_INTERVAL=6
640643
[[ "$TELEGRAM_CHAT_ID" =~ ^-?[0-9]+$ ]] || TELEGRAM_CHAT_ID=""
641644
}
@@ -2679,32 +2682,63 @@ _apply_country_rules() {
26792682
awk -v s="$setname" 'NF && !/^#/ { print "add " s " " $1 }' "$cache_file" \
26802683
| ipset restore -exist
26812684

2682-
# Add iptables DROP rule if not already present
2683-
if ! iptables -C INPUT -m set --match-set "$setname" src \
2684-
-p tcp --dport "$PROXY_PORT" \
2685-
-m comment --comment "$GEOBLOCK_COMMENT" -j DROP 2>/dev/null; then
2686-
iptables -I INPUT -m set --match-set "$setname" src \
2685+
if [ "$GEOBLOCK_MODE" = "whitelist" ]; then
2686+
# Whitelist: ACCEPT matching countries
2687+
if ! iptables -C INPUT -m set --match-set "$setname" src \
26872688
-p tcp --dport "$PROXY_PORT" \
2688-
-m comment --comment "$GEOBLOCK_COMMENT" -j DROP
2689+
-m comment --comment "$GEOBLOCK_COMMENT" -j ACCEPT 2>/dev/null; then
2690+
iptables -I INPUT -m set --match-set "$setname" src \
2691+
-p tcp --dport "$PROXY_PORT" \
2692+
-m comment --comment "$GEOBLOCK_COMMENT" -j ACCEPT
2693+
fi
2694+
else
2695+
# Blacklist: DROP matching countries
2696+
if ! iptables -C INPUT -m set --match-set "$setname" src \
2697+
-p tcp --dport "$PROXY_PORT" \
2698+
-m comment --comment "$GEOBLOCK_COMMENT" -j DROP 2>/dev/null; then
2699+
iptables -I INPUT -m set --match-set "$setname" src \
2700+
-p tcp --dport "$PROXY_PORT" \
2701+
-m comment --comment "$GEOBLOCK_COMMENT" -j DROP
2702+
fi
26892703
fi
26902704

2691-
log_success "Geo-blocking active for ${code^^} (port ${PROXY_PORT})"
2705+
log_success "Geo-${GEOBLOCK_MODE} active for ${code^^} (port ${PROXY_PORT})"
26922706
}
26932707

26942708
# Remove iptables rules and ipset for one country
26952709
_remove_country_rules() {
26962710
local code="$1"
26972711
local setname="${GEOBLOCK_IPSET_PREFIX}${code}"
26982712

2699-
# Remove iptables rule
2713+
# Remove iptables rule (try both DROP and ACCEPT for mode compatibility)
27002714
iptables -D INPUT -m set --match-set "$setname" src \
27012715
-p tcp --dport "$PROXY_PORT" \
27022716
-m comment --comment "$GEOBLOCK_COMMENT" -j DROP 2>/dev/null || true
2717+
iptables -D INPUT -m set --match-set "$setname" src \
2718+
-p tcp --dport "$PROXY_PORT" \
2719+
-m comment --comment "$GEOBLOCK_COMMENT" -j ACCEPT 2>/dev/null || true
27032720

27042721
# Destroy ipset
27052722
ipset destroy "$setname" 2>/dev/null || true
27062723
}
27072724

2725+
# Remove whitelist default-DROP rule
2726+
_remove_default_drop() {
2727+
iptables -D INPUT -p tcp --dport "$PROXY_PORT" \
2728+
-m comment --comment "${GEOBLOCK_COMMENT}-default" -j DROP 2>/dev/null || true
2729+
}
2730+
2731+
# Add whitelist default-DROP rule if in whitelist mode and countries exist
2732+
_ensure_default_drop() {
2733+
[ "$GEOBLOCK_MODE" = "whitelist" ] || return 0
2734+
[ -n "$BLOCKLIST_COUNTRIES" ] || return 0
2735+
if ! iptables -C INPUT -p tcp --dport "$PROXY_PORT" \
2736+
-m comment --comment "${GEOBLOCK_COMMENT}-default" -j DROP 2>/dev/null; then
2737+
iptables -A INPUT -p tcp --dport "$PROXY_PORT" \
2738+
-m comment --comment "${GEOBLOCK_COMMENT}-default" -j DROP
2739+
fi
2740+
}
2741+
27082742
# Reapply all saved geoblock rules (called on proxy start)
27092743
geoblock_reapply_all() {
27102744
[ -z "$BLOCKLIST_COUNTRIES" ] && return 0
@@ -2718,13 +2752,16 @@ geoblock_reapply_all() {
27182752
_apply_country_rules "$code" &>/dev/null || true
27192753
fi
27202754
done
2755+
2756+
# Whitelist mode: add default DROP for proxy port at the end (after all ACCEPTs)
2757+
_ensure_default_drop
27212758
}
27222759

27232760
# Remove ALL mtproxymax geoblock rules (called on uninstall)
27242761
geoblock_remove_all() {
2725-
# Remove all tagged iptables rules
2762+
# Remove all tagged iptables rules (both geoblock and geoblock-default)
27262763
if command -v iptables &>/dev/null; then
2727-
iptables-save 2>/dev/null | grep -- "--comment ${GEOBLOCK_COMMENT}" | \
2764+
iptables-save 2>/dev/null | grep -E -- "--comment ${GEOBLOCK_COMMENT}(-default)?" | \
27282765
sed 's/^-A/-D/' | while IFS= read -r rule; do
27292766
iptables $rule 2>/dev/null || true
27302767
done
@@ -2749,11 +2786,13 @@ show_geoblock_menu() {
27492786
clear_screen
27502787
draw_header "GEO-BLOCKING"
27512788
echo ""
2752-
echo -e " ${BOLD}Current blocklist:${NC} ${BLOCKLIST_COUNTRIES:-${DIM}none${NC}}"
2789+
echo -e " ${BOLD}Mode:${NC} ${GEOBLOCK_MODE}"
2790+
echo -e " ${BOLD}Countries:${NC} ${BLOCKLIST_COUNTRIES:-${DIM}none${NC}}"
27532791
echo ""
27542792
echo -e " ${DIM}[1]${NC} Add country"
27552793
echo -e " ${DIM}[2]${NC} Remove country"
27562794
echo -e " ${DIM}[3]${NC} Clear all"
2795+
echo -e " ${DIM}[4]${NC} Toggle mode (blacklist/whitelist)"
27572796
echo -e " ${DIM}[0]${NC} Back"
27582797

27592798
local choice
@@ -2771,12 +2810,13 @@ show_geoblock_menu() {
27712810
code=$(echo "$code" | tr '[:upper:]' '[:lower:]')
27722811
if [[ "$code" =~ ^[a-z]{2}$ ]]; then
27732812
if echo ",$BLOCKLIST_COUNTRIES," | grep -q ",${code},"; then
2774-
log_info "Country '${code}' is already blocked"
2813+
log_info "Country '${code}' is already in the list"
27752814
else
27762815
_ensure_ipset && _download_country_cidrs "$code" && {
27772816
[ -z "$BLOCKLIST_COUNTRIES" ] && BLOCKLIST_COUNTRIES="$code" || BLOCKLIST_COUNTRIES="${BLOCKLIST_COUNTRIES},${code}"
27782817
save_settings
27792818
_apply_country_rules "$code"
2819+
_ensure_default_drop
27802820
}
27812821
fi
27822822
else
@@ -2795,9 +2835,11 @@ show_geoblock_menu() {
27952835
save_settings
27962836
_remove_country_rules "$rm_code"
27972837
rm -f "${GEOBLOCK_CACHE_DIR}/${rm_code}.zone"
2838+
# Remove default-DROP if no countries left in whitelist mode
2839+
[ -z "$BLOCKLIST_COUNTRIES" ] && _remove_default_drop
27982840
log_success "Removed ${rm_code^^} — rules and cache cleared"
27992841
else
2800-
log_info "Country '${rm_code}' is not in the blocklist"
2842+
log_info "Country '${rm_code}' is not in the list"
28012843
fi
28022844
else
28032845
log_error "Invalid country code (use 2-letter ISO code)"
@@ -2812,11 +2854,24 @@ show_geoblock_menu() {
28122854
_remove_country_rules "$code"
28132855
rm -f "${GEOBLOCK_CACHE_DIR}/${code}.zone"
28142856
done
2857+
_remove_default_drop
28152858
BLOCKLIST_COUNTRIES=""
28162859
save_settings
28172860
log_success "All geo-blocks cleared"
28182861
press_any_key
28192862
;;
2863+
4)
2864+
# Remove all current rules before switching mode
2865+
geoblock_remove_all
2866+
_remove_default_drop
2867+
# Toggle mode
2868+
[ "$GEOBLOCK_MODE" = "blacklist" ] && GEOBLOCK_MODE="whitelist" || GEOBLOCK_MODE="blacklist"
2869+
save_settings
2870+
# Reapply rules in new mode
2871+
[ -n "$BLOCKLIST_COUNTRIES" ] && geoblock_reapply_all
2872+
log_success "Geo-blocking mode: ${GEOBLOCK_MODE}"
2873+
press_any_key
2874+
;;
28202875
0|"") return ;;
28212876
*) ;;
28222877
esac
@@ -3310,7 +3365,7 @@ load_tg_settings() {
33103365
case "$key" in
33113366
PROXY_PORT|PROXY_DOMAIN|PROXY_METRICS_PORT|PROXY_CONCURRENCY|\
33123367
PROXY_CPUS|PROXY_MEMORY|CUSTOM_IP|PROXY_PROTOCOL|MASKING_ENABLED|MASKING_HOST|MASKING_PORT|\
3313-
AD_TAG|BLOCKLIST_COUNTRIES|AUTO_UPDATE_ENABLED|\
3368+
AD_TAG|GEOBLOCK_MODE|BLOCKLIST_COUNTRIES|AUTO_UPDATE_ENABLED|\
33143369
TELEGRAM_ENABLED|TELEGRAM_BOT_TOKEN|TELEGRAM_CHAT_ID|\
33153370
TELEGRAM_INTERVAL|TELEGRAM_SERVER_LABEL|TELEGRAM_ALERTS_ENABLED)
33163371
printf -v "$key" '%s' "$val" ;;
@@ -4592,6 +4647,8 @@ cli_main() {
45924647
fi
45934648
check_root
45944649
if validate_port "$new_port"; then
4650+
# Remove geoblock rules on old port before changing
4651+
[ -n "$BLOCKLIST_COUNTRIES" ] && { geoblock_remove_all; _remove_default_drop; }
45954652
PROXY_PORT="$new_port"
45964653
save_settings
45974654
log_success "Port changed to ${new_port}"
@@ -4723,6 +4780,7 @@ cli_main() {
47234780
[ -z "$BLOCKLIST_COUNTRIES" ] && BLOCKLIST_COUNTRIES="$code" || BLOCKLIST_COUNTRIES="${BLOCKLIST_COUNTRIES},${code}"
47244781
save_settings
47254782
_apply_country_rules "$code"
4783+
_ensure_default_drop
47264784
}
47274785
fi
47284786
else
@@ -4738,6 +4796,7 @@ cli_main() {
47384796
save_settings
47394797
_remove_country_rules "$code"
47404798
rm -f "${GEOBLOCK_CACHE_DIR}/${code}.zone"
4799+
[ -z "$BLOCKLIST_COUNTRIES" ] && _remove_default_drop
47414800
log_success "Removed ${code^^} — rules and cache cleared"
47424801
else
47434802
log_info "Country '${code^^}' is not blocked"
@@ -4755,6 +4814,7 @@ cli_main() {
47554814
_remove_country_rules "$code"
47564815
rm -f "${GEOBLOCK_CACHE_DIR}/${code}.zone"
47574816
done
4817+
_remove_default_drop
47584818
BLOCKLIST_COUNTRIES=""
47594819
save_settings
47604820
log_success "All geo-blocks cleared"
@@ -5406,6 +5466,8 @@ show_settings_menu() {
54065466
echo -en " ${BOLD}New port:${NC} "
54075467
local p; read -r p
54085468
if validate_port "$p"; then
5469+
# Remove geoblock rules on old port before changing
5470+
[ -n "$BLOCKLIST_COUNTRIES" ] && { geoblock_remove_all; _remove_default_drop; }
54095471
PROXY_PORT="$p"
54105472
save_settings
54115473
log_success "Port set to ${p}"

0 commit comments

Comments
 (0)