-t 拡張 ssh/http等を検出

This commit is contained in:
joe 2026-02-04 10:17:18 +09:00
parent 5f0c92953e
commit 6dd9812ec9

197
ipf
View file

@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
# Name: ipf # Name: ipf
# Version: 1.6.3 # Version: 1.6.7
# Date: 2026-01-23 # Date: 2026-02-04
# Description: Restored the rich UI (asterisk, highlighting) from 1.6.1 # Description: Finalized multi-protocol detection.
# while maintaining robust logic and AI-refined backends. # Handles OpenResty/Nginx "Plain HTTP to HTTPS" (400) cases intelligently.
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 1. Root & Environment Check # 1. Root & Environment Check
@ -14,15 +14,15 @@ fi
# Global Configurations # Global Configurations
TABLE_NAME="ipf" TABLE_NAME="ipf"
VERSION="1.6.3" VERSION="1.6.7"
PROTO="tcp" PROTO="tcp"
FORCE_FLAG=false FORCE_FLAG=false
SKIP_TEST=false SKIP_TEST=false
QUIET_MODE=false QUIET_MODE=false
RESET_MODE=false RESET_MODE=false
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 2. Messaging & System Functions (Rich UI) # 2. Messaging & System Functions
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
msg() { msg() {
@ -39,23 +39,14 @@ err() {
enable_forwarding() { enable_forwarding() {
msg "\e[34m[System]\e[0m Enabling IP forwarding..." msg "\e[34m[System]\e[0m Enabling IP forwarding..."
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1
# Check sysctl success (Robustness)
if ! sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1; then
err "Critical: Could not enable net.ipv4.ip_forward. Please check permissions."
fi
for dev in /proc/sys/net/ipv4/conf/*/route_localnet; do for dev in /proc/sys/net/ipv4/conf/*/route_localnet; do
if [[ -f "$dev" ]]; then [[ -f "$dev" ]] && echo 1 > "$dev" 2>/dev/null
echo 1 > "$dev" 2>/dev/null
fi
done done
} }
init_nft() { init_nft() {
nft add table inet "${TABLE_NAME}" 2>/dev/null nft add table inet "${TABLE_NAME}" 2>/dev/null
# Precise chain initialization with correct priorities
nft add chain inet "${TABLE_NAME}" prerouting "{ type nat hook prerouting priority -100 ; policy accept ; }" 2>/dev/null nft add chain inet "${TABLE_NAME}" prerouting "{ type nat hook prerouting priority -100 ; policy accept ; }" 2>/dev/null
nft add chain inet "${TABLE_NAME}" output "{ type nat hook output priority -100 ; policy accept ; }" 2>/dev/null nft add chain inet "${TABLE_NAME}" output "{ type nat hook output priority -100 ; policy accept ; }" 2>/dev/null
nft add chain inet "${TABLE_NAME}" postrouting "{ type nat hook postrouting priority 100 ; policy accept ; }" 2>/dev/null nft add chain inet "${TABLE_NAME}" postrouting "{ type nat hook postrouting priority 100 ; policy accept ; }" 2>/dev/null
@ -63,41 +54,77 @@ init_nft() {
} }
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 3. Data & Counter Processing # 3. Connectivity & Counter Functions
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
get_total_packets() { get_total_packets() {
local uuid=$1 local uuid=$1
# Optimized sum logic: one-pass via awk nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{sum+=$2} END {print sum+0}'
local total=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{sum+=$2} END {print sum+0}')
echo "$total"
} }
test_connection() { test_connection() {
nc -z -w 1 "$1" "$2" >/dev/null 2>&1 local host=$1
return $? local port=$2
local info=""
# A. Check for Protocol Banners (SSH, FTP, etc.)
local banner=$(timeout 0.5s nc -w 1 "$host" "$port" 2>/dev/null | head -n 1 | tr -d '\000-\031')
if [[ -n "$banner" ]]; then
case "$banner" in
SSH*) info=" (SSH: ${banner:0:15})" ;;
"220"*) info=" (FTP/SMTP)" ;;
"RFB"*) info=" (VNC)" ;;
"mysql"*) info=" (MySQL)" ;;
*) info=" (Banner: ${banner:0:15})" ;;
esac
echo -e "\e[32mUP\e[0m${info}"
return 0
fi
# B. Check for HTTP/HTTPS
if nc -z -w 1 "$host" "$port" >/dev/null 2>&1; then
local schemas=("http" "https")
[[ "$port" == "443" ]] && schemas=("https" "http")
for sch in "${schemas[@]}"; do
# Try HEAD first
local res=$(curl -IsLk -m 2 -A "Mozilla/5.0 ipf-tester" -o /dev/null -w "%{http_code}" "${sch}://${host}:${port}/" 2>/dev/null)
# Try GET if HEAD fails (timeout or 000)
if [[ "$res" == "000" ]]; then
res=$(curl -sLk -m 2 -A "Mozilla/5.0 ipf-tester" -o /dev/null -w "%{http_code}" "${sch}://${host}:${port}/" 2>/dev/null)
fi
if [[ "$res" == "400" ]]; then
# Special Case: Nginx/OpenResty plain HTTP to HTTPS port
info=" (HTTPS? 400 Bad Request)"
break
elif [[ "$res" =~ ^[0-9]+$ && "$res" -ne 000 ]]; then
info=" (${sch^^} $res)"
break
fi
done
echo -e "\e[32mUP\e[0m${info}"
return 0
else
echo -e "\e[31mDOWN\e[0m"
return 1
fi
} }
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 4. Rule Management Functions (UUID-Driven) # 4. Rule Management Functions
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
delete_by_handle() { delete_by_handle() {
local h=$1 local h=$1
local uuid="" local uuid=""
# Search UUID across all relevant chains (Robustness)
local search_chains=("prerouting" "output" "forward" "postrouting") local search_chains=("prerouting" "output" "forward" "postrouting")
for sc in "${search_chains[@]}"; do for sc in "${search_chains[@]}"; do
uuid=$(nft -a list chain inet "${TABLE_NAME}" "$sc" 2>/dev/null | grep "handle $h" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) uuid=$(nft -a list chain inet "${TABLE_NAME}" "$sc" 2>/dev/null | grep "handle $h" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1)
[[ -n "$uuid" ]] && break [[ -n "$uuid" ]] && break
done done
[[ -z "$uuid" ]] && return 1
if [[ -z "$uuid" ]]; then
return 1
fi
# Atomic-like deletion across all chains for this ID
for c in "${search_chains[@]}"; do for c in "${search_chains[@]}"; do
nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do
nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh" nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh"
@ -109,12 +136,8 @@ delete_by_handle() {
test_strict_handle() { test_strict_handle() {
local h=$1 local h=$1
local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h")
[[ -z "$info" ]] && { err "Handle $h not found"; return; }
if [[ -z "$info" ]]; then
err "Handle $h not found in table '${TABLE_NAME}'"
return
fi
local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}') local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}')
local target=$(echo "$info" | grep -oE 'to ([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | awk '{print $2}') local target=$(echo "$info" | grep -oE 'to ([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | awk '{print $2}')
local tip=$(echo "$target" | cut -d':' -f1 | tr -d '[]') local tip=$(echo "$target" | cut -d':' -f1 | tr -d '[]')
@ -124,18 +147,13 @@ test_strict_handle() {
echo -n "Testing handle $h (Local :$lp -> Target $tip:$tp) [${short_id}]... " echo -n "Testing handle $h (Local :$lp -> Target $tip:$tp) [${short_id}]... "
local count_before=$(get_total_packets "$uuid") local count_before=$(get_total_packets "$uuid")
test_connection "$tip" "$tp"
if test_connection "$tip" "$tp"; then
nc -z -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 nc -z -w 1 127.0.0.1 "$lp" >/dev/null 2>&1
sleep 0.1 sleep 0.1
local count_after=$(get_total_packets "$uuid") local count_after=$(get_total_packets "$uuid")
if (( count_after > count_before )); then if (( count_after > count_before )); then
echo -e "\e[32mPASSED\e[0m" echo -e " \e[32mPASSED (Counter: +$((count_after - count_before)))\e[0m"
else
echo -e "\e[33mSKIPPED (Check shadowing)\e[0m"
fi
else
echo -e "\e[31mOFFLINE (Target Down)\e[0m"
fi fi
} }
@ -143,19 +161,15 @@ list_rules() {
[[ "$QUIET_MODE" == true ]] && return [[ "$QUIET_MODE" == true ]] && return
local highlight_uuid=$1 local highlight_uuid=$1
init_nft init_nft
msg "Forwarding Rules (Table: ${TABLE_NAME}):" msg "Forwarding Rules (Table: ${TABLE_NAME}):"
printf "%-10s %-6s %-20s %-20s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" printf "%-10s %-6s %-20s %-20s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID"
echo "-----------------------------------------------------------------------------------" echo "-----------------------------------------------------------------------------------"
nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | while read -r line; do nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | while read -r line; do
local handle=$(echo "$line" | grep -o 'handle [0-9]*' | awk '{print $2}') local handle=$(echo "$line" | grep -o 'handle [0-9]*' | awk '{print $2}')
local proto=$(echo "$line" | grep -q "udp" && echo "udp" || echo "tcp") local proto=$(echo "$line" | grep -q "udp" && echo "udp" || echo "tcp")
local target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | head -n 1) local target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | head -n 1)
local lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}') local lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}')
local full_uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2) local full_uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2)
# UI: Highlight and Asterisk for the newly added rule
if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then
printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}"
else else
@ -168,8 +182,6 @@ add_rule() {
local raw=$1 local raw=$1
init_nft init_nft
[[ "$FORCE_FLAG" == true ]] && enable_forwarding [[ "$FORCE_FLAG" == true ]] && enable_forwarding
# Parsing (LPORT:TIP:TPORT / LPORT:TPORT / LPORT)
if [[ "$raw" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then if [[ "$raw" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then
lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]}
elif [[ "$raw" =~ ^([0-9]+):([0-9]+)$ ]]; then elif [[ "$raw" =~ ^([0-9]+):([0-9]+)$ ]]; then
@ -179,72 +191,31 @@ add_rule() {
else else
err "Error: Invalid rule format '$raw'"; return 1 err "Error: Invalid rule format '$raw'"; return 1
fi fi
# Conflict Management: Overwrite existing port rules
local conflicts=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $lp" | grep -o 'handle [0-9]*' | awk '{print $2}') local conflicts=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $lp" | grep -o 'handle [0-9]*' | awk '{print $2}')
for ch in $conflicts; do for ch in $conflicts; do
msg "\e[33mOverwriting existing rule on port :$lp (Handle $ch)...\e[0m" msg "\e[33mOverwriting existing rule on port :$lp (Handle $ch)...\e[0m"
delete_by_handle "$ch" delete_by_handle "$ch"
done done
# UUID Generation
local u_raw=$(cat /proc/sys/kernel/random/uuid) local u_raw=$(cat /proc/sys/kernel/random/uuid)
local u="ipf-id:$u_raw" local u="ipf-id:$u_raw"
local fam="ip"; [[ "$tip" =~ : ]] && fam="ip6" local fam="ip"; [[ "$tip" =~ : ]] && fam="ip6"
# Rule Block (NAT, Filter, Masquerade)
nft insert rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\""
nft insert rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\""
nft insert rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\""
nft insert rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\""
nft insert rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\""
nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\"" nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\""
list_rules "$u_raw" list_rules "$u_raw"
if [[ "$SKIP_TEST" == false && "$FORCE_FLAG" == false ]]; then if [[ "$SKIP_TEST" == false && "$FORCE_FLAG" == false ]]; then
local nh=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "$u_raw" | grep -o 'handle [0-9]*' | awk '{print $2}') local nh=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "$u_raw" | grep -o 'handle [0-9]*' | awk '{print $2}')
echo ""; test_strict_handle "$nh" echo ""; test_strict_handle "$nh"
fi fi
} }
all_clear() {
if [[ "$FORCE_FLAG" == false ]]; then
echo -e "\e[33mWarning: This will delete ALL rules in table '${TABLE_NAME}'.\e[0m"
read -p "Confirm removal? (y/N): " confirm
[[ ! "$confirm" =~ ^[yY]$ ]] && exit 0
fi
nft delete table inet "${TABLE_NAME}" 2>/dev/null
init_nft
msg "Table '${TABLE_NAME}' has been reset."
list_rules; exit 0
}
show_help() {
echo "ipf version ${VERSION}"
echo "Usage: ipf [OPTIONS] [RULE]"
echo ""
echo "Example Rules:"
echo " 80:10.0.0.1:8080 Forward local :80 to 10.0.0.1:8080"
echo " 8080:80 Forward local :8080 to localhost :80"
echo " 2222 Forward local :2222 to localhost :2222"
echo ""
echo "Options:"
echo " -f Kernel: Enable forwarding & route_localnet"
echo " Global: Force mode (Skip confirmation/tests)"
echo " -l, -L List all forwarding rules"
echo " -d HANDLE/:PORT/all Delete specific rule(s)"
echo " -t [TARGET] Test connectivity (Handle, :Port, or IP:Port)"
echo " -R Reset: Clear ALL rules in the table"
echo " -v, --version Show version"
echo " -h, --help Show this help message"
}
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 5. Main Loop (Parsing) # 5. Main Parsing Loop
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Global Flag Pre-processing
for arg in "$@"; do for arg in "$@"; do
case "$arg" in case "$arg" in
-q*) QUIET_MODE=true; FORCE_FLAG=true; SKIP_TEST=true ;; -q*) QUIET_MODE=true; FORCE_FLAG=true; SKIP_TEST=true ;;
@ -253,42 +224,42 @@ for arg in "$@"; do
esac esac
done done
if [[ "$RESET_MODE" == true ]]; then all_clear; fi [[ "$RESET_MODE" == true ]] && { nft delete table inet "${TABLE_NAME}" 2>/dev/null; init_nft; msg "Reset done."; list_rules; exit 0; }
if [[ $# -eq 0 ]]; then list_rules; exit 0; fi [[ $# -eq 0 ]] && { list_rules; exit 0; }
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-h|--help) show_help; exit 0 ;; -h|--help)
echo "ipf version ${VERSION}"
echo "Usage: ipf [OPTIONS] [RULE]"
echo "Example: ipf 80:10.10.100.1:80"
exit 0 ;;
-v|--version) echo "ipf version ${VERSION}"; exit 0 ;; -v|--version) echo "ipf version ${VERSION}"; exit 0 ;;
-l|-L) list_rules; exit 0 ;; -l|-L) list_rules; exit 0 ;;
-f|-y|-q) [[ "$1" == "-f" ]] && enable_forwarding; shift; [[ $# -eq 0 ]] && exit 0; continue ;; -f|-y|-q) [[ "$1" == "-f" ]] && enable_forwarding; shift; [[ $# -eq 0 ]] && exit 0; continue ;;
-*[d]*) -*[d]*)
target="${1#-d}"; [[ -z "$target" ]] && { target="$2"; shift; } target="${1#-d}"; [[ -z "$target" ]] && { target="$2"; shift; }
[[ "$target" == "all" ]] && all_clear [[ "$target" == "all" ]] && { nft delete table inet "${TABLE_NAME}" 2>/dev/null; init_nft; list_rules; exit 0; }
IFS=',' read -r -a parts <<< "$target" IFS=',' read -r -a parts <<< "$target"
for p in "${parts[@]}"; do for p in "${parts[@]}"; do
if [[ "$p" =~ ^:([0-9]+)$ ]]; then if [[ "$p" =~ ^:([0-9]+)$ ]]; then
port="${BASH_REMATCH[1]}" ports=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport ${BASH_REMATCH[1]}" | grep -o 'handle [0-9]*' | awk '{print $2}')
for h in $(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $port" | grep -o 'handle [0-9]*' | awk '{print $2}'); do for h in $ports; do delete_by_handle "$h"; done
delete_by_handle "$h" else delete_by_handle "$p"; fi
done
else
delete_by_handle "$p" || err "Error: Handle $p not found."
fi
done done
list_rules; exit 0 ;; list_rules; exit 0 ;;
-t*) -t*)
target="${1#-t}"; [[ -z "$target" ]] && { target="$2"; shift; } target="${1#-t}"; [[ -z "$target" ]] && { target="$2"; shift; }
if [[ "$target" =~ ^([0-9\.]+):([0-9]+)$ ]]; then if [[ "$target" =~ ^([0-9\.]+):([0-9]+)$ ]]; then
echo -n "Target $target... "; test_connection "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" echo -n "Target $target... "; test_connection "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
elif [[ "$target" =~ ^:([0-9]+)$ ]]; then elif [[ "$target" =~ ^:([0-9]+)$ ]]; then
echo -n "Local $target... "; nc -z -w 1 127.0.0.1 "${BASH_REMATCH[1]}" >/dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || echo -e "\e[31mOFFLINE\e[0m" echo -n "Local $target... "; test_connection "127.0.0.1" "${BASH_REMATCH[1]}"
else else
test_strict_handle "$target" test_strict_handle "$target"
fi fi
exit 0 ;; exit 0 ;;
[0-9]*) add_rule "$1"; exit 0 ;; [0-9]*) add_rule "$1"; exit 0 ;;
*) err "Unknown option '$1'. Use -h for help."; exit 1 ;; *) err "Unknown option '$1'."; exit 1 ;;
esac esac
shift shift
done done