Originally published December 19, 2018 @ 6:32 pm

Imagine this scenario: a particular process on your server is connecting to a host outside your internal network and you don’t like that. On the other hand, you can’t just kill that process because you need it.

The simple script below uses tcpkill and iptables to kill and block outbound connections initiated by processes matching the names you’ve provided. The tcpkill utility is provided by the dsniff package on CentOS/RHEL and can be installed like so:

yum -y install dsniff --enablerepo=epel

You can add the script to the cron to run, say, every few minutes. Here’s an example using sudo to source root’s environment (not necessary in this case – just an example):

*/15 * * * * sudo su - root -c '/var/adm/bin/pid_network_block.sh splunkd avastd' >/dev/null 2>&1

You may notice that multiple instances of tcpkill are hanging around – one per each blocked IP. This is not an error. This utility will only kill connections if there is traffic.

This means that, if there was no traffic at the moment you ran the command, it will stick around and wait until it sees activity. However, because the script also tells iptables to block traffic to these IPs, the tcpkill instance will remain. You can certainly change this by prepending tcpkill with timeout instead of nohup.

You can download the script here. This is what the script does exactly:

  1. Obtain the PIDs of the processes matching the process names you provided as CLI arguments
  2. Identify the IPs to which those PIDs are currently connected, excluding anything on your private network. Add those IPs to the array.
  3. Cycle through the array and run tcpkill and iptables DROP for each new IP.
  4. Clean up iptables configuration to get rid of duplicate entries.
#!/bin/bash
if [ -z "" ]
then
  exit 1
else
  procnames="${@}"
  s=0
fi

function func_ipget() {
  # Get a list of IPs accessed by the specified process
  # Exclude private networks
  for procname in ${procnames}
  do
    a+=($(/usr/sbin/lsof -i -n $(pidof "${procname}" | sed 's/[^ ]* */-p &/g') 2>/dev/null | grep EST | grep -oP "(?<=->)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(?=:)" | grep -vP "(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)"))
  done
}

function func_ipdrop() {
  # Block outbound access to those IPs
  for i in $(printf '%s\n' ${a[@]} | sort -u)
  do
    if [ $(/bin/ps -ef | grep -cE "[t]cpkill.*${i}$") -eq 0 ]
    then
      nohup /usr/sbin/tcpkill -9 host ${i} </dev/null >/dev/null 2>&1 &
    fi

    if [ $(/sbin/iptables -S | grep -cE " ${i}/") -eq 0 ]
    then
      /sbin/iptables -A OUTPUT -d ${i} -j DROP
      (( s = s + 1 ))
    fi
  done
}

function func_iptables_save() {
  # Remove duplicate entries from iptables
  tmpfile=$(mktemp)
  /sbin/iptables-save | awk '/^COMMIT$/ { delete x; }; !x[$0]++' > ${tmpfile}
  /sbin/iptables -F
  /sbin/iptables-restore < ${tmpfile}
  /sbin/service iptables save
  /sbin/service iptables restart
  /bin/rm -f ${tmpfile}
}

# RUNTIME
func_ipget
func_ipdrop
if [ ${s} -gt 0 ]
then
  func_iptables_save
fi