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

🔥Feature (v3): Add TrustInternalIPs Config Option #3137

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// 3. c.Host() and c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
// will be used to get the hostname.
//
// If you wish to trust all internal ip addresses (loopback, private, and link-local unicast)
// without manually adding them to TrustedProxies whitelist, enable TrustInternalIPs.
//
// Default: false
EnableTrustedProxyCheck bool `json:"enable_trusted_proxy_check"`

Expand All @@ -354,6 +357,11 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
trustedProxiesMap map[string]struct{}
trustedProxyRanges []*net.IPNet

// Read EnableTrustedProxyCheck doc.
//
// Default: false
TrustInternalIPs bool `json:"trust_internal_ips"`

// If set to true, c.IP() and c.IPs() will validate IP addresses before returning them.
// Also, c.IP() will return only the first valid IP rather than just the raw header
// WARNING: this has a performance cost associated with it.
Expand Down
11 changes: 10 additions & 1 deletion ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1828,6 +1828,10 @@

ip := c.fasthttp.RemoteIP()

if c.app.config.TrustInternalIPs && c.isInternalHost(ip) {
return true

Check warning on line 1832 in ctx.go

View check run for this annotation

Codecov / codecov/patch

ctx.go#L1832

Added line #L1832 was not covered by tests
}

if _, trusted := c.app.config.trustedProxiesMap[ip.String()]; trusted {
return true
}
Expand All @@ -1841,9 +1845,14 @@
return false
}

// isInternalHost will return true if address is an internal address.
func (*DefaultCtx) isInternalHost(ip net.IP) bool {
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast()
}

var localHosts = [...]string{"127.0.0.1", "::1"}

// IsLocalHost will return true if address is a localhost address.
// isLocalHost will return true if address is a localhost address.
func (*DefaultCtx) isLocalHost(address string) bool {
for _, h := range localHosts {
if address == h {
Expand Down
46 changes: 46 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1714,6 +1714,16 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}

{
app := New(Config{
EnableTrustedProxyCheck: true,

TrustInternalIPs: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
}

// go test -run Test_Ctx_Hostname
Expand Down Expand Up @@ -6351,4 +6361,40 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
app.ReleaseCtx(c)
})
})

// Scenario with trusted proxy check with TrustInternalIPs true
b.Run("WithProxyCheckOnlyTrustInternalIPs", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustInternalIPs: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})

// Scenario with trusted proxy check with all subnets with TrustInternalIPs true in parallel
b.Run("WithProxyCheckParallelOnlyTrustInternalIPs", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustInternalIPs: true,
})
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
for pb.Next() {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
})
}
6 changes: 4 additions & 2 deletions docs/api/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ app.Get("/", func(c fiber.Ctx) error {

## IsFromLocal

Returns true if request came from localhost
Returns true if request came from localhost.

```go title="Signature"
func (c Ctx) IsFromLocal() bool {
Expand All @@ -831,7 +831,7 @@ app.Get("/", func(c fiber.Ctx) error {
## IsProxyTrusted

Checks trustworthiness of remote ip.
If [`EnableTrustedProxyCheck`](fiber.md#enabletrustedproxycheck) false, it returns true
If [`EnableTrustedProxyCheck`](fiber.md#enabletrustedproxycheck) false, it returns true.
IsProxyTrusted can check remote ip by proxy ranges and ip map.

```go title="Signature"
Expand All @@ -845,6 +845,8 @@ app := fiber.New(fiber.Config{
EnableTrustedProxyCheck: true,
// TrustedProxies is a list of trusted proxy IP addresses
TrustedProxies: []string{"0.8.0.0", "0.8.0.1"},
// TrustInternalIPs can be used to trust all private/loopback/linklocal-unicast addresses
TrustInternalIPs: true,
})


Expand Down
3 changes: 2 additions & 1 deletion docs/api/fiber.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ app := fiber.New(fiber.Config{
| <Reference id="bodylimit">BodyLimit</Reference> | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` |
| <Reference id="casesensitive">CaseSensitive</Reference> | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| <Reference id="colorscheme">ColorScheme</Reference> | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="concurrency">Concurrency</Reference> | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| <Reference id="disabledefaultcontenttype">DisableDefaultContentType</Reference> | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| <Reference id="disabledefaultdate">DisableDefaultDate</Reference> | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
Expand All @@ -75,6 +75,7 @@ app := fiber.New(fiber.Config{
| <Reference id="strictrouting">StrictRouting</Reference> | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| <Reference id="structvalidator">StructValidator</Reference> | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` |
| <Reference id="trustedproxies">TrustedProxies</Reference> | `[]string` | Contains the list of trusted proxy IP's. Look at `EnableTrustedProxyCheck` doc. <br /> <br /> It can take IP or IP range addresses. | `nil` |
| <Reference id="trustedproxies">TrustInternalIPs</Reference> | `bool` | If `EnableTrustedProxyCheck` is `true`, automatically trust all private, loopback, and link-local unicast IP addresses. | `false` |
| <Reference id="unescapepath">UnescapePath</Reference> | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` |
| <Reference id="views">Views</Reference> | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| <Reference id="viewslayout">ViewsLayout</Reference> | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |
Expand Down
Loading