Based on top answer idea, I wrote a simple script to automate the process. The single argument is the listening port, and it returns the ip and outgoing port of the client.
#!/bin/bash
PORT_TUNEL=$1
PID=`fuser $PORT_TUNEL/tcp 2>/dev/null`
# xargs is just used for trimming
PARENT_PID=`ps -p ${PID:-$$} -o ppid= | xargs`
netstat -ptn | awk -v ppid="$PARENT_PID/sshd:" '{if($7==ppid) print $5}'
Usage:
rsship 39963
Sample output:
67.214.221.43:26412
Explanation:
This script asumes you already know which listening port you want to obtain the client's IP. As explained in other answers, it is directly impossible to know that from the connection itself. Instead we need to obtain the process id (pid) that is holding the listening port. We're using fuser in order to do that and storing it in a variable. Since fuser has some output in stderr that would mess up with our code, we redirect it to /dev/null. Now we have the PID that holds the listening port in our $PID variable.
The next step it to get the parent process, which is the process maintaining the SSH connection with your client. It uses ps in order to do that. Xargs is used to trim the output, as it may come with some blankspaces that would mess up with our awk later.
At last, it uses netstat to get a list of all the connections, and awk to display column 5 when column 7 matches the string "$PARENT_PID/sshd:". Note that -v option is used to pass a bash variable to awk in the proper way. I'm not going to extend myself on this, search "passing bash variables to awk script" if you need more insights abouts this. I'm declaring ppid variable, holding "$PARENT_PID/sshd:", which is the text I want to match against the 7th col.
We're using this instead of grep because 1150/sshd: would match 150/sshd:. There are multiple ways of doing this, but this awk command was the simplest and most readable I could come up with. This comes after some struggles because greping just $PARENT_PID often matched some random ports on diferent connections giving unexpected results.
I'm aware this could be written in a much more compact way, but I like to keep the scripts readable and self-explanatory in a glipse when it makes sense
Listening ports enumerator
I've a script to list ALL listenings reverse tunnels on my machine and fetch their client's IP and outgoing port:
In my setup I have a special SSH server for this purpose, so all connections come through "sshtunnel" user. You'll have to modify the script if you're using your normal ssh server and come up with another way to get your first list of listening ports. using "grep sshd" will ALMOST do the trick, although it will list your main shh server listening ports too, although in MY particular situation it works because ssh server was bind to *, so the grep 127.0.0.1 would filter it out too:
#!/bin/bash
IP_SCRIPT="/etc/openvpn/rsship"
PORTS=`lsof -i -P -n | grep LISTEN | grep sshtunnel | grep 127.0.0.1 | awk -F" " '{print $9}' | awk -F":" '{print $2}' | awk '{if($1==$1+0 && $1>16000 && $1<16255) print $1}' | sort -n`
while IFS= read -r line; do
REAL_IP=$($IP_SCRIPT $line)
echo $line$'\t'$REAL_IP
done <<< "$PORTS"
Note: $IP_SCRIPT references to the first script posted.
grep 127.0.0.1 is used to remove ipv6 lines AND those binded to all interfaces (marked in the output with asterisk *) in a single pass (in our setup, all IPv6 have IPv4 counterparts).
This script only lists ports between 16000 and 16255 (because thats my particular use case). You can remove the whole awk part to make it accept ANY port. Like this:
#!/bin/bash
IP_SCRIPT="/etc/openvpn/rsship"
PORTS=`lsof -i -P -n | grep LISTEN | grep sshtunnel | grep 127.0.0.1 | awk -F" " '{print $9}' | awk -F":" '{print $2}' | sort -n`
while IFS= read -r line; do
REAL_IP=$($IP_SCRIPT $line)
echo $line$'\t'$REAL_IP
done <<< "$PORTS"
The lsof part can be made better, though, it was already made, and since I was in a rush I decided to reuse that code without giving it a though. It is basically using awk to split and print a particular text of a column (the port, specifically). As we usually say: works on my machine. If you want to improve it, use "lsof -i -P -n | grep LISTEN" and see how can you narrow it down further it in a better way, as my line may not be suitable for your particular case. I'm not 100% sure, but I think that the same results can be achieved using only netstat.
If you're running this script very frequently and have multiple ports, you may consider giving it a twist so it calls netstat just once and process that output over and over to fetch all data.
sshd
server itself that has set up the port forwarding you requested with the-R
option. To really see the ESTABLISHED connections, take thel
(el) away from thenetstat
command. I suppose you might instead want to use thet
option to see only TCP sockets, and also thea
option to see both ESTABLISHED connections and LISTEN-ing sockets. That is:netstat -ptan
telnet localhost 39963
from the server: thessh
on your client machine will connect to whatever other host:port you indicated on yourssh -R
command. Then you’ll see an ESTABLISHED connection on your server. Only, it won't be interesting because it'll be 127.0.0.1. The real remote IP will be seen from your client