diff --git a/assets b/assets index 1446fc93b..266f8574b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1446fc93bf93a0404944b3c6c7d480da6877357e +Subproject commit 266f8574bea1672d030ca921a8facf323e693d3a diff --git a/deploy/helm/ifrcgo-helm/templates/api/ingress.yaml b/deploy/helm/ifrcgo-helm/templates/api/ingress.yaml index 99f23f4b7..2d9af8f81 100644 --- a/deploy/helm/ifrcgo-helm/templates/api/ingress.yaml +++ b/deploy/helm/ifrcgo-helm/templates/api/ingress.yaml @@ -8,6 +8,9 @@ metadata: nginx.ingress.kubernetes.io/proxy-connect-timeout: "360s" nginx.ingress.kubernetes.io/proxy-send-timeout: "360s" nginx.ingress.kubernetes.io/proxy-read-timeout: "360s" + nginx.ingress.kubernetes.io/configuration-snippet: | + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; spec: ingressClassName: nginx @@ -57,6 +60,9 @@ metadata: annotations: nginx.ingress.kubernetes.io/permanent-redirect: "https://www.chromatic.com/library?appId=66557be6b68dacbf0a96db23&branch=develop" nginx.ingress.kubernetes.io/permanent-redirect-code: "301" + nginx.ingress.kubernetes.io/configuration-snippet: | + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; spec: ingressClassName: nginx diff --git a/deploy/helm/ifrcgo-helm/values.yaml b/deploy/helm/ifrcgo-helm/values.yaml index bbe7bbd1d..72d7f5f5f 100644 --- a/deploy/helm/ifrcgo-helm/values.yaml +++ b/deploy/helm/ifrcgo-helm/values.yaml @@ -46,6 +46,7 @@ env: SENTRY_DSN: '' SENTRY_SAMPLE_RATE: '' DJANGO_READ_ONLY: '' + LOG_REQUEST_IP: false AUTO_TRANSLATION_TRANSLATOR: '' IFRC_TRANSLATION_DOMAIN: '' IFRC_TRANSLATION_HEADER_API_KEY: '' diff --git a/main/settings.py b/main/settings.py index d2e2daf1a..9878ebcc6 100644 --- a/main/settings.py +++ b/main/settings.py @@ -121,6 +121,7 @@ DJANGO_READ_ONLY=(bool, False), # Misc DISABLE_API_CACHE=(bool, False), + LOG_REQUEST_IP=(bool, False), # jwt private and public key (NOTE: Used algorithm ES256) # FIXME: Deprecated configuration. Remove this and it references JWT_PRIVATE_KEY_BASE64_ENCODED=(str, None), @@ -152,6 +153,8 @@ POWERBI_DATASET_IDS=(str, None), ) +LOG_REQUEST_IP = True # temporary setting. For long term: env("LOG_REQUEST_IP") + # Requires uppercase variable https://docs.djangoproject.com/en/2.1/topics/settings/#creating-your-own-settings @@ -674,7 +677,7 @@ def log_render_extra_context(record): "console": { "()": "colorlog.ColoredFormatter", "format": ( - "%(log_color)s%(levelname)-8s%(red)s%(module)-8s%(reset)s %(asctime)s %(blue)s%(message)s %(context)s" + "%(log_color)s%(levelname)-8s%(red)s%(module)-11s%(reset)s %(asctime)s %(blue)s%(message)s %(context)s" ), }, }, diff --git a/middlewares/middlewares.py b/middlewares/middlewares.py index 623943248..692dd27a3 100644 --- a/middlewares/middlewares.py +++ b/middlewares/middlewares.py @@ -1,11 +1,25 @@ +import logging import threading +from django.conf import settings from django.http import HttpResponse # from reversion.middleware import RevisionMiddleware _threadlocal = threading.local() +logger = logging.getLogger("django") + + +def get_client_ip(request): + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") + if x_forwarded_for: + # X-Forwarded-For can be a list of IPs: client, proxy1, proxy2 + return x_forwarded_for.split(",")[0].strip() + x_real_ip = request.META.get("HTTP_X_REAL_IP") + if x_real_ip: + return x_real_ip.strip() + return request.META.get("REMOTE_ADDR") def get_signal_request(): @@ -45,10 +59,29 @@ def __call__(self, request): # workaround for safelink check: if request.method == "HEAD": + if settings.LOG_REQUEST_IP: + client_ip = get_client_ip(request) + logger.info( + " %s %s %s | %s", + request.method, + request.path, + 200, + client_ip, + ) return HttpResponse(status=200) setattr(_threadlocal, "request", request) - return self.get_response(request) + request.client_ip = get_client_ip(request) + response = self.get_response(request) + if settings.LOG_REQUEST_IP: + logger.info( + " %s %s %s | %s", + request.method, + request.path, + response.status_code, + request.client_ip, + ) + return response # Without this class the 'request revision' still works fine.