Fix concurrent map writes on digest auth

This commit is contained in:
Michael 2018-01-12 20:00:06 +01:00 committed by Traefiker
parent b5ee5c34f2
commit 5316b412d2
5 changed files with 18 additions and 9 deletions

7
Gopkg.lock generated
View file

@ -120,10 +120,11 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
branch = "containous-fork"
name = "github.com/abbot/go-http-auth" name = "github.com/abbot/go-http-auth"
packages = ["."] packages = ["."]
revision = "0ddd408d5d60ea76e320503cc7dd091992dee608" revision = "65b0cdae8d7fe5c05c7430e055938ef6d24a66c9"
version = "v0.4.0" source = "github.com/containous/go-http-auth"
[[projects]] [[projects]]
name = "github.com/aokoli/goutils" name = "github.com/aokoli/goutils"
@ -1385,6 +1386,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "8cd40e70454298aa3ef967edf2c501aef87f1964b9e5cef3318f2c99fc5e620e" inputs-digest = "bd1e7a1b07d95ff85c675468bbfc4bc7a91c39cf1feceeb58dfcdba9592180a5"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -38,8 +38,9 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/NYTimes/gziphandler" name = "github.com/NYTimes/gziphandler"
[[constraint]] [[constraint]]
branch = "containous-fork"
name = "github.com/abbot/go-http-auth" name = "github.com/abbot/go-http-auth"
version = "0.4.0" source = "github.com/containous/go-http-auth"
[[constraint]] [[constraint]]
branch = "master" branch = "master"

View file

@ -89,7 +89,7 @@ Users can be specified directly in the toml file, or indirectly by referencing a
# To enable digest auth on the webui with 2 user/realm/pass: test:traefik:test and test2:traefik:test2 # To enable digest auth on the webui with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
[web.auth.digest] [web.auth.digest]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
usersFile = "/path/to/.htdigest" usersFile = "/path/to/.htdigest"
# ... # ...

View file

@ -136,7 +136,7 @@ Users can be specified directly in the toml file, or indirectly by referencing a
[entryPoints.http] [entryPoints.http]
address = ":80" address = ":80"
[entryPoints.http.auth.digest] [entryPoints.http.auth.digest]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
usersFile = "/path/to/.htdigest" usersFile = "/path/to/.htdigest"
``` ```

View file

@ -39,7 +39,7 @@ type DigestAuth struct {
ClientCacheTolerance int ClientCacheTolerance int
clients map[string]*digest_client clients map[string]*digest_client
mutex sync.Mutex mutex sync.RWMutex
} }
// check that DigestAuth implements AuthenticatorInterface // check that DigestAuth implements AuthenticatorInterface
@ -84,11 +84,16 @@ func (a *DigestAuth) Purge(count int) {
(or requires reauthentication). (or requires reauthentication).
*/ */
func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) { func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
a.mutex.Lock()
defer a.mutex.Unlock()
if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance { if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
a.Purge(a.ClientCacheTolerance * 2) a.Purge(a.ClientCacheTolerance * 2)
} }
nonce := RandomKey() nonce := RandomKey()
a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
w.Header().Set(contentType, a.Headers.V().UnauthContentType) w.Header().Set(contentType, a.Headers.V().UnauthContentType)
w.Header().Set(a.Headers.V().Authenticate, w.Header().Set(a.Headers.V().Authenticate,
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`, fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
@ -118,8 +123,8 @@ func DigestAuthParams(authorization string) map[string]string {
Authentication-Info response header. Authentication-Info response header.
*/ */
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) { func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
da.mutex.Lock() da.mutex.RLock()
defer da.mutex.Unlock() defer da.mutex.RUnlock()
username = "" username = ""
authinfo = nil authinfo = nil
auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization)) auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
@ -242,6 +247,8 @@ func (a *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
// NewContext returns a context carrying authentication information for the request. // NewContext returns a context carrying authentication information for the request.
func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context { func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
a.mutex.Lock()
defer a.mutex.Unlock()
username, authinfo := a.CheckAuth(r) username, authinfo := a.CheckAuth(r)
info := &Info{Username: username, ResponseHeaders: make(http.Header)} info := &Info{Username: username, ResponseHeaders: make(http.Header)}
if username != "" { if username != "" {