Merge "Filter out details from taskflow logs with v2+jobboard" into stable/yoga
This commit is contained in:
commit
bc724fbebd
@ -23,6 +23,7 @@ from oslo_utils import uuidutils
|
||||
from taskflow.conductors.backends import impl_blocking
|
||||
from taskflow import engines
|
||||
from taskflow import exceptions as taskflow_exc
|
||||
from taskflow.jobs.base import Job
|
||||
from taskflow.listeners import base
|
||||
from taskflow.listeners import logging
|
||||
from taskflow.persistence import models
|
||||
@ -47,6 +48,44 @@ def retryMaskFilter(record):
|
||||
LOG.logger.addFilter(retryMaskFilter)
|
||||
|
||||
|
||||
def _details_filter(obj):
|
||||
if isinstance(obj, dict):
|
||||
ret = {}
|
||||
for key in obj:
|
||||
if (key in ('certificate', 'private_key', 'passphrase') and
|
||||
isinstance(obj[key], str)):
|
||||
ret[key] = '***'
|
||||
elif key == 'intermediates' and isinstance(obj[key], list):
|
||||
ret[key] = ['***'] * len(obj[key])
|
||||
else:
|
||||
ret[key] = _details_filter(obj[key])
|
||||
return ret
|
||||
if isinstance(obj, list):
|
||||
return [_details_filter(e) for e in obj]
|
||||
return obj
|
||||
|
||||
|
||||
class FilteredJob(Job):
|
||||
def __str__(self):
|
||||
# Override the detault __str__ method from taskflow.job.base.Job,
|
||||
# filter out private information from details
|
||||
cls_name = type(self).__name__
|
||||
details = _details_filter(self.details)
|
||||
return "%s: %s (priority=%s, uuid=%s, details=%s)" % (
|
||||
cls_name, self.name, self.priority,
|
||||
self.uuid, details)
|
||||
|
||||
|
||||
class JobDetailsFilter(log.logging.Filter):
|
||||
def filter(self, record):
|
||||
# If the first arg is a Job, convert it now to a string with our custom
|
||||
# method
|
||||
if isinstance(record.args[0], Job):
|
||||
arg0 = record.args[0]
|
||||
record.args = (FilteredJob.__str__(arg0),) + record.args[1:]
|
||||
return True
|
||||
|
||||
|
||||
class BaseTaskFlowEngine(object):
|
||||
"""This is the task flow engine
|
||||
|
||||
@ -125,6 +164,11 @@ class TaskFlowServiceController(object):
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
# Install filter for taskflow executor logger
|
||||
taskflow_logger = log.logging.getLogger(
|
||||
"taskflow.conductors.backends.impl_executor")
|
||||
taskflow_logger.addFilter(JobDetailsFilter())
|
||||
|
||||
def run_poster(self, flow_factory, *args, **kwargs):
|
||||
with self.driver.persistence_driver.get_persistence() as persistence:
|
||||
with self.driver.job_board(persistence) as job_board:
|
||||
|
@ -18,6 +18,7 @@ from unittest import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as oslo_fixture
|
||||
from taskflow import engines as tf_engines
|
||||
from taskflow.jobs.base import Job
|
||||
|
||||
from octavia.common import base_taskflow
|
||||
import octavia.tests.unit.base as base
|
||||
@ -154,3 +155,62 @@ class TestTaskFlowServiceController(base.TestCase):
|
||||
"test2", self.jobboard_mock.__enter__(),
|
||||
persistence=self.persistence_mock.__enter__(),
|
||||
engine='parallel')
|
||||
|
||||
|
||||
class TestJobDetailsFilter(base.TestCase):
|
||||
def test_filter(self):
|
||||
log_filter = base_taskflow.JobDetailsFilter()
|
||||
|
||||
tls_container_data = {
|
||||
'certificate': '<CERTIFICATE>',
|
||||
'private_key': '<PRIVATE_KEY>',
|
||||
'passphrase': '<PASSPHRASE>',
|
||||
'intermediates': [
|
||||
'<INTERMEDIATE1>',
|
||||
'<INTERMEDIATE2>'
|
||||
]
|
||||
}
|
||||
|
||||
job = mock.Mock(spec=Job)
|
||||
job.details = {
|
||||
'store': {
|
||||
'listeners': [
|
||||
{
|
||||
'name': 'listener_name',
|
||||
'default_tls_container_data': tls_container_data
|
||||
}
|
||||
],
|
||||
'any_recursive': {
|
||||
'type': [
|
||||
{
|
||||
'other_list': [
|
||||
tls_container_data,
|
||||
{
|
||||
'test': tls_container_data,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record = mock.Mock()
|
||||
record.args = (job, 'something')
|
||||
|
||||
ret = log_filter.filter(record)
|
||||
self.assertTrue(ret)
|
||||
|
||||
self.assertNotIn(tls_container_data['certificate'], record.args[0])
|
||||
self.assertNotIn(tls_container_data['private_key'], record.args[0])
|
||||
self.assertNotIn(tls_container_data['passphrase'], record.args[0])
|
||||
self.assertNotIn(tls_container_data['intermediates'][0],
|
||||
record.args[0])
|
||||
self.assertNotIn(tls_container_data['intermediates'][1],
|
||||
record.args[0])
|
||||
self.assertIn('listener_name', record.args[0])
|
||||
|
||||
record.args = ('arg1', 2)
|
||||
|
||||
ret = log_filter.filter(record)
|
||||
self.assertTrue(ret)
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
security:
|
||||
- |
|
||||
Filter out private information from the taskflow logs when ''INFO'' level
|
||||
messages are enabled and when jobboard is enabled. Logs might have included
|
||||
TLS certificates and private_key. By default, in Octavia only WARNING and
|
||||
above messages are enabled in taskflow and jobboard is disabled.
|
||||
fixes:
|
||||
- |
|
||||
The parameters of a taskflow Flow were logged in ''INFO'' level messages by
|
||||
taskflow, it included TLS-enabled listeners and pools parameters, such as
|
||||
certificates and private_key.
|
Loading…
Reference in New Issue
Block a user