From 9894af021e40ee01a99d1a06f7088861eab5205a Mon Sep 17 00:00:00 2001 From: example Date: Sun, 10 Oct 2021 23:34:24 +0200 Subject: [PATCH 01/25] add error handling for argument and class creation --- source/server/setstatus.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/server/setstatus.py b/source/server/setstatus.py index f62a2e3..fc615be 100755 --- a/source/server/setstatus.py +++ b/source/server/setstatus.py @@ -60,6 +60,11 @@ class SetStatus: """ return: boolean """ + try: + self.status = int(self.status) + except Exception as e: + self.log.error('Status argument does not represent a integer') + return False if self.status in (0, 1): self.log.debug('Set value to {}'.format(self.status)) self.status = bytes([self.status]) @@ -233,3 +238,12 @@ class SetStatus: self.log.error('Error: {}'.format(e)) exit(7) + +if __name__ == '__main__': + s = SetStatus() + if len(argv) < 2: + log.error('Usage: setstatus.py <0|1>') + exit(255) + else: + s.run(argv[1]) + -- 2.39.5 From 5f3bb44c7b9035e4828c7f1069b2427a19586eaa Mon Sep 17 00:00:00 2001 From: example Date: Sun, 6 Mar 2022 11:55:29 +0100 Subject: [PATCH 02/25] verarbeitung der statusdaten umgestellt, code verschlankt statusdaten werden jetzt mit den funktionen encode() und decode() verarbeitet, antwort des servers als variable, finaly klausel wieder entfernt --- source/server/apistatusd.py | 41 ++++++++++++------------------------- source/server/setstatus.py | 18 +++++++--------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index eff1010..a59b555 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# file: statusd.py +# file: apistatusd.py # date: 26.07.2019 # email: berhsi@web.de @@ -76,13 +76,12 @@ def display_peercert(cert): def receive_buffer_is_valid(raw_data): ''' Checks validity of the received buffer contents. - param 1: byte + param 1: byte object return: boolean ''' - if raw_data in (b'\x00', b'\x01'): + if raw_data.decode('utf-8', 'strict') in ('0', '1'): logging.debug('Argument is valid: {}'.format(raw_data)) return True - logging.debug('Argument is not valid: {}'.format(raw_data)) return False @@ -90,7 +89,7 @@ def receive_buffer_is_valid(raw_data): def change_status(raw_data, api): ''' Write the new status together with a timestamp into the Space API JSON. - param 1: byte + param 1: byte object param 2: string return: boolean ''' @@ -113,6 +112,7 @@ def change_status(raw_data, api): 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?') @@ -152,10 +152,10 @@ def set_values(raw_data): ''' Create a timestamp, changes the value of the given byte into a string and returns both. - param 1: byte + param 1: byte object return: tuple ''' - status = True if raw_data == b'\x01' else False + status = True if raw_data.decode('utf-8', 'strict') == '1' else False timestamp = int(str(time()).split('.')[0]) logging.debug('Set values for timestamp: {} and status: {}'.format( @@ -173,6 +173,7 @@ def main(): (cve-2011-3389) ''' + answer = '3'.encode(encoding='utf-8', errors='strict') loglevel = logging.WARNING formatstring = '%(asctime)s: %(levelname)s: %(message)s' logging.basicConfig(format=formatstring, level=loglevel) @@ -196,7 +197,7 @@ def main(): 'template': './api_template' } } - configfile = './statusd.conf' + configfile = './apistatusd.conf' config = configparser.ConfigParser() config.read_dict(default_config) if not config.read(configfile): @@ -273,19 +274,10 @@ def main(): raw_data = conn.recv(1) if receive_buffer_is_valid(raw_data) is True: if change_status(raw_data, config['api']['api']) is True: - logging.debug('Send {} back'.format(raw_data)) - conn.send(raw_data) - # change_status returns false: - else: - logging.info('Failed to change status') - if conn: - conn.send(b'\x03') - # receive_handle returns false: - else: - logging.info('Invalid argument received: {}'.format(raw_data)) - logging.debug('Send {} back'.format(b'\x03')) - if conn: - conn.send(b'\x03') + answer = raw_data + if conn: + logging.debug('Send {} back'.format(raw_data)) + conn.send(answer) sleep(0.1) # protection against dos except KeyboardInterrupt: logging.info('Keyboard interrupt received') @@ -293,13 +285,6 @@ def main(): except Exception as e: logging.error('{}'.format(e)) continue - finally: - if mySocket: - logging.info('Shutdown socket') - try: - mySocket.shutdown(socket.SHUT_RDWR) - except Exception as e: - logging.error(f'Error while shutdown socket: {e}') return 0 diff --git a/source/server/setstatus.py b/source/server/setstatus.py index fc615be..e0a36d2 100755 --- a/source/server/setstatus.py +++ b/source/server/setstatus.py @@ -60,15 +60,10 @@ class SetStatus: """ return: boolean """ - try: - self.status = int(self.status) - except Exception as e: - self.log.error('Status argument does not represent a integer') - return False - if self.status in (0, 1): - self.log.debug('Set value to {}'.format(self.status)) - self.status = bytes([self.status]) + if self.status in ('0', '1'): + self.log.debug('Set status to {}'.format(self.status)) return True + self.log.debug('{} is not a valid status'.format(self.status)) return False def set_config(self): @@ -197,7 +192,6 @@ class SetStatus: # check given status if self.check_status() is False: - self.log.error('No valid status given') exit(1) # log config if level is debug @@ -222,12 +216,14 @@ class SetStatus: # send status try: self.log.debug('Send new status: {}'.format(self.status)) - self.connection.send(self.status) + self.connection.send(self.status.encode(encoding='utf-8', + errors='strict')) except Exception as e: self.log.error('Error: {}'.format(e)) exit(6) try: - response = self.connection.recv(1) + response = self.connection.recv(1).decode(encoding='utf-8', + errors='strict') self.log.debug('Server returns: {}'.format(response)) if response == self.status: self.log.info('Status sucessfull updated') -- 2.39.5 From 58d9c327c9b8d525e28e6ccd54a18604bdbb643a Mon Sep 17 00:00:00 2001 From: example Date: Fri, 11 Mar 2022 13:28:03 +0100 Subject: [PATCH 03/25] =?UTF-8?q?debugausgaben=20f=C3=BCr=20den=20ssl-cont?= =?UTF-8?q?ext=20hinzu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/server/apistatusd.py | 62 ++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index a59b555..563960f 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -43,35 +43,51 @@ def print_config(config): for i in config[section]: logging.debug(' {}: {}'.format(i, config[section][i])) - def print_ciphers(cipherlist): ''' Prints the list of allowed ciphers. param1: dictionary return: boolean ''' - print('Available ciphers') + logging.debug('Available ciphers') for i in cipherlist: - print('\n') for j in i.keys(): - print('{}: {}'.format(j, i[j])) - print('\n') + if j in ('name', 'protocol'): + logging.debug('{}: {}'.format(j, i[j])) +def print_context(ctx): + ''' + Prints the ssl settings for the given ssl context. + param1: context object + ''' + logging.debug('----------- context ----------------') + logging.debug('Minimum version ssl: {}'.format(ctx.minimum_version)) + logging.debug('Maximum version ssl: {}'.format(ctx.maximum_version)) + logging.debug('SSL options enabled: {}'.format(ctx.options)) + logging.debug('Protocol: {}'.format(ctx.protocol)) + logging.debug('Verify flags certificates: {}'.format(ctx.verify_flags)) + logging.debug('Verify mode: {}'.format(ctx.verify_mode)) + print_ciphers(ctx.get_ciphers()) + logging.debug('------------------------------------') def display_peercert(cert): ''' Displays the values of a given certificate. - param1: dictionary - return: boolean + param1: dictionary or none ''' - for i in cert.keys(): - print('{}:'.format(i)) - if i in ('subject', 'issuer'): - for j in cert[i]: - print('\t{}'.format(j)) - else: - print('\t{}'.format(cert[i])) - + if cert == None: + logging.debug('Peer does not offer a certificate') + elif len(cert) == 0: + logging.debug('Peer certificate was not valid') + else: + logging.debug('Peer certificate commonName: {}'.format( + cert['subject'][5][0][1])) + logging.debug('Peer certificate serialNumber: {}'.format( + cert['serialNumber'])) + logging.debug('Peer certificate notBefore: {}'.format( + cert['notBefore'])) + logging.debug('Peer certificate notAfter: {}'.format( + cert['notAfter'])) def receive_buffer_is_valid(raw_data): ''' @@ -225,12 +241,11 @@ def main(): context.load_cert_chain(certfile=config['server']['cert'], keyfile=config['server']['key']) context.load_verify_locations(cafile=config['client']['cert']) - context.set_ciphers('EECDH+AESGCM') # only ciphers for tls 1.2 and 1.3 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') - # print_ciphers(context.get_ciphers()) + print_context(context) with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as mySocket: logging.debug('Socket created') @@ -263,13 +278,18 @@ def main(): conn.settimeout(float(config['general']['timeout'])) except socket.timeout: logging.error('Socket timeout') + continue except Exception as e: logging.error('Connection failed: {}'.format(e)) + continue logging.info('Connection established') - logging.info('Peer certificate commonName: {}'.format( - conn.getpeercert()['subject'][5][0][1])) - logging.debug('Peer certificate serialNumber: {}'.format( - conn.getpeercert()['serialNumber'])) + try: + cert = conn.getpeercert(binary_form=False) + display_peercert(cert) + except ValueError: + logging.debug('SSL handshake has not been done yet') + except Exception as e: + logging.debug('Unexpected error: {}'.format(e)) raw_data = conn.recv(1) if receive_buffer_is_valid(raw_data) is True: -- 2.39.5 From 804e9a10e56e6b71c2220a2d27f630b8806d199c Mon Sep 17 00:00:00 2001 From: example Date: Wed, 6 Apr 2022 10:20:06 +0200 Subject: [PATCH 04/25] clientauthentifizierung auf optional gesetzt --- source/server/apistatusd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 563960f..a4c7244 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -237,7 +237,7 @@ def main(): sys.exit(1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - context.verify_mode = ssl.CERT_REQUIRED + 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']) -- 2.39.5 From e79258b8bee9b04a1dbf4bafa7b976bd0f0ee314 Mon Sep 17 00:00:00 2001 From: example Date: Wed, 6 Apr 2022 10:22:36 +0200 Subject: [PATCH 05/25] zertifikate umbenannt --- source/server/apistatusd.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/server/apistatusd.conf b/source/server/apistatusd.conf index 73433e8..e0306de 100644 --- a/source/server/apistatusd.conf +++ b/source/server/apistatusd.conf @@ -14,11 +14,11 @@ loglevel = debug [server] host = localhost port = 10001 -cert = ./certs/server-pub.pem -key = ./certs/server-key.pem +cert = ./certs/statusd-pub.pem +key = ./certs/statusd-key.pem [client] -cert = ./certs/client-pub.pem +cert = ./certs/statusclient-pub.pem [api] api = ./api -- 2.39.5 From b6acaa08a831f64b1cd17523752985b050a0f721 Mon Sep 17 00:00:00 2001 From: example Date: Wed, 6 Apr 2022 10:39:15 +0200 Subject: [PATCH 06/25] =?UTF-8?q?kommentare=20eingef=C3=BCgt,=20fehlerbeha?= =?UTF-8?q?ndlung=20ge=C3=A4ndert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/server/setstatus.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/source/server/setstatus.py b/source/server/setstatus.py index e0a36d2..eb9f3cf 100755 --- a/source/server/setstatus.py +++ b/source/server/setstatus.py @@ -58,6 +58,7 @@ class SetStatus: def check_status(self): """ + checkes, if the self.status variable is a valid value return: boolean """ if self.status in ('0', '1'): @@ -68,6 +69,8 @@ class SetStatus: def set_config(self): """ + Tries to read and use the values from the configuration file. If + this failes, we still use the default values. """ self.log = logging.getLogger() # read config file @@ -89,7 +92,8 @@ class SetStatus: def check_certs(self, certs): """ - Check if certs readable. + Check if certs are readable. + return: boolean """ self.log.debug('Check certificates') for certfile in certs: @@ -111,25 +115,28 @@ class SetStatus: def create_ssl_context(self): """ + Creates SSL context + return: context object or false """ - context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, - cafile=self.config['server']['cert']) - if not context: + try: + context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) + except Exception as e: self.log.error('Failed to create SSL Context') return False + context.load_verify_locations(cafile=self.config['server']['cert']) + context.load_cert_chain(certfile=self.config['client']['cert'], + keyfile=self.config['client']['key']) context.set_ciphers('EECDH+AESGCM') # only ciphers for tls 1.2 and 1.3 context.options |= getattr(ssl._ssl, 'OP_NO_COMPRESSION', 0) - try: - context.load_cert_chain(certfile=self.config['client']['cert'], - keyfile=self.config['client']['key']) - except Exception as e: - self.log.error('Failed to load cert chain') - return False; self.log.debug('SSL context created') return context def create_ssl_socket(self, config, context): """ + Opens a socket and wrapes the socket into the given ssl context. + param1: dictionary + param2: ssl context + return: ssl-socket or false """ bare_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) if not bare_socket: @@ -210,7 +217,7 @@ class SetStatus: if self.context is False: exit(3) - # get connection + # get a ssl encrypted connection self.connection = self.create_ssl_connection() # send status -- 2.39.5 From 56742d87077f54546d0333a86239120814f936e3 Mon Sep 17 00:00:00 2001 From: example Date: Wed, 6 Apr 2022 10:43:16 +0200 Subject: [PATCH 07/25] veralteten kommentar entfernt --- source/server/setstatus.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/server/setstatus.py b/source/server/setstatus.py index eb9f3cf..e6fccfa 100755 --- a/source/server/setstatus.py +++ b/source/server/setstatus.py @@ -9,8 +9,7 @@ # krautspaces doorstatus. # client, who connects to the statusserver at port 10001 to update the -# krautspace door status. If no status is given as argument, he reads from -# stdin until input is 0 or 1. +# krautspace door status. import os import ssl -- 2.39.5 From 7598b237bffbc60fac9bac4db471fc62121ee763 Mon Sep 17 00:00:00 2001 From: example Date: Wed, 6 Apr 2022 11:37:51 +0200 Subject: [PATCH 08/25] =?UTF-8?q?initialer=20commit=20eines=20statusclient?= =?UTF-8?q?s=20f=C3=BCr=20eine=20NodeMCU?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/nodemcu/statusclient/certs.template | 36 ++++ source/nodemcu/statusclient/config.h | 18 ++ .../nodemcu/statusclient/credentials.template | 17 ++ source/nodemcu/statusclient/statusclient.ino | 202 ++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 source/nodemcu/statusclient/certs.template create mode 100644 source/nodemcu/statusclient/config.h create mode 100644 source/nodemcu/statusclient/credentials.template create mode 100644 source/nodemcu/statusclient/statusclient.ino diff --git a/source/nodemcu/statusclient/certs.template b/source/nodemcu/statusclient/certs.template new file mode 100644 index 0000000..07679bb --- /dev/null +++ b/source/nodemcu/statusclient/certs.template @@ -0,0 +1,36 @@ +/* + * file: certs.template + * desc: This file is part of the Krautspace Doorstatus project. It contains + * certificates for the statusclient.ino programm, that runs on a NodeMCU + * with a ESP8266 chip. + * + * Replace the comments in certificate sections with our own certificates. + */ + + +const char SERVER_CERT[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +/* + * lace for the public server certificate to authenticate the doorstatus + * server. + */ +-----END CERTIFICATE----- +)EOF"; + +const char CLIENT_CERT[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +/* + * Place for the clients (this program) public certificate to authenticate + * client against the server. + */ +-----END CERTIFICATE----- +)EOF"; + +const char CLIENT_KEY[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +/* + * Place for the clients private key file. + */ +-----END CERTIFICATE----- +)EOF"; + diff --git a/source/nodemcu/statusclient/config.h b/source/nodemcu/statusclient/config.h new file mode 100644 index 0000000..a9bbde5 --- /dev/null +++ b/source/nodemcu/statusclient/config.h @@ -0,0 +1,18 @@ +/* + * file: config.h + */ + +/* endpoint */ +#define SERVER_URL "status.kraut.space" +#define SERVER_PORT 10001 + +/* serial interface settings */ +#define BAUD_RATE 115200 +#define DEBUG true + +/* frequence to read the pin */ +#define FREQUENCY 10000 + +/* time server settings */ +#define NTP_URL "pool.ntp.org" +#define TZ_STRING "CET-1CDT,M3.5.0,M10.5.0/3" diff --git a/source/nodemcu/statusclient/credentials.template b/source/nodemcu/statusclient/credentials.template new file mode 100644 index 0000000..2ba711d --- /dev/null +++ b/source/nodemcu/statusclient/credentials.template @@ -0,0 +1,17 @@ +/* + * file: credentials.template + * desc: This file is part of the Krautspace Doorstatus project. It contains + * wifi ssid and passwords for the statusclient.ino programm, that runs on a + * NodeMCU with a ESP8266 chip. + * + * Rename this file into 'credentials.h' and adapt the values to your + * wifi conditions. + */ + +/* wifi credentials */ +#define SSID_1 "your_first__wlan_ssid" +#define PSK_1 "your_first_wlan_passwort" +#define SSID_2 "your_second_wlan_ssid" +#define PSK_2 "your_seconde_wlan_password" +#define SSID_3 "your_third_wlan_ssid" +#define PSK_3 "your_third_wlan_password" diff --git a/source/nodemcu/statusclient/statusclient.ino b/source/nodemcu/statusclient/statusclient.ino new file mode 100644 index 0000000..6ef9ded --- /dev/null +++ b/source/nodemcu/statusclient/statusclient.ino @@ -0,0 +1,202 @@ +/* + * file: statusclient.ino + * desc: This file is part of the Krautspace Doorstatus project. It's the + * main file for a client, who deals with the input from a reed sensor and + * push these values to a server. The code is make to run on a NodeMCU with + * ESP8266 chip. + */ + +#include +#include +#include + +#include "config.h" +#include "certs.h" +#include "credentials.h" + +const int LED_PIN = 16; // D0 +const int REED_PIN = 5; // D1 + +typedef enum { + DOOR_CLOSED = 0, + DOOR_OPEN = 1 +} door_state; +door_state current_door_state = DOOR_CLOSED; +door_state new_door_state = DOOR_CLOSED; + + +void init_serial() { + /* + * set baudrate and debug modus + */ + Serial.begin(BAUD_RATE); + Serial.setDebugOutput(DEBUG); + Serial.println(); + Serial.println("[Srl] Serial interface initialized"); +} + +void init_pins() { + /* + * set gpio for reed sensor and led + */ + pinMode(REED_PIN, INPUT_PULLUP); + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + Serial.println("[Pin] LED and REED initialized"); +} + + +void init_wifi() { + /* + * first turn wifi off and than in access point mode + * maybe turn of is not needed! + */ + ESP8266WiFiMulti wifi; + WiFi.mode(WIFI_OFF); + WiFi.mode(WIFI_STA); + wifi.addAP(SSID_1, PSK_1); + wifi.addAP(SSID_2, PSK_2); + Serial.println("[Wifi] Wifi initialized"); + wifi.run(); + if (WiFi.status() == WL_CONNECTED) { + Serial.print("[Wif] Connected to "); + Serial.println(WiFi.SSID()); + Serial.print("[Wifi] IP: "); + Serial.println(WiFi.localIP()); + } else { + Serial.println("[Wifi] Error: Failed to connect"); + } +} + +door_state read_door_state() { + /* + * die initialisierung des reed-pin mit pullup bewirkt, daß am pin + * 3,3 volt anliegen. die verbindung des pins mit GND sorgt dafür, + * daß die spannung "abfließen" kann. dadurch hat der pin dann den + * status 'low'. + * geschlossene tür -> reed geschlossen -> low + * geöffnete tür -> reed offen -> high + */ + if (digitalRead(REED_PIN) == HIGH) { + return DOOR_OPEN; + } + return DOOR_CLOSED; +} + +void toggle_led(door_state state) { + /* + * turns onboard led on or depends on the door state + */ + if (state == DOOR_OPEN) { + digitalWrite(LED_PIN, LOW); + } else { + digitalWrite(LED_PIN, HIGH); + } + delay(500); +} + +void set_clock() { + + configTime(TZ_STRING, NTP_URL); + + Serial.print("Waiting for NTP time sync: "); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + Serial.print("Current time: "); + Serial.print(asctime(&timeinfo)); +} + +int send_status(door_state state) { + + /* + * geht die initialisierung mit einem byte länge? + * terminiert strcpy den status mit \0? + */ + char status[2] = ""; + + if (state == DOOR_CLOSED) { + strncpy(status, "0", 1); + } else if (state == DOOR_OPEN) { + strncpy(status, "1", 1); + } else { + return 1; + } + + BearSSL::WiFiClientSecure client; + BearSSL::X509List server_cert(SERVER_CERT); + BearSSL::X509List client_cert(CLIENT_CERT); + BearSSL::PrivateKey client_key(CLIENT_KEY); + client.setTrustAnchors(&server_cert); + client.setClientRSACert(&client_cert, &client_key); + delay(500); + Serial.println("[Ctx] SSL Context initialized"); + Serial.print("[Ctx] Free Heap: "); + Serial.println(ESP.getFreeHeap()); + delay(500); + Serial.printf("[Send] Connect to %s:%i\n", SERVER_URL, SERVER_PORT); + client.connect(SERVER_URL, SERVER_PORT); + if (!client.connected()) { + Serial.println("[Send] Can't connect to server"); + Serial.print("[Send] SSL Error: "); + Serial.println(client.getLastSSLError()); + client.stop(); + return 1; + } else { + ESP.resetFreeContStack(); + uint32_t freeStackStart = ESP.getFreeContStack(); + Serial.println("[Send] Connection successful established"); + Serial.printf("[Send] Send status: %s\n", status); + client.write(status); + + } + return 0; +} + + + + + +void setup() { + + /* + * things to do once at boot time + */ + init_serial(); + Serial.print("[Init] Free Heap ( after serial init): "); + Serial.println(ESP.getFreeHeap()); + init_pins(); + Serial.print("[Init] Free Heap (after pins init): "); + Serial.println(ESP.getFreeHeap()); + init_wifi(); + Serial.print("[Init] Free Heap (after wifi init): "); + Serial.println(ESP.getFreeHeap()); + delay(500); + set_clock(); + Serial.print("[Init] Free Heap (after setting clock): "); + Serial.println(ESP.getFreeHeap()); + delay(500); +} + +void loop() { + + /* + * things are running in a endless loop + */ + new_door_state = read_door_state(); + if (new_door_state != current_door_state) { + Serial.printf("[Loop] Status has changed to %i\n", new_door_state); + toggle_led(new_door_state); + send_status(new_door_state); + current_door_state = new_door_state; + } + Serial.print("[Loop] Free Heap: "); + Serial.println(ESP.getFreeHeap()); + delay(FREQUENCY); +} -- 2.39.5 From a1d14235ff8b710be1368b64706f6677c5178c82 Mon Sep 17 00:00:00 2001 From: example Date: Thu, 7 Apr 2022 01:07:44 +0200 Subject: [PATCH 09/25] frequenz der abfrage des reed-pins halbiert --- source/nodemcu/statusclient/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/nodemcu/statusclient/config.h b/source/nodemcu/statusclient/config.h index a9bbde5..b078888 100644 --- a/source/nodemcu/statusclient/config.h +++ b/source/nodemcu/statusclient/config.h @@ -11,7 +11,7 @@ #define DEBUG true /* frequence to read the pin */ -#define FREQUENCY 10000 +#define FREQUENCY 5000 /* time server settings */ #define NTP_URL "pool.ntp.org" -- 2.39.5 From 8f4f6d82d4966ab39594afab7d8a84ea6c5b12e0 Mon Sep 17 00:00:00 2001 From: example Date: Thu, 7 Apr 2022 01:09:05 +0200 Subject: [PATCH 10/25] =?UTF-8?q?kommentare=20eingef=C3=BCgt,=20led=20blin?= =?UTF-8?q?kt=20fehlercodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/nodemcu/statusclient/statusclient.ino | 184 ++++++++++++------- 1 file changed, 122 insertions(+), 62 deletions(-) diff --git a/source/nodemcu/statusclient/statusclient.ino b/source/nodemcu/statusclient/statusclient.ino index 6ef9ded..ed21eb0 100644 --- a/source/nodemcu/statusclient/statusclient.ino +++ b/source/nodemcu/statusclient/statusclient.ino @@ -18,9 +18,9 @@ const int LED_PIN = 16; // D0 const int REED_PIN = 5; // D1 typedef enum { - DOOR_CLOSED = 0, - DOOR_OPEN = 1 -} door_state; + DOOR_CLOSED = 0, + DOOR_OPEN = 1 +} door_state; door_state current_door_state = DOOR_CLOSED; door_state new_door_state = DOOR_CLOSED; @@ -41,30 +41,33 @@ void init_pins() { */ pinMode(REED_PIN, INPUT_PULLUP); pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LOW); + digitalWrite(LED_PIN, HIGH); Serial.println("[Pin] LED and REED initialized"); } void init_wifi() { - /* - * first turn wifi off and than in access point mode - * maybe turn of is not needed! - */ + /* + * Creates the ssl context. Turns wifi off and than into + * access point mode. + * TODO: is 'turn of' needed! + */ ESP8266WiFiMulti wifi; WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_STA); wifi.addAP(SSID_1, PSK_1); - wifi.addAP(SSID_2, PSK_2); + wifi.addAP(SSID_2, NULL); Serial.println("[Wifi] Wifi initialized"); wifi.run(); - if (WiFi.status() == WL_CONNECTED) { - Serial.print("[Wif] Connected to "); - Serial.println(WiFi.SSID()); - Serial.print("[Wifi] IP: "); - Serial.println(WiFi.localIP()); + if (WiFi.status() == WL_CONNECTED) { + Serial.print("[Wif] Connected to "); + Serial.println(WiFi.SSID()); + Serial.print("[Wifi] IP: "); + Serial.println(WiFi.localIP()); + set_clock(); } else { - Serial.println("[Wifi] Error: Failed to connect"); + Serial.println("[Wifi] Error: Failed to connect"); + signal_wifi_failed(); } } @@ -83,41 +86,112 @@ door_state read_door_state() { return DOOR_CLOSED; } -void toggle_led(door_state state) { - /* - * turns onboard led on or depends on the door state +void signal_door_changed() { + /* + * LED signal, if door is opened */ - if (state == DOOR_OPEN) { + uint8_t count = 2; + for(uint8_t i=0; i!= count; ++i) { digitalWrite(LED_PIN, LOW); - } else { + delay(100); digitalWrite(LED_PIN, HIGH); + delay(100); } - delay(500); +} + +void signal_send_successful() { + /* + * LED signal, if new status was send successful + */ + uint8_t count = 5; + for(uint8_t i=0; i!= count; ++i) { + digitalWrite(LED_PIN, LOW); + delay(100); + digitalWrite(LED_PIN, HIGH); + delay(100); + } +} + +void signal_clock_failed() { + /* + * LED signal, if time setting failed + */ + uint8_t count = 2; + for(uint8_t i=0; i!= count; ++i) { + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + } + delay(2000); +} + +void signal_wifi_failed() { + /* + * LED signal, if wifi initialication was failed + */ + uint8_t count = 3; + for(uint8_t i=0; i!= count; ++i) { + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + } + delay(2000); +} + +void signal_connect_failed() { + /* + * LED signal, if door is opened + */ + uint8_t count = 4; + for(uint8_t i=0; i!= count; ++i) { + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + } + delay(2000); +} + +void signal_send_failed() { + /* + * LED signal, if door is opened + */ + uint8_t count = 5; + for(uint8_t i=0; i!= count; ++i) { + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + } + delay(2000); } void set_clock() { + /* + * We need time for certificate authorization + */ + configTime(TZ_STRING, NTP_URL); - configTime(TZ_STRING, NTP_URL); - - Serial.print("Waiting for NTP time sync: "); - time_t now = time(nullptr); - while (now < 8 * 3600 * 2) { - delay(500); - Serial.print("."); - now = time(nullptr); - } - Serial.println(""); - struct tm timeinfo; - gmtime_r(&now, &timeinfo); - Serial.print("Current time: "); - Serial.print(asctime(&timeinfo)); + Serial.print("[Clock] Waiting for NTP time sync"); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + Serial.print("[Clock] Current time: "); + Serial.println(asctime(&timeinfo)); } int send_status(door_state state) { /* - * geht die initialisierung mit einem byte länge? - * terminiert strcpy den status mit \0? + * Inits wifi (if needed) and send the status */ char status[2] = ""; @@ -135,17 +209,19 @@ int send_status(door_state state) { BearSSL::PrivateKey client_key(CLIENT_KEY); client.setTrustAnchors(&server_cert); client.setClientRSACert(&client_cert, &client_key); - delay(500); + delay(200); Serial.println("[Ctx] SSL Context initialized"); - Serial.print("[Ctx] Free Heap: "); - Serial.println(ESP.getFreeHeap()); - delay(500); + delay(200); + if (WiFi.status() != WL_CONNECTED) { + init_wifi(); + } Serial.printf("[Send] Connect to %s:%i\n", SERVER_URL, SERVER_PORT); client.connect(SERVER_URL, SERVER_PORT); if (!client.connected()) { Serial.println("[Send] Can't connect to server"); Serial.print("[Send] SSL Error: "); Serial.println(client.getLastSSLError()); + signal_send_failed(); client.stop(); return 1; } else { @@ -154,34 +230,19 @@ int send_status(door_state state) { Serial.println("[Send] Connection successful established"); Serial.printf("[Send] Send status: %s\n", status); client.write(status); - + signal_send_successful(); } return 0; } - - - void setup() { /* * things to do once at boot time */ init_serial(); - Serial.print("[Init] Free Heap ( after serial init): "); - Serial.println(ESP.getFreeHeap()); init_pins(); - Serial.print("[Init] Free Heap (after pins init): "); - Serial.println(ESP.getFreeHeap()); - init_wifi(); - Serial.print("[Init] Free Heap (after wifi init): "); - Serial.println(ESP.getFreeHeap()); - delay(500); - set_clock(); - Serial.print("[Init] Free Heap (after setting clock): "); - Serial.println(ESP.getFreeHeap()); - delay(500); } void loop() { @@ -192,11 +253,10 @@ void loop() { new_door_state = read_door_state(); if (new_door_state != current_door_state) { Serial.printf("[Loop] Status has changed to %i\n", new_door_state); - toggle_led(new_door_state); - send_status(new_door_state); - current_door_state = new_door_state; + signal_door_changed();; + if (send_status(new_door_state) == 0) { + current_door_state = new_door_state; + } } - Serial.print("[Loop] Free Heap: "); - Serial.println(ESP.getFreeHeap()); delay(FREQUENCY); } -- 2.39.5 From a93bb9ea0a46328d5bf954a09bd03db6ceef17a9 Mon Sep 17 00:00:00 2001 From: +++ Date: Thu, 7 Apr 2022 23:05:46 +0200 Subject: [PATCH 11/25] struktureller umbau MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClientSecure instanz jetzt global, init_wifi() ins setup verlagert, prüfung auf wifi in jedem loop, client.stop() auch bei erfolg --- source/nodemcu/statusclient/statusclient.ino | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/source/nodemcu/statusclient/statusclient.ino b/source/nodemcu/statusclient/statusclient.ino index ed21eb0..5faaa2e 100644 --- a/source/nodemcu/statusclient/statusclient.ino +++ b/source/nodemcu/statusclient/statusclient.ino @@ -22,8 +22,8 @@ typedef enum { DOOR_OPEN = 1 } door_state; door_state current_door_state = DOOR_CLOSED; -door_state new_door_state = DOOR_CLOSED; +BearSSL::WiFiClientSecure client; void init_serial() { /* @@ -45,7 +45,6 @@ void init_pins() { Serial.println("[Pin] LED and REED initialized"); } - void init_wifi() { /* * Creates the ssl context. Turns wifi off and than into @@ -203,18 +202,12 @@ int send_status(door_state state) { return 1; } - BearSSL::WiFiClientSecure client; BearSSL::X509List server_cert(SERVER_CERT); BearSSL::X509List client_cert(CLIENT_CERT); BearSSL::PrivateKey client_key(CLIENT_KEY); client.setTrustAnchors(&server_cert); client.setClientRSACert(&client_cert, &client_key); - delay(200); Serial.println("[Ctx] SSL Context initialized"); - delay(200); - if (WiFi.status() != WL_CONNECTED) { - init_wifi(); - } Serial.printf("[Send] Connect to %s:%i\n", SERVER_URL, SERVER_PORT); client.connect(SERVER_URL, SERVER_PORT); if (!client.connected()) { @@ -231,6 +224,7 @@ int send_status(door_state state) { Serial.printf("[Send] Send status: %s\n", status); client.write(status); signal_send_successful(); + client.stop(); } return 0; } @@ -243,6 +237,7 @@ void setup() { */ init_serial(); init_pins(); + init_wifi(); } void loop() { @@ -250,10 +245,13 @@ void loop() { /* * things are running in a endless loop */ - new_door_state = read_door_state(); + if (WiFi.status() != WL_CONNECTED) { + init_wifi(); + } + door_state new_door_state = read_door_state(); if (new_door_state != current_door_state) { Serial.printf("[Loop] Status has changed to %i\n", new_door_state); - signal_door_changed();; + signal_door_changed(); if (send_status(new_door_state) == 0) { current_door_state = new_door_state; } -- 2.39.5 From c4d02a73c9772b819f20d34ce992884eee2249c0 Mon Sep 17 00:00:00 2001 From: +++ Date: Sun, 10 Jul 2022 17:54:37 +0200 Subject: [PATCH 12/25] konfiguration fuer mastodon hinzu --- source/server/apistatusd.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/server/apistatusd.conf b/source/server/apistatusd.conf index e0306de..177f44a 100644 --- a/source/server/apistatusd.conf +++ b/source/server/apistatusd.conf @@ -23,3 +23,9 @@ cert = ./certs/statusclient-pub.pem [api] api = ./api template = ./api_template + +[mastodon] +send = true +host = localhost +token = aaaaa-bbbbb-ccccc-ddddd-eeeee + -- 2.39.5 From edece83dd1c9c2ff6d341300f1c4d4a323b4b2e5 Mon Sep 17 00:00:00 2001 From: +++ Date: Sun, 10 Jul 2022 18:17:33 +0200 Subject: [PATCH 13/25] kleinere umstrukturierung auswertung der konfig fuer mastodon, default_config erweitert, set_values() in get_status_and_timestamp() umbenannt, statusstring und timestamp werden in main() aufgerufen und an andere funktionen uebergeben --- source/server/apistatusd.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index a4c7244..fca5c21 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -14,6 +14,7 @@ import os import socket import ssl import sys +from mastodon import Mastodon from time import time, sleep import configparser @@ -102,7 +103,7 @@ def receive_buffer_is_valid(raw_data): return False -def change_status(raw_data, api): +def change_status(status, timestamp, filename): ''' Write the new status together with a timestamp into the Space API JSON. param 1: byte object @@ -112,14 +113,13 @@ def change_status(raw_data, api): logging.debug('Change status API') # todo: use walrus operator := when migrating to python >= 3.8 - data = read_api(api) + data = read_api(filename) if data is False: return False - status, timestamp = set_values(raw_data) - if os.access(api, os.W_OK): + if os.access(filename, os.W_OK): logging.debug('API file is writable') - with open(api, 'w') as api_file: + with open(filename, 'w') as api_file: logging.debug('API file open successfull') data["state"]["open"] = status data["state"]["lastchange"] = timestamp @@ -133,7 +133,7 @@ def change_status(raw_data, api): else: logging.error('API file is not writable. Wrong permissions?') return False - logging.info('Status successfull changed to {}'.format(status)) + logging.info('API file successfull changed to {}'.format(status)) return True @@ -164,7 +164,7 @@ def read_api(api): return api_json_data -def set_values(raw_data): +def get_status_and_time(raw_data): ''' Create a timestamp, changes the value of the given byte into a string and returns both. @@ -211,6 +211,11 @@ def main(): 'api': { 'api': './api', 'template': './api_template' + }, + 'mastodon': { + 'send': 'false', + 'host': 'localhost', + 'token': 'aaaaa-bbbbb-ccccc-ddddd-eeeee' } } configfile = './apistatusd.conf' @@ -293,8 +298,12 @@ def main(): raw_data = conn.recv(1) if receive_buffer_is_valid(raw_data) is True: - if change_status(raw_data, config['api']['api']) is True: + status, timestamp = get_status_and_time(raw_data) + if change_status(status, timestamp, config['api']['api']) is True: answer = raw_data + if config['mastodon']['send'].lower() == 'true': + logging.debug('Try to toot status') + else: logging.debug('Toot is set to false') if conn: logging.debug('Send {} back'.format(raw_data)) conn.send(answer) -- 2.39.5 From 930ab7eef3f834a46c523f11ce4e1289a4e4d9b9 Mon Sep 17 00:00:00 2001 From: +++ Date: Sun, 10 Jul 2022 19:38:29 +0200 Subject: [PATCH 14/25] funktion send_toot() hinzu --- source/server/apistatusd.py | 39 ++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index fca5c21..0cae07f 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -15,7 +15,7 @@ import socket import ssl import sys from mastodon import Mastodon -from time import time, sleep +from time import time, localtime, strftime, sleep import configparser @@ -103,6 +103,37 @@ def receive_buffer_is_valid(raw_data): return False +def send_toot(status, timestamp, host, token): + ''' + param1: boolean + param2: integer + param3: string + param4: string + return: boolean + ''' + msg = None + timeformat = '%d.%m.%Y %H:%M Uhr' + if status not in (True, False): + logging.error('Invalid status to toot') + return False + timestring = strftime(timeformat, localtime(timestamp)) + + logging.debug('Try to toot status to {}'.format(host)) + if status == True: + msg = 'The krautspace is open since: {}'.format(timestring) + elif status == False: + msg = 'The krautspace is closed since: {}'.format(timestring) + logging.debug('Send message: {}'.format(msg)) + try: + mastodon = Mastodon(api_base_url = host, + access_token = token) + mastodon.toot(mag) + except Exception as e: + logging.error('Failed to toot status') + return False + return False + + def change_status(status, timestamp, filename): ''' Write the new status together with a timestamp into the Space API JSON. @@ -169,7 +200,7 @@ def get_status_and_time(raw_data): Create a timestamp, changes the value of the given byte into a string and returns both. param 1: byte object - return: tuple + return: tuple (boolean, integer) ''' status = True if raw_data.decode('utf-8', 'strict') == '1' else False timestamp = int(str(time()).split('.')[0]) @@ -302,7 +333,9 @@ def main(): if change_status(status, timestamp, config['api']['api']) is True: answer = raw_data if config['mastodon']['send'].lower() == 'true': - logging.debug('Try to toot status') + send_toot(status, timestamp, + config['mastodon']['host'], + config['mastodon']['token']) else: logging.debug('Toot is set to false') if conn: logging.debug('Send {} back'.format(raw_data)) -- 2.39.5 From c7fc0b9eff256300191565c5e380b2322042f9d6 Mon Sep 17 00:00:00 2001 From: +++ Date: Tue, 12 Jul 2022 21:57:20 +0200 Subject: [PATCH 15/25] angefangen toot in thread auszulagern --- source/server/apistatusd.py | 95 ++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 0cae07f..c4ece63 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -14,11 +14,69 @@ import os import socket import ssl import sys +import threading from mastodon import Mastodon from time import time, localtime, strftime, sleep import configparser +class Toot(threading.Thread): + ''' + The thread to toot the status to mastodon. + ''' + def __init__(self, status, timestamp, config): + ''' + param1: boolean + param2: integer + param3: dictionary + ''' + threading.Thread.__init__(self) + self.status = status + self.config = config + self.timestamp = timestamp + self.mastodon = Mastodon(api_base_url = self.config['mastodon']['host'], + access_token = self.config['mastodon']['token']) + + def run(self): + ''' + return: boolean + send_toot(status, timestamp, + config['mastodon']['host'], + config['mastodon']['token']) + ''' + msg = None + timeformat = '%d.%m.%Y %H:%M Uhr' + timestring = strftime(timeformat, localtime(self.timestamp)) + + if self.status not in (True, False): + logging.error('Invalid status to toot') + timestring = strftime(timeformat, localtime(self.timestamp)) + + logging.debug('Try to toot status to {}'.format(host)) + if self.status == True: + msg = 'The krautspace is open since: {}'.format(timestring) + elif self.status == False: + msg = 'The krautspace is closed since: {}'.format(timestring) + logging.debug('Send message: {}'.format(msg)) + try: + mastodon = Mastodon(api_base_url = host, + access_token = token) + mastodon.toot(mag) + except Exception as e: + logging.error('Failed to toot status') + return False + return False + + + def send_toot(self): + ''' + Starts the thread + ''' + send_toot(status, timestamp, + config['mastodon']['host'], + config['mastodon']['token']) + + def certs_readable(config): ''' checks at start, if the needed certificates defined (no nullstring) and @@ -103,37 +161,6 @@ def receive_buffer_is_valid(raw_data): return False -def send_toot(status, timestamp, host, token): - ''' - param1: boolean - param2: integer - param3: string - param4: string - return: boolean - ''' - msg = None - timeformat = '%d.%m.%Y %H:%M Uhr' - if status not in (True, False): - logging.error('Invalid status to toot') - return False - timestring = strftime(timeformat, localtime(timestamp)) - - logging.debug('Try to toot status to {}'.format(host)) - if status == True: - msg = 'The krautspace is open since: {}'.format(timestring) - elif status == False: - msg = 'The krautspace is closed since: {}'.format(timestring) - logging.debug('Send message: {}'.format(msg)) - try: - mastodon = Mastodon(api_base_url = host, - access_token = token) - mastodon.toot(mag) - except Exception as e: - logging.error('Failed to toot status') - return False - return False - - def change_status(status, timestamp, filename): ''' Write the new status together with a timestamp into the Space API JSON. @@ -333,9 +360,9 @@ def main(): if change_status(status, timestamp, config['api']['api']) is True: answer = raw_data if config['mastodon']['send'].lower() == 'true': - send_toot(status, timestamp, - config['mastodon']['host'], - config['mastodon']['token']) + toot_threat = Toot(status, timestamp, config) + toot_thread.run() + logging.debug('Toot thread started') else: logging.debug('Toot is set to false') if conn: logging.debug('Send {} back'.format(raw_data)) -- 2.39.5 From 666a997a90af229c92200be861f0e8ec1a0fef65 Mon Sep 17 00:00:00 2001 From: +++ Date: Wed, 13 Jul 2022 19:10:17 +0200 Subject: [PATCH 16/25] erste version eines toots --- source/server/apistatusd.py | 56 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index c4ece63..eb59dc3 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -20,6 +20,14 @@ from time import time, localtime, strftime, sleep import configparser +class InitException(Exception): + ''' + If the initialisation from the mastodon instance failes then we raise + this exception. + ''' + def __init__(self, error): + self.error = error + class Toot(threading.Thread): ''' The thread to toot the status to mastodon. @@ -34,49 +42,43 @@ class Toot(threading.Thread): self.status = status self.config = config self.timestamp = timestamp - self.mastodon = Mastodon(api_base_url = self.config['mastodon']['host'], - access_token = self.config['mastodon']['token']) - + try: + self.mastodon = Mastodon( + api_base_url = self.config['mastodon']['host'], + access_token = self.config['mastodon']['token']) + except Exception as e: + logging.error('Exception occurred: {}'.format(e)) + raise InitException('Mastodon instance initialisation failed') + def run(self): ''' return: boolean - send_toot(status, timestamp, - config['mastodon']['host'], - config['mastodon']['token']) ''' msg = None timeformat = '%d.%m.%Y %H:%M Uhr' - timestring = strftime(timeformat, localtime(self.timestamp)) - if self.status not in (True, False): logging.error('Invalid status to toot') - timestring = strftime(timeformat, localtime(self.timestamp)) - - logging.debug('Try to toot status to {}'.format(host)) + return False + try: + timestring = strftime(timeformat, localtime(self.timestamp)) + except Exception as e: + logging.error('Can not convert timestamp into timestring') + return False + logging.debug('Try to toot status to {}'.format(self.config['mastodon']['host'])) if self.status == True: msg = 'The krautspace is open since: {}'.format(timestring) elif self.status == False: msg = 'The krautspace is closed since: {}'.format(timestring) logging.debug('Send message: {}'.format(msg)) try: - mastodon = Mastodon(api_base_url = host, - access_token = token) mastodon.toot(mag) + return True except Exception as e: logging.error('Failed to toot status') return False return False - def send_toot(self): - ''' - Starts the thread - ''' - send_toot(status, timestamp, - config['mastodon']['host'], - config['mastodon']['token']) - - def certs_readable(config): ''' checks at start, if the needed certificates defined (no nullstring) and @@ -360,9 +362,13 @@ def main(): if change_status(status, timestamp, config['api']['api']) is True: answer = raw_data if config['mastodon']['send'].lower() == 'true': - toot_threat = Toot(status, timestamp, config) - toot_thread.run() - logging.debug('Toot thread started') + try: + toot_thread = Toot(status, timestamp, config) + toot_thread.run() + except InitException as e: + logging.debug('InitException: {}'.format(e)) + except Exception as ex: + logging.debug('Exception: {}'.format(ex)) else: logging.debug('Toot is set to false') if conn: logging.debug('Send {} back'.format(raw_data)) -- 2.39.5 From 0eba169038c70b2a2a965a444697e9c645df2b59 Mon Sep 17 00:00:00 2001 From: +++ Date: Wed, 13 Jul 2022 22:10:07 +0200 Subject: [PATCH 17/25] kleine aenderung im logging --- source/server/apistatusd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index eb59dc3..c0d6e00 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -362,11 +362,12 @@ def main(): if change_status(status, timestamp, config['api']['api']) is True: answer = raw_data if config['mastodon']['send'].lower() == 'true': + logging.debug('Toot is set to true') try: toot_thread = Toot(status, timestamp, config) toot_thread.run() except InitException as e: - logging.debug('InitException: {}'.format(e)) + logging.error('InitException: {}'.format(e)) except Exception as ex: logging.debug('Exception: {}'.format(ex)) else: logging.debug('Toot is set to false') -- 2.39.5 From 9fe94d7e6e4ba5f4fb45b6cb4485cbd44cdb5c3b Mon Sep 17 00:00:00 2001 From: +++ Date: Thu, 14 Jul 2022 21:47:16 +0200 Subject: [PATCH 18/25] umstellung auf requests modul mastodon gegen requests getauscht, import exceptions eingefuegt, InitException wieder entfernt --- source/server/apistatusd.py | 173 ++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index c0d6e00..461cad6 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -2,81 +2,25 @@ # file: apistatusd.py # date: 26.07.2019 -# email: berhsi@web.de +# mail: berhsi@web.de # Status server, listening for door status updates. The IPv4 address and port # to listen on are configurable, by default localhost:10001 is used. The # connection is secured by TLS and client side authentication. -import json -import logging -import os -import socket -import ssl -import sys -import threading -from mastodon import Mastodon -from time import time, localtime, strftime, sleep -import configparser - - -class InitException(Exception): - ''' - If the initialisation from the mastodon instance failes then we raise - this exception. - ''' - def __init__(self, error): - self.error = error - -class Toot(threading.Thread): - ''' - The thread to toot the status to mastodon. - ''' - def __init__(self, status, timestamp, config): - ''' - param1: boolean - param2: integer - param3: dictionary - ''' - threading.Thread.__init__(self) - self.status = status - self.config = config - self.timestamp = timestamp - try: - self.mastodon = Mastodon( - api_base_url = self.config['mastodon']['host'], - access_token = self.config['mastodon']['token']) - except Exception as e: - logging.error('Exception occurred: {}'.format(e)) - raise InitException('Mastodon instance initialisation failed') - - def run(self): - ''' - return: boolean - ''' - msg = None - timeformat = '%d.%m.%Y %H:%M Uhr' - if self.status not in (True, False): - logging.error('Invalid status to toot') - return False - try: - timestring = strftime(timeformat, localtime(self.timestamp)) - except Exception as e: - logging.error('Can not convert timestamp into timestring') - return False - logging.debug('Try to toot status to {}'.format(self.config['mastodon']['host'])) - if self.status == True: - msg = 'The krautspace is open since: {}'.format(timestring) - elif self.status == False: - msg = 'The krautspace is closed since: {}'.format(timestring) - logging.debug('Send message: {}'.format(msg)) - try: - mastodon.toot(mag) - return True - except Exception as e: - logging.error('Failed to toot status') - return False - return False +try: + import json + import logging + import os + import socket + import ssl + import sys + import requests + import threading + from time import time, localtime, strftime, sleep + import configparser +except ImportException as e: + print('Import error: {}'.format(e)) def certs_readable(config): @@ -93,7 +37,6 @@ def certs_readable(config): return False return True - def print_config(config): ''' Logs the config with level debug. @@ -102,7 +45,10 @@ def print_config(config): for section in config.sections(): logging.debug('Section {}'.format(section)) for i in config[section]: - logging.debug(' {}: {}'.format(i, config[section][i])) + if i == 'token': + logging.debug(' {}: {}'.format(i, 'aaaaa-bbbbb-ccccc-ddddd-eeeee')) + else: + logging.debug(' {}: {}'.format(i, config[section][i])) def print_ciphers(cipherlist): ''' @@ -162,7 +108,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. @@ -170,7 +115,6 @@ def change_status(status, timestamp, filename): param 2: string return: boolean ''' - logging.debug('Change status API') # todo: use walrus operator := when migrating to python >= 3.8 data = read_api(filename) @@ -196,7 +140,6 @@ def change_status(status, timestamp, filename): 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 @@ -206,7 +149,6 @@ def read_api(api): return: dict or boolean ''' logging.debug('Open API file: {}'.format(api)) - # return early if the API JSON cannot be read if not os.access(api, os.R_OK): logging.error('Failed to read API file') @@ -223,7 +165,6 @@ def read_api(api): return False return api_json_data - def get_status_and_time(raw_data): ''' Create a timestamp, changes the value of the given byte into a string @@ -238,6 +179,83 @@ def get_status_and_time(raw_data): str(timestamp), str(status))) return (status, timestamp) +def join_path(host, api): + ''' + Becomes two parts (host and api) of the mastodon url and concanate them + param1: string + param2: string + return: string + ''' + url = '' + try: + if host[-1] == '/' and api[0] == '/': + url = ''.join((host, api[1:])) + elif host[-1] != '/' and api[0] != '/': + url = '/'.join((host, api)) + else: + url = ''.join((host, api)) + except TypeError as e: + logging.error('Can´t join path: {}'.format(e)) + return url + +class Toot(threading.Thread): + ''' + The thread to toot the status to mastodon. + ''' + def __init__(self, status, timestamp, config): + ''' + param1: boolean + param2: integer + param3: dictionary + ''' + threading.Thread.__init__(self) + self.status = status + self.config = config + self.timestamp = timestamp + self.api = '/api/v1/statuses' + self.auth = {'Authorization': ''} + self.data = {'status': ''} + self.url = '' + + def run(self): + ''' + return: boolean + ''' + timeformat = '%d.%m.%Y %H:%M Uhr' + # check if status is valid + if self.status not in (True, False): + logging.error('Invalid status to toot') + return False + # convert seconds into timestring + try: + timestring = strftime(timeformat, localtime(self.timestamp)) + except Exception as e: + logging.error('Can not convert timestamp into timestring') + return False + # set status message + if self.status == True: + self.data['status'] = 'Krautspace is open since: {}'.format(timestring) + elif self.status == False: + self.data['status'] = 'Krautspace is closed since: {}'.format(timestring) + logging.debug('Message: {}'.format(self.data['status'])) + # build mastodon api url + self.url = join_path(self.config['mastodon']['host'], self.api) + # build authentcation header + self.auth['Authorization'] = 'Bearer {}'.format( + self.config['mastodon']['token']) + # and finaly send request to mastodon + try: + logging.debug('Try to toot status') + response = requests.post(self.url, data = self.data, + headers = self.auth) + if response.status_code == 200: + logging.info('Toot successful send') + return True + else: + logging.error('Failed to toot. Response: {}'.format(response.status_code)) + except Exception as e: + logging.error('Exception occurred: {}'.format(e)) + return False def main(): ''' @@ -248,7 +266,6 @@ def main(): OP_DONT_ISERT_EMPTY_FRAGMENTS: prevention agains CBC 4 attack (cve-2011-3389) ''' - answer = '3'.encode(encoding='utf-8', errors='strict') loglevel = logging.WARNING formatstring = '%(asctime)s: %(levelname)s: %(message)s' -- 2.39.5 From c7bd0eafffb054c30c2bd9f0fc2633fd46acdddd Mon Sep 17 00:00:00 2001 From: +++ Date: Mon, 18 Jul 2022 20:15:25 +0200 Subject: [PATCH 19/25] default loglevel auf info gesetzt --- source/server/apistatusd.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 461cad6..2b0bbe8 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -267,14 +267,14 @@ def main(): (cve-2011-3389) ''' answer = '3'.encode(encoding='utf-8', errors='strict') - loglevel = logging.WARNING + loglevel = logging.INFO formatstring = '%(asctime)s: %(levelname)s: %(message)s' logging.basicConfig(format=formatstring, level=loglevel) default_config = { 'general': { 'timeout': 3.0, - 'loglevel': 'warning' + 'loglevel': 'info' }, 'server': { 'host': 'localhost', @@ -295,6 +295,7 @@ def main(): 'token': 'aaaaa-bbbbb-ccccc-ddddd-eeeee' } } + logging.info('Try to read config file') configfile = './apistatusd.conf' config = configparser.ConfigParser() config.read_dict(default_config) @@ -309,6 +310,7 @@ def main(): default_config['general']['loglevel']) config.set('general', 'loglevel', default_config['general']['loglevel']) + logging.info('Set loglevel to {}'.format(config['general']['loglevel'].upper())) logger.setLevel(config['general']['loglevel'].upper()) print_config(config) -- 2.39.5 From ef3981fe66b798b6df1343241d83aae8bf934cb8 Mon Sep 17 00:00:00 2001 From: example Date: Sat, 23 Jul 2022 01:42:10 +0200 Subject: [PATCH 20/25] funktion main() ueberarbeitet --- source/server/apistatusd.py | 118 ++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 67 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 2b0bbe8..4a75570 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -331,76 +331,60 @@ def main(): logging.debug('SSL context created') print_context(context) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as mySocket: - logging.debug('Socket created') - mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - keep = mySocket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) - logging.debug('Socket keepalive: {}'.format(keep)) - try: - mySocket.bind((config['server']['host'], int(config['server']['port']))) - mySocket.listen(5) - except Exception as e: - logging.error('Unable to bind and listen') - logging.error('{}'.format(e)) - sys.exit(1) - logging.info('Listening on {} at Port {}'.format(config['server']['host'], - config['server']['port'])) - - while True: + try: + # normalen socket öffnen => MySocket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as MySocket: + logging.debug('Socket created') + MySocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + keep = MySocket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) + logging.debug('Socket keepalive: {}'.format(keep)) try: - fromSocket, fromAddr = mySocket.accept() - logging.info('Client connected: {}:{}'.format(fromAddr[0], fromAddr[1])) - try: - fromSocket.settimeout(float(config['general']['timeout'])) - logging.debug('Connection timeout set to {}'.format( - config['general']['timeout'])) - except Exception: - logging.error('Cannot set timeout to {}'.format( - config['general']['timeout'])) - try: - conn = context.wrap_socket(fromSocket, server_side=True) - conn.settimeout(float(config['general']['timeout'])) - except socket.timeout: - logging.error('Socket timeout') - continue - except Exception as e: - logging.error('Connection failed: {}'.format(e)) - continue - logging.info('Connection established') - try: - cert = conn.getpeercert(binary_form=False) - display_peercert(cert) - except ValueError: - logging.debug('SSL handshake has not been done yet') - except Exception as e: - logging.debug('Unexpected error: {}'.format(e)) - - raw_data = conn.recv(1) - if receive_buffer_is_valid(raw_data) is True: - status, timestamp = get_status_and_time(raw_data) - if change_status(status, timestamp, config['api']['api']) is True: - answer = raw_data - if config['mastodon']['send'].lower() == 'true': - logging.debug('Toot is set to true') - try: - toot_thread = Toot(status, timestamp, config) - toot_thread.run() - except InitException as e: - logging.error('InitException: {}'.format(e)) - except Exception as ex: - logging.debug('Exception: {}'.format(ex)) - else: logging.debug('Toot is set to false') - if conn: - logging.debug('Send {} back'.format(raw_data)) - conn.send(answer) - sleep(0.1) # protection against dos - except KeyboardInterrupt: - logging.info('Keyboard interrupt received') - sys.exit(1) + MySocket.bind((config['server']['host'], int(config['server']['port']))) + MySocket.listen(5) + logging.info('Listening on {} at Port {}'.format(config['server']['host'], + config['server']['port'])) except Exception as e: + logging.error('Unable to bind and listen') logging.error('{}'.format(e)) - continue - return 0 + sys.exit(1) + # den socket in den ssl-context einwickeln => MySecSocket + with context.wrap_socket(MySocket, server_side=True) as MySecSocket: + while True: + connection, remote = MySecSocket.accept() + logging.info('Client connected: {}:{}'.format(remote[0], remote[1])) + connection.settimeout(float(config['general']['timeout'])) + # die bestehende connection + with connection: + try: + cert = connection.getpeercert(binary_form=False) + display_peercert(cert) + except ValueError: + logging.debug('SSL handshake has not been done yet') + except Exception as e: + logging.debug('Unexpected error: {}'.format(e)) + + raw_data = connection.recv(1) + if receive_buffer_is_valid(raw_data) is True: + status, timestamp = get_status_and_time(raw_data) + if change_status(status, timestamp, config['api']['api']) is True: + answer = raw_data + if config['mastodon']['send'].lower() == 'true': + logging.debug('Toot is set to true') + try: + toot_thread = Toot(status, timestamp, config) + toot_thread.run() + except InitException as e: + logging.error('InitException: {}'.format(e)) + except Exception as ex: + logging.debug('Exception: {}'.format(ex)) + else: logging.debug('Toot is set to false') + logging.debug('Send {} back'.format(raw_data)) + connection.send(answer) + except KeyboardInterrupt: + logging.info('Keyboard interrupt received') + sys.exit(1) + except Exception as e: + logging.error('{}'.format(e)) if __name__ == '__main__': -- 2.39.5 From 7dd6dbab122d1250c038c9e4ad3506798acc0cc1 Mon Sep 17 00:00:00 2001 From: example Date: Sat, 30 Jul 2022 10:05:32 +0200 Subject: [PATCH 21/25] schweren fehler in main() beseitigt, finally klausel hinzu, SO_REUSEADDR hinzu --- source/server/apistatusd.py | 76 ++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 4a75570..aff8193 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -332,9 +332,10 @@ def main(): print_context(context) try: - # normalen socket öffnen => MySocket + # tcp socket öffnen => MySocket with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as MySocket: - logging.debug('Socket created') + logging.debug('TCP Socket created') + MySocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) MySocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) keep = MySocket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) logging.debug('Socket keepalive: {}'.format(keep)) @@ -347,44 +348,49 @@ def main(): logging.error('Unable to bind and listen') logging.error('{}'.format(e)) sys.exit(1) - # den socket in den ssl-context einwickeln => MySecSocket - with context.wrap_socket(MySocket, server_side=True) as MySecSocket: - while True: - connection, remote = MySecSocket.accept() - logging.info('Client connected: {}:{}'.format(remote[0], remote[1])) - connection.settimeout(float(config['general']['timeout'])) - # die bestehende connection - with connection: - try: - cert = connection.getpeercert(binary_form=False) - display_peercert(cert) - except ValueError: - logging.debug('SSL handshake has not been done yet') - except Exception as e: - logging.debug('Unexpected error: {}'.format(e)) - - raw_data = connection.recv(1) - if receive_buffer_is_valid(raw_data) is True: - status, timestamp = get_status_and_time(raw_data) - if change_status(status, timestamp, config['api']['api']) is True: - answer = raw_data - if config['mastodon']['send'].lower() == 'true': - logging.debug('Toot is set to true') - try: - toot_thread = Toot(status, timestamp, config) - toot_thread.run() - except InitException as e: - logging.error('InitException: {}'.format(e)) - except Exception as ex: - logging.debug('Exception: {}'.format(ex)) - else: logging.debug('Toot is set to false') - logging.debug('Send {} back'.format(raw_data)) - connection.send(answer) + # 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.settimeout(float(config['general']['timeout'])) + logging.debug('Connection timeout set to {}'.format(config['general']['timeout'])) + cert = Connection.getpeercert(binary_form=False) + display_peercert(cert) + except Exception as e: + logging.error('Unexpected error: {}'.format(e)) + continue + # empfangen und antworten + raw_data = Connection.recv(1) + if receive_buffer_is_valid(raw_data) is True: + status, timestamp = get_status_and_time(raw_data) + if change_status(status, timestamp, config['api']['api']) is True: + answer = raw_data + if config['mastodon']['send'].lower() == 'true': + logging.debug('Toot is set to true') + try: + toot_thread = Toot(status, timestamp, config) + toot_thread.run() + except InitException as e: + logging.error('InitException: {}'.format(e)) + except Exception as ex: + logging.debug('Exception: {}'.format(ex)) + else: logging.debug('Toot is set to false') + logging.debug('Send {} back'.format(raw_data)) + Connection.send(answer) + 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') if __name__ == '__main__': -- 2.39.5 From aed3616cf8626828f5d7f6cba7794a2b43581f62 Mon Sep 17 00:00:00 2001 From: example Date: Sat, 30 Jul 2022 10:32:03 +0200 Subject: [PATCH 22/25] kleine aenderungen der logmessages --- source/server/apistatusd.py | 68 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index aff8193..4fe1eda 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -108,38 +108,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 +124,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 -- 2.39.5 From 991eeea9f8f13717fc1c57870d0b0f5fca2fd716 Mon Sep 17 00:00:00 2001 From: example Date: Sat, 30 Jul 2022 12:09:38 +0200 Subject: [PATCH 23/25] verify_mode ueber config setzbar, ssl context in funktion ausgelagert --- source/server/apistatusd.conf | 4 ++- source/server/apistatusd.py | 52 ++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/source/server/apistatusd.conf b/source/server/apistatusd.conf index 177f44a..601204d 100644 --- a/source/server/apistatusd.conf +++ b/source/server/apistatusd.conf @@ -19,13 +19,15 @@ key = ./certs/statusd-key.pem [client] cert = ./certs/statusclient-pub.pem +# possible values: true, false, may +required = true [api] api = ./api template = ./api_template [mastodon] -send = true +send = false host = localhost token = aaaaa-bbbbb-ccccc-ddddd-eeeee diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index 4fe1eda..f8e3355 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -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. @@ -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') - print_context(context) + # 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,7 +373,7 @@ 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() @@ -384,7 +410,7 @@ def main(): Connection.close() except KeyboardInterrupt: logging.info('Keyboard interrupt received') - sys.exit(1) + sys.exit(255) except Exception as e: logging.error('{}'.format(e)) finally: -- 2.39.5 From 6bd34360a6661b6ba26ac0ddabf2cf322302a408 Mon Sep 17 00:00:00 2001 From: example Date: Wed, 3 Aug 2022 17:58:27 +0200 Subject: [PATCH 24/25] secure socket wieder in try-except; finally-klauses wieder raus --- source/server/apistatusd.py | 67 +++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py index f8e3355..9a89b4a 100755 --- a/source/server/apistatusd.py +++ b/source/server/apistatusd.py @@ -379,44 +379,47 @@ def main(): 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: + try: + Connection = context.wrap_socket(ClientSocket, server_side=True) logging.info('SSL Connection established') - try: - Connection.settimeout(float(config['general']['timeout'])) - logging.debug('Connection timeout set to {}'.format(config['general']['timeout'])) - cert = Connection.getpeercert(binary_form=False) - display_peercert(cert) - except Exception as e: - logging.error('Unexpected error: {}'.format(e)) - continue - # empfangen und antworten - raw_data = Connection.recv(1) - if receive_buffer_is_valid(raw_data) is True: - status, timestamp = get_status_and_time(raw_data) - if change_status(status, timestamp, config['api']['api']) is True: - answer = raw_data - if config['mastodon']['send'].lower() == 'true': - logging.debug('Toot is set to true') - try: - toot_thread = Toot(status, timestamp, config) - toot_thread.run() - except InitException as e: - logging.error('InitException: {}'.format(e)) - except Exception as ex: - logging.debug('Exception: {}'.format(ex)) - else: logging.debug('Toot is set to false') - logging.debug('Send {} back'.format(raw_data)) - Connection.send(answer) - Connection.close() + Connection.settimeout(float(config['general']['timeout'])) + logging.debug('Connection timeout set to {}'.format(config['general']['timeout'])) + cert = Connection.getpeercert(binary_form=False) + display_peercert(cert) + except Exception as e: + logging.error('Unexpected error: {}'.format(e)) + continue + # empfangen und antworten + raw_data = Connection.recv(1) + if receive_buffer_is_valid(raw_data) is True: + status, timestamp = get_status_and_time(raw_data) + if change_status(status, timestamp, config['api']['api']) is True: + answer = raw_data + if config['mastodon']['send'].lower() == 'true': + logging.debug('Toot is set to true') + try: + toot_thread = Toot(status, timestamp, config) + toot_thread.run() + except InitException as e: + logging.error('InitException: {}'.format(e)) + except Exception as ex: + logging.debug('Exception: {}'.format(ex)) + else: logging.debug('Toot is set to false') + logging.debug('Send {} back'.format(raw_data)) + Connection.send(answer) + Connection.close() except KeyboardInterrupt: logging.info('Keyboard interrupt received') - sys.exit(255) - 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__': -- 2.39.5 From 381bd390df27a57fedf240d6d4d4bfe4961e997d Mon Sep 17 00:00:00 2001 From: example Date: Wed, 31 Aug 2022 18:21:21 +0200 Subject: [PATCH 25/25] restart on failure --- source/server/apistatusd.service | 1 + 1 file changed, 1 insertion(+) diff --git a/source/server/apistatusd.service b/source/server/apistatusd.service index a76f4dc..33111bc 100644 --- a/source/server/apistatusd.service +++ b/source/server/apistatusd.service @@ -4,6 +4,7 @@ After=systemd-network.service network.target [Service] Type=simple +Restart=on-failure WorkingDirectory=/opt/doorstatus/ ExecStart=/opt/doorstatus/apistatusd.py SyslogIdentifier=doorstatus -- 2.39.5