Last active
November 1, 2025 04:32
-
-
Save lukapaunovic/8ec3716a5be907f7afcfcf120a6e1a98 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # kill_foreign_sessions.sh | |
| # Kills session leader PIDs from `who -u` whose remote IP is not the allowed IP. | |
| # DEFAULT: dry-run mode. Set DRY_RUN=0 to actually kill. | |
| # | |
| # Usage: | |
| # DRY_RUN=1 sudo /usr/local/bin/kill_foreign_sessions.sh # just show what WOULD be killed | |
| # DRY_RUN=0 sudo /usr/local/bin/kill_foreign_sessions.sh # perform kills | |
| ALLOWED_IP="91.150.72.133" | |
| DRY_RUN="${DRY_RUN:-1}" # 1 = dry run (default), 0 = actually kill | |
| LOG="/var/log/kill_foreign_sessions.log" | |
| timestamp() { printf '%s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')"; } | |
| echo "=== $(timestamp) run (DRY_RUN=${DRY_RUN}) ===" >> "$LOG" | |
| # Parse `who -u` in a robust way: | |
| # typical line: user tty YYYY-MM-DD HH:MM idle pid (host) | |
| # We'll only process lines with at least 7 fields and extract pid ($6) and host ($7). | |
| who -u | awk 'NF>=7 { pid=$6; host=$7; gsub(/^\(|\)$/,"",host); print pid " " host }' | | |
| while read -r pid host; do | |
| # Validate pid numeric | |
| if ! [[ "$pid" =~ ^[0-9]+$ ]]; then | |
| echo "$(timestamp) skipping non-numeric pid=\"$pid\" host=\"$host\"" >> "$LOG" | |
| continue | |
| fi | |
| # If no host (local console), skip | |
| if [[ -z "$host" || "$host" == "-" ]]; then | |
| echo "$(timestamp) skipping pid $pid (no remote host)" >> "$LOG" | |
| continue | |
| fi | |
| # Preserve allowed IP | |
| if [[ "$host" == "$ALLOWED_IP" ]]; then | |
| echo "$(timestamp) preserving pid $pid from allowed IP $host" >> "$LOG" | |
| continue | |
| fi | |
| # Safety: don't touch system-critical PIDs | |
| if (( pid <= 1 )); then | |
| echo "$(timestamp) skipping unsafe pid $pid" >> "$LOG" | |
| continue | |
| fi | |
| # Check /proc exists | |
| if [ ! -d "/proc/$pid" ]; then | |
| echo "$(timestamp) pid $pid not present in /proc, skipping" >> "$LOG" | |
| continue | |
| fi | |
| # Read cmdline (may be empty for kernel threads) | |
| cmdline=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null || true) | |
| exe=$(readlink -f "/proc/$pid/exe" 2>/dev/null || true) | |
| summary="${cmdline:-$exe}" | |
| # Extra safety: skip if likely critical system process | |
| lower=$(printf '%s' "$summary" | tr '[:upper:]' '[:lower:]') | |
| if printf '%s' "$lower" | grep -qE '(^|/)(sshd|systemd|init|cron|rsyslogd|mysqld|mariadbd|postgres|nginx|httpd|apache2|php-fpm)'; then | |
| echo "$(timestamp) skipping pid $pid because it looks critical: $summary" >> "$LOG" | |
| continue | |
| fi | |
| # Log what would be done / will be done | |
| echo "$(timestamp) target pid=$pid host=$host cmd=\"$summary\"" | tee -a "$LOG" | |
| if [[ "$DRY_RUN" -eq 0 ]]; then | |
| # polite terminate, then force after timeout | |
| kill -TERM "$pid" 2>/dev/null || true | |
| sleep 4 | |
| if kill -0 "$pid" 2>/dev/null; then | |
| echo "$(timestamp) pid $pid still alive, sending KILL" >> "$LOG" | |
| kill -KILL "$pid" 2>/dev/null || true | |
| sleep 1 | |
| fi | |
| if kill -0 "$pid" 2>/dev/null; then | |
| echo "$(timestamp) FAILED to kill pid $pid" >> "$LOG" | |
| else | |
| echo "$(timestamp) pid $pid terminated" >> "$LOG" | |
| fi | |
| fi | |
| done | |
| echo "=== $(timestamp) finished ===" >> "$LOG" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment