mirror of
https://github.com/bytedream/docker4ssh.git
synced 2025-05-09 12:15:11 +02:00
161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
package cmd
|
|
|
|
import (
|
|
"docker4ssh/api"
|
|
c "docker4ssh/config"
|
|
"docker4ssh/database"
|
|
"docker4ssh/docker"
|
|
"docker4ssh/logging"
|
|
"docker4ssh/ssh"
|
|
"docker4ssh/validate"
|
|
"fmt"
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/zap"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
var startCmd = &cobra.Command{
|
|
Use: "start",
|
|
Short: "Starts the docker4ssh server",
|
|
Args: cobra.MaximumNArgs(0),
|
|
|
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
|
return preStart()
|
|
},
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
start()
|
|
},
|
|
}
|
|
|
|
func preStart() error {
|
|
if !docker.IsRunning() {
|
|
return fmt.Errorf("docker daemon is not running")
|
|
}
|
|
|
|
cli, err := docker.InitCli()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
config, err := c.InitConfig(true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
validator := validate.NewConfigValidator(cli, false, config)
|
|
|
|
if result := validator.ValidateLogging(); !result.Ok() {
|
|
return fmt.Errorf(result.String())
|
|
}
|
|
|
|
level := zap.NewAtomicLevel()
|
|
level.UnmarshalText([]byte(config.Logging.Level))
|
|
var outputFiles, errorFiles []string
|
|
if config.Logging.ConsoleOutput {
|
|
outputFiles = append(outputFiles, "/dev/stdout")
|
|
}
|
|
if config.Logging.OutputFile != "" {
|
|
outputFiles = append(outputFiles, config.Logging.OutputFile)
|
|
}
|
|
if config.Logging.ConsoleError {
|
|
errorFiles = append(errorFiles, "/dev/stderr")
|
|
}
|
|
if config.Logging.ErrorFile != "" {
|
|
errorFiles = append(errorFiles, config.Logging.ErrorFile)
|
|
}
|
|
logging.InitLogging(level, outputFiles, errorFiles)
|
|
|
|
if result := validator.Validate(); !result.Ok() {
|
|
return fmt.Errorf(result.String())
|
|
}
|
|
c.SetConfig(config)
|
|
|
|
db, err := database.NewSqlite3Connection(config.Database.Sqlite3File)
|
|
if err != nil {
|
|
zap.S().Fatalf("Failed to initialize database: %v", err)
|
|
}
|
|
database.SetDatabase(db)
|
|
|
|
return nil
|
|
}
|
|
|
|
func start() {
|
|
config := c.GetConfig()
|
|
|
|
if config.SSH.Passphrase == "" {
|
|
zap.S().Warn("YOU HAVE AN EMPTY PASSPHRASE WHICH IS INSECURE, SUGGESTING CREATING A NEW SSH KEY WITH A PASSPHRASE.\n" +
|
|
"IF YOU'RE DOWNLOADED THIS VERSION FROM THE RELEASES (https://github.com/ByteDream/docker4ssh/releases/latest), MAKE SURE TO CHANGE YOUR SSH KEY IMMEDIATELY BECAUSE ANYONE COULD DECRYPT THE SSH SESSION!!\n" +
|
|
"USE 'ssh-keygen -t ed25519 -f /etc/docker4ssh/docker4ssh.key -b 4096' AND UPDATE THE PASSPHRASE IN /etc/docker4ssh/docker4ssh.conf UNDER ssh.Passphrase")
|
|
}
|
|
|
|
serverConfig, err := ssh.NewSSHConfig(config)
|
|
if err != nil {
|
|
zap.S().Fatalf("Failed to initialize ssh server config: %v", err)
|
|
}
|
|
|
|
sshErrChan, sshCloser := ssh.StartServing(config, serverConfig)
|
|
zap.S().Infof("Started ssh serving on port %d", config.SSH.Port)
|
|
apiErrChan, apiCloser := api.ServeAPI(config)
|
|
zap.S().Infof("Started api serving on port %d", config.Api.Port)
|
|
|
|
done := make(chan struct{})
|
|
sig := make(chan os.Signal)
|
|
signal.Notify(sig, syscall.SIGUSR1, os.Interrupt, os.Kill, syscall.SIGINT, syscall.SIGTERM)
|
|
go func() {
|
|
s := <-sig
|
|
|
|
if sshCloser != nil {
|
|
sshCloser()
|
|
}
|
|
if apiCloser != nil {
|
|
apiCloser()
|
|
}
|
|
|
|
database.GetDatabase().Close()
|
|
|
|
if s != syscall.SIGUSR1 {
|
|
// Errorf is called here instead of Fatalf because the original exit signal should be kept to exit with it later
|
|
zap.S().Errorf("(FATAL actually) received abort signal %d: %s", s.(syscall.Signal), strings.ToUpper(s.String()))
|
|
os.Exit(int(s.(syscall.Signal)))
|
|
}
|
|
|
|
done <- struct{}{}
|
|
}()
|
|
|
|
select {
|
|
case err = <-sshErrChan:
|
|
case err = <-apiErrChan:
|
|
}
|
|
|
|
if err != nil {
|
|
zap.S().Errorf("Failed to start working: %v", err)
|
|
sig <- os.Interrupt
|
|
} else {
|
|
select {
|
|
case <-sig:
|
|
if err != nil {
|
|
zap.S().Errorf("Serving failed due error: %v", err)
|
|
} else {
|
|
zap.S().Info("Serving stopped")
|
|
}
|
|
default:
|
|
sig <- syscall.SIGUSR1
|
|
}
|
|
}
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(5 * time.Second):
|
|
// if the timeout of 5 seconds expires, forcefully exit
|
|
os.Exit(int(syscall.SIGKILL))
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(startCmd)
|
|
}
|