mirror of
https://github.com/bytedream/docker4ssh.git
synced 2025-05-09 12:15:11 +02:00
191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package ssh
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
c "docker4ssh/config"
|
|
"docker4ssh/database"
|
|
"docker4ssh/docker"
|
|
"docker4ssh/terminal"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/crypto/ssh"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
users = make([]*User, 0)
|
|
|
|
profiles c.Profiles
|
|
dynamicProfile c.Profile
|
|
)
|
|
|
|
type User struct {
|
|
*ssh.ServerConn
|
|
|
|
ID string
|
|
IP string
|
|
Profile *c.Profile
|
|
Terminal *terminal.Terminal
|
|
Container *docker.SimpleContainer
|
|
}
|
|
|
|
func GetUser(ip string) *User {
|
|
for _, user := range users {
|
|
if container := user.Container; container != nil && container.Network.IP == ip {
|
|
return user
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type extras struct {
|
|
containerID string
|
|
}
|
|
|
|
func StartServing(config *c.Config, serverConfig *ssh.ServerConfig) (errChan chan error, closer func() error) {
|
|
errChan = make(chan error, 1)
|
|
|
|
var err error
|
|
profiles, err = c.LoadProfileDir(config.Profile.Dir, c.DefaultPreProfileFromConfig(config))
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
zap.S().Debugf("Loaded %d profile(s)", len(profiles))
|
|
|
|
if config.Profile.Dynamic.Enable {
|
|
dynamicProfile, err = c.DynamicProfileFromConfig(config, c.DefaultPreProfileFromConfig(config))
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
zap.S().Debugf("Loaded dynamic profile")
|
|
}
|
|
|
|
cli, err := docker.InitCli()
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
zap.S().Debugf("Initialized docker cli")
|
|
|
|
network, err := docker.InitNetwork(context.Background(), cli, config)
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
zap.S().Debugf("Initialized docker networks")
|
|
|
|
client := &docker.Client{
|
|
Client: cli,
|
|
Database: database.GetDatabase(),
|
|
Network: network,
|
|
}
|
|
|
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", config.SSH.Port))
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
zap.S().Debugf("Created ssh listener")
|
|
|
|
var closed bool
|
|
go func() {
|
|
db := database.GetDatabase()
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
if closed {
|
|
return
|
|
}
|
|
zap.S().Errorf("Failed to accept new ssh user: %v", err)
|
|
continue
|
|
}
|
|
serverConn, chans, requests, err := ssh.NewServerConn(conn, serverConfig)
|
|
if err != nil {
|
|
zap.S().Errorf("Failed to establish new ssh connection: %v", err)
|
|
continue
|
|
}
|
|
|
|
idBytes := md5.Sum([]byte(strings.Split(serverConn.User(), ":")[0]))
|
|
idString := hex.EncodeToString(idBytes[:])
|
|
|
|
zap.S().Infof("New ssh connection from %s with %s (%s)", serverConn.RemoteAddr().String(), serverConn.ClientVersion(), idString)
|
|
|
|
var profile *c.Profile
|
|
if name, ok := serverConn.Permissions.CriticalOptions["profile"]; ok {
|
|
if name == "dynamic" {
|
|
if image, ok := serverConn.Permissions.CriticalOptions["image"]; ok {
|
|
tempDynamicProfile := dynamicProfile
|
|
tempDynamicProfile.Image = image
|
|
profile = &tempDynamicProfile
|
|
}
|
|
}
|
|
if profile == nil {
|
|
if profile, ok = profiles.GetByName(name); !ok {
|
|
zap.S().Errorf("Failed to get profile %s", name)
|
|
continue
|
|
}
|
|
}
|
|
} else if containerID, ok := serverConn.Permissions.CriticalOptions["containerID"]; ok {
|
|
if settings, err := db.SettingsByContainerID(containerID); err == nil {
|
|
profile = &c.Profile{
|
|
NetworkMode: *settings.NetworkMode,
|
|
Configurable: *settings.Configurable,
|
|
RunLevel: *settings.RunLevel,
|
|
StartupInformation: *settings.StartupInformation,
|
|
ExitAfter: *settings.ExitAfter,
|
|
KeepOnExit: *settings.KeepOnExit,
|
|
ContainerID: containerID,
|
|
}
|
|
} else {
|
|
for _, container := range allContainers {
|
|
if container.ContainerID == containerID {
|
|
cconfig := c.GetConfig()
|
|
profile = &c.Profile{
|
|
Password: regexp.MustCompile(cconfig.Profile.Default.Password),
|
|
NetworkMode: cconfig.Profile.Default.NetworkMode,
|
|
Configurable: cconfig.Profile.Default.Configurable,
|
|
RunLevel: cconfig.Profile.Default.RunLevel,
|
|
StartupInformation: cconfig.Profile.Default.StartupInformation,
|
|
ExitAfter: cconfig.Profile.Default.ExitAfter,
|
|
KeepOnExit: cconfig.Profile.Default.KeepOnExit,
|
|
Image: "",
|
|
ContainerID: containerID,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
zap.S().Debugf("User %s has profile %s", idString, profile.Name())
|
|
|
|
user := &User{
|
|
ServerConn: serverConn,
|
|
ID: idString,
|
|
Terminal: &terminal.Terminal{},
|
|
Profile: profile,
|
|
}
|
|
users = append(users, user)
|
|
|
|
go ssh.DiscardRequests(requests)
|
|
go handleChannels(chans, client, user)
|
|
}
|
|
}()
|
|
|
|
return errChan, func() error {
|
|
closed = true
|
|
|
|
// close all containers
|
|
closeAllContainers(context.Background())
|
|
|
|
// close the listener
|
|
return listener.Close()
|
|
}
|
|
}
|