From b5190d45cd9096e738c3b45d669572a580b9db9a Mon Sep 17 00:00:00 2001 From: Yu Cao Date: Thu, 31 May 2018 14:30:45 -0400 Subject: [PATCH] basic auth --- probe/appclient/probe_config.go | 7 +++- prog/app.go | 9 +++++ prog/app_auth.go | 66 +++++++++++++++++++++++++++++++++ prog/main.go | 14 +++++++ prog/probe.go | 9 +++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 prog/app_auth.go diff --git a/probe/appclient/probe_config.go b/probe/appclient/probe_config.go index be491b463..8e2cecd58 100644 --- a/probe/appclient/probe_config.go +++ b/probe/appclient/probe_config.go @@ -31,6 +31,7 @@ func init() { // ProbeConfig contains all the info needed for a probe to do HTTP requests type ProbeConfig struct { + BasicAuth bool Token string ProbeVersion string ProbeID string @@ -38,7 +39,11 @@ type ProbeConfig struct { } func (pc ProbeConfig) authorizeHeaders(headers http.Header) { - headers.Set("Authorization", fmt.Sprintf("Scope-Probe token=%s", pc.Token)) + if pc.BasicAuth { + headers.Set("Authorization", fmt.Sprintf("Basic %s", pc.Token)) + } else { + headers.Set("Authorization", fmt.Sprintf("Scope-Probe token=%s", pc.Token)) + } headers.Set(xfer.ScopeProbeIDHeader, pc.ProbeID) headers.Set(xfer.ScopeProbeVersionHeader, pc.ProbeVersion) } diff --git a/prog/app.go b/prog/app.go index 234fe51e1..564e04ef3 100644 --- a/prog/app.go +++ b/prog/app.go @@ -299,6 +299,15 @@ func appMain(flags appFlags) { }.Wrap(handler) } + if flags.basicAuth { + log.Infof("Basic authentication enabled") + handler = BasicAuthentication{ + Realm: "Restricted", + User: flags.username, + Password: flags.password, + }.Wrap(handler) + } + server := &graceful.Server{ // we want to manage the stop condition ourselves below NoSignalHandling: true, diff --git a/prog/app_auth.go b/prog/app_auth.go new file mode 100644 index 000000000..75562c5a4 --- /dev/null +++ b/prog/app_auth.go @@ -0,0 +1,66 @@ +package main + +import ( + "bytes" + "crypto/sha256" + "crypto/subtle" + "encoding/base64" + "fmt" + "net/http" + "strings" +) + +// BasicAuthentication middleware authenticate http request +type BasicAuthentication struct { + Realm string + User string + Password string +} + +// Wrap implements Middleware +func (b BasicAuthentication) Wrap(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authenticated := b.authenticate(r) + if !authenticated { + b.requestAuth(w, r) + } else { + next.ServeHTTP(w, r) + } + }) +} +func (b *BasicAuthentication) authenticate(r *http.Request) bool { + const basicScheme string = "Basic " + // Confirm the request is sending Basic Authentication credentials. + auth := r.Header.Get("Authorization") + if !strings.HasPrefix(auth, basicScheme) { + return false + } + str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):]) + if err != nil { + return false + } + + creds := bytes.SplitN(str, []byte(":"), 2) + + if len(creds) != 2 { + return false + } + + givenUser := sha256.Sum256([]byte(string(creds[0]))) + givenPass := sha256.Sum256([]byte(string(creds[1]))) + requiredUser := sha256.Sum256([]byte(b.User)) + requiredPass := sha256.Sum256([]byte(b.Password)) + // Compare the supplied credentials to those set in our options + if subtle.ConstantTimeCompare(givenUser[:], requiredUser[:]) == 1 && + subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1 { + return true + } + + return false +} + +func (b *BasicAuthentication) requestAuth(w http.ResponseWriter, r *http.Request) { + w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.Realm)) + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return +} diff --git a/prog/main.go b/prog/main.go index 9630b816b..ca68f3bb7 100644 --- a/prog/main.go +++ b/prog/main.go @@ -94,6 +94,9 @@ type flags struct { type probeFlags struct { printOnStdout bool + basicAuth bool + username string + password string token string httpListen string publishInterval time.Duration @@ -147,6 +150,10 @@ type appFlags struct { logHTTP bool logHTTPHeaders bool + basicAuth bool + username string + password string + weaveEnabled bool weaveAddr string weaveHostname string @@ -280,6 +287,9 @@ func setupFlags(flags *flags) { // Probe flags flag.BoolVar(&flags.probe.printOnStdout, "probe.publish.stdout", false, "Print reports on stdout instead of sending to app, for debugging") + flag.BoolVar(&flags.probe.basicAuth, "probe.basicAuth", true, "Use basic authentication to authenticate with app") + flag.StringVar(&flags.probe.username, "probe.basicAuth.username", "admin", "Username for basic authentication") + flag.StringVar(&flags.probe.password, "probe.basicAuth.password", "admin", "Password for basic authentication") flag.StringVar(&flags.probe.token, serviceTokenFlag, "", "Token to authenticate with cloud.weave.works") flag.StringVar(&flags.probe.token, probeTokenFlag, "", "Token to authenticate with cloud.weave.works") flag.StringVar(&flags.probe.httpListen, "probe.http.listen", "", "listen address for HTTP profiling and instrumentation server") @@ -349,6 +359,10 @@ func setupFlags(flags *flags) { flag.BoolVar(&flags.app.logHTTP, "app.log.http", false, "Log individual HTTP requests") flag.BoolVar(&flags.app.logHTTPHeaders, "app.log.httpHeaders", false, "Log HTTP headers. Needs app.log.http to be enabled.") + flag.BoolVar(&flags.app.basicAuth, "app.basicAuth", true, "Enable basic authentication for app") + flag.StringVar(&flags.app.username, "app.basicAuth.username", "admin", "Usrname for basic authentication") + flag.StringVar(&flags.app.password, "app.basicAuth.password", "admin", "Password for basic authentication") + flag.StringVar(&flags.app.weaveAddr, "app.weave.addr", app.DefaultWeaveURL, "Address on which to contact WeaveDNS") flag.StringVar(&flags.app.weaveHostname, "app.weave.hostname", "", "Hostname to advertise in WeaveDNS") flag.StringVar(&flags.app.containerName, "app.container.name", app.DefaultContainerName, "Name of this container (to lookup container ID)") diff --git a/prog/probe.go b/prog/probe.go index 756a5d00b..2281eaba1 100644 --- a/prog/probe.go +++ b/prog/probe.go @@ -1,6 +1,8 @@ package main import ( + "encoding/base64" + "fmt" "math/rand" "net" "net/http" @@ -125,7 +127,14 @@ func probeMain(flags probeFlags, targets []appclient.Target) { token = url.User.Username() url.User = nil // erase credentials, as we use a special header } + + if flags.basicAuth { + log.Infof("Basic authentication enabled") + token = base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", flags.username, flags.password))) + } + probeConfig := appclient.ProbeConfig{ + BasicAuth: flags.basicAuth, Token: token, ProbeVersion: version, ProbeID: probeID,