Technical notes › Watch a remote server’s packets in Wireshark, live
Like the heading says, it’s occasionally useful to see the exact network traffic that’s being sent and received by a server as it happens.
If you ever need to debug something that’s happening at the network level, this functionality is indispensible.
Sure, you can use
tcpdump to save packets to a file, which you can then analyse later — but then you lose the real-time aspect.
To do this, you have to put a script on the server that runs
tcpdump, configured to ignore the SSH packets for the current session.
You can then either pipe this script into
tshark, the command-line version of Wireshark, or you can send the packets back over SSH and analyse them inside Wireshark directly, so you get to keep using its GUI.
The script and the SSH problem
Here is the script that runs
It dumps all packets to standard output as their raw network frames — so binary, not text.
Don’t just run it in your terminal!
It’ll barf strange characters everywhere.
#!/bin/bash if [ -z "$SSH_CLIENT" ]; then echo "[Not SSHing? Just run tcpdump manually]" exit 1 else read -r client_host _local_port client_port <<< "$SSH_CLIENT" sudo tcpdump -U -s 0 -w - "not (port $client_port and host $client_host)" fi
It uses the following flags:
-Uflag disables buffering, so captured packets are shown immediately.
-s 0arguments tell tcpdump to capture everything, rather than to stop after a certain number of bytes. This functionality is usually used when saving all network traffic to multiple files for long-term storage.
-w -arguments tell tcpdump to print the packets to standard output, rather than to a file.
If you were going to run this script on your local computer, these three arguments would be sufficient. But there’s an added hurdle when running the script over SSH. Because it logs all network traffic, each packet sent will result in some more SSH traffic — which then gets captured by the script itself, and sent over SSH, which gets captured, and sent, and so on, ad infinitum.
To solve this, we analyse the
$SSH_CLIENT environment variable to specifically exclude the packets of the current SSH session from being returned.
Once you have put the script somewhere a regular user can run it, such as at
/usr/local/bin/packet-capture, you can invoke it over SSH and pipe the results directly to Wireshark:
ssh $some-remote-host packet-capture | wireshark -k -i -
This will connect to the host using SSH and open Wireshark, and if everything goes correctly, that machine’s packets will be displayed in the local Wireshark.
One thing to keep in mind is that the Stop button in Wireshark just stops packets from being displayed, not from being captured. To stop the process entirely, close Wireshark or Control-C the shell pipeline.
The tcpdump problem
The annoying part about this is that for security reasons, by default in Linux, only the
root user is allowed to see the entire machine’s network traffic like this.
And as you’re (hopefully) not SSHing onto the machine as
root, we need to find some way to do this.
Use passwordless sudo, meaning you can just use
sudoin the script with no further mess or fuss.
Make an exception in
/etc/sudoersthat specifically allows passwordless
sudofor this script in particular.
Write a program in a compiled language that emulates this shell script’s functionality.
One way you might expect to work, but doesn’t, is setting the
setuid bit of the shell script itself.
Linux helpfully ignores this flag for interpreted scripts, only taking it into account when running binaries.
Which way you pick is up to you, but unfortunately you will have to pick one.∎