The computer world has a tendency of reinventing the while once in awhile. I am not a fan of that process, but sometimes I just have tobite the bullet and adapt to change. This post explains how I adaptedto one particular change: the netstat to sockstat transition.
I used to do this to show which processes where listening on whichport on a server:
netstat -anpe
It was a handy mnemonic as, in France, ANPE was the agencyresponsible for the unemployed (basically). That would list allsockets (-a), not resolve hostnames (-n, because it's slow), showprocesses attached to the socket (-p) with extra info like the user(-e). This still works, but sometimes fail to find the actualprocess hooked to the port. Plus, it lists a whole bunch of UNIXsockets and non-listening sockets, which are generally irrelevantfor such an audit.
What I really wanted to use was really something like:
netstat -pleunt | sort
... which has the "pleut" mnemonic ("rains", but plural, which makesno sense and would be badly spelled anyway). That also only listslistening (-l) and network sockets, specifically UDP (-u) and TCP(-t).
But enough with the legacy, let's try the brave new world of sockstatwhich has the unfortunate acronym ss.
The equivalent sockstat command to the above is:
ss -pleuntO
It's similar to the above, except we need the -O flag otherwise ssdoes that confusing thing where it splits the output on multiplelines. But I actually use:
ss -plunt0
... i.e. without the -e as the information it gives (cgroup, fdnumber, etc) is not much more useful than what's already provided with-p (service and UID).
All of the above also show sockets that are not actually a concernbecause they only listen on localhost. Those one should be filteredout. So now we embark into that wild filtering ride.
This is going to list all open sockets and show the port number andservice:
ss -pluntO --no-header | sed 's/^\([a-z]*\) *[A-Z]* *[0-9]* [0-9]* *[0-9]* */\1/' | sed 's/^[^:]*:\(:\]:\)\?//;s/\([0-9]*\) *[^ ]*/\1\t/;s/,fd=[0-9]*//' | sort -gu
For example on my desktop, it looks like:
anarcat@angela:~$ sudo ss -pluntO --no-header | sed 's/^\([a-z]*\) *[A-Z]* *[0-9]* [0-9]* *[0-9]* */\1/' | sed 's/^[^:]*:\(:\]:\)\?//;s/\([0-9]*\) *[^ ]*/\1\t/;s/,fd=[0-9]*//' | sort -gu [::]:* users:(("unbound",pid=1864)) 22 users:(("sshd",pid=1830)) 25 users:(("master",pid=3150)) 53 users:(("unbound",pid=1864)) 323 users:(("chronyd",pid=1876)) 500 users:(("charon",pid=2817)) 631 users:(("cups-browsed",pid=2744)) 2628 users:(("dictd",pid=2825)) 4001 users:(("emacs",pid=3578)) 4500 users:(("charon",pid=2817)) 5353 users:(("avahi-daemon",pid=1423)) 6600 users:(("systemd",pid=3461)) 8384 users:(("syncthing",pid=232169)) 9050 users:(("tor",pid=2857)) 21027 users:(("syncthing",pid=232169)) 22000 users:(("syncthing",pid=232169)) 33231 users:(("syncthing",pid=232169)) 34953 users:(("syncthing",pid=232169)) 35770 users:(("syncthing",pid=232169)) 44944 users:(("syncthing",pid=232169)) 47337 users:(("syncthing",pid=232169)) 48903 users:(("mosh-client",pid=234126)) 52774 users:(("syncthing",pid=232169)) 52938 users:(("avahi-daemon",pid=1423)) 54029 users:(("avahi-daemon",pid=1423)) anarcat@angela:~$
But that doesn't filter out the localhost stuff, lots of falsepositive (like emacs, above). And this is where it gets... not fun, asyou need to match "localhost" but we don't resolve names, so you needto do some fancy pattern matching:
ss -pluntO --no-header | \ sed 's/^\([a-z]*\) *[A-Z]* *[0-9]* [0-9]* *[0-9]* */\1/;s/^tcp//;s/^udp//' | \ grep -v -e '^\[fe80::' -e '^127.0.0.1' -e '^\[::1\]' -e '^192\.' -e '^172\.' | \ sed 's/^[^:]*:\(:\]:\)\?//;s/\([0-9]*\) *[^ ]*/\1\t/;s/,fd=[0-9]*//' |\ sort -gu
This is kind of horrible, but it works, those are the actually openports on my machine:
anarcat@angela:~$ sudo ss -pluntO --no-header | sed 's/^\([a-z]*\) *[A-Z]* *[0-9]* [0-9]* *[0-9]* */\1/;s/^tcp//;s/^udp//' | grep -v -e '^\[fe80::' -e '^127.0.0.1' -e '^\[::1\]' -e '^192\.' -e '^172\.' | sed 's/^[^:]*:\(:\]:\)\?//;s/\([0-9]*\) *[^ ]*/\1\t/;s/,fd=[0-9]*//' | sort -gu22 users:(("sshd",pid=1830)) 500 users:(("charon",pid=2817)) 631 users:(("cups-browsed",pid=2744)) 4500 users:(("charon",pid=2817)) 5353 users:(("avahi-daemon",pid=1423)) 6600 users:(("systemd",pid=3461)) 21027 users:(("syncthing",pid=232169)) 22000 users:(("syncthing",pid=232169)) 34953 users:(("syncthing",pid=232169)) 35770 users:(("syncthing",pid=232169)) 48903 users:(("mosh-client",pid=234126)) 52938 users:(("avahi-daemon",pid=1423)) 54029 users:(("avahi-daemon",pid=1423))
Surely there must be a better way. It turns out that lsof can dosome of this, and it's relatively straightforward. This lists alllistening TCP sockets:
lsof -iTCP -sTCP:LISTEN +c 15 | grep -v localhost | sort
In theory, this would do the equivalent on UDP
lsof -iUDP -sUDP:^Idle
... but in reality, it looks like lsof on Linux can't figure out thestate of a UDP socket:
lsof: no UDP state names available: UDP:^Idle
... which, honestly, I'm baffled by. It's strange because ss canfigure out the state of those sockets, heck it's how -l vs -aworks after all. So we need something else to show listening UDPsockets.
The following actually looks pretty good after all:
ss -pluO
That will list localhost sockets of course, so we can explicitly askss to resolve those and filter them out with something like:
ss -plurO | grep -v localhost
oh, and look here! ss supports pattern matching, so we can actuallytell it to ignore localhost directly, which removes that horriblesed line we used earlier:
ss -pluntO '! ( src = localhost )'
That actually gives a pretty readable output. One annoyance is wecan't really modify the columns here, so we still need some god-awfulsed hacking on top of that to get a cleaner output:
ss -nplutO '! ( src = localhost )' | \ sed 's/\(udp\|tcp\).*:\([0-9][0-9]*\)/\2\t\1\t/;s/\([0-9][0-9]*\t[udtcp]*\t\)[^u]*users:(("/\1/;s/".*//;s/.*Address:Port.*/Netid\tPort\tProcess/' | \ sort -nu
That looks horrible and is basically impossible to memorize. But itsure looks nice:
anarcat@angela:~$ sudo ss -nplutO '! ( src = localhost )' | sed 's/\(udp\|tcp\).*:\([0-9][0-9]*\)/\2\t\1\t/;s/\([0-9][0-9]*\t[udtcp]*\t\)[^u]*users:(("/\1/;s/".*//;s/.*Address:Port.*/Port\tNetid\tProcess/' | sort -nuPort Netid Process22 tcp sshd500 udp charon546 udp NetworkManager631 udp cups-browsed4500 udp charon5353 udp avahi-daemon6600 tcp systemd21027 udp syncthing22000 udp syncthing34953 udp syncthing35770 udp syncthing48903 udp mosh-client52938 udp avahi-daemon54029 udp avahi-daemon
Better ideas welcome.