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:
parent
5f9ee3d3c5
commit
1b912c4365
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
82
confluent_server/confluent/swarm/manager.py
Normal file
82
confluent_server/confluent/swarm/manager.py
Normal 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))
|
Loading…
Reference in New Issue
Block a user