Visualizing TCP connections with Powershell and Graphviz
I've recently been somewhat obsessed with Graphviz, particularly with the PSGraph Powershell Module. At work I use it as part of the CI/CD of various projects to automatically document the flow of information between functions, servers, etc.
But in this post, I want to show how i start with the ss command (socket statistics), and end up with a graph that shows who you're connected to, and how.
First of all, you need a few things:
- A Linux distro. I use Pop OS.
- Powershell
- PSGraph
- Graphviz (sudo apt-get install graphviz)
So first, I create a class with the properties I want.
Then I run the ss command, and declare an empty array.
Then I loop through the output of the ss command, parse it into the individual pieces of information I want, and add it to the array.
Then the PSGraph magic. To be honest, I struggle to explain in great detail how it works, but the things to note are:
- -FromScript are the "start" boxes
- -ToScript are the "end" boxes.
- label is the text on the line
Running it as I type this post, this is the generated graph:
I find this fascinating, as it may tell you about something nefarious or just interesting.
For example here, I can see that Chrome is talking to my Chromecasts, and Syncthing is talking to my file server.
As a bonus, here is my folder tree under /dev
Just slightly modified from the example in the documentation.
That's all for now!
/Kevin
Hi Kevin
ReplyDeleteYou could do all this job using only PS. Let me show a sample code.
Import-Module PSGraph
# Gathering Data
$TCPConnection = Get-NetTCPConnection -State Established |
Where-Object -FilterScript {$_.RemoteAddress -NotLike "127.0.0.1" -and $_.RemoteAddress -NotLike "192.168.0.x"} |
Select-Object -Property @{Label = "LocalAddress" ; Expression = {$_.LocalAddress}},
@{Label = "LocalPort" ; Expression = {$_.LocalPort}},
@{Label = "RemoteAddress" ; Expression = {$_.RemoteAddress}},
@{Label = "RemoteName" ; Expression = {(Resolve-DnsName -Name $_.RemoteAddress -Type A).NameHost}},
@{Label = "RemotePort" ; Expression = {$_.RemotePort}},
@{Label = "ProcessName" ; Expression = {(Get-Process -id $_.OwningProcess).ProcessName }},
@{Label = "OwningProcess" ; Expression = {$_.OwningProcess}}
$TCPConnection # RemoteName are not resolved in any cases.
# Graph Data
graph network @{rankdir='LR'} {
node @{shape='rect'}
#edge $TCPConnection -FromScript {$_.ProcessName} -ToScript {$_.RemoteAddress} @{Label={'{0}:{1}' -f $_.LocalPort, $_.RemoteAdress}}
edge $TCPConnection -FromScript {$_.ProcessName} -ToScript {$_.RemoteAddress} @{Label={$_.RemotePort}}
} | Export-PSGraph -ShowGraph -GraphVizPath "C:\Program Files\PackageManagement\NuGet\Packages\Graphviz.2.38.0.2\dot.exe"
Nota 1: I'm using Get-NTCConnection cmdlet (like nbtstat -a in a cmd) but the output is an object.
Nota 2: I'm using Filtening at different level (State, RemoteAddress not like LoopBack IP and Non-Routable IP.
Nota3: I'm selecting a custom output (i.e. to get the ProcessName)
At this step, my object $TCPConnection is as i would like.
Then, I'm using Graphviz to graph
Nota4: I've had an error, cause the install path for Graphviz is not one of them defined in the module PSGraph, then I'm passing the InstallPatch for graphvizwith the Export-PSGraph cmdlet.
Object - Object - Object : everywhere objects. No need to parse a "legacy" (DOS or bash command) to an object, when a equivalent cmdlet is allready existing. :-)