diff --git a/cinderclient/client.py b/cinderclient/client.py index 27171528a..e456cfe6d 100644 --- a/cinderclient/client.py +++ b/cinderclient/client.py @@ -21,6 +21,7 @@ OpenStack Client interface. Handles the REST calls and responses. from __future__ import print_function import glob +import hashlib import imp import itertools import logging @@ -39,6 +40,7 @@ from cinderclient import exceptions import cinderclient.extension from cinderclient.openstack.common import importutils from cinderclient.openstack.common.gettextutils import _ +from oslo_utils import encodeutils from oslo_utils import strutils osprofiler_web = importutils.try_import("osprofiler.web") @@ -145,6 +147,7 @@ class SessionClient(adapter.LegacyJsonAdapter): class HTTPClient(object): + SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',) USER_AGENT = 'python-cinderclient' def __init__(self, user, password, projectid, auth_url=None, @@ -198,6 +201,16 @@ class HTTPClient(object): self._logger = logging.getLogger(__name__) + def _safe_header(self, name, value): + if name in HTTPClient.SENSITIVE_HEADERS: + encoded = value.encode('utf-8') + hashed = hashlib.sha1(encoded) + digested = hashed.hexdigest() + return encodeutils.safe_decode(name), "{SHA1}%s" % digested + else: + return (encodeutils.safe_decode(name), + encodeutils.safe_decode(value)) + def http_log_req(self, args, kwargs): if not self.http_log_debug: return @@ -210,7 +223,8 @@ class HTTPClient(object): string_parts.append(' %s' % element) for element in kwargs['headers']: - header = ' -H "%s: %s"' % (element, kwargs['headers'][element]) + header = ("-H '%s: %s'" % + self._safe_header(element, kwargs['headers'][element])) string_parts.append(header) if 'data' in kwargs: diff --git a/cinderclient/openstack/common/apiclient/client.py b/cinderclient/openstack/common/apiclient/client.py index da2e17738..69b551b48 100644 --- a/cinderclient/openstack/common/apiclient/client.py +++ b/cinderclient/openstack/common/apiclient/client.py @@ -33,13 +33,16 @@ try: except ImportError: import json +import hashlib import requests from cinderclient.openstack.common.apiclient import exceptions from cinderclient.openstack.common import importutils +from oslo_utils import encodeutils _logger = logging.getLogger(__name__) +SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',) class HTTPClient(object): @@ -97,6 +100,16 @@ class HTTPClient(object): self.cached_token = None + def _safe_header(self, name, value): + if name in SENSITIVE_HEADERS: + encoded = value.encode('utf-8') + hashed = hashlib.sha1(encoded) + digested = hashed.hexdigest() + return encodeutils.safe_decode(name), "{SHA1}%s" % digested + else: + return (encodeutils.safe_decode(name), + encodeutils.safe_decode(value)) + def _http_log_req(self, method, url, kwargs): if not self.debug: return @@ -108,7 +121,8 @@ class HTTPClient(object): ] for element in kwargs['headers']: - header = "-H '%s: %s'" % (element, kwargs['headers'][element]) + header = ("-H '%s: %s'" % + self._safe_header(element, kwargs['headers'][element])) string_parts.append(header) _logger.debug("REQ: %s" % " ".join(string_parts)) diff --git a/cinderclient/tests/unit/test_client.py b/cinderclient/tests/unit/test_client.py index 8ac677a83..fbb15cc4a 100644 --- a/cinderclient/tests/unit/test_client.py +++ b/cinderclient/tests/unit/test_client.py @@ -186,3 +186,30 @@ class ClientTest(utils.TestCase): # AuthorizationFailure exception, check exceptions.from_response # is not getting called. self.assertFalse(mock_from_resp.called) + + +class ClientTestSensitiveInfo(utils.TestCase): + def test_req_does_not_log_sensitive_info(self): + self.logger = self.useFixture( + fixtures.FakeLogger( + format="%(message)s", + level=logging.DEBUG, + nuke_handlers=True + ) + ) + + secret_auth_token = "MY_SECRET_AUTH_TOKEN" + kwargs = { + 'headers': {"X-Auth-Token": secret_auth_token}, + 'data': ('{"auth": {"tenantName": "fakeService",' + ' "passwordCredentials": {"username": "fakeUser",' + ' "password": "fakePassword"}}}') + } + + cs = cinderclient.client.HTTPClient("user", None, None, + "http://127.0.0.1:5000") + cs.http_log_debug = True + cs.http_log_req('PUT', kwargs) + + output = self.logger.output.split('\n') + self.assertNotIn(secret_auth_token, output[1])