Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Usage of trusted_proxies is mandatory for Decision API to accept X-Forwarded-* headers #111

Merged
merged 4 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions docs/content/docs/configuration/services/decision_api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,9 @@ api:
+
The usage of the Decision API makes only sense, if operated behind some sort of proxy, like API Gateway, etc. In such cases certain header information may be sent to Heimdall using special `X-Forwarded-*` headers or the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded[Forwarded] header. For example, the `Host` HTTP header is usually used to return the requested host. But when you’re behind a proxy, the actual host may be stored in an `X-Forwarded-Host` header, which could, however, also be spoofed.
+
To prevent header spoofing and allowing such headers to be accepted from trusted proxies only (so the systems, you have configured to make use of Heimdall's Decision API), you should configure the `trusted_proxies` option and list the IPs, or IP ranges (CIDR notation) of your proxies, which make use of Heimdall's Decision API. If not configured, Heimdall will accept those headers from any client.
To prevent header spoofing and allowing such headers to be accepted from trusted proxies only (so the systems, you have configured to make use of Heimdall's Decision API), you should configure the `trusted_proxies` option and list the IPs, or IP ranges (CIDR notation) of your proxies, which make use of Heimdall's Decision API. If not configured, Heimdall will not accept those headers from any client.
+
If you configure `trusted_proxies` to an empty list, Heimdall will not make use of any headers that could be spoofed. This would usually be the configuration for proxies, which do not send information about the client request in headers, but use the corresponding HTTP scheme, method, path, etc, while communicating with Heimdall (e.g. Nginx). If you put Heimdall behind e.g. Traefik, which forwards this information in `X-Forwarded-*` headers, you should configure the `trusted_proxies` list to contain the addresses of your Traefik instances only.
+
.Disable usage of any headers, which could be spoofed for all clients
====
[source, yaml]
----
api:
trusted_proxies: []
----
====
+
.Disable usage of any headers, which could be spoofed for all clients, except those listed in the configuration.
.Enable the usage of the above said headers for only those requests, which came from the IPs listed in the configuration.
====
[source, yaml]
----
Expand All @@ -149,7 +138,7 @@ api:
----
====
+
.Disable usage of any headers, which could be spoofed for all clients, except those which are within the configured IP range.
.Enable the usage of the above said headers for only those requests, which came from the network listed in the configuration.
====
[source, yaml]
----
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/guides/traefik.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ To achieve this,

[IMPORTANT]
====
Traefik makes use of `X-Forwarded-*` HTTP headers to forward the HTTP method, protocol, host, etc. to the ForwardAuth middleware. These headers can be spoofed, if no caution is taken. To ensure, heimdall accepts such headers only from trusted sources, you should consider setting link:{{< relref "/docs/configuration/services/decision_api.adoc#_trusted_proxies" >}}[trusted proxies] in heimdall's decision api configuration.
Traefik makes use of `X-Forwarded-*` HTTP headers to forward the HTTP method, protocol, host, etc. to the ForwardAuth middleware. These headers can be spoofed, if no caution is taken. To allow, heimdall making use of such headers, you must configure link:{{< relref "/docs/configuration/services/decision_api.adoc#_trusted_proxies" >}}[trusted proxies] in heimdall's decision api configuration to contain the IPs or networks of your traefik instances. For test purposes, you can set it to "0.0.0.0/0", which would basically disable the check and let heimdall trust requests from any client.
====

.Using Docker labels
Expand Down
14 changes: 7 additions & 7 deletions internal/handler/decision/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {
},
{
uc: "successful rule execution - request method, path and hostname " +
"all are taken from the headers (trusted proxy not configured)",
"are not taken from the headers (trusted proxy not configured)",
createRequest: func(t *testing.T) *http.Request {
t.Helper()

Expand All @@ -204,15 +204,15 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {

req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Host", "test.com")
req.Header.Set("X-Forwarded-Uri", "bar")
req.Header.Set("X-Forwarded-Path", "bar")
req.Header.Set("X-Forwarded-Method", "GET")

return req
},
configureMocks: func(t *testing.T, repository *mocks2.MockRepository, rule *mocks4.MockRule) {
t.Helper()

rule.On("MatchesMethod", "GET").Return(true)
rule.On("MatchesMethod", "POST").Return(true)
rule.On("Execute", mock.MatchedBy(func(ctx *requestcontext.RequestContext) bool {
ctx.AddHeaderForUpstream("X-Foo-Bar", "baz")
ctx.AddCookieForUpstream("X-Bar-Foo", "zab")
Expand All @@ -221,7 +221,7 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {
})).Return(nil)

repository.On("FindRule", mock.MatchedBy(func(reqURL *url.URL) bool {
return reqURL.Scheme == "https" && reqURL.Host == "test.com" && reqURL.Path == "bar"
return reqURL.Scheme == "http" && reqURL.Host == "heimdall.test.local" && reqURL.Path == "/foobar"
})).Return(rule, nil)
},
assertResponse: func(t *testing.T, err error, response *http.Response) {
Expand Down Expand Up @@ -257,7 +257,7 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {

req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Host", "test.com")
req.Header.Set("X-Forwarded-Uri", "bar")
req.Header.Set("X-Forwarded-Path", "bar")
req.Header.Set("X-Forwarded-Method", "GET")

return req
Expand Down Expand Up @@ -374,7 +374,7 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {
"http://heimdall.test.local/decisions/foobar",
nil)

req.Header.Set("X-Forwarded-Uri", "bar")
req.Header.Set("X-Forwarded-Path", "bar")

return req
},
Expand Down Expand Up @@ -442,7 +442,7 @@ func TestHandleDecisionEndpointRequest(t *testing.T) {

req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Host", "test.com")
req.Header.Set("X-Forwarded-Uri", "bar")
req.Header.Set("X-Forwarded-Path", "bar")
req.Header.Set("X-Forwarded-Method", "PATCH")

return req
Expand Down
2 changes: 1 addition & 1 deletion internal/handler/decision/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func newFiberApp(conf config.Configuration, cache cache.Cache) *fiber.App {
IdleTimeout: service.Timeout.Idle,
DisableStartupMessage: true,
ErrorHandler: errorhandler.NewErrorHandler(service.VerboseErrors),
EnableTrustedProxyCheck: service.TrustedProxies != nil,
EnableTrustedProxyCheck: true,
TrustedProxies: x.IfThenElseExec(service.TrustedProxies != nil,
func() []string { return *service.TrustedProxies },
func() []string { return []string{} }),
Expand Down