2323 validate_x_trusted_proxy_header ,
2424)
2525from django .conf import settings
26- from django .core .cache import cache
2726from django .core .exceptions import ValidationError
2827from django .db import transaction
2928from django .db .models import F
3534from rest_framework .permissions import AllowAny
3635from rest_framework .response import Response
3736
37+ from aap_eda .api .blacklist import BlacklistManager
3838from aap_eda .api .event_stream_authentication import (
3939 BasicAuthentication ,
4040 EcdsaAuthentication ,
5050from aap_eda .core .utils .credentials import get_resolved_secrets
5151from aap_eda .services .pg_notify import PGNotify
5252
53- FAILURE_THRESHOLD = 5
54- FAILURE_WINDOW = 60 # seconds
5553logger = logging .getLogger (__name__ )
5654UNSAFE_HEADER_KEYS = {"X-Trusted-Proxy" , "X-Forwarded-For" , "X-Real-IP" }
5755REDACTED_STRING = "********"
56+ blacklist_manager = BlacklistManager ()
5857
5958
6059class ExternalEventStreamViewSet (viewsets .GenericViewSet ):
@@ -286,41 +285,32 @@ def _handle_auth(self, request, inputs):
286285 raise
287286
288287 def _get_client_ip (self , request ):
288+ """Return the client IP from the request.
289+
290+ Uses X-Forwarded-For when trusted proxy validation is enabled,
291+ otherwise falls back to REMOTE_ADDR.
292+ """
289293 if settings .EVENT_STREAM_REQUIRE_TRUSTED_PROXY :
290294 x_forwarded_for = request .META .get ("HTTP_X_FORWARDED_FOR" )
291295 if x_forwarded_for :
292296 return x_forwarded_for .split ("," )[0 ].strip ()
293297 return request .META .get ("REMOTE_ADDR" )
294298
295- def _check_rate_limit (self , request , event_stream_uuid ):
296- key = (
297- f"es_auth_fail: { event_stream_uuid } : "
298- f"{ self ._get_client_ip (request )} "
299- )
300- failures = cache .get (key , 0 )
301- if failures >= FAILURE_THRESHOLD :
302- raise AuthenticationFailed ("Too many failed attempts" )
303-
304- def _record_failure (self , request , event_stream_uuid ):
305- key = (
306- f"es_auth_fail: { event_stream_uuid } : "
307- f"{ self ._get_client_ip (request )} "
308- )
309- failures = cache .get (key , 0 )
310- cache .set (key , failures + 1 , FAILURE_WINDOW )
311-
312299 @extend_schema (exclude = True )
313300 @action (detail = True , methods = ["POST" ], rbac_action = None )
314301 def post (self , request , * _args , ** kwargs ):
315302 """Handle posts from external vendors."""
303+ client_ip = self ._get_client_ip (request )
304+ blacklist_manager .check_blacklist (client_ip )
305+
316306 try :
317307 self .event_stream = EventStream .objects .get (uuid = kwargs ["pk" ])
318308 except (EventStream .DoesNotExist , ValidationError ) as exc :
309+ blacklist_manager .record_invalid_uuid (client_ip )
319310 raise ParseError ("bad uuid specified" ) from exc
320311
321312 # Validate X-Trusted-Proxy header from Gateway/Envoy
322313 self ._validate_trusted_proxy_header (request )
323- self ._check_rate_limit (request , kwargs ["pk" ])
324314
325315 try :
326316 inputs = get_resolved_secrets (self .event_stream .eda_credential )
@@ -341,10 +331,11 @@ def post(self, request, *_args, **kwargs):
341331 headers = yaml .dump (event_headers ),
342332 )
343333 raise ParseError (message )
334+
344335 try :
345336 self ._handle_auth (request , inputs )
346337 except AuthenticationFailed :
347- self . _record_failure ( request , kwargs [ "pk" ] )
338+ blacklist_manager . record_auth_failure ( client_ip )
348339 raise
349340
350341 body = self ._parse_body (
0 commit comments