Add fingerpint_hash_type option to ssh_auth state and related functions

In PR #40543, the fingerprint_hash_type option was added to the state
function in salt/states/ssh_known_hosts.py, as well as the function in
the ssh execution module that the ssh_known_hosts states called out to.

This caused issue #40878 because the fingerprint_hash_type state setting
was not added to the salt/states/ssh_auth.py file. This PR adds this
setting to the ssh_auth file, as well as the functions that are used in
the ssh execution module. This allows the user to set the fingerprint_hash_type
option so avoid the warnings about the default changing.

Fixes #40878
This commit is contained in:
rallytime 2017-06-19 13:31:11 -06:00
parent 2c681887d3
commit 48ff5d2a62
2 changed files with 116 additions and 51 deletions

View File

@ -169,7 +169,7 @@ def _replace_auth_key(
)
def _validate_keys(key_file):
def _validate_keys(key_file, fingerprint_hash_type):
'''
Return a dict containing validated keys in the passed file
'''
@ -205,7 +205,7 @@ def _validate_keys(key_file):
enc = comps[0]
key = comps[1]
comment = ' '.join(comps[2:])
fingerprint = _fingerprint(key)
fingerprint = _fingerprint(key, fingerprint_hash_type)
if fingerprint is None:
continue
@ -221,7 +221,7 @@ def _validate_keys(key_file):
return ret
def _fingerprint(public_key, fingerprint_hash_type=None):
def _fingerprint(public_key, fingerprint_hash_type):
'''
Return a public key fingerprint based on its base64-encoded representation
@ -352,7 +352,9 @@ def host_keys(keydir=None, private=True):
return keys
def auth_keys(user=None, config='.ssh/authorized_keys'):
def auth_keys(user=None,
config='.ssh/authorized_keys',
fingerprint_hash_type=None):
'''
Return the authorized keys for users
@ -382,7 +384,7 @@ def auth_keys(user=None, config='.ssh/authorized_keys'):
pass
if full and os.path.isfile(full):
keys[u] = _validate_keys(full)
keys[u] = _validate_keys(full, fingerprint_hash_type)
if old_output_when_one_user:
if user[0] in keys:
@ -396,7 +398,8 @@ def auth_keys(user=None, config='.ssh/authorized_keys'):
def check_key_file(user,
source,
config='.ssh/authorized_keys',
saltenv='base'):
saltenv='base',
fingerprint_hash_type=None):
'''
Check a keyfile from a source destination against the local keys and
return the keys to change
@ -410,7 +413,7 @@ def check_key_file(user,
keyfile = __salt__['cp.cache_file'](source, saltenv)
if not keyfile:
return {}
s_keys = _validate_keys(keyfile)
s_keys = _validate_keys(keyfile, fingerprint_hash_type)
if not s_keys:
err = 'No keys detected in {0}. Is file properly ' \
'formatted?'.format(source)
@ -426,12 +429,19 @@ def check_key_file(user,
s_keys[key]['enc'],
s_keys[key]['comment'],
s_keys[key]['options'],
config)
config=config,
fingerprint_hash_type=fingerprint_hash_type)
return ret
def check_key(user, key, enc, comment, options, config='.ssh/authorized_keys',
cache_keys=None):
def check_key(user,
key,
enc,
comment,
options,
config='.ssh/authorized_keys',
cache_keys=None,
fingerprint_hash_type=None):
'''
Check to see if a key needs updating, returns "update", "add" or "exists"
@ -444,7 +454,9 @@ def check_key(user, key, enc, comment, options, config='.ssh/authorized_keys',
if cache_keys is None:
cache_keys = []
enc = _refine_enc(enc)
current = auth_keys(user, config)
current = auth_keys(user,
config=config,
fingerprint_hash_type=fingerprint_hash_type)
nline = _format_auth_line(key, enc, comment, options)
# Removing existing keys from the auth_keys isn't really a good idea
@ -473,9 +485,10 @@ def check_key(user, key, enc, comment, options, config='.ssh/authorized_keys',
def rm_auth_key_from_file(user,
source,
config='.ssh/authorized_keys',
saltenv='base'):
source,
config='.ssh/authorized_keys',
saltenv='base',
fingerprint_hash_type=None):
'''
Remove an authorized key from the specified user's authorized key file,
using a file as source
@ -492,7 +505,7 @@ def rm_auth_key_from_file(user,
'Failed to pull key file from salt file server'
)
s_keys = _validate_keys(lfile)
s_keys = _validate_keys(lfile, fingerprint_hash_type)
if not s_keys:
err = (
'No keys detected in {0}. Is file properly formatted?'.format(
@ -508,7 +521,8 @@ def rm_auth_key_from_file(user,
rval += rm_auth_key(
user,
key,
config
config=config,
fingerprint_hash_type=fingerprint_hash_type
)
# Due to the ability for a single file to have multiple keys, it's
# possible for a single call to this function to have both "replace"
@ -522,7 +536,10 @@ def rm_auth_key_from_file(user,
return 'Key not present'
def rm_auth_key(user, key, config='.ssh/authorized_keys'):
def rm_auth_key(user,
key,
config='.ssh/authorized_keys',
fingerprint_hash_type=None):
'''
Remove an authorized key from the specified user's authorized key file
@ -532,7 +549,9 @@ def rm_auth_key(user, key, config='.ssh/authorized_keys'):
salt '*' ssh.rm_auth_key <user> <key>
'''
current = auth_keys(user, config)
current = auth_keys(user,
config=config,
fingerprint_hash_type=fingerprint_hash_type)
linere = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$')
if key in current:
# Remove the key
@ -590,7 +609,8 @@ def rm_auth_key(user, key, config='.ssh/authorized_keys'):
def set_auth_key_from_file(user,
source,
config='.ssh/authorized_keys',
saltenv='base'):
saltenv='base',
fingerprint_hash_type=None):
'''
Add a key to the authorized_keys file, using a file as the source.
@ -607,7 +627,7 @@ def set_auth_key_from_file(user,
'Failed to pull key file from salt file server'
)
s_keys = _validate_keys(lfile)
s_keys = _validate_keys(lfile, fingerprint_hash_type)
if not s_keys:
err = (
'No keys detected in {0}. Is file properly formatted?'.format(
@ -623,11 +643,12 @@ def set_auth_key_from_file(user,
rval += set_auth_key(
user,
key,
s_keys[key]['enc'],
s_keys[key]['comment'],
s_keys[key]['options'],
config,
list(s_keys.keys())
enc=s_keys[key]['enc'],
comment=s_keys[key]['comment'],
options=s_keys[key]['options'],
config=config,
cache_keys=list(s_keys.keys()),
fingerprint_hash_type=fingerprint_hash_type
)
# Due to the ability for a single file to have multiple keys, it's
# possible for a single call to this function to have both "replace"
@ -650,7 +671,8 @@ def set_auth_key(
comment='',
options=None,
config='.ssh/authorized_keys',
cache_keys=None):
cache_keys=None,
fingerprint_hash_type=None):
'''
Add a key to the authorized_keys file. The "key" parameter must only be the
string of text that is the encoded key. If the key begins with "ssh-rsa"
@ -677,11 +699,18 @@ def set_auth_key(
# the same filtering done when reading the authorized_keys file. Apply
# the same check to ensure we don't insert anything that will not
# subsequently be read)
key_is_valid = _fingerprint(key) is not None
key_is_valid = _fingerprint(key, fingerprint_hash_type) is not None
if not key_is_valid:
return 'Invalid public key'
status = check_key(user, key, enc, comment, options, config, cache_keys)
status = check_key(user,
key,
enc,
comment,
options,
config=config,
cache_keys=cache_keys,
fingerprint_hash_type=fingerprint_hash_type)
if status == 'update':
_replace_auth_key(user, key, enc, comment, options or [], config)
return 'replace'

View File

@ -55,7 +55,7 @@ import sys
import salt.ext.six as six
def _present_test(user, name, enc, comment, options, source, config):
def _present_test(user, name, enc, comment, options, source, config, fingerprint_hash_type):
'''
Run checks for "present"
'''
@ -65,7 +65,8 @@ def _present_test(user, name, enc, comment, options, source, config):
user,
source,
config,
saltenv=__env__)
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type)
if keys:
comment = ''
for key, status in six.iteritems(keys):
@ -111,7 +112,8 @@ def _present_test(user, name, enc, comment, options, source, config):
enc,
comment,
options,
config)
config=config,
fingerprint_hash_type=fingerprint_hash_type)
if check == 'update':
comment = (
'Key {0} for user {1} is set to be updated'
@ -128,7 +130,7 @@ def _present_test(user, name, enc, comment, options, source, config):
return result, comment
def _absent_test(user, name, enc, comment, options, source, config):
def _absent_test(user, name, enc, comment, options, source, config, fingerprint_hash_type):
'''
Run checks for "absent"
'''
@ -138,7 +140,8 @@ def _absent_test(user, name, enc, comment, options, source, config):
user,
source,
config,
saltenv=__env__)
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type)
if keys:
comment = ''
for key, status in list(keys.items()):
@ -184,7 +187,8 @@ def _absent_test(user, name, enc, comment, options, source, config):
enc,
comment,
options,
config)
config=config,
fingerprint_hash_type=fingerprint_hash_type)
if check == 'update' or check == 'exists':
comment = ('Key {0} for user {1} is set for removal').format(name, user)
else:
@ -202,6 +206,7 @@ def present(
source='',
options=None,
config='.ssh/authorized_keys',
fingerprint_hash_type=None,
**kwargs):
'''
Verifies that the specified SSH key is present for the specified user
@ -243,6 +248,17 @@ def present(
The location of the authorized keys file relative to the user's home
directory, defaults to ".ssh/authorized_keys". Token expansion %u and
%h for username and home path supported.
fingerprint_hash_type
The public key fingerprint hash type that the public key fingerprint
was originally hashed with. This defaults to ``md5`` if not specified.
.. versionadded:: 2016.11.7
.. note::
The default value of the ``fingerprint_hash_type`` will change to
``sha256`` in Salt Nitrogen.
'''
ret = {'name': name,
'changes': {},
@ -279,7 +295,7 @@ def present(
options or [],
source,
config,
)
fingerprint_hash_type)
return ret
# Get only the path to the file without env referrences to check if exists
@ -305,10 +321,11 @@ def present(
data = __salt__['ssh.set_auth_key_from_file'](
user,
source,
config,
saltenv=__env__)
config=config,
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type)
else:
# Split keyline to get key und commen
# Split keyline to get key und comment
keyline = keyline.split(' ')
key_type = keyline[0]
key_value = keyline[1]
@ -316,18 +333,20 @@ def present(
data = __salt__['ssh.set_auth_key'](
user,
key_value,
key_type,
key_comment,
options or [],
config)
enc=key_type,
comment=key_comment,
options=options or [],
config=config,
fingerprint_hash_type=fingerprint_hash_type)
else:
data = __salt__['ssh.set_auth_key'](
user,
name,
enc,
comment,
options or [],
config)
enc=enc,
comment=comment,
options=options or [],
config=config,
fingerprint_hash_type=fingerprint_hash_type)
if data == 'replace':
ret['changes'][name] = 'Updated'
@ -369,7 +388,8 @@ def absent(name,
comment='',
source='',
options=None,
config='.ssh/authorized_keys'):
config='.ssh/authorized_keys',
fingerprint_hash_type=None):
'''
Verifies that the specified SSH key is absent
@ -401,6 +421,17 @@ def absent(name,
directory, defaults to ".ssh/authorized_keys". Token expansion %u and
%h for username and home path supported.
fingerprint_hash_type
The public key fingerprint hash type that the public key fingerprint
was originally hashed with. This defaults to ``md5`` if not specified.
.. versionadded:: 2016.11.7
.. note::
The default value of the ``fingerprint_hash_type`` will change to
``sha256`` in Salt Nitrogen.
'''
ret = {'name': name,
'changes': {},
@ -416,7 +447,7 @@ def absent(name,
options or [],
source,
config,
)
fingerprint_hash_type)
return ret
# Extract Key from file if source is present
@ -434,13 +465,15 @@ def absent(name,
ret['comment'] = __salt__['ssh.rm_auth_key_from_file'](user,
source,
config,
saltenv=__env__)
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type)
else:
# Split keyline to get key
keyline = keyline.split(' ')
ret['comment'] = __salt__['ssh.rm_auth_key'](user,
keyline[1],
config)
config=config,
fingerprint_hash_type=fingerprint_hash_type)
else:
# Get just the key
sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$')
@ -461,7 +494,10 @@ def absent(name,
name = comps[1]
if len(comps) == 3:
comment = comps[2]
ret['comment'] = __salt__['ssh.rm_auth_key'](user, name, config)
ret['comment'] = __salt__['ssh.rm_auth_key'](user,
name,
config=config,
fingerprint_hash_type=fingerprint_hash_type)
if ret['comment'] == 'User authorized keys file not present':
ret['result'] = False