The auth proxy was only given the user and their password or public key, so a proxy program had no way to restrict logins to particular networks, or to record where an authentication attempt came from. The JSON sent to the program now has a client_ip key holding the bare IP the client connected from, with the port stripped so IPv6 arrives as 2001:db8::1 rather than [2001:db8::1]:52344. An IPv4-mapped IPv6 address is reported as plain IPv4 so that a client arriving over a dual-stack listener still matches IPv4 networks. The key is omitted when the client has no IP address. The IP is also mixed into the backend cache key. That is needed as the program is only run on a cache miss, so a client from a non-allowlisted address presenting valid credentials within the 5 minute cache lifetime would get a cache hit and be let in without the program being consulted at all.
140 lines
5.2 KiB
Go
140 lines
5.2 KiB
Go
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/config/flags"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
// AuthHelp returns text describing the http authentication to add to the command help.
|
|
func AuthHelp(prefix string) string {
|
|
help := `#### 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 ` + "`--{{ .Prefix }}user` and `--{{ .Prefix }}pass`" + ` flags.
|
|
|
|
Alternatively, you can have the reverse proxy manage authentication and use the
|
|
username provided in the configured header with ` + "`--user-from-header`" + ` (e.g., ` + "`--{{ .Prefix }}user-from-header=x-remote-user`" + `).
|
|
Ensure the proxy is trusted and headers cannot be spoofed, as misconfiguration
|
|
may lead to unauthorized access.
|
|
|
|
If either of the above authentication methods is not configured and client
|
|
certificates are required by the ` + "`--client-ca`" + ` flag passed to the server, the
|
|
client certificate common name will be considered as the username.
|
|
|
|
Use ` + "`--{{ .Prefix }}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.
|
|
|
|
To create an htpasswd file:
|
|
|
|
` + "```console" + `
|
|
touch htpasswd
|
|
htpasswd -B htpasswd user
|
|
htpasswd -B htpasswd anotherUser
|
|
` + "```" + `
|
|
|
|
The password file can be updated while rclone is running.
|
|
|
|
Use ` + "`--{{ .Prefix }}realm`" + ` to set the authentication realm.
|
|
|
|
Use ` + "`--{{ .Prefix }}salt`" + ` to change the password hashing salt from the default.
|
|
|
|
`
|
|
tmpl, err := template.New("auth help").Parse(help)
|
|
if err != nil {
|
|
fs.Fatal(nil, fmt.Sprint("Fatal error parsing template", err))
|
|
}
|
|
|
|
data := struct {
|
|
Prefix string
|
|
}{
|
|
Prefix: prefix,
|
|
}
|
|
buf := &bytes.Buffer{}
|
|
err = tmpl.Execute(buf, data)
|
|
if err != nil {
|
|
fs.Fatal(nil, fmt.Sprint("Fatal error executing template", err))
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// CustomAuthFn if used will be used to authenticate user, pass. If an error
|
|
// is returned then the user is not authenticated.
|
|
//
|
|
// r is the request being authenticated, for example to find the address
|
|
// the client connected from in r.RemoteAddr.
|
|
//
|
|
// If a non nil value is returned then it is added to the context under the key
|
|
type CustomAuthFn func(r *http.Request, user, pass string) (value any, err error)
|
|
|
|
// AuthConfigInfo descripts the Options in use
|
|
var AuthConfigInfo = fs.Options{{
|
|
Name: "htpasswd",
|
|
Default: "",
|
|
Help: "A htpasswd file - if not provided no authentication is done",
|
|
}, {
|
|
Name: "realm",
|
|
Default: "",
|
|
Help: "Realm for authentication",
|
|
}, {
|
|
Name: "user",
|
|
Default: "",
|
|
Help: "User name for authentication",
|
|
}, {
|
|
Name: "pass",
|
|
Default: "",
|
|
Help: "Password for authentication",
|
|
}, {
|
|
Name: "salt",
|
|
Default: "dlPL2MqE",
|
|
Help: "Password hashing salt",
|
|
}, {
|
|
Name: "user_from_header",
|
|
Default: "",
|
|
Help: "User name from a defined HTTP header",
|
|
}}
|
|
|
|
// AuthConfig contains options for the http authentication
|
|
type AuthConfig struct {
|
|
HtPasswd string `config:"htpasswd"` // htpasswd file - if not provided no authentication is done
|
|
Realm string `config:"realm"` // realm for authentication
|
|
BasicUser string `config:"user"` // single username for basic auth if not using Htpasswd
|
|
BasicPass string `config:"pass"` // password for BasicUser
|
|
Salt string `config:"salt"` // password hashing salt
|
|
UserFromHeader string `config:"user_from_header"` // retrieve user name from a defined HTTP header
|
|
CustomAuthFn CustomAuthFn `json:"-" config:"-"` // custom Auth (not set by command line flags)
|
|
}
|
|
|
|
// AddFlagsPrefix adds flags to the flag set for AuthConfig
|
|
func (cfg *AuthConfig) AddFlagsPrefix(flagSet *pflag.FlagSet, prefix string) {
|
|
flags.StringVarP(flagSet, &cfg.HtPasswd, prefix+"htpasswd", "", cfg.HtPasswd, "A htpasswd file - if not provided no authentication is done", prefix)
|
|
flags.StringVarP(flagSet, &cfg.Realm, prefix+"realm", "", cfg.Realm, "Realm for authentication", prefix)
|
|
flags.StringVarP(flagSet, &cfg.BasicUser, prefix+"user", "", cfg.BasicUser, "User name for authentication", prefix)
|
|
flags.StringVarP(flagSet, &cfg.BasicPass, prefix+"pass", "", cfg.BasicPass, "Password for authentication", prefix)
|
|
flags.StringVarP(flagSet, &cfg.Salt, prefix+"salt", "", cfg.Salt, "Password hashing salt", prefix)
|
|
flags.StringVarP(flagSet, &cfg.UserFromHeader, prefix+"user-from-header", "", cfg.UserFromHeader, "Retrieve the username from a specified HTTP header if no other authentication methods are configured (ideal for proxied setups)", prefix)
|
|
}
|
|
|
|
// AddAuthFlagsPrefix adds flags to the flag set for AuthConfig
|
|
func AddAuthFlagsPrefix(flagSet *pflag.FlagSet, prefix string, cfg *AuthConfig) {
|
|
cfg.AddFlagsPrefix(flagSet, prefix)
|
|
}
|
|
|
|
// DefaultAuthCfg returns a new config which can be customized by command line flags
|
|
//
|
|
// Note that this needs to be kept in sync with AuthConfigInfo above and
|
|
// can be removed when all callers have been converted.
|
|
func DefaultAuthCfg() AuthConfig {
|
|
return AuthConfig{
|
|
Salt: "dlPL2MqE",
|
|
}
|
|
}
|