Skip to content

Commit

Permalink
Merge pull request #1 from maytech/basicauth-htpasswd-reread
Browse files Browse the repository at this point in the history
Implement BasicAuth htpasswd file refresh
  • Loading branch information
mfuterko committed Feb 27, 2019
2 parents 5950e3d + 05e9f8b commit 0bc395d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 10 deletions.
39 changes: 36 additions & 3 deletions auth/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package auth
import (
"log"
"net/http"
"os"
"time"

"github.com/fabiolb/fabio/config"
"github.com/tg123/go-htpasswd"
Expand All @@ -15,14 +17,45 @@ type basic struct {
}

func newBasicAuth(cfg config.BasicAuth) (AuthScheme, error) {
secrets, err := htpasswd.New(cfg.File, htpasswd.DefaultSystems, func(err error) {
log.Println("[WARN] Error reading Htpasswd file: ", err)
})
bad := func(err error) {
log.Println("[WARN] Error processing a line in an htpasswd file:", err)
}

stat, err := os.Stat(cfg.File)
if err != nil {
return nil, err
}
cfg.ModTime = stat.ModTime()

secrets, err := htpasswd.New(cfg.File, htpasswd.DefaultSystems, bad)
if err != nil {
return nil, err
}

if cfg.Refresh > 0 {
go func() {
ticker := time.NewTicker(cfg.Refresh).C
for range ticker {
stat, err := os.Stat(cfg.File)
if err != nil {
log.Println("[WARN] Error accessing htpasswd file:", err)
continue // to prevent nil pointer dereference below
}

// refresh the htpasswd file only if its modification time has chanhed
// even if the new htpasswd file is older then previously loaded
if cfg.ModTime != stat.ModTime() {
if err := secrets.Reload(bad); err == nil {
log.Println("[INFO] The htpasswd file has been successfully reloaded")
cfg.ModTime = stat.ModTime()
} else {
log.Println("[WARN] Error reloading htpasswd file:", err)
}
}
}
}()
}

return &basic{
secrets: secrets,
realm: cfg.Realm,
Expand Down
6 changes: 4 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ type AuthScheme struct {
}

type BasicAuth struct {
Realm string
File string
Realm string
File string
Refresh time.Duration
ModTime time.Time // the htpasswd file last modification time
}
17 changes: 15 additions & 2 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,9 @@ func parseAuthScheme(cfg map[string]string) (a AuthScheme, err error) {
return AuthScheme{}, fmt.Errorf("missing 'type' in auth '%s'", a.Name)
case "basic":
a.Basic = BasicAuth{
File: cfg["file"],
Realm: cfg["realm"],
File: cfg["file"],
Realm: cfg["realm"],
Refresh: 0, // the htpasswd file refresh is disabled by default
}

if a.Basic.File == "" {
Expand All @@ -623,6 +624,18 @@ func parseAuthScheme(cfg map[string]string) (a AuthScheme, err error) {
if a.Basic.Realm == "" {
a.Basic.Realm = a.Name
}

if cfg["refresh"] != "" {
d, err := time.ParseDuration(cfg["refresh"])
if err != nil {
return AuthScheme{}, err
}
if d < time.Second {
d = time.Second
}
a.Basic.Refresh = d
}

default:
return AuthScheme{}, fmt.Errorf("unknown auth type '%s'", a.Type)
}
Expand Down
14 changes: 11 additions & 3 deletions fabio.properties
Original file line number Diff line number Diff line change
Expand Up @@ -481,11 +481,15 @@
# Basic
#
# The basic auth scheme leverages http basic authentication using
# one htpasswd file which is loaded at startup and is cached until
# the service exits.
# one htpasswd file which is loaded at startup and by default is cached until
# the service exits. However, it's possible to refresh htpasswd file
# periodically by setting the refresh interval with 'refresh' option.
#
# The 'file' option contains the path to the htpasswd file. The 'realm'
# option contains realm name (optional, default is the scheme name
# option contains realm name (optional, default is the scheme name).
# The 'refresh' option can set the htpasswd file refresh interval. Minimal
# refresh interval is 1s to void busy loop.
# By default refresh is disabled i.e. set to zero.
#
# name=<name>;type=basic;file=p/creds.htpasswd;realm=foo
#
Expand All @@ -495,6 +499,10 @@
#
# name=mybasicauth;type=basic;file=p/creds.htpasswd;
#
# # single basic auth scheme with refresh interval set to 30 seconds
#
# name=mybasicauth;type=basic;file=p/creds.htpasswd;refresh=30s
#
# # basic auth with multiple schemes
#
# proxy.auth = name=mybasicauth;type=basic;file=p/creds.htpasswd
Expand Down

0 comments on commit 0bc395d

Please sign in to comment.