diff --git a/.gitignore b/.gitignore index e470b09..b3c8448 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,25 @@ +# Ignore pycache __pycache__ -.DS_Store -.vscode/ -.coverage -.noseids -.pytest_cache -nosetests.xml -tests/report -tests/tmp.db -logs/ \ No newline at end of file + +# Ignore .hidden files +.* + +# Except . +!.github/ +!.gitignore + +# Ignore items in test (report, cache, etc), except files starting with test +tests/* +!tests/test* +!tests/test_certs +!tests/pytest.ini + +# Ignore logs/data/certs +logs/* +data/* +certs/* + +# Except README in logs/data/certs +!logs/README.md +!data/README.md +!certs/README.md diff --git a/.travis.yml b/.travis.yml index 3b82981..1b952c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ install: - pip install pipenv - pipenv install --dev -script: pipenv run python -m pytest --cov=./ +script: pipenv run python -m pytest --cov=./ tests after_success: - codecov \ No newline at end of file diff --git a/Pipfile b/Pipfile index 6f8a142..bfb75b6 100644 --- a/Pipfile +++ b/Pipfile @@ -20,6 +20,8 @@ pytest-aiohttp = "*" pytest-cov = "*" testfixtures = "*" codecov = "*" +autoflake = "*" +pytest-env = "*" [pipenv] allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock index 6e256b6..05354a2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "243154e4a18d9995bb246342d1ed65910cda2fa674bced665a9706fc385459fc" + "sha256": "309c2ca578f67a8663325695e4d283f7e3f604c7cec451bf57e71af7dcdb7294" }, "pipfile-spec": 6, "requires": {}, @@ -127,20 +127,20 @@ }, "pyyaml": { "hashes": [ - "sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c", - "sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95", - "sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2", - "sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4", - "sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad", - "sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba", - "sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1", - "sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e", - "sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673", - "sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13", - "sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19" + "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3", + "sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043", + "sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7", + "sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265", + "sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391", + "sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778", + "sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225", + "sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955", + "sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e", + "sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190", + "sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd" ], "index": "pypi", - "version": "==5.1" + "version": "==5.1.1" }, "six": { "hashes": [ @@ -271,6 +271,13 @@ ], "version": "==19.1.0" }, + "autoflake": { + "hashes": [ + "sha256:6b59e5b9b82e30077499578856282debb81186d10b4f899e8c2e1d616cdef973" + ], + "index": "pypi", + "version": "==1.3" + }, "black": { "hashes": [ "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", @@ -471,6 +478,13 @@ ], "version": "==1.8.0" }, + "pyflakes": { + "hashes": [ + "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", + "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" + ], + "version": "==2.1.1" + }, "pylint": { "hashes": [ "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", @@ -517,6 +531,13 @@ "index": "pypi", "version": "==2.7.1" }, + "pytest-env": { + "hashes": [ + "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2" + ], + "index": "pypi", + "version": "==0.6.2" + }, "requests": { "hashes": [ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", diff --git a/README.md b/README.md index 8e338c0..83ce61e 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ Bumper is a standalone and self-hosted implementation of the central server used **Note:** The current master branch is unstable, and in active development. -| Master Build Status | Status | -|--|--| -|AppVeyor (Win32) | ![AppVeyor](https://img.shields.io/appveyor/ci/bmartin5692/bumper.svg)| -|TravisCI (Linux) | ![Travis (.org)](https://img.shields.io/travis/bmartin5692/bumper.svg)| +| Master Build Status | Status | +| ------------------- | ---------------------------------------------------------------------- | +| AppVeyor (Win32) | ![AppVeyor](https://img.shields.io/appveyor/ci/bmartin5692/bumper.svg) | +| TravisCI (Linux) | ![Travis (.org)](https://img.shields.io/travis/bmartin5692/bumper.svg) | Code Test Coverage: ![Codecov](https://img.shields.io/codecov/c/github/bmartin5692/bumper.svg) @@ -20,13 +20,13 @@ Bumper needs users to assist with testing in order to ensure compatability as bu As work to reverse the protocols and provide a self-hosted central server is still in progress, Bumper has had limited testing. There are a number of EcoVacs models that it hasn't been tested against. Bumper should be compatible with most wifi-enabled robots that use either the Ecovacs Android/iOS app or the Ecovacs Home Android/iOS app, but has only been reported to work on the below: -| Model | Protocol Used | Bumper Version Tested | EcoVacs App Tested | -|--|--|--|--| -| Deebot 900/901 | MQTT | master | Ecovacs/Ecovacs Home | -| Deebot 600 | MQTT | master | Ecovacs Home | -| Deebot Ozmo 601 | XMPP | master | Ecovacs | -| Deebot Ozmo 930 | XMPP | master | Ecovacs | -| Deebot M81 Pro | XMPP | v0.1.0 | Ecovacs | +| Model | Protocol Used | Bumper Version Tested | EcoVacs App Tested | +| --------------- | ------------- | --------------------- | -------------------- | +| Deebot 900/901 | MQTT | master | Ecovacs/Ecovacs Home | +| Deebot 600 | MQTT | master | Ecovacs Home | +| Deebot Ozmo 601 | XMPP | master | Ecovacs | +| Deebot Ozmo 930 | XMPP | master | Ecovacs | +| Deebot M81 Pro | XMPP | v0.1.0 | Ecovacs | For more information about the protocols and how Bumper works, see the [How does it work?](docs/How_It_Works.md) page in the docs. If you test against another model and it works, please open an issue to report it. @@ -64,14 +64,36 @@ But seriously, there are a several reasons for eliminating the central server: ### Starting Bumper -- Start Bumper with `pipenv run python start_bumper.py` +Bumper requires certificates to function. If certificates aren't found it will prompt to generate them for you. + +For more information on generating certificates manually, see the [Creating Certs](docs/Create_Certs.md) doc + +- Start Bumper with `pipenv run python -m bumper` + - If prompted to generate certificates choose yes or no - Reboot your robot - **Note:** Some models may require removing and re-inserting the battery pack. - This doesn't seem to be required for models that don't have easily accessible batteries such as the 900/901. - - If your configuration is correct, the robot will connect to Bumper within about 30 seconds. Bumper will output information about the connection status. +#### Command-Line Usage + +`start_bumper.py` is a helper script that starts up Bumper for you. It has a number of available command-line arguments that can be viewed by adding the `-h` flag. + + ```` + usage: start_bumper.py [-h] [--listen LISTEN] [--announce ANNOUNCE] [--debug] + + optional arguments: + -h, --help show this help message and exit + --listen LISTEN start serving on address + --announce ANNOUNCE announce address to bots on checkin + --debug enable debug logs + ```` + +#### Environment Variables + +Bumper looks for a number of Environment Variables at initialization allowing for customizing a number of settings. For more information see the [Environment Variables](docs/Env_Var.md) doc. + ## Thanks A big thanks to the original project creator @torbjornaxelsson, without his work this project would have taken much longer to build. diff --git a/appveyor.yml b/appveyor.yml index cd58fa4..56cc65c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ test_script: # Note that you must use the environment variable %PYTHON% to refer to # the interpreter you're using - Appveyor does not do anything special # to put the Python version you want to use on PATH. - - "pipenv run python -m pytest --cov=./" + - "pipenv run python -m pytest --cov=./ tests" after_test: - "pipenv run python -m codecov" \ No newline at end of file diff --git a/bumper/__init__.py b/bumper/__init__.py index 4280b1b..104a120 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -5,27 +5,65 @@ from bumper.mqttserver import MQTTServer, MQTTHelperBot from bumper.xmppserver import XMPPServer import asyncio import json -import time from datetime import datetime, timedelta -import platform -import os, sys +import os import logging from logging.handlers import RotatingFileHandler -from base64 import b64decode, b64encode from tinydb import TinyDB, Query -import json from tinydb.storages import MemoryStorage +import socket +import sys -ca_cert = "./certs/CA/cacert.pem" -server_cert = "./certs/cert.pem" -server_key = "./certs/key.pem" + +def strtobool(strbool): + if str(strbool).lower() in ["true", "1", "t", "y", "on", "yes"]: + 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" +) + +# 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() +) + + +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 token_validity_seconds = 3600 # 1 hour db = None -# Logs -os.makedirs("logs", exist_ok=True) # Ensure logs directory exists or create +mqtt_server = None +mqtt_helperbot = None +conf_server = None +conf_server_2 = None +xmpp_server = None + +shutting_down = False + # Set format for all logs logformat = logging.Formatter( "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s" @@ -74,9 +112,121 @@ xmppserverlog.addHandler(xmpp_rotate) # Override the logging level # xmppserverlog.setLevel(logging.INFO) - logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger +mqtt_listen_address = bumper_listen +mqtt_listen_port = 8883 +conf1_listen_address = bumper_listen +conf1_listen_port = 443 +conf2_listen_address = bumper_listen +conf2_listen_port = 8007 +xmpp_listen_address = bumper_listen +xmpp_listen_port = 5223 + +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", + ) + + if not bumper_listen: + logging.log(logging.FATAL, "No listen address configured") + return + + if not ( + os.path.exists(ca_cert) + and os.path.exists(server_cert) + and os.path.exists(server_key) + ): + logging.log(logging.FATAL, "Certificate(s) don't exist at paths specified") + return + + bumperlog.info("Starting Bumper") + global mqtt_server + mqtt_server = MQTTServer((mqtt_listen_address, mqtt_listen_port)) + global mqtt_helperbot + mqtt_helperbot = MQTTHelperBot((mqtt_listen_address, mqtt_listen_port)) + global conf_server + conf_server = ConfServer( + (conf1_listen_address, conf1_listen_port), usessl=True, helperbot=mqtt_helperbot + ) + global conf_server_2 + conf_server_2 = ConfServer( + (conf2_listen_address, conf2_listen_port), usessl=False, helperbot=mqtt_helperbot + ) + global xmpp_server + xmpp_server = XMPPServer((xmpp_listen_address, xmpp_listen_port)) + + # 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(5) + + +async def maintenance(): + revoke_expired_tokens() + + +async def shutdown(): + try: + bumperlog.info("Shutting down") + + await conf_server.stop_server() + await conf_server_2.stop_server() + if mqtt_server.broker.transitions.state == "started": + await mqtt_server.broker.shutdown() + elif mqtt_server.broker.transitions.state == "starting": + while mqtt_server.broker.transitions.state == "starting": + await asyncio.sleep(0.1) + if mqtt_server.broker.transitions.state == "started": + await mqtt_server.broker.shutdown() + await mqtt_helperbot.Client.disconnect() + if xmpp_server.server: + if xmpp_server.server._serving: + xmpp_server.server.close() + await xmpp_server.server.wait_closed() + 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): return int(round(timetoconvert * 1000)) @@ -89,20 +239,8 @@ def db_file(): return os_db_path() -def os_db_path(createdir=True): - # createdir - Set to False during tests to not create paths - 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 os_db_path(): # createdir=True): + return os.path.join(data_dir, "bumper.db") def db_get(): @@ -300,12 +438,6 @@ class VacBotDevice(object): "xmpp_connection": self.xmpp_connection, } - def toJSON(self): - return json.dumps( - self, default=lambda o: o.__dict__, sort_keys=False - ) # , indent=4) - - class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home UILogicId = "" ota = True @@ -811,3 +943,111 @@ API_ERRORS = { ERR_WRONG_EMAIL_ADDRESS: "1008", ERR_WRONG_PWD_FROMATE: "1009", } + +def create_certs(): + import platform + import os + import subprocess + import sys + path = os.path.dirname(sys.modules[__name__].__file__) + path = os.path.join(path, "..") + sys.path.insert(0, path) + + print("Creating certificates") + odir = os.path.realpath(os.curdir) + os.chdir("certs") + if str(platform.system()).lower() == "windows": + # run for win + subprocess.run( + [os.path.join("..", "create_certs", "create_certs_windows.exe")] + ) + elif str(platform.system()).lower() == "darwin": + # run on mac + subprocess.run([os.path.join("..", "create_certs", "create_certs_osx")]) + elif str(platform.system()).lower() == "linux": + if "arm" in platform.machine().lower(): + # run for pi + subprocess.run([os.path.join("..", "create_certs", "create_certs_rpi")]) + else: + # run for linux + subprocess.run( + [os.path.join("..", "create_certs", "create_certs_linux")] + ) + + else: + logging.log(logging.FATAL, "Can't determine platform. Create certs manually and try again.") + return + + print("Certificates created") + os.chdir(odir) + print(os.path.realpath(os.curdir)) + if "__main__.py" in sys.argv[0]: + os.execv( + sys.executable, ["python", "-m", "bumper"] + sys.argv[1:] + ) # Start again + + else: + os.execv(sys.executable, ["python"] + sys.argv) # Start again + +def firstrun_input(): + return input( + "No certificates found, would you like to create them automatically? (y/n): " + ).lower() + +def first_run(): + yes = {"yes", "y", "ye", ""} + print("") + if firstrun_input() in yes: + create_certs() + + else: + logging.log(logging.FATAL, "Can't continue without certificates, please create some then try again.") + +def main(argv=None): + import argparse + global bumper_debug + global bumper_listen + global bumper_announce_ip + + try: + + if not ( + os.path.exists(ca_cert) + and os.path.exists(server_cert) + and os.path.exists(server_key) + ): + first_run() + return + + parser = argparse.ArgumentParser() + parser.add_argument( + "--listen", type=str, default=None, help="start serving on address" + ) + parser.add_argument( + "--announce", type=str, default=None, help="announce address to bots on checkin" + ) + parser.add_argument("--debug", action="store_true", help="enable debug logs") + args = parser.parse_args(args=argv) + + if args.debug: + bumper_debug = True + + if args.listen: + bumper_listen = args.listen + + if args.announce: + bumper_announce_ip = args.announce + + asyncio.run(start()) + + except KeyboardInterrupt: + bumperlog.info("Keyboard Interrupt!") + pass + + except Exception as e: + bumperlog.exception(e) + pass + + finally: + asyncio.run(shutdown()) + diff --git a/bumper/__main__.py b/bumper/__main__.py new file mode 100644 index 0000000..7434132 --- /dev/null +++ b/bumper/__main__.py @@ -0,0 +1,4 @@ +import bumper + +if __name__ == "__main__": + bumper.main() \ No newline at end of file diff --git a/bumper/confserver.py b/bumper/confserver.py index 5180769..afc9fe1 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -from threading import Thread -import socket, logging, ssl, json +import json +import logging +import ssl import string import random import bumper -import time from datetime import datetime, timedelta import asyncio from aiohttp import web @@ -63,6 +63,8 @@ class ConfServer: self.confthread = None self.run_async = False self.app = None + self.site = None + self.runner = None def confserver_app(self): self.app = web.Application(loop=asyncio.get_event_loop()) @@ -167,36 +169,43 @@ class ConfServer: confserverlog.info( "Starting ConfServer at {}:{}".format(self.address[0], self.address[1]) ) - runner = web.AppRunner(self.app) - await runner.setup() + self.runner = web.AppRunner(self.app) + await self.runner.setup() if self.usessl: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key) - site = web.TCPSite( - runner, + self.site = web.TCPSite( + self.runner, host=self.address[0], port=self.address[1], ssl_context=ssl_ctx, ) else: - site = web.TCPSite(runner, host=self.address[0], port=self.address[1]) + self.site = web.TCPSite( + self.runner, host=self.address[0], port=self.address[1] + ) - await site.start() + await self.site.start() except PermissionError as e: - if "bind" in e.strerror: - confserverlog.exception( - "Error binding confserver, exiting. Try using a different hostname or IP - {}".format( - e - ) - ) - exit(1) + confserverlog.error(e.strerror) + asyncio.create_task(bumper.shutdown()) + + except asyncio.CancelledError: + pass + + except Exception as e: + confserverlog.exception("{}".format(e)) + asyncio.create_task(bumper.shutdown()) + + async def stop_server(self): + try: + await self.runner.shutdown() except Exception as e: confserverlog.exception("{}".format(e)) - exit(1) async def handle_base(self, request): try: @@ -877,19 +886,26 @@ class ConfServer: if todo == "FindBest": service = postbody["service"] if service == "EcoMsgNew": - srvip = socket.gethostbyname(socket.gethostname()) + srvip = bumper.bumper_announce_ip srvport = 5223 confserverlog.info( - "Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( + "Announcing EcoMsgNew Server to bot as: {}:{}".format( 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": srvip = "47.88.66.164" # EcoVacs Server srvport = 8005 confserverlog.info( - "Reporting FindBest-EcoUpdate Server to Bot as: {}:{}".format( + "Announcing EcoUpdate Server to bot as: {}:{}".format( srvip, srvport ) ) @@ -937,9 +953,7 @@ class ConfServer: bumper.bot_remove(postbody["did"]) body = {"result": "ok", "todo": "result"} - confserverlog.debug( - "\r\n POST: {} \r\n Response: {}".format(postbody, body) - ) + confserverlog.debug("POST: {} - Response: {}".format(postbody, body)) return web.json_response(body) @@ -982,9 +996,7 @@ class ConfServer: "todo": "result", } - confserverlog.debug( - "\r\n POST: {} \r\n Response: {}".format(postbody, body) - ) + confserverlog.debug("POST: {} - Response: {}".format(postbody, body)) return web.json_response(body) @@ -1012,10 +1024,10 @@ class ConfServer: if todo == "FindBest": service = postbody["service"] if service == "EcoMsgNew": - srvip = socket.gethostbyname(socket.gethostname()) + srvip = bumper.bumper_announce_ip srvport = 5223 confserverlog.info( - "Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( + "Announcing EcoMsgNew Server to bot as: {}:{}".format( srvip, srvport ) ) @@ -1025,17 +1037,18 @@ class ConfServer: " ", "" ) # 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) elif service == "EcoUpdate": - body = {"result": "ok", "ip": "47.88.66.164", "port": 8005} + srvip = "47.88.66.164" # EcoVacs Server + srvport = 8005 + confserverlog.info( + "Announcing EcoUpdate Server to bot as: {}:{}".format( + srvip, srvport + ) + ) + body = {"result": "ok", "ip": srvip, "port": srvport} - confserverlog.debug( - "\r\n POST: {} \r\n Response: {}".format(postbody, body) - ) return web.json_response(body) except Exception as e: @@ -1097,7 +1110,7 @@ class ConfServer: body = {"ret": "ok", "logs": []} confserverlog.debug( - "\r\n POST: {} \r\n Response: {}".format(json_body, body) + "POST: {} - Response: {}".format(json_body, body) ) return web.json_response(body) diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index 4edaca7..5e247a4 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -9,8 +9,6 @@ from hbmqtt.client import MQTTClient from hbmqtt.mqtt.constants import QOS_0, QOS_1, QOS_2 import pkg_resources import time -from threading import Thread -import ssl import bumper import json from datetime import datetime, timedelta @@ -60,8 +58,20 @@ class MQTTHelperBot: ("iot/atr/+", QOS_0), ] ) + asyncio.create_task(self.get_msg()) + except ConnectionRefusedError as e: + helperbotlog.Error(e) + pass + + except asyncio.CancelledError as e: + pass + + except hbmqtt.client.ConnectException as e: + helperbotlog.Error(e) + pass + except Exception as e: helperbotlog.exception("{}".format(e)) @@ -194,25 +204,23 @@ class MQTTServer: broker = None async def broker_coro(self): + + mqttserverlog.info( + "Starting MQTT Server at {}:{}".format(self.address[0], self.address[1]) + ) + self.broker = hbmqtt.broker.Broker(config=self.default_config) + try: - mqttserverlog.info( - "Starting MQTT Server at {}:{}".format(self.address[0], self.address[1]) - ) - self.broker = hbmqtt.broker.Broker(config=self.default_config) await self.broker.start() - except PermissionError as e: - if "bind" in e.strerror: - mqttserverlog.exception( - "Error binding mqttserver, exiting. Try using a different hostname or IP - {}".format( - e - ) - ) - exit(1) + except hbmqtt.broker.BrokerException as e: + mqttserverlog.exception(e) + asyncio.create_task(bumper.shutdown()) + pass except Exception as e: mqttserverlog.exception("{}".format(e)) - exit(1) + asyncio.create_task(bumper.shutdown()) def __init__(self, address): try: @@ -300,10 +308,8 @@ class BumperMQTTServer_Plugin: "eco-ng", ) - mqttserverlog.debug( - "new bot authenticated SN: {} DID: {}".format( - username, didsplit[0] - ) + mqttserverlog.info( + "bot authenticated SN: {} DID: {}".format(username, didsplit[0]) ) authenticated = True @@ -324,9 +330,7 @@ class BumperMQTTServer_Plugin: if auth: bumper.client_add(userid, realm, resource) - mqttserverlog.debug( - "client authenticated {}".format(userid) - ) + mqttserverlog.info("client authenticated {}".format(userid)) authenticated = True else: diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index 453de22..ef43a58 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 -from threading import Thread -import sys, socket, threading, re, time, logging, uuid, xml.etree.ElementTree as ET +import logging +import re +import uuid +import xml.etree.ElementTree as ET import base64 import ssl import bumper -import asyncio, functools +import asyncio xmppserverlog = logging.getLogger("xmppserver") @@ -22,17 +24,30 @@ class XMPPServer: self.xmpp_protocol = lambda: XMPPServer_Protocol() async def start_async_server(self): - xmppserverlog.info( - "Starting XMPP Server at {}:{}".format(self.address[0], self.address[1]) - ) + try: + xmppserverlog.info( + "Starting XMPP Server at {}:{}".format(self.address[0], self.address[1]) + ) - loop = asyncio.get_running_loop() + loop = asyncio.get_running_loop() - self.server = await loop.create_server( - self.xmpp_protocol, host=self.address[0], port=self.address[1] - ) + self.server = await loop.create_server( + self.xmpp_protocol, host=self.address[0], port=self.address[1] + ) - self.server_coro = loop.create_task(self.server.serve_forever()) + self.server_coro = loop.create_task(self.server.serve_forever()) + + except PermissionError as e: + xmppserverlog.error(e.strerror) + asyncio.create_task(bumper.shutdown()) + pass + + except asyncio.CancelledError: + pass + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + asyncio.create_task(bumper.shutdown()) def disconnect(self): try: @@ -51,10 +66,6 @@ class XMPPServer: class XMPPServer_Protocol(asyncio.Protocol): client_id = None 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 def connection_made(self, transport): @@ -506,7 +517,7 @@ class XMPPAsyncClient: if self.devclass: # if there is a devclass it is a bot bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy") self.type = self.BOT - xmppserverlog.debug("bot authenticated {}".format(self.uid)) + xmppserverlog.info("bot authenticated SN: {}".format(self.uid)) # Send response self.send( '' @@ -525,7 +536,7 @@ class XMPPAsyncClient: if auth: self.type = self.CONTROLLER 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 self._set_state("INIT") diff --git a/certs/CA/cacert.crt b/certs/CA/cacert.crt deleted file mode 100644 index 6d6a03e..0000000 Binary files a/certs/CA/cacert.crt and /dev/null differ diff --git a/certs/CA/cacert.pem b/certs/CA/cacert.pem deleted file mode 100644 index a219c22..0000000 --- a/certs/CA/cacert.pem +++ /dev/null @@ -1,85 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11078087720677522868 (0x99bd46167599bdb4) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Validity - Not Before: Feb 1 05:59:32 2019 GMT - Not After : Jan 29 05:59:32 2029 GMT - Subject: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:aa:6e:af:15:99:3b:09:32:a1:a7:4b:ee:f7:13: - 68:c1:94:4f:09:71:54:99:02:2d:27:c1:5f:16:2e: - 45:d3:a6:44:24:fb:bd:f0:9b:a9:f6:6c:49:6f:09: - 13:76:d6:5b:88:c2:fc:8b:8b:14:4c:39:df:8a:b5: - 51:a8:83:c1:73:ca:aa:f1:b7:b4:38:e1:f0:91:35: - be:6f:c6:99:7c:e1:ef:d9:c0:ee:d5:fb:f8:f5:0f: - a7:96:b3:78:95:d6:2a:04:7d:3b:31:42:b6:33:c0: - 36:2c:06:f1:6d:db:11:99:16:3e:77:c6:c6:df:ed: - 95:40:b8:39:a2:82:40:fe:bb:f6:8b:63:0c:c6:96: - ae:fd:3b:c7:79:a4:f5:5f:4e:83:87:27:33:bb:6f: - cf:43:86:55:73:6c:0c:28:61:89:66:72:b3:90:3d: - d8:54:53:6a:3a:57:76:01:05:58:ac:78:3d:0d:0a: - 52:b4:b7:be:2b:4e:9a:6a:f4:f8:a2:3f:21:99:b6: - b4:74:fb:d5:c0:23:7b:e8:51:f5:0a:ec:6a:11:4e: - 9b:c3:f2:56:00:06:3b:58:e3:cf:67:b6:c3:26:50: - 0e:f7:72:11:3f:39:2c:c9:b7:d5:69:1a:33:dc:8f: - 81:66:c5:88:3f:c7:01:94:f0:3d:ac:aa:f3:a0:2a: - e8:c7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - 5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - X509v3 Authority Key Identifier: - keyid:5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - DirName:/C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper - serial:99:BD:46:16:75:99:BD:B4 - - X509v3 Basic Constraints: - CA:TRUE - X509v3 Subject Alternative Name: - email:null@null.com - Signature Algorithm: sha1WithRSAEncryption - 5e:45:c7:34:7f:9b:a4:6b:cc:3c:e7:34:84:76:ee:ec:98:be: - 0f:de:ab:b2:a5:6d:59:18:eb:20:de:a4:fc:2c:4d:e4:9a:9c: - 63:99:be:16:6a:1d:c0:57:73:e8:7f:9e:78:4d:2a:e6:11:a3: - b6:57:18:84:6e:ae:a7:97:08:7e:1f:4d:33:fc:46:06:a7:d1: - 6e:3a:d1:d9:ef:ba:fd:87:b8:e0:ea:51:85:d2:a6:de:49:e1: - a6:43:d1:26:15:e2:f9:8b:8c:f0:94:fe:a3:62:dd:cc:ab:be: - a5:dc:90:d8:28:0c:85:4b:fb:df:4a:f1:ac:35:86:7f:63:fd: - ca:c6:aa:29:0b:b0:e1:e3:08:e6:4c:71:d2:08:32:c9:09:22: - 6c:61:ec:87:49:8d:77:48:7a:f7:75:86:bb:4e:17:b0:fe:43: - 4d:2f:95:bd:d5:0f:c1:ef:ba:f7:3e:e0:b6:2f:fa:4a:c9:45: - 36:97:56:a4:6d:af:86:88:ff:98:2f:b2:e4:04:4d:c4:02:20: - fc:66:8c:d5:2b:e9:5a:6c:1f:d2:67:34:c3:39:7e:f4:c7:37: - 53:6b:11:99:a8:29:8b:3f:f6:3a:82:ae:f5:2c:d5:b7:23:88: - bb:03:84:ad:cb:d0:f6:8d:24:9f:54:08:d1:39:da:1a:a9:f6: - 1b:c9:53:65 ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJAJm9RhZ1mb20MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV -BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ8wDQYDVQQKEwZCdW1wZXIxDzAN -BgNVBAsTBkJ1bXBlcjEPMA0GA1UEAxMGQnVtcGVyMB4XDTE5MDIwMTA1NTkzMloX -DTI5MDEyOTA1NTkzMlowVTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju -aWExDzANBgNVBAoTBkJ1bXBlcjEPMA0GA1UECxMGQnVtcGVyMQ8wDQYDVQQDEwZC -dW1wZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqbq8VmTsJMqGn -S+73E2jBlE8JcVSZAi0nwV8WLkXTpkQk+73wm6n2bElvCRN21luIwvyLixRMOd+K -tVGog8Fzyqrxt7Q44fCRNb5vxpl84e/ZwO7V+/j1D6eWs3iV1ioEfTsxQrYzwDYs -BvFt2xGZFj53xsbf7ZVAuDmigkD+u/aLYwzGlq79O8d5pPVfToOHJzO7b89DhlVz -bAwoYYlmcrOQPdhUU2o6V3YBBViseD0NClK0t74rTppq9PiiPyGZtrR0+9XAI3vo -UfUK7GoRTpvD8lYABjtY489ntsMmUA73chE/OSzJt9VpGjPcj4FmxYg/xwGU8D2s -qvOgKujHAgMBAAGjgdIwgc8wHQYDVR0OBBYEFF45x3gxgzBRgtmIm3MLcu9EdHvV -MIGFBgNVHSMEfjB8gBReOcd4MYMwUYLZiJtzC3LvRHR71aFZpFcwVTELMAkGA1UE -BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAoTBkJ1bXBlcjEPMA0G -A1UECxMGQnVtcGVyMQ8wDQYDVQQDEwZCdW1wZXKCCQCZvUYWdZm9tDAMBgNVHRME -BTADAQH/MBgGA1UdEQQRMA+BDW51bGxAbnVsbC5jb20wDQYJKoZIhvcNAQEFBQAD -ggEBAF5FxzR/m6RrzDznNIR27uyYvg/eq7KlbVkY6yDepPwsTeSanGOZvhZqHcBX -c+h/nnhNKuYRo7ZXGIRurqeXCH4fTTP8Rgan0W460dnvuv2HuODqUYXSpt5J4aZD -0SYV4vmLjPCU/qNi3cyrvqXckNgoDIVL+99K8aw1hn9j/crGqikLsOHjCOZMcdII -MskJImxh7IdJjXdIevd1hrtOF7D+Q00vlb3VD8Hvuvc+4LYv+krJRTaXVqRtr4aI -/5gvsuQETcQCIPxmjNUr6VpsH9JnNMM5fvTHN1NrEZmoKYs/9jqCrvUs1bcjiLsD -hK3L0PaNJJ9UCNE52hqp9hvJU2U= ------END CERTIFICATE----- diff --git a/certs/CA/careq.pem b/certs/CA/careq.pem deleted file mode 100644 index f4c94e5..0000000 --- a/certs/CA/careq.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIC2TCCAcECAQAwaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEx -EjAQBgNVBAcTCUhhd3Rob3JuZTEPMA0GA1UEChMGQnVtcGVyMQ8wDQYDVQQLEwZC -dW1wZXIxDzANBgNVBAMTBkJ1bXBlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAKpurxWZOwkyoadL7vcTaMGUTwlxVJkCLSfBXxYuRdOmRCT7vfCbqfZs -SW8JE3bWW4jC/IuLFEw534q1UaiDwXPKqvG3tDjh8JE1vm/GmXzh79nA7tX7+PUP -p5azeJXWKgR9OzFCtjPANiwG8W3bEZkWPnfGxt/tlUC4OaKCQP679otjDMaWrv07 -x3mk9V9Og4cnM7tvz0OGVXNsDChhiWZys5A92FRTajpXdgEFWKx4PQ0KUrS3vitO -mmr0+KI/IZm2tHT71cAje+hR9QrsahFOm8PyVgAGO1jjz2e2wyZQDvdyET85LMm3 -1WkaM9yPgWbFiD/HAZTwPayq86Aq6McCAwEAAaArMCkGCSqGSIb3DQEJDjEcMBow -GAYDVR0RBBEwD4ENbnVsbEBudWxsLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAQU/v -8GRj5M3Qs0at2UGxz7uKZF5RKt6hE5WreS/joaMkK4+kTqA8FsfCGz0zUyyLWNKh -1o4b99yvYXG+rP8euMyBql/WSnEcF0KYrrGyUjH0qQQfTHqC0UqolvqoQCkPbvUO -sWZVzHeVxIH3nNtXgkIS+PHvTIhDLA3CxrDSe5jz4XLaACPoK/dawojAd0Qet0zZ -GfXk9Vm/5D0k/7MPkstOTAFCvfln2cdoYhA34WNkPW+uZNGBEdXTN3Tg/Oj8qAqV -RdIXR8joIzgAJnCit1IDyTPHR88UNTlFtk4AJ9Ott4v1hmUOf3kBqB0owXj+fCpq -3Mz7yL1DioePr8h7iw== ------END CERTIFICATE REQUEST----- diff --git a/certs/CA/certreqs/bumper_req.txt b/certs/CA/certreqs/bumper_req.txt deleted file mode 100644 index f59043e..0000000 --- a/certs/CA/certreqs/bumper_req.txt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIC0DCCAbgCAQAwETEPMA0GA1UEAwwGQnVtcGVyMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA0eEqslnaJcbhqTalVMQTNT8/s6957rLSw78cZSVQEISN -MwyS4Q5tYYJzXXEmqyp8rBMWAArfWGaPO8sgKs6rC9UcYurVr1GbxtsVdRRRI/fg -tt2rtWQYdkBI7Dz9vjkcUF1tWwydWzPtKunzHGq9XNWY8NvJ4eq5dnO1oF1wyPWe -5bbKURJpA3gOXuCtEY+TOv++8PK3vdPQnkE1pFRnTfBP5rmatnReFlbfxCUCwcnq -X5a0+BeDanw4GSm4m0MCFXGWEb07P+8JUdUP+2rbMIZwUb42BBz/UKBeg410vgwh -PISyQetW4XGn3bLJk933GFO6gvOU9CVdzPdxRPPLPQIDAQABoHoweAYJKoZIhvcN -AQkOMWswaTAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEG -CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwJwYDVR0RBCAwHoINKi5lY291c2VyLm5l -dIINKi5lY292YWNzLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAloHqI4fjQEL0H0I0 -TMr+DemH5GhYyVC7hs5bPfajqUbrQdTMvfUhfcUm0JgAmicKlAvVe1+75grkXeCF -sfGBl10f5dbO9CEHBT9kzDJWhFT/ZFzGlblAwB6Kad7QGpIMKncJCZBUwkE4zrwO -wEI1hAo0zvJr/SETte4YO9JianzT1Tu5f3nVgvKYVM+XJ0KZ7ReIdrT0Ev95mZhB -tmZ1R7WeYtO8yZXVHP28uOHZcHCKgKcnrpb13yuNd6mkR643xMoirwp9t7xayJAt -RnQgvb9o8HXNZoO1whr4ZF8gNs0MQGnT72MoV5E2KSZ9HMO4nQlzj9wIiEMls+ss -UTUhBg== ------END CERTIFICATE REQUEST----- - diff --git a/certs/CA/certsdb/99BD46167599BDB4.pem b/certs/CA/certsdb/99BD46167599BDB4.pem deleted file mode 100644 index a219c22..0000000 --- a/certs/CA/certsdb/99BD46167599BDB4.pem +++ /dev/null @@ -1,85 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11078087720677522868 (0x99bd46167599bdb4) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Validity - Not Before: Feb 1 05:59:32 2019 GMT - Not After : Jan 29 05:59:32 2029 GMT - Subject: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:aa:6e:af:15:99:3b:09:32:a1:a7:4b:ee:f7:13: - 68:c1:94:4f:09:71:54:99:02:2d:27:c1:5f:16:2e: - 45:d3:a6:44:24:fb:bd:f0:9b:a9:f6:6c:49:6f:09: - 13:76:d6:5b:88:c2:fc:8b:8b:14:4c:39:df:8a:b5: - 51:a8:83:c1:73:ca:aa:f1:b7:b4:38:e1:f0:91:35: - be:6f:c6:99:7c:e1:ef:d9:c0:ee:d5:fb:f8:f5:0f: - a7:96:b3:78:95:d6:2a:04:7d:3b:31:42:b6:33:c0: - 36:2c:06:f1:6d:db:11:99:16:3e:77:c6:c6:df:ed: - 95:40:b8:39:a2:82:40:fe:bb:f6:8b:63:0c:c6:96: - ae:fd:3b:c7:79:a4:f5:5f:4e:83:87:27:33:bb:6f: - cf:43:86:55:73:6c:0c:28:61:89:66:72:b3:90:3d: - d8:54:53:6a:3a:57:76:01:05:58:ac:78:3d:0d:0a: - 52:b4:b7:be:2b:4e:9a:6a:f4:f8:a2:3f:21:99:b6: - b4:74:fb:d5:c0:23:7b:e8:51:f5:0a:ec:6a:11:4e: - 9b:c3:f2:56:00:06:3b:58:e3:cf:67:b6:c3:26:50: - 0e:f7:72:11:3f:39:2c:c9:b7:d5:69:1a:33:dc:8f: - 81:66:c5:88:3f:c7:01:94:f0:3d:ac:aa:f3:a0:2a: - e8:c7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - 5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - X509v3 Authority Key Identifier: - keyid:5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - DirName:/C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper - serial:99:BD:46:16:75:99:BD:B4 - - X509v3 Basic Constraints: - CA:TRUE - X509v3 Subject Alternative Name: - email:null@null.com - Signature Algorithm: sha1WithRSAEncryption - 5e:45:c7:34:7f:9b:a4:6b:cc:3c:e7:34:84:76:ee:ec:98:be: - 0f:de:ab:b2:a5:6d:59:18:eb:20:de:a4:fc:2c:4d:e4:9a:9c: - 63:99:be:16:6a:1d:c0:57:73:e8:7f:9e:78:4d:2a:e6:11:a3: - b6:57:18:84:6e:ae:a7:97:08:7e:1f:4d:33:fc:46:06:a7:d1: - 6e:3a:d1:d9:ef:ba:fd:87:b8:e0:ea:51:85:d2:a6:de:49:e1: - a6:43:d1:26:15:e2:f9:8b:8c:f0:94:fe:a3:62:dd:cc:ab:be: - a5:dc:90:d8:28:0c:85:4b:fb:df:4a:f1:ac:35:86:7f:63:fd: - ca:c6:aa:29:0b:b0:e1:e3:08:e6:4c:71:d2:08:32:c9:09:22: - 6c:61:ec:87:49:8d:77:48:7a:f7:75:86:bb:4e:17:b0:fe:43: - 4d:2f:95:bd:d5:0f:c1:ef:ba:f7:3e:e0:b6:2f:fa:4a:c9:45: - 36:97:56:a4:6d:af:86:88:ff:98:2f:b2:e4:04:4d:c4:02:20: - fc:66:8c:d5:2b:e9:5a:6c:1f:d2:67:34:c3:39:7e:f4:c7:37: - 53:6b:11:99:a8:29:8b:3f:f6:3a:82:ae:f5:2c:d5:b7:23:88: - bb:03:84:ad:cb:d0:f6:8d:24:9f:54:08:d1:39:da:1a:a9:f6: - 1b:c9:53:65 ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJAJm9RhZ1mb20MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV -BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ8wDQYDVQQKEwZCdW1wZXIxDzAN -BgNVBAsTBkJ1bXBlcjEPMA0GA1UEAxMGQnVtcGVyMB4XDTE5MDIwMTA1NTkzMloX -DTI5MDEyOTA1NTkzMlowVTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju -aWExDzANBgNVBAoTBkJ1bXBlcjEPMA0GA1UECxMGQnVtcGVyMQ8wDQYDVQQDEwZC -dW1wZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqbq8VmTsJMqGn -S+73E2jBlE8JcVSZAi0nwV8WLkXTpkQk+73wm6n2bElvCRN21luIwvyLixRMOd+K -tVGog8Fzyqrxt7Q44fCRNb5vxpl84e/ZwO7V+/j1D6eWs3iV1ioEfTsxQrYzwDYs -BvFt2xGZFj53xsbf7ZVAuDmigkD+u/aLYwzGlq79O8d5pPVfToOHJzO7b89DhlVz -bAwoYYlmcrOQPdhUU2o6V3YBBViseD0NClK0t74rTppq9PiiPyGZtrR0+9XAI3vo -UfUK7GoRTpvD8lYABjtY489ntsMmUA73chE/OSzJt9VpGjPcj4FmxYg/xwGU8D2s -qvOgKujHAgMBAAGjgdIwgc8wHQYDVR0OBBYEFF45x3gxgzBRgtmIm3MLcu9EdHvV -MIGFBgNVHSMEfjB8gBReOcd4MYMwUYLZiJtzC3LvRHR71aFZpFcwVTELMAkGA1UE -BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAoTBkJ1bXBlcjEPMA0G -A1UECxMGQnVtcGVyMQ8wDQYDVQQDEwZCdW1wZXKCCQCZvUYWdZm9tDAMBgNVHRME -BTADAQH/MBgGA1UdEQQRMA+BDW51bGxAbnVsbC5jb20wDQYJKoZIhvcNAQEFBQAD -ggEBAF5FxzR/m6RrzDznNIR27uyYvg/eq7KlbVkY6yDepPwsTeSanGOZvhZqHcBX -c+h/nnhNKuYRo7ZXGIRurqeXCH4fTTP8Rgan0W460dnvuv2HuODqUYXSpt5J4aZD -0SYV4vmLjPCU/qNi3cyrvqXckNgoDIVL+99K8aw1hn9j/crGqikLsOHjCOZMcdII -MskJImxh7IdJjXdIevd1hrtOF7D+Q00vlb3VD8Hvuvc+4LYv+krJRTaXVqRtr4aI -/5gvsuQETcQCIPxmjNUr6VpsH9JnNMM5fvTHN1NrEZmoKYs/9jqCrvUs1bcjiLsD -hK3L0PaNJJ9UCNE52hqp9hvJU2U= ------END CERTIFICATE----- diff --git a/certs/CA/certsdb/99BD46167599BDB5.pem b/certs/CA/certsdb/99BD46167599BDB5.pem deleted file mode 100644 index 64ccbbf..0000000 --- a/certs/CA/certsdb/99BD46167599BDB5.pem +++ /dev/null @@ -1,85 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11078087720677522869 (0x99bd46167599bdb5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Validity - Not Before: Feb 1 06:17:18 2019 GMT - Not After : Jan 29 06:17:18 2029 GMT - Subject: CN=Bumper - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:d1:e1:2a:b2:59:da:25:c6:e1:a9:36:a5:54:c4: - 13:35:3f:3f:b3:af:79:ee:b2:d2:c3:bf:1c:65:25: - 50:10:84:8d:33:0c:92:e1:0e:6d:61:82:73:5d:71: - 26:ab:2a:7c:ac:13:16:00:0a:df:58:66:8f:3b:cb: - 20:2a:ce:ab:0b:d5:1c:62:ea:d5:af:51:9b:c6:db: - 15:75:14:51:23:f7:e0:b6:dd:ab:b5:64:18:76:40: - 48:ec:3c:fd:be:39:1c:50:5d:6d:5b:0c:9d:5b:33: - ed:2a:e9:f3:1c:6a:bd:5c:d5:98:f0:db:c9:e1:ea: - b9:76:73:b5:a0:5d:70:c8:f5:9e:e5:b6:ca:51:12: - 69:03:78:0e:5e:e0:ad:11:8f:93:3a:ff:be:f0:f2: - b7:bd:d3:d0:9e:41:35:a4:54:67:4d:f0:4f:e6:b9: - 9a:b6:74:5e:16:56:df:c4:25:02:c1:c9:ea:5f:96: - b4:f8:17:83:6a:7c:38:19:29:b8:9b:43:02:15:71: - 96:11:bd:3b:3f:ef:09:51:d5:0f:fb:6a:db:30:86: - 70:51:be:36:04:1c:ff:50:a0:5e:83:8d:74:be:0c: - 21:3c:84:b2:41:eb:56:e1:71:a7:dd:b2:c9:93:dd: - f7:18:53:ba:82:f3:94:f4:25:5d:cc:f7:71:44:f3: - cb:3d - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - X509v3 Subject Key Identifier: - 26:7E:4F:14:80:43:9E:EF:64:C2:61:6E:81:68:4B:6A:F2:4E:B5:40 - X509v3 Authority Key Identifier: - keyid:5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - - X509v3 Subject Alternative Name: - DNS:*.ecouser.net, DNS:*.ecovacs.com - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: critical - TLS Web Server Authentication, TLS Web Client Authentication - Signature Algorithm: sha1WithRSAEncryption - 89:11:41:18:ac:22:0d:4a:08:31:ee:18:d4:cf:88:02:93:f0: - ef:53:f5:86:e1:b2:16:0e:8d:87:f9:e9:29:7a:aa:95:4e:d7: - 89:96:ac:1c:9a:19:8c:84:62:41:7a:b8:8c:46:07:8f:56:ae: - 18:31:52:cb:60:3f:01:57:4d:65:d6:5f:95:da:bc:c1:53:35: - fa:cc:c6:49:aa:fe:35:d3:05:15:2c:bd:9b:ac:f0:36:d7:71: - bc:ed:3e:f9:ac:c8:84:58:5e:f0:02:02:47:9c:0f:f3:f6:06: - 35:4d:3b:69:2f:bb:81:cd:ae:61:84:75:97:b1:c2:e7:e1:74: - 18:61:bc:c0:6b:2e:4d:f6:61:0d:36:72:3d:e2:ef:ef:e5:cf: - 7a:17:af:d6:7c:d2:a4:88:ca:b3:4e:27:7e:f3:da:96:33:ef: - 12:12:02:5a:31:70:82:e9:b6:e4:a9:57:d7:a4:b3:f6:3f:ea: - fa:63:6a:e6:b7:26:73:bb:c1:87:4e:e2:13:40:65:b9:04:3c: - c9:95:d7:8e:07:6f:9d:af:d2:36:ee:aa:5d:e5:f9:bb:cc:47: - 9c:ce:62:cf:28:2e:a3:58:8b:63:a2:e3:c3:f4:b0:69:98:aa: - 84:ff:a5:c2:52:8b:e8:b4:5c:2c:67:ef:63:b3:5c:42:95:6d: - 6b:d7:5b:11 ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIJAJm9RhZ1mb21MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV -BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ8wDQYDVQQKEwZCdW1wZXIxDzAN -BgNVBAsTBkJ1bXBlcjEPMA0GA1UEAxMGQnVtcGVyMB4XDTE5MDIwMTA2MTcxOFoX -DTI5MDEyOTA2MTcxOFowETEPMA0GA1UEAwwGQnVtcGVyMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA0eEqslnaJcbhqTalVMQTNT8/s6957rLSw78cZSVQ -EISNMwyS4Q5tYYJzXXEmqyp8rBMWAArfWGaPO8sgKs6rC9UcYurVr1GbxtsVdRRR -I/fgtt2rtWQYdkBI7Dz9vjkcUF1tWwydWzPtKunzHGq9XNWY8NvJ4eq5dnO1oF1w -yPWe5bbKURJpA3gOXuCtEY+TOv++8PK3vdPQnkE1pFRnTfBP5rmatnReFlbfxCUC -wcnqX5a0+BeDanw4GSm4m0MCFXGWEb07P+8JUdUP+2rbMIZwUb42BBz/UKBeg410 -vgwhPISyQetW4XGn3bLJk933GFO6gvOU9CVdzPdxRPPLPQIDAQABo4GpMIGmMAkG -A1UdEwQCMAAwHQYDVR0OBBYEFCZ+TxSAQ57vZMJhboFoS2ryTrVAMB8GA1UdIwQY -MBaAFF45x3gxgzBRgtmIm3MLcu9EdHvVMCcGA1UdEQQgMB6CDSouZWNvdXNlci5u -ZXSCDSouZWNvdmFjcy5jb20wDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQG -CCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAiRFBGKwiDUoI -Me4Y1M+IApPw71P1huGyFg6Nh/npKXqqlU7XiZasHJoZjIRiQXq4jEYHj1auGDFS -y2A/AVdNZdZfldq8wVM1+szGSar+NdMFFSy9m6zwNtdxvO0++azIhFhe8AICR5wP -8/YGNU07aS+7gc2uYYR1l7HC5+F0GGG8wGsuTfZhDTZyPeLv7+XPehev1nzSpIjK -s04nfvPaljPvEhICWjFwgum25KlX16Sz9j/q+mNq5rcmc7vBh07iE0BluQQ8yZXX -jgdvna/SNu6qXeX5u8xHnM5izyguo1iLY6Ljw/SwaZiqhP+lwlKL6LRcLGfvY7Nc -QpVta9dbEQ== ------END CERTIFICATE----- diff --git a/certs/CA/index.txt b/certs/CA/index.txt deleted file mode 100644 index 613826e..0000000 --- a/certs/CA/index.txt +++ /dev/null @@ -1,2 +0,0 @@ -V 290129055932Z 99BD46167599BDB4 unknown /C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper -V 290129061718Z 99BD46167599BDB5 unknown /CN=Bumper diff --git a/certs/CA/index.txt.attr b/certs/CA/index.txt.attr deleted file mode 100644 index 8f7e63a..0000000 --- a/certs/CA/index.txt.attr +++ /dev/null @@ -1 +0,0 @@ -unique_subject = yes diff --git a/certs/CA/index.txt.attr.old b/certs/CA/index.txt.attr.old deleted file mode 100644 index 8f7e63a..0000000 --- a/certs/CA/index.txt.attr.old +++ /dev/null @@ -1 +0,0 @@ -unique_subject = yes diff --git a/certs/CA/index.txt.old b/certs/CA/index.txt.old deleted file mode 100644 index de82fa9..0000000 --- a/certs/CA/index.txt.old +++ /dev/null @@ -1 +0,0 @@ -V 290129055932Z 99BD46167599BDB4 unknown /C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper diff --git a/certs/CA/openssl.cnf b/certs/CA/openssl.cnf deleted file mode 100644 index 77bb39a..0000000 --- a/certs/CA/openssl.cnf +++ /dev/null @@ -1,180 +0,0 @@ -HOME = . -RANDFILE = $ENV::HOME/.rnd - -#################################################################### -# CA Definition -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -# Per the above, this is where we define CA values -[ CA_default ] - -dir = . # Where everything is kept -certs = $dir/certsdb # Where the issued certs are kept -new_certs_dir = $certs # default place for new certs. -database = $dir/index.txt # database index file. -certificate = $dir/cacert.pem # The CA certificate -private_key = $dir/private/cakey.pem# The private key -serial = $dir/serial # The current serial number -RANDFILE = $dir/private/.rand # private random number file - -crldir = $dir/crl -crlnumber = $dir/crlnumber # the current crl number -crl = $crldir/crl.pem # The current CRL - -# By default we use "user certificate" extensions when signing -x509_extensions = usr_cert # The extentions to add to the cert - -# Honor extensions requested of us -copy_extensions = copy - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -#crl_extensions = crl_ext -default_days = 3650 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = sha1 # which md to use. -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_anything - -#################################################################### -# The default policy for the CA when signing requests, requires some -# resemblence to the CA cert -# -[ policy_match ] -countryName = match # Must be the same as the CA -stateOrProvinceName = match # Must be the same as the CA -organizationName = match # Must be the same as the CA -organizationalUnitName = optional # not required -commonName = supplied # must be there, whatever it is -emailAddress = optional # not required - -#################################################################### -# An alternative policy not referred to anywhere in this file. Can -# be used by specifying '-policy policy_anything' to ca(8). -# -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -# This is where we define how to generate CSRs -[ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name # where to get DN for reqs -attributes = req_attributes # req attributes -x509_extensions = v3_ca # The extentions to add to self signed certs -req_extensions = v3_req # The extensions to add to req's -prompt = no - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString. -# utf8only: only UTF8Strings. -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings -# so use this option with caution! -string_mask = nombstr - - -#################################################################### -# Per "req" section, this is where we define DN info -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = US -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = California - -localityName = Locality Name (eg, city) -localityName_default = Hawthorne - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = PhilNet - -organizationalUnitName = Organizational Unit Name (eg, section) - -commonName = Common Name (eg, YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - - -#################################################################### -# We don't want these, but the section must exist -[ req_attributes ] -#challengePassword = A challenge password -#challengePassword_min = 4 -#challengePassword_max = 20 -#unstructuredName = An optional company name - - - -#################################################################### -[ v3_req ] -keyUsage=critical,digitalSignature,keyEncipherment -extendedKeyUsage=critical,serverAuth,clientAuth -basicConstraints=critical,CA:false -subjectAltName = @alt_names -[ alt_names ] -DNS.0 = *.ecouser.net -DNS.1 = *.ecovacs.com - -#################################################################### -# Extensions for when we sign normal certs (specified as default) -[ usr_cert ] -basicConstraints = CA:false -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer -subjectAltName = @alt_names -[ alt_names ] -DNS.0 = *.ecouser.net -DNS.1 = *.ecovacs.com - -#################################################################### -# Same as above, but cert req already has SubjectAltName -[ usr_cert_has_san ] -basicConstraints = CA:false -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer -subjectAltName = @alt_names -[ alt_names ] -DNS.0 = *.ecouser.net -DNS.1 = *.ecovacs.com - -#################################################################### -# Extensions to use when signing a CA -[ v3_ca ] -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always -basicConstraints = CA:true -subjectAltName=email:move - -#################################################################### -# Same as above, but CA req already has SubjectAltName -[ v3_ca_has_san ] -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always -basicConstraints = CA:true - diff --git a/certs/CA/private/cakey.pem b/certs/CA/private/cakey.pem deleted file mode 100644 index fd39a20..0000000 --- a/certs/CA/private/cakey.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIzLScD02ODzQCAggA -MB0GCWCGSAFlAwQBKgQQzjzZ8LiXuLxb/Rx75kGXkwSCBNAmHUh4RTgy1bJmwZN7 -uyb4gGHtY9+ZvKZikibq7LpiwmDAwjjwguJz/HRsquq8fsZDpB+rV4hD4lB3lHuj -jqETzKst5qLTgP0cC9BS0S0P9f0/L7MEcwtiBkds6dVs4Ot6IDr4napC+6wQCBfr -ISrTN8+ls9IVghiS1eOrarFTy2ZxkXFM6PJczLd15/pbTm91etLWl1vnPCJn3MTc -vUvneAChKFiwaX4Cbx8bhQP11OBBNshM1zANSoGAdijRpvRCGfeOIvgLsbBl9N0d -+lV6iJR0TzOr4GquBDXor/y2Mofn6wYRwtSLRUgHgAxy1USosWVuXwrtGO4htPoR -aGt8V3h4XQ90VGAh6ez43/nmkVDgIj8VU9qZ+skCz7Y4TxPZYXUIcnG14KFj9lsk -r+uV6HmHyBKSlSkAE+D9uukQb71oIW83tsSn6I4ulcWaax6VsOS5n/Jrhf9V+Nz0 -e0CjrOJeLUSd2vTiyz03ho32nIrsGFeNDHhFmKzXNynjcW3z6Mx9/dSjqPHn8wFn -0dcVkq0GAZ+L8woNyDhr4e7Hhy6FEDhRX9bWz7FlswnBzym2AzU/zhagDrP2MIQ9 -o/cjAp3AoX70IIF7kIha8OXG4ew4tHpQIAj9E5SG5uEeD6gVTg7HB93dH+NEeLNR -F65wzQHPhf+V2zR7qlpCg3gqzyE9mvvTN/DMAJwASXWXK37ITIjcXhErH/2shHSK -jRwYuQiAWuhPrAr04EUoeHQFM6EAKDqBsFRr+/Ot34DqhAwAOlmkyCM/mw7IDl73 -pX/Ey9CQc2u2PS0zqYkDIzWE1Osek3FIF6JHSzaiRYODjaE6v15FLe7hZ56x6NMI -EDzpScp6HwMg6jsudbONrz3KwvXE6j4+yWpp8tgkZzwsIfnOMtWSrGgX4Oz9Y0Ep -jZ1yKDLQBjKZor2z4FBNze74tEnzJdBl59mQoN3ffFWHRlnkOhAZ+HsaXoIKB6AZ -3NPzlgGhOmtza6HKAwlseMgjpNH7EmeTLplr5nUlEMwwJ2+i8VGS4J9458vipcBm -50MbqaR8N8jAsanxyAbBF7gLPzsezN9Kzus99B6uB2nZIgZIsOeCeBLEZGzz9bUv -AiI3rM38nFkkKeHjJwqcYsO6LiLxdstnaHSveE7d6ugmN3+bNaZ+QCUDFlfELr4M -n5Eiwv3+hlCL8UBubacdQypfYvYI/z8rTdH+5eTVxBjeTCG0QmjDlYiuHcycL+q3 -iE3WxQ0yGpeJL6V985Ej/0SHkudTI49T5o+jcp6HFNS7xQelBGndpXGq9Xyc+OV/ -c+1zYWxz/sMFAbMkCF2pMMLfKL5EBkLBCpOdxHtWFt9JMxuhRfl3YzN81xSV1m0A -FtURjPUSwGqI7QFPl4R4MA1+0AdveZYprGo2nSUQpSoCW9WHl8U8tLIUNtew1MPA -v19h5DSqJ3tj+pPdxmFixoG+hBNk8FO1lJvH6P2an6lYVTlZ3/uDU5/DHTUTHuen -9x8FY3LQDQZA65QEnN092cUxXWw6cha+xXvwrwZGZiWXtuMBh/mfa4apxQ5+KY0N -/RCbIBctvLgijuC+xQw8GqBcG5L7urCOhnyZCXspWW7+loI7uZ0EfpNi7jY8BmAs -3S3MwMwwp+uvloikg3ZTUJmx/g== ------END ENCRYPTED PRIVATE KEY----- diff --git a/certs/CA/private/key.pem b/certs/CA/private/key.pem deleted file mode 100644 index f3ece38..0000000 --- a/certs/CA/private/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDR4SqyWdolxuGp -NqVUxBM1Pz+zr3nustLDvxxlJVAQhI0zDJLhDm1hgnNdcSarKnysExYACt9YZo87 -yyAqzqsL1Rxi6tWvUZvG2xV1FFEj9+C23au1ZBh2QEjsPP2+ORxQXW1bDJ1bM+0q -6fMcar1c1Zjw28nh6rl2c7WgXXDI9Z7ltspREmkDeA5e4K0Rj5M6/77w8re909Ce -QTWkVGdN8E/muZq2dF4WVt/EJQLByepflrT4F4NqfDgZKbibQwIVcZYRvTs/7wlR -1Q/7atswhnBRvjYEHP9QoF6DjXS+DCE8hLJB61bhcafdssmT3fcYU7qC85T0JV3M -93FE88s9AgMBAAECggEBAJ4N3aOWeuv8DDDsUDG3v2dYDFb7DO4viP2UzIN3SAZI -bDVHgM0vmjvWi56Q+bfpNR44+VZq2auvAKrc24qhPLqacNglVWrEIHXuwwf6luhp -YbcU1JyzlqBm5QPTIpvAUoxK73Gj3FS0lCS/Li/2SkjBKn4oZq5Vn/mORfdQpwEO -aY5YzNNCzCwbqL/XcwvN1XRpF9puhBb+o2TtfGPLBf5LiScJWxrtPM7q+dFG/Ef7 -f9L28iX+W6e3lD0/d93XEtlh1IsaZTWkl4m01Etg0f2FFtcja38eG+Yys4YNSpPe -VUWtFL2eDX+u5wv7nbaG9GsZmithXL6xa0PdwOd0KAECgYEA/D7K2KcxS+HeBgcG -V3b0m6QH1bRbqRxzK22TKnCVLAlRW7+5bfK56sHqMrOnDp/PuyAdq93roeJBNnAg -DrUt9i/lFTZ2kSBTJIlKW7cHv2Hocw3sQSDY/PhAXtALULxa8NHadPGsntHQDLzq -TVUNhOU6+GqPcqTp8BeSy+XrGD0CgYEA1QDvbxh/5tQxCFXcKn5KQCpJhbCfKBSW -drd8YF3Gdpxt3d2vTFTOsQ3xyAvEhSV0Jy9WUXobmx+KK8OjZqsR4PoYpYN3IWIW -N9jBKE9bahPRFF3g3FxaeVrCBkZBVK+ZAv/E5gshCyCXOmgHT+Kgi1Xpb4X0J/Pj -T83O7y/DrwECgYEAlagfq4kOy2m307WpQo12NT2N1HIMi/owLoz1apGfP6BLn/bR -anU/bxQcAnbqX9fbZLd6QzUIj2Rwsn611mQSX+Ussng8wn/+NEgsUNv4AclyI4kY -Kc/2TahwcJBYlk8aWZT4WrfkZdov6M7Q6I7U1Ys3gJmggDFuBp/F3amM4TUCgYBv -IofKvUuukjPrF3FCMpZnGCk4Ockwzl0bH+0jEoqERGYGhq35853MP2xRZeJpXftW -npo49K8GptQetXrCDKvNaHAHm6pCnrzc8pkFgTI/DIvgRoMWNpF2E+Z8A5f+qYf6 -SpzfMhtRhPWiobh+8820JWUgrRGQ7u1BLC62GYPOAQKBgG/rt2Xu42DJcQ/wm7wN -LB8zn54G/rKWq+mXQbenpr1lN4zBOGgZILrRb1HTcQ7a19056BHBmx3J52yQAnGH -cxuhOyppsH3WgUkUqYN4bzO1CJorX3cP9udBAcmN4BWLF7pLgT7zHrLkJg05sWnd -UHWm2y7vdXeTvdD381p0Ug0S ------END PRIVATE KEY----- diff --git a/certs/CA/serial b/certs/CA/serial deleted file mode 100644 index 3bf84d2..0000000 --- a/certs/CA/serial +++ /dev/null @@ -1 +0,0 @@ -99BD46167599BDB6 diff --git a/certs/CA/serial.old b/certs/CA/serial.old deleted file mode 100644 index 9d0f0bc..0000000 --- a/certs/CA/serial.old +++ /dev/null @@ -1 +0,0 @@ -99BD46167599BDB5 diff --git a/certs/README.md b/certs/README.md new file mode 100644 index 0000000..9881855 --- /dev/null +++ b/certs/README.md @@ -0,0 +1,3 @@ +# Certs Directory + +Place your created certs in this directory diff --git a/certs/cert.pem b/certs/cert.pem deleted file mode 100644 index 64ccbbf..0000000 --- a/certs/cert.pem +++ /dev/null @@ -1,85 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11078087720677522869 (0x99bd46167599bdb5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=California, O=Bumper, OU=Bumper, CN=Bumper - Validity - Not Before: Feb 1 06:17:18 2019 GMT - Not After : Jan 29 06:17:18 2029 GMT - Subject: CN=Bumper - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:d1:e1:2a:b2:59:da:25:c6:e1:a9:36:a5:54:c4: - 13:35:3f:3f:b3:af:79:ee:b2:d2:c3:bf:1c:65:25: - 50:10:84:8d:33:0c:92:e1:0e:6d:61:82:73:5d:71: - 26:ab:2a:7c:ac:13:16:00:0a:df:58:66:8f:3b:cb: - 20:2a:ce:ab:0b:d5:1c:62:ea:d5:af:51:9b:c6:db: - 15:75:14:51:23:f7:e0:b6:dd:ab:b5:64:18:76:40: - 48:ec:3c:fd:be:39:1c:50:5d:6d:5b:0c:9d:5b:33: - ed:2a:e9:f3:1c:6a:bd:5c:d5:98:f0:db:c9:e1:ea: - b9:76:73:b5:a0:5d:70:c8:f5:9e:e5:b6:ca:51:12: - 69:03:78:0e:5e:e0:ad:11:8f:93:3a:ff:be:f0:f2: - b7:bd:d3:d0:9e:41:35:a4:54:67:4d:f0:4f:e6:b9: - 9a:b6:74:5e:16:56:df:c4:25:02:c1:c9:ea:5f:96: - b4:f8:17:83:6a:7c:38:19:29:b8:9b:43:02:15:71: - 96:11:bd:3b:3f:ef:09:51:d5:0f:fb:6a:db:30:86: - 70:51:be:36:04:1c:ff:50:a0:5e:83:8d:74:be:0c: - 21:3c:84:b2:41:eb:56:e1:71:a7:dd:b2:c9:93:dd: - f7:18:53:ba:82:f3:94:f4:25:5d:cc:f7:71:44:f3: - cb:3d - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - X509v3 Subject Key Identifier: - 26:7E:4F:14:80:43:9E:EF:64:C2:61:6E:81:68:4B:6A:F2:4E:B5:40 - X509v3 Authority Key Identifier: - keyid:5E:39:C7:78:31:83:30:51:82:D9:88:9B:73:0B:72:EF:44:74:7B:D5 - - X509v3 Subject Alternative Name: - DNS:*.ecouser.net, DNS:*.ecovacs.com - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: critical - TLS Web Server Authentication, TLS Web Client Authentication - Signature Algorithm: sha1WithRSAEncryption - 89:11:41:18:ac:22:0d:4a:08:31:ee:18:d4:cf:88:02:93:f0: - ef:53:f5:86:e1:b2:16:0e:8d:87:f9:e9:29:7a:aa:95:4e:d7: - 89:96:ac:1c:9a:19:8c:84:62:41:7a:b8:8c:46:07:8f:56:ae: - 18:31:52:cb:60:3f:01:57:4d:65:d6:5f:95:da:bc:c1:53:35: - fa:cc:c6:49:aa:fe:35:d3:05:15:2c:bd:9b:ac:f0:36:d7:71: - bc:ed:3e:f9:ac:c8:84:58:5e:f0:02:02:47:9c:0f:f3:f6:06: - 35:4d:3b:69:2f:bb:81:cd:ae:61:84:75:97:b1:c2:e7:e1:74: - 18:61:bc:c0:6b:2e:4d:f6:61:0d:36:72:3d:e2:ef:ef:e5:cf: - 7a:17:af:d6:7c:d2:a4:88:ca:b3:4e:27:7e:f3:da:96:33:ef: - 12:12:02:5a:31:70:82:e9:b6:e4:a9:57:d7:a4:b3:f6:3f:ea: - fa:63:6a:e6:b7:26:73:bb:c1:87:4e:e2:13:40:65:b9:04:3c: - c9:95:d7:8e:07:6f:9d:af:d2:36:ee:aa:5d:e5:f9:bb:cc:47: - 9c:ce:62:cf:28:2e:a3:58:8b:63:a2:e3:c3:f4:b0:69:98:aa: - 84:ff:a5:c2:52:8b:e8:b4:5c:2c:67:ef:63:b3:5c:42:95:6d: - 6b:d7:5b:11 ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIJAJm9RhZ1mb21MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV -BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ8wDQYDVQQKEwZCdW1wZXIxDzAN -BgNVBAsTBkJ1bXBlcjEPMA0GA1UEAxMGQnVtcGVyMB4XDTE5MDIwMTA2MTcxOFoX -DTI5MDEyOTA2MTcxOFowETEPMA0GA1UEAwwGQnVtcGVyMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA0eEqslnaJcbhqTalVMQTNT8/s6957rLSw78cZSVQ -EISNMwyS4Q5tYYJzXXEmqyp8rBMWAArfWGaPO8sgKs6rC9UcYurVr1GbxtsVdRRR -I/fgtt2rtWQYdkBI7Dz9vjkcUF1tWwydWzPtKunzHGq9XNWY8NvJ4eq5dnO1oF1w -yPWe5bbKURJpA3gOXuCtEY+TOv++8PK3vdPQnkE1pFRnTfBP5rmatnReFlbfxCUC -wcnqX5a0+BeDanw4GSm4m0MCFXGWEb07P+8JUdUP+2rbMIZwUb42BBz/UKBeg410 -vgwhPISyQetW4XGn3bLJk933GFO6gvOU9CVdzPdxRPPLPQIDAQABo4GpMIGmMAkG -A1UdEwQCMAAwHQYDVR0OBBYEFCZ+TxSAQ57vZMJhboFoS2ryTrVAMB8GA1UdIwQY -MBaAFF45x3gxgzBRgtmIm3MLcu9EdHvVMCcGA1UdEQQgMB6CDSouZWNvdXNlci5u -ZXSCDSouZWNvdmFjcy5jb20wDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQG -CCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAiRFBGKwiDUoI -Me4Y1M+IApPw71P1huGyFg6Nh/npKXqqlU7XiZasHJoZjIRiQXq4jEYHj1auGDFS -y2A/AVdNZdZfldq8wVM1+szGSar+NdMFFSy9m6zwNtdxvO0++azIhFhe8AICR5wP -8/YGNU07aS+7gc2uYYR1l7HC5+F0GGG8wGsuTfZhDTZyPeLv7+XPehev1nzSpIjK -s04nfvPaljPvEhICWjFwgum25KlX16Sz9j/q+mNq5rcmc7vBh07iE0BluQQ8yZXX -jgdvna/SNu6qXeX5u8xHnM5izyguo1iLY6Ljw/SwaZiqhP+lwlKL6LRcLGfvY7Nc -QpVta9dbEQ== ------END CERTIFICATE----- diff --git a/certs/key.pem b/certs/key.pem deleted file mode 100644 index f3ece38..0000000 --- a/certs/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDR4SqyWdolxuGp -NqVUxBM1Pz+zr3nustLDvxxlJVAQhI0zDJLhDm1hgnNdcSarKnysExYACt9YZo87 -yyAqzqsL1Rxi6tWvUZvG2xV1FFEj9+C23au1ZBh2QEjsPP2+ORxQXW1bDJ1bM+0q -6fMcar1c1Zjw28nh6rl2c7WgXXDI9Z7ltspREmkDeA5e4K0Rj5M6/77w8re909Ce -QTWkVGdN8E/muZq2dF4WVt/EJQLByepflrT4F4NqfDgZKbibQwIVcZYRvTs/7wlR -1Q/7atswhnBRvjYEHP9QoF6DjXS+DCE8hLJB61bhcafdssmT3fcYU7qC85T0JV3M -93FE88s9AgMBAAECggEBAJ4N3aOWeuv8DDDsUDG3v2dYDFb7DO4viP2UzIN3SAZI -bDVHgM0vmjvWi56Q+bfpNR44+VZq2auvAKrc24qhPLqacNglVWrEIHXuwwf6luhp -YbcU1JyzlqBm5QPTIpvAUoxK73Gj3FS0lCS/Li/2SkjBKn4oZq5Vn/mORfdQpwEO -aY5YzNNCzCwbqL/XcwvN1XRpF9puhBb+o2TtfGPLBf5LiScJWxrtPM7q+dFG/Ef7 -f9L28iX+W6e3lD0/d93XEtlh1IsaZTWkl4m01Etg0f2FFtcja38eG+Yys4YNSpPe -VUWtFL2eDX+u5wv7nbaG9GsZmithXL6xa0PdwOd0KAECgYEA/D7K2KcxS+HeBgcG -V3b0m6QH1bRbqRxzK22TKnCVLAlRW7+5bfK56sHqMrOnDp/PuyAdq93roeJBNnAg -DrUt9i/lFTZ2kSBTJIlKW7cHv2Hocw3sQSDY/PhAXtALULxa8NHadPGsntHQDLzq -TVUNhOU6+GqPcqTp8BeSy+XrGD0CgYEA1QDvbxh/5tQxCFXcKn5KQCpJhbCfKBSW -drd8YF3Gdpxt3d2vTFTOsQ3xyAvEhSV0Jy9WUXobmx+KK8OjZqsR4PoYpYN3IWIW -N9jBKE9bahPRFF3g3FxaeVrCBkZBVK+ZAv/E5gshCyCXOmgHT+Kgi1Xpb4X0J/Pj -T83O7y/DrwECgYEAlagfq4kOy2m307WpQo12NT2N1HIMi/owLoz1apGfP6BLn/bR -anU/bxQcAnbqX9fbZLd6QzUIj2Rwsn611mQSX+Ussng8wn/+NEgsUNv4AclyI4kY -Kc/2TahwcJBYlk8aWZT4WrfkZdov6M7Q6I7U1Ys3gJmggDFuBp/F3amM4TUCgYBv -IofKvUuukjPrF3FCMpZnGCk4Ockwzl0bH+0jEoqERGYGhq35853MP2xRZeJpXftW -npo49K8GptQetXrCDKvNaHAHm6pCnrzc8pkFgTI/DIvgRoMWNpF2E+Z8A5f+qYf6 -SpzfMhtRhPWiobh+8820JWUgrRGQ7u1BLC62GYPOAQKBgG/rt2Xu42DJcQ/wm7wN -LB8zn54G/rKWq+mXQbenpr1lN4zBOGgZILrRb1HTcQ7a19056BHBmx3J52yQAnGH -cxuhOyppsH3WgUkUqYN4bzO1CJorX3cP9udBAcmN4BWLF7pLgT7zHrLkJg05sWnd -UHWm2y7vdXeTvdD381p0Ug0S ------END PRIVATE KEY----- diff --git a/create_certs/Bumper_SAN.txt b/create_certs/Bumper_SAN.txt new file mode 100644 index 0000000..9994fc3 --- /dev/null +++ b/create_certs/Bumper_SAN.txt @@ -0,0 +1,6 @@ +ecovacs.com +*.ecovacs.com +ecouser.net +*.ecouser.net +ecovacs.net +*.ecovacs.net \ No newline at end of file diff --git a/create_certs/create_certs_linux b/create_certs/create_certs_linux new file mode 100755 index 0000000..e809faa Binary files /dev/null and b/create_certs/create_certs_linux differ diff --git a/create_certs/create_certs_osx b/create_certs/create_certs_osx new file mode 100755 index 0000000..c985f63 Binary files /dev/null and b/create_certs/create_certs_osx differ diff --git a/create_certs/create_certs_rpi b/create_certs/create_certs_rpi new file mode 100755 index 0000000..c678ef0 Binary files /dev/null and b/create_certs/create_certs_rpi differ diff --git a/create_certs/create_certs_windows.exe b/create_certs/create_certs_windows.exe new file mode 100755 index 0000000..18bfa69 Binary files /dev/null and b/create_certs/create_certs_windows.exe differ diff --git a/create_certs/src/build.sh b/create_certs/src/build.sh new file mode 100755 index 0000000..03e85bb --- /dev/null +++ b/create_certs/src/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +SCRIPTPATH=$(dirname "$BASH_SOURCE") +OUTPUTPATH=$(dirname $(dirname "$BASH_SOURCE")) +echo "Building for Windows: $OUTPUTPATH/create_certs_windows.exe" +GOOS=windows go build -o $OUTPUTPATH/create_certs_windows.exe $SCRIPTPATH/create_certs.go +echo "Building for OSX $OUTPUTPATH/create_certs_osx" +GOOS=darwin go build -o $OUTPUTPATH/create_certs_osx $SCRIPTPATH/create_certs.go +echo "Building for Linux $OUTPUTPATH/create_certs_linux" +GOOS=linux go build -o $OUTPUTPATH/create_certs_linux $SCRIPTPATH/create_certs.go +echo "Building for ARM (Raspberry Pi) $OUTPUTPATH/create_certs_rpi" +GOOS=linux GOARCH=arm GOARM=5 go build -o $OUTPUTPATH/create_certs_rpi $SCRIPTPATH/create_certs.go +echo "Build Complete" \ No newline at end of file diff --git a/create_certs/src/create_certs.go b/create_certs/src/create_certs.go new file mode 100644 index 0000000..5eaa1a8 --- /dev/null +++ b/create_certs/src/create_certs.go @@ -0,0 +1,193 @@ +package main + +import ( + "bufio" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "log" + "math/big" + "net" + "os" + "time" +) + +func main() { + + make_CA() + + signCert() +} + +func make_CA() { + priv, _ := rsa.GenerateKey(rand.Reader, 2048) + pub := &priv.PublicKey + + ca := &x509.Certificate{ + SerialNumber: big.NewInt(1653), + Subject: pkix.Name{ + CommonName: string("Bumper CA"), + Organization: []string{"Bumper"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + SubjectKeyId: bigIntHash(priv.N), + AuthorityKeyId: bigIntHash(priv.N), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{}, + IsCA: true, + BasicConstraintsValid: true, + MaxPathLen: -1, + } + + ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) + if err != nil { + log.Println("create ca failed", err) + return + } + + // Public key + certOut, err := os.Create("ca.crt") + if err != nil { + log.Fatal("create ca.crt failed", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: ca_b}) + certOut.Close() + log.Print("ca.crt created\n") + + // Private key + keyOut, err := os.OpenFile("ca.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatal("create ca.key failed", err) + } + pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + keyOut.Close() + log.Print("ca.key created\n") +} + +func signCert() { + + // Load CA + catls, err := tls.LoadX509KeyPair("ca.crt", "ca.key") + if err != nil { + log.Fatal("error loading ca cert", err) + } + + ca, err := x509.ParseCertificate(catls.Certificate[0]) + if err != nil { + log.Fatal("error parsing ca cert", err) + } + + hostname, _ := os.Hostname() + ifaces, err := net.Interfaces() + var ips []net.IP + // handle err + for _, i := range ifaces { + addrs, _ := i.Addrs() + // handle err + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + ips = append(ips, ip) + // process IP address + } + } + ips = append(ips, net.ParseIP("127.0.0.1")) + + privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) + pubKey := &privateKey.PublicKey + + //DNS/SAN names for cert + dnsNames := []string{hostname, "localhost"} + //Read SANs from file + if fileExists("Bumper_SAN.txt") { + sans, err := readLines("Bumper_SAN.txt") + if err != nil { + log.Fatalf("readLines: %s", err) + } + dnsNames = append(dnsNames, sans...) + } + + // Prepare certificate + template := x509.Certificate{ + SerialNumber: big.NewInt(1658), + Issuer: ca.Subject, + Subject: pkix.Name{ + CommonName: "Bumper Server", + Organization: []string{"Bumper"}, + }, + SubjectKeyId: bigIntHash(privateKey.N), + AuthorityKeyId: ca.RawSubject, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: false, + BasicConstraintsValid: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + DNSNames: dnsNames, + IPAddresses: ips, + } + + // Sign the certificate + cert_b, err := x509.CreateCertificate(rand.Reader, &template, ca, pubKey, catls.PrivateKey) + + // Public key + certOut, err := os.Create("bumper.crt") + if err != nil { + log.Fatal("create bumper.crt failed", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert_b}) + certOut.Close() + log.Print("bumper.crt created\n") + + // Private key + keyOut, err := os.OpenFile("bumper.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatal("create bumper.key failed", err) + } + pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) + keyOut.Close() + log.Print("bumper.key created\n") +} + +func bigIntHash(n *big.Int) []byte { + h := sha1.New() + h.Write(n.Bytes()) + return h.Sum(nil) +} + +// readLines reads a whole file into memory +// and returns a slice of its lines. +func readLines(path string) ([]string, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return lines, scanner.Err() +} + +// fileExists checks if a file exists and is not a directory before we +// try using it to prevent further errors. +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} diff --git a/data/README.md b/data/README.md new file mode 100644 index 0000000..d7e7d7d --- /dev/null +++ b/data/README.md @@ -0,0 +1,3 @@ +# Data Directory + +Where Bumper executes persistent data will be stored here (bumper.db) diff --git a/docs/Create_Certs.md b/docs/Create_Certs.md new file mode 100644 index 0000000..e3e5391 --- /dev/null +++ b/docs/Create_Certs.md @@ -0,0 +1,163 @@ +# Creating Certs + +Bumper requires specially crafted certificates to work properly. In the spirit of security, Bumper will not ship with default certificates. Users will need to generate and provide their own certificates. + +Certificates should be placed in the `{bumper_home}/certs` directory. If certificates are located elsewhere [environment variables](Env_Var.md) can be set that point to their location. + +Users can generate certificates in the following ways: + +| Method | Description | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| [Create_Certs](#creating-certs-using-createcerts) | ***(Preferred)*** This is a utility that has been created to assist in generating the certificates required easily. | +| [OpenSSL](#manually-create-certs-with-openssl) | Users can manually create the same certificates as Create_Certs by utilizing OpenSSL. | +| [Custom CA/Self](#using-a-custom-caself) | If a user has their own CA the certificates can be generated there and used within Bumper. | + +**Certificate Requirements:** + +* A CA Cert must be provided that can be imported into devices (phones, browsers, etc). +* Server certificate should include [SANs (Subject Alternate Names)](#subject-alternative-name) for all of the *.ecovacs, etc domains. + +## Creating certs using Create_Certs + +Create_Certs was created to ease creation of certificates specifically for Bumper. Binaries are provided for Windows/Linux/OSX/RPi (ARMv5) in the Create_Certs directory. + +| Binary | Platform | +| ------------------------ | -------------------- | +| create_certs_linux | Linux | +| create_certs_osx | macOS/x | +| create_certs_windows.exe | Windows | +| create_certs_rpi | RaspberryPi (ARM v5) | + +Create_Certs is written in Go which allows cross-platform compiling. If the binaries don't work on your platform, install Go. + +With Go installed you can: +* Execute the go code - `go run create_certs/src/create_certs.go` +* Build a new binary for your platform - `go build create_certs/src/create_certs.go` + +### Usage + +Create_Certs will automatically create the required certificates in the directory it is executed from. For best results change to the {bumper_home}/certs directory prior to executing, otherwise you'll need to move the certs after. + +1. `cd certs` +2. Execute create_certs (using the binary fitting your platform) - `../create_certs/create_certs_{platform}` +3. The certificates are generated and should be available in the current directory (certs) + +## Subject Alternative Name + +The server certificate requires a number of SAN (Subject Alternative Names) be added. Create_Certs handles this automatically by loading any SANs listed in the `create_certs/Bumper_SAN.txt` file. If creating certificates manually via OpenSSl/Custom CA these will need to be added. + +## Manually create certs with OpenSSL + +I get it, you don't trust create_certs and want to do it manually. The easiest way to create the required certs is at https://certificatetools.com/. In fact the below OpenSSL commands come straight from that site, post creation via the GUI. + +### Create a Root CA + +1. Create csrconfig.txt for use in later commands + csrconfig.txt + ```` + [ req ] + default_md = sha256 + prompt = no + req_extensions = req_ext + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + commonName = Bumper CA + organizationName = Bumper + [ req_ext ] + keyUsage=critical,keyCertSign,cRLSign + basicConstraints=critical,CA:true,pathlen:1 + ```` + +1. Create certconfig.txt for use in later commands + certconfig.txt + ```` + [ req ] + default_md = sha256 + prompt = no + req_extensions = req_ext + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + commonName = Bumper CA + organizationName = Bumper + [ req_ext ] + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + keyUsage=critical,keyCertSign,cRLSign + basicConstraints=critical,CA:true,pathlen:1 + ```` + +1. Generate the RSA private key +`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key` + +1. Create the CSR +`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr` + +1. Self-sign your CSR +`openssl req -x509 -nodes -in cert.csr -days 3650 -key priv.key -config certconfig.txt -extensions req_ext -out cert.crt` + +### Create the Server Certificate + +1. Create csrconfig.txt for use in later commands + csrconfig.txt + ```` + [ req ] + default_md = sha256 + prompt = no + req_extensions = req_ext + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + commonName = Bumper Server + organizationName = Bumper + [ req_ext ] + keyUsage=critical,digitalSignature,keyEncipherment + extendedKeyUsage=serverAuth,clientAuth + basicConstraints=critical,CA:false + subjectAltName = @alt_names + [ alt_names ] + DNS.0 = ecovacs.com + DNS.1 = *.ecovacs.com + DNS.2 = ecouser.net + DNS.3 = *.ecouser.net + DNS.4 = ecovacs.net + DNS.5 = *.ecovacs.net + ```` + +1. Create certconfig.txt for use in later commands + certconfig.txt + ```` + [ req ] + default_md = sha256 + prompt = no + req_extensions = req_ext + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + commonName = Bumper Server + organizationName = Bumper + [ req_ext ] + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + keyUsage=critical,digitalSignature,keyEncipherment + extendedKeyUsage=serverAuth,clientAuth + basicConstraints=critical,CA:false + subjectAltName = @alt_names + [ alt_names ] + DNS.0 = ecovacs.com + DNS.1 = *.ecovacs.com + DNS.2 = ecouser.net + DNS.3 = *.ecouser.net + DNS.4 = ecovacs.net + DNS.5 = *.ecovacs.net + ```` + +1. Generate the RSA private key +`openssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.key` + +1. Create the CSR +`openssl req -new -nodes -key priv.key -config csrconfig.txt -out cert.csr` + +1. Sign your CSR with a root CA cert +`openssl x509 -req -in cert.csr -days 3650 -CA ca.crt -CAkey priv.key -extfile certconfig.txt -extensions req_ext -CAserial /tmp/tmp-10593TSH1OlVSxC7C -CAcreateserial -out cert.crt` + +## Using a Custom CA/Self + +This should work siimilar to the OpenSSL method. Ensure the server certificate has the proper [SANs](#subject-alternative-name) in place. \ No newline at end of file diff --git a/docs/DNS_Setup.md b/docs/DNS_Setup.md index 65e4a42..e90d378 100644 --- a/docs/DNS_Setup.md +++ b/docs/DNS_Setup.md @@ -28,13 +28,23 @@ Not all domains have been documented at this point, and this list will be update - Example: If you see `eco-{countrycode}-api.ecovacs.com` and you live in the US/North America you would use: `eco-us-api.ecovacs.com` -| Address | Description | -|--|--| -| `lb-{countrycode}.ecovacs.net` | Load-balancer that is checked by the app/robot | -| `eco-{countrycode}-api.ecovacs.com` | Used for Login | -| `portal-{countrycode}.ecouser.net` | Used for Login and Rest API | -| `portal-ww.ecouser.net` | Used for various Rest APIs | -| `msg-{countrycode}.ecouser.net` | Used for XMPP | -| `mq-ww.ecouser.net` | Used for MQTT | -| `gl-{countrycode}-api.ecovacs.com` | Used by Ecovacs Home app for API | -| `recommender.ecovacs.com` | Used by Ecovacs Home app | \ No newline at end of file +| Address | Description | +| --------------------------------------- | ---------------------------------------------- | +| `lb-{countrycode}.ecovacs.net` | Load-balancer that is checked by the app/robot | +| `lb-{countrycode}.ecouser.net` | Load-balancer that is checked by the app/robot | +| `lbus.ecouser.net` | Load-balancer that is checked by the app/robot | +| `lb{countrycode}.ecouser.net` | Load-balancer that is checked by the app/robot | +| `eco-{countrycode}-api.ecovacs.com` | Used for Login | +| `gl-{countrycode}-api.ecovacs.com` | Used by EcoVacs Home app | +| `gl-{countrycode}-openapi.ecovacs.com` | Used by EcoVacs Home app | +| `portal-{countrycode}.ecouser.net` | Used for Login and Rest API | +| `portal-ww.ecouser.net` | Used for various Rest APIs | +| `msg-{countrycode}.ecouser.net` | Used for XMPP | +| `msg-ww.ecouser.net` | Used for XMPP | +| `mq-ww.ecouser.net` | Used for MQTT | +| `mq-{countrycode}.ecouser.net` | Used for MQTT | +| `gl-{countrycode}-api.ecovacs.com` | Used by Ecovacs Home app for API | +| `recommender.ecovacs.com` | Used by Ecovacs Home app | +| `bigdata-international.ecovacs.com` | Telemetry/tracking | +| `bigdata-northamerica.ecovacs.com` | Telemetry/tracking | +| `bigdata-{unknown regions}.ecovacs.com` | Telemetry/tracking | diff --git a/docs/Env_Var.md b/docs/Env_Var.md new file mode 100644 index 0000000..f5b0697 --- /dev/null +++ b/docs/Env_Var.md @@ -0,0 +1,14 @@ +# Environment Variables + +Bumper has a number of environment variables to help with custom deployments and configuration. These should be set prior to executing Bumper. + +| Setting | Value | Description | +| ------------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| BUMPER_LISTEN | {ipv4 address} | IP address to start server listeners on | +| BUMPER_ANNOUNCE_IP | {ipv4 address} | IP address to tell bots when they request the server location. May need to be set in cases such as when LISTEN is 0.0.0.0. | +| BUMPER_CA | {full path to ca.crt location} | The public CA certificate (ca.crt) to be loaded | +| BUMPER_CERT | {full path to bumper.crt location} | The public server certificate (bumper.crt) to be used by the Bumper server | +| BUMPER_KEY | {full path to bumper.key location} | The private server key (bumper.key) to be used by the Bumper server | +| BUMPER_LOGS | {full path to logs directory} | The directory where logs should be stored | +| BUMPER_DATA | {full path to data directory} | The directory where persistent data should be stored (bumper.db) | +| BUMPER_DEBUG | true | Run Bumper with debug mode/logging | \ No newline at end of file diff --git a/docs/How_It_Works.md b/docs/How_It_Works.md index ed8eec1..978b46b 100644 --- a/docs/How_It_Works.md +++ b/docs/How_It_Works.md @@ -4,11 +4,11 @@ Bumper runs multiple services to re-create what the central EcoVacs servers prov **Services** -| Service | Description | Ports | Source File | -|--|--|--|--| +| Service | Description | Ports | Source File | +| ----------- | ------------------------------------------------ | ------------ | --------------- | | Web Servers | Provide authentication and for MQTT bots command | 443 and 8007 | `confserver.py` | -| XMPP Server | For bots that utilize this protocol | 5223 | `xmppserver.py` | -| MQTT Server | For bots that utilize this protocol | 8883 | `mqttserver.py` | +| XMPP Server | For bots that utilize this protocol | 5223 | `xmppserver.py` | +| MQTT Server | For bots that utilize this protocol | 8883 | `mqttserver.py` | **App/Authentication** diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 0000000..7e8687a --- /dev/null +++ b/logs/README.md @@ -0,0 +1,3 @@ +# Logs Directory + +When Bumper executes, logs will be created here diff --git a/start_bumper.py b/start_bumper.py deleted file mode 100644 index 677e219..0000000 --- a/start_bumper.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 - -import logging -import bumper -import sys, socket -import time -import platform -import os - -# os.environ['PYTHONASYNCIODEBUG'] = '1' # Uncomment to enable ASYNCIODEBUG -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__": - try: - asyncio.run(main()) - - except KeyboardInterrupt: - bumper.bumperlog.info("Keyboard Interrupt!") - pass - finally: - bumper.bumperlog.info("Bumper Exiting!") diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 0000000..9ca2dc7 --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +env = + D:BUMPER_CA=tests/test_certs/ca.crt + D:BUMPER_CERT=tests/test_certs/bumper.crt + D:BUMPER_KEY=tests/test_certs/bumper.key diff --git a/tests/test_certs/bumper.crt b/tests/test_certs/bumper.crt new file mode 100644 index 0000000..c80e131 --- /dev/null +++ b/tests/test_certs/bumper.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJTEPMA0GA1UEChMGQnVt +cGVyMRIwEAYDVQQDEwlCdW1wZXIgQ0EwHhcNMTkwNjA2MTMwMTE3WhcNMjkwNjA2 +MTMwMTE3WjApMQ8wDQYDVQQKEwZCdW1wZXIxFjAUBgNVBAMTDUJ1bXBlciBTZXJ2 +ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN94pW5U+IJ70BQRKO +5Va2Inuef9AMvTm11pZMStvIskuzCi+fiIXhrPyKXRNbg21MX9eTrbEfc8t1sBKU +3rYdZ+JteE+9f4pJjHXEyJSMiPZcmGKC3CbSZyuPgw9asLch6FENrwDcxEV7HbJd +oINOjwVqXXPdsSRk2GPuvHjvlwC0h0vt/fErQhlD8U3TIIRDtCfg0BSQUuLXBfJh +R1aksl2nwWQTu62akWTxlZ7hQnkk1WldjIqQarjyaY7Uh0L0/kRpnbhmnW085gzc +E2bi0YwZ7WPwAwPvEszJr30piPtt71JzPPvPUdIcsTj2HotWyAT1ljdxfaKvWgI3 +HemrAgMBAAGjggGQMIIBjDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB +BQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFMiQCLdfA6g8 +0xTW2wROUJ42R06hMB8GA1UdIwQYMBaAFKYPByFyuubjTQKzczNsX045VPDbMIIB +CwYDVR0RBIIBAjCB/4ILZWNvdmFjcy5jb22CDSouZWNvdmFjcy5jb22CC2Vjb3Vz +ZXIubmV0gg0qLmVjb3VzZXIubmV0ggtlY292YWNzLm5ldIINKi5lY292YWNzLm5l +dIIIbWFjYm9vazGCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYcQ +/oAAAAAAAAAAAAAAAAAAAYcQ/oAAAAAAAAAE2n0e5bOcu4cECgABKIcQ/oAAAAAA +AABAPHz//ktLWIcQ/oAAAAAAAAD+dglg8MXdSIcQ/oAAAAAAAAALkYrhchk/YIcQ +/oAAAAAAAAAY1ZBa52Ist4cECgABCocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA +QSFKoR3LxCk0N1eaNm44WF7ho57l1XhqQKWojljsJYWoc+LwcWamdyfTJ5M1o07/ +yIMh16VRFnjr1NPTk9WmLCcy5VN0oP24XAF9VeX5Q2Rkj8j3e7RmRn5jn0BH6sxk +X/a9GMuEqSDYi6j9ggi9AEdAM8tyfcEkXEP3pdVW29je8F0u7Hia42zGUjGwjndl +2Q8mYBKr0ViG1UV7bKAEJl/WvnOTxpp8ZSTLr6RSleGZ7JvCGpISPzNgeIuPRKOT +D77LiGnDdKgJnuwx266tuKV5AN4B3okG/AqbmFtB/LFfNE2769g94pG6jpVZpSum +kq2jok6bnc1fdGkO0ou15w== +-----END CERTIFICATE----- diff --git a/tests/test_certs/bumper.key b/tests/test_certs/bumper.key new file mode 100644 index 0000000..9dfc1df --- /dev/null +++ b/tests/test_certs/bumper.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzfeKVuVPiCe9AUESjuVWtiJ7nn/QDL05tdaWTErbyLJLswov +n4iF4az8il0TW4NtTF/Xk62xH3PLdbASlN62HWfibXhPvX+KSYx1xMiUjIj2XJhi +gtwm0mcrj4MPWrC3IehRDa8A3MRFex2yXaCDTo8Fal1z3bEkZNhj7rx475cAtIdL +7f3xK0IZQ/FN0yCEQ7Qn4NAUkFLi1wXyYUdWpLJdp8FkE7utmpFk8ZWe4UJ5JNVp +XYyKkGq48mmO1IdC9P5EaZ24Zp1tPOYM3BNm4tGMGe1j8AMD7xLMya99KYj7be9S +czz7z1HSHLE49h6LVsgE9ZY3cX2ir1oCNx3pqwIDAQABAoIBADpMVLXf1jEdvlu6 +hXw44eBPxlnIp6TTcqBLiuMxVSBrhw24Vl57kBmekyS+z5nZi3hQsH42baoPZol7 +Ouc0WqvcGbUOltN2ZDgJL9RxScq1gbYX09piYgKv/R5z5EaxOkS4syiCXmYej+QX +NcNWiZQU212N8hvP5hM9RJvwcPKvcpQIrW5NdcMvhok+kd/abJcIzXhLXEyLcrRJ +XZh6cCesUZQnZWPVecM+bc/Wt9U5pbw/PNTTiQSFW6wrjCOXVKA57p/zmeb3EWFd +V/rlho+QXLBo9I0DLZKScCfMHgVoguK5Ud7drGiB5rPlgPAv0sFW7fhUd1j7hX7H +l4uDrqECgYEA+6xJgJzJp//6eR3wNiSp/4Y8vY+WCtWNCOwhJbw1a8co5Sex0vAk +0y3mye+4rOgRbfXhhpPm+y+EnHbXlVk8Ux37OfxHviewCiRbkjRU3uycE60ZFtLr +rdqA9FNwutfz3AJqJ/zjVoMneeO7h/GkIAHqxOJHAACV/+lObrSMO0MCgYEA0YIV +M032bk+q9zgfd0IY1ZdUhUS2QyQhMY380F+Dym69Xv2FmG+zeynJT5jxrZs3kmcN +X9jnscTIMGO70EHq2sI/h9IFfqr/7iTlbwgpYeKEPYqWkmuetiKziVOL/Uf4USPd +bQiWRIaQpSZFXUCjVrKL8nGdxhA73iOuQKLmjXkCgYEA2mHxlex2/5Sbn6Ve31yJ +NvUgl8yUgeo0a12alcfvAwrabZ44K7W9/pGCBhBBy6fEGAqYG9MTRztAcxSclTfN +pMILUZGcH2EI3IQYAZHvY1ElTd2SO7/iSgWkiF4vH7JsJKjjvLcv/c3o31fBToFb +zXpcYWOIAHSBtIKaUG3SkUkCgYBiJq4l9f1gWqR+42Uiau3ViusG8FTvCYsfeySY +vlawzMadLOqcqqAMMFxmlQGMUq5rQSor4CNTzhv3R5Iik+8iA2TeDY+Tcyspd1Gh +fdJdUFChwD6gZBjbvVAdIu3vf/sekE3ZGe/ENMKpDE/syy56nHUh5QaEVvClNlQZ +ZffbCQKBgHM2fk3Nlsjk7UCVb8P2hZdX4gx83OJDJPfIfpu/Sf5vPDpmeADI0L30 +wlMLo+px1bYBqP6sNx0E1iaBces+WqALnR1tIypItVJmZF7eGeWmJ/KJX1FwtYec +D0cElOusX7pIo6s3lTw4KMxPOMPYRt75zlboVEi+z4cNOCXjf49B +-----END RSA PRIVATE KEY----- diff --git a/tests/test_certs/ca.crt b/tests/test_certs/ca.crt new file mode 100644 index 0000000..b346818 --- /dev/null +++ b/tests/test_certs/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgICBnUwDQYJKoZIhvcNAQELBQAwJTEPMA0GA1UEChMGQnVt +cGVyMRIwEAYDVQQDEwlCdW1wZXIgQ0EwHhcNMTkwNjA2MTMwMTE3WhcNMjkwNjA2 +MTMwMTE3WjAlMQ8wDQYDVQQKEwZCdW1wZXIxEjAQBgNVBAMTCUJ1bXBlciBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOE/zW15yRS7jZhlF0K1s8c +8tN5tYWVieeGCbmdk89vtTA/doYbvbP0ut95OUSS8YEDP98UdqzoTXpYqNehnk5q +VjRyHyXxv2KekhxmTmv+f5OXns8fCgoNlOWAugAlXgk+LGy7non+RLNQ5/vh8E/C +IE0lbxelptkOMirboFPP4LIFqHMjERlC6m63LBftVjRCjDzoWtN9XNCquan27fBd +5/I3wdEBdDcGA7heGPXKrkzyaiZUncHQD3oqbn05ScUFvSJAs7bz4Qdslkna7zK1 +AAJoXEBAjWTTau8xp4Fp1qzloMMh0RrqCBgeBlGeL6OcqvMUNRrFNnuDxEXXuTsC +AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFKYPByFyuubjTQKzczNsX045VPDbMB8GA1UdIwQYMBaAFKYPByFyuubjTQKz +czNsX045VPDbMA0GCSqGSIb3DQEBCwUAA4IBAQCy0dzBuANBlmL58XtvwvrSxhJr +oBscfqeYRvWaUkSAbVX4aH7UsUQWDiLtJZdLsWxMLl0LqbDo+t3I8kpSxOAkJ+Dn +zkLAL2ze0ivZEYkLqGXhO+lEK8YcFWJxkgIA/aykmI7suQyv/1x67mTpCVbxMjbI +iVv11L2o9Ilu/ZLIlRg5G3vnTz7Eql3JMFUN+hHzEzuNTyl9JkSUIZJLQyqg3u4Y +NIGD3nxFiNM1LL17Baz2EjfloAoSyaPGZUzKyRT2BUzHCs2vBNnbTN+XRBHJGZX/ +VU76YW8l/f6OPzP6XR3noI6Rpm5OpTOrQTTqmYkcb3QwhBD3I4Aqyjs/qxIT +-----END CERTIFICATE----- diff --git a/tests/test_certs/ca.key b/tests/test_certs/ca.key new file mode 100644 index 0000000..cd7f03a --- /dev/null +++ b/tests/test_certs/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA84T/NbXnJFLuNmGUXQrWzxzy03m1hZWJ54YJuZ2Tz2+1MD92 +hhu9s/S633k5RJLxgQM/3xR2rOhNelio16GeTmpWNHIfJfG/Yp6SHGZOa/5/k5ee +zx8KCg2U5YC6ACVeCT4sbLueif5Es1Dn++HwT8IgTSVvF6Wm2Q4yKtugU8/gsgWo +cyMRGULqbrcsF+1WNEKMPOha031c0Kq5qfbt8F3n8jfB0QF0NwYDuF4Y9cquTPJq +JlSdwdAPeipufTlJxQW9IkCztvPhB2yWSdrvMrUAAmhcQECNZNNq7zGngWnWrOWg +wyHRGuoIGB4GUZ4vo5yq8xQ1GsU2e4PERde5OwIDAQABAoIBAQCUveGcjvUWDMmP +0d5rTlmSvB3d6fNkGsjW79o00/katSK4y6q7k2o15v/VzBecuRxmLvPucHNkfagF +WO4Yd5FYWD87XZGE/bpmpHCehCbWc+a84dmQrWbWaSFPZWc2+5wKjT1ZVOgrZXHV +DVAFXCYKsYpFVN4cVYQqBC/EWc3+IkQXcw8POLNwa0ml6ZyFZOcmpik/5o+EbCH2 +KL+Rqqi1iKnMKdR9HG9yg7PCk140/Zn0Z5ROWpGehFy1xNlh/+DO9Fjbkm+Zj3sU +/X3UfRRe1Gtbt8z68fZxLqjJfBMujc+J5kSVMn0MH3vXfCLTSktGaxZmHC5xhk36 +59QzpQ7hAoGBAPdvFoaD9jJ8bF3nwIJ2uxYAhH/y9IYb4rW4FN1Dz/fBJuIZ8JO9 +NBDHX1JK1NzgjTE0mqZ0C5UQTNR9FMBVe0iTFDpndL6YcOtU0LIyHeePNGqnVWMp +SjjrjtjSO0Uug7YKSuLOrfMtKZyN0t19KDllThoMZlxSoiTL1Jzor0BJAoGBAPvz +N4kp8t+ngCl/BoHVEKjhtYe90m0VQflwVfl+myJwIVjtEJliAn2ZZ4uKeTqY1EGE +cAKwikpxI3yDVetM/hrivOEcULbk1SwhcmtTaaGCAw6iLm32suPcEROnSQF/FVyj +BxyvHyrSG7FRh6f4YcMg3G6W7VxE4IYVgoVdLPVjAoGBAKBOnK1x+aQj8KG2cIcW +HYfXtgCfi8UHdA5/wSTJtoex4FtEqxVM09q7EQrj6J1h7pgemMOXE5n0AZL5puQl +fcY7ZDOiq4SAh0gwnzWUOsO3iCJBX5TrP2c5VkcrjbL4u+TJLlNbzF9stzTyvdc6 +K0FkXQGLXxYZg2uLk6hFKKXhAoGAODCN+jE40Vhx5M9HjD0QchmkepmDDIDpLdA3 +3GS4BH4tcb+tLAgHtis4RI6lasQa0mHO+UqWDPQeje5cgxWwth5LMYk3FxMB1829 +zuHTdQmh1OmmoS+bxeWWiWONjJ/jC73wIB2OPeaN9oXQU025v+LcRm2ykhlqWX0C +luuo0zECgYEA6FI0/2L+3prIQ6ravX2sWNMEFeWMEUg/ld8lHcOZ+amAuLzMvrPO +oSVb3JQ+OQbpVsorNBjWqQwbUKTJ3zU9/ievyptCs3G39aQ2wUsDdYKfpkxnfKFe +ZgU5YfwB54rgkQc4vCheTnWo/JuhyB5cgvtjtKOdtMKhXfwpX0bFA/w= +-----END RSA PRIVATE KEY----- diff --git a/tests/test_confserver.py b/tests/test_confserver.py index 3c85115..15fdac7 100644 --- a/tests/test_confserver.py +++ b/tests/test_confserver.py @@ -26,6 +26,11 @@ def async_return(result): return f +def remove_existing_db(): + if os.path.exists("tests/tmp.db"): + os.remove("tests/tmp.db") # Remove existing db + + async def test_confserver_ssl(): conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=True, helperbot=None) conf_server.confserver_app() @@ -39,8 +44,7 @@ async def test_confserver_no_ssl(): async def test_base(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -51,8 +55,7 @@ async def test_base(aiohttp_client): async def test_login(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -66,8 +69,7 @@ async def test_login(aiohttp_client): assert "uid" in jsonresp["data"] assert "username" in jsonresp["data"] - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing # Test global_e without user @@ -104,8 +106,7 @@ async def test_login(aiohttp_client): async def test_logout(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -126,8 +127,7 @@ async def test_logout(aiohttp_client): async def test_checkLogin(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -217,8 +217,7 @@ async def test_checkLogin(aiohttp_client): async def test_getAuthCode(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -273,8 +272,7 @@ async def test_getAuthCode(aiohttp_client): async def test_checkAgreement(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -295,8 +293,7 @@ async def test_checkAgreement(aiohttp_client): async def test_homePageAlert(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -310,8 +307,7 @@ async def test_homePageAlert(aiohttp_client): async def test_checkVersion(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -323,8 +319,7 @@ async def test_checkVersion(aiohttp_client): async def test_checkAppVersion(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -338,8 +333,7 @@ async def test_checkAppVersion(aiohttp_client): async def test_uploadDeviceInfo(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -353,8 +347,7 @@ async def test_uploadDeviceInfo(aiohttp_client): async def test_getAdByPositionType(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -368,8 +361,7 @@ async def test_getAdByPositionType(aiohttp_client): async def test_getBootScreen(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -383,8 +375,7 @@ async def test_getBootScreen(aiohttp_client): async def test_hasUnreadMsg(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -398,8 +389,7 @@ async def test_hasUnreadMsg(aiohttp_client): async def test_getMsgList(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -413,8 +403,7 @@ async def test_getMsgList(aiohttp_client): async def test_getSystemReminder(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -428,8 +417,7 @@ async def test_getSystemReminder(aiohttp_client): async def test_getCnWapShopConfig(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -443,8 +431,7 @@ async def test_getCnWapShopConfig(aiohttp_client): async def test_neng_hasUnreadMessage(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -466,8 +453,7 @@ async def test_neng_hasUnreadMessage(aiohttp_client): async def test_getProductIotMap(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -479,8 +465,7 @@ async def test_getProductIotMap(aiohttp_client): async def test_getUsersAPI(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -492,8 +477,7 @@ async def test_getUsersAPI(aiohttp_client): async def test_getUserAccountInfo(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing bumper.user_add("testuser") bumper.user_add_device("testuser", "dev_1234") @@ -516,8 +500,7 @@ async def test_getUserAccountInfo(aiohttp_client): async def test_postUsersAPI(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -668,8 +651,7 @@ async def test_postUsersAPI(aiohttp_client): async def test_appsvr_api(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -708,8 +690,7 @@ async def test_appsvr_api(aiohttp_client): async def test_lg_logs(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing bumper.bot_add("sn_1234", "did_1234", "ls1ok3", "res_1234", "eco-ng") bumper.bot_set_mqtt("did_1234", True) @@ -725,7 +706,6 @@ async def test_lg_logs(aiohttp_client): confserver.helperbot.send_command = mock.MagicMock( return_value=async_return(command_getstatus_resp) ) - # Test GetGlobalDeviceList postbody = { @@ -756,8 +736,7 @@ async def test_lg_logs(aiohttp_client): async def test_postLookup(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing client = await aiohttp_client(create_app) @@ -779,8 +758,7 @@ async def test_postLookup(aiohttp_client): async def test_devmgr(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing confserver = create_confserver() client = await aiohttp_client(create_app) @@ -800,7 +778,7 @@ async def test_devmgr(aiohttp_client): text = await resp.text() test_resp = json.loads(text) assert test_resp["ret"] == "ok" - assert test_resp["unRead"] == False + assert test_resp["unRead"] == False # Test BotCommand bumper.bot_add("sn_1234", "did_1234", "dev_1234", "res_1234", "eco-ng") @@ -846,8 +824,7 @@ async def test_devmgr(aiohttp_client): async def test_dim_devmanager(aiohttp_client): - if os.path.exists("tests/tmp.db"): - os.remove("tests/tmp.db") # Remove existing db + remove_existing_db() bumper.db = "tests/tmp.db" # Set db location for testing confserver = create_confserver() client = await aiohttp_client(create_app) diff --git a/tests/test_init.py b/tests/test_init.py index d1e9060..88faec4 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -1,4 +1,5 @@ import mock +from mock import patch import pytest from tinydb.storages import MemoryStorage from tinydb import TinyDB, Query @@ -6,6 +7,9 @@ import bumper import os import datetime, time import platform +import json +import asyncio +from testfixtures import LogCapture def test_get_milli_time(): @@ -19,23 +23,61 @@ def test_get_milli_time(): ) +def test_strtobool(): + assert bumper.strtobool("t") == True + assert bumper.strtobool("f") == False + assert bumper.strtobool(0) == False + +async def test_start_stop(): + with LogCapture() as l: + if os.path.exists("tests/tmp.db"): + os.remove("tests/tmp.db") # Remove existing db + + b = bumper + b.db = "tests/tmp.db" # Set db location for testing + b.conf1_listen_address = "0.0.0.0" + b.conf1_listen_port = 443 + asyncio.create_task(b.start()) + await asyncio.sleep(0.1) + l.check_present(("bumper", "INFO", "Starting Bumper")) + l.clear() + assert b.shutting_down == False + asyncio.create_task(b.shutdown()) + await asyncio.sleep(0.1) + l.check_present( + ("bumper", "INFO", "Shutting down"), ("bumper", "INFO", "Shutdown complete") + ) + assert b.shutting_down == True + + +async def test_start_stop_debug(): + with LogCapture() as l: + if os.path.exists("tests/tmp.db"): + os.remove("tests/tmp.db") # Remove existing db + + b = bumper + b.db = "tests/tmp.db" # Set db location for testing + b.bumper_listen = "0.0.0.0" + b.bumper_debug = True + asyncio.create_task(b.start()) + + await asyncio.sleep(0.1) + asyncio.create_task(b.shutdown()) + l.check_present(("bumper", "INFO", "Starting Bumper")) + l.clear() + await asyncio.sleep(0.1) + l.check_present( + ("bumper", "INFO", "Shutting down"), ("bumper", "INFO", "Shutdown complete") + ) + assert b.shutting_down == True + + +def test_db_path(): + bumper.db = None + assert bumper.db_file() == os.path.join(bumper.data_dir, "bumper.db") + + def test_user_db(): - if os.path.exists("tests/tmp.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.user_add("testuser") # Add testuser diff --git a/tests/test_mqttserver.py b/tests/test_mqttserver.py index 24ef16f..cf2232b 100644 --- a/tests/test_mqttserver.py +++ b/tests/test_mqttserver.py @@ -18,13 +18,6 @@ async def test_helperbot_message(): mqtt_address = ("127.0.0.1", 8883) mqtt_server = bumper.MQTTServer(mqtt_address) await mqtt_server.broker_coro() - #broker = mqtt_server.broker - #mqtt_address = ("127.0.0.1", 8883) - #mqtt_server = bumper.MQTTServer(mqtt_address) - #broker = hbmqtt.broker.Broker( - # mqtt_server.default_config, plugin_namespace="hbmqtt.test.plugins" - #) - #await broker.start() # Test broadcast message mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) @@ -140,12 +133,12 @@ async def test_helperbot_expire_message(): mqtt_address = ("127.0.0.1", 8883) mqtt_server = bumper.MQTTServer(mqtt_address) await mqtt_server.broker_coro() - #mqtt_address = ("127.0.0.1", 8883) - #mqtt_server = bumper.MQTTServer(mqtt_address) - #broker = hbmqtt.broker.Broker( + # mqtt_address = ("127.0.0.1", 8883) + # mqtt_server = bumper.MQTTServer(mqtt_address) + # broker = hbmqtt.broker.Broker( # mqtt_server.default_config, plugin_namespace="hbmqtt.test.plugins" - #) - #await broker.start() + # ) + # await broker.start() # Test broadcast message mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) @@ -209,12 +202,12 @@ async def test_helperbot_sendcommand(): mqtt_address = ("127.0.0.1", 8883) mqtt_server = bumper.MQTTServer(mqtt_address) await mqtt_server.broker_coro() - #mqtt_address = ("127.0.0.1", 8883) - #mqtt_server = bumper.MQTTServer(mqtt_address) - #broker = hbmqtt.broker.Broker( + # mqtt_address = ("127.0.0.1", 8883) + # mqtt_server = bumper.MQTTServer(mqtt_address) + # broker = hbmqtt.broker.Broker( # mqtt_server.default_config, plugin_namespace="hbmqtt.test.plugins" - #) - #await broker.start() + # ) + # await broker.start() mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) await mqtt_helperbot.start_helper_bot() @@ -334,7 +327,7 @@ async def test_mqttserver(): mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected await mqtt_helperbot.Client.disconnect() - + # Test client connect bumper.user_add("user_123") # Add user to db bumper.client_add("user_123", "ecouser.net", "resource_123") # Add client to db @@ -365,7 +358,7 @@ async def test_mqttserver(): fake_bot.Client._connected_state._value == True ) # Check fake_bot is connected await fake_bot.Client.disconnect() - + await asyncio.sleep(0.1) - await mqtt_server.broker.shutdown() \ No newline at end of file + await mqtt_server.broker.shutdown() diff --git a/tests/test_xmppserver.py b/tests/test_xmppserver.py index 6ed558a..74d2a0c 100644 --- a/tests/test_xmppserver.py +++ b/tests/test_xmppserver.py @@ -221,16 +221,12 @@ async def test_xmpp_server_client_tls(): await writer.drain() - await asyncio.sleep(0.1) - writer.write( b'AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl' ) # Send Auth await writer.drain() - await asyncio.sleep(0.1) - xmpp_address = ("127.0.0.1", 5223) xmpp_server = bumper.XMPPServer(xmpp_address) await xmpp_server.start_async_server() diff --git a/tests/test_z_problem.py b/tests/test_z_problem.py new file mode 100644 index 0000000..bf93b00 --- /dev/null +++ b/tests/test_z_problem.py @@ -0,0 +1,111 @@ +import mock +from mock import patch +import pytest +from tinydb.storages import MemoryStorage +from tinydb import TinyDB, Query +import bumper +import os +import datetime, time +import platform +import json +import asyncio +from testfixtures import LogCapture +import sys + + +@patch("bumper.firstrun_input") +@patch("bumper.create_certs") +def test_firstrun(mock_input, mock_create): + with LogCapture() as l: + + bumper.firstrun_input.return_value = "n" + bumper.first_run() + l.check_present( + ( + "root", + "CRITICAL", + "Can't continue without certificates, please create some then try again.", + ) + ) + + bumper.firstrun_input.return_value = "y" + bumper.first_run() + assert mock_create.called == True + + +def mock_subrun(*args): + return args + +@patch("bumper.start") +def test_argparse(mock_start): + bumper.ca_cert = "tests/test_certs/ca.crt" + bumper.server_cert = "tests/test_certs/bumper.crt" + bumper.server_key = "tests/test_certs/bumper.key" + + bumper.main(["--debug"]) + assert bumper.bumper_debug == True + assert mock_start.called == True + + bumper.main(["--listen", "127.0.0.1"]) + assert bumper.bumper_listen == "127.0.0.1" + assert mock_start.called == True + + bumper.main(["--announce", "127.0.0.1"]) + assert bumper.bumper_announce_ip == "127.0.0.1" + assert mock_start.called == True + + bumper.main(["--debug", "--listen", "127.0.0.1", "--announce", "127.0.0.1"]) + assert bumper.bumper_debug == True + assert bumper.bumper_announce_ip == "127.0.0.1" + assert bumper.bumper_listen == "127.0.0.1" + assert mock_start.called == True + + +@patch("subprocess.run") +@patch("platform.system") +@patch("platform.machine") +@patch("os.execv") +def test_createcert(mock_run, mock_platform, mock_machine, mock_exec): + mock_run.side_effect = mock_subrun + platform.system.return_value = "darwin" + bumper.create_certs() + assert mock_run.called == True + assert os.path.join("..", "create_certs", "create_certs_osx") in mock_exec.call_args.args[0] + + platform.system.return_value = "windows" + bumper.create_certs() + assert mock_run.called == True + assert os.path.join("..", "create_certs", "create_certs_windows.exe") in mock_exec.call_args.args[0] + + platform.system.return_value = "linux" + bumper.create_certs() + assert mock_run.called == True + assert os.path.join("..", "create_certs","create_certs_linux") in mock_exec.call_args.args[0] + + platform.system.return_value = "linux" + platform.machine.return_value = "arm" + bumper.create_certs() + assert mock_run.called == True + assert os.path.join("..", "create_certs", "create_certs_rpi") in mock_exec.call_args.args[0] + + with LogCapture() as l: + platform.system.return_value = "nixbad" + bumper.create_certs() + + l.check_present( + ( + "root", + "CRITICAL", + "Can't determine platform. Create certs manually and try again.", + ) + ) + + + +@patch("bumper.first_run") +def test_main(mock_firstrun): + bumper.ca_cert = "sf" + bumper.main() + assert mock_firstrun.called == True + bumper.ca_cert = "tests/test_certs/ca.crt" + diff --git a/tests/tests.md b/tests/tests.md index c485135..c4cbdaf 100644 --- a/tests/tests.md +++ b/tests/tests.md @@ -5,12 +5,12 @@ Bumper uses pytest for the majority of test cases. Install requirements using ` Enter pipenv shell `pipenv shell` ### Run tests -`python -m pytest` +`python -m pytest tests` ### Run tests with coverage -`python -m pytest --cov=bumper tests/` +`python -m pytest --cov=./ tests` ### Run tests with coverage html report -`python -m pytest --cov=bumper tests/ --cov-report html:tests/report` +`python -m pytest --cov=./ tests --cov-report html:tests/report` The report will be output into tests/report/index.html for further analysis. \ No newline at end of file