48

Quick question - I run two linux boxes, one my own desktop and the other my VPS. For security reasons on the VPS end I opted for socket connections to MySQL (/var/run/mysqld/mysql.sock). I know I can tunnel like this: ssh -L 3307:127.0.0.1:3306 user@site.com if I set up the remote sql server to listen on some port, but what I want to know is can I do something like: ssh -L /path/to/myremotesqlserver.sock:/var/run/mysqld/mysql.sock thereby tunnelling two sockets, as opposed to two ports?

A perfectly acceptable solution would also be to forward a local port to the remote socket file, but where possible I'm trying not to have tcp servers running on the remote box.

(and yes, I know tcp would be easier).

7 Answers7

68

Altough in the time, when the question was asked, it was really impossible, but it is possible nowadays.

You can to both: UNIX => TCP and UNIX => UNIX forwarding.

For example (LOCAL PORT TO REMOTE):

ssh \
  -R /var/run/mysql.sock:/var/run/mysql.sock \
  -R 127.0.0.1:3306:/var/run/mysql.sock \
  somehost

For example (REMOTE PORT TO LOCAL):

ssh \
  -L /var/run/mysql.sock:/var/run/mysql.sock \
  -L 127.0.0.1:3306:/var/run/mysql.sock \
  somehost

Notice the -R and -L

It is possible since OpenSSH 6.7.

XPLOT1ON
  • 107
41

Forward a local socket on demand

  • Setup SSH public key authentication
  • Install socat at both ends
  • create a directory locally for your sockets, inaccessible to other users.
export SOCKET_DIR=~/.remote-sockets
mkdir -p $SOCKET_DIR
socat "UNIX-LISTEN:$SOCKET_DIR/mysqld.sock,reuseaddr,fork" \
EXEC:'ssh user@server socat STDIO UNIX-CONNECT\:/var/run/mysqld/mysqld.sock'

then

mysql -S $SOCKET_DIR/mysqld.sock -u mysqluser -p

stolen from forwarding unix domain sockets with ssh and socat

ijk
  • 523
11

i haven't done this, but i would try with socat. maybe something like:

ssh xxx@yyy.zzz -L 9999:localhost:9999 "socat TCP-LISTEN:localhost:9999 UNIX-CONNECT:/var/run/mysqld/mysql.sock"
socat UNIX-LISTEN:/path/to/local/socket TCP:localhost:9999

again, i have never done anything like this.

Javier
  • 9,486
9

No more socat is needed since ssh 6.7. You can forward unix domain sockets directly like:

ssh -nNT -L $(pwd)/docker.sock:/var/run/docker.sock user@someremote

More info: https://medium.com/@dperny/forwarding-the-docker-socket-over-ssh-e6567cfab160

2

Another modification of @mpontes'/@javier's answer that

ssh user@remoteserver -L 9999:localhost:9999 'socat TCP-LISTEN:9999,fork,bind=localhost UNIX-CONNECT:/var/run/mysqld/mysql.sock& pid=$!; trap "kill $pid" 0; while echo -ne " \b"; do sleep 5; done'

Cleaner

ssh user@remoteserver -L 9999:localhost:9999 '
  socat TCP-LISTEN:9999,fork,bind=localhost UNIX-CONNECT:/var/run/mysqld/mysql.sock&
  pid=$!
  trap "kill $pid" 0
  while echo -ne " \b"; do
    sleep 5
  done
'

PROS

  1. Works on openssh earlier than 6.7 (like CentOS 7)
  2. Kills socat on ssh termination instead of having to re-ssh into the remote server
  3. Allows non-public ssh login (unlike ijk solution)

Feature

  1. Since the -f option is not used, you can either use a public key and run in the background via & or you could log in interactively and use Ctrl+Z and use the same $! to store the pid.

CONS

  1. Can't easily use the -f ssh option, as you'll lose the pid of ssh that way. This method relies on running in the foreground and Ctrl+C to kill.
  2. Far more complicated

Explanation

  • socat ...& - run socat in background on remote server
  • pid=$! - store the pid
  • trap kill\ $pid 0 - run kill $pid on bash termination
  • while :; sleep... - sit in an infinite loop
  • echo -ne \ \b - Echo space followed by backspace. This fails as soon as the ssh is disconnected. With a sleep 5, this means that socat can run up to 5 seconds after ssh

Note: Actually tested using docker, port 2375, /var/run/docker.sock, and environment variable DOCKER_HOST='tcp://localhost:2375', but should work for mysql all the same

Update

Using SSH Controls, you can use the -f flag using my way, just add the following flags

-f -o ControlPath=~/.ssh/%C -o ControlMaster=auto

And you'll get

ssh -f -o ControlPath=~/.ssh/%C -o ControlMaster=auto user@remoteserver -L 9999:localhost:9999 'set -m; socat TCP-LISTEN:9999,fork,bind=localhost UNIX-CONNECT:/var/run/mysqld/mysql.sock& pid=$!; trap "kill $pid" 0; while echo -ne " \b"; do sleep 5; done'

Now you can terminate all the controlled sessions using

ssh -o ControlPath=~/.ssh/%C -O exit remoteserver

The -o options can be saved in your .ssh/config file, or you can use -S instead (but you'll still need -o ControlMaster)

Andy
  • 127
  • 4
1

Elaborating on Javier's answer, this works for me:

ssh -f xxx@yyy.zzz -L 9999:localhost:9999 "socat TCP-LISTEN:9999,fork,bind=localhost UNIX-CONNECT:/var/run/mysqld/mysql.sock"

You need fork in order to be able to connect multiple times without socat dying once you close the first connection. Also, the way socat lets you specify an address to bind to is through the bind option, not as an address before the port.

After this, just connect to localhost:9999 normally as you would. Then to teardown the tunnel:

ssh -f xxx@yyy.zzz "killall socat"

(or something similar, you can do more elaborate things that involve keeping socat's PID)

mpontes
  • 111
1

Yes, you can using socat.

First do the TCP tunnel with SSH. Then use socat like this:

socat unix-listen:/var/run/mysqld/mysqld.sock,fork,unlink-early tcp:127.0.0.1:3306

Then give permissions to new created socket (chmod 777 may be)

user190133
  • 19
  • 1