Simple firewalld watcher

Posted on Sun 05 April 2026 • 2 min read

While doing HackTheBox, CTF’s or similar stuff, I sometimes need a reverse shell to connect back to my computer.

This is almost always using a VPN, so I don’t have to think about NAT, firewall openings, etc

But more than once I’ve struggled to get a rev shell to connect back. Why? My computer has a local firewall 🤦

The solution is simple: sudo systemctl stop firewalld.service - but I actually want firewalld running most of the time, so this is not a good long-term solution.

Instead wouldn’t it be nice to get a notification whenever firewalld blocked something, to quickly diagnose the problem?

firewalld alert using script below

We can do that by first configuring firewalld to log all blocked connections:

firewall-cmd --set-log-denied=all   # or: unicast, broadcast, multicast
firewall-cmd --runtime-to-permanent

By watching journalctl1 for REJECT log entries, we can use notify-send to spawn a GUI alert!

journalctl --follow --dmesg --grep="filter_.*REJECT" -o cat | while read -r line; do
    declare -A kv
    for pair in $line; do
        key=${pair%%=*}
        val=${pair#*=}
        kv["$key"]="$val"
    done
    notify-send -i network-error "firewalld blocked" "[${kv[PROTO]}]: ${kv[SRC]}:${kv[SPT]} -> ${kv[DST]}:${kv[DPT]}"
done

The above is a long-running process which will “hang” your terminal. If you background it, it will still be a child process, so it stops when your terminal dies.

We can solve this by disowning the process, but now we also need to make sure we don’t end up spawning multiple processes that monitors the same thing. One ugly hack is simply to pgrep the somewhat unique string, if it works, it works.

The full script to put in your ~/.*rc:

firewall-watcher() {
    # https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/security_guide/configuring_logging_for_denied_packets
    if [[ $(firewall-cmd --get-log-denied) != "all" ]]; then
        echo "firewalld log-denied is not set to 'all'." >&2
        echo "Please run: firewall-cmd --set-log-denied=all" >&2
    fi
    pgrep -f 'journalctl .*filter_.*REJECT' > /dev/null && return 0
    journalctl --follow --dmesg --grep="filter_.*REJECT" -o cat | while read -r line; do
        declare -A kv
        for pair in $line; do
            key=${pair%%=*}
            val=${pair#*=}
            kv["$key"]="$val"
        done
        notify-send -i network-error "firewalld blocked" "[${kv[PROTO]}]: ${kv[SRC]}:${kv[SPT]} -> ${kv[DST]}:${kv[DPT]}"
    done &
    disown
}

  1. Fun-fact: users aren’t allowed to read dmesg directly (why is that??), but journalctl --dmesg exposes it, which is nice