diff --git a/configuration.go b/configuration.go index b1ce08464..5317e1c35 100644 --- a/configuration.go +++ b/configuration.go @@ -163,6 +163,7 @@ type EntryPoint struct { Address string TLS *TLS Redirect *Redirect + Auth *types.Auth } // Redirect configures a redirection of an entry point to another, or to an URL diff --git a/glide.lock b/glide.lock index c0e265256..ec1a8226c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 745d05424943c3345ff5fca5b121c6af3930f62fc13195d87d9fcce6686620ea -updated: 2016-07-22T15:14:53.608798979+02:00 +hash: 70ad4e576bc1fa845512cce6b4ade5c422ba4fb5bb0472b37e1d3a93f13809cd +updated: 2016-07-07T17:33:16.358775373+02:00 imports: - name: github.com/abbot/go-http-auth version: cb4372376e1e00e9f6ab9ec142e029302c9e7140 @@ -36,7 +36,7 @@ imports: subpackages: - spew - name: github.com/docker/distribution - version: 2b72dd3927b2958160a2336f16145c0c421aa6a4 + version: 4e17ab5d319ac5b70b2769442947567a83386fbc subpackages: - reference - digest @@ -53,50 +53,6 @@ imports: version: 534753663161334baba06f13b8efa4cad22b5bc5 subpackages: - namesgenerator - - pkg/namesgenerator - - pkg/random - - pkg/urlutil - - cliconfig - - cliconfig/configfile - - pkg/jsonmessage - - pkg/promise - - pkg/stdcopy - - pkg/term - - reference - - registry - - runconfig/opts - - pkg/homedir - - pkg/jsonlog - - pkg/system - - pkg/term/windows - - image - - image/v1 - - pkg/ioutils - - opts - - pkg/httputils - - pkg/mflag - - pkg/stringid - - pkg/tarsum - - pkg/mount - - pkg/signal - - builder - - builder/dockerignore - - pkg/archive - - pkg/fileutils - - pkg/progress - - pkg/streamformatter - - layer - - pkg/longpath - - api/types/backend - - pkg/chrootarchive - - pkg/gitutils - - pkg/symlink - - pkg/idtools - - pkg/pools - - daemon/graphdriver - - pkg/reexec - - pkg/plugins - - pkg/plugins/transport - name: github.com/docker/engine-api version: 62043eb79d581a32ea849645277023c550732e52 subpackages: @@ -140,6 +96,20 @@ imports: - logger - lookup - version + subpackages: + - docker + - project + - project/events + - project/options + - config + - docker/builder + - docker/client + - labels + - logger + - lookup + - utils + - yaml + - version - name: github.com/docker/libkv version: aabc039ad04deb721e234f99cd1b4aa28ac71a40 subpackages: @@ -157,7 +127,7 @@ imports: - name: github.com/go-check/check version: 4f90aeace3a26ad7021961c297b22c42160c7b25 - name: github.com/gogo/protobuf - version: 6abcf94fd4c97dcb423fdafd42fe9f96ca7e421b + version: 8b3113fff1787050d4f5fcbf1173b857eec36566 subpackages: - proto - name: github.com/golang/glog @@ -169,7 +139,7 @@ imports: - name: github.com/gorilla/context version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 - name: github.com/hashicorp/consul - version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d + version: 64e3033d3c7f80af3f925f4665a9bc1af75d6153 subpackages: - api - name: github.com/hashicorp/go-cleanhttp @@ -186,7 +156,11 @@ imports: subpackages: - check - name: github.com/libkermit/docker - version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 + version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 +- name: github.com/libkermit/docker-check + version: bb75a86b169c6c5d22c0ee98278124036f272d7b + subpackages: + - compose - name: github.com/mailgun/manners version: fada45142db3f93097ca917da107aa3fad0ffcb5 - name: github.com/mailgun/timetools @@ -226,7 +200,7 @@ imports: - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: fb221651e5120cd287a76c7c1b6c877520fbd034 + version: 9d7831e41d3ef428b67685eeb27f2b4a22a92391 subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest @@ -297,6 +271,8 @@ imports: version: 911fafb28f4ee7c7bd483539a6c96190bbbccc3f subpackages: - ocsp + - bcrypt + - blowfish - name: golang.org/x/net version: b400c2eff1badec7022a8c8f5bea058b6315eed7 subpackages: @@ -319,30 +295,4 @@ imports: subpackages: - cipher - json -testImports: -- name: github.com/Azure/go-ansiterm - version: fa152c58bc15761d0200cb75fe958b89a9d4888e - subpackages: - - winterm -- name: github.com/cloudfoundry-incubator/candiedyaml - version: 99c3df83b51532e3615f851d8c2dbb638f5313bf -- name: github.com/flynn/go-shlex - version: 3f9db97f856818214da2e1057f8ad84803971cff -- name: github.com/gorilla/mux - version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e -- name: github.com/libkermit/docker-check - version: bb75a86b169c6c5d22c0ee98278124036f272d7b -- name: github.com/spf13/pflag - version: 5644820622454e71517561946e3d94b9f9db6842 -- name: github.com/vbatts/tar-split - version: 28bc4c32f9fa9725118a685c9ddd7ffdbdbfe2c8 - subpackages: - - tar/asm - - tar/storage - - archive/tar -- name: github.com/xeipuuv/gojsonpointer - version: e0fe6f68307607d540ed8eac07a342c33fa1b54a -- name: github.com/xeipuuv/gojsonreference - version: e02fc20de94c78484cd5ffb007f8af96be030a45 -- name: github.com/xeipuuv/gojsonschema - version: 66a3de92def23708184148ae337750915875e7c1 +devImports: [] diff --git a/glide.yaml b/glide.yaml index 70314fdaf..473085722 100644 --- a/glide.yaml +++ b/glide.yaml @@ -64,8 +64,6 @@ import: version: b2fad6198110326662e9e356a97199078a4a775c subpackages: - acme -- package: github.com/miekg/dns - version: 5d001d020961ae1c184f9f8152fdc73810481677 - package: golang.org/x/net subpackages: - context diff --git a/middlewares/authenticator.go b/middlewares/authenticator.go new file mode 100644 index 000000000..b5e6b0eb1 --- /dev/null +++ b/middlewares/authenticator.go @@ -0,0 +1,99 @@ +package middlewares + +import ( + "fmt" + log "github.com/Sirupsen/logrus" + "github.com/abbot/go-http-auth" + "github.com/codegangsta/negroni" + "github.com/containous/traefik/types" + "net/http" + "strings" +) + +// Authenticator is a middleware that provides HTTP basic and digest authentication +type Authenticator struct { + handler negroni.Handler + users map[string]string +} + +// NewAuthenticator builds a new Autenticator given a config +func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) { + if authConfig == nil { + return nil, fmt.Errorf("Error creating Authenticator: auth is nil") + } + var err error + authenticator := Authenticator{} + if authConfig.Basic != nil { + authenticator.users, err = parserBasicUsers(authConfig.Basic.Users) + if err != nil { + return nil, err + } + basicAuth := auth.NewBasicAuthenticator("traefik", authenticator.secretBasic) + authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if username := basicAuth.CheckAuth(r); username == "" { + log.Debugf("Auth failed...") + basicAuth.RequireAuth(w, r) + } else { + next.ServeHTTP(w, r) + } + }) + } else if authConfig.Digest != nil { + authenticator.users, err = parserDigestUsers(authConfig.Digest.Users) + if err != nil { + return nil, err + } + digestAuth := auth.NewDigestAuthenticator("traefik", authenticator.secretDigest) + authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if username, _ := digestAuth.CheckAuth(r); username == "" { + digestAuth.RequireAuth(w, r) + } else { + next.ServeHTTP(w, r) + } + }) + } + return &authenticator, nil +} + +func parserBasicUsers(users types.Users) (map[string]string, error) { + userMap := make(map[string]string) + for _, user := range users { + split := strings.Split(user, ":") + if len(split) != 2 { + return nil, fmt.Errorf("Error parsing Authenticator user: %v", user) + } + userMap[split[0]] = split[1] + } + return userMap, nil +} + +func parserDigestUsers(users types.Users) (map[string]string, error) { + userMap := make(map[string]string) + for _, user := range users { + split := strings.Split(user, ":") + if len(split) != 3 { + return nil, fmt.Errorf("Error parsing Authenticator user: %v", user) + } + userMap[split[0]+":"+split[1]] = split[2] + } + return userMap, nil +} + +func (a *Authenticator) secretBasic(user, realm string) string { + if secret, ok := a.users[user]; ok { + return secret + } + log.Debugf("User not found: %s", user) + return "" +} + +func (a *Authenticator) secretDigest(user, realm string) string { + if secret, ok := a.users[user+":"+realm]; ok { + return secret + } + log.Debugf("User not found: %s:%s", user, realm) + return "" +} + +func (a *Authenticator) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + a.handler.ServeHTTP(rw, r, next) +} diff --git a/server.go b/server.go index 4e83c0ac8..b224648fc 100644 --- a/server.go +++ b/server.go @@ -118,7 +118,15 @@ func (server *Server) Close() { func (server *Server) startHTTPServers() { server.serverEntryPoints = server.buildEntryPoints(server.globalConfiguration) for newServerEntryPointName, newServerEntryPoint := range server.serverEntryPoints { - newsrv, err := server.prepareServer(newServerEntryPointName, newServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], nil, server.loggerMiddleware, metrics) + serverMiddlewares := []negroni.Handler{server.loggerMiddleware, metrics} + if server.globalConfiguration.EntryPoints[newServerEntryPointName].Auth != nil { + authMiddleware, err := middlewares.NewAuthenticator(server.globalConfiguration.EntryPoints[newServerEntryPointName].Auth) + if err != nil { + log.Fatal("Error starting server: ", err) + } + serverMiddlewares = append(serverMiddlewares, authMiddleware) + } + newsrv, err := server.prepareServer(newServerEntryPointName, newServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], nil, serverMiddlewares...) if err != nil { log.Fatal("Error preparing server: ", err) } diff --git a/types/types.go b/types/types.go index e9c9b1c71..cc933ac79 100644 --- a/types/types.go +++ b/types/types.go @@ -183,3 +183,22 @@ func (cs *Constraints) SetValue(val interface{}) { func (cs *Constraints) Type() string { return fmt.Sprint("constraint") } + +// Auth holds authentication configuration (BASIC, DIGEST, users) +type Auth struct { + Basic *Basic + Digest *Digest +} + +// Users authentication users +type Users []string + +// Basic HTTP basic authentication +type Basic struct { + Users +} + +// Digest HTTP authentication +type Digest struct { + Users +}