Description
Is your feature request related to a problem? Please describe.
My application resides behind a Google Application Load Balancer. To parse the client's IP I must use the X-Forwarded-For header, which is documented in GCP's docs here.
To achieve this, I set the following config:
AXES_IPWARE_META_PRECEDENCE_ORDER = ['HTTP_X_FORWARDED_FOR']
AXES_IPWARE_PROXY_COUNT = 1
This works fine when the header is formed like so:
X-Forwarded-For: <client-ip>,<load-balancer-ip>
However, because the Google loadbalancer will propagate the user's own supplied X-Forwarded-For header, it may also be in this format:
X-Forwarded-For: <supplied-value>,<client-ip>,<load-balancer-ip>
As this is a valid header, I want axes to continue to use <client-ip>
, which is fully determinable because we set PROXY_COUNT
accordingly, so it should use the value PROXY_COUNT
away from the right-most IP address.
However, because axes uses IpWare in strict mode, it will reject this header and instead populate the client's IP as None
.
Here's how this currently works:
- Axes calls IpWare using this helper function:
Lines 196 to 203 in 9acda1f
- And IpWare handles this here.
- In the exert, this calls
get_client_ip
hardcoded with strict=True. This is not exposed as a configurable value
- In the exert, this calls
- Because we have
strict=True
means theis_proxy_count_valid
check inpython-ipware
fails in my described scenario. See here.-
This is because when the user supplies their own value the
ip_count
is not exactlyproxy_count+1
parts. There is an indefinite number of parts (as the user-supplied value may be any value with any number of parts). -
However, if I could use the same logic with
strict=False
, all my problems would go away, because theget_best_ip
function would still resolve to the correct client IP (right-most, offset by the configured proxy count):
if self.proxy_count is not None: best_client_ip_index = self.proxy_count + 1 best_client_ip = ip_list[-best_client_ip_index] return best_client_ip, True
-
Describe the solution you'd like
I would like django-axes to expose a AXES_IPWARE_STRICT
config item. The value could default to True
for backwards-compatibility reasons. This should get propagated to ipware's get_client_ip
arg:
def get_client_ip(
self,
meta: Dict[str, str],
strict: bool = False,
) -> Tuple[OptionalIpAddressType, bool]:
Describe alternatives you've considered
Alternatively, I could use django-axes's config item AXES_CLIENT_IP_CALLABLE
to define my own IP resolver function. This would allow me to define my own logic, such as calling IpWare myself with the desired config.