Code Monkey home page Code Monkey logo

django-ipware's Introduction

Django IPware

A Django application to retrieve client's IP address

status-image version-image coverage-image

Alternative package

If you prefer a python only version that does not integrate with Django directly, but allows for more flexibility and advanced features, you can use the python-ipware package instead. django-ipware is a wrapper using python-ipware under the hood staring from version 6.0.0.

Overview

Best attempt to get client's IP address while keeping it DRY.

Notice

There is no perfect out-of-the-box solution against fake IP addresses, aka IP Address Spoofing. You are encouraged to read the (Advanced users) section of this page and use trusted_proxies_ips and/or proxy_count features to match your needs, especially if you are planning to include ipware in any authentication, security or anti-fraud related architecture.

This is an open source project, with the source code visible to all. Therefore, it may be exploited through unimplemented, or improperly implemented features.

Please use ipware ONLY as a complement to your firewall security measures!

How to install

1. easy_install django-ipware
2. pip install django-ipware
3. git clone http://github.com/un33k/django-ipware
    a. cd django-ipware
    b. run python setup.py install
4. wget https://github.com/un33k/django-ipware/zipball/master
    a. unzip the downloaded file
    b. cd into django-ipware-* directory
    c. run python setup.py install

How to use

 # In a view or a middleware where the `request` object is available

 from ipware import get_client_ip
 client_ip, is_routable = get_client_ip(request)
 if client_ip is None:
    # Unable to get the client's IP address
 else:
     # We got the client's IP address
     if is_routable:
         # The client's IP address is publicly routable on the Internet
     else:
         # The client's IP address is private

Advanced users:

  • Precedence Order

    The default meta precedence order is top to bottom. You may customize the order by providing your own IPWARE_META_PRECEDENCE_ORDER by adding it to your project's settings.py

     # The default meta precedence order (update as needed)
     IPWARE_META_PRECEDENCE_ORDER = (
          "X_FORWARDED_FOR",  # Load balancers or proxies such as AWS ELB (default client is `left-most` [`<client>, <proxy1>, <proxy2>`])
          "HTTP_X_FORWARDED_FOR",  # Similar to X_FORWARDED_TO
          "HTTP_CLIENT_IP",  # Standard headers used by providers such as Amazon EC2, Heroku etc.
          "HTTP_X_REAL_IP",  # Standard headers used by providers such as Amazon EC2, Heroku etc.
          "HTTP_X_FORWARDED",  # Squid and others
          "HTTP_X_CLUSTER_CLIENT_IP",  # Rackspace LB and Riverbed Stingray
          "HTTP_FORWARDED_FOR",  # RFC 7239
          "HTTP_FORWARDED",  # RFC 7239
          "HTTP_CF_CONNECTING_IP",  # CloudFlare
          "X-CLIENT-IP",  # Microsoft Azure
          "X-REAL-IP",  # NGINX
          "X-CLUSTER-CLIENT-IP",  # Rackspace Cloud Load Balancers
          "X_FORWARDED",  # Squid
          "FORWARDED_FOR",  # RFC 7239
          "CF-CONNECTING-IP",  # CloudFlare
          "TRUE-CLIENT-IP",  # CloudFlare Enterprise,
          "FASTLY-CLIENT-IP",  # Firebase, Fastly
          "FORWARDED",  # RFC 7239
          "CLIENT-IP",  # Akamai and Cloudflare: True-Client-IP and Fastly: Fastly-Client-IP
          "REMOTE_ADDR",  # Default
      )

    Alternatively, you can provide your custom request header meta precedence order when calling get_client_ip().

get_client_ip(request, request_header_order=['X_FORWARDED_FOR'])
get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'HTTP_X_FORWARDED_FOR'])
  • Proxy Count

    The default meta proxy count is 0 unless explictly provided as an argument to get_client_ip(). You may customize the order by providing your own IPWARE_META_PROXY_COUNT by adding it to your project's settings.py

Trusted Proxies

If your Django server is behind one or more known proxy server(s), you can filter out unwanted requests by providing the trusted proxy list when calling get_client_ip(request, proxy_trusted_ips=['177.139.233.133']). In the following example, your load balancer (LB) can be seen as a trusted proxy.

 `Real` Client  <public> <---> <public> LB (Server) <private> <--------> <private> Django Server
                                                                   ^
                                                                   |
 `Fake` Client  <private> <---> <private> LB (Server) <private> ---^
# In the above scenario, use your load balancer IP address as a way to filter out unwanted requests.
client_ip, is_routable = get_client_ip(request, proxy_trusted_ips=['177.139.233.133'])

# If you have multiple proxies, simply add them to the list
client_ip, is_routable = get_client_ip(request, proxy_trusted_ips=['177.139.233.133', '177.139.233.134'])

# For proxy servers with fixed sub-domain and dynamic IP, use the following pattern.
client_ip, is_routable = get_client_ip(request, proxy_trusted_ips=['177.139.', '177.140'])
client_ip, is_routable = get_client_ip(request, proxy_trusted_ips=['177.139.233.', '177.139.240'])

Please note: By default, the right-most proxy in the chain is the trusted proxy and that is the one your django server talks to. Therefore, ipware checks to see if the right-most proxy address starts with any ip pattern that was passed in via the proxy_trusted_ips list.

Proxy Count

If your Django server is behind a known number of proxy server(s), you can filter out unwanted requests by providing the number of proxies when calling get_client_ip(request, proxy_count=1). In the following example, your load balancer (LB) can be seen as the only proxy.

 `Real` Client  <public> <---> <public> LB (Server) <private> <--------> <private> Django Server
                                                                   ^
                                                                   |
                                       `Fake` Client  <private> ---^
# In the above scenario, the total number of proxies can be used as a way to filter out unwanted requests.
client_ip, is_routable = get_client_ip(request, proxy_count=1)

# The above may be very useful in cases where your proxy server's IP address is assigned dynamically.
# However, If you have the proxy IP address, you can use it in combination to the proxy count.
client_ip, is_routable = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['177.139.233.133'])

Originating Request

If your proxy server is configured such that the right-most IP address is that of the originating client, you can indicate right-most as your proxy_order when calling get_client_ip(request, proxy_order="right-most"). Please note that the de-facto standard for the originating client IP address is the left-most as per <client>, <proxy1>, <proxy2>.

Running the tests

To run the tests against the current environment:

python manage.py test

License

Released under a (MIT) license.

Version

X.Y.Z Version

`MAJOR` version -- when you make incompatible API changes,
`MINOR` version -- when you add functionality in a backwards-compatible manner, and
`PATCH` version -- when you make backwards-compatible bug fixes.

Sponsors

Neekware Inc.

django-ipware's People

Contributors

adamchainz avatar antonrom1 avatar awais786 avatar bashu avatar cclauss avatar edsfocci avatar felixxm avatar gopackgo90 avatar illagrenan avatar irtazaakram avatar jchappell82 avatar joshuadavidthomas avatar jrobichaud avatar kishorkunal-raj avatar lorddaedra avatar mcroni avatar mikaraunio avatar mmcclelland1002 avatar petrdlouhy avatar rposborne avatar sabueso avatar sinwoobang avatar therefromhere avatar un33k avatar uneekvu avatar xf-fw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-ipware's Issues

Using ipware on localhost

Hi folks, i am using it to get the ip_address of users on django, after that i get more info from third party sites like http://ipstack.com, etc. But when we ran it on localhost the ip returned from the ipware is None, there is any way to return my ip address on development environment?

If it return my real ip, that already should help.
I'm not sure if that makes sense, i'm just trying to figure out how to use it on localhost without adding specific code just for running on localhost or using a tunnel like http://ngrok.com.

Thanks in advance.

Isn't it bug?

Inside of get_trusted_ip function, there is a conditional statement: len(ips) > 1.

I think that it should be len(ips) >= 1 ?

Feature request: allow to add custom http headers, also allow to disable some http headers

Some CDN providers provides custom HTTP header, to provide the client ip. Example: Aliyun CDN, which providing HTTP_ALI_CDN_REAL_IP header.

Some CDN providers only use a subset of the HTTP header of what your library provides. In this situation, malicious users could make faked ip through these headers.

So, it's best to allow developers specify the HTTP headers that can be used to determine the IP, the list of which is configured per CDN provider.
Example:

request_header_list= [
'HTTP_ALI_CDN_REAL_IP',
'HTTP_X_FORWARDED_FOR',
'REMOTE_ADDR'
]

So that the IP address can be obtained while the security is enforced for advanced developers.


After checking the existing source code, this feature is already implemented through request_header_order.

However,

  • Document about this feature can be provided.
  • request_header_list is a better name than request_header_order.

Python 3

Is this library compatible with Python 3?

Security risks of the default settings

The default IPWARE_META_PRECEDENCE_ORDER settings cause django-ipware to check for various types of headers by default. However, not all headers may be there, or get filtered through the webserver.

In a standard setup (apache+mod_wsgi or nginx+uwsgi), only REMOTE_ADDR applies and needs to be read. Since there are many other headers in IPWARE_META_PRECEDENCE_ORDER, I could pass any of these to fake an IP address:

  • X-Forwarded-For
  • Client-IP
  • X-Real-IP
  • X-Forwarded
  • X-Cluster-Client-IP
  • Forwarded-For
  • Forwarded
  • Via

I'd strongly recommend reducing IPWARE_META_PRECEDENCE_ORDER to just REMOTE_ADDR and let users configure their system explicitly when they use a forwarding http server in their setup.

What is the difference between getting IP from django-ipware versus stream

Hi I came across this question in stack overflow where a user wants to know a REMOTE_PORT number.
https://stackoverflow.com/questions/66892747/how-do-i-get-the-client-remote-port-number-in-a-django

The answer uses the request from views:

sock = request._stream.stream.stream.raw._sock
client_ip, port = sock.getpeername()

I was wondering what difference it is with django-ipware's IP versus this the client_ip above?
It seems that the above method is the most accurate as it gets the actual ip from SOCK connection?

Where django-ipware ip collected using this code:

from ipware import get_client_ip

client_ip, is_routable = get_client_ip(request)

Thanks all.

Any IP starting with `::` is treated as private

I believe that all IPv6 addresses other than that can be written with a :: prefix (except for :: and ::1) are reserved but routable. However, django-ipware considers these addresses non-public:

>>> import ipware.utils as ipware_utils
>>> ipware_utils.is_public_ip('::1:2:3:4')
False

In comparison, here's Python's built-in ipaddress module, available since 3.3 and using is_global which is available since 3.4:

>>> import ipaddress
>>> ipaddress.ip_address('::1:2:3:4').is_global
True

The bug here is in the use of startsWith for IP address comparison, particularly when it comes to IPv6 addresses. Similarly, ipware_utils.is_loopback_ip('::1:2:3:4') incorrectly returns True, and ipware_utils.is_loopback_ip('0:0:0:0:0:0:0:1') incorrectly returns False.

I would recommend switching to using the builtin ipaddress module now that it is available.

Using django-ipware get_client_ip instead of get_real_ip

Please help!
Already asked in stackoverflow: https://stackoverflow.com/questions/52162711/using-django-ipware-get-client-ip-instead-of-get-real-ip

In django-ipware version 2.1 ; old get_real_ip function is deprecated. When I use the new get_client_ip ; my test units are not showing the same results. Means that the two functions do not behave the same.

The following is an original test from django-ipware test unit (not mine)

def test_http_x_forwarded_for_multiple(self):
    request = HttpRequest()
    request.META = {
        'HTTP_X_FORWARDED_FOR': '192.168.255.182, 10.0.0.0, 127.0.0.1, 198.84.193.157, 177.139.233.139',
        'HTTP_X_REAL_IP': '177.139.233.132',
        'REMOTE_ADDR': '177.139.233.133',
    }
    ip = get_real_ip(request)
    self.assertEqual(ip, "198.84.193.157")

The above works fine of course, but I want to ensure that using the new get_client_ip will give the same results (for a system upgrade purposes). But the test is actually failing the assertion:

def test_http_x_forwarded_for_multiple(self):
    request = HttpRequest()
    request.META = {
        'HTTP_X_FORWARDED_FOR': '192.168.255.182, 10.0.0.0, 127.0.0.1, 198.84.193.157, 177.139.233.139',
        'HTTP_X_REAL_IP': '177.139.233.132',
        'REMOTE_ADDR': '177.139.233.133',
    }
    ip, is_routable = get_client_ip(request)
    self.assertEqual(ip, "198.84.193.157")

resulting:

AssertionError: '177.139.233.132' != '198.84.193.157'

After digging into the code, I found that the new get_client_ip is not iterating inside the meta like get_real_ip . It checks out the left-most ip (or right-most depending on the settings) and skips to the next meta if a Public IP is not found

My question(s) now are:
How can I call get_client_ip in a way that returns the same ip returned by get_real_ip ? What is the logic behind changing the behavior of the function ? Should I trust the new get_client_ip and forget about get_real_ip, or keep using the deprecated get_real_ip and forget about the new get_client_ip ?????

Looking for 1-2 contributors to de-risk the project as it is now used by many projects

django-ipware is now used by many projects and many major companies. As such, it would be smart to have more than one person with admin access to the project, especially, in times of pandemics.

If you would like to contribute in de-risking django-ipware and posses solid python background, with keen interests in supporting open source projects for the benefit of many; - send me an email with links to a few python projects you are maintaining.

I will select two individuals and promote them to admin level.

I'd appreciate your consideration.

Val

get_client_ip returns the wrong value

In the beginning, the function was working perfectly but all of the sudden like 3 days ago things just changed. when I query it returns the private IP address of the network load balancer. My config is like
client -> Network LB -> Application LB -> Nginx -> Django server

I just call the function with default values, I didn't pass the arguments like proxy count or trusted IPs. Are these values a must or it is the issue with LB configuration?

Any idea, please help?

Clarification of "publicly accessible"

Could you please clarify what you mean by "publicly accessible"? For example if I have an application server running on a different physical server than my web server is running on, and the application server is only accessible from the web server, does the application server count as publicly accessible? It's not directly accessible by the public, but information from it is publicly accessible via the web server. Does it still count as a publicly accessible server for the purposes of using ipware?

1.0.0.0/8 and 2.0.0.0/8

You list the 1.0.0.0/8 and 2.0.0.0/8 spaces as private by default. That's not true anymore — they have been opened up for use around 2010.

Currently, we're getting errors cuz we cannot get the IP of some visitors, with 1.38… prefix. While of course I can override the setting, it'd be more convenient (and helpful for others) to drop those two ranges from the defaults list.

using ipware in model pre_save

I have a model in my project without view which creates a new associated record on post_save of parent model.

I used

from django.urls import reverse
from ipware.ip import get_real_ip

class ShortUrlVisitLogs(models.Model):
     shorturl = models.ForeignKey(ShortUrl, on_delete=models.CASCADE, blank=True)
     ip_address = models.CharField(max_length=225, blank=True, null=True)
     visit_time = models.DateTimeField(auto_now_add=True)

@receiver(pre_save, sender=ShortUrlVisitLogs)
def pre_save_short_url_visit_logs(sender, instance, *args, **kwargs):
    instance.ip_address = get_real_ip(request)

Since this does not require any view. I want to use get_real_ip(request) in the model pre_save receiver as shown above.

But this is returning error

module 'django.http.request' has no attribute 'META'

'NoneType' object has no attribute 'strip' when META['REMOTE_ADDR'] is None

Hi, in local development mode with gunicorn it sets REMOTE_ADDR to None, and it crashed application. Here's fix.

From 26043ba8ea16122db8f2e693cc54483604843fe3 Mon Sep 17 00:00:00 2001
From: Serg Tereshchenko <[email protected]>
Date: Fri, 27 May 2022 11:32:07 +0300
Subject: [PATCH] fix: Fix 'NoneType' object has no attribute 'strip' when
 META['REMOTE_ADDR'] is None

---
 ipware/utils.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/ipware/utils.py b/ipware/utils.py
index 13ce776..dd3283b 100644
--- a/ipware/utils.py
+++ b/ipware/utils.py
@@ -73,7 +73,11 @@ def get_request_meta(request, key):
     """
     Given a key, it returns a cleaned up version of the value from request.META, or None
     """
-    value = request.META.get(key, request.META.get(key.replace('_', '-'), '')).strip()
+    value = (
+        request.META.get(key)
+        or request.META.get(key.replace('_', '-'))
+        or ''
+    ).strip()
     if value == '':
         return None
     return value
--
2.36.1

Release this package with Django 3.2 support

Hey, we are working on upgrading edX codebase to Django 3.2. This package is being used in the codebase but it isn't yet released with Django 3.2 support. Kindly consider releasing it with Django 3.2 support.
Thanks

Django 1.9 support

I'm thinking into upgrade a project that uses django-ipware very soon to Django 1.9. Is this already supported? Thanks in advance.

Feature proposal: get IP address in list of IPs based on `ip_count`

First - thanks for this package & especially in our case the use of it in django-axes.

I have read #8 about this topic and I believe this proposal stays in line with the spirit of this project while giving some more control for advanced users.

Recently I've been looking into the expected and actual behaviour of these packages with regard to spoofing, especially in the context where we build and implement projects without having much control over the infrastructure where it's deployed and being able to ship with "safe" defaults as much as possible. Please keep this context in mind while I describe the feature.

Currently, if you specify proxy_count and this does not match the amount of ip addresses in the header value, then that header value is skipped from processing, ultimately leading to the detected IP being None.

E.g. with a Kubernetes ingress (reverse proxy) in front of an nginx reverse proxy in front of the django wsgi server, you have two reverse proxies in total (and a value of proxy_count=1). Given a client spoofing X-Forwarded-For: 1.2.3.4, the list of IP addresses seen in the request headers then turns up as [1.2.3.4, real-ip, ingress-ip] for X-Forwarded-For (with Remote-Addr being the nginx proxy ip). Because of the proxy_count mismatch (1 != 3 -1), the resulting IP address down the road is None. However, we could have grabbed ips[-2] based on proxy_count - ips[-proxy_count - 1] (for left-most ordering), which would have yielded the correct IP address in this situation.

This is also in line with how django-rest-framework treats NUM_PROXIES: https://github.com/encode/django-rest-framework/blob/master/rest_framework/throttling.py#L37

There are probably a number of edge with left-most/right-most ordering and peculiarities, but would you be open to the concept?

Edit - via the django-axes issue I also stumbled on #33, I think that makes it clear enough that the trusted proxies settings should be used instead.

Document Django middleware

I see in #36 that a middleware has been implemented, could this be documented? It's very useful but I didn't see it anywhere in the README.

Suggestion - define default settings value

In

IPWARE_META_PRECEDENCE_ORDER = getattr(settings,
, django-ipware defines a local constant which leaves it unclear to Django users what the default setting is without reading the code.

django-cas-ng has a different approach, but I would move django-cas-ng's settings to the apps module.

Suggesting similar so that settings.IPWARE_META_PRECEDENCE_ORDER will be defined with default value if it is not set when app is readied by Django.

proxy_count and proxy_trusted_ips returning None on localhost/ngrok

I am testing on localhost with ngrok on multiple devices and I am having problems whenever I add either proxy_count or proxy_trusted_ips to get_client_ip(). get_client_ip(request) works well on localhost on its own and gets the IP address of each device using the ngrok link. I am not using nginx or apache to test.

Example:

ip = get_client_ip(request, proxy_count=1, proxy_trusted_ips=['177.139.233.133']
print(ip)
None

It prints None each time.

I made a stackoverflow question regarding this. But I have narrowed it down to just adding either proxy_count or proxy_trusted_ips values to get_client_ip() on localhost and ngrok. You can see all the trouble it causes whenever it returns None as the ip variable.

https://stackoverflow.com/questions/67758059/django-ipware-environment-variable-for-proxy-trusted-ips

I want to be able to test this on localhost before going to production even if it is dummy proxy_trusted_ips or proxy_count values such as 127.0.0.1 or the ngrok device ip addresses produced by my devices. I want to see that it is working otherwise it does not give me much confidence about using this library in production.

This is an extension of issue 39 which did not solve my problem and there is not enough info in the docs or on stackoverflow regarding this.

Another question I have is the proxy_trusted_ips just acts as IP addresses that you want to ignore? In other words, do not capture them / print them if their IP address is picked up and perhaps set the value as None since it gets ignored?

Is there a way to always get ipv4?

For my use case, I need the ipv4, but django-ipware keeps giving me ipv6. Is there any way to get ipv4 only?

Thank you.

edit: I figured my question doesn't make sense. I had to disable ipv6 in my website.

'NoneType' object has no attribute 'META'

I am trying to use the request object in a forms.py file. Yes, this is out of the ordinary but for my use case I need it.

These are some examples I am following:

https://stackoverflow.com/questions/2683689/django-access-request-object-from-admins-form-clean

https://stackoverflow.com/questions/1057252/how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clean-met

This problem seems to derive from:
self.request = kwargs.pop('request', None)

and using

get_client_ip(self.request) or get_client_ip(request)

Trying not to override the view of the third party app I am using.

Traceback (most recent call last):
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/allauth/account/views.py", line 101, in post
    if form.is_valid():
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/forms/forms.py", line 175, in is_valid
    return self.is_bound and not self.errors
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/forms/forms.py", line 170, in errors
    self.full_clean()
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/forms/forms.py", line 373, in full_clean
    self._clean_form()
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/django/forms/forms.py", line 400, in _clean_form
    cleaned_data = self.clean()
  File "/home/name/video-membership-master/courses/forms.py", line 624, in clean
    ip, is_routable = get_client_ip(self.request)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/ipware/ip.py", line 25, in get_client_ip
    value = util.get_request_meta(request, key)
  File "/home/name/.local/share/virtualenvs/project_name/lib/python3.8/site-packages/ipware/utils.py", line 76, in get_request_meta
    value = request.META.get(key, request.META.get(key.replace('_', '-'), '')).strip()
AttributeError: 'NoneType' object has no attribute 'META'

Add middleware for easy usage

I'm aware that writing the middleware manually is just a few lines of code but it would be very useful to have a simple middleware available

Within views it's easy enough to simply import the methods but Django still needs patching to have INTERNAL_IPS work correctly

Potential bug in version 1.0.0?

I'm not sure if this is a bug or not, especially since I've not run into this but the one time, but I figured I'd submit and see if it's something you all want to look at. I received a 500 error from my server when trying to get_real_ip. The call stack is copied below.

File ".\sdcgis\context_processors.py", line 15, in login_context_processor
address = get_real_ip(request)
File "C:\Virtual\Django17\lib\site-packages\ipware\ip.py", line 36, in get_real_ip
return get_ip(request, real_ip_only=True, right_most_proxy=right_most_proxy)
File "C:\Virtual\Django17\lib\site-packages\ipware\ip.py", line 20, in get_ip
if ip_str and is_valid_ip(ip_str):
File "C:\Virtual\Django17\lib\site-packages\ipware\utils.py", line 37, in is_valid_ip
return is_valid_ipv4(ip_str) or is_valid_ipv6(ip_str)
File "C:\Virtual\Django17\lib\site-packages\ipware\utils.py", line 27, in is_valid_ipv6
socket.inet_pton(socket.AF_INET6, ip_str)
AttributeError: 'module' object has no attribute 'inet_pton'

Option to configure the number of proxies

Hi, I think there should be an option to configure the number of trusted proxies. The django-axes module switched over to this module and previously had configuration in place to allow configuring proxy count as implemented in django-axes #225 and I think it would be a nifty feature here as well.

I understand that this package seems to aim more on efficiency and usability than security and filtering out noise and that resolving IPs is a best-effort case, but adding a simple check for number of proxies shouldn't be too hard, if it is deemed useful.

[Here is the some ongoing discussion about this] (jazzband/django-axes#286) for reference.

Unable to change settings in tests

In the module defaults in IPWARE_META_PRECEDENCE_ORDER HTTP_X_FORWARDED_FOR precedes REMOTE_ADDR. If I change the order then I should get the value of REMOTE_ADDR in the following test which is not the case.

class IPv4TestCase(TestCase):
    @override_settings(IPWARE_META_PRECEDENCE_ORDER=('REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR'))
    def test_setting_override(self):
        request = HttpRequest()
        request.META = {
            'HTTP_X_FORWARDED_FOR': '177.139.233.139, 198.84.193.157, 198.84.193.158',
            'REMOTE_ADDR': '177.139.233.133',
        }
        result = get_client_ip(request)
        self.assertEqual(result, ("177.139.233.133", True))

PR #43 fixes this.

Non-public IPs don't follow precendence.

In ip.get_ip(), if real_ip_only is False and no public IPs are found, then the function will return the LAST non-public IP it finds, rather than the first.

Is this the intended behavior?

get_ip() fails if any meta field from IPWARE_META_PRECEDENCE_LIST is None

Hi there,

if any of the META keys in IPWARE_META_PRECEDENCE_LIST happens to be undefined/None, get_ip() fails.

Traceback:
File "/Users/patrick/.virtualenvs/ib-cms/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/patrick/.virtualenvs/ib-cms/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  58.         return view_func(*args, **kwargs)
File "/Users/patrick/Dev/ib-main/cms/views.py" in fallback_upload_view
  192.                 queue.client_ip = get_ip(request)
File "/Users/patrick/.virtualenvs/ib-cms/lib/python2.7/site-packages/ipware/ip.py" in get_ip
  14.         value = request.META.get(key, '').strip()

Will send a pull request...

IPv6 address routability

All (other than specifically fc00:) IPv6 ULA addresses are incorrectly identified as globally routable. Not really a big issue, but an annoyance regardless.

I would suggest adding the use of ipaddress or similar library for checking the actual addresses. They would have to be written in proper CIDR notation, i.e. fc00::/7 and checked with the library's functions. As an added benefit, the list of IPs in the defaults.py would be somewhat shorter.

I'd be happy to submit a patch if you think this is an idea you'd consider.

get_trusted_ip fails with right_most_proxy

get_trusted_ip fails with right_most_proxy=True, for example:

get_trusted_ip(request, right_most_proxy=True, trusted_proxies=['177.139.233.139'])
Traceback (most recent call last):
File "django-ipware/ipware/ip.py", line 57, in get_trusted_ip
    if proxy in ips[-1]:
TypeError: 'list_reverseiterator' object is not subscriptable

Cannot run pytest tests after installing django-ipware

Hi, after installing django-ipware package I cannot run pytest tests, here is a stacktrace of the error

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/_pytest/main.py", line 187, in wrap_session
INTERNALERROR> config._do_configure()
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/_pytest/config/init.py", line 820, in _do_configure
INTERNALERROR> self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/hooks.py", line 308, in call_historic
INTERNALERROR> res = self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/manager.py", line 87, in
INTERNALERROR> firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR> return outcome.get_result()
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pytest_sugar.py", line 176, in pytest_configure
INTERNALERROR> sugar_reporter = SugarTerminalReporter(standard_reporter)
INTERNALERROR> File "/usr/local/lib/python3.7/site-packages/pytest_sugar.py", line 214, in init
INTERNALERROR> self.writer = self._tw
INTERNALERROR> AttributeError: can't set attribute

Packages installed:
auth0-python==3.9.1 # via -r requirements.in
boto3==1.12.18 # via -r requirements.in
botocore==1.15.18 # via boto3, s3transfer
certifi==2019.11.28 # via requests
cffi==1.14.0 # via cryptography
chardet==3.0.4 # via requests
cryptography==2.8 # via -r requirements.in, pyjwt
django-admin-display==1.1.0 # via -r requirements.in
django-constance[database]==2.6.0 # via -r requirements.in
django-cors-headers==3.2.1 # via -r requirements.in
django-filter==2.2.0 # via -r requirements.in
django-grappelli==2.14.1 # via -r requirements.in
django-mysql==3.3.0 # via -r requirements.in
django-picklefield==2.1.1 # via django-constance
django-storages==1.9.1 # via -r requirements.in
django==2.2.7 # via -r requirements.in, django-cors-headers, django-filter, django-mysql, django-picklefield, django-storages
djangorestframework==3.10.3 # via -r requirements.in
docutils==0.15.2 # via botocore
faker==4.0.1 # via -r requirements.in
gunicorn==20.0.4 # via -r requirements.in
idna==2.9 # via requests
jmespath==0.9.5 # via boto3, botocore
mysqlclient==1.4.6 # via -r requirements.in
phonenumbers==8.11.5 # via -r requirements.in
pillow==7.0.0 # via -r requirements.in
pycountry==19.8.18 # via -r requirements.in
pycparser==2.20 # via cffi
pyjwt[crypto]==1.7.1 # via -r requirements.in
python-dateutil==2.8.1 # via botocore, faker
pytz==2019.3 # via django
redis==3.4.1 # via -r requirements.in
requests==2.23.0 # via auth0-python
s3transfer==0.3.3 # via boto3
six==1.14.0 # via cryptography, django-grappelli, python-dateutil
sqlparse==0.3.1 # via django
text-unidecode==1.3 # via faker
urllib3==1.25.8 # via botocore, requests
whitenoise==5.0.1 # via -r requirements.in

Django 2.0 and Python 3.7 support

Hi @un33k,
First of all thanks for this package. Are there any plans to support Django 2.0?
I know everybody is very busy, so just a yes/no would be sufficient.
Best regards!

Source IP Port

Hello,
How could we get the source IP Port as well? Is there an option for it?

[install] installation error with setuptools

When runnning python setup.py develop, I am getting the following error:

  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 154, in save_modules
    yield saved
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 195, in setup_context
    yield
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 250, in run_setup
    _execfile(setup_script, ns)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 45, in _execfile
    exec(code, globals, locals)
  File "/tmp/easy_install-jvg8elii/django-ipware-3.0.0/setup.py", line 85, in <module>
  File "/usr/local/lib/python3.6/site-packages/setuptools/__init__.py", line 165, in setup
    return distutils.core.setup(**attrs)
  File "/usr/local/lib/python3.6/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/local/lib/python3.6/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 222, in run
    os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 275, in zip_safe
    return analyze_egg(self.bdist_dir, self.stubs)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 391, in analyze_egg
    safe = scan_module(egg_dir, base, name, stubs) and safe
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 430, in scan_module
    code = marshal.load(f)
ValueError: bad marshal data (digit out of range in long)


Traceback (most recent call last):
  File "setup.py", line 67, in <module>
    'Programming Language :: Python :: 3',
  File "/usr/local/lib/python3.6/site-packages/setuptools/__init__.py", line 165, in setup
    return distutils.core.setup(**attrs)
  File "/usr/local/lib/python3.6/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/local/lib/python3.6/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/develop.py", line 38, in run
    self.install_for_development()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/develop.py", line 155, in install_for_development
    self.process_distribution(None, self.dist, not self.no_deps)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 759, in process_distribution
    [requirement], self.local_index, self.easy_install
  File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 781, in resolve
    replace_conflicting=replace_conflicting
  File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1064, in best_match
    return self.obtain(req, installer)
  File "/usr/local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1076, in obtain
    return installer(requirement)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 686, in easy_install
    return self.install_item(spec, dist.location, tmpdir, deps)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 712, in install_item
    dists = self.install_eggs(spec, download, tmpdir)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 897, in install_eggs
    return self.build_and_install(setup_script, setup_base)
    dists = self.install_eggs(spec, download, tmpdir)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 897, in install_eggs
    return self.build_and_install(setup_script, setup_base)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 1167, in build_and_install
    self.run_setup(setup_script, setup_base, args)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/easy_install.py", line 1151, in run_setup
    run_setup(setup_script, args)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 253, in run_setup
    raise
  File "/usr/local/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 195, in setup_context
    yield
  File "/usr/local/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 166, in save_modules
    saved_exc.resume()
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 141, in resume
    six.reraise(type, exc, self._tb)
  File "/usr/local/lib/python3.6/site-packages/setuptools/_vendor/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 154, in save_modules
    yield saved
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 195, in setup_context
    yield
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 250, in run_setup
    _execfile(setup_script, ns)
  File "/usr/local/lib/python3.6/site-packages/setuptools/sandbox.py", line 45, in _execfile
    exec(code, globals, locals)
  File "/tmp/easy_install-jvg8elii/django-ipware-3.0.0/setup.py", line 85, in <module>
  File "/usr/local/lib/python3.6/site-packages/setuptools/__init__.py", line 165, in setup
    return distutils.core.setup(**attrs)
  File "/usr/local/lib/python3.6/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/local/lib/python3.6/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 222, in run
    os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 275, in zip_safe
    return analyze_egg(self.bdist_dir, self.stubs)
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 391, in analyze_egg
    safe = scan_module(egg_dir, base, name, stubs) and safe
  File "/usr/local/lib/python3.6/site-packages/setuptools/command/bdist_egg.py", line 430, in scan_module
    code = marshal.load(f)
ValueError: bad marshal data (digit out of range in long)

However, after adding pip install django-ipware, it works perfectly.

Check logs: https://travis-ci.org/github/openwisp/openwisp-radius/jobs/715471028
(after adding pip install django-ipware before python setup.py develop, it works perfectly): https://travis-ci.org/github/openwisp/openwisp-radius/jobs/715559387

Travis is running Python 3.6.10, I am able to reproduce it on 3.6.11 and it doesn't happen in 3.7

Improve test runner configuration

I have several ideas how we can improve our test configs:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.