mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-07-15 22:41:14 +00:00
refactor: rework the way trusted proxies ip work (#1007)
This commit is contained in:
@@ -30,6 +30,16 @@ func (app *BootstrapApp) setupRouter() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set trusted proxies: %w", err)
|
return fmt.Errorf("failed to set trusted proxies: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.runtime.TrustedProxiesConfigured = true
|
||||||
|
} else {
|
||||||
|
err := engine.SetTrustedProxies(nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set trusted proxies: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.log.App.Warn().Msg("Trusted proxies are not configured, IP access controls will NOT work")
|
||||||
}
|
}
|
||||||
|
|
||||||
middlewareProvideFor := []any{
|
middlewareProvideFor := []any{
|
||||||
|
|||||||
@@ -111,9 +111,10 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
|||||||
clientIP := c.ClientIP()
|
clientIP := c.ClientIP()
|
||||||
|
|
||||||
aclsCtx := &service.ACLContext{
|
aclsCtx := &service.ACLContext{
|
||||||
ACLs: acls,
|
ACLs: acls,
|
||||||
IP: net.ParseIP(clientIP),
|
IP: net.ParseIP(clientIP),
|
||||||
Path: proxyCtx.Path,
|
Path: proxyCtx.Path,
|
||||||
|
TrustedProxiesConfigured: controller.runtime.TrustedProxiesConfigured,
|
||||||
}
|
}
|
||||||
|
|
||||||
if controller.policyEngine.Evaluate(service.RuleIPBypassed, aclsCtx) {
|
if controller.policyEngine.Evaluate(service.RuleIPBypassed, aclsCtx) {
|
||||||
|
|||||||
+12
-11
@@ -1,17 +1,18 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type RuntimeConfig struct {
|
type RuntimeConfig struct {
|
||||||
AppURL string
|
AppURL string
|
||||||
UUID string
|
UUID string
|
||||||
CookieDomain string
|
CookieDomain string
|
||||||
SessionCookieName string
|
SessionCookieName string
|
||||||
CSRFCookieName string
|
CSRFCookieName string
|
||||||
RedirectCookieName string
|
RedirectCookieName string
|
||||||
OAuthSessionCookieName string
|
OAuthSessionCookieName string
|
||||||
LocalUsers []LocalUser
|
LocalUsers []LocalUser
|
||||||
OAuthProviders map[string]OAuthServiceConfig
|
OAuthProviders map[string]OAuthServiceConfig
|
||||||
OAuthWhitelist []string
|
OAuthWhitelist []string
|
||||||
ConfiguredProviders []Provider
|
ConfiguredProviders []Provider
|
||||||
|
TrustedProxiesConfigured bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
|
|||||||
@@ -215,6 +215,10 @@ type IPAllowedRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
|
if !ctx.TrustedProxiesConfigured {
|
||||||
|
return EffectAllow // We can't block the proxy
|
||||||
|
}
|
||||||
|
|
||||||
// merge global and per-app block/allow lists
|
// merge global and per-app block/allow lists
|
||||||
blockedIps := append([]string{}, rule.Config.Auth.IP.Block...)
|
blockedIps := append([]string{}, rule.Config.Auth.IP.Block...)
|
||||||
allowedIPs := append([]string{}, rule.Config.Auth.IP.Allow...)
|
allowedIPs := append([]string{}, rule.Config.Auth.IP.Allow...)
|
||||||
@@ -263,6 +267,10 @@ type IPBypassedRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
|
if !ctx.TrustedProxiesConfigured {
|
||||||
|
return EffectDeny
|
||||||
|
}
|
||||||
|
|
||||||
// merge global and per-app bypass lists
|
// merge global and per-app bypass lists
|
||||||
bypassList := append([]string{}, rule.Config.Auth.IP.Bypass...)
|
bypassList := append([]string{}, rule.Config.Auth.IP.Bypass...)
|
||||||
if ctx.ACLs != nil {
|
if ctx.ACLs != nil {
|
||||||
|
|||||||
@@ -611,11 +611,20 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
ctx *ACLContext
|
ctx *ACLContext
|
||||||
expected Effect
|
expected Effect
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "when trusted proxies are not configured, IP is allowed",
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{},
|
||||||
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
},
|
||||||
|
expected: EffectAllow,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "allows when ACLs are nil and no global lists configured",
|
name: "allows when ACLs are nil and no global lists configured",
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -625,7 +634,8 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Block: []string{"10.0.0.1"}},
|
IP: model.AppIP{Block: []string{"10.0.0.1"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -637,8 +647,9 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -648,7 +659,8 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Allow: []string{"192.168.1.0/24"}},
|
IP: model.AppIP{Allow: []string{"192.168.1.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("192.168.1.10"),
|
IP: net.ParseIP("192.168.1.10"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -660,8 +672,9 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("192.168.1.10"),
|
IP: net.ParseIP("192.168.1.10"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -671,15 +684,17 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Allow: []string{"192.168.1.0/24"}},
|
IP: model.AppIP{Allow: []string{"192.168.1.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allows when no block or allow lists are configured",
|
name: "allows when no block or allow lists are configured",
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -692,7 +707,8 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
Allow: []string{"10.0.0.1"},
|
Allow: []string{"10.0.0.1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -705,7 +721,8 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
Allow: []string{"10.0.0.1"},
|
Allow: []string{"10.0.0.1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -735,12 +752,23 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ctx *ACLContext
|
ctx *ACLContext
|
||||||
expected Effect
|
expected Effect
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "when trusted proxies are not configured, IP is not bypassed",
|
||||||
|
rule: defaultIPBR,
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{},
|
||||||
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: false,
|
||||||
|
},
|
||||||
|
expected: EffectDeny,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "deny when ACLs are nil and no global bypass",
|
name: "deny when ACLs are nil and no global bypass",
|
||||||
rule: defaultIPBR,
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -748,8 +776,9 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
name: "allows when ACLs are nil but IP matches global bypass",
|
name: "allows when ACLs are nil but IP matches global bypass",
|
||||||
rule: globBypassIPBR,
|
rule: globBypassIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -757,8 +786,9 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
name: "denies when ACLs are nil and IP does not match global bypass",
|
name: "denies when ACLs are nil and IP does not match global bypass",
|
||||||
rule: globBypassIPBR,
|
rule: globBypassIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("192.168.1.1"),
|
IP: net.ParseIP("192.168.1.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -769,7 +799,8 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -780,7 +811,8 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"172.16.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"172.16.0.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -791,7 +823,8 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
@@ -802,7 +835,8 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("192.168.1.1"),
|
IP: net.ParseIP("192.168.1.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -810,8 +844,9 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
name: "denies when bypass list is empty",
|
name: "denies when bypass list is empty",
|
||||||
rule: defaultIPBR,
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
@@ -822,7 +857,8 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
||||||
},
|
},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ type Rule interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ACLContext struct {
|
type ACLContext struct {
|
||||||
ACLs *model.App
|
ACLs *model.App
|
||||||
UserContext *model.UserContext
|
UserContext *model.UserContext
|
||||||
IP net.IP
|
IP net.IP
|
||||||
Path string
|
Path string
|
||||||
|
TrustedProxiesConfigured bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type PolicyEngine struct {
|
type PolicyEngine struct {
|
||||||
|
|||||||
@@ -163,9 +163,10 @@ func CreateTestConfigs(t *testing.T) (model.Config, model.RuntimeConfig) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CookieDomain: "example.com",
|
CookieDomain: "example.com",
|
||||||
AppURL: "https://tinyauth.example.com",
|
AppURL: "https://tinyauth.example.com",
|
||||||
SessionCookieName: "tinyauth-session",
|
SessionCookieName: "tinyauth-session",
|
||||||
|
TrustedProxiesConfigured: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, runtime
|
return config, runtime
|
||||||
|
|||||||
Reference in New Issue
Block a user