2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-01-12 19:12:37 +00:00

Merge pull request #58 from jjohnson42/sshkey

SSH known hosts handling
This commit is contained in:
Jarrod Johnson
2016-02-21 15:00:19 -05:00
4 changed files with 49 additions and 9 deletions

View File

@@ -278,4 +278,8 @@ node = {
'description': ('Fingerprint of the TLS certificate recognized as'
'belonging to the hardware manager of the server'),
},
'pubkeys.ssh': {
'description': ('Fingerprint of the SSH key of the OS running on the '
'system.'),
},
}

View File

@@ -77,10 +77,12 @@ class PubkeyInvalid(ConfluentException):
apierrorcode = 502
apierrorstr = '502 - Invalid certificate or key on target'
def __init__(self, text, certificate, fingerprint, attribname):
def __init__(self, text, certificate, fingerprint, attribname, event):
super(PubkeyInvalid, self).__init__(self, text)
self.fingerprint = fingerprint
bodydata = {'fingerprint': fingerprint,
bodydata = {'message': text,
'event': event,
'fingerprint': fingerprint,
'fingerprintfield': attribname,
'certificate': base64.b64encode(certificate)}
self.errorbody = json.dumps(bodydata)

View File

@@ -14,17 +14,50 @@
# See the License for the specific language governing permissions and
# limitations under the License.
__author__ = 'jjohnson2'
# This plugin provides an ssh implementation comforming to the 'console'
# specification. consoleserver or shellserver would be equally likely
# to use this.
import confluent.exceptions as cexc
import confluent.interface.console as conapi
import confluent.log as log
import eventlet
import hashlib
paramiko = eventlet.import_patched('paramiko')
class HostKeyHandler(paramiko.client.MissingHostKeyPolicy):
def __init__(self, configmanager, node):
self.cfm = configmanager
self.node = node
def missing_host_key(self, client, hostname, key):
fingerprint = 'sha512$' + hashlib.sha512(key.asbytes()).hexdigest()
cfg = self.cfm.get_node_attributes(
self.node, ('pubkeys.ssh', 'pubkeys.addpolicy'))
if 'pubkeys.ssh' not in cfg[self.node]:
if ('pubkeys.addpolicy' in cfg[self.node] and
cfg[self.node]['pubkeys.addpolicy'] and
cfg[self.node]['pubkeys.addpolicy']['value'] == 'manual'):
raise cexc.PubkeyInvalid('New ssh key detected',
key.asbytes(), fingerprint,
'pubkeys.ssh', 'newkey')
auditlog = log.Logger('audit')
auditlog.log({'node': self.node, 'event': 'sshautoadd',
'fingerprint': fingerprint})
self.cfm.set_node_attributes(
{self.node: {'pubkeys.ssh': fingerprint}})
return True
elif cfg[self.node]['pubkeys.ssh']['value'] == fingerprint:
return True
raise cexc.PubkeyInvalid(
'Mismatched SSH host key detected', key.asbytes(), fingerprint,
'pubkeys.ssh', 'mismatch'
)
class SshShell(conapi.Console):
def __init__(self, node, config, username='', password=''):
@@ -33,7 +66,7 @@ class SshShell(conapi.Console):
self.nodeconfig = config
self.username = username
self.password = password
self.inputmode = 0 # 0 = username, 1 = password...
self.inputmode = 0 # 0 = username, 1 = password...
def recvdata(self):
while self.connected:
@@ -45,7 +78,7 @@ class SshShell(conapi.Console):
def connect(self, callback):
# for now, we just use the nodename as the presumptive ssh destination
#TODO(jjohnson2): use a 'nodeipget' utility function for architectures
# TODO(jjohnson2): use a 'nodeipget' utility function for architectures
# that would rather not use the nodename as anything but an opaque
# identifier
self.datacallback = callback
@@ -58,7 +91,8 @@ class SshShell(conapi.Console):
def logon(self):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.ssh.set_missing_host_key_policy(
HostKeyHandler(self.nodeconfig, self.node))
try:
self.ssh.connect(self.node, username=self.username,
password=self.password, allow_agent=False,

View File

@@ -82,7 +82,7 @@ class TLSCertVerifier(object):
# manually
raise cexc.PubkeyInvalid('New certificate detected',
certificate, fingerprint,
self.fieldname)
self.fieldname, 'newkey')
# since the policy is not manual, go ahead and add new key
# after logging to audit log
auditlog = log.Logger('audit')
@@ -93,6 +93,6 @@ class TLSCertVerifier(object):
return True
elif storedprint[self.node][self.fieldname]['value'] == fingerprint:
return True
raise cexc.PubKeyInvalid(
raise cexc.PubkeyInvalid(
'Mismatched certificate detected', certificate, fingerprint,
self.fieldname)
self.fieldname, 'mismatch')