Merge pull request #20400 from Azidburn/develop

Fix for feature requests #3817 , #10258 in develop branch
This commit is contained in:
Thomas S Hatch 2015-02-06 10:29:45 -07:00
commit babebdaa9d
2 changed files with 202 additions and 34 deletions

View File

@ -394,6 +394,65 @@ def check_key(user, key, enc, comment, options, config='.ssh/authorized_keys',
return 'exists'
def rm_auth_key_from_file(user,
source,
config='.ssh/authorized_keys',
saltenv='base',
env=None):
'''
Remove an authorized key from the specified user's authorized key file, using a file as source
CLI Example:
.. code-block:: bash
salt '*' ssh.rm_auth_key_from_file <user> salt://ssh_keys/<user>.id_rsa.pub
'''
if env is not None:
salt.utils.warn_until(
'Boron',
'Passing a salt environment should be done using \'saltenv\' '
'not \'env\'. This functionality will be removed in Salt Boron.'
)
# Backwards compatibility
saltenv = env
lfile = __salt__['cp.cache_file'](source, saltenv)
if not os.path.isfile(lfile):
raise CommandExecutionError(
'Failed to pull key file from salt file server'
)
s_keys = _validate_keys(lfile)
if not s_keys:
err = (
'No keys detected in {0}. Is file properly formatted?'.format(
source
)
)
log.error(err)
__context__['ssh_auth.error'] = err
return 'fail'
else:
rval = ''
for key in s_keys:
rval += rm_auth_key(
user,
key,
config
)
# 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"
# and "new" as possible valid returns. I ordered the following as I
# thought best.
if 'Key not removed' in rval:
return 'Key not removed'
elif 'Key removed' in rval:
return 'Key removed'
else:
return 'Key not present'
def rm_auth_key(user, key, config='.ssh/authorized_keys'):
'''
Remove an authorized key from the specified user's authorized key file

View File

@ -127,6 +127,71 @@ def _present_test(user, name, enc, comment, options, source, config):
return result, comment
def _absent_test(user, name, enc, comment, options, source, config):
'''
Run checks for "absent"
'''
result = None
if source:
keys = __salt__['ssh.check_key_file'](
user,
source,
config,
saltenv=__env__)
if keys:
comment = ''
for key, status in keys.items():
if status == 'exists':
continue
comment += 'Set to {0}: {1}\n'.format(status, key)
if comment:
return result, comment
err = sys.modules[
__salt__['test.ping'].__module__
].__context__.pop('ssh_auth.error', None)
if err:
return False, err
else:
return (
True,
'All host keys in file {0} are already absent'.format(source)
)
else:
# check if this is of form {options} {enc} {key} {comment}
sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$')
fullkey = sshre.search(name)
# if it is {key} [comment]
if not fullkey:
key_and_comment = name.split()
name = key_and_comment[0]
if len(key_and_comment) == 2:
comment = key_and_comment[1]
else:
# if there are options, set them
if fullkey.group(1):
options = fullkey.group(1).split(',')
# key is of format: {enc} {key} [comment]
comps = fullkey.group(2).split()
enc = comps[0]
name = comps[1]
if len(comps) == 3:
comment = comps[2]
check = __salt__['ssh.check_key'](
user,
name,
enc,
comment,
options,
config)
if check == 'update' or check == 'exists':
comment = ('Key {0} for user {1} is set for removal').format(name, user)
else:
comment = ('Key is already absent')
return result, comment
def present(
name,
user,
@ -154,8 +219,8 @@ def present(
source
The source file for the key(s). Can contain any number of public keys,
in standard "authorized_keys" format. If this is set, comment, enc,
and options will be ignored.
in standard "authorized_keys" format. If this is set, comment and enc
will be ignored.
.. note::
The source file must contain keys in the format ``<enc> <key>
@ -215,11 +280,31 @@ def present(
return ret
if source != '':
data = __salt__['ssh.set_auth_key_from_file'](
user,
key = __salt__['cp.get_file_str'](
source,
config,
saltenv=__env__)
filehasoptions = False
# check if this is of form {options} {enc} {key} {comment}
sshre = re.compile(r'^(ssh\-|ecds).*')
key = key.rstrip().split('\n')
for keyline in key:
filehasoptions = sshre.match(keyline)
if not filehasoptions:
data = __salt__['ssh.set_auth_key_from_file'](
user,
source,
config,
saltenv=__env__)
else:
# Split keyline to get key und commen
keyline = keyline.split(' ')
data = __salt__['ssh.set_auth_key'](
user,
keyline[1],
keyline[0],
keyline[2],
options or [],
config)
else:
data = __salt__['ssh.set_auth_key'](
user,
@ -263,6 +348,7 @@ def absent(name,
user,
enc='ssh-rsa',
comment='',
source='',
options=None,
config='.ssh/authorized_keys'):
'''
@ -284,6 +370,11 @@ def absent(name,
options
The options passed to the key, pass a list object
source
The source file for the key(s). Can contain any number of public keys,
in standard "authorized_keys" format. If this is set, comment, enc and
options will be ignored.
config
The location of the authorized keys file relative to the user's home
directory, defaults to ".ssh/authorized_keys"
@ -293,43 +384,61 @@ def absent(name,
'result': True,
'comment': ''}
# Get just the key
sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$')
fullkey = sshre.search(name)
# if it is {key} [comment]
if not fullkey:
key_and_comment = name.split()
name = key_and_comment[0]
if len(key_and_comment) == 2:
comment = key_and_comment[1]
# Extract Key from file if source is present
if source != '':
key = __salt__['cp.get_file_str'](
source,
saltenv=__env__)
filehasoptions = False
# check if this is of form {options} {enc} {key} {comment}
sshre = re.compile(r'^(ssh\-|ecds).*')
key = key.rstrip().split('\n')
for keyline in key:
filehasoptions = sshre.match(keyline)
if not filehasoptions:
ret['comment'] = __salt__['ssh.rm_auth_key_from_file'](user,
source,
config,
saltenv=__env__)
else:
# Split keyline to get key
keyline = keyline.split(' ')
ret['comment'] = __salt__['ssh.rm_auth_key'](user,
keyline[1],
config)
else:
# if there are options, set them
if fullkey.group(1):
options = fullkey.group(1).split(',')
# key is of format: {enc} {key} [comment]
comps = fullkey.group(2).split()
enc = comps[0]
name = comps[1]
if len(comps) == 3:
comment = comps[2]
# Get just the key
sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$')
fullkey = sshre.search(name)
# if it is {key} [comment]
if not fullkey:
key_and_comment = name.split()
name = key_and_comment[0]
if len(key_and_comment) == 2:
comment = key_and_comment[1]
else:
# if there are options, set them
if fullkey.group(1):
options = fullkey.group(1).split(',')
# key is of format: {enc} {key} [comment]
comps = fullkey.group(2).split()
enc = comps[0]
name = comps[1]
if len(comps) == 3:
comment = comps[2]
ret['comment'] = __salt__['ssh.rm_auth_key'](user, name, config)
if __opts__['test']:
check = __salt__['ssh.check_key'](
ret['result'], ret['comment'] = _absent_test(
user,
name,
enc,
comment,
options or [],
config)
if check == 'update' or check == 'exists':
ret['result'] = None
ret['comment'] = 'Key {0} is set for removal'.format(name)
return ret
else:
ret['comment'] = 'Key is already absent'
return ret
ret['comment'] = __salt__['ssh.rm_auth_key'](user, name, config)
source,
config,
)
return ret
if ret['comment'] == 'User authorized keys file not present':
ret['result'] = False