헤드리스 Pi 콘솔/journalctl에서 한글이 깨져 보이는 문제로, Pi 번들(02/03/04)에 들어가는 모든 소스의 사람-대상 텍스트를 영어로 전환. 코드·식별자·설정키·경로·숫자·hex·서명 스킴·f-string 플레이스홀더는 불변. - raspberry_pi4/, raspberry_pi4_sht30/: 로그·주석·문서·센서명·위치 영어화 - tools/make_deploy_bundles.py: 생성 템플릿(config_local.py/setup.sh/SETUP.md/PI_META 위치) 영어화 - tools/harden_pi.sh, tools/set_network.sh: echo/주석 영어화 - WIRING.html: 영어화 + SHT30 전압 경고 정정 반영 - 서버 번들(01, php/)은 한글 유지(브라우저 대시보드, Pi에서 안 깨짐) - 전 파이썬/셸 문법검사 통과, JSON 본문 서명 무영향
183 lines
8.8 KiB
Bash
183 lines
8.8 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# set_network.sh — sensor node wired static IP setup (for on-site console after hardening)
|
|
#
|
|
# Background: after harden_pi.sh runs, SSH and console auto-login are disabled. So this
|
|
# script assumes you run it "after logging in as pi at the on-site physical console
|
|
# (monitor + keyboard)".
|
|
# The default network manager on Raspberry Pi OS Bookworm is NetworkManager (nmcli).
|
|
#
|
|
# Relationship to the hardening firewall: nftables drops inbound only; outbound is accept +
|
|
# established/related allowed. Even after switching to a static IP, the server POST
|
|
# (outbound) still goes out and no inbound rule needs to be added. This script does not
|
|
# touch the firewall.
|
|
#
|
|
# Air-gapped: no network use. Performs local nmcli configuration only.
|
|
#
|
|
# Usage:
|
|
# Interactive: sudo bash set_network.sh
|
|
# Non-interactive: sudo bash set_network.sh --ip 192.168.0.50/24 --gw 192.168.0.1 \
|
|
# --dns "8.8.8.8 1.1.1.1" --yes
|
|
# Show current: sudo bash set_network.sh --show
|
|
# Revert to DHCP: sudo bash set_network.sh --dhcp --yes
|
|
#
|
|
# Options:
|
|
# --ip A.B.C.D/PREFIX static IP + subnet (CIDR). e.g. 192.168.0.50/24 (=255.255.255.0)
|
|
# --gw A.B.C.D gateway
|
|
# --dns "A.B.C.D ..." DNS (space-separated, multiple allowed). Required if the server is a domain.
|
|
# --iface NAME interface (default: auto-detect, usually eth0)
|
|
# --con NAME NM connection name (default: auto-detect wired connection)
|
|
# --dhcp release manual IP -> revert to automatic (DHCP)
|
|
# --show print current settings only and exit
|
|
# --yes | -y skip the confirmation prompt (non-interactive)
|
|
#
|
|
# Subnet = CIDR suffix: /24=255.255.255.0 /16=255.255.0.0 /25=255.255.255.128
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
die(){ echo "[ERROR] $*" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "Run with sudo: sudo bash set_network.sh"
|
|
# Move cwd to the script folder so it can find test_connection.py etc.
|
|
cd "$(dirname "$0")" 2>/dev/null || true
|
|
|
|
command -v nmcli >/dev/null 2>&1 || die \
|
|
"nmcli(NetworkManager) not found. If this is not the Bookworm default, check your network manager (dhcpcd/systemd-networkd)."
|
|
|
|
# ---- argument parsing ------------------------------------------------------
|
|
IPCIDR="" GW="" DNS="" IFACE="" CON="" ASSUME_YES=0 MODE="static"
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--ip) IPCIDR="${2:-}"; shift 2;;
|
|
--gw) GW="${2:-}"; shift 2;;
|
|
--dns) DNS="${2:-}"; shift 2;;
|
|
--iface) IFACE="${2:-}"; shift 2;;
|
|
--con) CON="${2:-}"; shift 2;;
|
|
--dhcp) MODE="dhcp"; shift;;
|
|
--show) MODE="show"; shift;;
|
|
--yes|-y) ASSUME_YES=1; shift;;
|
|
-h|--help) sed -n '2,40p' "$0"; exit 0;;
|
|
*) die "Unknown option: $1 (help: --help)";;
|
|
esac
|
|
done
|
|
|
|
# ---- validation helpers ----------------------------------------------------
|
|
valid_ipv4(){
|
|
local ip="$1" o
|
|
[[ "$ip" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] || return 1
|
|
for o in "${BASH_REMATCH[@]:1:4}"; do [ "$o" -le 255 ] || return 1; done
|
|
return 0
|
|
}
|
|
valid_cidr(){
|
|
[[ "$1" == */* ]] || return 1
|
|
local ip="${1%/*}" pfx="${1#*/}"
|
|
valid_ipv4 "$ip" || return 1
|
|
[[ "$pfx" =~ ^[0-9]+$ ]] && [ "$pfx" -ge 0 ] && [ "$pfx" -le 32 ]
|
|
}
|
|
ip2int(){ local a b c d; IFS=. read -r a b c d <<<"$1"; echo $(( (a<<24)|(b<<16)|(c<<8)|d )); }
|
|
same_subnet(){ # $1=ip $2=gw $3=prefix — whether the gateway is on the same subnet as the IP
|
|
local m=$(( (0xFFFFFFFF << (32-$3)) & 0xFFFFFFFF ))
|
|
[ $(( $(ip2int "$1") & m )) -eq $(( $(ip2int "$2") & m )) ]
|
|
}
|
|
|
|
# ---- auto-detect wired connection/interface --------------------------------
|
|
if [ -z "$IFACE" ]; then
|
|
IFACE="$(nmcli -t -f DEVICE,TYPE dev status 2>/dev/null | awk -F: '$2=="ethernet"{print $1; exit}')"
|
|
IFACE="${IFACE:-eth0}"
|
|
fi
|
|
if [ -z "$CON" ]; then
|
|
# Prefer the active connection bound to this interface -> else the first wired connection -> finally the default name.
|
|
CON="$(nmcli -t -f NAME,DEVICE con show --active 2>/dev/null | awk -F: -v d="$IFACE" '$2==d{print $1; exit}')"
|
|
[ -n "$CON" ] || CON="$(nmcli -t -f NAME,TYPE con show 2>/dev/null | awk -F: '$2=="802-3-ethernet"{print $1; exit}')"
|
|
[ -n "$CON" ] || CON="Wired connection 1"
|
|
fi
|
|
|
|
show_current(){
|
|
echo "== Interface: $IFACE / Connection: $CON =="
|
|
nmcli -f ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns con show "$CON" 2>/dev/null \
|
|
|| echo " (connection '$CON' not found)"
|
|
echo "-- Actual IP:"; ip -4 addr show "$IFACE" 2>/dev/null | awk '/inet /{print " "$2}' || true
|
|
echo "-- Routing:"; ip route 2>/dev/null | sed 's/^/ /' || true
|
|
}
|
|
|
|
# ---- --show : print current state only -------------------------------------
|
|
if [ "$MODE" = show ]; then show_current; exit 0; fi
|
|
|
|
# ---- --dhcp : revert to automatic ------------------------------------------
|
|
if [ "$MODE" = dhcp ]; then
|
|
echo "Connection '$CON' -> revert to automatic (DHCP)."
|
|
[ "$ASSUME_YES" = 1 ] || { read -r -p "Proceed? [y/N] " a; [[ "$a" =~ ^[Yy]$ ]] || die "Cancelled"; }
|
|
nmcli con mod "$CON" ipv4.method auto ipv4.addresses "" ipv4.gateway "" ipv4.dns "" ipv4.ignore-auto-dns no
|
|
nmcli con down "$CON" >/dev/null 2>&1 || true
|
|
nmcli con up "$CON" || die "Activation failed — check physical link/switch."
|
|
echo "── Result ──"; show_current
|
|
exit 0
|
|
fi
|
|
|
|
# ---- static : interactive prompts (only when values are missing and on a TTY) ---
|
|
if [ -t 0 ]; then
|
|
[ -n "$IPCIDR" ] || read -r -p "Static IP/subnet (e.g. 192.168.0.50/24): " IPCIDR
|
|
[ -n "$GW" ] || read -r -p "Gateway (e.g. 192.168.0.1): " GW
|
|
[ -n "$DNS" ] || read -r -p "DNS (space-separated, e.g. '8.8.8.8 1.1.1.1', press Enter for none): " DNS
|
|
fi
|
|
|
|
# ---- value validation ------------------------------------------------------
|
|
valid_cidr "$IPCIDR" || die "Invalid IP/subnet format: '$IPCIDR' (e.g. 192.168.0.50/24)"
|
|
valid_ipv4 "$GW" || die "Invalid gateway format: '$GW'"
|
|
IPADDR="${IPCIDR%/*}"; PREFIX="${IPCIDR#*/}"
|
|
same_subnet "$IPADDR" "$GW" "$PREFIX" \
|
|
|| die "Gateway $GW is outside the $IPCIDR subnet. Check the IP/subnet/gateway values."
|
|
for d in $DNS; do valid_ipv4 "$d" || die "Invalid DNS format: '$d'"; done
|
|
[ -n "$DNS" ] || echo "[WARN] No DNS specified — name resolution will fail if the server address is a domain. Fine if the server is an IP."
|
|
|
|
# ---- summary + confirmation ------------------------------------------------
|
|
echo "───────────── To be applied ─────────────"
|
|
echo " Interface : $IFACE"
|
|
echo " Connection : $CON"
|
|
echo " IP/subnet : $IPCIDR"
|
|
echo " Gateway : $GW"
|
|
echo " DNS : ${DNS:-(none)}"
|
|
echo "─────────────────────────────────────"
|
|
[ "$ASSUME_YES" = 1 ] || { read -r -p "Apply? [y/N] " a; [[ "$a" =~ ^[Yy]$ ]] || die "Cancelled"; }
|
|
|
|
# ---- back up previous settings (for recovery) ------------------------------
|
|
BK="/root/net_backup_$(date +%Y%m%d_%H%M%S).txt"
|
|
{ echo "# '$CON' previous settings (recovery reference — $(date))"; nmcli con show "$CON" 2>/dev/null | grep -E '^ipv4\.'; } \
|
|
> "$BK" 2>/dev/null || true
|
|
echo "[INFO] Previous settings backed up: $BK"
|
|
|
|
# ---- apply -----------------------------------------------------------------
|
|
echo "[APPLY] Setting static IP via nmcli..."
|
|
nmcli con mod "$CON" \
|
|
ipv4.method manual \
|
|
ipv4.addresses "$IPCIDR" \
|
|
ipv4.gateway "$GW" \
|
|
ipv4.dns "$DNS" \
|
|
ipv4.ignore-auto-dns yes
|
|
# Re-apply (ignore down failure — may be before the first activation)
|
|
nmcli con down "$CON" >/dev/null 2>&1 || true
|
|
nmcli con up "$CON" \
|
|
|| die "Activation failed. Recovery: 'sudo bash set_network.sh --dhcp --yes' or see backup ($BK)."
|
|
|
|
# ---- verification ----------------------------------------------------------
|
|
echo ""
|
|
echo "───────────── Verification ─────────────"
|
|
echo "-- IP:"; ip -4 addr show "$IFACE" 2>/dev/null | awk '/inet /{print " "$2}' || true
|
|
echo "-- Routing:"; ip route 2>/dev/null | sed 's/^/ /' || true
|
|
echo "-- Gateway ping:"
|
|
if ping -c2 -W2 "$GW" >/dev/null 2>&1; then echo " OK ($GW reachable)"; else \
|
|
echo " [WARN] $GW not responding — check wiring/switch/gateway value"; fi
|
|
|
|
# Final server connectivity check (run test_connection.py if present)
|
|
for t in ./test_connection.py "$HOME/raspberry_pi4/test_connection.py" "$HOME/raspberry_pi4_sht30/test_connection.py"; do
|
|
if [ -f "$t" ]; then
|
|
echo "-- Server connectivity test: $t"
|
|
( cd "$(dirname "$t")" && python3 "$(basename "$t")" ) \
|
|
|| echo " [WARN] Server test failed — check DNS/gateway/server address"
|
|
break
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Done. If there are problems, recover: see backup ($BK) or run sudo bash set_network.sh --dhcp --yes (revert to DHCP)."
|
|
echo "Check settings: sudo bash set_network.sh --show"
|