Created
August 24, 2015 19:38
-
-
Save moul/a6cb3fc8685ca3aa2f87 to your computer and use it in GitHub Desktop.
ssh2docker tmp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"net" | |
"github.com/Sirupsen/logrus" | |
"github.com/moul/ssh2docker" | |
) | |
func main() { | |
server, err := ssh2docker.NewServer() | |
if err != nil { | |
logrus.Fatalf("Cannot create server: %v", err) | |
} | |
err = server.AddHostKeyFile("/Users/moul/Git/moul/ssh2docker/host_rsa") | |
if err != nil { | |
logrus.Fatalf("Cannot add host key file: %v", err) | |
} | |
listener, err := net.Listen("tcp", ":2222") | |
if err != nil { | |
logrus.Fatalf("Failed to start listener: %v", err) | |
} | |
logrus.Infof("Listening on port 2222") | |
for { | |
conn, err := listener.Accept() | |
if err != nil { | |
logrus.Error("Accept failed: %v", err) | |
continue | |
} | |
go server.Handle(conn) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ssh2docker | |
import ( | |
"encoding/binary" | |
"encoding/json" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"net" | |
"os" | |
"os/exec" | |
"sync" | |
"syscall" | |
"time" | |
"unsafe" | |
"github.com/Sirupsen/logrus" | |
"github.com/kr/pty" | |
"golang.org/x/crypto/ssh" | |
) | |
type sessionInfo struct { | |
User string | |
} | |
type Server struct { | |
sshConfig *ssh.ServerConfig | |
mu sync.RWMutex | |
sessions map[string]sessionInfo | |
} | |
type logEntry struct { | |
Timestamp string | |
Username string | |
Password string | |
ChannelTypes []string | |
RequestTypes []string | |
Error string | |
KeysOffered []string | |
ClientVersion string | |
} | |
func NewServer() (*Server, error) { | |
server := Server{ | |
sessions: make(map[string]sessionInfo), | |
} | |
server.sshConfig = &ssh.ServerConfig{ | |
PasswordCallback: server.PasswordCallback, | |
} | |
return &server, nil | |
} | |
func (s *Server) AddHostKeyFile(keypath string) error { | |
keystring, err := ioutil.ReadFile("host_rsa") | |
if err != nil { | |
return err | |
} | |
hostkey, err := ssh.ParsePrivateKey(keystring) | |
if err != nil { | |
return err | |
} | |
s.sshConfig.AddHostKey(hostkey) | |
return nil | |
} | |
func (s *Server) PasswordCallback(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { | |
s.mu.Lock() | |
session := s.sessions[string(conn.SessionID())] | |
session.User = conn.User() | |
// si.Keys | |
s.sessions[string(conn.SessionID())] = session | |
s.mu.Unlock() | |
return nil, nil | |
} | |
func (s *Server) Handle(netConn net.Conn) error { | |
le := &logEntry{Timestamp: time.Now().Format(time.RFC3339)} | |
defer json.NewEncoder(os.Stdout).Encode(le) | |
conn, chans, reqs, err := ssh.NewServerConn(netConn, s.sshConfig) | |
if err != nil { | |
le.Error = "Handshake failed: " + err.Error() | |
return err | |
} | |
defer func() { | |
s.mu.Lock() | |
delete(s.sessions, string(conn.SessionID())) | |
s.mu.Unlock() | |
conn.Close() | |
}() | |
go func(in <-chan *ssh.Request) { | |
for req := range in { | |
le.RequestTypes = append(le.RequestTypes, req.Type) | |
if req.WantReply { | |
req.Reply(false, nil) | |
} | |
} | |
}(reqs) | |
//s.mu.RLock() | |
//session := s.sessions[string(conn.ClientVersion())] | |
//s.mu.RUnlock() | |
le.Username = conn.User() | |
le.ClientVersion = fmt.Sprintf("%x", conn.ClientVersion()) | |
for newChannel := range chans { | |
le.ChannelTypes = append(le.ChannelTypes, newChannel.ChannelType()) | |
if newChannel.ChannelType() != "session" { | |
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") | |
continue | |
} | |
channel, requests, err := newChannel.Accept() | |
if err != nil { | |
le.Error = "Channel accept failed: " + err.Error() | |
return err | |
} | |
reqLock := &sync.Mutex{} | |
reqLock.Lock() | |
timeout := time.AfterFunc(30*time.Second, func() { reqLock.Unlock() }) | |
logrus.Infof("Creating pty...") | |
f, tty, err := pty.Open() | |
if err != nil { | |
le.Error = fmt.Sprintf("Could not start pty: %v", err) | |
continue | |
} | |
go func(in <-chan *ssh.Request) { | |
for req := range in { | |
le.RequestTypes = append(le.RequestTypes, req.Type) | |
ok := false | |
switch req.Type { | |
case "shell": | |
if len(req.Payload) != 0 { | |
break | |
} | |
ok = true | |
cmd := exec.Command("docker", "run", "-it", "--rm", conn.User(), "/bin/sh") | |
cmd.Env = []string{ | |
"TERM=xterm", | |
"DOCKER_HOST=" + os.Getenv("DOCKER_HOST"), | |
"DOCKER_CERT_PATH=" + os.Getenv("DOCKER_CERT_PATH"), | |
"DOCKER_TLS_VERIFY=" + os.Getenv("DOCKER_TLS_VERIFY"), | |
} | |
defer tty.Close() | |
cmd.Stdout = tty | |
cmd.Stdin = tty | |
cmd.Stderr = tty | |
cmd.SysProcAttr = &syscall.SysProcAttr{ | |
Setctty: true, | |
Setsid: true, | |
} | |
err := cmd.Start() | |
if err != nil { | |
le.Error = fmt.Sprintf("Could not start command: %v", err) | |
continue | |
} | |
var once sync.Once | |
close := func() { | |
channel.Close() | |
logrus.Infof("session closed") | |
} | |
go func() { | |
io.Copy(channel, f) | |
once.Do(close) | |
}() | |
go func() { | |
io.Copy(f, channel) | |
once.Do(close) | |
}() | |
case "pty-req": | |
ok = true | |
termLen := req.Payload[3] | |
termEnv := string(req.Payload[4 : termLen+4]) | |
w, h := parseDims(req.Payload[termLen+4:]) | |
SetWinsize(f.Fd(), w, h) | |
logrus.Infof("pty-req: %s", termEnv) | |
if timeout.Stop() { | |
reqLock.Unlock() | |
} | |
} | |
if req.WantReply { | |
req.Reply(ok, nil) | |
} | |
} | |
}(requests) | |
reqLock.Lock() | |
} | |
return nil | |
} | |
func parseDims(b []byte) (uint32, uint32) { | |
w := binary.BigEndian.Uint32(b) | |
h := binary.BigEndian.Uint32(b[4:]) | |
return w, h | |
} | |
type Winsize struct { | |
Height uint16 | |
Width uint16 | |
x uint16 // unused | |
y uint16 // unused | |
} | |
func SetWinsize(fd uintptr, w, h uint32) { | |
log.Printf("window resize %dx%d", w, h) | |
ws := &Winsize{Width: uint16(w), Height: uint16(h)} | |
syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment