mirror of
https://github.com/saltstack-formulas/openssh-formula.git
synced 2026-09-21 17:07:38 +02:00
Pillar openssh.known_hosts_salt_ssh (#128)
* Pillar openssh.known_hosts_salt_ssh * Dropped ill-named file * Fixed aliasing of host names * Improved pillar.example * Opt-in to include localhost * pillar/known_hosts_salt_ssh: clear cache in run() * Dropped forgotten debugging output
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
#!py
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
cache = {}
|
||||
ssh_key_pattern = re.compile("^[^ ]+ (ssh-.+)$")
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def config_dir():
|
||||
if '__master_opts__' in __opts__:
|
||||
# run started via salt-ssh
|
||||
return __opts__['__master_opts__']['config_dir']
|
||||
else:
|
||||
# run started via salt
|
||||
return __opts__['config_dir']
|
||||
|
||||
def cache_dir():
|
||||
if '__master_opts__' in __opts__:
|
||||
# run started via salt-ssh
|
||||
return __opts__['__master_opts__']['cachedir']
|
||||
else:
|
||||
# run started via salt
|
||||
return __opts__['cachedir']+'/../master'
|
||||
|
||||
def minions():
|
||||
if not 'minions' in cache:
|
||||
cache['minions'] = __salt__.slsutil.renderer(config_dir() + '/roster')
|
||||
return cache['minions']
|
||||
|
||||
def host_variants(minion):
|
||||
_variants = [minion]
|
||||
def add_port_variant(host):
|
||||
if 'port' in minions()[minion]:
|
||||
_variants.append("[{}]:{}".format(host, minions()[minion]['port']))
|
||||
add_port_variant(minion)
|
||||
if 'host' in minions()[minion]:
|
||||
host = minions()[minion]['host']
|
||||
_variants.append(host)
|
||||
add_port_variant(host)
|
||||
return _variants
|
||||
|
||||
def host_keys_from_known_hosts(minion, path):
|
||||
'''
|
||||
Fetches all host keys of the given minion.
|
||||
'''
|
||||
if not os.path.isfile(path):
|
||||
return []
|
||||
pubkeys = []
|
||||
def fill_pubkeys(host):
|
||||
for line in host_key_of(host, path).splitlines():
|
||||
match = ssh_key_pattern.search(line)
|
||||
if match:
|
||||
pubkeys.append(match.group(1))
|
||||
# Try the minion ID and its variants first
|
||||
for host in host_variants(minion):
|
||||
fill_pubkeys(host)
|
||||
# When no keys were found ...
|
||||
if not pubkeys:
|
||||
# ... fetch IP addresses via DNS and try them.
|
||||
for host in (salt['dig.A'](minion) + salt['dig.AAAA'](minion)):
|
||||
fill_pubkeys(host)
|
||||
# When not a single key was found anywhere:
|
||||
if not pubkeys:
|
||||
log.error("No SSH host key found for {}. "
|
||||
"You may need to add it to {}.".format(minion, path))
|
||||
return "\n".join(pubkeys)
|
||||
|
||||
def host_key_of(host, path):
|
||||
cmd = ["ssh-keygen", "-H", "-F", host, "-f", path]
|
||||
call = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
out, err = call.communicate()
|
||||
if err == '':
|
||||
return out
|
||||
else:
|
||||
log.error("{} failed:\nSTDERR: {}\nSTDOUT: {}".format(
|
||||
" ".join(cmd),
|
||||
err,
|
||||
out
|
||||
))
|
||||
return ""
|
||||
|
||||
def host_keys(minion_id):
|
||||
# Get keys from trusted known_hosts file
|
||||
trusted_keys = host_keys_from_known_hosts(minion_id,
|
||||
config_dir()+'/known_hosts')
|
||||
if trusted_keys:
|
||||
print "trusted_keys"
|
||||
return trusted_keys
|
||||
# Get keys from host key cache
|
||||
cache_file = "{}/known_hosts_salt_ssh/{}.pub".format(cache_dir(), minion_id)
|
||||
try:
|
||||
with open(cache_file, 'r') as f:
|
||||
return f.read()
|
||||
except IOError:
|
||||
return ''
|
||||
|
||||
def run():
|
||||
cache = {} # clear the cache
|
||||
config = {
|
||||
'public_ssh_host_keys': {},
|
||||
'public_ssh_host_names': {}
|
||||
}
|
||||
for minion in minions().keys():
|
||||
config['public_ssh_host_keys'][minion] = host_keys(minion)
|
||||
config['public_ssh_host_names'][minion] = minion
|
||||
return {'openssh': {'known_hosts': {'salt_ssh': config}}}
|
||||
|
||||
# vim: ts=4:sw=4:syntax=python
|
||||
Reference in New Issue
Block a user