diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 325bddb103..5b91e21bfb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -42,18 +42,20 @@ Changed Fixed ~~~~~ -* Refactored orquesta execution graph to fix performance issue for workflows with many - references to non-join tasks. st2workflowengine and DB models are refactored accordingly. - (improvement) StackStorm/orquesta#122. -* Fix orquesta workflow stuck in running status when one or more items failed execution for a - with items task. (bug fix) #4523 -* Fix orquesta workflow bug where context variables are being overwritten on task join. - (bug fix) StackStorm/orquesta#112 +* Refactored orquesta execution graph to fix performance issue for workflows with many references + to non-join tasks. st2workflowengine and DB models are refactored accordingly. (improvement) + StackStorm/orquesta#122. +* Fix orquesta workflow stuck in running status when one or more items failed execution for a with + items task. (bug fix) #4523 +* Fix orquesta workflow bug where context variables are being overwritten on task join. (bug fix) + StackStorm/orquesta#112 * Fix inadvertent regression in notifier service which would cause generic action trigger to only be dispatched for completed states even if custom states were specified using ``action_sensor.emit_when`` config option. (bug fix) - Reported by Shu Sugimoto (@shusugmt). #4591 +* Make sure we don't log auth token and api key inside st2api log file if those values are provided + via query parameter and not header (``?x-auth-token=foo``, ``?st2-api-key=bar``). (bug fix) #4592 + #4589 2.10.3 - March 06, 2019 ----------------------- diff --git a/st2common/st2common/middleware/logging.py b/st2common/st2common/middleware/logging.py index e4d30a1e7d..9eeaa4cfeb 100644 --- a/st2common/st2common/middleware/logging.py +++ b/st2common/st2common/middleware/logging.py @@ -14,16 +14,28 @@ # limitations under the License. from __future__ import absolute_import + import time import types import itertools +from oslo_config import cfg + from st2common.constants.api import REQUEST_ID_HEADER +from st2common.constants.auth import QUERY_PARAM_ATTRIBUTE_NAME +from st2common.constants.auth import QUERY_PARAM_API_KEY_ATTRIBUTE_NAME +from st2common.constants.secrets import MASKED_ATTRIBUTE_VALUE +from st2common.constants.secrets import MASKED_ATTRIBUTES_BLACKLIST from st2common import log as logging from st2common.router import Request, NotFoundException LOG = logging.getLogger(__name__) +SECRET_QUERY_PARAMS = [ + QUERY_PARAM_ATTRIBUTE_NAME, + QUERY_PARAM_API_KEY_ATTRIBUTE_NAME +] + MASKED_ATTRIBUTES_BLACKLIST + try: clock = time.perf_counter except AttributeError: @@ -46,12 +58,20 @@ def __call__(self, environ, start_response): request = Request(environ) + query_params = request.GET.dict_of_lists() + + # Mask secret / sensitive query params + secret_query_params = SECRET_QUERY_PARAMS + cfg.CONF.log.mask_secrets_blacklist + for param_name in secret_query_params: + if param_name in query_params: + query_params[param_name] = MASKED_ATTRIBUTE_VALUE + # Log the incoming request values = { 'method': request.method, 'path': request.path, 'remote_addr': request.remote_addr, - 'query': request.GET.dict_of_lists(), + 'query': query_params, 'request_id': request.headers.get(REQUEST_ID_HEADER, None) } diff --git a/st2common/tests/unit/test_logging_middleware.py b/st2common/tests/unit/test_logging_middleware.py new file mode 100644 index 0000000000..014634e414 --- /dev/null +++ b/st2common/tests/unit/test_logging_middleware.py @@ -0,0 +1,74 @@ +# Licensed to the StackStorm, Inc ('StackStorm') under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import unittest2 + +from oslo_config import cfg + +from st2common.middleware.logging import LoggingMiddleware +from st2common.constants.secrets import MASKED_ATTRIBUTE_VALUE + +__all__ = [ + 'LoggingMiddlewareTestCase' +] + + +class LoggingMiddlewareTestCase(unittest2.TestCase): + @mock.patch('st2common.middleware.logging.LOG') + @mock.patch('st2common.middleware.logging.Request') + def test_secret_parameters_are_masked_in_log_message(self, mock_request, mock_log): + + def app(environ, custom_start_response): + custom_start_response(status='200 OK', headers=[('Content-Length', 100)]) + return [None] + + router = mock.Mock() + endpoint = mock.Mock() + router.match.return_value = (endpoint, None) + middleware = LoggingMiddleware(app=app, router=router) + + cfg.CONF.set_override(group='log', name='mask_secrets_blacklist', + override=['blacklisted_4', 'blacklisted_5']) + + environ = {} + mock_request.return_value.GET.dict_of_lists.return_value = { + 'foo': 'bar', + 'bar': 'baz', + 'x-auth-token': 'secret', + 'st2-api-key': 'secret', + 'password': 'secret', + 'st2_auth_token': 'secret', + 'token': 'secret', + 'blacklisted_4': 'super secret', + 'blacklisted_5': 'super secret', + } + middleware(environ=environ, start_response=mock.Mock()) + + expected_query = { + 'foo': 'bar', + 'bar': 'baz', + 'x-auth-token': MASKED_ATTRIBUTE_VALUE, + 'st2-api-key': MASKED_ATTRIBUTE_VALUE, + 'password': MASKED_ATTRIBUTE_VALUE, + 'token': MASKED_ATTRIBUTE_VALUE, + 'st2_auth_token': MASKED_ATTRIBUTE_VALUE, + 'blacklisted_4': MASKED_ATTRIBUTE_VALUE, + 'blacklisted_5': MASKED_ATTRIBUTE_VALUE, + } + + call_kwargs = mock_log.info.call_args_list[0][1] + query = call_kwargs['extra']['query'] + self.assertEqual(query, expected_query)