diff --git a/README.md b/README.md index 886031f..99b839b 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,15 @@ Retrieves events from the HID Evo Solo door controllers, and pushes them to a SQ ## Systemd There are systemd units in the [`systemd`](./systemd/) folder, which can be used to run the various scripts regularly. + +## SSL Certificates + +The HID Evo Solo door controllers we use have a self-signed certificate, which is included in this repo as [`hidglobal.com.pem`](./hidglobal.com.pem). + +If you need to use a different certificate, you can either download it with your browser or the following command (replacing `SERVER` by the address of the door): + +```sh +openssl s_client -connect SERVER:443 example.pem +``` + +Then set `doorControllerCA_BUNDLE` in `config.yaml` to the path to the created pem file. If your doors have different certificates, (which ours annoyingly don't), you will need to concatenate the certificates together into a single file. diff --git a/config.example.yaml b/config.example.yaml index 9d6059b..ea9a079 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -6,6 +6,8 @@ doorControllers: Wood Shop Rear: {ip: 172.18.51.15, access: Wood Shop} Storage Closet: {ip: 172.18.51.16, access: Storage Closet} +doorControllerCA_BUNDLE: "hidglobal.com.pem" + # {member type: door schedule} memberLevels: CMS Staff: 7x24 diff --git a/hidglobal.com.pem b/hidglobal.com.pem new file mode 100644 index 0000000..395c11e --- /dev/null +++ b/hidglobal.com.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID9TCCAt2gAwIBAgIJAPBotjnyfGu6MA0GCSqGSIb3DQEBCwUAMIGQMQswCQYD +VQQGEwJVUzERMA8GA1UECAwIQ29sb3JhZG8xFDASBgNVBAcMC1dlc3RtaW5zdGVy +MQwwCgYDVQQKDANISUQxDDAKBgNVBAsMA05BUzEWMBQGA1UEAwwNaGlkZ2xvYmFs +LmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBoaWRnbG9iYWwuY29tMB4XDTE5 +MDcyMjEwMTQxMFoXDTI5MDcxOTEwMTQxMFowgZAxCzAJBgNVBAYTAlVTMREwDwYD +VQQIDAhDb2xvcmFkbzEUMBIGA1UEBwwLV2VzdG1pbnN0ZXIxDDAKBgNVBAoMA0hJ +RDEMMAoGA1UECwwDTkFTMRYwFAYDVQQDDA1oaWRnbG9iYWwuY29tMSQwIgYJKoZI +hvcNAQkBFhVzdXBwb3J0QGhpZGdsb2JhbC5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDSibuXB9Tn0EdwL2jDig26s/b1D9SX5B4xnZM+xZ4/mE6U +Meg5xbiTSMiWqtoSMVxG1WJDxogJWxCgZis2qk3AG89PBarg17pBmxPLYyCLricx +alyNvTJBxYgA/zKagPof6h6UqKOkhsW9qvulEmPe+TKk47pmlZXe+v+1A6PQDY5B +Y3MtqE23cnZ5nBTVanFAc1vbokMXUCCtRvE1Y/KhvuaJr2VjOSJ/KV3vcdTSCLGc +W9/n/Fv8udvI/eIkoPNpCUwngm8j3Aa7qN/OSg3SvVvBcl/Ykc08STSyZPMJGBaR +EuUcAraEBZbUDOCinDS488jKVHAXhrnvzzi7RMlhAgMBAAGjUDBOMB0GA1UdDgQW +BBSvOzxCjQi86ZUsuW0o4aa7GNAeSTAfBgNVHSMEGDAWgBSvOzxCjQi86ZUsuW0o +4aa7GNAeSTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA/8FCv6x8v +PHm2Ya/hbZ/S3amdl7/E1illeApNRZodTGCn/rlVSGanCfWYzEY2naHiDC2ImhZq +NkHK9uvUtxaVQFq5VN6WQMo351J78LLfcoqpKOLGX3b9byFvrw7WporZx3C7yL1U +LS3oxI/pgavxy1KbOIw/yl+QgV50vlfvQ7sKZ1E5YOrgWLP5nJ9OeEKRdsASJyZS +Jjl0k/eGaZreSvAZPmx4kaePfbi7DNDA+mNhSFygwt6AakjjVoF2xUZ1F+qwBtER +GPxdZWldywUYsdBRG1PPvBsMo9ME46HpPdXRIjMge8P01fsaMr/6H86ojWg9uJmH +cCKtiouo08hL +-----END CERTIFICATE----- diff --git a/memberPlumbing/config.py b/memberPlumbing/config.py index 18c9817..024aebb 100644 --- a/memberPlumbing/config.py +++ b/memberPlumbing/config.py @@ -22,6 +22,7 @@ class Config: self.DOOR_PASSWORD, name=doorName, access=doorData["access"], + cert=self.doorControllerCA_BUNDLE ) for doorName, doorData in self.doorControllers.items() } diff --git a/memberPlumbing/hid/DoorController.py b/memberPlumbing/hid/DoorController.py index f94d037..b60c13d 100644 --- a/memberPlumbing/hid/DoorController.py +++ b/memberPlumbing/hid/DoorController.py @@ -3,9 +3,10 @@ from datetime import datetime from io import StringIO import requests -import urllib3 from lxml import etree from lxml.builder import ElementMaker +from requests import Session +from requests.adapters import HTTPAdapter E_plain = ElementMaker(nsmap={"hid": "http://www.hidglobal.com/VertX"}) E = ElementMaker( @@ -22,9 +23,10 @@ fieldnames = "CardNumber,CardFormat,PinRequired,PinCode,ExtendedAccess,ExpiryDat "," ) -# TODO: where should this live? -# it's fine, ssl certs are for losers anyway -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +class HostNameIgnoringAdapter(HTTPAdapter): + def init_poolmanager(self, *args, **kwargs): + super().init_poolmanager(*args, **kwargs, assert_hostname=False) class RemoteError(Exception): @@ -33,12 +35,16 @@ class RemoteError(Exception): class DoorController: - def __init__(self, ip, username, password, name="", access=""): + def __init__(self, ip, username, password, name="", access="", cert=None): self.ip = ip self.username = username self.password = password self.name = name self.access = access + self.session = Session() + if cert is not None: + self.session.mount("https://", HostNameIgnoringAdapter()) + self.session.verify = cert # lazy evaluated, hopefully won't change for the lifetime of this object @property @@ -55,13 +61,12 @@ class DoorController: def doImport(self, params=None, files=None): """Send a request to the door control import script""" - r = requests.post( + r = self.session.post( "https://" + self.ip + "/cgi-bin/import.cgi", params=params, files=files, auth=requests.auth.HTTPDigestAuth(self.username, self.password), timeout=60, - verify=False, ) # ignore insecure SSL xml = etree.XML(r.content) if ( @@ -82,11 +87,10 @@ class DoorController: def doXMLRequest(self, xml, prefix=b''): if not isinstance(xml, bytes): xml = etree.tostring(xml) - r = requests.get( + r = self.session.get( "https://" + self.ip + "/cgi-bin/vertx_xml.cgi", params={"XML": prefix + xml}, auth=requests.auth.HTTPDigestAuth(self.username, self.password), - verify=False, ) resp_xml = etree.XML(r.content) # probably meed to be more sane about this