github.com/artpar/rclone@v1.67.3/cmd/serve/sftp/sftp.go (about)

     1  //go:build !plan9
     2  
     3  // Package sftp implements an SFTP server to serve an rclone VFS
     4  package sftp
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/artpar/rclone/cmd"
    10  	"github.com/artpar/rclone/cmd/serve/proxy"
    11  	"github.com/artpar/rclone/cmd/serve/proxy/proxyflags"
    12  	"github.com/artpar/rclone/fs"
    13  	"github.com/artpar/rclone/fs/config/flags"
    14  	"github.com/artpar/rclone/fs/rc"
    15  	"github.com/artpar/rclone/lib/systemd"
    16  	"github.com/artpar/rclone/vfs"
    17  	"github.com/artpar/rclone/vfs/vfsflags"
    18  	"github.com/spf13/cobra"
    19  	"github.com/spf13/pflag"
    20  )
    21  
    22  // Options contains options for the http Server
    23  type Options struct {
    24  	ListenAddr     string   // Port to listen on
    25  	HostKeys       []string // Paths to private host keys
    26  	AuthorizedKeys string   // Path to authorized keys file
    27  	User           string   // single username
    28  	Pass           string   // password for user
    29  	NoAuth         bool     // allow no authentication on connections
    30  	Stdio          bool     // serve on stdio
    31  }
    32  
    33  // DefaultOpt is the default values used for Options
    34  var DefaultOpt = Options{
    35  	ListenAddr:     "localhost:2022",
    36  	AuthorizedKeys: "~/.ssh/authorized_keys",
    37  }
    38  
    39  // Opt is options set by command line flags
    40  var Opt = DefaultOpt
    41  
    42  // AddFlags adds flags for the sftp
    43  func AddFlags(flagSet *pflag.FlagSet, Opt *Options) {
    44  	rc.AddOption("sftp", &Opt)
    45  	flags.StringVarP(flagSet, &Opt.ListenAddr, "addr", "", Opt.ListenAddr, "IPaddress:Port or :Port to bind server to", "")
    46  	flags.StringArrayVarP(flagSet, &Opt.HostKeys, "key", "", Opt.HostKeys, "SSH private host key file (Can be multi-valued, leave blank to auto generate)", "")
    47  	flags.StringVarP(flagSet, &Opt.AuthorizedKeys, "authorized-keys", "", Opt.AuthorizedKeys, "Authorized keys file", "")
    48  	flags.StringVarP(flagSet, &Opt.User, "user", "", Opt.User, "User name for authentication", "")
    49  	flags.StringVarP(flagSet, &Opt.Pass, "pass", "", Opt.Pass, "Password for authentication", "")
    50  	flags.BoolVarP(flagSet, &Opt.NoAuth, "no-auth", "", Opt.NoAuth, "Allow connections with no authentication if set", "")
    51  	flags.BoolVarP(flagSet, &Opt.Stdio, "stdio", "", Opt.Stdio, "Run an sftp server on stdin/stdout", "")
    52  }
    53  
    54  func init() {
    55  	vfsflags.AddFlags(Command.Flags())
    56  	proxyflags.AddFlags(Command.Flags())
    57  	AddFlags(Command.Flags(), &Opt)
    58  }
    59  
    60  // Command definition for cobra
    61  var Command = &cobra.Command{
    62  	Use:   "sftp remote:path",
    63  	Short: `Serve the remote over SFTP.`,
    64  	Long: `Run an SFTP server to serve a remote over SFTP. This can be used
    65  with an SFTP client or you can make a remote of type [sftp](/sftp) to use with it.
    66  
    67  You can use the [filter](/filtering) flags (e.g. ` + "`--include`, `--exclude`" + `)
    68  to control what is served.
    69  
    70  The server will respond to a small number of shell commands, mainly
    71  md5sum, sha1sum and df, which enable it to provide support for checksums
    72  and the about feature when accessed from an sftp remote.
    73  
    74  Note that this server uses standard 32 KiB packet payload size, which
    75  means you must not configure the client to expect anything else, e.g.
    76  with the [chunk_size](/sftp/#sftp-chunk-size) option on an sftp remote.
    77  
    78  The server will log errors.  Use ` + "`-v`" + ` to see access logs.
    79  
    80  ` + "`--bwlimit`" + ` will be respected for file transfers.
    81  Use ` + "`--stats`" + ` to control the stats printing.
    82  
    83  You must provide some means of authentication, either with
    84  ` + "`--user`/`--pass`" + `, an authorized keys file (specify location with
    85  ` + "`--authorized-keys`" + ` - the default is the same as ssh), an
    86  ` + "`--auth-proxy`" + `, or set the ` + "`--no-auth`" + ` flag for no
    87  authentication when logging in.
    88  
    89  If you don't supply a host ` + "`--key`" + ` then rclone will generate rsa, ecdsa
    90  and ed25519 variants, and cache them for later use in rclone's cache
    91  directory (see ` + "`rclone help flags cache-dir`" + `) in the "serve-sftp"
    92  directory.
    93  
    94  By default the server binds to localhost:2022 - if you want it to be
    95  reachable externally then supply ` + "`--addr :2022`" + ` for example.
    96  
    97  Note that the default of ` + "`--vfs-cache-mode off`" + ` is fine for the rclone
    98  sftp backend, but it may not be with other SFTP clients.
    99  
   100  If ` + "`--stdio`" + ` is specified, rclone will serve SFTP over stdio, which can
   101  be used with sshd via ~/.ssh/authorized_keys, for example:
   102  
   103      restrict,command="rclone serve sftp --stdio ./photos" ssh-rsa ...
   104  
   105  On the client you need to set ` + "`--transfers 1`" + ` when using ` + "`--stdio`" + `.
   106  Otherwise multiple instances of the rclone server are started by OpenSSH
   107  which can lead to "corrupted on transfer" errors. This is the case because
   108  the client chooses indiscriminately which server to send commands to while
   109  the servers all have different views of the state of the filing system.
   110  
   111  The "restrict" in authorized_keys prevents SHA1SUMs and MD5SUMs from being
   112  used. Omitting "restrict" and using  ` + "`--sftp-path-override`" + ` to enable
   113  checksumming is possible but less secure and you could use the SFTP server
   114  provided by OpenSSH in this case.
   115  
   116  ` + vfs.Help() + proxy.Help,
   117  	Annotations: map[string]string{
   118  		"versionIntroduced": "v1.48",
   119  		"groups":            "Filter",
   120  	},
   121  	Run: func(command *cobra.Command, args []string) {
   122  		var f fs.Fs
   123  		if proxyflags.Opt.AuthProxy == "" {
   124  			cmd.CheckArgs(1, 1, command, args)
   125  			f = cmd.NewFsSrc(args)
   126  		} else {
   127  			cmd.CheckArgs(0, 0, command, args)
   128  		}
   129  		cmd.Run(false, true, command, func() error {
   130  			if Opt.Stdio {
   131  				return serveStdio(f)
   132  			}
   133  			s := newServer(context.Background(), f, &Opt)
   134  			err := s.Serve()
   135  			if err != nil {
   136  				return err
   137  			}
   138  			defer systemd.Notify()()
   139  			s.Wait()
   140  			return nil
   141  		})
   142  	},
   143  }