Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework basic auth mechanism #8

Merged
merged 3 commits into from
Nov 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ var (
datadir string
debug bool
httpAddr string
httpAdmins arrayFlags
httpHost string
httpPrefix string
httpUsername string
letsencrypt bool
reverseProxyAuthHeader string
reverseProxyAuthIP string
Expand All @@ -49,9 +49,6 @@ var (
// archiver
archive *archiver.Archiver

// secrets
authsecret *Secret

// config
config *Config

Expand All @@ -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.Var(&httpAdmins, "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")
Expand Down Expand Up @@ -131,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")
Expand All @@ -148,11 +150,6 @@ func main() {
usage("invalid --http-addr")
}

// auth secret is the password for basic auth
if reverseProxyAuthIP == "" {
authsecret = NewSecret(filepath.Join(datadir, ".authsecret"))
}

//
// Routes
//
Expand Down Expand Up @@ -238,9 +235,6 @@ func main() {
Path: httpPrefix + "/",
})

if authsecret != nil {
logger.Infof("Login credentials: %s / %s", httpUsername, authsecret.Get())
}
logger.Fatal(plain.ListenAndServe())
}

Expand Down Expand Up @@ -323,7 +317,6 @@ func main() {
Host: hostport,
Path: httpPrefix + "/",
})
logger.Infof("Login credentials: %s / %s", httpUsername, authsecret.Get())
logger.Fatal(secure.Serve(tlsListener))
}

Expand All @@ -340,3 +333,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
}
49 changes: 0 additions & 49 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"syscall"
)

Expand Down Expand Up @@ -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)
}
17 changes: 11 additions & 6 deletions web.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +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 authsecret != nil {
user, password, _ := r.BasicAuth()
if user == httpUsername && password == authsecret.Get() {
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)
Expand Down