Docker Tunneling in Go
A trick i learned recently is that Docker client in Golang can accept custom dialer which can be so useful to connect to a remote Docker engine through SSH tunneling, all you need is a working SSH connection to a remote server and Docker engine on this server that listens on a local UNIX socket.
First let’s look at the NewClient
function for Docker client:
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error)
As you can see NewClient accepts host, version, http transport client which we will use to add the custom dailer, and finally httpHeaders that can be added to each request. First define the custom dialer that will use SSH to connect to the remote host and establish a tunnel connection to the Docker socket on this host:
type dialer struct {
host string
}
func (d *dialer) Dial(network, addr string) (net.Conn, error) {
sshAddr := d.host + ":22"
// Build SSH client configuration
cfg, err := makeSSHConfig()
if err != nil {
logrus.Fatalf("Error configuring SSH: %v", err)
}
// Establish connection with SSH server
conn, err := ssh.Dial("tcp", sshAddr, cfg)
if err != nil {
logrus.Fatalf("Error establishing SSH connection: %v", err)
}
remote, err := conn.Dial("unix", "/var/run/docker.sock")
if err != nil {
logrus.Fatalf("Error connecting to Docker socket: %v", err)
}
return remote, err
}
The makeSSHConfig
will return ssh.ClientConfig
that contain the authentication method, user, etc. To start using this custom dialer to initialize a Docker client, try using the following function:
func StartDockerTunnel() (*client.Client, error) {
dialer := &dialer{
host: "example-host.com",
}
httpClient := &http.Client{
Transport: &http.Transport{
Dial: dialer.Dial,
},
}
newClient, err := client.NewClient("unix:///var/run/docker.sock", DockerAPIVersion, httpClient, nil)
if err != nil {
return nil, fmt.Errorf("Can't connect to Docker: %v", err)
}
return newClient, nil
}
StartDockerTunnel
function initialezes a new Docker client and sets http client to our custom client that we just defined with the custom Dialer which in turn will connect directly to example-host.com
through ssh and establish a connection with the /var/run/docker.sock
at the remote server.