Create cert #40

Merged
bmartin5692 merged 18 commits from create_cert into master 2019-06-08 23:02:09 +02:00
54 changed files with 1219 additions and 1009 deletions

33
.gitignore vendored
View file

@ -1,10 +1,25 @@
# Ignore pycache
__pycache__ __pycache__
.DS_Store
.vscode/ # Ignore .hidden files
.coverage .*
.noseids
.pytest_cache # Except .
nosetests.xml !.github/
tests/report !.gitignore
tests/tmp.db
logs/ # 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

View file

@ -9,7 +9,7 @@ install:
- pip install pipenv - pip install pipenv
- pipenv install --dev - pipenv install --dev
script: pipenv run python -m pytest --cov=./ script: pipenv run python -m pytest --cov=./ tests
after_success: after_success:
- codecov - codecov

View file

@ -20,6 +20,8 @@ pytest-aiohttp = "*"
pytest-cov = "*" pytest-cov = "*"
testfixtures = "*" testfixtures = "*"
codecov = "*" codecov = "*"
autoflake = "*"
pytest-env = "*"
[pipenv] [pipenv]
allow_prereleases = true allow_prereleases = true

47
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "243154e4a18d9995bb246342d1ed65910cda2fa674bced665a9706fc385459fc" "sha256": "309c2ca578f67a8663325695e4d283f7e3f604c7cec451bf57e71af7dcdb7294"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -127,20 +127,20 @@
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c", "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95", "sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2", "sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4", "sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad", "sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba", "sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1", "sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e", "sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673", "sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13", "sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19" "sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.1" "version": "==5.1.1"
}, },
"six": { "six": {
"hashes": [ "hashes": [
@ -271,6 +271,13 @@
], ],
"version": "==19.1.0" "version": "==19.1.0"
}, },
"autoflake": {
"hashes": [
"sha256:6b59e5b9b82e30077499578856282debb81186d10b4f899e8c2e1d616cdef973"
],
"index": "pypi",
"version": "==1.3"
},
"black": { "black": {
"hashes": [ "hashes": [
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
@ -471,6 +478,13 @@
], ],
"version": "==1.8.0" "version": "==1.8.0"
}, },
"pyflakes": {
"hashes": [
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
},
"pylint": { "pylint": {
"hashes": [ "hashes": [
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
@ -517,6 +531,13 @@
"index": "pypi", "index": "pypi",
"version": "==2.7.1" "version": "==2.7.1"
}, },
"pytest-env": {
"hashes": [
"sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"
],
"index": "pypi",
"version": "==0.6.2"
},
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",

View file

@ -5,7 +5,7 @@ Bumper is a standalone and self-hosted implementation of the central server used
**Note:** The current master branch is unstable, and in active development. **Note:** The current master branch is unstable, and in active development.
| Master Build Status | Status | | Master Build Status | Status |
|--|--| | ------------------- | ---------------------------------------------------------------------- |
| AppVeyor (Win32) | ![AppVeyor](https://img.shields.io/appveyor/ci/bmartin5692/bumper.svg) | | AppVeyor (Win32) | ![AppVeyor](https://img.shields.io/appveyor/ci/bmartin5692/bumper.svg) |
| TravisCI (Linux) | ![Travis (.org)](https://img.shields.io/travis/bmartin5692/bumper.svg) | | TravisCI (Linux) | ![Travis (.org)](https://img.shields.io/travis/bmartin5692/bumper.svg) |
@ -21,7 +21,7 @@ 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: 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 | | Model | Protocol Used | Bumper Version Tested | EcoVacs App Tested |
|--|--|--|--| | --------------- | ------------- | --------------------- | -------------------- |
| Deebot 900/901 | MQTT | master | Ecovacs/Ecovacs Home | | Deebot 900/901 | MQTT | master | Ecovacs/Ecovacs Home |
| Deebot 600 | MQTT | master | Ecovacs Home | | Deebot 600 | MQTT | master | Ecovacs Home |
| Deebot Ozmo 601 | XMPP | master | Ecovacs | | Deebot Ozmo 601 | XMPP | master | Ecovacs |
@ -64,14 +64,36 @@ But seriously, there are a several reasons for eliminating the central server:
### Starting Bumper ### 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 - Reboot your robot
- **Note:** Some models may require removing and re-inserting the battery pack. - **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. - 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. - 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 ## Thanks
A big thanks to the original project creator @torbjornaxelsson, without his work this project would have taken much longer to build. A big thanks to the original project creator @torbjornaxelsson, without his work this project would have taken much longer to build.

View file

@ -19,7 +19,7 @@ test_script:
# Note that you must use the environment variable %PYTHON% to refer to # Note that you must use the environment variable %PYTHON% to refer to
# the interpreter you're using - Appveyor does not do anything special # the interpreter you're using - Appveyor does not do anything special
# to put the Python version you want to use on PATH. # 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: after_test:
- "pipenv run python -m codecov" - "pipenv run python -m codecov"

View file

@ -5,27 +5,65 @@ from bumper.mqttserver import MQTTServer, MQTTHelperBot
from bumper.xmppserver import XMPPServer from bumper.xmppserver import XMPPServer
import asyncio import asyncio
import json import json
import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import platform import os
import os, sys
import logging import logging
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from base64 import b64decode, b64encode
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
import json
from tinydb.storages import MemoryStorage from tinydb.storages import MemoryStorage
import socket
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 use_auth = False
token_validity_seconds = 3600 # 1 hour token_validity_seconds = 3600 # 1 hour
db = None db = None
# Logs mqtt_server = None
os.makedirs("logs", exist_ok=True) # Ensure logs directory exists or create mqtt_helperbot = None
conf_server = None
conf_server_2 = None
xmpp_server = None
shutting_down = False
# Set format for all logs # Set format for all logs
logformat = logging.Formatter( logformat = logging.Formatter(
"[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s" "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s"
@ -74,9 +112,121 @@ xmppserverlog.addHandler(xmpp_rotate)
# Override the logging level # Override the logging level
# xmppserverlog.setLevel(logging.INFO) # xmppserverlog.setLevel(logging.INFO)
logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger
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): def get_milli_time(timetoconvert):
return int(round(timetoconvert * 1000)) return int(round(timetoconvert * 1000))
@ -89,20 +239,8 @@ def db_file():
return os_db_path() return os_db_path()
def os_db_path(createdir=True): def os_db_path(): # createdir=True):
# createdir - Set to False during tests to not create paths return os.path.join(data_dir, "bumper.db")
if platform.system() == "Windows":
if createdir:
os.makedirs(
os.getenv("APPDATA"), exist_ok=True
) # Ensure db_path directory exists or create
return os.path.join(os.getenv("APPDATA"), "bumper.db")
else:
if createdir:
os.makedirs(
os.path.expanduser("~/.config"), exist_ok=True
) # Ensure db_path directory exists or create
return os.path.expanduser("~/.config/bumper.db")
def db_get(): def db_get():
@ -300,12 +438,6 @@ class VacBotDevice(object):
"xmpp_connection": self.xmpp_connection, "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 class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home
UILogicId = "" UILogicId = ""
ota = True ota = True
@ -811,3 +943,111 @@ API_ERRORS = {
ERR_WRONG_EMAIL_ADDRESS: "1008", ERR_WRONG_EMAIL_ADDRESS: "1008",
ERR_WRONG_PWD_FROMATE: "1009", 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())

4
bumper/__main__.py Normal file
View file

@ -0,0 +1,4 @@
import bumper
if __name__ == "__main__":
bumper.main()

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from threading import Thread import json
import socket, logging, ssl, json import logging
import ssl
import string import string
import random import random
import bumper import bumper
import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import asyncio import asyncio
from aiohttp import web from aiohttp import web
@ -63,6 +63,8 @@ class ConfServer:
self.confthread = None self.confthread = None
self.run_async = False self.run_async = False
self.app = None self.app = None
self.site = None
self.runner = None
def confserver_app(self): def confserver_app(self):
self.app = web.Application(loop=asyncio.get_event_loop()) self.app = web.Application(loop=asyncio.get_event_loop())
@ -167,36 +169,43 @@ class ConfServer:
confserverlog.info( confserverlog.info(
"Starting ConfServer at {}:{}".format(self.address[0], self.address[1]) "Starting ConfServer at {}:{}".format(self.address[0], self.address[1])
) )
runner = web.AppRunner(self.app) self.runner = web.AppRunner(self.app)
await runner.setup() await self.runner.setup()
if self.usessl: if self.usessl:
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key) ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
site = web.TCPSite( self.site = web.TCPSite(
runner, self.runner,
host=self.address[0], host=self.address[0],
port=self.address[1], port=self.address[1],
ssl_context=ssl_ctx, ssl_context=ssl_ctx,
) )
else: 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: except PermissionError as e:
if "bind" in e.strerror: confserverlog.error(e.strerror)
confserverlog.exception( asyncio.create_task(bumper.shutdown())
"Error binding confserver, exiting. Try using a different hostname or IP - {}".format(
e except asyncio.CancelledError:
) pass
)
exit(1) 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: except Exception as e:
confserverlog.exception("{}".format(e)) confserverlog.exception("{}".format(e))
exit(1)
async def handle_base(self, request): async def handle_base(self, request):
try: try:
@ -877,19 +886,26 @@ class ConfServer:
if todo == "FindBest": if todo == "FindBest":
service = postbody["service"] service = postbody["service"]
if service == "EcoMsgNew": if service == "EcoMsgNew":
srvip = socket.gethostbyname(socket.gethostname()) srvip = bumper.bumper_announce_ip
srvport = 5223 srvport = 5223
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( "Announcing EcoMsgNew Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
body = {"result": "ok", "ip": srvip, "port": srvport} msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
msgserver = json.dumps(msgserver)
msgserver = msgserver.replace(
" ", ""
) # bot seems to be very picky about having no spaces, only way was with text
return web.json_response(text=msgserver)
elif service == "EcoUpdate": elif service == "EcoUpdate":
srvip = "47.88.66.164" # EcoVacs Server srvip = "47.88.66.164" # EcoVacs Server
srvport = 8005 srvport = 8005
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoUpdate Server to Bot as: {}:{}".format( "Announcing EcoUpdate Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
@ -937,9 +953,7 @@ class ConfServer:
bumper.bot_remove(postbody["did"]) bumper.bot_remove(postbody["did"])
body = {"result": "ok", "todo": "result"} body = {"result": "ok", "todo": "result"}
confserverlog.debug( confserverlog.debug("POST: {} - Response: {}".format(postbody, body))
"\r\n POST: {} \r\n Response: {}".format(postbody, body)
)
return web.json_response(body) return web.json_response(body)
@ -982,9 +996,7 @@ class ConfServer:
"todo": "result", "todo": "result",
} }
confserverlog.debug( confserverlog.debug("POST: {} - Response: {}".format(postbody, body))
"\r\n POST: {} \r\n Response: {}".format(postbody, body)
)
return web.json_response(body) return web.json_response(body)
@ -1012,10 +1024,10 @@ class ConfServer:
if todo == "FindBest": if todo == "FindBest":
service = postbody["service"] service = postbody["service"]
if service == "EcoMsgNew": if service == "EcoMsgNew":
srvip = socket.gethostbyname(socket.gethostname()) srvip = bumper.bumper_announce_ip
srvport = 5223 srvport = 5223
confserverlog.info( confserverlog.info(
"Reporting FindBest-EcoMsgNew Server to Bot as: {}:{}".format( "Announcing EcoMsgNew Server to bot as: {}:{}".format(
srvip, srvport srvip, srvport
) )
) )
@ -1025,17 +1037,18 @@ class ConfServer:
" ", "" " ", ""
) # bot seems to be very picky about having no spaces, only way was with text ) # bot seems to be very picky about having no spaces, only way was with text
confserverlog.debug(
"\r\n POST: {} \r\n Response: {}".format(postbody, msgserver)
)
return web.json_response(text=msgserver) return web.json_response(text=msgserver)
elif service == "EcoUpdate": elif service == "EcoUpdate":
body = {"result": "ok", "ip": "47.88.66.164", "port": 8005} srvip = "47.88.66.164" # EcoVacs Server
srvport = 8005
confserverlog.debug( confserverlog.info(
"\r\n POST: {} \r\n Response: {}".format(postbody, body) "Announcing EcoUpdate Server to bot as: {}:{}".format(
srvip, srvport
) )
)
body = {"result": "ok", "ip": srvip, "port": srvport}
return web.json_response(body) return web.json_response(body)
except Exception as e: except Exception as e:
@ -1097,7 +1110,7 @@ class ConfServer:
body = {"ret": "ok", "logs": []} body = {"ret": "ok", "logs": []}
confserverlog.debug( confserverlog.debug(
"\r\n POST: {} \r\n Response: {}".format(json_body, body) "POST: {} - Response: {}".format(json_body, body)
) )
return web.json_response(body) return web.json_response(body)

View file

@ -9,8 +9,6 @@ from hbmqtt.client import MQTTClient
from hbmqtt.mqtt.constants import QOS_0, QOS_1, QOS_2 from hbmqtt.mqtt.constants import QOS_0, QOS_1, QOS_2
import pkg_resources import pkg_resources
import time import time
from threading import Thread
import ssl
import bumper import bumper
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -60,8 +58,20 @@ class MQTTHelperBot:
("iot/atr/+", QOS_0), ("iot/atr/+", QOS_0),
] ]
) )
asyncio.create_task(self.get_msg()) 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: except Exception as e:
helperbotlog.exception("{}".format(e)) helperbotlog.exception("{}".format(e))
@ -194,25 +204,23 @@ class MQTTServer:
broker = None broker = None
async def broker_coro(self): async def broker_coro(self):
try:
mqttserverlog.info( mqttserverlog.info(
"Starting MQTT Server at {}:{}".format(self.address[0], self.address[1]) "Starting MQTT Server at {}:{}".format(self.address[0], self.address[1])
) )
self.broker = hbmqtt.broker.Broker(config=self.default_config) self.broker = hbmqtt.broker.Broker(config=self.default_config)
try:
await self.broker.start() await self.broker.start()
except PermissionError as e: except hbmqtt.broker.BrokerException as e:
if "bind" in e.strerror: mqttserverlog.exception(e)
mqttserverlog.exception( asyncio.create_task(bumper.shutdown())
"Error binding mqttserver, exiting. Try using a different hostname or IP - {}".format( pass
e
)
)
exit(1)
except Exception as e: except Exception as e:
mqttserverlog.exception("{}".format(e)) mqttserverlog.exception("{}".format(e))
exit(1) asyncio.create_task(bumper.shutdown())
def __init__(self, address): def __init__(self, address):
try: try:
@ -300,10 +308,8 @@ class BumperMQTTServer_Plugin:
"eco-ng", "eco-ng",
) )
mqttserverlog.debug( mqttserverlog.info(
"new bot authenticated SN: {} DID: {}".format( "bot authenticated SN: {} DID: {}".format(username, didsplit[0])
username, didsplit[0]
)
) )
authenticated = True authenticated = True
@ -324,9 +330,7 @@ class BumperMQTTServer_Plugin:
if auth: if auth:
bumper.client_add(userid, realm, resource) bumper.client_add(userid, realm, resource)
mqttserverlog.debug( mqttserverlog.info("client authenticated {}".format(userid))
"client authenticated {}".format(userid)
)
authenticated = True authenticated = True
else: else:

View file

@ -1,11 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from threading import Thread import logging
import sys, socket, threading, re, time, logging, uuid, xml.etree.ElementTree as ET import re
import uuid
import xml.etree.ElementTree as ET
import base64 import base64
import ssl import ssl
import bumper import bumper
import asyncio, functools import asyncio
xmppserverlog = logging.getLogger("xmppserver") xmppserverlog = logging.getLogger("xmppserver")
@ -22,6 +24,7 @@ class XMPPServer:
self.xmpp_protocol = lambda: XMPPServer_Protocol() self.xmpp_protocol = lambda: XMPPServer_Protocol()
async def start_async_server(self): async def start_async_server(self):
try:
xmppserverlog.info( xmppserverlog.info(
"Starting XMPP Server at {}:{}".format(self.address[0], self.address[1]) "Starting XMPP Server at {}:{}".format(self.address[0], self.address[1])
) )
@ -34,6 +37,18 @@ class XMPPServer:
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): def disconnect(self):
try: try:
xmppserverlog.debug("waiting for all client threads to exit") xmppserverlog.debug("waiting for all client threads to exit")
@ -51,10 +66,6 @@ class XMPPServer:
class XMPPServer_Protocol(asyncio.Protocol): class XMPPServer_Protocol(asyncio.Protocol):
client_id = None client_id = None
exit_flag = False exit_flag = False
server_cert = "./certs/cert.pem"
server_key = "./certs/key.pem"
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(server_cert, server_key)
aclient = None aclient = None
def connection_made(self, transport): def connection_made(self, transport):
@ -506,7 +517,7 @@ class XMPPAsyncClient:
if self.devclass: # if there is a devclass it is a bot if self.devclass: # if there is a devclass it is a bot
bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy") bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy")
self.type = self.BOT self.type = self.BOT
xmppserverlog.debug("bot authenticated {}".format(self.uid)) xmppserverlog.info("bot authenticated SN: {}".format(self.uid))
# Send response # Send response
self.send( self.send(
'<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
@ -525,7 +536,7 @@ class XMPPAsyncClient:
if auth: if auth:
self.type = self.CONTROLLER self.type = self.CONTROLLER
bumper.client_add(self.uid, "bumper", self.clientresource) bumper.client_add(self.uid, "bumper", self.clientresource)
xmppserverlog.debug("client authenticated {}".format(self.uid)) xmppserverlog.info("client authenticated {}".format(self.uid))
# Client authenticated, move to next state # Client authenticated, move to next state
self._set_state("INIT") self._set_state("INIT")

Binary file not shown.

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -1,2 +0,0 @@
V 290129055932Z 99BD46167599BDB4 unknown /C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper
V 290129061718Z 99BD46167599BDB5 unknown /CN=Bumper

View file

@ -1 +0,0 @@
unique_subject = yes

View file

@ -1 +0,0 @@
unique_subject = yes

View file

@ -1 +0,0 @@
V 290129055932Z 99BD46167599BDB4 unknown /C=US/ST=California/O=Bumper/OU=Bumper/CN=Bumper

View file

@ -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

View file

@ -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-----

View file

@ -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-----

View file

@ -1 +0,0 @@
99BD46167599BDB6

View file

@ -1 +0,0 @@
99BD46167599BDB5

3
certs/README.md Normal file
View file

@ -0,0 +1,3 @@
# Certs Directory
Place your created certs in this directory

View file

@ -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-----

View file

@ -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-----

View file

@ -0,0 +1,6 @@
ecovacs.com
*.ecovacs.com
ecouser.net
*.ecouser.net
ecovacs.net
*.ecovacs.net

BIN
create_certs/create_certs_linux Executable file

Binary file not shown.

BIN
create_certs/create_certs_osx Executable file

Binary file not shown.

BIN
create_certs/create_certs_rpi Executable file

Binary file not shown.

Binary file not shown.

12
create_certs/src/build.sh Executable file
View file

@ -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"

View file

@ -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()
}

3
data/README.md Normal file
View file

@ -0,0 +1,3 @@
# Data Directory
Where Bumper executes persistent data will be stored here (bumper.db)

163
docs/Create_Certs.md Normal file
View file

@ -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.

View file

@ -29,12 +29,22 @@ 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` - 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 | | Address | Description |
|--|--| | --------------------------------------- | ---------------------------------------------- |
| `lb-{countrycode}.ecovacs.net` | Load-balancer that is checked by the app/robot | | `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 | | `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-{countrycode}.ecouser.net` | Used for Login and Rest API |
| `portal-ww.ecouser.net` | Used for various Rest APIs | | `portal-ww.ecouser.net` | Used for various Rest APIs |
| `msg-{countrycode}.ecouser.net` | Used for XMPP | | `msg-{countrycode}.ecouser.net` | Used for XMPP |
| `msg-ww.ecouser.net` | Used for XMPP |
| `mq-ww.ecouser.net` | Used for MQTT | | `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 | | `gl-{countrycode}-api.ecovacs.com` | Used by Ecovacs Home app for API |
| `recommender.ecovacs.com` | Used by Ecovacs Home app | | `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 |

14
docs/Env_Var.md Normal file
View file

@ -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 |

View file

@ -5,7 +5,7 @@ Bumper runs multiple services to re-create what the central EcoVacs servers prov
**Services** **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` | | 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` | | XMPP Server | For bots that utilize this protocol | 5223 | `xmppserver.py` |
| MQTT Server | For bots that utilize this protocol | 8883 | `mqttserver.py` | | MQTT Server | For bots that utilize this protocol | 8883 | `mqttserver.py` |

3
logs/README.md Normal file
View file

@ -0,0 +1,3 @@
# Logs Directory
When Bumper executes, logs will be created here

View file

@ -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!")

5
tests/pytest.ini Normal file
View file

@ -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

View file

@ -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-----

View file

@ -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-----

19
tests/test_certs/ca.crt Normal file
View file

@ -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-----

27
tests/test_certs/ca.key Normal file
View file

@ -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-----

View file

@ -26,6 +26,11 @@ def async_return(result):
return f 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(): async def test_confserver_ssl():
conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=True, helperbot=None) conf_server = bumper.ConfServer(("127.0.0.1", 111111), usessl=True, helperbot=None)
conf_server.confserver_app() conf_server.confserver_app()
@ -39,8 +44,7 @@ async def test_confserver_no_ssl():
async def test_base(aiohttp_client): async def test_base(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -51,8 +55,7 @@ async def test_base(aiohttp_client):
async def test_login(aiohttp_client): async def test_login(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -66,8 +69,7 @@ async def test_login(aiohttp_client):
assert "uid" in jsonresp["data"] assert "uid" in jsonresp["data"]
assert "username" in jsonresp["data"] assert "username" in jsonresp["data"]
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
# Test global_e without user # Test global_e without user
@ -104,8 +106,7 @@ async def test_login(aiohttp_client):
async def test_logout(aiohttp_client): async def test_logout(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -126,8 +127,7 @@ async def test_logout(aiohttp_client):
async def test_checkLogin(aiohttp_client): async def test_checkLogin(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -217,8 +217,7 @@ async def test_checkLogin(aiohttp_client):
async def test_getAuthCode(aiohttp_client): async def test_getAuthCode(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -273,8 +272,7 @@ async def test_getAuthCode(aiohttp_client):
async def test_checkAgreement(aiohttp_client): async def test_checkAgreement(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -295,8 +293,7 @@ async def test_checkAgreement(aiohttp_client):
async def test_homePageAlert(aiohttp_client): async def test_homePageAlert(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -310,8 +307,7 @@ async def test_homePageAlert(aiohttp_client):
async def test_checkVersion(aiohttp_client): async def test_checkVersion(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -323,8 +319,7 @@ async def test_checkVersion(aiohttp_client):
async def test_checkAppVersion(aiohttp_client): async def test_checkAppVersion(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -338,8 +333,7 @@ async def test_checkAppVersion(aiohttp_client):
async def test_uploadDeviceInfo(aiohttp_client): async def test_uploadDeviceInfo(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -353,8 +347,7 @@ async def test_uploadDeviceInfo(aiohttp_client):
async def test_getAdByPositionType(aiohttp_client): async def test_getAdByPositionType(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -368,8 +361,7 @@ async def test_getAdByPositionType(aiohttp_client):
async def test_getBootScreen(aiohttp_client): async def test_getBootScreen(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -383,8 +375,7 @@ async def test_getBootScreen(aiohttp_client):
async def test_hasUnreadMsg(aiohttp_client): async def test_hasUnreadMsg(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -398,8 +389,7 @@ async def test_hasUnreadMsg(aiohttp_client):
async def test_getMsgList(aiohttp_client): async def test_getMsgList(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -413,8 +403,7 @@ async def test_getMsgList(aiohttp_client):
async def test_getSystemReminder(aiohttp_client): async def test_getSystemReminder(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -428,8 +417,7 @@ async def test_getSystemReminder(aiohttp_client):
async def test_getCnWapShopConfig(aiohttp_client): async def test_getCnWapShopConfig(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -443,8 +431,7 @@ async def test_getCnWapShopConfig(aiohttp_client):
async def test_neng_hasUnreadMessage(aiohttp_client): async def test_neng_hasUnreadMessage(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -466,8 +453,7 @@ async def test_neng_hasUnreadMessage(aiohttp_client):
async def test_getProductIotMap(aiohttp_client): async def test_getProductIotMap(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -479,8 +465,7 @@ async def test_getProductIotMap(aiohttp_client):
async def test_getUsersAPI(aiohttp_client): async def test_getUsersAPI(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -492,8 +477,7 @@ async def test_getUsersAPI(aiohttp_client):
async def test_getUserAccountInfo(aiohttp_client): async def test_getUserAccountInfo(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
bumper.user_add("testuser") bumper.user_add("testuser")
bumper.user_add_device("testuser", "dev_1234") bumper.user_add_device("testuser", "dev_1234")
@ -516,8 +500,7 @@ async def test_getUserAccountInfo(aiohttp_client):
async def test_postUsersAPI(aiohttp_client): async def test_postUsersAPI(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -668,8 +651,7 @@ async def test_postUsersAPI(aiohttp_client):
async def test_appsvr_api(aiohttp_client): async def test_appsvr_api(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -708,8 +690,7 @@ async def test_appsvr_api(aiohttp_client):
async def test_lg_logs(aiohttp_client): async def test_lg_logs(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
bumper.bot_add("sn_1234", "did_1234", "ls1ok3", "res_1234", "eco-ng") bumper.bot_add("sn_1234", "did_1234", "ls1ok3", "res_1234", "eco-ng")
bumper.bot_set_mqtt("did_1234", True) bumper.bot_set_mqtt("did_1234", True)
@ -726,7 +707,6 @@ async def test_lg_logs(aiohttp_client):
return_value=async_return(command_getstatus_resp) return_value=async_return(command_getstatus_resp)
) )
# Test GetGlobalDeviceList # Test GetGlobalDeviceList
postbody = { postbody = {
"auth": { "auth": {
@ -756,8 +736,7 @@ async def test_lg_logs(aiohttp_client):
async def test_postLookup(aiohttp_client): async def test_postLookup(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -779,8 +758,7 @@ async def test_postLookup(aiohttp_client):
async def test_devmgr(aiohttp_client): async def test_devmgr(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
confserver = create_confserver() confserver = create_confserver()
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
@ -846,8 +824,7 @@ async def test_devmgr(aiohttp_client):
async def test_dim_devmanager(aiohttp_client): async def test_dim_devmanager(aiohttp_client):
if os.path.exists("tests/tmp.db"): remove_existing_db()
os.remove("tests/tmp.db") # Remove existing db
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
confserver = create_confserver() confserver = create_confserver()
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)

View file

@ -1,4 +1,5 @@
import mock import mock
from mock import patch
import pytest import pytest
from tinydb.storages import MemoryStorage from tinydb.storages import MemoryStorage
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
@ -6,6 +7,9 @@ import bumper
import os import os
import datetime, time import datetime, time
import platform import platform
import json
import asyncio
from testfixtures import LogCapture
def test_get_milli_time(): def test_get_milli_time():
@ -19,23 +23,61 @@ def test_get_milli_time():
) )
def test_user_db(): 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"): if os.path.exists("tests/tmp.db"):
os.remove("tests/tmp.db") # Remove existing db os.remove("tests/tmp.db") # Remove existing db
# Test os_db_path b = bumper
platform.system = mock.MagicMock(return_value="Windows") b.db = "tests/tmp.db" # Set db location for testing
p = platform.system() b.conf1_listen_address = "0.0.0.0"
os.getenv = mock.MagicMock(return_value="C:\\AppData") b.conf1_listen_port = 443
o = os.getenv("APPDATA") asyncio.create_task(b.start())
assert bumper.os_db_path(createdir=False) == os.path.join( await asyncio.sleep(0.1)
os.getenv("APPDATA"), "bumper.db" 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
platform.system = mock.MagicMock(return_value="Linux")
assert bumper.os_db_path(createdir=False) == os.path.expanduser( async def test_start_stop_debug():
"~/.config/bumper.db" 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():
bumper.db = "tests/tmp.db" # Set db location for testing bumper.db = "tests/tmp.db" # Set db location for testing
bumper.user_add("testuser") # Add testuser bumper.user_add("testuser") # Add testuser

View file

@ -18,13 +18,6 @@ async def test_helperbot_message():
mqtt_address = ("127.0.0.1", 8883) mqtt_address = ("127.0.0.1", 8883)
mqtt_server = bumper.MQTTServer(mqtt_address) mqtt_server = bumper.MQTTServer(mqtt_address)
await mqtt_server.broker_coro() 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 # Test broadcast message
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)

View file

@ -221,16 +221,12 @@ async def test_xmpp_server_client_tls():
await writer.drain() await writer.drain()
await asyncio.sleep(0.1)
writer.write( writer.write(
b'<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl</auth>' b'<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl</auth>'
) # Send Auth ) # Send Auth
await writer.drain() await writer.drain()
await asyncio.sleep(0.1)
xmpp_address = ("127.0.0.1", 5223) xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address) xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server() await xmpp_server.start_async_server()

111
tests/test_z_problem.py Normal file
View file

@ -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"

View file

@ -5,12 +5,12 @@ Bumper uses pytest for the majority of test cases. Install requirements using `
Enter pipenv shell `pipenv shell` Enter pipenv shell `pipenv shell`
### Run tests ### Run tests
`python -m pytest` `python -m pytest tests`
### Run tests with coverage ### Run tests with coverage
`python -m pytest --cov=bumper tests/` `python -m pytest --cov=./ tests`
### Run tests with coverage html report ### 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. The report will be output into tests/report/index.html for further analysis.