From e7b2cec800059c3c615cdb6a0bc158fd169d22e1 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 30 Sep 2015 10:18:35 +0200 Subject: [PATCH 1/2] Add support of password-auth-script (fix #28) --- README.md | 1 + auth.go | 45 ++++++++++++++++++++++++++++++++++++++++-- client.go | 23 ++++++++++++++------- cmd/ssh2docker/main.go | 5 +++++ server.go | 17 +++++++++------- utils.go | 22 +++++++++++++++++++++ 6 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 utils.go diff --git a/README.md b/README.md index 7c04258..d59b833 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ GLOBAL OPTIONS: ### master (unreleased) +* Support of 'ssh2docker --password-auth-script' options ([#28](https://github.com/moul/ssh2docker/issues/28)) * Add docker support ([#17](https://github.com/moul/ssh2docker/issues/17)) * Add GOXC support to build binaries for multiple architectures ([#18](https://github.com/moul/ssh2docker/issues/18)) * Support of 'ssh2docker --clean-on-startup' ([#23](https://github.com/moul/ssh2docker/issues/23)) diff --git a/auth.go b/auth.go index 010646c..bc8694a 100644 --- a/auth.go +++ b/auth.go @@ -1,8 +1,11 @@ package ssh2docker import ( + "encoding/json" "fmt" + "os/exec" + "github.com/Sirupsen/logrus" "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) @@ -20,10 +23,48 @@ func (s *Server) ImageIsAllowed(target string) bool { } // PasswordCallback is called when the user tries to authenticate using a password -func (s *Server) PasswordCallback(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { - image := conn.User() +func (s *Server) PasswordCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + username := conn.User() + clientID := conn.RemoteAddr().String() + + logrus.Debugf("PasswordCallback: %q %q", username, password) + var image string + + if s.PasswordAuthScript != "" { + // Using a hook script + script, err := expandUser(s.PasswordAuthScript) + if err != nil { + logrus.Warnf("Failed to expandUser: %v", err) + return nil, err + } + cmd := exec.Command(script, username, string(password)) + output, err := cmd.CombinedOutput() + if err != nil { + logrus.Warnf("Failed to execute password-auth-script: %v", err) + return nil, err + } + + var config ClientConfig + err = json.Unmarshal(output, &config) + if err != nil { + logrus.Warnf("Failed to unmarshal json %q: %v", string(output), err) + return nil, err + } + s.ClientConfigs[clientID] = &config + if config.Allowed == false { + logrus.Warnf("Hook returned allowed:false") + return nil, fmt.Errorf("Access not allowed") + } + + return nil, nil + } else { + // Default behavior + image = username + } + if s.ImageIsAllowed(image) { return nil, nil } + return nil, fmt.Errorf("TEST") } diff --git a/client.go b/client.go index 7b7ecda..b2e9bf9 100644 --- a/client.go +++ b/client.go @@ -26,8 +26,13 @@ type Client struct { Server *Server Pty, Tty *os.File Env Environment - RemoteUser string - ImageName string + Config *ClientConfig +} + +type ClientConfig struct { + ImageName string `json:"image-name",omitempty` + RemoteUser string `json:"remote-user",omitempty` + Allowed bool `json:"allowed",omitempty` } // NewClient initializes a new client @@ -39,14 +44,18 @@ func NewClient(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *s Chans: chans, Reqs: reqs, Server: server, - ImageName: conn.User(), - RemoteUser: "nobody", Env: Environment{ "TERM": os.Getenv("TERM"), "DOCKER_HOST": os.Getenv("DOCKER_HOST"), "DOCKER_CERT_PATH": os.Getenv("DOCKER_CERT_PATH"), "DOCKER_TLS_VERIFY": os.Getenv("DOCKER_TLS_VERIFY"), }, + + // Default ClientConfig, maybe we should completely remove it + Config: &ClientConfig{ + ImageName: conn.User(), + RemoteUser: "anonymous", + }, } clientCounter++ @@ -124,7 +133,7 @@ func (c *Client) HandleChannelRequests(channel ssh.Channel, requests <-chan *ssh // checking if a container already exists for this user existingContainer := "" if !c.Server.NoJoin { - cmd := exec.Command("docker", "ps", "--filter=label=ssh2docker", fmt.Sprintf("--filter=label=image=%s", c.ImageName), fmt.Sprintf("--filter=label=user=%s", c.RemoteUser), "--quiet", "--no-trunc") + cmd := exec.Command("docker", "ps", "--filter=label=ssh2docker", fmt.Sprintf("--filter=label=image=%s", c.Config.ImageName), fmt.Sprintf("--filter=label=user=%s", c.Config.RemoteUser), "--quiet", "--no-trunc") buf, err := cmd.CombinedOutput() if err != nil { logrus.Warnf("docker ps ... failed: %v", err) @@ -146,8 +155,8 @@ func (c *Client) HandleChannelRequests(channel ssh.Channel, requests <-chan *ssh // Creating and attaching to a new container args := []string{"run"} args = append(args, c.Server.DockerRunArgs...) - args = append(args, "--label=ssh2docker", fmt.Sprintf("--label=user=%s", c.RemoteUser), fmt.Sprintf("--label=image=%s", c.ImageName)) - args = append(args, c.ImageName, c.Server.DefaultShell) + args = append(args, "--label=ssh2docker", fmt.Sprintf("--label=user=%s", c.Config.RemoteUser), fmt.Sprintf("--label=image=%s", c.Config.ImageName)) + args = append(args, c.Config.ImageName, c.Server.DefaultShell) logrus.Debugf("Executing 'docker %s'", strings.Join(args, " ")) cmd = exec.Command("docker", args...) cmd.Env = c.Env.List() diff --git a/cmd/ssh2docker/main.go b/cmd/ssh2docker/main.go index 4d05e09..808a83d 100644 --- a/cmd/ssh2docker/main.go +++ b/cmd/ssh2docker/main.go @@ -93,6 +93,10 @@ func main() { Name: "clean-on-startup", Usage: "Cleanup Docker containers created by ssh2docker on start", }, + cli.StringFlag{ + Name: "password-auth-script", + Usage: "Password auth hook file", + }, } app.Action = Action @@ -128,6 +132,7 @@ func Action(c *cli.Context) { server.DockerRunArgs = strings.Split(c.String("docker-run-args"), " ") server.NoJoin = c.Bool("no-join") server.CleanOnStartup = c.Bool("clean-on-startup") + server.PasswordAuthScript = c.String("password-auth-script") // Register the SSH host key hostKey := c.String("host-key") diff --git a/server.go b/server.go index bf8b6e6..4322356 100644 --- a/server.go +++ b/server.go @@ -10,13 +10,15 @@ import ( // Server is the ssh2docker main structure type Server struct { SshConfig *ssh.ServerConfig - // Clients []Client + // Clients map[string]Client + ClientConfigs map[string]*ClientConfig - AllowedImages []string - DefaultShell string - DockerRunArgs []string - NoJoin bool - CleanOnStartup bool + AllowedImages []string + DefaultShell string + DockerRunArgs []string + NoJoin bool + CleanOnStartup bool + PasswordAuthScript string initialized bool } @@ -27,7 +29,7 @@ func NewServer() (*Server, error) { server.SshConfig = &ssh.ServerConfig{ PasswordCallback: server.PasswordCallback, } - server.AllowedImages = nil + server.ClientConfigs = make(map[string]*ClientConfig, 0) server.DefaultShell = "/bin/sh" server.DockerRunArgs = []string{"-it", "--rm"} return &server, nil @@ -64,6 +66,7 @@ func (s *Server) Handle(netConn net.Conn) error { return err } client := NewClient(conn, chans, reqs, s) + client.Config = s.ClientConfigs[conn.RemoteAddr().String()] // Handle requests if err = client.HandleRequests(); err != nil { diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..122b313 --- /dev/null +++ b/utils.go @@ -0,0 +1,22 @@ +package ssh2docker + +import ( + "errors" + "os" + "strings" +) + +func expandUser(path string) (string, error) { + if path[:2] == "~/" { + homeDir := os.Getenv("HOME") // *nix + if homeDir == "" { // Windows + homeDir = os.Getenv("USERPROFILE") + } + if homeDir == "" { + return "", errors.New("user home directory not found") + } + + return strings.Replace(path, "~", homeDir, 1), nil + } + return path, nil +} From 2a3ef6d484ec5be0b5aada769ffc23bcb2233a17 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 30 Sep 2015 10:18:48 +0200 Subject: [PATCH 2/2] party -c -t -d=vendor --- auth.go | 2 +- docker.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auth.go b/auth.go index bc8694a..f20906f 100644 --- a/auth.go +++ b/auth.go @@ -5,7 +5,7 @@ import ( "fmt" "os/exec" - "github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) diff --git a/docker.go b/docker.go index 2953de5..774d528 100644 --- a/docker.go +++ b/docker.go @@ -4,7 +4,7 @@ import ( "os/exec" "strings" - "github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" ) // DockerCleanup cleans all containers created by ssh2docker