November 13, 2020

Reverse Tunnelling via Kubectl

While working on kubectl-trace I needed to have the kubernetes pod be able to talk to a local HTTP service. kubectl supports setting up tunnels from the client to a pod but not in the opposite direction.

kubectl exec allows us to start a process and have bi-directional streaming between the client and the pod. So, in theory a reverse tunnel could be set up over these streams. It would look like

  • Run local service on port 8080
  • Start a process on the pod that listens on a port and streams data to stdout (and reads via stdin.)
  • Start a process on the client that connects to port 8080 and streams data via stdin/stdout.
  • Connect the stdout/stdin of client porxy to stdin/stdout of server proxy.

Luckily we already have a utility socat that can do most of this.

# Set up named pipes (fifo) for communication
$ mkfifo in out

# Start local server
$ python3 -m http.server 8080

# Make socat listen on fifo and connect to server
$ socat TCP4:localhost:8080,fork STDIO <in >out

# In a separate shell, make socat listen on pod
# Notice how it is writing to "in" and reading from "out" as they are connected to the socat above
$ kubectl exec -i pod/ubuntu -- socat TCP4-LISTEN:5000,bind=localhost,fork,reuseaddr STDIO >in <out

# Now curl-ing port 5000 from within the pod should work!
$ kubectl exec pod/ubuntu -- curl localhost:5000

Since this is a common use case socat has support for executing a process and swapping the streams. This removes the need for making explicit named pipes.

$ socat \
  TCP4:localhost:8080 \
  SYSTEM:'"kubectl exec -i pod/ubuntu -- socat TCP4-LISTEN:5000,bind=localhost,reuseaddr STDIO"'