mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
Merge pull request #1315 from imankulov/ssh_known_hosts
Support for ssh_known_hosts in modules and states
This commit is contained in:
commit
99ea439ab6
@ -1,9 +1,10 @@
|
|||||||
'''
|
'''
|
||||||
Manage client ssh components
|
Manage client ssh components
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import binascii
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
def _refine_enc(enc):
|
def _refine_enc(enc):
|
||||||
@ -119,16 +120,38 @@ def _validate_keys(key_file):
|
|||||||
enc = comps[0]
|
enc = comps[0]
|
||||||
key = comps[1]
|
key = comps[1]
|
||||||
comment = ' '.join(comps[2:])
|
comment = ' '.join(comps[2:])
|
||||||
|
fingerprint = _fingerprint(key)
|
||||||
|
if fingerprint is None:
|
||||||
|
continue
|
||||||
|
|
||||||
ret[key] = {'enc': enc,
|
ret[key] = {'enc': enc,
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'options': options}
|
'options': options,
|
||||||
|
'fingerprint': fingerprint}
|
||||||
except IOError:
|
except IOError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def _fingerprint(public_key):
|
||||||
|
"""
|
||||||
|
Return a public key fingerprint based on its base64-encoded representation
|
||||||
|
|
||||||
|
The fingerprint string is formatted according to RFC 4716 (ch.4), that is,
|
||||||
|
in the form "xx:xx:...:xx"
|
||||||
|
|
||||||
|
If the key is invalid (incorrect base64 string), return None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
raw_key = public_key.decode('base64')
|
||||||
|
except binascii.Error:
|
||||||
|
return None
|
||||||
|
ret = hashlib.md5(raw_key).hexdigest()
|
||||||
|
chunks = [ret[i:i+2] for i in range(0, len(ret), 2)]
|
||||||
|
return ':'.join(chunks)
|
||||||
|
|
||||||
|
|
||||||
def host_keys(keydir=None):
|
def host_keys(keydir=None):
|
||||||
'''
|
'''
|
||||||
Return the minion's host keys
|
Return the minion's host keys
|
||||||
@ -368,3 +391,167 @@ def set_auth_key(
|
|||||||
else:
|
else:
|
||||||
open(fconfig, 'a+').write('{0}'.format(auth_line))
|
open(fconfig, 'a+').write('{0}'.format(auth_line))
|
||||||
return 'new'
|
return 'new'
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_openssh_output(lines):
|
||||||
|
'''
|
||||||
|
Helper function which parses ssh-keygen -F and ssh-keyscan function output
|
||||||
|
and yield dict with keys information, one by one.
|
||||||
|
'''
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('#'):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
hostname, enc, key = line.split()
|
||||||
|
except ValueError: # incorrect format
|
||||||
|
continue
|
||||||
|
fingerprint = _fingerprint(key)
|
||||||
|
if not fingerprint:
|
||||||
|
continue
|
||||||
|
yield {'hostname': hostname, 'key': key, 'enc': enc,
|
||||||
|
'fingerprint': fingerprint}
|
||||||
|
|
||||||
|
|
||||||
|
def get_known_host(user, hostname, config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Return information about known host from the configfile, if any.
|
||||||
|
If there is no such key, return None.
|
||||||
|
|
||||||
|
CLI Example::
|
||||||
|
|
||||||
|
salt '*' ssh.get_known_host <user> <hostname>
|
||||||
|
'''
|
||||||
|
uinfo = __salt__['user.info'](user)
|
||||||
|
full = os.path.join(uinfo['home'], config)
|
||||||
|
if not os.path.isfile(full):
|
||||||
|
return None
|
||||||
|
cmd = 'ssh-keygen -F "{0}" -f "{1}"'.format(hostname, full)
|
||||||
|
lines = __salt__['cmd.run'](cmd).splitlines()
|
||||||
|
known_hosts = list(_parse_openssh_output(lines))
|
||||||
|
return known_hosts[0] if known_hosts else None
|
||||||
|
|
||||||
|
|
||||||
|
def recv_known_host(user, hostname, enc=None, port=None, hash_hostname=False):
|
||||||
|
'''
|
||||||
|
Retreive information about host public key from remote server
|
||||||
|
|
||||||
|
CLI Example::
|
||||||
|
|
||||||
|
salt '*' ssh.recv_known_host <user> <hostname> enc=<enc> port=<port>
|
||||||
|
'''
|
||||||
|
chunks = ['ssh-keyscan', ]
|
||||||
|
if port:
|
||||||
|
chunks += ['-p', str(port)]
|
||||||
|
if enc:
|
||||||
|
chunks += ['-t', str(enc)]
|
||||||
|
if hash_hostname:
|
||||||
|
chunks.append('-H')
|
||||||
|
chunks.append(str(hostname))
|
||||||
|
cmd = ' '.join(chunks)
|
||||||
|
lines = __salt__['cmd.run'](cmd).splitlines()
|
||||||
|
known_hosts = list(_parse_openssh_output(lines))
|
||||||
|
return known_hosts[0] if known_hosts else None
|
||||||
|
|
||||||
|
|
||||||
|
def check_known_host(user, hostname, key=None, fingerprint=None,
|
||||||
|
config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Check the record in known_hosts file, either by its value or by fingerprint
|
||||||
|
(it's enough to set up either key or fingerprint, you don't need to set up
|
||||||
|
both).
|
||||||
|
|
||||||
|
If provided key or fingerprint doesn't match with stored value, return
|
||||||
|
"update", if no value is found for a given host, return "add", otherwise
|
||||||
|
return "exists".
|
||||||
|
|
||||||
|
If neither key, nor fingerprint is defined, then additional validation is
|
||||||
|
not performed.
|
||||||
|
|
||||||
|
CLI Example::
|
||||||
|
|
||||||
|
salt '*' ssh.check_known_host <user> <hostname> key='AAAA...FAaQ=='
|
||||||
|
'''
|
||||||
|
known_host = get_known_host(user, hostname, config=config)
|
||||||
|
if not known_host:
|
||||||
|
return 'add'
|
||||||
|
if key:
|
||||||
|
return 'exists' if key == known_host['key'] else 'update'
|
||||||
|
elif fingerprint:
|
||||||
|
return 'exists' if fingerprint == known_host['fingerprint'] else 'update'
|
||||||
|
else:
|
||||||
|
return 'exists'
|
||||||
|
|
||||||
|
|
||||||
|
def rm_known_host(user, hostname, config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Remove all keys belonging to hostname from a known_hosts file.
|
||||||
|
|
||||||
|
CLI Example::
|
||||||
|
|
||||||
|
salt '*' ssh.rm_known_host <user> <hostname>
|
||||||
|
'''
|
||||||
|
uinfo = __salt__['user.info'](user)
|
||||||
|
full = os.path.join(uinfo['home'], config)
|
||||||
|
if not os.path.isfile(full):
|
||||||
|
return {'status': 'error',
|
||||||
|
'error': 'Known hosts file {0} does not exist'.format(full)}
|
||||||
|
cmd = 'ssh-keygen -R "{0}" -f "{1}"'.format(hostname, full)
|
||||||
|
cmd_result = __salt__['cmd.run'](cmd).strip()
|
||||||
|
return {'status': 'removed', 'comment': cmd_result}
|
||||||
|
|
||||||
|
|
||||||
|
def set_known_host(user, hostname,
|
||||||
|
fingerprint=None,
|
||||||
|
port=None,
|
||||||
|
enc=None,
|
||||||
|
hash_hostname=True,
|
||||||
|
config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Download SSH public key from remote host "hostname", optionally validate
|
||||||
|
its fingerprint against "fingerprint" variable and save the record in the
|
||||||
|
known_hosts file.
|
||||||
|
|
||||||
|
If such a record does already exists in there, do nothing.
|
||||||
|
|
||||||
|
|
||||||
|
CLI Example::
|
||||||
|
|
||||||
|
salt '*' ssh.set_known_host <user> fingerprint='xx:xx:..:xx' enc='ssh-rsa'\
|
||||||
|
config='.ssh/known_hosts'
|
||||||
|
'''
|
||||||
|
update_required = False
|
||||||
|
stored_host = get_known_host(user, hostname, config)
|
||||||
|
|
||||||
|
if not stored_host:
|
||||||
|
update_required = True
|
||||||
|
elif fingerprint and fingerprint != stored_host['fingerprint']:
|
||||||
|
update_required = True
|
||||||
|
|
||||||
|
if not update_required:
|
||||||
|
return {'status': 'exists', 'key': stored_host}
|
||||||
|
|
||||||
|
remote_host = recv_known_host(user, hostname, enc=enc, port=port,
|
||||||
|
hash_hostname=True)
|
||||||
|
if not remote_host:
|
||||||
|
return {'status': 'error',
|
||||||
|
'error': 'Unable to receive remote host key'}
|
||||||
|
|
||||||
|
if fingerprint and fingerprint != remote_host['fingerprint']:
|
||||||
|
return {'status': 'error',
|
||||||
|
'error': ('Remote host public key found but its fingerprint '
|
||||||
|
'does not match one you have provided')}
|
||||||
|
|
||||||
|
# remove everything we had in the config so far
|
||||||
|
rm_known_host(user, hostname, config=config)
|
||||||
|
# set up new value
|
||||||
|
uinfo = __salt__['user.info'](user)
|
||||||
|
full = os.path.join(uinfo['home'], config)
|
||||||
|
line = '{hostname} {enc} {key}\n'.format(**remote_host)
|
||||||
|
with open(full, 'w') as fd:
|
||||||
|
fd.write(line)
|
||||||
|
return {'status': 'updated', 'old': stored_host, 'new': remote_host}
|
||||||
|
|
||||||
|
status = check_known_host(user, hostname, fingerprint=fingerprint,
|
||||||
|
config=config)
|
||||||
|
if status == 'exists':
|
||||||
|
return None
|
||||||
|
104
salt/states/ssh_known_hosts.py
Normal file
104
salt/states/ssh_known_hosts.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
'''
|
||||||
|
SSH known hosts management
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Manage the information stored in the known_hosts files
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
github.com:
|
||||||
|
ssh_known_hosts:
|
||||||
|
- present
|
||||||
|
- user: root
|
||||||
|
- fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
|
||||||
|
|
||||||
|
example.com:
|
||||||
|
ssh_known_hosts:
|
||||||
|
- absent
|
||||||
|
- user: root
|
||||||
|
'''
|
||||||
|
|
||||||
|
def present(
|
||||||
|
name,
|
||||||
|
user,
|
||||||
|
fingerprint=None,
|
||||||
|
port=None,
|
||||||
|
enc=None,
|
||||||
|
config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Verifies that the specified host is known by the specified user
|
||||||
|
|
||||||
|
name
|
||||||
|
The name of the remote host (i.e. "github.com")
|
||||||
|
|
||||||
|
user
|
||||||
|
The user who owns the ssh authorized keys file to modify
|
||||||
|
|
||||||
|
enc
|
||||||
|
Defines what type of key is being used, can be ssh-rsa or ssh-dss
|
||||||
|
|
||||||
|
fingerprint
|
||||||
|
The fingerprint of the key which must be presented in the known_hosts
|
||||||
|
file
|
||||||
|
|
||||||
|
port
|
||||||
|
optional parameter, denoting the port of the remote host, which will be
|
||||||
|
used in case, if the public key will be requested from it. By default
|
||||||
|
the port 22 is used.
|
||||||
|
|
||||||
|
config
|
||||||
|
The location of the authorized keys file relative to the user's home
|
||||||
|
directory, defaults to ".ssh/known_hosts"
|
||||||
|
'''
|
||||||
|
ret = {'name': name,
|
||||||
|
'changes': {},
|
||||||
|
'result': True,
|
||||||
|
'comment': ''}
|
||||||
|
|
||||||
|
result = __salt__['ssh.set_known_host'](user, name,
|
||||||
|
fingerprint=fingerprint,
|
||||||
|
port=port,
|
||||||
|
enc=enc,
|
||||||
|
config=config)
|
||||||
|
if result['status'] == 'exists':
|
||||||
|
return {'name': name,
|
||||||
|
'result': None,
|
||||||
|
'comment': '{0} already exists in {1}'.format(name, config)}
|
||||||
|
elif result['status'] == 'error':
|
||||||
|
return {'name': name,
|
||||||
|
'result': False,
|
||||||
|
'comment': result['error']}
|
||||||
|
else: # 'updated'
|
||||||
|
return {'name': name,
|
||||||
|
'result': True,
|
||||||
|
'changes': {'old': result['old'], 'new': result['new']},
|
||||||
|
'comment': '{0}\'s key saved to {1} (fingerprint: {2})'.format(
|
||||||
|
name, config, result['new']['fingerprint'])}
|
||||||
|
|
||||||
|
|
||||||
|
def absent(name, user, config='.ssh/known_hosts'):
|
||||||
|
'''
|
||||||
|
Verifies that the specified host is not known by the given user
|
||||||
|
|
||||||
|
name
|
||||||
|
The host name
|
||||||
|
|
||||||
|
user
|
||||||
|
The user who owns the ssh authorized keys file to modify
|
||||||
|
|
||||||
|
config
|
||||||
|
The location of the authorized keys file relative to the user's home
|
||||||
|
directory, defaults to ".ssh/known_hosts"
|
||||||
|
'''
|
||||||
|
ret = {'name': name,
|
||||||
|
'changes': {},
|
||||||
|
'result': True,
|
||||||
|
'comment': ''}
|
||||||
|
known_host = __salt__['ssh.get_known_host'](user, name, config=config)
|
||||||
|
if not known_host:
|
||||||
|
return dict(ret, result=None, comment='Host is already absent')
|
||||||
|
rm_result = __salt__['ssh.rm_known_host'](user, name, config=config)
|
||||||
|
if rm_result['status'] == 'error':
|
||||||
|
return dict(ret, result=False, comment=rm_result['error'])
|
||||||
|
else:
|
||||||
|
return dict(ret, result=True, comment=rm_result['comment'])
|
1
tests/integration/files/ssh/authorized_keys
Normal file
1
tests/integration/files/ssh/authorized_keys
Normal file
@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== github.com
|
1
tests/integration/files/ssh/known_hosts
Normal file
1
tests/integration/files/ssh/known_hosts
Normal file
@ -0,0 +1 @@
|
|||||||
|
|1|muzcBqgq7+ByUY7aLICytOff8UI=|rZ1JBNlIOqRnwwsJl9yP+xMxgf8= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
1
tests/integration/files/ssh/raw
Normal file
1
tests/integration/files/ssh/raw
Normal file
@ -0,0 +1 @@
|
|||||||
|
AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
148
tests/integration/modules/ssh.py
Normal file
148
tests/integration/modules/ssh.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
'''
|
||||||
|
Test the ssh module
|
||||||
|
'''
|
||||||
|
# Import python libs
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Import Salt libs
|
||||||
|
from saltunittest import TestLoader, TextTestRunner
|
||||||
|
import integration
|
||||||
|
from integration import TestDaemon
|
||||||
|
|
||||||
|
|
||||||
|
AUTHORIZED_KEYS = os.path.join(integration.TMP, 'authorized_keys')
|
||||||
|
KNOWN_HOSTS = os.path.join(integration.TMP, 'known_hosts')
|
||||||
|
GITHUB_FINGERPRINT = '16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
|
||||||
|
|
||||||
|
|
||||||
|
class SSHModuleTest(integration.ModuleCase):
|
||||||
|
'''
|
||||||
|
Test the ssh module
|
||||||
|
'''
|
||||||
|
def setUp(self):
|
||||||
|
super(SSHModuleTest, self).setUp()
|
||||||
|
with open(os.path.join(integration.FILES, 'ssh', 'raw')) as fd:
|
||||||
|
self.key = fd.read().strip()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if os.path.isfile(AUTHORIZED_KEYS):
|
||||||
|
os.remove(AUTHORIZED_KEYS)
|
||||||
|
if os.path.isfile(KNOWN_HOSTS):
|
||||||
|
os.remove(KNOWN_HOSTS)
|
||||||
|
super(SSHModuleTest, self).tearDown()
|
||||||
|
|
||||||
|
def test_auth_keys(self):
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'authorized_keys'),
|
||||||
|
AUTHORIZED_KEYS)
|
||||||
|
ret = self.run_function('ssh.auth_keys', ['root', AUTHORIZED_KEYS])
|
||||||
|
self.assertEqual(len(ret.items()), 1) # exactply one key is found
|
||||||
|
key_data = ret.items()[0][1]
|
||||||
|
self.assertEqual(key_data['comment'], 'github.com')
|
||||||
|
self.assertEqual(key_data['enc'], 'ssh-rsa')
|
||||||
|
self.assertEqual(key_data['options'], [])
|
||||||
|
self.assertEqual(key_data['fingerprint'], GITHUB_FINGERPRINT)
|
||||||
|
|
||||||
|
def test_get_known_host(self):
|
||||||
|
"""
|
||||||
|
Check that known host information is returned from ~/.ssh/config
|
||||||
|
"""
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'known_hosts'),
|
||||||
|
KNOWN_HOSTS)
|
||||||
|
arg = ['root', 'github.com']
|
||||||
|
kwargs = {'config': KNOWN_HOSTS}
|
||||||
|
ret = self.run_function('ssh.get_known_host', arg, **kwargs)
|
||||||
|
self.assertEqual(ret['enc'], 'ssh-rsa')
|
||||||
|
self.assertEqual(ret['key'], self.key)
|
||||||
|
self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT)
|
||||||
|
|
||||||
|
def test_recv_known_host(self):
|
||||||
|
"""
|
||||||
|
Check that known host information is returned from remote host
|
||||||
|
"""
|
||||||
|
ret = self.run_function('ssh.recv_known_host', ['root', 'github.com'])
|
||||||
|
self.assertEqual(ret['enc'], 'ssh-rsa')
|
||||||
|
self.assertEqual(ret['key'], self.key)
|
||||||
|
self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT)
|
||||||
|
|
||||||
|
def test_check_known_host_add(self):
|
||||||
|
"""
|
||||||
|
Check known hosts by its fingerprint. File needs to be updated
|
||||||
|
"""
|
||||||
|
arg = ['root', 'github.com']
|
||||||
|
kwargs = {'fingerprint': GITHUB_FINGERPRINT, 'config': KNOWN_HOSTS}
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg, **kwargs)
|
||||||
|
self.assertEqual(ret, 'add')
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_known_host_update(self):
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'known_hosts'),
|
||||||
|
KNOWN_HOSTS)
|
||||||
|
arg = ['root', 'github.com']
|
||||||
|
kwargs = {'config': KNOWN_HOSTS}
|
||||||
|
# wrong fingerprint
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg,
|
||||||
|
**dict(kwargs, fingerprint='aa:bb:cc:dd'))
|
||||||
|
self.assertEqual(ret, 'update')
|
||||||
|
# wrong keyfile
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg,
|
||||||
|
**dict(kwargs, key='YQ=='))
|
||||||
|
self.assertEqual(ret, 'update')
|
||||||
|
|
||||||
|
def test_check_known_host_exists(self):
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'known_hosts'),
|
||||||
|
KNOWN_HOSTS)
|
||||||
|
arg = ['root', 'github.com']
|
||||||
|
kwargs = {'config': KNOWN_HOSTS}
|
||||||
|
# wrong fingerprint
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg,
|
||||||
|
**dict(kwargs, fingerprint=GITHUB_FINGERPRINT))
|
||||||
|
self.assertEqual(ret, 'exists')
|
||||||
|
# wrong keyfile
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg,
|
||||||
|
**dict(kwargs, key=self.key))
|
||||||
|
self.assertEqual(ret, 'exists')
|
||||||
|
|
||||||
|
def test_rm_known_host(self):
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'known_hosts'),
|
||||||
|
KNOWN_HOSTS)
|
||||||
|
arg = ['root', 'github.com']
|
||||||
|
kwargs = {'config': KNOWN_HOSTS, 'key': self.key}
|
||||||
|
# before removal
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg, **kwargs)
|
||||||
|
self.assertEqual(ret, 'exists')
|
||||||
|
# remove
|
||||||
|
self.run_function('ssh.rm_known_host', arg, config=KNOWN_HOSTS)
|
||||||
|
# after removal
|
||||||
|
ret = self.run_function('ssh.check_known_host', arg, **kwargs)
|
||||||
|
self.assertEqual(ret, 'add')
|
||||||
|
|
||||||
|
def test_set_known_host(self):
|
||||||
|
# add item
|
||||||
|
ret = self.run_function('ssh.set_known_host', ['root', 'github.com'],
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
self.assertEqual(ret['status'], 'updated')
|
||||||
|
self.assertEqual(ret['old'], None)
|
||||||
|
self.assertEqual(ret['new']['fingerprint'], GITHUB_FINGERPRINT)
|
||||||
|
# check that item does exist
|
||||||
|
ret = self.run_function('ssh.get_known_host', ['root', 'github.com'],
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT)
|
||||||
|
# add the same item once again
|
||||||
|
ret = self.run_function('ssh.set_known_host', ['root', 'github.com'],
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
self.assertEqual(ret['status'], 'exists')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
loader = TestLoader()
|
||||||
|
tests = loader.loadTestsFromTestCase(SSHModuleTest)
|
||||||
|
print('Setting up Salt daemons to execute tests')
|
||||||
|
with TestDaemon():
|
||||||
|
runner = TextTestRunner(verbosity=1).run(tests)
|
||||||
|
sys.exit(runner.wasSuccessful())
|
73
tests/integration/states/ssh.py
Normal file
73
tests/integration/states/ssh.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
'''
|
||||||
|
Test the ssh_known_hosts state
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import integration
|
||||||
|
|
||||||
|
|
||||||
|
KNOWN_HOSTS = os.path.join(integration.TMP, 'known_hosts')
|
||||||
|
GITHUB_FINGERPRINT = '16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKnownHostsStateTest(integration.ModuleCase):
|
||||||
|
'''
|
||||||
|
Validate the ssh state
|
||||||
|
'''
|
||||||
|
def tearDown(self):
|
||||||
|
if os.path.isfile(KNOWN_HOSTS):
|
||||||
|
os.remove(KNOWN_HOSTS)
|
||||||
|
super(SSHKnownHostsStateTest, self).tearDown()
|
||||||
|
|
||||||
|
def test_present(self):
|
||||||
|
'''
|
||||||
|
ssh_known_hosts.present
|
||||||
|
'''
|
||||||
|
# save once
|
||||||
|
_ret = self.run_state('ssh_known_hosts.present',
|
||||||
|
name='github.com',
|
||||||
|
user='root',
|
||||||
|
fingerprint=GITHUB_FINGERPRINT,
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
ret = _ret.values()[0]
|
||||||
|
self.assertTrue(ret['result'], ret)
|
||||||
|
# save twice
|
||||||
|
_ret = self.run_state('ssh_known_hosts.present',
|
||||||
|
name='github.com',
|
||||||
|
user='root',
|
||||||
|
fingerprint=GITHUB_FINGERPRINT,
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
ret = _ret.values()[0]
|
||||||
|
self.assertEqual(ret['result'], None, ret)
|
||||||
|
|
||||||
|
def test_present_fail(self):
|
||||||
|
# save something wrong
|
||||||
|
_ret = self.run_state('ssh_known_hosts.present',
|
||||||
|
name='github.com',
|
||||||
|
user='root',
|
||||||
|
fingerprint='aa:bb:cc:dd',
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
ret = _ret.values()[0]
|
||||||
|
self.assertFalse(ret['result'], ret)
|
||||||
|
|
||||||
|
def test_absent(self):
|
||||||
|
'''
|
||||||
|
ssh_known_hosts.absent
|
||||||
|
'''
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(integration.FILES, 'ssh', 'known_hosts'),
|
||||||
|
KNOWN_HOSTS)
|
||||||
|
# remove once
|
||||||
|
_ret = self.run_state('ssh_known_hosts.absent',
|
||||||
|
name='github.com',
|
||||||
|
user='root',
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
ret = _ret.values()[0]
|
||||||
|
self.assertTrue(ret['result'], ret)
|
||||||
|
# remove twice
|
||||||
|
_ret = self.run_state('ssh_known_hosts.absent',
|
||||||
|
name='github.com',
|
||||||
|
user='root',
|
||||||
|
config=KNOWN_HOSTS)
|
||||||
|
ret = _ret.values()[0]
|
||||||
|
self.assertEqual(ret['result'], None, ret)
|
Loading…
Reference in New Issue
Block a user