|
|
|
@ -50,6 +50,36 @@ def print_config(config):
|
|
|
|
|
else:
|
|
|
|
|
logging.debug(' {}: {}'.format(i, config[section][i]))
|
|
|
|
|
|
|
|
|
|
def create_ssl_context(config):
|
|
|
|
|
'''
|
|
|
|
|
Creates the ssl context.
|
|
|
|
|
return: context object or None
|
|
|
|
|
'''
|
|
|
|
|
context = None
|
|
|
|
|
requirement = None
|
|
|
|
|
required = config['client']['required'].lower()
|
|
|
|
|
if required == 'false':
|
|
|
|
|
requirement = ssl.CERT_NONE
|
|
|
|
|
elif required == 'may':
|
|
|
|
|
requirement = ssl.CERT_OPTIONAL
|
|
|
|
|
else: requirement = ssl.CERT_REQUIRED
|
|
|
|
|
try:
|
|
|
|
|
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
|
|
|
context.verify_mode = requirement
|
|
|
|
|
context.load_cert_chain(certfile=config['server']['cert'],
|
|
|
|
|
keyfile=config['server']['key'])
|
|
|
|
|
context.load_verify_locations(cafile=config['client']['cert'])
|
|
|
|
|
# ensure, compression is disabled (disabled by default anyway at the moment)
|
|
|
|
|
context.options |= ssl.OP_NO_COMPRESSION
|
|
|
|
|
context.options = ssl.PROTOCOL_TLS_SERVER
|
|
|
|
|
context.options = ssl.OP_CIPHER_SERVER_PREFERENCE
|
|
|
|
|
logging.debug('SSL context created')
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('Failed to create SSL context')
|
|
|
|
|
logging.error('Error: {}'.format(e))
|
|
|
|
|
return None
|
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
def print_ciphers(cipherlist):
|
|
|
|
|
'''
|
|
|
|
|
Prints the list of allowed ciphers.
|
|
|
|
@ -108,38 +138,6 @@ def receive_buffer_is_valid(raw_data):
|
|
|
|
|
logging.debug('Argument is not valid: {}'.format(raw_data))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def change_status(status, timestamp, filename):
|
|
|
|
|
'''
|
|
|
|
|
Write the new status together with a timestamp into the Space API JSON.
|
|
|
|
|
param 1: byte object
|
|
|
|
|
param 2: string
|
|
|
|
|
return: boolean
|
|
|
|
|
'''
|
|
|
|
|
logging.debug('Change status API')
|
|
|
|
|
# todo: use walrus operator := when migrating to python >= 3.8
|
|
|
|
|
data = read_api(filename)
|
|
|
|
|
if data is False:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if os.access(filename, os.W_OK):
|
|
|
|
|
logging.debug('API file is writable')
|
|
|
|
|
with open(filename, 'w') as api_file:
|
|
|
|
|
logging.debug('API file open successfull')
|
|
|
|
|
data["state"]["open"] = status
|
|
|
|
|
data["state"]["lastchange"] = timestamp
|
|
|
|
|
try:
|
|
|
|
|
json.dump(data, api_file, indent=4)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('Failed to change API file')
|
|
|
|
|
logging.error('{}'.format(e))
|
|
|
|
|
return False
|
|
|
|
|
logging.debug('API file changed')
|
|
|
|
|
else:
|
|
|
|
|
logging.error('API file is not writable. Wrong permissions?')
|
|
|
|
|
return False
|
|
|
|
|
logging.info('API file successfull changed to {}'.format(status))
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def read_api(api):
|
|
|
|
|
'''
|
|
|
|
|
Reads the Space API JSON into a dict. Returns the dict on success and
|
|
|
|
@ -156,15 +154,47 @@ def read_api(api):
|
|
|
|
|
|
|
|
|
|
logging.debug('API is readable')
|
|
|
|
|
with open(api, 'r') as api_file:
|
|
|
|
|
logging.debug('API file successfully opened')
|
|
|
|
|
logging.debug('API file successfully readable opened')
|
|
|
|
|
try:
|
|
|
|
|
api_json_data = json.load(api_file)
|
|
|
|
|
logging.debug('API file read successfull')
|
|
|
|
|
logging.debug('API file successfully read')
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('Failed to read API file: {}'.format(e))
|
|
|
|
|
return False
|
|
|
|
|
return api_json_data
|
|
|
|
|
|
|
|
|
|
def change_status(status, timestamp, filename):
|
|
|
|
|
'''
|
|
|
|
|
Write the new status together with a timestamp into the Space API JSON.
|
|
|
|
|
param 1: byte object
|
|
|
|
|
param 2: string
|
|
|
|
|
return: boolean
|
|
|
|
|
'''
|
|
|
|
|
logging.debug('Change status API')
|
|
|
|
|
# todo: use walrus operator := when migrating to python >= 3.8
|
|
|
|
|
data = read_api(filename)
|
|
|
|
|
if data is False:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if os.access(filename, os.W_OK):
|
|
|
|
|
logging.debug('API file is writable')
|
|
|
|
|
with open(filename, 'w') as api_file:
|
|
|
|
|
logging.debug('API file successfull writable opened')
|
|
|
|
|
data["state"]["open"] = status
|
|
|
|
|
data["state"]["lastchange"] = timestamp
|
|
|
|
|
try:
|
|
|
|
|
json.dump(data, api_file, indent=4)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('Failed to change API file')
|
|
|
|
|
logging.error('{}'.format(e))
|
|
|
|
|
return False
|
|
|
|
|
logging.debug('API file changed')
|
|
|
|
|
else:
|
|
|
|
|
logging.error('API file is not writable. Wrong permissions?')
|
|
|
|
|
return False
|
|
|
|
|
logging.info('API file successfull changed to {}'.format(status))
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def get_status_and_time(raw_data):
|
|
|
|
|
'''
|
|
|
|
|
Create a timestamp, changes the value of the given byte into a string
|
|
|
|
@ -283,7 +313,8 @@ def main():
|
|
|
|
|
'key': './certs/server.key'
|
|
|
|
|
},
|
|
|
|
|
'client': {
|
|
|
|
|
'cert': './certs/client.crt'
|
|
|
|
|
'cert': './certs/client.crt',
|
|
|
|
|
'required': 'true'
|
|
|
|
|
},
|
|
|
|
|
'api': {
|
|
|
|
|
'api': './api',
|
|
|
|
@ -320,16 +351,11 @@ def main():
|
|
|
|
|
logging.error('Cert check failed\nExit')
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
|
|
|
context.verify_mode = ssl.CERT_OPTIONAL
|
|
|
|
|
context.load_cert_chain(certfile=config['server']['cert'],
|
|
|
|
|
keyfile=config['server']['key'])
|
|
|
|
|
context.load_verify_locations(cafile=config['client']['cert'])
|
|
|
|
|
context.options = ssl.OP_CIPHER_SERVER_PREFERENCE
|
|
|
|
|
# ensure, compression is disabled (disabled by default anyway at the moment)
|
|
|
|
|
context.options |= ssl.OP_NO_COMPRESSION
|
|
|
|
|
logging.debug('SSL context created')
|
|
|
|
|
# ssl context erstellen
|
|
|
|
|
context = create_ssl_context(config)
|
|
|
|
|
if context is not None:
|
|
|
|
|
print_context(context)
|
|
|
|
|
else: sys.exit(2)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# tcp socket öffnen => MySocket
|
|
|
|
@ -347,15 +373,15 @@ def main():
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('Unable to bind and listen')
|
|
|
|
|
logging.error('{}'.format(e))
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
sys.exit(3)
|
|
|
|
|
# endlos auf verbindungen warten => ClientSocket
|
|
|
|
|
while True:
|
|
|
|
|
ClientSocket, ClientAddress = MySocket.accept()
|
|
|
|
|
logging.info('Client connected: {}:{}'.format(ClientAddress[0], ClientAddress[1]))
|
|
|
|
|
# die verbindung in den ssl-context verpacken => Connection
|
|
|
|
|
with context.wrap_socket(ClientSocket, server_side=True) as Connection:
|
|
|
|
|
logging.info('SSL Connection established')
|
|
|
|
|
try:
|
|
|
|
|
Connection = context.wrap_socket(ClientSocket, server_side=True)
|
|
|
|
|
logging.info('SSL Connection established')
|
|
|
|
|
Connection.settimeout(float(config['general']['timeout']))
|
|
|
|
|
logging.debug('Connection timeout set to {}'.format(config['general']['timeout']))
|
|
|
|
|
cert = Connection.getpeercert(binary_form=False)
|
|
|
|
@ -384,13 +410,16 @@ def main():
|
|
|
|
|
Connection.close()
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
logging.info('Keyboard interrupt received')
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('{}'.format(e))
|
|
|
|
|
finally:
|
|
|
|
|
if MySocket:
|
|
|
|
|
MySocket.close()
|
|
|
|
|
logging.debug('TCP socket closed')
|
|
|
|
|
sys.exit(255)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error('{}'.format(e))
|
|
|
|
|
if MySocket:
|
|
|
|
|
MySocket.close()
|
|
|
|
|
logging.debug('TCP socket closed')
|
|
|
|
|
sys.exit(254)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|