Saltstack Official OpenSSH Formula
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

known_hosts_salt_ssh.sls 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!py
  2. import logging
  3. import os.path
  4. import re
  5. import subprocess
  6. cache = {}
  7. ssh_key_pattern = re.compile("^[^ ]+ (ssh-.+)$")
  8. log = logging.getLogger(__name__)
  9. def config_dir():
  10. if '__master_opts__' in __opts__:
  11. # run started via salt-ssh
  12. return __opts__['__master_opts__']['config_dir']
  13. else:
  14. # run started via salt
  15. return __opts__['config_dir']
  16. def cache_dir():
  17. if '__master_opts__' in __opts__:
  18. # run started via salt-ssh
  19. return __opts__['__master_opts__']['cachedir']
  20. else:
  21. # run started via salt
  22. return __opts__['cachedir']+'/../master'
  23. def minions():
  24. if not 'minions' in cache:
  25. cache['minions'] = __salt__.slsutil.renderer(config_dir() + '/roster')
  26. return cache['minions']
  27. def host_variants(minion):
  28. _variants = [minion]
  29. def add_port_variant(host):
  30. if 'port' in minions()[minion]:
  31. _variants.append("[{}]:{}".format(host, minions()[minion]['port']))
  32. add_port_variant(minion)
  33. if 'host' in minions()[minion]:
  34. host = minions()[minion]['host']
  35. _variants.append(host)
  36. add_port_variant(host)
  37. return _variants
  38. def host_keys_from_known_hosts(minion, path):
  39. '''
  40. Fetches all host keys of the given minion.
  41. '''
  42. if not os.path.isfile(path):
  43. return []
  44. pubkeys = []
  45. def fill_pubkeys(host):
  46. for line in host_key_of(host, path).splitlines():
  47. match = ssh_key_pattern.search(line)
  48. if match:
  49. pubkeys.append(match.group(1))
  50. # Try the minion ID and its variants first
  51. for host in host_variants(minion):
  52. fill_pubkeys(host)
  53. # When no keys were found ...
  54. if not pubkeys:
  55. # ... fetch IP addresses via DNS and try them.
  56. for host in (salt['dig.A'](minion) + salt['dig.AAAA'](minion)):
  57. fill_pubkeys(host)
  58. # When not a single key was found anywhere:
  59. if not pubkeys:
  60. log.error("No SSH host key found for {}. "
  61. "You may need to add it to {}.".format(minion, path))
  62. return "\n".join(pubkeys)
  63. def host_key_of(host, path):
  64. cmd = ["ssh-keygen", "-H", "-F", host, "-f", path]
  65. call = subprocess.Popen(
  66. cmd,
  67. stdout=subprocess.PIPE,
  68. stderr=subprocess.PIPE
  69. )
  70. out, err = call.communicate()
  71. if err == '':
  72. return out
  73. else:
  74. log.error("{} failed:\nSTDERR: {}\nSTDOUT: {}".format(
  75. " ".join(cmd),
  76. err,
  77. out
  78. ))
  79. return ""
  80. def host_keys(minion_id):
  81. # Get keys from trusted known_hosts file
  82. trusted_keys = host_keys_from_known_hosts(minion_id,
  83. config_dir()+'/known_hosts')
  84. if trusted_keys:
  85. print "trusted_keys"
  86. return trusted_keys
  87. # Get keys from host key cache
  88. cache_file = "{}/known_hosts_salt_ssh/{}.pub".format(cache_dir(), minion_id)
  89. try:
  90. with open(cache_file, 'r') as f:
  91. return f.read()
  92. except IOError:
  93. return ''
  94. def run():
  95. cache = {} # clear the cache
  96. config = {
  97. 'public_ssh_host_keys': {},
  98. 'public_ssh_host_names': {}
  99. }
  100. for minion in minions().keys():
  101. config['public_ssh_host_keys'][minion] = host_keys(minion)
  102. config['public_ssh_host_names'][minion] = minion
  103. return {'openssh': {'known_hosts': {'salt_ssh': config}}}
  104. # vim: ts=4:sw=4:syntax=python