| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 | 
							- package cmd
 
- import (
 
- 	"io"
 
- 	"os"
 
- 	"os/user"
 
- 	"path/filepath"
 
- 	"github.com/rs/xid"
 
- 	"github.com/rs/zerolog"
 
- 	"github.com/spf13/cobra"
 
- 	"github.com/spf13/viper"
 
- 	"github.com/drakkan/sftpgo/v2/common"
 
- 	"github.com/drakkan/sftpgo/v2/config"
 
- 	"github.com/drakkan/sftpgo/v2/dataprovider"
 
- 	"github.com/drakkan/sftpgo/v2/logger"
 
- 	"github.com/drakkan/sftpgo/v2/sdk/plugin"
 
- 	"github.com/drakkan/sftpgo/v2/sftpd"
 
- 	"github.com/drakkan/sftpgo/v2/version"
 
- )
 
- var (
 
- 	logJournalD     = false
 
- 	preserveHomeDir = false
 
- 	baseHomeDir     = ""
 
- 	subsystemCmd    = &cobra.Command{
 
- 		Use:   "startsubsys",
 
- 		Short: "Use SFTPGo as SFTP file transfer subsystem",
 
- 		Long: `In this mode SFTPGo speaks the server side of SFTP protocol to stdout and
 
- expects client requests from stdin.
 
- This mode is not intended to be called directly, but from sshd using the
 
- Subsystem option.
 
- For example adding a line like this one in "/etc/ssh/sshd_config":
 
- Subsystem	sftp	sftpgo startsubsys
 
- Command-line flags should be specified in the Subsystem declaration.
 
- `,
 
- 		Run: func(cmd *cobra.Command, args []string) {
 
- 			logSender := "startsubsys"
 
- 			connectionID := xid.New().String()
 
- 			logLevel := zerolog.DebugLevel
 
- 			if !logVerbose {
 
- 				logLevel = zerolog.InfoLevel
 
- 			}
 
- 			if logJournalD {
 
- 				logger.InitJournalDLogger(logLevel)
 
- 			} else {
 
- 				logger.InitStdErrLogger(logLevel)
 
- 			}
 
- 			osUser, err := user.Current()
 
- 			if err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to get the current user: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			username := osUser.Username
 
- 			homedir := osUser.HomeDir
 
- 			logger.Info(logSender, connectionID, "starting SFTPGo %v as subsystem, user %#v home dir %#v config dir %#v base home dir %#v",
 
- 				version.Get(), username, homedir, configDir, baseHomeDir)
 
- 			err = config.LoadConfig(configDir, configFile)
 
- 			if err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to load configuration: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			commonConfig := config.GetCommonConfig()
 
- 			// idle connection are managed externally
 
- 			commonConfig.IdleTimeout = 0
 
- 			config.SetCommonConfig(commonConfig)
 
- 			if err := common.Initialize(config.GetCommonConfig()); err != nil {
 
- 				logger.Error(logSender, connectionID, "%v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			kmsConfig := config.GetKMSConfig()
 
- 			if err := kmsConfig.Initialize(); err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to initialize KMS: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			if err := plugin.Initialize(config.GetPluginsConfig(), logVerbose); err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to initialize plugin system: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			dataProviderConf := config.GetProviderConf()
 
- 			if dataProviderConf.Driver == dataprovider.SQLiteDataProviderName || dataProviderConf.Driver == dataprovider.BoltDataProviderName {
 
- 				logger.Debug(logSender, connectionID, "data provider %#v not supported in subsystem mode, using %#v provider",
 
- 					dataProviderConf.Driver, dataprovider.MemoryDataProviderName)
 
- 				dataProviderConf.Driver = dataprovider.MemoryDataProviderName
 
- 				dataProviderConf.Name = ""
 
- 				dataProviderConf.PreferDatabaseCredentials = true
 
- 			}
 
- 			config.SetProviderConf(dataProviderConf)
 
- 			err = dataprovider.Initialize(dataProviderConf, configDir, false)
 
- 			if err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to initialize the data provider: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			httpConfig := config.GetHTTPConfig()
 
- 			if err := httpConfig.Initialize(configDir); err != nil {
 
- 				logger.Error(logSender, connectionID, "unable to initialize http client: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			user, err := dataprovider.UserExists(username)
 
- 			if err == nil {
 
- 				if user.HomeDir != filepath.Clean(homedir) && !preserveHomeDir {
 
- 					// update the user
 
- 					user.HomeDir = filepath.Clean(homedir)
 
- 					err = dataprovider.UpdateUser(&user)
 
- 					if err != nil {
 
- 						logger.Error(logSender, connectionID, "unable to update user %#v: %v", username, err)
 
- 						os.Exit(1)
 
- 					}
 
- 				}
 
- 			} else {
 
- 				user.Username = username
 
- 				if baseHomeDir != "" && filepath.IsAbs(baseHomeDir) {
 
- 					user.HomeDir = filepath.Join(baseHomeDir, username)
 
- 				} else {
 
- 					user.HomeDir = filepath.Clean(homedir)
 
- 				}
 
- 				logger.Debug(logSender, connectionID, "home dir for new user %#v", user.HomeDir)
 
- 				user.Password = connectionID
 
- 				user.Permissions = make(map[string][]string)
 
- 				user.Permissions["/"] = []string{dataprovider.PermAny}
 
- 				err = dataprovider.AddUser(&user)
 
- 				if err != nil {
 
- 					logger.Error(logSender, connectionID, "unable to add user %#v: %v", username, err)
 
- 					os.Exit(1)
 
- 				}
 
- 			}
 
- 			err = sftpd.ServeSubSystemConnection(&user, connectionID, os.Stdin, os.Stdout)
 
- 			if err != nil && err != io.EOF {
 
- 				logger.Warn(logSender, connectionID, "serving subsystem finished with error: %v", err)
 
- 				os.Exit(1)
 
- 			}
 
- 			logger.Info(logSender, connectionID, "serving subsystem finished")
 
- 			plugin.Handler.Cleanup()
 
- 			os.Exit(0)
 
- 		},
 
- 	}
 
- )
 
- func init() {
 
- 	subsystemCmd.Flags().BoolVarP(&preserveHomeDir, "preserve-home", "p", false, `If the user already exists, the existing home
 
- directory will not be changed`)
 
- 	subsystemCmd.Flags().StringVarP(&baseHomeDir, "base-home-dir", "d", "", `If the user does not exist specify an alternate
 
- starting directory. The home directory for a new
 
- user will be:
 
- [base-home-dir]/[username]
 
- base-home-dir must be an absolute path.`)
 
- 	subsystemCmd.Flags().BoolVarP(&logJournalD, "log-to-journald", "j", false, `Send logs to journald. Only available on Linux.
 
- Use:
 
- $ journalctl -o verbose -f
 
- To see full logs.
 
- If not set, the logs will be sent to the standard
 
- error`)
 
- 	addConfigFlags(subsystemCmd)
 
- 	viper.SetDefault(logVerboseKey, defaultLogVerbose)
 
- 	viper.BindEnv(logVerboseKey, "SFTPGO_LOG_VERBOSE") //nolint:errcheck
 
- 	subsystemCmd.Flags().BoolVarP(&logVerbose, logVerboseFlag, "v", viper.GetBool(logVerboseKey),
 
- 		`Enable verbose logs. This flag can be set
 
- using SFTPGO_LOG_VERBOSE env var too.
 
- `)
 
- 	viper.BindPFlag(logVerboseKey, subsystemCmd.Flags().Lookup(logVerboseFlag)) //nolint:errcheck
 
- 	rootCmd.AddCommand(subsystemCmd)
 
- }
 
 
  |