mirror of
https://github.com/tinyauthapp/tinyauth.git
synced 2026-08-22 21:03:32 +08:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf5d5cab6e | ||
|
|
75c7aad40e | ||
|
|
3052cc0da3 |
@@ -227,6 +227,8 @@ TINYAUTH_LDAP_GROUPCACHETTL=900
|
|||||||
|
|
||||||
# Enable the OAuth bridge, uses a new way to format OAuth user information.
|
# Enable the OAuth bridge, uses a new way to format OAuth user information.
|
||||||
TINYAUTH_EXPERIMENTAL_OAUTHBRIDGEENABLED=false
|
TINYAUTH_EXPERIMENTAL_OAUTHBRIDGEENABLED=false
|
||||||
|
# Disable the fallback to forward_auth modules when auth_request or ext_authz fail.
|
||||||
|
TINYAUTH_EXPERIMENTAL_DISABLEAUTHMODULEFALLBACK=false
|
||||||
|
|
||||||
# tailscale config
|
# tailscale config
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ type ProxyContext struct {
|
|||||||
type ProxyController struct {
|
type ProxyController struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
runtime *model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
|
config *model.Config
|
||||||
acls *service.AccessControlsService
|
acls *service.AccessControlsService
|
||||||
auth *service.AuthService
|
auth *service.AuthService
|
||||||
policyEngine *service.PolicyEngine
|
policyEngine *service.PolicyEngine
|
||||||
@@ -67,6 +68,7 @@ type ProxyControllerInput struct {
|
|||||||
|
|
||||||
Log *logger.Logger
|
Log *logger.Logger
|
||||||
RuntimeConfig *model.RuntimeConfig
|
RuntimeConfig *model.RuntimeConfig
|
||||||
|
Config *model.Config
|
||||||
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
ACLsService *service.AccessControlsService
|
ACLsService *service.AccessControlsService
|
||||||
AuthService *service.AuthService
|
AuthService *service.AuthService
|
||||||
@@ -77,6 +79,7 @@ func NewProxyController(i ProxyControllerInput) *ProxyController {
|
|||||||
controller := &ProxyController{
|
controller := &ProxyController{
|
||||||
log: i.Log,
|
log: i.Log,
|
||||||
runtime: i.RuntimeConfig,
|
runtime: i.RuntimeConfig,
|
||||||
|
config: i.Config,
|
||||||
acls: i.ACLsService,
|
acls: i.ACLsService,
|
||||||
auth: i.AuthService,
|
auth: i.AuthService,
|
||||||
policyEngine: i.PolicyEngine,
|
policyEngine: i.PolicyEngine,
|
||||||
@@ -465,6 +468,10 @@ func (controller *ProxyController) getExtAuthzContext(c *gin.Context) (ProxyCont
|
|||||||
// We get the path from the query string
|
// We get the path from the query string
|
||||||
path := c.Query("path")
|
path := c.Query("path")
|
||||||
|
|
||||||
|
if strings.TrimSpace(path) == "" {
|
||||||
|
return ProxyContext{}, errors.New("path not found")
|
||||||
|
}
|
||||||
|
|
||||||
// For envoy we need to support every method
|
// For envoy we need to support every method
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
|
|
||||||
@@ -482,9 +489,17 @@ func (controller *ProxyController) determineAuthModules(proxy ProxyType) []AuthM
|
|||||||
case Traefik, Caddy:
|
case Traefik, Caddy:
|
||||||
return []AuthModuleType{ForwardAuth}
|
return []AuthModuleType{ForwardAuth}
|
||||||
case Envoy:
|
case Envoy:
|
||||||
return []AuthModuleType{ExtAuthz, ForwardAuth}
|
authModules := []AuthModuleType{ExtAuthz}
|
||||||
|
if !controller.config.Experimental.DisableAuthModuleFallback {
|
||||||
|
authModules = append(authModules, ForwardAuth)
|
||||||
|
}
|
||||||
|
return authModules
|
||||||
case Nginx:
|
case Nginx:
|
||||||
return []AuthModuleType{AuthRequest, ForwardAuth}
|
authModules := []AuthModuleType{AuthRequest}
|
||||||
|
if !controller.config.Experimental.DisableAuthModuleFallback {
|
||||||
|
authModules = append(authModules, ForwardAuth)
|
||||||
|
}
|
||||||
|
return authModules
|
||||||
default:
|
default:
|
||||||
return []AuthModuleType{}
|
return []AuthModuleType{}
|
||||||
}
|
}
|
||||||
@@ -514,6 +529,39 @@ func (controller *ProxyController) getContextFromAuthModule(c *gin.Context, modu
|
|||||||
return ProxyContext{}, fmt.Errorf("unsupported auth module: %v", module)
|
return ProxyContext{}, fmt.Errorf("unsupported auth module: %v", module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (controller *ProxyController) authModuleIdentifiersPresent(c *gin.Context, module AuthModuleType) bool {
|
||||||
|
switch module {
|
||||||
|
case ForwardAuth:
|
||||||
|
_, host := controller.getHeader(c, "x-forwarded-host")
|
||||||
|
_, uri := controller.getHeader(c, "x-forwarded-uri")
|
||||||
|
return host || uri
|
||||||
|
case AuthRequest:
|
||||||
|
_, ok := controller.getHeader(c, "x-original-url")
|
||||||
|
return ok
|
||||||
|
case ExtAuthz:
|
||||||
|
return strings.TrimSpace(c.Query("path")) != ""
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ProxyController) ensureNoMultipleAuthModules(c *gin.Context, authModules []AuthModuleType) error {
|
||||||
|
present := 0
|
||||||
|
|
||||||
|
for _, module := range authModules {
|
||||||
|
if controller.authModuleIdentifiersPresent(c, module) {
|
||||||
|
present++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if present > 1 {
|
||||||
|
controller.log.App.Warn().Msg("Request carries headers for multiple auth modules, possible spoofing attempt, denying")
|
||||||
|
return fmt.Errorf("conflicting auth module headers")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext, error) {
|
func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext, error) {
|
||||||
var req Proxy
|
var req Proxy
|
||||||
|
|
||||||
@@ -536,22 +584,30 @@ func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext
|
|||||||
return ProxyContext{}, fmt.Errorf("no auth modules supported for proxy: %v", req.Proxy)
|
return ProxyContext{}, fmt.Errorf("no auth modules supported for proxy: %v", req.Proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctx ProxyContext
|
err = controller.ensureNoMultipleAuthModules(c, authModules)
|
||||||
|
|
||||||
for _, module := range authModules {
|
|
||||||
controller.log.App.Debug().Msgf("Trying to get context from auth module %v", module)
|
|
||||||
ctx, err = controller.getContextFromAuthModule(c, module)
|
|
||||||
if err == nil {
|
|
||||||
controller.log.App.Debug().Msgf("Successfully got context from auth module %v", module)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
controller.log.App.Debug().Msgf("Failed to get context from auth module %v: %v", module, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ProxyContext{}, err
|
return ProxyContext{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ctx *ProxyContext
|
||||||
|
|
||||||
|
for _, module := range authModules {
|
||||||
|
controller.log.App.Debug().Msgf("Trying to get context from auth module %v", module)
|
||||||
|
authModuleCtx, err := controller.getContextFromAuthModule(c, module)
|
||||||
|
if err != nil {
|
||||||
|
controller.log.App.Debug().Msgf("Failed to get context from auth module %v: %v", module, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
controller.log.App.Debug().Msgf("Successfully got context from auth module %v", module)
|
||||||
|
ctx = &authModuleCtx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx == nil {
|
||||||
|
return ProxyContext{}, fmt.Errorf("failed to get context from any auth module")
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the raw path to populate the cleaned path used for ACLs
|
// Parse the raw path to populate the cleaned path used for ACLs
|
||||||
upath, err := url.Parse(ctx.PathRaw)
|
upath, err := url.Parse(ctx.PathRaw)
|
||||||
|
|
||||||
@@ -577,5 +633,5 @@ func (controller *ProxyController) getProxyContext(c *gin.Context) (ProxyContext
|
|||||||
|
|
||||||
ctx.IsBrowser = isBrowser
|
ctx.IsBrowser = isBrowser
|
||||||
ctx.ProxyType = proxy
|
ctx.ProxyType = proxy
|
||||||
return ctx, nil
|
return *ctx, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ func TestProxyController(t *testing.T) {
|
|||||||
description: "Ensure forward auth fallback for envoy",
|
description: "Ensure forward auth fallback for envoy",
|
||||||
middlewares: []gin.HandlerFunc{},
|
middlewares: []gin.HandlerFunc{},
|
||||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
req := httptest.NewRequest("HEAD", "/api/auth/envoy?path=/hello", nil)
|
req := httptest.NewRequest("HEAD", "/api/auth/envoy", nil)
|
||||||
req.Host = ""
|
req.Host = ""
|
||||||
req.Header.Set("x-forwarded-host", "test.example.com")
|
req.Header.Set("x-forwarded-host", "test.example.com")
|
||||||
req.Header.Set("x-forwarded-proto", "https")
|
req.Header.Set("x-forwarded-proto", "https")
|
||||||
@@ -261,7 +261,7 @@ func TestProxyController(t *testing.T) {
|
|||||||
description: "Ensure extauthz with envoy non browser returns json",
|
description: "Ensure extauthz with envoy non browser returns json",
|
||||||
middlewares: []gin.HandlerFunc{},
|
middlewares: []gin.HandlerFunc{},
|
||||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
req := httptest.NewRequest("HEAD", "/api/auth/envoy?path=/hello", nil)
|
req := httptest.NewRequest("HEAD", "/api/auth/envoy", nil)
|
||||||
req.Header.Set("x-forwarded-host", "test.example.com")
|
req.Header.Set("x-forwarded-host", "test.example.com")
|
||||||
req.Header.Set("x-forwarded-proto", "https")
|
req.Header.Set("x-forwarded-proto", "https")
|
||||||
req.Header.Set("x-forwarded-uri", "/hello")
|
req.Header.Set("x-forwarded-uri", "/hello")
|
||||||
@@ -877,6 +877,32 @@ func TestProxyController(t *testing.T) {
|
|||||||
assert.Equal(t, "bar", recorder.Header().Get("x-foo"))
|
assert.Equal(t, "bar", recorder.Header().Get("x-foo"))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Forward auth and auth request headers should fail for nginx",
|
||||||
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
|
req := httptest.NewRequest("GET", "/api/auth/nginx", nil)
|
||||||
|
req.Header.Set("x-forwarded-host", "foo.example.com")
|
||||||
|
req.Header.Set("x-forwarded-proto", "https")
|
||||||
|
req.Header.Set("x-forwarded-uri", "/foo?bar=foo")
|
||||||
|
req.Header.Set("x-original-url", "https://foo.example.com/foo?bar=foo")
|
||||||
|
router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Forward auth and ext authz headers should fail for envoy",
|
||||||
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
|
req := httptest.NewRequest("HEAD", "/api/auth/envoy?path=/hello", nil)
|
||||||
|
req.Host = "foo.example.com"
|
||||||
|
req.Header.Set("x-forwarded-host", "foo.example.com")
|
||||||
|
req.Header.Set("x-forwarded-proto", "https")
|
||||||
|
req.Header.Set("x-forwarded-uri", "/foo?bar=foo")
|
||||||
|
router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
store := memory.New()
|
store := memory.New()
|
||||||
|
|||||||
@@ -239,7 +239,8 @@ type LogStreamConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ExperimentalConfig struct {
|
type ExperimentalConfig struct {
|
||||||
OAuthBridgeEnabled bool `description:"Enable the OAuth bridge, uses a new way to format OAuth user information." yaml:"oauthBridgeEnabled,omitempty"`
|
OAuthBridgeEnabled bool `description:"Enable the OAuth bridge, uses a new way to format OAuth user information." yaml:"oauthBridgeEnabled,omitempty"`
|
||||||
|
DisableAuthModuleFallback bool `description:"Disable the fallback to forward_auth modules when auth_request or ext_authz fail." yaml:"disableAuthModuleFallback,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TailscaleConfig struct {
|
type TailscaleConfig struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user