diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 2a974ecf3b..5d598a5921 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -425,6 +425,18 @@ use = egg:swift#proxy_logging # access_log_statsd_metric_prefix = # access_log_headers = false # +# By default, the X-Auth-Token is logged. To obscure the value, +# set reveal_sensitive_prefix to the number of characters to log. +# For example, if set to 12, only the first 12 characters of the +# token appear in the log. An unauthorized access of the log file +# won't allow unauthorized usage of the token. However, the first +# 12 or so characters is unique enough that you can trace/debug +# token usage. Set to 0 to suppress the token completely (replaced +# by '...' in the log). +# Note: reveal_sensitive_prefix will not affect the value +# logged with access_log_headers=True. +# reveal_sensitive_prefix = 8192 +# # What HTTP methods are allowed for StatsD logging (comma-sep); request methods # not in this list will have "BAD_METHOD" for the portion of the metric. # log_statsd_valid_http_methods = GET,HEAD,POST,PUT,DELETE,COPY,OPTIONS diff --git a/swift/common/middleware/proxy_logging.py b/swift/common/middleware/proxy_logging.py index 081188a98b..b40ef36ac5 100644 --- a/swift/common/middleware/proxy_logging.py +++ b/swift/common/middleware/proxy_logging.py @@ -77,6 +77,7 @@ from swift.common.swob import Request from swift.common.utils import (get_logger, get_remote_client, get_valid_utf8_str, config_true_value, InputProxy) +from swift.common.constraints import MAX_HEADER_SIZE QUOTE_SAFE = '/:' @@ -112,6 +113,8 @@ class ProxyLoggingMiddleware(object): self.access_logger = get_logger(access_log_conf, log_route='proxy-access') self.access_logger.set_statsd_prefix('proxy-server') + self.reveal_sensitive_prefix = int(conf.get('reveal_sensitive_prefix', + MAX_HEADER_SIZE)) def method_from_req(self, req): return req.environ.get('swift.orig_req_method', req.method) @@ -122,6 +125,13 @@ class ProxyLoggingMiddleware(object): def mark_req_logged(self, req): req.environ['swift.proxy_access_log_made'] = True + def obscure_sensitive(self, value): + if not value: + return '-' + if len(value) > self.reveal_sensitive_prefix: + return value[:self.reveal_sensitive_prefix] + '...' + return value + def log_request(self, req, status_int, bytes_received, bytes_sent, request_time): """ @@ -156,7 +166,7 @@ class ProxyLoggingMiddleware(object): status_int, req.referer, req.user_agent, - req.headers.get('x-auth-token'), + self.obscure_sensitive(req.headers.get('x-auth-token')), bytes_received, bytes_sent, req.headers.get('etag', None), diff --git a/test/unit/common/middleware/test_proxy_logging.py b/test/unit/common/middleware/test_proxy_logging.py index 4a08b68b73..0986566d18 100644 --- a/test/unit/common/middleware/test_proxy_logging.py +++ b/test/unit/common/middleware/test_proxy_logging.py @@ -571,6 +571,77 @@ class TestProxyLogging(unittest.TestCase): log_parts = self._log_parts(app) self.assertEquals(log_parts[17], 'one%2Cand%20two') + def test_log_auth_token(self): + auth_token = 'b05bf940-0464-4c0e-8c70-87717d2d73e8' + + # Default - no reveal_sensitive_prefix in config + # No x-auth-token header + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], '-') + # Has x-auth-token header + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET', + 'HTTP_X_AUTH_TOKEN': auth_token}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], auth_token) + + # Truncate to first 8 characters + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), { + 'reveal_sensitive_prefix': '8'}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], '-') + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), { + 'reveal_sensitive_prefix': '8'}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET', + 'HTTP_X_AUTH_TOKEN': auth_token}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], 'b05bf940...') + + # Token length and reveal_sensitive_prefix are same (no truncate) + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), { + 'reveal_sensitive_prefix': str(len(auth_token))}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET', + 'HTTP_X_AUTH_TOKEN': auth_token}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], auth_token) + + # Don't log x-auth-token + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), { + 'reveal_sensitive_prefix': '0'}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], '-') + app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), { + 'reveal_sensitive_prefix': '0'}) + app.access_logger = FakeLogger() + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET', + 'HTTP_X_AUTH_TOKEN': auth_token}) + resp = app(req.environ, start_response) + resp_body = ''.join(resp) + log_parts = self._log_parts(app) + self.assertEquals(log_parts[9], '...') + if __name__ == '__main__': unittest.main()