Skip to content

Commit fcf0df3

Browse files
committed
Merge branch 'main' into feat/proxy-integration-tests
2 parents 1feffaa + dc3fa58 commit fcf0df3

2 files changed

Lines changed: 277 additions & 127 deletions

File tree

internal/controller/proxy_controller.go

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,16 @@ import (
1717
"github.com/google/go-querystring/query"
1818
)
1919

20-
type RequestType int
20+
type AuthModuleType int
2121

2222
const (
23-
AuthRequest RequestType = iota
23+
AuthRequest AuthModuleType = iota
2424
ExtAuthz
2525
ForwardAuth
2626
)
2727

2828
var BrowserUserAgentRegex = regexp.MustCompile("Chrome|Gecko|AppleWebKit|Opera|Edge")
2929

30-
var SupportedProxies = []string{"nginx", "traefik", "caddy", "envoy"}
31-
3230
type Proxy struct {
3331
Proxy string `uri:"proxy" binding:"required"`
3432
}
@@ -38,7 +36,11 @@ type ProxyContext struct {
3836
Proto string
3937
Path string
4038
Method string
39+
<<<<<<< HEAD
4140
Type RequestType
41+
=======
42+
Type AuthModuleType
43+
>>>>>>> main
4244
IsBrowser bool
4345
}
4446

@@ -339,12 +341,10 @@ func (controller *ProxyController) getForwardAuthContext(c *gin.Context) (ProxyC
339341
return ProxyContext{}, errors.New("x-forwarded-proto not found")
340342
}
341343

344+
// Normally we should only allow GET for forward auth but since it's a fallback
345+
// for envoy we should allow everything, not a big deal
342346
method := c.Request.Method
343347

344-
if method != http.MethodGet {
345-
return ProxyContext{}, errors.New("method not allowed")
346-
}
347-
348348
return ProxyContext{
349349
Host: host,
350350
Proto: proto,
@@ -368,14 +368,20 @@ func (controller *ProxyController) getAuthRequestContext(c *gin.Context) (ProxyC
368368
}
369369

370370
host := url.Host
371+
372+
if strings.TrimSpace(host) == "" {
373+
return ProxyContext{}, errors.New("host not found")
374+
}
375+
371376
proto := url.Scheme
372-
path := url.Path
373-
method := c.Request.Method
374377

375-
if method != http.MethodGet {
376-
return ProxyContext{}, errors.New("method not allowed")
378+
if strings.TrimSpace(proto) == "" {
379+
return ProxyContext{}, errors.New("proto not found")
377380
}
378381

382+
path := url.Path
383+
method := c.Request.Method
384+
379385
return ProxyContext{
380386
Host: host,
381387
Proto: proto,
@@ -386,31 +392,72 @@ func (controller *ProxyController) getAuthRequestContext(c *gin.Context) (ProxyC
386392
}
387393

388394
func (controller *ProxyController) getExtAuthzContext(c *gin.Context) (ProxyContext, error) {
395+
// We hope for the someone to set the x-forwarded-proto header
389396
proto, ok := controller.getHeader(c, "x-forwarded-proto")
390397

391398
if !ok {
392399
return ProxyContext{}, errors.New("x-forwarded-proto not found")
393400
}
394401

395-
host, ok := controller.getHeader(c, "host")
402+
// It sets the host to the original host, not the forwarded host
403+
host := c.Request.Host
396404

397-
if !ok {
405+
if strings.TrimSpace(host) == "" {
398406
return ProxyContext{}, errors.New("host not found")
399407
}
400408

401-
// Seems like we can't get the path?
409+
// We get the path from the query string
410+
path := c.Query("path")
402411

403412
// For envoy we need to support every method
404413
method := c.Request.Method
405414

406415
return ProxyContext{
407416
Host: host,
408417
Proto: proto,
418+
Path: path,
409419
Method: method,
410420
Type: ExtAuthz,
411421
}, nil
412422
}
413423

424+
func (controller *ProxyController) determineAuthModules(proxy string) []AuthModuleType {
425+
switch proxy {
426+
case "traefik", "caddy":
427+
return []AuthModuleType{ForwardAuth}
428+
case "envoy":
429+
return []AuthModuleType{ExtAuthz, ForwardAuth}
430+
case "nginx":
431+
return []AuthModuleType{AuthRequest, ForwardAuth}
432+
default:
433+
return []AuthModuleType{}
434+
}
435+
}
436+
437+
func (controller *ProxyController) getContextFromAuthModule(c *gin.Context, module AuthModuleType) (ProxyContext, error) {
438+
switch module {
439+
case ForwardAuth:
440+
ctx, err := controller.getForwardAuthContext(c)
441+
if err != nil {
442+
return ProxyContext{}, err
443+
}
444+
return ctx, nil
445+
case ExtAuthz:
446+
ctx, err := controller.getExtAuthzContext(c)
447+
if err != nil {
448+
return ProxyContext{}, err
449+
}
450+
return ctx, nil
451+
case AuthRequest:
452+
ctx, err := controller.getAuthRequestContext(c)
453+
if err != nil {
454+
return ProxyContext{}, err
455+
}
456+
return ctx, nil
457+
}
458+
return ProxyContext{}, fmt.Errorf("unsupported auth module: %v", module)
459+
}
460+
414461
func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext, error) {
415462
var req Proxy
416463

@@ -419,54 +466,38 @@ func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext
419466
return ProxyContext{}, err
420467
}
421468

469+
tlog.App.Debug().Msgf("Proxy: %v", req.Proxy)
470+
471+
authModules := controller.determineAuthModules(req.Proxy)
472+
473+
if len(authModules) == 0 {
474+
return ProxyContext{}, fmt.Errorf("no auth modules supported for proxy: %v", req.Proxy)
475+
}
476+
422477
var ctx ProxyContext
423478

424-
switch req.Proxy {
425-
// For nginx we need to handle both forward_auth and auth_request extraction since it can be
426-
// used either with something line nginx proxy manager with advanced config or with
427-
// the kubernetes ingress controller
428-
case "nginx":
429-
tlog.App.Debug().Str("proxy", req.Proxy).Msg("Attempting forward_auth compatible extraction")
430-
forwardAuthCtx, err := controller.getForwardAuthContext(c)
479+
for _, module := range authModules {
480+
tlog.App.Debug().Msgf("Trying auth module: %v", module)
481+
ctx, err = controller.getContextFromAuthModule(c, module)
431482
if err == nil {
432-
tlog.App.Debug().Str("proxy", req.Proxy).Msg("Extractions success using forward_auth")
433-
ctx = forwardAuthCtx
434-
} else {
435-
tlog.App.Debug().Str("proxy", req.Proxy).Msg("Extractions failed using forward_auth trying with auth_request")
436-
authRequestCtx, err := controller.getAuthRequestContext(c)
437-
if err != nil {
438-
tlog.App.Warn().Str("proxy", req.Proxy).Msg("Failed to determine required module for header extraction")
439-
return ProxyContext{}, err
440-
}
441-
ctx = authRequestCtx
483+
tlog.App.Debug().Msgf("Auth module %v succeeded", module)
484+
break
442485
}
443-
case "envoy":
444-
tlog.App.Debug().Str("proxy", req.Proxy).Msg("Attempting ext_authz compatible extraction")
445-
extAuthzCtx, err := controller.getExtAuthzContext(c)
446-
if err != nil {
447-
tlog.App.Warn().Str("proxy", req.Proxy).Msg("Failed to determine required module for header extraction")
448-
return ProxyContext{}, err
449-
}
450-
ctx = extAuthzCtx
451-
// By default we fallback to the forward_auth module which supports most proxies like traefik or caddy
452-
default:
453-
tlog.App.Debug().Str("proxy", req.Proxy).Msg("Attempting forward_auth compatible extraction")
454-
forwardAuthCtx, err := controller.getForwardAuthContext(c)
455-
if err != nil {
456-
tlog.App.Warn().Str("proxy", req.Proxy).Msg("Failed to determine required module for header extraction")
457-
return ProxyContext{}, err
458-
}
459-
ctx = forwardAuthCtx
486+
tlog.App.Debug().Err(err).Msgf("Auth module %v failed", module)
487+
}
488+
489+
if err != nil {
490+
return ProxyContext{}, err
460491
}
461492

462493
// We don't care if the header is empty, we will just assume it's not a browser
463494
userAgent, _ := controller.getHeader(c, "user-agent")
464495
isBrowser := BrowserUserAgentRegex.MatchString(userAgent)
465496

466497
if isBrowser {
467-
tlog.App.Debug().Msg("Request identified as (most likely) coming from a browser")
498+
tlog.App.Debug().Msg("Request identified as coming from a browser")
468499
} else {
469-
tlog.App.Debug().Msg("Request identified as (most likely) coming from a non-browser client")
500+
tlog.App.Debug().Msg("Request identified as coming from a non-browser client")
470501
}
471502

472503
ctx.IsBrowser = isBrowser

0 commit comments

Comments
 (0)