diff --git a/authentik/lib/utils/http.py b/authentik/lib/utils/http.py index 35e5a32a6..a06293327 100644 --- a/authentik/lib/utils/http.py +++ b/authentik/lib/utils/http.py @@ -3,6 +3,9 @@ from typing import Any, Optional from django.http import HttpRequest +OUTPOST_REMOTE_IP_HEADER = "HTTP_X_AUTHENTIK_REMOTE_IP" +USER_ATTRIBUTE_CAN_OVERRIDE_IP = "goauthentik.io/user/override-ips" + def _get_client_ip_from_meta(meta: dict[str, Any]) -> Optional[str]: """Attempt to get the client's IP by checking common HTTP Headers. @@ -18,9 +21,27 @@ def _get_client_ip_from_meta(meta: dict[str, Any]) -> Optional[str]: return None +def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]: + """Get the actual remote IP when set by an outpost. Only + allowed when the request is authenticated, by a user with USER_ATTRIBUTE_CAN_OVERRIDE_IP set + to outpost""" + if not hasattr(request, "user"): + return None + if not request.user.is_authenticated: + return None + if OUTPOST_REMOTE_IP_HEADER not in request.META: + return None + if request.user.attributes.get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False): + return None + return request.META[OUTPOST_REMOTE_IP_HEADER] + + def get_client_ip(request: Optional[HttpRequest]) -> Optional[str]: """Attempt to get the client's IP by checking common HTTP Headers. Returns none if no IP Could be found""" if request: + override = _get_outpost_override_ip(request) + if override: + return override return _get_client_ip_from_meta(request.META) return None diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index d669d66e9..9cc3e087b 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -32,6 +32,7 @@ from authentik.crypto.models import CertificateKeyPair from authentik.lib.config import CONFIG from authentik.lib.models import InheritanceForeignKey from authentik.lib.sentry import SentryIgnoredException +from authentik.lib.utils.http import USER_ATTRIBUTE_CAN_OVERRIDE_IP from authentik.outposts.docker_tls import DockerInlineTLS OUR_VERSION = parse(__version__) @@ -330,6 +331,7 @@ class Outpost(models.Model): if not users.exists(): user: User = User.objects.create(username=self.user_identifier) user.attributes[USER_ATTRIBUTE_SA] = True + user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True user.set_unusable_password() user.save() else: diff --git a/web/src/pages/events/EventListPage.ts b/web/src/pages/events/EventListPage.ts index 32bac4d25..c4165dcca 100644 --- a/web/src/pages/events/EventListPage.ts +++ b/web/src/pages/events/EventListPage.ts @@ -70,7 +70,7 @@ export class EventListPage extends TablePage { renderExpanded(item: Event): TemplateResult { return html` - +