109

I use a lot of SSH tunnels to various servers on my linux machine (for tunnelling to databases, web servers etc) and it would be really handy to view a list of current open tunnels via a shell script.

I can identify local connections via a grep on netstat along the lines of:

netstat -n --protocol inet | grep ':22'

but this won't show me the remote port its connected to (and obviously includes standard SSH connections that aren't tunnelled)

UPDATE: The answers are fine but are not showing me the remote port I'm connected to. E.g I often have a tunnel through to mysql, say localhost:3308 mapping to :3306 on the server. Normally I can guess by the local ports I've chosen but would be nice to have access to both.

Any ideas?

1
  • 5
    I've seen a couple questions like this recently (not specifically what you are asking), but related to ssh providing information about the connection. As cool as ssh is, it sucks at providing some basic useful information like that. There are some client internal commands you can run like <ret><ret> ~# and the $SSH_CONNECTION environment variable, but they really are sparse on details. A list of running tunnels would be nice. Maybe its time for a feature request.
    – deltaray
    Commented Feb 22, 2011 at 2:31

12 Answers 12

117

if you only want to list tunnels created by ssh:

% sudo lsof -i -n | egrep '\<ssh\>'
ssh  19749  user  3u  IPv4 148088244   TCP x.x.x.x:39689->y.y.y.y:22 (ESTABLISHED)
ssh  19749  user  4u  IPv6 148088282   TCP [::1]:9090 (LISTEN)
ssh  19749  user  5u  IPv4 148088283   TCP 127.0.0.1:9090 (LISTEN)

(that would be a -L 9090:localhost:80 tunnel)

if you want to see the tunnels / connections made to a sshd:

 % sudo lsof -i -n | egrep '\<sshd\>'
sshd  15767  root  3u  IPv4 147401205   TCP x.x.x.x:22->y.y.y.y:27479 (ESTABLISHED)
sshd  15842  user  3u  IPv4 147401205   TCP x.x.x.x:22->y.y.y.y:27479 (ESTABLISHED)
sshd  15842  user  9u  IPv4 148002889   TCP 127.0.0.1:33999->127.0.0.1:www (ESTABLISHED)
sshd  1396   user  9u  IPv4 148056581   TCP 127.0.0.1:5000 (LISTEN)
sshd  25936  root  3u  IPv4 143971728   TCP *:22 (LISTEN)

the ssh-daemon listens on port 22 (last line), 2 subprocesses are spawned (first 2 lines, login of 'user'), a -R tunnel created on port 5000, and a -L tunnel which forwards a port from my (local) machine to localhost:80 (www).

5
  • 1
    The 3rd line is only there because the TCP socket is in use. It just says the something through a ssh tunnel has hit your local web server, not that the 33999 port is forwarded to the 80 one.
    – shellholic
    Commented Feb 21, 2011 at 13:02
  • thats the essence of a -L tunnel...
    – akira
    Commented Feb 21, 2011 at 13:03
  • That's fine, its showing the remote IP address and the list of tunnelled ports. What I ideally want to know is what the remote port its tunnelled to. For example, if I've got a tunnel open from 3308 locally to 3306 on the server I want to see both. Commented Feb 23, 2011 at 10:33
  • for that you would have to either login to the server and execute the sshd-related lsof there (reliable) or parse the output of /proc/PID/cmdline for all of your ssh-commands .. which might give you misleading results since you can specify tunnels via .ssh/config as well.
    – akira
    Commented Feb 23, 2011 at 11:06
  • Yep, makes sense. Need to be a bit cleverer with the script then to parse the results, get a list of remote servers and execute the same command on each to retrieve the remote ports. Definitely doable. Will get on it! Commented Mar 2, 2011 at 9:49
29

Try this command, it might be useful:

ps aux | grep ssh
3
23

not exactly the solution for your problem, but also handy sometimes:

From within an ssh session:

  1. press enter
  2. type ~ and then #

shows you a list of all open connections over your tunnels for that session.

1
  • 3
    That works only for interactive tunnels (without -N and -f, …), but interesting to know.
    – erik
    Commented Mar 13, 2014 at 23:35
16
netstat -tpln | grep ssh
  • t: TCP
  • p: show process
  • l: listening
  • n: numeric values

EDIT: example for @akira comment:

(header added, tested on Debian wheezy)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:1443          0.0.0.0:*               LISTEN      4036/ssh        

Which can be read as: SSH (not SSHd) is listening to local TCP port 1443.

3
  • Also note that -p shows only your own processes (all processes by root). Also, that command shows sshd too.
    – Olli
    Commented Feb 21, 2011 at 12:11
  • for -R tunnels you have to avoid -l
    – akira
    Commented Feb 21, 2011 at 13:16
  • You can't see locally the -R tunnels if not in use. But right, if in use, you can catch them without the -l
    – shellholic
    Commented Feb 21, 2011 at 13:26
5

This is the top google result for this question, so I will put my answer here. I stayed up all night filtering the results, and came up with a long complex command that shows you only your reverse ssh tunnels in this format:

publicipaddress:remoteforwardedport

Here is the code, I am running Ubuntu Server 12. I am running reverse ssh tunnels that forward local port 5900 to my public ssh server, and this nifty command shows all my public ip addresses with the remote port.

sudo lsof -i -n | egrep '\<sshd\>' | grep -v ":ssh" | grep LISTEN | sed 1~2d | awk '{ print $2}' | while read line; do sudo lsof -i -n | egrep $line | sed 3~3d | sed 's/.*->//' | sed 's/:......*(ESTABLISHED)//' | sed 's/.*://' | sed 's/(.*//' | sed 'N;s/\n/:/' 2>&1 ;done
3
report_local_port_forwardings() {

  # -a ands the selection criteria (default is or)
  # -i4 limits to ipv4 internet files
  # -P inhibits the conversion of port numbers to port names
  # -c /regex/ limits to commands matching the regex
  # -u$USER limits to processes owned by $USER
  # http://man7.org/linux/man-pages/man8/lsof.8.html
  # https://stackoverflow.com/q/34032299

  echo 
  echo "LOCAL PORT FORWARDING"
  echo
  echo "You set up the following local port forwardings:"
  echo

  lsof -a -i4 -P -c '/^ssh$/' -u$USER -s TCP:LISTEN

  echo
  echo "The processes that set up these forwardings are:"
  echo

  ps -f -p $(lsof -t -a -i4 -P -c '/^ssh$/' -u$USER -s TCP:LISTEN)

}

report_remote_port_forwardings() {

  echo 
  echo "REMOTE PORT FORWARDING"
  echo
  echo "You set up the following remote port forwardings:"
  echo

  ps -f -p $(lsof -t -a -i -c '/^ssh$/' -u$USER -s TCP:ESTABLISHED) | awk '
  NR == 1 || /R (\S+:)?[[:digit:]]+:\S+:[[:digit:]]+.*/
  '
}

report_local_port_forwardings
report_remote_port_forwardings

Sample output:

LOCAL PORT FORWARDING

You set up the following local port forwardings:

COMMAND   PID  USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
ssh     10086 user     7u  IPv4 1924960      0t0  TCP localhost:2301 (LISTEN)
ssh     10086 user     9u  IPv4 1924964      0t0  TCP localhost:2380 (LISTEN)
ssh     10086 user    11u  IPv4 1924968      0t0  TCP localhost:2381 (LISTEN)

The processes that set up these forwardings are:

UID        PID  PPID  C STIME TTY          TIME CMD
user     10086  7074  0 13:05 pts/21   00:00:00 ssh -N ssh.example.com

REMOTE PORT FORWARDING

You set up the following remote port forwardings:

UID        PID  PPID  C STIME TTY      STAT   TIME CMD
user      7570 30953  0 11:14 pts/18   S      0:00 ssh -N -R 9000:localhost:3000 ssh.example.com
2

We use ssh tunnels for DB connections. May not fit your use case but ps shows both ports.

$ ps -ef | grep 'ssh -N'
svc_us2  5803 1  0 Nov17 00:00:00 ssh -N -f -L 15302:dev333:1530 svc_us2@dev333
svc_us2  6098 1  0 Nov17 00:00:00 ssh -N -f -L 15301:dev338:1530 svc_us2@dev338
svc_usr 24734 1  0 Nov17 00:00:00 ssh -N -f -L 10851:dev101:8003 svc_usr@dev101

Last item shows tunnel from local 10851 to target server 8003.

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Dec 9, 2022 at 21:53
1

lsof -i shows the network connections associated with the ssh port.

lsof -i:ssh

or

lsof -i:22    

Sample output for two outgoing ssh tunnels:

COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ssh     8492   me    3u  IPv4 0x9fdfe6f804b4a45d      0t0  TCP vpnhost.example.net:59464->remotehost.com:ssh (ESTABLISHED)
ssh     8571   me    3u  IPv4 0x9fdfe6f804b5688d      0t0  TCP vpnhost.example.net:59607->remotehost.com:ssh (ESTABLISHED)
      

Explanation

From the lsof manual:

An Internet address is specified in the form (Items in square brackets are optional.):

[46][protocol][@hostname|hostaddr][:service|port]

where:

  • 46 specifies the IP version, IPv4 or IPv6. If neither is specified, the following address applies to all IP versions.
  • protocol is a protocol name - TCP, UDP
  • hostname is an Internet host name and hostaddr is a numeric Internet IPv4 address in dot form; or an IPv6 numeric address in colon form, enclosed in brackets, if the UNIX dialect supports IPv6. When an IP version is selected, only its numeric addresses may be specified.
  • service is an /etc/services name - e.g., smtp - or a list of them; port is a port number, or a list of them.
0
/sbin/ip tunnel list # replacement for the deprecated iptunnel command
0
#!/bin/csh -f
echo SSH Tunnels Connected
echo
foreach f (`netstat -an -p | grep tcp | grep sshd | grep -v :: | grep -v 0:22 | grep LISTEN | cut -d" " -f45- | cut -d"/" -f1`)
set ip = `netstat -an -p | grep tcp | grep sshd | grep -v :: | grep -v 0:22 | grep ESTABLISH | grep $f | cut -d" " -f20- | cut -d":" -f1`
#set h = `grep -a "$ip" /htdocs/impsip.html | grep br | cut -d" " -f2`
echo -n "$ip    "
echo  `netstat -an -p | grep tcp | grep sshd | grep -v :: | grep -v 0:22 | grep LISTEN | grep $f | cut -d":" -f2  | cut -d" " -f1`
#echo  " $h"
end
0

Since I don't like lsof, I suggest an alternative method (another guy taught me :)):

$ netstat -l | grep ssh

In this way you show the ssh tunnels created by ssh that are opened in LISTEN mode (and are omitted by default by netstat).

0

As an alternative to lsof you can use ss like this:

ss -tlp 2>/dev/null | grep -E '\<sshd\>'

netstat is considered deprecated.

You must log in to answer this question.

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