Skip to content

Add password-auth-script support #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
45 changes: 43 additions & 2 deletions auth.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package ssh2docker

import (
"encoding/json"
"fmt"
"os/exec"

"github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus"
"github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh"
)

Expand All @@ -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")
}
23 changes: 16 additions & 7 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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++
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions cmd/ssh2docker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 10 additions & 7 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
22 changes: 22 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -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
}