45

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 [email protected] 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).

4
  • 1
    If the reason you don't want to use TCP on the mySQL box is because of security concerns (i.e. remote attacks, etc) you can ofcourse either firewall it, and if that is not good enough, make mySQL only listen to 127.0.0.1 for its TCP connections, then you can tunnel through SSH easily. If not, I support the socat solution below. Feb 22, 2012 at 23:30
  • lwn.net/Articles/609321 OpenSSH 6.7 will bring socket forwarding
    – Hubbitus
    Feb 25, 2015 at 9:01
  • @Hubbitus is this feature available now, if so, can you provide an example answer? Jul 17, 2017 at 5:43
  • That comment was in answer form, but converted into comment by someone. And I see now you already suggest answer below.
    – Hubbitus
    Jul 19, 2017 at 10:12

7 Answers 7

60

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.

4
  • 3
    I managed to forward my local socket to remote server socket using above and run docker client remotely (it could be done by ssh directly, but where is the fun in that :) ssh -L /home/user/docker.sock:/var/run/docker.sock dockerhost -N
    – user235422
    Apr 14, 2018 at 7:12
  • 1
    The answer has -R but for sockets it's -L
    – malhal
    Jan 3, 2020 at 15:21
  • 1
    @malhal The difference between -L and -R is not about TCP versus sockets but the direction of the forwarding. Both options supports TCP ports as well as sockets. Jun 14, 2021 at 15:44
  • It looks like it is impossible again (Since OpenSSH 8.0, RHEL8) bugzilla.redhat.com/show_bug.cgi?id=1527565
    – ibre5041
    Dec 16, 2021 at 19:33
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

1
  • This is awesome and should be the accepted answer. Dec 10, 2015 at 21:58
11

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

ssh [email protected] -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.

3
  • I'll give it a go and let you know how it goes. I've been using the tcp-based implementation.
    – user32616
    Mar 30, 2010 at 20:18
  • I can't get it to work at the moment but +1 anyway for the idea, I like it. I'll let you know if I fix it.
    – user32616
    Mar 30, 2010 at 20:32
  • +1 looks like a useful utility.
    – Warner
    Mar 30, 2010 at 20:43
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

5
  • I also found this useful for connecting to a local socket as a different user. This let me run client programs against a server process which disconnects certain user IDs (connections coming from its own job runners).
    – Warbo
    Jul 12, 2018 at 15:10
  • Forwarding a socket to the same machine, but different directory controlled by a different user, using sshd to authenticate the access? What is your usecase? Jul 13, 2018 at 1:23
  • Directory permissions/access aren't a problem. Rather, a particular daemon drops any connection attempts made by users in a certain group (supposedly to prevent newbies getting confused). Using ssh allows those users to connect (via the tunnelled socket) as if they were a user not in that group, and hence not get disconnected.
    – Warbo
    Jul 13, 2018 at 2:11
  • Interesting, funny how you mean the nix-daemon, I'm also dealing with issues regarding that. I'm wondering why nix-daemon is dropping connections to users in a certain group? Is this a security configuration thing? Or related to: nixos.org/nix/manual/#idm140737318362784? Jul 13, 2018 at 2:22
  • According to the commit message it's to prevent "accidental" usage. I brought it up here
    – Warbo
    Jul 14, 2018 at 3:41
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)

4
  • I'm using scripts inside a Docker container to deploy code to other hosts. Your connection logic is amazing but ends when the bash session ends. This prevents my from calling docker run ... connect.sh to setup the tunnel followed by docker run ... deploy.sh. I tried nohup, &, and disown but they seem to break socat by closing stdout and triggering the kill. Any ideas for tweaks to support this case (i.e. still close when SSH does but survive a disown)?
    – claytond
    Aug 1, 2019 at 22:29
  • OK. This actually works fine (w/ -f and & disown) in a terminal. Apparently the issue is created by the script "wrapper". I'd welcome input but am looking at more appropriate Q&As now.
    – claytond
    Aug 1, 2019 at 23:04
  • @claytond Not sure exactly what you are doing, but yes, all other commands are terminated when pid 1 of a container ends.What I would typically do in your deploy script scenario is use the command "deploy" and write an entrypoint for my container image that will say "if $1 (the command) == deploay then" run the ssh commands, and the deploy commands, and then end, which will close the ssh connection. You can also docker run ... connect.sh, and docker exec {container name/id} deploy.sh, so they play together.
    – Andy
    Aug 2, 2019 at 19:14
  • You're right. I'm actually using exec on an already run container and the SSH pipe wasn't persisting after exec ended. I clarified that it was due to a quirk with the way scripts are executed. If I moved the call to it's own script and called nohup <script.sh> & disown, it survives the completion of the exec.
    – claytond
    Aug 2, 2019 at 23:19
1

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

ssh -f [email protected] -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 [email protected] "killall socat"

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

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)

2
  • This is possible since OpenSSH 6.6.
    – ysdx
    Jan 9, 2015 at 18:07
  • 4
    chmod 777: nonononono! Never ever run chmod 777. It is practically never required! Not even for "testing purposes". If the file is readable, then it's readable. If it's writable by the user or group that need to write to it, then it's writable. There is absolutely zero need to give everyone write permissions, and forgetting to chmod it back to something sane is exactly how multinationals get hacked. Just don't do it. Ever. I wrote an introduction of Unix permissions. Please read it! Mar 13, 2016 at 6:00

You must log in to answer this question.