94 lines
2.5 KiB
Bash
94 lines
2.5 KiB
Bash
#!/bin/bash
|
|
# Вызывается из socat EXEC; в окружении есть SOCAT_PEERADDR.
|
|
|
|
set -uo pipefail
|
|
|
|
STATUS_FILE="${STATUS_FILE:-/var/www/cfb/status.html}"
|
|
ALLOW="${STATUS_HTTP_ALLOW_IPS:-}"
|
|
|
|
ip_to_int() {
|
|
local a b c d IFS=.
|
|
read -r a b c d <<<"$1"
|
|
[[ -n "$d" ]] || return 1
|
|
echo $(((a << 24) | (b << 16) | (c << 8) | d))
|
|
}
|
|
|
|
ipv4_in_cidr() {
|
|
local ip="$1" cidr="$2" base masklen ipn basen mask
|
|
[[ "$cidr" =~ ^([0-9]{1,3}(\.[0-9]{1,3}){3})/([0-9]{1,2})$ ]] || return 1
|
|
base="${BASH_REMATCH[1]}"
|
|
masklen="${BASH_REMATCH[3]}"
|
|
(( masklen >= 0 && masklen <= 32 )) || return 1
|
|
(( masklen == 0 )) && return 0
|
|
ipn=$(ip_to_int "$ip") || return 1
|
|
basen=$(ip_to_int "$base") || return 1
|
|
(( masklen == 32 )) && { [[ "$ip" == "$base" ]]; return; }
|
|
mask=$(( (0xFFFFFFFF << (32 - masklen)) & 0xFFFFFFFF ))
|
|
(( (ipn & mask) == (basen & mask) ))
|
|
}
|
|
|
|
extract_peer_ip() {
|
|
local raw="${SOCAT_PEERADDR:-}"
|
|
[[ -n "$raw" ]] || { echo ""; return 1; }
|
|
if [[ "$raw" == \[*\]* ]]; then
|
|
local inner="${raw#\[}"
|
|
echo "${inner%%\]*}"
|
|
return 0
|
|
fi
|
|
if [[ "$raw" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}:[0-9]+$ ]]; then
|
|
echo "${raw%:*}"
|
|
return 0
|
|
fi
|
|
echo "$raw"
|
|
}
|
|
|
|
allowed_ip() {
|
|
local peer="$1" entry trimmed
|
|
[[ -n "$peer" ]] || return 1
|
|
IFS=',' read -ra _entries <<<"$ALLOW"
|
|
for entry in "${_entries[@]}"; do
|
|
trimmed="${entry#"${entry%%[![:space:]]*}"}"
|
|
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
|
|
[[ -n "$trimmed" ]] || continue
|
|
if [[ "$trimmed" == */* ]]; then
|
|
if [[ "$peer" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
|
|
ipv4_in_cidr "$peer" "$trimmed" && return 0
|
|
fi
|
|
continue
|
|
fi
|
|
[[ "$peer" == "$trimmed" ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Считать минимальный HTTP-запрос
|
|
while IFS= read -r line; do
|
|
line="${line%$'\r'}"
|
|
[[ -z "$line" ]] && break
|
|
done
|
|
|
|
peer_ip=$(extract_peer_ip)
|
|
|
|
hdr_ok=$'HTTP/1.0 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\n\r\n'
|
|
hdr_denied=$'HTTP/1.0 403 Forbidden\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\nForbidden\r\n'
|
|
|
|
if [[ -z "${ALLOW// }" ]]; then
|
|
# только loopback — на всякий случай
|
|
if [[ "$peer_ip" != "127.0.0.1" && "$peer_ip" != "::1" ]]; then
|
|
printf '%b' "$hdr_denied"
|
|
exit 0
|
|
fi
|
|
else
|
|
if ! allowed_ip "$peer_ip"; then
|
|
printf '%b' "$hdr_denied"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
printf '%b' "$hdr_ok"
|
|
if [[ -r "$STATUS_FILE" ]]; then
|
|
cat "$STATUS_FILE"
|
|
else
|
|
printf '%s\n' '<!DOCTYPE html><html><body><p>status pending</p></body></html>'
|
|
fi
|