mirror of
https://github.com/openziti/zrok.git
synced 2025-06-26 04:31:30 +02:00
fully roughed in auth models; none, basic (#12)
This commit is contained in:
parent
c6c1a470d3
commit
d87ec1257a
154
cmd/zrok/http.go
154
cmd/zrok/http.go
@ -1,17 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
ui "github.com/gizak/termui/v3"
|
|
||||||
"github.com/gizak/termui/v3/widgets"
|
|
||||||
"github.com/go-openapi/runtime"
|
"github.com/go-openapi/runtime"
|
||||||
httptransport "github.com/go-openapi/runtime/client"
|
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/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"
|
||||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/tunnel"
|
"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/rest_model_zrok"
|
||||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"os"
|
||||||
@ -22,22 +20,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(httpCmd)
|
rootCmd.AddCommand(newHttpCommand().cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpCmd = &cobra.Command{
|
type httpCommand struct {
|
||||||
Use: "http <endpoint>",
|
basicAuth []string
|
||||||
Short: "Start an http terminator",
|
cmd *cobra.Command
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: handleHttp,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHttp(_ *cobra.Command, args []string) {
|
func newHttpCommand() *httpCommand {
|
||||||
if err := ui.Init(); err != nil {
|
cmd := &cobra.Command{
|
||||||
panic(err)
|
Use: "http <endpoint>",
|
||||||
|
Short: "Start an HTTP terminator",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
}
|
}
|
||||||
defer ui.Close()
|
command := &httpCommand{cmd: cmd}
|
||||||
tb.SetInputMode(tb.InputEsc)
|
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...")
|
||||||
|
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()
|
idCfg, err := zrokdir.IdentityConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -62,6 +72,19 @@ func handleHttp(_ *cobra.Command, args []string) {
|
|||||||
req.Body = &rest_model_zrok.TunnelRequest{
|
req.Body = &rest_model_zrok.TunnelRequest{
|
||||||
ZitiIdentityID: id,
|
ZitiIdentityID: id,
|
||||||
Endpoint: cfg.EndpointAddress,
|
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)
|
resp, err := zrok.Tunnel.Tunnel(req, auth)
|
||||||
if err != nil {
|
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 := widgets.NewParagraph()
|
||||||
p.Border = true
|
p.Border = true
|
||||||
p.Title = " access your zrok service "
|
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.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.TextStyle = ui.Style{Fg: ui.ColorWhite}
|
||||||
p.PaddingTop = 1
|
p.PaddingTop = 1
|
||||||
p.SetRect(5, 5, w-10, 10)
|
p.SetRect(5, 5, w-10, 10)
|
||||||
|
|
||||||
lastRequests := float64(0)
|
lastRequests := float64(0)
|
||||||
var requestData []float64
|
var requestData []float64
|
||||||
spk := widgets.NewSparkline()
|
spk := widgets.NewSparkline()
|
||||||
spk.Title = " requests "
|
spk.Title = " requests "
|
||||||
spk.Data = requestData
|
spk.Data = requestData
|
||||||
spk.LineColor = ui.ColorCyan
|
spk.LineColor = ui.ColorCyan
|
||||||
|
|
||||||
slg := widgets.NewSparklineGroup(spk)
|
slg := widgets.NewSparklineGroup(spk)
|
||||||
slg.SetRect(5, 11, w-10, h-5)
|
slg.SetRect(5, 11, w-10, h-5)
|
||||||
|
|
||||||
ui.Render(p, slg)
|
ui.Render(p, slg)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second).C
|
ticker := time.NewTicker(time.Second).C
|
||||||
uiEvents := ui.PollEvents()
|
uiEvents := ui.PollEvents()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case e := <-uiEvents:
|
case e := <-uiEvents:
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case ui.ResizeEvent:
|
case ui.ResizeEvent:
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
w, h = ui.TerminalDimensions()
|
w, h = ui.TerminalDimensions()
|
||||||
p.SetRect(5, 5, w-10, 10)
|
p.SetRect(5, 5, w-10, 10)
|
||||||
slg.SetRect(5, 11, w-10, h-5)
|
slg.SetRect(5, 11, w-10, h-5)
|
||||||
ui.Render(p, slg)
|
ui.Render(p, slg)
|
||||||
|
|
||||||
case ui.KeyboardEvent:
|
case ui.KeyboardEvent:
|
||||||
switch e.ID {
|
switch e.ID {
|
||||||
case "q", "<C-c>":
|
case "q", "<C-c>":
|
||||||
ui.Close()
|
ui.Close()
|
||||||
cleanupHttp(id, cfg, zrok, auth)
|
cleanupHttp(id, cfg, zrok, auth)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case <-ticker:
|
case <-ticker:
|
||||||
currentRequests := float64(httpProxy.Requests())
|
currentRequests := float64(httpProxy.Requests())
|
||||||
deltaRequests := currentRequests - lastRequests
|
deltaRequests := currentRequests - lastRequests
|
||||||
requestData = append(requestData, deltaRequests)
|
requestData = append(requestData, deltaRequests)
|
||||||
lastRequests = currentRequests
|
lastRequests = currentRequests
|
||||||
requestData = append(requestData, deltaRequests)
|
requestData = append(requestData, deltaRequests)
|
||||||
for len(requestData) > w-17 {
|
for len(requestData) > w-17 {
|
||||||
requestData = requestData[1:]
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package proxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/openziti-test-kitchen/zrok/model"
|
"github.com/openziti-test-kitchen/zrok/model"
|
||||||
"github.com/openziti-test-kitchen/zrok/util"
|
"github.com/openziti-test-kitchen/zrok/util"
|
||||||
@ -39,12 +38,7 @@ func Run(cfg *Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
proxy.Transport = zTransport
|
proxy.Transport = zTransport
|
||||||
users := &model.BasicAuth{
|
return http.ListenAndServe(cfg.Address, basicAuth(util.NewProxyHandler(proxy), "zrok", &resolver{}, zCtx))
|
||||||
Users: []*model.AuthUser{
|
|
||||||
{Username: "hello", Password: "world"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return http.ListenAndServe(cfg.Address, basicAuth(util.NewProxyHandler(proxy), users, "zrok", &resolver{}, zCtx))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolver struct{}
|
type resolver struct{}
|
||||||
@ -165,38 +159,74 @@ func getRefreshedService(name string, ctx ziti.Context) (*edge.Service, bool) {
|
|||||||
return svc, found
|
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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
svcName := rslv.Service(r.Host)
|
svcName := rslv.Service(r.Host)
|
||||||
if svc, found := getRefreshedService(svcName, ctx); found {
|
if svc, found := getRefreshedService(svcName, ctx); found {
|
||||||
if cfg, found := svc.Configs[model.ZrokProxyConfig]; found {
|
if cfg, found := svc.Configs[model.ZrokProxyConfig]; found {
|
||||||
if scheme, found := cfg["auth_scheme"]; found {
|
if scheme, found := cfg["auth_scheme"]; found {
|
||||||
switch scheme {
|
switch scheme {
|
||||||
case model.None:
|
case string(model.None):
|
||||||
|
logrus.Infof("auth scheme none '%v'", svcName)
|
||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
|
||||||
case model.Basic:
|
case string(model.Basic):
|
||||||
|
logrus.Infof("auth scheme basic '%v", svcName)
|
||||||
inUser, inPass, ok := r.BasicAuth()
|
inUser, inPass, ok := r.BasicAuth()
|
||||||
if !ok {
|
if !ok {
|
||||||
writeUnauthorizedResponse(w, realm)
|
writeUnauthorizedResponse(w, realm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
authed := false
|
authed := false
|
||||||
for _, v := range users.Users {
|
if v, found := cfg["basic_auth"]; found {
|
||||||
if subtle.ConstantTimeCompare([]byte(inUser), []byte(v.Username)) == 1 && subtle.ConstantTimeCompare([]byte(inPass), []byte(v.Password)) == 1 {
|
if basicAuth, ok := v.(map[string]interface{}); ok {
|
||||||
authed = true
|
if v, found := basicAuth["users"]; found {
|
||||||
break
|
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 {
|
if !authed {
|
||||||
writeUnauthorizedResponse(w, realm)
|
writeUnauthorizedResponse(w, realm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler.ServeHTTP(w, r)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user