From cc9d7156e4b04b0579ea4e582d053969b85beb63 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 14 Feb 2018 23:14:14 +0000 Subject: [PATCH] serve http/webdav: add --user --pass authentication #1802 --- cmd/serve/httplib/httplib.go | 57 ++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/cmd/serve/httplib/httplib.go b/cmd/serve/httplib/httplib.go index 0b8ba0df8..d051ef78a 100644 --- a/cmd/serve/httplib/httplib.go +++ b/cmd/serve/httplib/httplib.go @@ -14,13 +14,19 @@ import ( // Globals var ( bindAddress = "localhost:8080" - htPasswdFile string + htPasswdFile = "" + realm = "rclone" + basicUser = "" + basicPass = "" ) // AddFlags adds the http server specific flags func AddFlags(flagSet *pflag.FlagSet) { flagSet.StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.") flagSet.StringVarP(&htPasswdFile, "htpasswd", "", htPasswdFile, "File to use for htpasswd authentication.") + flagSet.StringVarP(&realm, "realm", "", realm, "Realm name for authentication.") + flagSet.StringVarP(&basicUser, "user", "", basicUser, "User name for authentication.") + flagSet.StringVarP(&basicPass, "pass", "", basicPass, "Password for authentication.") } // Help contains text describing the http server to add to the command @@ -32,6 +38,13 @@ Use --addr to specify which IP address and port the server should listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all IPs. By default it only listens on localhost. +#### Authentication + +By default this will serve files without needing a login. + +You can either use an htpasswd file which can take lots of users, or +set a single username and password with the --user and --pass flags. + Use --htpasswd /path/to/htpasswd to provide an htpasswd file. This is in standard apache format and supports MD5, SHA1 and BCrypt for basic authentication. Bcrypt is recommended. @@ -41,27 +54,47 @@ To create an htpasswd file: touch htpasswd htpasswd -B htpasswd user htpasswd -B htpasswd anotherUser -` + +Use --realm to set the authentication realm.` // Server contains info about the running http server type Server struct { - bindAddress string - httpServer *http.Server + bindAddress string + httpServer *http.Server + basicUser string + basicPassHashed string +} + +// singleUserProvider provides the encrypted password for a single user +func (s *Server) singleUserProvider(user, realm string) string { + if user == s.basicUser { + return s.basicPassHashed + } + return "" } // NewServer creates an http server func NewServer(handler http.Handler) *Server { - // Use htpasswd if required on everything - if htPasswdFile != "" { - fs.Infof(nil, "Using %q as htpasswd storage", htPasswdFile) - secretProvider := auth.HtpasswdFileProvider(htPasswdFile) - authenticator := auth.NewBasicAuthenticator("rclone", secretProvider) - handler = auth.JustCheck(authenticator, handler.ServeHTTP) - } - s := &Server{ bindAddress: bindAddress, } + + // Use htpasswd if required on everything + if htPasswdFile != "" || basicUser != "" { + var secretProvider auth.SecretProvider + if htPasswdFile != "" { + fs.Infof(nil, "Using %q as htpasswd storage", htPasswdFile) + secretProvider = auth.HtpasswdFileProvider(htPasswdFile) + } else { + fs.Infof(nil, "Using --user %s --pass XXXX as authenticated user", basicUser) + s.basicUser = basicUser + s.basicPassHashed = string(auth.MD5Crypt([]byte(basicPass), []byte("dlPL2MqE"), []byte("$1$"))) + secretProvider = s.singleUserProvider + } + authenticator := auth.NewBasicAuthenticator(realm, secretProvider) + handler = auth.JustCheck(authenticator, handler.ServeHTTP) + } + // FIXME make a transport? s.httpServer = &http.Server{ Addr: s.bindAddress,