Skip to content

Instantly share code, notes, and snippets.

@benhylau
Forked from RickyCook/Command.md
Created March 14, 2017 21:25
Show Gist options
  • Save benhylau/7edb74a349f25ceae86a80936e177f16 to your computer and use it in GitHub Desktop.
Save benhylau/7edb74a349f25ceae86a80936e177f16 to your computer and use it in GitHub Desktop.
Using socat to forward new ports via STDIO in a running Docker container

What?... Why?

Imagine you're messing around in a container, and you install some stuff, add some config, and now it's time to load up your client and check it out! Oh wait, you forgot to forward ports when you created the container! Fear not, all is not lost, for in the world of pipes, and streams, there is always a way to do something disgusting.

The Dockerfile

Example Dockerfile included will install Nginx, and socat in a container, and make Nginx run in foreground mode. To build, and run:

docker build .
docker run -it --name socat-nginx <image_id>

This will give you a container running Nginx on port 80, but with no forwarded ports.

The awful socat one-liner

I'll just get right into it. Forgive me:

socat TCP-LISTEN:8080,reuseaddr,fork 'EXEC:docker exec -i socat-nginx "socat STDIO TCP-CONNECT:localhost:80"'

So what's this doing?

  • TCP-LISTEN:8080: socat running locally will accept connections of port 8080
  • reuseaddr,fork: When accepting a connection, socat will fork to handle it. This means that it can handle parallel connections, and won't die after the first connection
  • EXEC:...: Instructs socat to execute a command, and forward the accepted connection (in this case, our TCP-LISTEN) to the standard input/output. This will happen every time socat accepts a connection
  • docker exec -i socat-nginx: Execute a new command in our running container. -i lets Docker know that we want to immediately attach (formard stardard in/out/err)
  • socat STDIO: In the Docker container, run socat and accept standard input/output as the stream source
  • TCP-CONNECT:localhost:80: Open a connection to port 80 in the container, and stream stdin to it's write buffer. Socat will read, and stream from the port to stdout (which is passed back through Docker, to local socat, and out the TCP port we've given)
FROM alpine:3.2
RUN apk add --update nginx socat && \
rm -rf /var/cache/apk/*
RUN echo 'daemon off;' >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]
@TomyLobo
Copy link

This is awful!
And exactly what I need!
I modified it to accept ssl connections on port 8443 and forward them to port 8080 inside the container:

FILENAME=server
openssl genrsa -out $FILENAME.key 1024
openssl req -new -key $FILENAME.key -x509 -days 3653 -out $FILENAME.crt
cat $FILENAME.key $FILENAME.crt >$FILENAME.pem
socat OPENSSL-LISTEN:8443,reuseaddr,fork,cert=server.pem,verify=0 'EXEC:docker exec -i containername "socat STDIO TCP-CONNECT:localhost:8080"'

My specific use case is that a VPN blocks traffic to the container, so I needed a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment