mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New password obfuscation in syslog messages that:
* makes speed acceptable for some datasets * obfuscates multiple detected passwords * obfuscates more characters to account for some corner cases when trying to detect passwords. Fixes #8364
This commit is contained in:
parent
4f55bcc298
commit
1afc8161a1
1 changed files with 82 additions and 22 deletions
|
@ -966,6 +966,81 @@ class AnsibleModule(object):
|
||||||
params2.update(params)
|
params2.update(params)
|
||||||
return (params2, args)
|
return (params2, args)
|
||||||
|
|
||||||
|
def _heuristic_log_sanitize(self, data):
|
||||||
|
''' Remove strings that look like passwords from log messages
|
||||||
|
|
||||||
|
Currently filters out things like:
|
||||||
|
* user:pass@foo/whatever
|
||||||
|
* http://username:pass@wherever/foo
|
||||||
|
|
||||||
|
Currently, the heuristics are subject to false positives. This could
|
||||||
|
change in the future should we decide that no_log is what we want to
|
||||||
|
push people towards.
|
||||||
|
'''
|
||||||
|
# Regexes can be too slow for this. Pathological cases for regexes
|
||||||
|
# seem to be large amounts of data with many ':' but no '@'
|
||||||
|
# This function gets slower when there are many replacements but not
|
||||||
|
# nearly as slow as the worst case for regexes. If we need the
|
||||||
|
# flexibility of regex's in the future, re.sub() is faster than
|
||||||
|
# re.match() + str.join(). We might be able to decide to use a regex
|
||||||
|
# strategy if we detect a large number of '@' symbolsand use this
|
||||||
|
# function otherwise.
|
||||||
|
|
||||||
|
# begin points to the beginning of a password containing string
|
||||||
|
# end points to the end of the password containing string
|
||||||
|
# sep points to the char in between username and password
|
||||||
|
# prev_begin keeps track of where in the string to start a new search
|
||||||
|
# for the end of a password substring
|
||||||
|
# sep_search_end keeps track of where in the string to end a search
|
||||||
|
# for the separator
|
||||||
|
output = []
|
||||||
|
begin = len(data)
|
||||||
|
prev_begin = begin
|
||||||
|
sep = 1 # Prime the pump with a sentinel value
|
||||||
|
while sep:
|
||||||
|
# Find the potential end of a password
|
||||||
|
try:
|
||||||
|
end = data.rindex('@', 0, begin)
|
||||||
|
except ValueError:
|
||||||
|
# No end marker, so add the rest of the data
|
||||||
|
output.insert(0, data[0:begin])
|
||||||
|
break
|
||||||
|
|
||||||
|
# Search for the beginning of the password
|
||||||
|
sep = None
|
||||||
|
sep_search_end = end
|
||||||
|
while not sep:
|
||||||
|
# Search for the beginning of a URL-style username+password
|
||||||
|
try:
|
||||||
|
begin = data.rindex('://', 0, sep_search_end)
|
||||||
|
except ValueError:
|
||||||
|
# If we don't find that, then we default to the start of
|
||||||
|
# the string (b/c ssh-style username+password could
|
||||||
|
# be taking up all of this parameter).
|
||||||
|
begin = 0
|
||||||
|
# Search for a separator character inside of where we know the
|
||||||
|
# password might live.
|
||||||
|
try:
|
||||||
|
sep = data.index(':', begin + 3, end)
|
||||||
|
except ValueError:
|
||||||
|
# No separator, now we have choices:
|
||||||
|
if begin == 0:
|
||||||
|
# We've searched the whole string so there's no
|
||||||
|
# password here. Return the remaining data
|
||||||
|
output.insert(0, data[0:begin])
|
||||||
|
break
|
||||||
|
# Search for a different beginning of the password field.
|
||||||
|
sep_search_end = begin
|
||||||
|
continue
|
||||||
|
if sep:
|
||||||
|
# Password was found; remove it.
|
||||||
|
output.insert(0, data[end:prev_begin])
|
||||||
|
output.insert(0, '********')
|
||||||
|
output.insert(0, data[begin:sep + 1])
|
||||||
|
prev_begin = begin
|
||||||
|
|
||||||
|
return ''.join(output)
|
||||||
|
|
||||||
def _log_invocation(self):
|
def _log_invocation(self):
|
||||||
''' log that ansible ran the module '''
|
''' log that ansible ran the module '''
|
||||||
# TODO: generalize a separate log function and make log_invocation use it
|
# TODO: generalize a separate log function and make log_invocation use it
|
||||||
|
@ -973,12 +1048,6 @@ class AnsibleModule(object):
|
||||||
log_args = dict()
|
log_args = dict()
|
||||||
passwd_keys = ['password', 'login_password']
|
passwd_keys = ['password', 'login_password']
|
||||||
|
|
||||||
filter_re = [
|
|
||||||
# filter out things like user:pass@foo/whatever
|
|
||||||
# and http://username:pass@wherever/foo
|
|
||||||
re.compile('^(?P<before>.*:)(?P<password>.*)(?P<after>\@.*)$'),
|
|
||||||
]
|
|
||||||
|
|
||||||
for param in self.params:
|
for param in self.params:
|
||||||
canon = self.aliases.get(param, param)
|
canon = self.aliases.get(param, param)
|
||||||
arg_opts = self.argument_spec.get(canon, {})
|
arg_opts = self.argument_spec.get(canon, {})
|
||||||
|
@ -989,21 +1058,12 @@ class AnsibleModule(object):
|
||||||
elif param in passwd_keys:
|
elif param in passwd_keys:
|
||||||
log_args[param] = 'NOT_LOGGING_PASSWORD'
|
log_args[param] = 'NOT_LOGGING_PASSWORD'
|
||||||
else:
|
else:
|
||||||
found = False
|
param_val = self.params[param]
|
||||||
for filter in filter_re:
|
if not isinstance(param_val, basestring):
|
||||||
param_val = self.params[param]
|
param_val = str(param_val)
|
||||||
if not isinstance(param_val, basestring):
|
elif isinstance(param_val, unicode):
|
||||||
param_val = str(param_val)
|
param_val = param_val.encode('utf-8')
|
||||||
elif isinstance(param_val, unicode):
|
log_args[param] = self._heuristic_log_sanitize(param_val)
|
||||||
param_val = param_val.encode('utf-8')
|
|
||||||
m = filter.match(param_val)
|
|
||||||
if m:
|
|
||||||
d = m.groupdict()
|
|
||||||
log_args[param] = d['before'] + "********" + d['after']
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
log_args[param] = self.params[param]
|
|
||||||
|
|
||||||
module = 'ansible-%s' % os.path.basename(__file__)
|
module = 'ansible-%s' % os.path.basename(__file__)
|
||||||
msg = []
|
msg = []
|
||||||
|
|
Loading…
Reference in a new issue