//go:build unix package cmd import ( "errors" "io" "os" "github.com/pkg/sftp" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" sshserver "github.com/netbirdio/netbird/client/ssh/server" ) var ( sftpUID uint32 sftpGID uint32 sftpGroupsInt []uint sftpWorkingDir string ) var sshSftpCmd = &cobra.Command{ Use: "sftp", Short: "SFTP server with privilege dropping (internal use)", Hidden: true, RunE: sftpMain, } func init() { sshSftpCmd.Flags().Uint32Var(&sftpUID, "uid", 0, "Target user ID") sshSftpCmd.Flags().Uint32Var(&sftpGID, "gid", 0, "Target group ID") sshSftpCmd.Flags().UintSliceVar(&sftpGroupsInt, "groups", nil, "Supplementary group IDs (can be repeated)") sshSftpCmd.Flags().StringVar(&sftpWorkingDir, "working-dir", "", "Working directory") } func sftpMain(cmd *cobra.Command, _ []string) error { privilegeDropper := sshserver.NewPrivilegeDropper() var groups []uint32 for _, groupInt := range sftpGroupsInt { groups = append(groups, uint32(groupInt)) } config := sshserver.ExecutorConfig{ UID: sftpUID, GID: sftpGID, Groups: groups, WorkingDir: sftpWorkingDir, Shell: "", Command: "", } log.Tracef("dropping privileges for SFTP to UID=%d, GID=%d, groups=%v", config.UID, config.GID, config.Groups) if err := privilegeDropper.DropPrivileges(config.UID, config.GID, config.Groups); err != nil { cmd.PrintErrf("privilege drop failed: %v\n", err) os.Exit(sshserver.ExitCodePrivilegeDropFail) } if config.WorkingDir != "" { if err := os.Chdir(config.WorkingDir); err != nil { cmd.PrintErrf("failed to change to working directory %s: %v\n", config.WorkingDir, err) } } sftpServer, err := sftp.NewServer(struct { io.Reader io.WriteCloser }{ Reader: os.Stdin, WriteCloser: os.Stdout, }) if err != nil { cmd.PrintErrf("SFTP server creation failed: %v\n", err) os.Exit(sshserver.ExitCodeShellExecFail) } log.Tracef("starting SFTP server with dropped privileges") if err := sftpServer.Serve(); err != nil && !errors.Is(err, io.EOF) { cmd.PrintErrf("SFTP server error: %v\n", err) if closeErr := sftpServer.Close(); closeErr != nil { cmd.PrintErrf("SFTP server close error: %v\n", closeErr) } os.Exit(sshserver.ExitCodeShellExecFail) } if closeErr := sftpServer.Close(); closeErr != nil { cmd.PrintErrf("SFTP server close error: %v\n", closeErr) } os.Exit(sshserver.ExitCodeSuccess) return nil }