Create cert #40

Merged
bmartin5692 merged 18 commits from create_cert into master 2019-06-08 23:02:09 +02:00
6 changed files with 197 additions and 157 deletions
Showing only changes of commit 72f3b5d049 - Show all commits

View file

@ -6,6 +6,7 @@ from bumper.xmppserver import XMPPServer
import asyncio import asyncio
import json import json
import time import time
import distutils
from datetime import datetime, timedelta from datetime import datetime, timedelta
import platform import platform
import os, sys import os, sys
@ -13,19 +14,64 @@ import logging
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
import json
from tinydb.storages import MemoryStorage from tinydb.storages import MemoryStorage
import socket
ca_cert = "./certs/CA/cacert.pem" def strtobool(strbool):
server_cert = "./certs/cert.pem" if str(strbool).lower() in ['true', '1', 't', 'y', 'on','yes']:
server_key = "./certs/key.pem" return True
else:
return False
# os.environ['PYTHONASYNCIODEBUG'] = '1' # Uncomment to enable ASYNCIODEBUG
bumper_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
# Set defaults from environment variables first
# Certs
ca_cert = os.environ.get("BUMPER_CA") or os.path.join(bumper_dir, "certs/ca.crt")
server_cert = os.environ.get("BUMPER_CERT") or os.path.join(
bumper_dir, "certs/bumper.crt"
)
server_key = os.environ.get("BUMPER_KEY") or os.path.join(
bumper_dir, "certs/bumper.key"
)
if not (
os.path.exists(ca_cert) or os.path.exists(server_cert) or os.path.exists(server_key)
):
logging.log(logging.FATAL, "Certificate(s) don't exist at paths specified")
os._exit(1)
# Folders
logs_dir = os.environ.get("BUMPER_LOGS") or os.path.join(bumper_dir, "logs")
os.makedirs(logs_dir, exist_ok=True) # Ensure logs directory exists or create
data_dir = os.environ.get("BUMPER_DATA") or os.path.join(bumper_dir, "data")
os.makedirs(data_dir, exist_ok=True) # Ensure data directory exists or create
# Listeners
bumper_listen = os.environ.get("BUMPER_LISTEN") or socket.gethostbyname(
socket.gethostname()
)
if not bumper_listen:
logging.log(logging.FATAL, "No listen address configured!")
os._exit(1)
bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
# Other
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
use_auth = False use_auth = False
token_validity_seconds = 3600 # 1 hour token_validity_seconds = 3600 # 1 hour
db = None db = None
# Logs mqtt_server = None
os.makedirs("logs", exist_ok=True) # Ensure logs directory exists or create mqtt_helperbot = None
conf_server = None
conf_server_2 = None
xmpp_server = None
shutting_down = False
# Set format for all logs # Set format for all logs
logformat = logging.Formatter( logformat = logging.Formatter(
"[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s" "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s"
@ -74,9 +120,90 @@ xmppserverlog.addHandler(xmpp_rotate)
# Override the logging level # Override the logging level
# xmppserverlog.setLevel(logging.INFO) # xmppserverlog.setLevel(logging.INFO)
logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger
async def start():
try:
loop = asyncio.get_event_loop()
except:
loop = asyncio.new_event_loop()
if bumper_debug:
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s",
)
loop.set_debug(True) # Set asyncio loop to debug
# logging.getLogger("asyncio").setLevel(logging.DEBUG) # Show debug asyncio logs (disabled in init, uncomment for debugging asyncio)
else:
logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s",
)
bumperlog.info("Starting Bumper")
global mqtt_server
mqtt_server = MQTTServer((bumper_listen, 8883))
global mqtt_helperbot
mqtt_helperbot = MQTTHelperBot((bumper_listen, 8883))
global conf_server
conf_server = ConfServer(
(bumper_listen, 443), usessl=True, helperbot=mqtt_helperbot
)
global conf_server_2
conf_server_2 = ConfServer(
(bumper_listen, 8007), usessl=False, helperbot=mqtt_helperbot
)
global xmpp_server
xmpp_server = XMPPServer((bumper_listen, 5223))
# Start web servers
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
conf_server_2.confserver_app()
asyncio.create_task(conf_server_2.start_server())
# Start MQTT Server
asyncio.create_task(mqtt_server.broker_coro())
# Start MQTT Helperbot
asyncio.create_task(mqtt_helperbot.start_helper_bot())
# Start XMPP Server
asyncio.create_task(xmpp_server.start_async_server())
# Start maintenance
while not shutting_down:
asyncio.create_task(maintenance())
await asyncio.sleep(30)
async def maintenance():
revoke_expired_tokens()
async def shutdown():
try:
bumperlog.info("Shutting down")
await mqtt_server.broker.shutdown()
xmpp_server.server.close()
await xmpp_server.server.wait_closed()
await conf_server.stop_server()
await conf_server_2.stop_server()
global shutting_down
shutting_down = True
except asyncio.CancelledError:
bumperlog.info("Coroutine canceled")
except Exception as e:
bumperlog.info("Exception: {}".format(e))
finally:
bumperlog.info("Shutdown complete")
def get_milli_time(timetoconvert): def get_milli_time(timetoconvert):
return int(round(timetoconvert * 1000)) return int(round(timetoconvert * 1000))
@ -89,20 +216,8 @@ def db_file():
return os_db_path() return os_db_path()
def os_db_path(createdir=True): def os_db_path(): # createdir=True):
# createdir - Set to False during tests to not create paths return os.path.join(data_dir, "bumper.db")
if platform.system() == "Windows":
if createdir:
os.makedirs(
os.getenv("APPDATA"), exist_ok=True
) # Ensure db_path directory exists or create
return os.path.join(os.getenv("APPDATA"), "bumper.db")
else:
if createdir:
os.makedirs(
os.path.expanduser("~/.config"), exist_ok=True
) # Ensure db_path directory exists or create
return os.path.expanduser("~/.config/bumper.db")
def db_get(): def db_get():

View file

@ -63,6 +63,7 @@ class ConfServer:
self.confthread = None self.confthread = None
self.run_async = False self.run_async = False
self.app = None self.app = None
self.site = None
def confserver_app(self): def confserver_app(self):
self.app = web.Application(loop=asyncio.get_event_loop()) self.app = web.Application(loop=asyncio.get_event_loop())
@ -173,7 +174,7 @@ class ConfServer:
if self.usessl: if self.usessl:
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key) ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
site = web.TCPSite( self.site = web.TCPSite(
runner, runner,
host=self.address[0], host=self.address[0],
port=self.address[1], port=self.address[1],
@ -181,9 +182,9 @@ class ConfServer:
) )
else: else:
site = web.TCPSite(runner, host=self.address[0], port=self.address[1]) self.site = web.TCPSite(runner, host=self.address[0], port=self.address[1])
await site.start() await self.site.start()
except PermissionError as e: except PermissionError as e:
if "bind" in e.strerror: if "bind" in e.strerror:
@ -198,6 +199,13 @@ class ConfServer:
confserverlog.exception("{}".format(e)) confserverlog.exception("{}".format(e))
exit(1) exit(1)
async def stop_server(self):
try:
await self.site.stop()
except Exception as e:
confserverlog.exception("{}".format(e))
async def handle_base(self, request): async def handle_base(self, request):
try: try:
# TODO - API Options here for viewing clients, tokens, restarting the server, etc. # TODO - API Options here for viewing clients, tokens, restarting the server, etc.
@ -877,19 +885,26 @@ class ConfServer:
if todo == "FindBest": if todo == "FindBest":
service = postbody["service"] service = postbody["service"]
if service == "EcoMsgNew": if service == "EcoMsgNew":
srvip = socket.gethostbyname(socket.gethostname()) srvip = bumper.bumper_announce_ip
srvport = 5223 srvport = 5223
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( "Announcing EcoMsgNew Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
body = {"result": "ok", "ip": srvip, "port": srvport} msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
msgserver = json.dumps(msgserver)
msgserver = msgserver.replace(
" ", ""
) # bot seems to be very picky about having no spaces, only way was with text
return web.json_response(text=msgserver)
elif service == "EcoUpdate": elif service == "EcoUpdate":
srvip = "47.88.66.164" # EcoVacs Server srvip = "47.88.66.164" # EcoVacs Server
srvport = 8005 srvport = 8005
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoUpdate Server to Bot as: {}:{}".format( "Announcing EcoUpdate Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
@ -937,9 +952,7 @@ class ConfServer:
bumper.bot_remove(postbody["did"]) bumper.bot_remove(postbody["did"])
body = {"result": "ok", "todo": "result"} body = {"result": "ok", "todo": "result"}
confserverlog.debug( confserverlog.debug("POST: {} - Response: {}".format(postbody, body))
"\r\n POST: {} \r\n Response: {}".format(postbody, body)
)
return web.json_response(body) return web.json_response(body)
@ -982,9 +995,7 @@ class ConfServer:
"todo": "result", "todo": "result",
} }
confserverlog.debug( confserverlog.debug("POST: {} - Response: {}".format(postbody, body))
"\r\n POST: {} \r\n Response: {}".format(postbody, body)
)
return web.json_response(body) return web.json_response(body)
@ -1012,10 +1023,10 @@ class ConfServer:
if todo == "FindBest": if todo == "FindBest":
service = postbody["service"] service = postbody["service"]
if service == "EcoMsgNew": if service == "EcoMsgNew":
srvip = socket.gethostbyname(socket.gethostname()) srvip = bumper.bumper_announce_ip
srvport = 5223 srvport = 5223
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( "Announcing EcoMsgNew Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
@ -1025,17 +1036,18 @@ class ConfServer:
" ", "" " ", ""
) # bot seems to be very picky about having no spaces, only way was with text ) # bot seems to be very picky about having no spaces, only way was with text
confserverlog.debug(
"\r\n POST: {} \r\n Response: {}".format(postbody, msgserver)
)
return web.json_response(text=msgserver) return web.json_response(text=msgserver)
elif service == "EcoUpdate": elif service == "EcoUpdate":
body = {"result": "ok", "ip": "47.88.66.164", "port": 8005} srvip = "47.88.66.164" # EcoVacs Server
srvport = 8005
confserverlog.debug( confserverlog.info(
"\r\n POST: {} \r\n Response: {}".format(postbody, body) "Announcing EcoUpdate Server to bot as: {}:{}".format(
srvip, srvport
) )
)
body = {"result": "ok", "ip": srvip, "port": srvport}
return web.json_response(body) return web.json_response(body)
except Exception as e: except Exception as e:
@ -1097,7 +1109,7 @@ class ConfServer:
body = {"ret": "ok", "logs": []} body = {"ret": "ok", "logs": []}
confserverlog.debug( confserverlog.debug(
"\r\n POST: {} \r\n Response: {}".format(json_body, body) "POST: {} - Response: {}".format(json_body, body)
) )
return web.json_response(body) return web.json_response(body)

View file

@ -300,8 +300,8 @@ class BumperMQTTServer_Plugin:
"eco-ng", "eco-ng",
) )
mqttserverlog.debug( mqttserverlog.info(
"new bot authenticated SN: {} DID: {}".format( "bot authenticated SN: {} DID: {}".format(
username, didsplit[0] username, didsplit[0]
) )
) )
@ -324,7 +324,7 @@ class BumperMQTTServer_Plugin:
if auth: if auth:
bumper.client_add(userid, realm, resource) bumper.client_add(userid, realm, resource)
mqttserverlog.debug( mqttserverlog.info(
"client authenticated {}".format(userid) "client authenticated {}".format(userid)
) )
authenticated = True authenticated = True

View file

@ -51,10 +51,6 @@ class XMPPServer:
class XMPPServer_Protocol(asyncio.Protocol): class XMPPServer_Protocol(asyncio.Protocol):
client_id = None client_id = None
exit_flag = False exit_flag = False
server_cert = "./certs/cert.pem"
server_key = "./certs/key.pem"
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(server_cert, server_key)
aclient = None aclient = None
def connection_made(self, transport): def connection_made(self, transport):
@ -506,7 +502,7 @@ class XMPPAsyncClient:
if self.devclass: # if there is a devclass it is a bot if self.devclass: # if there is a devclass it is a bot
bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy") bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy")
self.type = self.BOT self.type = self.BOT
xmppserverlog.debug("bot authenticated {}".format(self.uid)) xmppserverlog.info("bot authenticated SN: {}".format(self.uid))
# Send response # Send response
self.send( self.send(
'<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
@ -525,7 +521,7 @@ class XMPPAsyncClient:
if auth: if auth:
self.type = self.CONTROLLER self.type = self.CONTROLLER
bumper.client_add(self.uid, "bumper", self.clientresource) bumper.client_add(self.uid, "bumper", self.clientresource)
xmppserverlog.debug("client authenticated {}".format(self.uid)) xmppserverlog.info("client authenticated {}".format(self.uid))
# Client authenticated, move to next state # Client authenticated, move to next state
self._set_state("INIT") self._set_state("INIT")

View file

@ -1,102 +1,33 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import logging
import bumper import bumper
import sys, socket import argparse
import time
import platform
import os
# os.environ['PYTHONASYNCIODEBUG'] = '1' # Uncomment to enable ASYNCIODEBUG
import asyncio import asyncio
async def main():
try:
loop = asyncio.get_event_loop()
except:
loop = asyncio.new_event_loop()
args = sys.argv
listen_host = ""
if len(args) > 0:
if "--debug" in args:
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s",
)
loop.set_debug(True) # Set asyncio loop to debug
# logging.getLogger("asyncio").setLevel(logging.DEBUG) # Show debug asyncio logs (disabled in init, uncomment for debugging asyncio)
else:
logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s",
)
if "--listen" in args:
listen_host = args[args.index("--listen") + 1]
if listen_host == "":
if platform.system() == "Darwin": # If a Mac, use 0.0.0.0 for listening
listen_host = "0.0.0.0"
else:
listen_host = socket.gethostbyname(socket.gethostname())
conf_address_443 = (listen_host, 443)
conf_address_8007 = (listen_host, 8007)
xmpp_address = (listen_host, 5223)
mqtt_address = (listen_host, 8883)
mqtt_server = bumper.MQTTServer(mqtt_address)
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
conf_server = bumper.ConfServer(
conf_address_443, usessl=True, helperbot=mqtt_helperbot
)
conf_server_2 = bumper.ConfServer(
conf_address_8007, usessl=False, helperbot=mqtt_helperbot
)
xmpp_server = bumper.XMPPServer(xmpp_address)
try:
# Start web servers
conf_server.confserver_app()
asyncio.create_task(conf_server.start_server())
conf_server_2.confserver_app()
asyncio.create_task(conf_server_2.start_server())
# Start MQTT Server
asyncio.create_task(mqtt_server.broker_coro())
# Start MQTT Helperbot
asyncio.create_task(mqtt_helperbot.start_helper_bot())
# Start XMPP Server
asyncio.create_task(xmpp_server.start_async_server())
maintain = asyncio.create_task(maintenance_tasks())
await maintain # Keeps the loop running until this exits
finally:
# Cleanup and close tasks/loop
for task in asyncio.Task.all_tasks():
task.cancel()
loop.close()
async def maintenance_tasks():
while True:
await asyncio.sleep(30) # Sleep 30 seconds
bumper.revoke_expired_tokens()
if __name__ == "__main__": if __name__ == "__main__":
try: try:
asyncio.run(main()) parser = argparse.ArgumentParser()
parser.add_argument("--listen", type=str, default=None, help="listen address")
parser.add_argument(
"--announce", type=str, default=None, help="announce address (for bot)"
)
parser.add_argument("--debug", action="store_true")
args = parser.parse_args()
if args.debug:
bumper.bumper_debug = True
if args.listen:
bumper.bumper_listen = args.listen
if args.announce:
bumper.bumper_announce_ip = args.announce
asyncio.run(bumper.start())
except KeyboardInterrupt: except KeyboardInterrupt:
bumper.bumperlog.info("Keyboard Interrupt!") bumper.bumperlog.info("Keyboard Interrupt!")
pass pass
finally: finally:
bumper.bumperlog.info("Bumper Exiting!") asyncio.run(bumper.shutdown())

View file

@ -23,20 +23,6 @@ def test_user_db():
if os.path.exists("tests/tmp.db"): if os.path.exists("tests/tmp.db"):
os.remove("tests/tmp.db") # Remove existing db os.remove("tests/tmp.db") # Remove existing db
# Test os_db_path
platform.system = mock.MagicMock(return_value="Windows")
p = platform.system()
os.getenv = mock.MagicMock(return_value="C:\\AppData")
o = os.getenv("APPDATA")
assert bumper.os_db_path(createdir=False) == os.path.join(
os.getenv("APPDATA"), "bumper.db"
)
platform.system = mock.MagicMock(return_value="Linux")
assert bumper.os_db_path(createdir=False) == os.path.expanduser(
"~/.config/bumper.db"
)
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
bumper.user_add("testuser") # Add testuser bumper.user_add("testuser") # Add testuser