Repository: privacyidea/privacyidea

Issue:
Allow X-Fordwarded-For for all endpoints.
In get_client_ip the IP address is only proxied for the auth and validate requests.
https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/utils.py#L550
This makes sense if the client IP address is passed with the client parameter - like the RADIUS plugin does.

We should consider using the proxy/X-Forward-For for all endpoints and only restrict the client parameter to the auth/ and validate/ endpoints.





Discussion:

Resolving patch:
diff --git a/privacyidea/lib/utils.py b/privacyidea/lib/utils.py
--- a/privacyidea/lib/utils.py
+++ b/privacyidea/lib/utils.py
@@ -576,21 +576,25 @@ def get_client_ip(request, proxy_settings):
     :return:
     """
     client_ip = request.remote_addr
-    # We only do the mapping for authentication requests!
+
+    # Set the possible mapped IP to X-Forwarded-For
+    mapped_ip = request.access_route[0] if request.access_route else None
+
+    # We only do the client-param mapping for authentication requests!
     if not hasattr(request, "blueprint") or \
                     request.blueprint in ["validate_blueprint", "ttype_blueprint",
                                           "jwtauth"]:
         # The "client" parameter should overrule a possible X-Forwarded-For
-        mapped_ip = request.all_data.get("client") or \
-                    (request.access_route[0] if request.access_route else None)
-        if mapped_ip:
-            if proxy_settings and check_proxy(client_ip, mapped_ip,
-                                              proxy_settings):
-                client_ip = mapped_ip
-            elif mapped_ip != client_ip:
-                log.warning("Proxy {client_ip} not allowed to set IP to "
-                            "{mapped_ip}.".format(client_ip=client_ip,
-                                                  mapped_ip=mapped_ip))
+        mapped_ip = request.all_data.get("client") or mapped_ip
+
+    if mapped_ip:
+        if proxy_settings and check_proxy(client_ip, mapped_ip,
+                                          proxy_settings):
+            client_ip = mapped_ip
+        elif mapped_ip != client_ip:
+            log.warning("Proxy {client_ip} not allowed to set IP to "
+                        "{mapped_ip}.".format(client_ip=client_ip,
+                                              mapped_ip=mapped_ip))
 
     return client_ip
 


Test patch:
diff --git a/tests/test_lib_utils.py b/tests/test_lib_utils.py
--- a/tests/test_lib_utils.py
+++ b/tests/test_lib_utils.py
@@ -14,12 +14,12 @@
                                    parse_timedelta, to_unicode,
                                    parse_int, convert_column_to_unicode, censor_connect_string,
                                    truncate_comma_list, check_pin_policy,
-                                   get_module_class, decode_base32check)
+                                   get_module_class, decode_base32check,
+                                   get_client_ip)
 from datetime import timedelta, datetime
 from netaddr import IPAddress, IPNetwork, AddrFormatError
 from dateutil.tz import tzlocal, tzoffset
 from privacyidea.lib.tokenclass import DATE_FORMAT
-import hashlib
 
 
 class UtilsTestCase(MyTestCase):
@@ -506,3 +506,39 @@ def test_22_decodebase32check(self):
         # change the client component in between
         client_component = "TIXQW4ydvn2aos4cj6ba"
         self.assertRaises(Exception, decode_base32check, client_component)
+
+    def test_23_get_client_ip(self):
+
+        class RequestMock():
+            blueprint = None
+            remote_addr = None
+            all_data = {}
+            access_route = []
+
+        r = RequestMock()
+        r.blueprint = "token_blueprint"
+        # The real client
+        direct_client = "10.0.0.1"
+        r.remote_addr = direct_client
+        # The client parameter
+        client_parameter = "192.168.2.1"
+        r.all_data = {"client": client_parameter}
+        # The X-Forwarded-For
+        client_proxy = "172.16.1.2"
+        r.access_route = [client_proxy]
+
+        proxy_settings = ""
+        ip = get_client_ip(r, proxy_settings)
+        self.assertEqual(ip, direct_client)
+
+        # If there is a proxy_setting, the X-Forwarded-For will
+        # work, but not the client_parameter
+        proxy_settings = direct_client
+        ip = get_client_ip(r, proxy_settings)
+        self.assertEqual(ip, client_proxy)
+
+        # If the request is a validate request, the
+        # client_parameter will overrule the X-Forwarded-For
+        r.blueprint = "validate_blueprint"
+        ip = get_client_ip(r, proxy_settings)
+        self.assertEqual(ip, client_parameter)