Get rid of pysftp, it was pretty broken, now use paramiko directly

This commit is contained in:
Ondrej Mikle 2023-09-16 21:51:47 +02:00
parent 31e4549e8b
commit 30ca988239
2 changed files with 45 additions and 8 deletions

View file

@ -4,8 +4,11 @@ This is an access-control system implementation via contactless ISO 14443A cards
and a PN53x-based reader. So you basically swipe your card, and if it's in and a PN53x-based reader. So you basically swipe your card, and if it's in
database, the door unlocks. database, the door unlocks.
We still run this at this day (2023-09-16) because porting to Python 3 and SWIG is
not that simple, especially the SWIG part.
It's a bit old project, so requires python 2 (didn't have time for porting). At least currently It's a bit old project, so requires python 2 (didn't have time for porting). At least currently
(2010-05-28) still works on latest Raspberry 4 and Raspbian Buster. (2023-09-16) still works on latest Raspberry 4 and Raspbian Buster.
Info about authorized users and their cards and keys is stored in sqlite database. Info about authorized users and their cards and keys is stored in sqlite database.
@ -29,6 +32,9 @@ and panic trigger) or a cheap electromagnetic lock.
* Yubikey Neo HMAC-SHA1 - most safe option, uncloneable * Yubikey Neo HMAC-SHA1 - most safe option, uncloneable
* Mifare Desfire - Ed25519 signature of UID (currently no known clones available; although some features could be cloned, * Mifare Desfire - Ed25519 signature of UID (currently no known clones available; although some features could be cloned,
it's not enough for anyone to create such partial clones) it's not enough for anyone to create such partial clones)
* working Mifare Desfire clones are nowhere to be seen in 2023, even though
the datasheet was leaked loooong time ago (adding encryption wouldn't be so
hard with `libfreefare`)
Test code is also provided to get payment signature (cryptogram) from Visa and Mastercard, but it's not used. Test code is also provided to get payment signature (cryptogram) from Visa and Mastercard, but it's not used.
@ -63,7 +69,7 @@ You need just to run `make`. Additional dependencies:
- [SWIG version 2](http://www.swig.org/) - to generate Python-C++ bindings, SWIG 3 is known to cause segfaults sometimes - [SWIG version 2](http://www.swig.org/) - to generate Python-C++ bindings, SWIG 3 is known to cause segfaults sometimes
- [WiringPi2 pythonic binding](https://github.com/WiringPi/WiringPi2-Python) (for switching lock on Raspberry), install from pip, `pip install wiringpi` - [WiringPi2 pythonic binding](https://github.com/WiringPi/WiringPi2-Python) (for switching lock on Raspberry), install from pip, `pip install wiringpi`
- [python-irc](https://pypi.python.org/pypi/irc) >= 16.0, use "pip install irc", the one in repos is old - [python-irc](https://pypi.python.org/pypi/irc) >= 16.0, use "pip install irc", the one in repos is old
- [pysftp](https://pypi.org/project/pysftp/) - for uploading SpaceAPI-formatted status to some host - [paramiko](https://pypi.org/project/paramiko/) - for uploading SpaceAPI-formatted status to some host
- optional runtime dependency, not needed unless you set SFTP SpaceAPI upload to true - optional runtime dependency, not needed unless you set SFTP SpaceAPI upload to true
All dependencies can be installed on Ubuntu or Debian/Raspbian via: All dependencies can be installed on Ubuntu or Debian/Raspbian via:

View file

@ -382,21 +382,52 @@ class SpaceAPIUploader(object):
:param isOpen - whether space is opened. :param isOpen - whether space is opened.
:raises paramiko.ssh_exception.SSHException when upload fails (timeout, can't connect, host key mismatch...) :raises paramiko.ssh_exception.SSHException when upload fails (timeout, can't connect, host key mismatch...)
""" """
import pysftp import paramiko
dirname, targetFname = os.path.split(self.config.sftpDestFile) dirname, targetFname = os.path.split(self.config.sftpDestFile)
spaceApiJson = json.load(file("spaceapi_template.json")) spaceApiJson = json.load(file("spaceapi_template.json"))
spaceApiJson["state"] = {"open": isOpen, "lastchange": time.time()} spaceApiJson["state"] = {"open": isOpen, "lastchange": time.time()}
with tempfile.NamedTemporaryFile() as tf: with tempfile.NamedTemporaryFile() as tf:
log_paramiko = logging.getLogger("paramiko")
if log_paramiko:
log_paramiko.setLevel(logging.WARNING)
json.dump(spaceApiJson, tf, indent=4, ensure_ascii=True, encoding='utf-8') json.dump(spaceApiJson, tf, indent=4, ensure_ascii=True, encoding='utf-8')
tf.flush() tf.flush()
localFilename = tf.name localFilename = tf.name
with pysftp.Connection(self.config.sftpHost, username=self.config.sftpUsername, port=self.config.sftpPort,
private_key=self.config.sftpKey) as sftp: pkey = None
sftp.timeout = 15 ssh = None
with sftp.cd(dirname): sftp = None
sftp.put(localFilename, remotepath=targetFname)
for pkey_class in (paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey, paramiko.Ed25519Key):
try:
pkey = pkey_class.from_private_key_file(self.config.sftpKey)
logging.debug("Loaded SSH key of type %s", pkey_class)
break
except paramiko.ssh_exception.SSHException:
pass
if pkey is None:
logging.error("Failed to load private SSH key", self.config.sftpKey)
raise paramiko.ssh_exception.SSHException("Can't read private SSH key")
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.config.sftpHost, username=self.config.sftpUsername, pkey=pkey, timeout=20)
sftp = ssh.open_sftp()
sftp.chdir(dirname)
sftp.put(localFilename, targetFname)
finally:
if sftp:
sftp.close()
if ssh:
ssh.close()
class OpenSwitchThread(threading.Thread): class OpenSwitchThread(threading.Thread):