From a101a669ea32874b490db67c7195cfc8538c24de Mon Sep 17 00:00:00 2001 From: Thomas Krzero Date: Mon, 6 Nov 2017 22:23:11 +0100 Subject: [PATCH 1/3] Rework basic auth mechanism --- main.go | 16 ++-------------- utils.go | 49 ------------------------------------------------- web.go | 8 ++++++-- 3 files changed, 8 insertions(+), 65 deletions(-) diff --git a/main.go b/main.go index 4eea8da..94201ff 100644 --- a/main.go +++ b/main.go @@ -31,9 +31,9 @@ var ( datadir string debug bool httpAddr string + httpAdmin string httpHost string httpPrefix string - httpUsername string letsencrypt bool reverseProxyAuthHeader string reverseProxyAuthIP string @@ -49,9 +49,6 @@ var ( // archiver archive *archiver.Archiver - // secrets - authsecret *Secret - // config config *Config @@ -64,8 +61,8 @@ func init() { cli.StringVar(&datadir, "data-dir", "/data", "data directory") cli.BoolVar(&debug, "debug", false, "debug mode") cli.StringVar(&httpAddr, "http-addr", ":80", "listen address") + cli.StringVar(&httpAdmin, "http-admin", "", "HTTP basic auth user/password for admin") cli.StringVar(&httpHost, "http-host", "", "HTTP host") - cli.StringVar(&httpUsername, "http-username", "streamlist", "HTTP basic auth username") cli.StringVar(&httpPrefix, "http-prefix", "/streamlist", "HTTP URL prefix (not actually supported yet!)") cli.BoolVar(&letsencrypt, "letsencrypt", false, "enable TLS using Let's Encrypt") cli.StringVar(&reverseProxyAuthHeader, "reverse-proxy-header", "X-Authenticated-User", "reverse proxy auth header") @@ -148,11 +145,6 @@ func main() { usage("invalid --http-addr") } - // auth secret is the password for basic auth - if reverseProxyAuthIP == "" { - authsecret = NewSecret(filepath.Join(datadir, ".authsecret")) - } - // // Routes // @@ -238,9 +230,6 @@ func main() { Path: httpPrefix + "/", }) - if authsecret != nil { - logger.Infof("Login credentials: %s / %s", httpUsername, authsecret.Get()) - } logger.Fatal(plain.ListenAndServe()) } @@ -323,7 +312,6 @@ func main() { Host: hostport, Path: httpPrefix + "/", }) - logger.Infof("Login credentials: %s / %s", httpUsername, authsecret.Get()) logger.Fatal(secure.Serve(tlsListener)) } diff --git a/utils.go b/utils.go index 40b7227..6a2bd9a 100644 --- a/utils.go +++ b/utils.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "syscall" ) @@ -69,51 +68,3 @@ func Overwrite(filename string, data []byte, perm os.FileMode) error { } return os.Rename(f.Name(), filename) } - -// Secret generates a random value and stores it in a file for persistent access. -type Secret struct { - filename string -} - -// NewSecret tries to create the secret file and return the Secret. -func NewSecret(filename string) *Secret { - s := &Secret{filename: filename} - s.Get() - return s -} - -// Get returns the secret, creating it if necessary. -func (s Secret) Get() string { - // Write the value if it doesn't exist already. - if _, err := os.Stat(s.filename); os.IsNotExist(err) { - if err := s.Reset(); err != nil { - panic(err) - } - } - // Read the value that must exist now. - value, err := ioutil.ReadFile(s.filename) - if err != nil { - panic(err) - } - return strings.TrimSpace(string(value)) -} - -// Reset generates and writes a new secret to the file. -func (s Secret) Reset() error { - n, err := RandomNumber() - if err != nil { - return err - } - content := []byte(fmt.Sprintf("%d\n", n)) - - tmpfile, err := ioutil.TempFile(filepath.Dir(s.filename), ".tmpsecret") - if err != nil { - return err - } - defer os.Remove(tmpfile.Name()) - - if _, err := tmpfile.Write(content); err != nil { - return err - } - return os.Rename(tmpfile.Name(), s.filename) -} diff --git a/web.go b/web.go index 8eb08f1..cd123b9 100644 --- a/web.go +++ b/web.go @@ -122,9 +122,13 @@ func Auth(h httprouter.Handle, optional bool) httprouter.Handle { user := "" // Method: Basic Auth (if we're not behind a reverse proxy, use basic auth) - if authsecret != nil { + if httpAdmin != "" { + split := strings.Split(httpAdmin, ":") + httpUsername := split[0] + httpPassword := split[1] user, password, _ := r.BasicAuth() - if user == httpUsername && password == authsecret.Get() { + //if user == httpUsername && password == authsecret.Get() { + if user == httpUsername && password == httpPassword { ps = append(ps, httprouter.Param{Key: "user", Value: user}) h(w, r, ps) return From 113bb003350e0248daaca5ab58558c9e9c96d729 Mon Sep 17 00:00:00 2001 From: Thomas Krzero Date: Mon, 6 Nov 2017 22:45:23 +0100 Subject: [PATCH 2/3] Allow multiple admin user/password --- main.go | 15 +++++++++++++-- web.go | 21 +++++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index 94201ff..10730aa 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ var ( datadir string debug bool httpAddr string - httpAdmin string + httpAdmins arrayFlags httpHost string httpPrefix string letsencrypt bool @@ -61,7 +61,7 @@ func init() { cli.StringVar(&datadir, "data-dir", "/data", "data directory") cli.BoolVar(&debug, "debug", false, "debug mode") cli.StringVar(&httpAddr, "http-addr", ":80", "listen address") - cli.StringVar(&httpAdmin, "http-admin", "", "HTTP basic auth user/password for admin") + cli.Var(&httpAdmins, "http-admin", "HTTP basic auth user/password for admin.") cli.StringVar(&httpHost, "http-host", "", "HTTP host") cli.StringVar(&httpPrefix, "http-prefix", "/streamlist", "HTTP URL prefix (not actually supported yet!)") cli.BoolVar(&letsencrypt, "letsencrypt", false, "enable TLS using Let's Encrypt") @@ -328,3 +328,14 @@ func (l tcpKeepAliveListener) Accept() (c net.Conn, err error) { tc.SetKeepAlivePeriod(10 * time.Minute) return tc, nil } + +type arrayFlags []string + +func (i *arrayFlags) String() string { + return "my string representation" +} + +func (i *arrayFlags) Set(value string) error { + *i = append(*i, value) + return nil +} diff --git a/web.go b/web.go index cd123b9..db81389 100644 --- a/web.go +++ b/web.go @@ -122,16 +122,17 @@ func Auth(h httprouter.Handle, optional bool) httprouter.Handle { user := "" // Method: Basic Auth (if we're not behind a reverse proxy, use basic auth) - if httpAdmin != "" { - split := strings.Split(httpAdmin, ":") - httpUsername := split[0] - httpPassword := split[1] - user, password, _ := r.BasicAuth() - //if user == httpUsername && password == authsecret.Get() { - if user == httpUsername && password == httpPassword { - ps = append(ps, httprouter.Param{Key: "user", Value: user}) - h(w, r, ps) - return + if httpAdmins != nil { + for _, httpAdmin := range httpAdmins { + split := strings.Split(httpAdmin, ":") + httpUsername := split[0] + httpPassword := split[1] + user, password, _ := r.BasicAuth() + if user == httpUsername && password == httpPassword { + ps = append(ps, httprouter.Param{Key: "user", Value: user}) + h(w, r, ps) + return + } } if optional { h(w, r, ps) From 123a93dd449d97b46703d30b7e940be68e55b24b Mon Sep 17 00:00:00 2001 From: Thomas Krzero Date: Mon, 6 Nov 2017 23:00:36 +0100 Subject: [PATCH 3/3] http-admin flag required --- main.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 10730aa..ce0ed27 100644 --- a/main.go +++ b/main.go @@ -128,11 +128,16 @@ func main() { // usage usage := func(msg string) { fmt.Fprintf(os.Stderr, "ERROR: "+msg+"\n\n") - fmt.Fprintf(os.Stderr, "Usage: %s --http-host music.example.com\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: %s --http-host music.example.com --http-admin 'admin:$ecUrePas$0rd'\n\n", os.Args[0]) cli.PrintDefaults() os.Exit(1) } + // http admin + if httpAdmins == nil && reverseProxyAuthIP == "" { + usage("the --http-admin or the --reverseProxyAuthIP flag is required") + } + // http host if httpHost == "" { usage("the --http-host flag is required")