فهرست منبع

[middleware] Update audit log format and whitelist of operations

Logs specifically whitelisted useradmin operations to configured audit_event_log_dir.
Jenny Kim 10 سال پیش
والد
کامیت
92dab4d5c9
3فایلهای تغییر یافته به همراه96 افزوده شده و 17 حذف شده
  1. 5 0
      desktop/conf/pseudo-distributed.ini.tmpl
  2. 89 15
      desktop/core/src/desktop/middleware.py
  3. 2 2
      desktop/core/src/desktop/middleware_test.py

+ 5 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -133,6 +133,11 @@
   # Size in KB/MB/GB for audit log to rollover.
   ## audit_log_max_file_size=100MB
 
+  # How to handle audit failure
+  # Possible values are DROP, skip that event and allow the action we were trying to audit to continue
+  # and SHUTDOWN, which would cause Hue to stop entirely.
+  ## navigator_audit_queue_policy=DROP
+
   # A json file containing a list of log redaction rules for cleaning sensitive data
   # from log files. It is defined as:
   #

+ 89 - 15
desktop/core/src/desktop/middleware.py

@@ -23,6 +23,7 @@ import logging
 import os.path
 import re
 import tempfile
+import time
 
 import kerberos
 
@@ -46,7 +47,7 @@ import desktop.views
 import desktop.conf
 from desktop.context_processors import get_app_name
 from desktop.lib import apputil, i18n
-from desktop.lib.django_util import render, render_json, is_jframe_request
+from desktop.lib.django_util import render, render_json, is_jframe_request, get_username_re_rule, get_groupname_re_rule
 from desktop.lib.exceptions import StructuredException
 from desktop.lib.exceptions_renderable import PopupException
 from desktop.log.access import access_log, log_page_hit
@@ -341,32 +342,105 @@ class JsonMessage(object):
 
 class AuditLoggingMiddleware(object):
 
+  username_re = get_username_re_rule()
+  groupname_re = get_groupname_re_rule()
+
+  operations = {
+    '/accounts/login': 'USER_LOGIN',
+    '/accounts/logout': 'USER_LOGOUT',
+    '/useradmin/users/add_ldap_users': 'ADD_LDAP_USERS',
+    '/useradmin/users/add_ldap_groups': 'ADD_LDAP_GROUPS',
+    '/useradmin/users/sync_ldap_users_groups': 'SYNC_LDAP_USERS_GROUPS',
+    '/useradmin/users/new': 'CREATE_USER',
+    '/useradmin/groups/new': 'CREATE_GROUP',
+    '/useradmin/users/delete': 'DELETE_USER',
+    '/useradmin/groups/delete': 'DELETE_GROUP'
+  }
+
+  operation_patterns = {
+    '/useradmin/permissions/edit/(?P<app>.*)/(?P<priv>.*)': 'EDIT_PERMISSION',
+    '/useradmin/users/edit/(?P<username>%s)' % (username_re,): 'EDIT_USER',
+    '/useradmin/groups/edit/(?P<name>%s)' % (groupname_re,): 'EDIT_GROUP'
+  }
+
   def __init__(self):
-    from desktop.conf import AUDIT_EVENT_LOG_DIR
+    from desktop.conf import AUDIT_EVENT_LOG_DIR, SERVER_USER
+
+    self.impersonator = SERVER_USER.get()
 
     if not AUDIT_EVENT_LOG_DIR.get():
       LOG.info('Unloading AuditLoggingMiddleware')
       raise exceptions.MiddlewareNotUsed
 
+  def process_view(self, request, view_func, view_args, view_kwargs):
+    try:
+      operation = self._get_operation(request.path)
+      if operation == 'USER_LOGOUT':
+        self._log_message(operation, request)
+    except Exception, e:
+      LOG.error('Could not audit the request: %s' % e)
+
+    return None
+
   def process_response(self, request, response):
+    response['audited'] = False
     try:
-      audit_logger = get_audit_logger()
-      audit_logger.debug(JsonMessage(**{
-          datetime.utcnow().strftime('%s'): {
-            'user': request.user.username  if hasattr(request, 'user') else 'anonymous',
-            "status": response.status_code,
-            "impersonator": None,
-            "ip_address": request.META.get('REMOTE_ADDR'),
-            "authorization_failure": response.status_code == 401,
-            "service": get_app_name(request),
-            "url": request.path,
-          }
-      }))
-      response['audited'] = True
+      operation = self._get_operation(request.path)
+      if request.method == 'POST' and operation and operation != 'USER_LOGOUT':
+        self._log_message(operation, request, response)
+        response['audited'] = True
     except Exception, e:
       LOG.error('Could not audit the request: %s' % e)
     return response
 
+  def _log_message(self, operation, request, response=None):
+    audit_logger = get_audit_logger()
+
+    allowed = True
+    status = 200
+    if response is not None:
+      allowed = response.status_code != 401
+      status = response.status_code
+
+    audit_logger.debug(JsonMessage(**{
+      'username': self._get_username(request),
+      'impersonator': self.impersonator,
+      'ipAddress': self._get_client_ip(request),
+      'operation': operation,
+      'eventTime': self._milliseconds_since_epoch(),
+      'allowed': allowed,
+      'status': status,
+      'service': get_app_name(request),
+      'url': request.path
+    }))
+
+  def _get_client_ip(self, request):
+    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
+    if x_forwarded_for:
+        x_forwarded_for = x_forwarded_for.split(',')[0]
+    return request.META.get('HTTP_CLIENT_IP') or x_forwarded_for or request.META.get('REMOTE_ADDR')
+
+  def _get_username(self, request):
+    if hasattr(request, 'user') and not request.user.is_anonymous():
+      return request.user.get_username()
+    else:
+      return 'anonymous'
+
+  def _milliseconds_since_epoch(self):
+    return int(time.time() * 1000)
+
+  def _get_operation(self, path):
+    url = path.rstrip('/')
+
+    if url in AuditLoggingMiddleware.operations:
+      return AuditLoggingMiddleware.operations[url]
+    else:
+      for regex, operation in AuditLoggingMiddleware.operation_patterns.items():
+        pattern = re.compile(regex)
+        if pattern.match(url):
+          return operation
+
+    return None
 
 try:
   import tidylib

+ 2 - 2
desktop/core/src/desktop/middleware_test.py

@@ -83,7 +83,7 @@ def test_audit_logging_middleware_enable():
 
     try:
       # Check if we audit correctly
-      response = c.get("/beeswax/")
+      response = c.get("/useradmin/permissions/edit/beeswax/access")
       assert_true('audited' in response, response)
 
       audit = open(log_path).readlines()
@@ -91,7 +91,7 @@ def test_audit_logging_middleware_enable():
         audit_json = json.loads(line)
         audit_record = audit_json.values()[0]
         assert_equal('test_audit_logging', audit_record['user'], audit_record)
-        assert_equal('/beeswax/', audit_record['url'], audit_record)
+        assert_equal('/useradmin/permissions/edit/beeswax/access', audit_record['url'], audit_record)
 
     finally:
       settings.MIDDLEWARE_CLASSES.pop()