116

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?

James Frost
  • 1,263
  • 2
  • 9
  • 5

12 Answers12

129

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).

akira
  • 63,447
33

Try this command, it might be useful:

ps aux | grep ssh
slhck
  • 235,242
Rajesh
  • 339
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.

drevicko
  • 292
reto
  • 666
18
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.

shellholic
  • 12,559
  • 1
  • 18
  • 9
6

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
Brandon
  • 61
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.

Scott G
  • 21
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

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).

FSp
  • 101
0

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

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

netstat is considered deprecated.

TNT
  • 341
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