From 988e86c45b01d25e29409d0f404df11429a78921 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 7 Dec 2023 05:24:40 +0530 Subject: [PATCH 1/3] fixes client ip detection for v6 --- netbox/utilities/request.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/netbox/utilities/request.py b/netbox/utilities/request.py index 0f8ee9caee2..63ba788f8b4 100644 --- a/netbox/utilities/request.py +++ b/netbox/utilities/request.py @@ -1,4 +1,4 @@ -from netaddr import IPAddress +from netaddr import AddrFormatError, IPAddress __all__ = ( 'get_client_ip', @@ -17,10 +17,15 @@ def get_client_ip(request, additional_headers=()): ) for header in HTTP_HEADERS: if header in request.META: - client_ip = request.META[header].split(',')[0].partition(':')[0] + ip = request.META[header].split(',')[0].strip() + # Check if the IP address is v6 or v4 + if ip.count(':') > 1: + client_ip = ip + else: + client_ip = ip.partition(':')[0] try: return IPAddress(client_ip) - except ValueError: + except (AddrFormatError, ValueError): raise ValueError(f"Invalid IP address set for {header}: {client_ip}") # Could not determine the client IP address from request headers From 904e31b4c58cb652de160486de419c56991c1065 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 7 Dec 2023 05:24:52 +0530 Subject: [PATCH 2/3] adds test for get_client_ip --- netbox/utilities/tests/test_request.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 netbox/utilities/tests/test_request.py diff --git a/netbox/utilities/tests/test_request.py b/netbox/utilities/tests/test_request.py new file mode 100644 index 00000000000..37b1e77f527 --- /dev/null +++ b/netbox/utilities/tests/test_request.py @@ -0,0 +1,25 @@ +from django.test import TestCase, RequestFactory + +from netaddr import IPAddress +from utilities.request import get_client_ip + + +class GetClientIPTests(TestCase): + def setUp(self): + self.factory = RequestFactory() + + def test_ipv4_address(self): + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='192.168.1.1') + self.assertEqual(get_client_ip(request), IPAddress('192.168.1.1')) + + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='192.168.1.1:8080') + self.assertEqual(get_client_ip(request), IPAddress('192.168.1.1')) + + def test_ipv6_address(self): + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='2001:db8::8a2e:370:7334') + self.assertEqual(get_client_ip(request), IPAddress('2001:db8::8a2e:370:7334')) + + def test_invalid_ip_address(self): + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='invalid_ip') + with self.assertRaises(ValueError): + get_client_ip(request) From 7828008ff9a0326300c61c3802bb6826e8d31481 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Dec 2023 09:13:56 -0500 Subject: [PATCH 3/3] Employ urlparse() to strip port numbers from IPs --- netbox/utilities/request.py | 19 +++++++++++-------- netbox/utilities/tests/test_request.py | 5 ++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/netbox/utilities/request.py b/netbox/utilities/request.py index 63ba788f8b4..a5ca145e9b7 100644 --- a/netbox/utilities/request.py +++ b/netbox/utilities/request.py @@ -1,4 +1,5 @@ from netaddr import AddrFormatError, IPAddress +from urllib.parse import urlparse __all__ = ( 'get_client_ip', @@ -18,15 +19,17 @@ def get_client_ip(request, additional_headers=()): for header in HTTP_HEADERS: if header in request.META: ip = request.META[header].split(',')[0].strip() - # Check if the IP address is v6 or v4 - if ip.count(':') > 1: - client_ip = ip - else: - client_ip = ip.partition(':')[0] try: - return IPAddress(client_ip) - except (AddrFormatError, ValueError): - raise ValueError(f"Invalid IP address set for {header}: {client_ip}") + return IPAddress(ip) + except AddrFormatError: + # Parse the string with urlparse() to remove port number or any other cruft + ip = urlparse(f'//{ip}').hostname + + try: + return IPAddress(ip) + except AddrFormatError: + # We did our best + raise ValueError(f"Invalid IP address set for {header}: {ip}") # Could not determine the client IP address from request headers return None diff --git a/netbox/utilities/tests/test_request.py b/netbox/utilities/tests/test_request.py index 37b1e77f527..69f67732331 100644 --- a/netbox/utilities/tests/test_request.py +++ b/netbox/utilities/tests/test_request.py @@ -11,13 +11,16 @@ def setUp(self): def test_ipv4_address(self): request = self.factory.get('/', HTTP_X_FORWARDED_FOR='192.168.1.1') self.assertEqual(get_client_ip(request), IPAddress('192.168.1.1')) - request = self.factory.get('/', HTTP_X_FORWARDED_FOR='192.168.1.1:8080') self.assertEqual(get_client_ip(request), IPAddress('192.168.1.1')) def test_ipv6_address(self): request = self.factory.get('/', HTTP_X_FORWARDED_FOR='2001:db8::8a2e:370:7334') self.assertEqual(get_client_ip(request), IPAddress('2001:db8::8a2e:370:7334')) + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='[2001:db8::8a2e:370:7334]') + self.assertEqual(get_client_ip(request), IPAddress('2001:db8::8a2e:370:7334')) + request = self.factory.get('/', HTTP_X_FORWARDED_FOR='[2001:db8::8a2e:370:7334]:8080') + self.assertEqual(get_client_ip(request), IPAddress('2001:db8::8a2e:370:7334')) def test_invalid_ip_address(self): request = self.factory.get('/', HTTP_X_FORWARDED_FOR='invalid_ip')