mirror of
https://github.com/bytedream/docker4ssh.git
synced 2025-06-27 09:50:31 +02:00
Initial commit
This commit is contained in:
18
server/cmd/cmd.go
Normal file
18
server/cmd/cmd.go
Normal file
@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "docker4ssh",
|
||||
Short: "Docker containers and more via ssh",
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
}
|
||||
}
|
160
server/cmd/start.go
Normal file
160
server/cmd/start.go
Normal file
@ -0,0 +1,160 @@
|
||||
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)
|
||||
}
|
159
server/cmd/validate.go
Normal file
159
server/cmd/validate.go
Normal file
@ -0,0 +1,159 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
c "docker4ssh/config"
|
||||
"docker4ssh/docker"
|
||||
"docker4ssh/validate"
|
||||
"fmt"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cli *client.Client
|
||||
|
||||
var validateCmd = &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validate docker4ssh specific files (config / profile files)",
|
||||
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
cli, err = docker.InitCli()
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
var validateStrictFlag bool
|
||||
|
||||
var validateConfigCmd = &cobra.Command{
|
||||
Use: "config [files]",
|
||||
Short: "Validate a docker4ssh config file",
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return validateConfig(args)
|
||||
},
|
||||
}
|
||||
|
||||
var validateConfigFileFlag string
|
||||
|
||||
var validateProfileCmd = &cobra.Command{
|
||||
Use: "profile [files]",
|
||||
Short: "Validate docker4ssh profile files",
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return validateProfile(args)
|
||||
},
|
||||
}
|
||||
|
||||
func validateConfig(args []string) error {
|
||||
config, err := c.LoadConfig(validateConfigFileFlag, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validator := validate.NewConfigValidator(cli, validateStrictFlag, config)
|
||||
|
||||
var result *validate.ValidatorResult
|
||||
if len(args) == 0 {
|
||||
result = validator.Validate()
|
||||
} else {
|
||||
var validateFuncs []func() *validate.ValidatorResult
|
||||
for _, arg := range args {
|
||||
switch strings.ToLower(arg) {
|
||||
case "profile":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateProfile)
|
||||
case "api":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateAPI)
|
||||
case "ssh":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateSSH)
|
||||
case "database":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateDatabase)
|
||||
case "network":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateNetwork)
|
||||
case "logging":
|
||||
validateFuncs = append(validateFuncs, validator.ValidateLogging)
|
||||
default:
|
||||
return fmt.Errorf("'%s' is not a valid config section", arg)
|
||||
}
|
||||
}
|
||||
|
||||
var errors []*validate.ValidateError
|
||||
for _, validateFunc := range validateFuncs {
|
||||
errors = append(errors, validateFunc().Errors...)
|
||||
}
|
||||
|
||||
result = &validate.ValidatorResult{
|
||||
Strict: validateStrictFlag,
|
||||
Errors: errors,
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(result.String())
|
||||
|
||||
if len(result.Errors) > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateProfile(args []string) error {
|
||||
var files []string
|
||||
|
||||
if len(args) == 0 {
|
||||
args = append(args, "/etc/docker4ssh/profile")
|
||||
}
|
||||
for _, arg := range args {
|
||||
stat, err := os.Stat(arg)
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("file %s does not exist: %v", arg, err)
|
||||
}
|
||||
if stat.IsDir() {
|
||||
dir, err := os.ReadDir(arg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read directory %s: %v", arg, err)
|
||||
}
|
||||
for _, file := range dir {
|
||||
path, err := filepath.Abs(file.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var profiles c.Profiles
|
||||
for _, file := range files {
|
||||
p, err := c.LoadProfileFile(file, c.HardcodedPreProfile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profiles = append(profiles, p...)
|
||||
}
|
||||
|
||||
var errors []*validate.ValidateError
|
||||
for _, profile := range profiles {
|
||||
errors = append(errors, validate.NewProfileValidator(cli, validateStrictFlag, profile).Validate().Errors...)
|
||||
}
|
||||
|
||||
result := validate.ValidatorResult{
|
||||
Strict: validateStrictFlag,
|
||||
Errors: errors,
|
||||
}
|
||||
|
||||
fmt.Println(result.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(validateCmd)
|
||||
validateCmd.PersistentFlags().BoolVarP(&validateStrictFlag, "strict", "s", false, "If the check should be strict")
|
||||
|
||||
validateCmd.AddCommand(validateConfigCmd)
|
||||
validateConfigCmd.Flags().StringVarP(&validateConfigFileFlag, "file", "f", "/etc/docker4ssh/docker4ssh.conf", "Specify a file to check")
|
||||
|
||||
validateCmd.AddCommand(validateProfileCmd)
|
||||
}
|
Reference in New Issue
Block a user