From d87ec1257a0c8609914684b23fbd6a011cd5c594 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Tue, 16 Aug 2022 13:16:44 -0400 Subject: [PATCH] fully roughed in auth models; none, basic (#12) --- cmd/zrok/http.go | 154 ++++++++++++++++++++++++++++------------------- proxy/proxy.go | 58 +++++++++++++----- 2 files changed, 135 insertions(+), 77 deletions(-) diff --git a/cmd/zrok/http.go b/cmd/zrok/http.go index c1d30b29..ad7abe95 100644 --- a/cmd/zrok/http.go +++ b/cmd/zrok/http.go @@ -1,17 +1,15 @@ package main import ( - "fmt" - ui "github.com/gizak/termui/v3" - "github.com/gizak/termui/v3/widgets" "github.com/go-openapi/runtime" httptransport "github.com/go-openapi/runtime/client" - tb "github.com/nsf/termbox-go" "github.com/openziti-test-kitchen/zrok/http" + "github.com/openziti-test-kitchen/zrok/model" "github.com/openziti-test-kitchen/zrok/rest_client_zrok" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/tunnel" "github.com/openziti-test-kitchen/zrok/rest_model_zrok" "github.com/openziti-test-kitchen/zrok/zrokdir" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "os" @@ -22,22 +20,34 @@ import ( ) func init() { - rootCmd.AddCommand(httpCmd) + rootCmd.AddCommand(newHttpCommand().cmd) } -var httpCmd = &cobra.Command{ - Use: "http ", - Short: "Start an http terminator", - Args: cobra.ExactArgs(1), - Run: handleHttp, +type httpCommand struct { + basicAuth []string + cmd *cobra.Command } -func handleHttp(_ *cobra.Command, args []string) { - if err := ui.Init(); err != nil { - panic(err) +func newHttpCommand() *httpCommand { + cmd := &cobra.Command{ + Use: "http ", + Short: "Start an HTTP terminator", + Args: cobra.ExactArgs(1), } - defer ui.Close() - tb.SetInputMode(tb.InputEsc) + command := &httpCommand{cmd: cmd} + cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (,...") + cmd.Run = command.run + return command +} + +func (self *httpCommand) run(_ *cobra.Command, args []string) { + /* + if err := ui.Init(); err != nil { + panic(err) + } + defer ui.Close() + tb.SetInputMode(tb.InputEsc) + */ idCfg, err := zrokdir.IdentityConfigFile() if err != nil { @@ -62,6 +72,19 @@ func handleHttp(_ *cobra.Command, args []string) { req.Body = &rest_model_zrok.TunnelRequest{ ZitiIdentityID: id, Endpoint: cfg.EndpointAddress, + AuthScheme: string(model.None), + } + if len(self.basicAuth) > 0 { + logrus.Infof("configuring basic auth") + req.Body.AuthScheme = string(model.Basic) + for _, pair := range self.basicAuth { + tokens := strings.Split(pair, ":") + if len(tokens) == 2 { + req.Body.AuthUsers = append(req.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])}) + } else { + panic(errors.Errorf("invalid username:password pair '%v'", pair)) + } + } } resp, err := zrok.Tunnel.Tunnel(req, auth) if err != nil { @@ -88,64 +111,69 @@ func handleHttp(_ *cobra.Command, args []string) { } }() - ui.Clear() - w, h := ui.TerminalDimensions() + /* + ui.Clear() + w, h := ui.TerminalDimensions() - p := widgets.NewParagraph() - p.Border = true - p.Title = " access your zrok service " - p.Text = fmt.Sprintf("%v%v", strings.Repeat(" ", (((w-12)-len(resp.Payload.ProxyEndpoint))/2)-1), resp.Payload.ProxyEndpoint) - p.TextStyle = ui.Style{Fg: ui.ColorWhite} - p.PaddingTop = 1 - p.SetRect(5, 5, w-10, 10) + p := widgets.NewParagraph() + p.Border = true + p.Title = " access your zrok service " + p.Text = fmt.Sprintf("%v%v", strings.Repeat(" ", (((w-12)-len(resp.Payload.ProxyEndpoint))/2)-1), resp.Payload.ProxyEndpoint) + p.TextStyle = ui.Style{Fg: ui.ColorWhite} + p.PaddingTop = 1 + p.SetRect(5, 5, w-10, 10) - lastRequests := float64(0) - var requestData []float64 - spk := widgets.NewSparkline() - spk.Title = " requests " - spk.Data = requestData - spk.LineColor = ui.ColorCyan + lastRequests := float64(0) + var requestData []float64 + spk := widgets.NewSparkline() + spk.Title = " requests " + spk.Data = requestData + spk.LineColor = ui.ColorCyan - slg := widgets.NewSparklineGroup(spk) - slg.SetRect(5, 11, w-10, h-5) + slg := widgets.NewSparklineGroup(spk) + slg.SetRect(5, 11, w-10, h-5) - ui.Render(p, slg) + ui.Render(p, slg) - ticker := time.NewTicker(time.Second).C - uiEvents := ui.PollEvents() - for { - select { - case e := <-uiEvents: - switch e.Type { - case ui.ResizeEvent: - ui.Clear() - w, h = ui.TerminalDimensions() - p.SetRect(5, 5, w-10, 10) - slg.SetRect(5, 11, w-10, h-5) - ui.Render(p, slg) + ticker := time.NewTicker(time.Second).C + uiEvents := ui.PollEvents() + for { + select { + case e := <-uiEvents: + switch e.Type { + case ui.ResizeEvent: + ui.Clear() + w, h = ui.TerminalDimensions() + p.SetRect(5, 5, w-10, 10) + slg.SetRect(5, 11, w-10, h-5) + ui.Render(p, slg) - case ui.KeyboardEvent: - switch e.ID { - case "q", "": - ui.Close() - cleanupHttp(id, cfg, zrok, auth) - os.Exit(0) + case ui.KeyboardEvent: + switch e.ID { + case "q", "": + ui.Close() + cleanupHttp(id, cfg, zrok, auth) + os.Exit(0) + } } - } - case <-ticker: - currentRequests := float64(httpProxy.Requests()) - deltaRequests := currentRequests - lastRequests - requestData = append(requestData, deltaRequests) - lastRequests = currentRequests - requestData = append(requestData, deltaRequests) - for len(requestData) > w-17 { - requestData = requestData[1:] + case <-ticker: + currentRequests := float64(httpProxy.Requests()) + deltaRequests := currentRequests - lastRequests + requestData = append(requestData, deltaRequests) + lastRequests = currentRequests + requestData = append(requestData, deltaRequests) + for len(requestData) > w-17 { + requestData = requestData[1:] + } + spk.Title = fmt.Sprintf(" requests (%d) ", int(currentRequests)) + spk.Data = requestData + ui.Render(p, slg) } - spk.Title = fmt.Sprintf(" requests (%d) ", int(currentRequests)) - spk.Data = requestData - ui.Render(p, slg) } + */ + for { + time.Sleep(30 * time.Second) } } diff --git a/proxy/proxy.go b/proxy/proxy.go index e8e772b8..dbfa590b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -2,7 +2,6 @@ package proxy import ( "context" - "crypto/subtle" "fmt" "github.com/openziti-test-kitchen/zrok/model" "github.com/openziti-test-kitchen/zrok/util" @@ -39,12 +38,7 @@ func Run(cfg *Config) error { return err } proxy.Transport = zTransport - users := &model.BasicAuth{ - Users: []*model.AuthUser{ - {Username: "hello", Password: "world"}, - }, - } - return http.ListenAndServe(cfg.Address, basicAuth(util.NewProxyHandler(proxy), users, "zrok", &resolver{}, zCtx)) + return http.ListenAndServe(cfg.Address, basicAuth(util.NewProxyHandler(proxy), "zrok", &resolver{}, zCtx)) } type resolver struct{} @@ -165,38 +159,74 @@ func getRefreshedService(name string, ctx ziti.Context) (*edge.Service, bool) { return svc, found } -func basicAuth(handler http.Handler, users *model.BasicAuth, realm string, rslv ProxyServiceResolver, ctx ziti.Context) http.HandlerFunc { +func basicAuth(handler http.Handler, realm string, rslv ProxyServiceResolver, ctx ziti.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { svcName := rslv.Service(r.Host) if svc, found := getRefreshedService(svcName, ctx); found { if cfg, found := svc.Configs[model.ZrokProxyConfig]; found { if scheme, found := cfg["auth_scheme"]; found { switch scheme { - case model.None: + case string(model.None): + logrus.Infof("auth scheme none '%v'", svcName) handler.ServeHTTP(w, r) return - case model.Basic: + case string(model.Basic): + logrus.Infof("auth scheme basic '%v", svcName) inUser, inPass, ok := r.BasicAuth() if !ok { writeUnauthorizedResponse(w, realm) return } authed := false - for _, v := range users.Users { - if subtle.ConstantTimeCompare([]byte(inUser), []byte(v.Username)) == 1 && subtle.ConstantTimeCompare([]byte(inPass), []byte(v.Password)) == 1 { - authed = true - break + if v, found := cfg["basic_auth"]; found { + if basicAuth, ok := v.(map[string]interface{}); ok { + if v, found := basicAuth["users"]; found { + if arr, ok := v.([]interface{}); ok { + for _, v := range arr { + if um, ok := v.(map[string]interface{}); ok { + username := "" + if v, found := um["username"]; found { + if un, ok := v.(string); ok { + username = un + } + } + password := "" + if v, found := um["password"]; found { + if pw, ok := v.(string); ok { + password = pw + } + } + if username == inUser && password == inPass { + authed = true + break + } + } + } + } + } } } + if !authed { writeUnauthorizedResponse(w, realm) return } handler.ServeHTTP(w, r) + + default: + logrus.Infof("invalid auth scheme '%v'", scheme) + writeUnauthorizedResponse(w, realm) + return } + } else { + logrus.Infof("no auth scheme for '%v'", svcName) } + } else { + logrus.Infof("no proxy config for '%v'", svcName) } + } else { + logrus.Infof("service '%v' not found", svcName) } } }