142

My machine is a server so I want to ignore connections being made to my server (e.g. when someone visits my website). I want to see only connections/requests being made by my server to other places.

How do I see only those outgoing connections?

EDIT: I'm new to these type of things. What I'm trying to do is just see if anything from my server is being sent out other than data for my web apps. For example, if someone visits my websites, then obviously my server will send out data to the client's browser. But suppose there's also code somewhere in my web app's framework that sends statistical data to somewhere else I'm not aware of. I'd like to see those places my server is sending data to, if any. It's probably not likely, but suppose you decide to use a php or nodejs framework that you didn't write: there's a small chance it may send some type of data somewhere. If so, that's what I'd like to see.

8 Answers 8

133

Use netstat. For example

$ netstat -nputw
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
[...]
tcp        0      0 192.168.25.222:22       192.168.0.134:42903     ESTABLISHED 32663/sshd: gert [p

lists all UDP (u), TCP (t) and RAW (w) outgoing connections (not using l or a) in a numeric form (n, prevents possible long-running DNS queries) and includes the program (p) associated with that.

Consider adding the c option to get output being updated continuously.

5
  • Thanks. That's good info. Could you take a look at my updated question?
    – trusktr
    Commented Nov 24, 2012 at 9:43
  • 1
    Completely inappropriate hijack (except that this answer googled well): This answer works with Windows as well. To use netstat in "continuous mode" there, -c doesn't work. You use numbers instead, like netstat -na 1 | find "[Scan_Host_IP_Addr]" for it to update every 1 second (in this example). (source).
    – ruffin
    Commented Feb 9, 2015 at 14:06
  • 8
    This polls, so it won't always catch all connections. Commented Jun 29, 2015 at 8:38
  • Is there a way to display more user-friendly information such as domain names, info about the ip? Perhaps using a custom script?
    – awm
    Commented Mar 19, 2017 at 15:43
  • @awm Well, that would be doable with strace on the process, filtering for nameserver lookups (no details, this is not a full answer to it). Alternatively, you could show the reverse DNS of the IPs listed in netstat by omitting the n option included in my answer.
    – gertvdijk
    Commented Mar 20, 2017 at 10:09
31

I've tried a bunch of tools, including iftop, ntop, iptraf, and of course the very useful built-in netstat -tupln (supported options are OS-dependent), but the most practical for my use case turned out to be nethogs - it aggregates connections by the originating app, and is the least noisy of all.

Installable via:

sudo apt-get install nethogs

Run as root:

sudo nethogs

If your goal is to just see all TCP connections initiated by any app then you could use:

sudo tcpdump -i lo -A | grep Host:

Update: new addition: bandwhich — a new kid on the block similar to nethogs; written in rust, runs on linux, macos and windows:

13

If you just want to just log every connection attempt, the easiest is probably iptables LOG target on Linux (or the equivalent firewall logging feature on your system).

If you need more information like duration of the connection and amount of data exchanged in both directions, then conntrackd (on Linux) is probably the best option.

However note that those two above only log the traffic that goes through netfilter, which is generally all the traffic but doesn't account traffic generated with IP stacks in user space (like virtual machines or anything using raw sockets) or bridged traffic.

For more general solutions, you can have a look at things like argus, bro-ids, sancp or ntop that log all sorts of information based on traffic they sniff on an interface.

8

tcpdump allows you to see all IP traffic flowing to/from a specific interface with the ability to filter based on certain criteria. tcpdump is typically installed on most *nix systems by default, if not there's usually a port somewhere to grab it for your specific distro.

8

What I think you want to do is get a list of listening ports and then remove them from any other TCP connections, then that will be all of the outgoing connections. The ss (socket status) command outputs "Local Address:Port" and "Peer Address:Port" columns, we need to remove the listening ports from the "Local Address:Port" column and not the "Peer Address:Port" column, otherwise you may miss some outgoing connections. So to achieve that I'm using \s{2}+ behind the ":$port" string in the grep to match on the spaces that exist behind the "Local Address:Port" column; that column has two or more white spaces behind it, where the "Peer Address:Port" has one space and then a newline (grrr... should just have a newline, IMO, then I could have used \s+ instead of \s{2}+.) Normally I might try to use the filtering functionality of ss, like with ss -tn state established '(sport != :<port1> and sport !=:<port2>)' src <ip address>. But it appears there is a limit on how long that string can be, it bombed out on a system where I had a lot of listening ports. So I'm trying to do the same thing with grep. I believe the following will work:

FILTER=$(ss -tn state listening | gawk 'NR > 1 {n=split($3,A,":"); B[NR-1]=A[n]} END {for (i=1; i<length(B); i++) printf ":%s\\s{2}+|", B[i]; printf ":%s\\s{2}+", B[i]}')

ss -tn state established dst :* | grep -P -v "$FILTER"

Note this depends on the version of ss you're using, older versions (like: ss utility, iproute2-ss111117) has a different output format, so you may have to use $3 instead of $4 in awk. Note also ss -tln and ss -tn state listening gives you different output, which is a little counter-intuitive to me. YMMV.

I found a slightly more elegant solution that doesn't require knowing host's IP, ss -tn state established dst :* works well, I modified the command lines above.

5
  • Would you please add some explanation of how this works? (Edit the answer; don't post clarifying information as comments.) I think I've partly figured it out. It seems like it might need a bit more work. I ran ss -tn state listening on a host that runs an FTP server, and naturally the command showed that the machine was listening on port 21. So doesn't that mean that the egrep -v will filter out any remote hosts whose IP address includes "21" (including "210", "211", "212", ...)? Also, is the asort really necessary, or just window-dressing? Commented Mar 20, 2015 at 2:38
  • I had to fix pretty much whole thing, the corrected script is above.
    – Dale
    Commented Mar 20, 2015 at 4:06
  • 1
    Ultimately, it would be a lot nicer if ss or netstat would just include this functionality in it. Do a ss --out to get a list of outgoing connections.
    – Dale
    Commented Mar 20, 2015 at 14:16
  • Nice, I like this answer. I'll try it out when I get back to improving security on my server. :D
    – trusktr
    Commented Mar 31, 2015 at 17:39
  • Looks like this could be used for UDP, too, but you just have to use different flags and states, of course.
    – Dale
    Commented May 31, 2015 at 4:16
3

netstat is a good option.Use the parameters as required.(see its man pages) For example

netstat -antup

Here it can monitor all(a) listening numeric (n) tcp (t) and udp (u) process (p).

You can also try the ss command . For reference use :

SS Linux TCP / UDP Network and Socket Information

0
1

If you run Solaris or a derivative, have a look at conntrack

1
0

I've faced a similar issue over the years, where some outgoing connections hoarded resources. Here's my handy script to monitor all outgoing connections.

Steps

  1. Save the following script in a .sh file at say $HOME/netdump.sh. Make adjustments under Define section as required. Resolver option looks up reverse DNS for the target IP addresses.
#!/bin/bash

# Define
sleeps=10; #loop sleep in seconds
maxlogs=10; #max count to keep
maxlogsize=1000; #max size in KB
resolveip=1; #1-true; 0-false;
resolver=""; #use public dns like 9.9.9.9 or leave empty
ignoreips=("127.0.0.1" "0.0.0.0"); #Array of target IPs

# Make logs dir
if [[ ! -d $HOME/logs ]]; then
    mkdir $HOME/logs
fi

# Install net-tools for netstat
if [[ ! -x "$(command -v netstat)" ]]; then
    apt -y install net-tools
fi

# Install moreutils for ts
if [[ ! -x "$(command -v ts)" ]]; then
    apt -y install moreutils
fi

# Dump netstat to log, sleep and repeat
while [[ 1 -eq 1 ]]; do
    # Save netstat to tmp
    netstat -pnt | sed -e '1,2d' | ts %F-%H:%M:%S > $HOME/logs/netdump.tmp
    
    # Parse and log netstat with ram usage
    declare -A ptranswers;
    cat "$HOME/logs/netdump.tmp" | while read line; do
        # Ignore IPs
        targetip=$(echo $line | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' | sed -n 2p);
        if [[ ${ignoreips[*]} =~ $targetip ]]; then
            continue;
        fi
    
        # Fetch info
        pid=$(echo $line | awk '{print $NF}' | cut -d/ -f1);
        cpuusage=$(ps -p $pid -o %cpu,%mem | tail -n1 | awk '{print $1}');
        ramusage=$(ps -p $pid -o %cpu,%mem | tail -n1 | awk '{print $2}');
        ramuse=$(ps -eo size,pid | grep "$pid" | awk '{print $1/1024}');
        ramfree=$(free -m | grep 'Mem:' | awk '{print $4}');
        
        # Resolve target IP
        ptranswer="";
        if [[ $resolveip -eq 1 ]]; then
            if [[ -z ${ptranswers[$targetip]} ]]; then
                if [[ -n "$resolver" ]]; then
                    ptranswers[$targetip]=$(dig @${resolver} -x $targetip +short);
                else
                    ptranswers[$targetip]=$(dig -x $targetip +short);
                fi
            fi
            ptranswer=${ptranswers[$targetip]};
        fi
        
        # Log data
        newline="$line $ptranswer ${cpuusage}% ${ramusage}% ${ramuse}MB ${ramfree}MB";
        if [[ $(cat $HOME/logs/netdump.log | grep "$newline" | wc -l) -eq 0 ]]; then
            echo "$newline" >> $HOME/logs/netdump.log
        fi
    done
    
    # Log rotation
    logsize=$(du -k "$HOME/logs/netdump.log" | cut -f 1);
    if [[ $logsize -ge $maxlogsize ]]; then
        savelog -n -c $maxlogs $HOME/logs/netdump.log
    fi
    
    # Remove tmp
    rm $HOME/logs/netdump.tmp
    
    # Sleep
    sleep $sleeps;
done
  1. Run the script on terminal using bash $HOME/netdump.sh the first time to install necessary tools.

  2. Add a root cron job to run the script on reboot. To run as user, replace root with username.

sudo echo "@reboot root bash $HOME/netdump.sh >/dev/null 2>/dev/null &" >> /etc/crontab

All done! You can check the log tail $HOME/logs/netdump.log, and see log entries similar to this. The last four values are CPU usage in %, RAM usage in %, RAM use in MB, RAM free in MB in that order.

2024-02-15-13:17:18 tcp        0      0 {##localip}:42212     198.252.206.25:443      ESTABLISHED 5114/firefox stackoverflow.com. 2.9% 3.6% 937.434MB 747MB

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .