mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-11-04 11:03:44 +00:00 
			
		
		
		
	Merge pull request #3831 from dgageot/support-docker-over-ssh
Support remote docker over ssh
This commit is contained in:
		@@ -5,23 +5,49 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/reference"
 | 
			
		||||
	"github.com/docker/cli/cli/connhelper"
 | 
			
		||||
	dockertypes "github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/docker/docker/api/types/container"
 | 
			
		||||
	"github.com/docker/docker/client"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	clientOnce     sync.Once
 | 
			
		||||
	memoizedClient *client.Client
 | 
			
		||||
	errClient      error
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client get a docker client.
 | 
			
		||||
func Client() (*client.Client, error) {
 | 
			
		||||
	// for maximum compatibility as we use nothing new
 | 
			
		||||
	// 1.30 corresponds to Docker 17.06, supported until 2020.
 | 
			
		||||
	err := os.Setenv("DOCKER_API_VERSION", "1.30")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	clientOnce.Do(func() {
 | 
			
		||||
		memoizedClient, errClient = createClient()
 | 
			
		||||
	})
 | 
			
		||||
	return memoizedClient, errClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createClient() (*client.Client, error) {
 | 
			
		||||
	options := []client.Opt{
 | 
			
		||||
		client.WithAPIVersionNegotiation(),
 | 
			
		||||
		client.WithTLSClientConfigFromEnv(),
 | 
			
		||||
		client.WithHostFromEnv(),
 | 
			
		||||
	}
 | 
			
		||||
	return client.NewEnvClient()
 | 
			
		||||
 | 
			
		||||
	// Support connection over ssh.
 | 
			
		||||
	if host := os.Getenv(client.EnvOverrideHost); host != "" {
 | 
			
		||||
		helper, err := connhelper.GetConnectionHelper(host)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if helper != nil {
 | 
			
		||||
			options = append(options, client.WithDialContext(helper.Dialer))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return client.NewClientWithOpts(options...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasImage check if the provided ref is available in the docker cache.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								src/cmd/linuxkit/vendor/github.com/docker/cli/cli/connhelper/connhelper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/cmd/linuxkit/vendor/github.com/docker/cli/cli/connhelper/connhelper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
 | 
			
		||||
package connhelper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/cli/cli/connhelper/commandconn"
 | 
			
		||||
	"github.com/docker/cli/cli/connhelper/ssh"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConnectionHelper allows to connect to a remote host with custom stream provider binary.
 | 
			
		||||
type ConnectionHelper struct {
 | 
			
		||||
	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
 | 
			
		||||
	Host   string // dummy URL used for HTTP requests. e.g. "http://docker"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConnectionHelper returns Docker-specific connection helper for the given URL.
 | 
			
		||||
// GetConnectionHelper returns nil without error when no helper is registered for the scheme.
 | 
			
		||||
//
 | 
			
		||||
// ssh://<user>@<host> URL requires Docker 18.09 or later on the remote host.
 | 
			
		||||
func GetConnectionHelper(daemonURL string) (*ConnectionHelper, error) {
 | 
			
		||||
	return getConnectionHelper(daemonURL, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConnectionHelperWithSSHOpts returns Docker-specific connection helper for
 | 
			
		||||
// the given URL, and accepts additional options for ssh connections. It returns
 | 
			
		||||
// nil without error when no helper is registered for the scheme.
 | 
			
		||||
//
 | 
			
		||||
// Requires Docker 18.09 or later on the remote host.
 | 
			
		||||
func GetConnectionHelperWithSSHOpts(daemonURL string, sshFlags []string) (*ConnectionHelper, error) {
 | 
			
		||||
	return getConnectionHelper(daemonURL, sshFlags)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper, error) {
 | 
			
		||||
	u, err := url.Parse(daemonURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	switch scheme := u.Scheme; scheme {
 | 
			
		||||
	case "ssh":
 | 
			
		||||
		sp, err := ssh.ParseURL(daemonURL)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.Wrap(err, "ssh host connection is not valid")
 | 
			
		||||
		}
 | 
			
		||||
		return &ConnectionHelper{
 | 
			
		||||
			Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
 | 
			
		||||
				return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args("docker", "system", "dial-stdio")...)...)
 | 
			
		||||
			},
 | 
			
		||||
			Host: "http://docker.example.com",
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	// Future version may support plugins via ~/.docker/config.json. e.g. "dind"
 | 
			
		||||
	// See docker/cli#889 for the previous discussion.
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCommandConnectionHelper returns Docker-specific connection helper constructed from an arbitrary command.
 | 
			
		||||
func GetCommandConnectionHelper(cmd string, flags ...string) (*ConnectionHelper, error) {
 | 
			
		||||
	return &ConnectionHelper{
 | 
			
		||||
		Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
 | 
			
		||||
			return commandconn.New(ctx, cmd, flags...)
 | 
			
		||||
		},
 | 
			
		||||
		Host: "http://docker.example.com",
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								src/cmd/linuxkit/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/cmd/linuxkit/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
// Package ssh provides the connection helper for ssh:// URL.
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ParseURL parses URL
 | 
			
		||||
func ParseURL(daemonURL string) (*Spec, error) {
 | 
			
		||||
	u, err := url.Parse(daemonURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if u.Scheme != "ssh" {
 | 
			
		||||
		return nil, errors.Errorf("expected scheme ssh, got %q", u.Scheme)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var sp Spec
 | 
			
		||||
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		sp.User = u.User.Username()
 | 
			
		||||
		if _, ok := u.User.Password(); ok {
 | 
			
		||||
			return nil, errors.New("plain-text password is not supported")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sp.Host = u.Hostname()
 | 
			
		||||
	if sp.Host == "" {
 | 
			
		||||
		return nil, errors.Errorf("no host specified")
 | 
			
		||||
	}
 | 
			
		||||
	sp.Port = u.Port()
 | 
			
		||||
	if u.Path != "" {
 | 
			
		||||
		return nil, errors.Errorf("extra path after the host: %q", u.Path)
 | 
			
		||||
	}
 | 
			
		||||
	if u.RawQuery != "" {
 | 
			
		||||
		return nil, errors.Errorf("extra query after the host: %q", u.RawQuery)
 | 
			
		||||
	}
 | 
			
		||||
	if u.Fragment != "" {
 | 
			
		||||
		return nil, errors.Errorf("extra fragment after the host: %q", u.Fragment)
 | 
			
		||||
	}
 | 
			
		||||
	return &sp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Spec of SSH URL
 | 
			
		||||
type Spec struct {
 | 
			
		||||
	User string
 | 
			
		||||
	Host string
 | 
			
		||||
	Port string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Args returns args except "ssh" itself combined with optional additional command args
 | 
			
		||||
func (sp *Spec) Args(add ...string) []string {
 | 
			
		||||
	var args []string
 | 
			
		||||
	if sp.User != "" {
 | 
			
		||||
		args = append(args, "-l", sp.User)
 | 
			
		||||
	}
 | 
			
		||||
	if sp.Port != "" {
 | 
			
		||||
		args = append(args, "-p", sp.Port)
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "--", sp.Host)
 | 
			
		||||
	args = append(args, add...)
 | 
			
		||||
	return args
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								src/cmd/linuxkit/vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/cmd/linuxkit/vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -160,7 +160,9 @@ github.com/docker/cli/cli/config
 | 
			
		||||
github.com/docker/cli/cli/config/configfile
 | 
			
		||||
github.com/docker/cli/cli/config/credentials
 | 
			
		||||
github.com/docker/cli/cli/config/types
 | 
			
		||||
github.com/docker/cli/cli/connhelper
 | 
			
		||||
github.com/docker/cli/cli/connhelper/commandconn
 | 
			
		||||
github.com/docker/cli/cli/connhelper/ssh
 | 
			
		||||
# github.com/docker/distribution v2.8.1+incompatible
 | 
			
		||||
github.com/docker/distribution
 | 
			
		||||
github.com/docker/distribution/digestset
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user