2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-22 09:32:21 +00:00

Further advance the swarm concept

This marks the start of attempting to connect the invitation
to sockets and using the invitation to measure the certificates as
well as proving client knowledge of an invitation token.
This commit is contained in:
Jarrod Johnson 2018-04-24 14:44:45 -04:00
parent 5f9ee3d3c5
commit 1b912c4365
5 changed files with 90 additions and 62 deletions

View File

@ -1,55 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2018 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This handles the process of generating and tracking/validating invites
import base64
import hashlib
import hmac
import os
pending_invites = {}
def create_server_invitation(servername):
invitation = os.urandom(66)
pending_invites[servername] = invitation
return base64.b64encode(invitation)
def create_client_proof(invitation, mycert, peercert):
return hmac.new(invitation, peercert + mycert, hashlib.sha256).digest()
def check_server_proof(invitation, mycert, peercert, proof):
validproof = hmac.new(invitation, mycert + peercert, hashlib.sha256
).digest()
return proof == validproof
def check_client_proof(servername, mycert, peercert, proof):
invitation = pending_invites[servername]
validproof = hmac.new(invitation, mycert + peercert, hashlib.sha256
).digest()
if proof == validproof:
# We know that the client knew the secret, and that it measured our
# certificate, and thus calling code can bless the certificate, and
# we can forget the invitation
del pending_invites[servername]
# We now want to prove to the client that we also know the secret,
# and that we measured their certificate well
# Now to generate an answer...., reverse the cert order so our answer
# is different, but still proving things
return hmac.new(invitation, peercert + mycert, hashlib.sha256
).digest()
# The given proof did not verify the invitation
return False

View File

@ -44,7 +44,7 @@ import confluent.exceptions as exc
import confluent.log as log
import confluent.core as pluginapi
import confluent.shellserver as shellserver
import confluent.swarm.manager as swarm
tracelog = None
auditlog = None
@ -66,9 +66,9 @@ try:
# so we need to ffi that in using a strategy compatible with PyOpenSSL
import OpenSSL.SSL as libssln
from OpenSSL._util import ffi
import OpenSSL.crypto as crypto
except ImportError:
libssl = None
ffi = None
plainsocket = None
@ -104,9 +104,6 @@ def sessionhdl(connection, authname, skipauth=False, cert=None):
authenticated = False
authdata = None
cfm = None
if cert:
print(repr(crypto.dump_certificate(crypto.FILETYPE_ASN1,
cert)))
if skipauth:
authenticated = True
cfm = configmanager.ConfigManager(tenant=None, username=authname)
@ -119,6 +116,8 @@ def sessionhdl(connection, authname, skipauth=False, cert=None):
while not authenticated: # prompt for name and passphrase
send_data(connection, {'authpassed': 0})
response = tlvdata.recv(connection)
if 'swarm' in response:
return swarm.handle_connection(connection, cert, response['swarm'])
authname = response['username']
passphrase = response['password']
# note(jbjohnso): here, we need to authenticate, but not
@ -134,6 +133,8 @@ def sessionhdl(connection, authname, skipauth=False, cert=None):
cfm = authdata[1]
send_data(connection, {'authpassed': 1})
request = tlvdata.recv(connection)
if 'swarm' in request and skipauth:
swarm.handle_connection(connection, None, request['swarm'], local=True)
while request is not None:
try:
process_request(
@ -192,7 +193,7 @@ def process_request(connection, request, cfm, authdata, authname, skipauth):
if operation == 'start':
return start_term(authname, cfm, connection, params, path,
authdata, skipauth)
elif operation == 'shutdown':
elif operation == 'shutdown' and skipauth:
configmanager.ConfigManager.shutdown()
else:
hdlr = pluginapi.handle_path(path, operation, cfm, params)

View File

@ -25,7 +25,7 @@ pending_invites = {}
def create_server_invitation(servername):
invitation = os.urandom(66)
pending_invites[servername] = invitation
return base64.b64encode(invitation)
return base64.b64encode(servername + '@' + invitation)
def create_client_proof(invitation, mycert, peercert):
return hmac.new(invitation, peercert + mycert, hashlib.sha256).digest()

View File

@ -0,0 +1,82 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2018 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import base64
import confluent.swarm.invites as invites
import confluent.tlvdata as tlvdata
import confluent.util as util
import eventlet.green.socket as socket
import eventlet.green.ssl as ssl
try:
import OpenSSL.crypto as crypto
except ImportError:
# while not always required, we use pyopenssl required for at least swarm
crypto = None
swarmcerts = {}
def handle_connection(connection, cert, swarmrequest, local=False):
operation = swarmrequest['operation']
if cert:
cert = crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)
else:
if not local:
return
if 'invite' == operation:
name = swarmrequest['invite']['name']
invitation = invites.create_server_invitation(name)
tlvdata.send(connection, {'swarm': {'invitation': invitation}})
if 'join' == operation:
invitation = swarmrequest['invitation']
invitation = base64.b64decode(invitation)
name, invitation = invitation.split('@')
host = swarmrequest['server']
remote = socket.create_connection((host, 13001))
# This isn't what it looks like. We do CERT_NONE to disable
# openssl verification, but then use the invitation as a
# shared secret to validate the certs as part of the join
# operation
remote = ssl.wrap_socket(remote, cert_reqs=ssl.CERT_NONE,
keyfile='/etc/confluent/privkey.pem',
certfile='/etc/confluent/srvcert.pem')
mycert = util.get_certificate_from_file(
'/etc/confluent/srvcert.pem')
cert = remote.getpeercert(binary_form=True)
proof = base64.b64encode(invites.create_client_proof(
invitation, mycert, cert))
tlvdata.recv(remote) # ignore banner
tlvdata.recv(remote) # ignore authpassed: 0
tlvdata.send(remote, {'swarm': {'operation': 'joinchallenge',
'name': name, 'hmac': proof}})
rsp = tlvdata.recv(remote)
proof = rsp['swarm']['approval']
j = invites.check_server_proof(invitation, mycert, cert, proof)
if not j:
return
if 'joinchallenge' == operation:
mycert = util.get_certificate_from_file('/etc/confluent/srvcert.pem')
proof = base64.b64decode(swarmrequest['hmac'])
myrsp = invites.check_client_proof(swarmrequest['name'], mycert,
cert, proof)
if not myrsp:
connection.close()
return
myrsp = base64.b64encode(myrsp)
swarmcerts[swarmrequest['name']] = cert
tlvdata.send(connection, {'swarm': {'approval': myrsp}})
clientready = tlvdata.recv(connection)
print(repr(clientready))