|
| 1 | ++++ |
| 2 | +title = "IP Address" |
| 3 | +description = "IP address handling in Echo" |
| 4 | +[menu.main] |
| 5 | + name = "IP Address" |
| 6 | + parent = "guide" |
| 7 | ++++ |
| 8 | + |
| 9 | +IP address plays fundamental role in HTTP; it's used for access control, auditting, geo-based access analysis and more. |
| 10 | +Echo provides handy method [`Context#RealIP()`](https://godoc.org/github.com/labstack/echo#Context) for that. |
| 11 | + |
| 12 | +However, it is not trivial to retrieve the _real_ IP address from requests especially when you put L7 proxies before the application. |
| 13 | +In such situation, _real_ IP needs to be relayed on HTTP layer from proxies to your app, but you must not trust HTTP headers unconditionally. |
| 14 | +Otherwise you might give someone a chance of deceiving you. **A security risk!** |
| 15 | + |
| 16 | +To retrieve IP address reliably/securely, you must let your application be aware of the entire architecture of your infrastructrure. |
| 17 | +In Echo, this can be done by configuring `Echo#IPExtractor` appropriately. |
| 18 | +This guides show you why and how. |
| 19 | + |
| 20 | +> Note: if you dont' set `Echo#IPExtractor` explicitly, Echo fallback to legacy behavior, which is not a good choice. |
| 21 | +
|
| 22 | +Let's start from two questions to know the right direction: |
| 23 | + |
| 24 | +1. Do you put any HTTP (L7) proxy in front of the application? |
| 25 | + - It includes both cloud solutions (such as AWS ALB or GCP HTTP LB) and OSS ones (such as Nginx, Envoy or Istio ingress gateway). |
| 26 | +2. If yes, what HTTP header do your proxies use to pass client IP to the application? |
| 27 | + |
| 28 | +## Case 1. With no proxy |
| 29 | + |
| 30 | +If you put no proxy (e.g.: directory facing to the internet), all you need to (and have to) see is IP address from network layer. |
| 31 | +Any HTTP header is untrustable because the clients have full control what headers to be set. |
| 32 | + |
| 33 | +In this case, use `echo.ExtractIPDirect()`. |
| 34 | + |
| 35 | +```go |
| 36 | +e.IPExtractor = echo.ExtractIPDirect() |
| 37 | +``` |
| 38 | + |
| 39 | +## Case 2. With proxies using `X-Forwarded-For` header |
| 40 | + |
| 41 | +[`X-Forwared-For` (XFF)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) is the popular header to relay clients' IP addresses. |
| 42 | +At each hop on the proxies, they append the request IP address at the end of the header. |
| 43 | + |
| 44 | +Following example diagram illustrates this behavior. |
| 45 | + |
| 46 | +```text |
| 47 | + ┌──────────┐ ┌──────────┐ ┌──────────┐ |
| 48 | +───────────>│ Proxy 1 │───────────>│ Proxy 2 │───────────>│ Your app │ |
| 49 | + │ (IP: b) │ │ (IP: c) │ │ │ |
| 50 | + └──────────┘ └──────────┘ └──────────┘ |
| 51 | +
|
| 52 | +Case 1. |
| 53 | +XFF: "" "a" "a, b" |
| 54 | + ~~~~~~ |
| 55 | +Case 2. |
| 56 | +XFF: "x" "x, a" "x, a, b" |
| 57 | + ~~~~~~~~~ |
| 58 | + ↑ What your app will see |
| 59 | +``` |
| 60 | + |
| 61 | +In this case, use **first _untrustable_ IP reading from right**. Never use first one reading from left, as it is configurable by client. Here "trustable" means "you are sure the IP address belongs to your infrastructre". In above example, if `b` and `c` are trustable, the IP address of the client is `a` for both cases, never be `x`. |
| 62 | + |
| 63 | +In Echo, use `ExtractIPFromXFFHeader(...TrustOption)`. |
| 64 | + |
| 65 | +```go |
| 66 | +e.IPExtractor = echo.ExtractIPFromXFFHeader() |
| 67 | +``` |
| 68 | + |
| 69 | +By default, it trusts internal IP addresses (loopback, link-local unicast, private-use and unique local address from [RFC6890](https://tools.ietf.org/html/rfc6890), [RFC4291](https://tools.ietf.org/html/rfc4291) and [RFC4193](https://tools.ietf.org/html/rfc4193)). To control this behavior, use [`TrustOption`](https://godoc.org/github.com/labstack/echo#TrustOption)s. |
| 70 | + |
| 71 | +E.g.: |
| 72 | + |
| 73 | +```go |
| 74 | +e.IPExtractor = echo.ExtractIPFromXFFHeader( |
| 75 | + TrustLinkLocal(false), |
| 76 | + TrustIPRanges(lbIPRange), |
| 77 | +) |
| 78 | +``` |
| 79 | + |
| 80 | +- Ref: https://godoc.org/github.com/labstack/echo#TrustOption |
| 81 | + |
| 82 | +## Case 3. With proxies using `X-Real-IP` header |
| 83 | + |
| 84 | +`X-Real-IP` is another HTTP header to relay clients' IP addresses, but it carries only one address unlike XFF. |
| 85 | + |
| 86 | +If your proxies set this header, use `ExtractIPFromRealIPHeader(...TrustOption)`. |
| 87 | + |
| 88 | +```go |
| 89 | +e.IPExtractor = echo.ExtractIPFromRealIPHeader() |
| 90 | +``` |
| 91 | + |
| 92 | +Again, it trusts internal IP addresses by default (loopback, link-local unicast, private-use and unique local address from [RFC6890](https://tools.ietf.org/html/rfc6890), [RFC4291](https://tools.ietf.org/html/rfc4291) and [RFC4193](https://tools.ietf.org/html/rfc4193)). To control this behavior, use [`TrustOption`](https://godoc.org/github.com/labstack/echo#TrustOption)s. |
| 93 | + |
| 94 | +- Ref: https://godoc.org/github.com/labstack/echo#TrustOption |
| 95 | + |
| 96 | +> **Never forget** to configure the outermost proxy (i.e.; at the edge of your infrastructure) **not to pass through incoming headers**. |
| 97 | +> Otherwise there is a chance of fraud, as it is what clients can control. |
| 98 | +
|
| 99 | +## About default behavior |
| 100 | + |
| 101 | +In default behavior, Echo sees all of first XFF header, X-Real-IP header and IP from network layer. |
| 102 | + |
| 103 | +As you might already notice, after reading this article, this is not good. |
| 104 | +Sole reason this is default is just backward compatibility. |
0 commit comments