From 4ed08790e7c4d99d2effe6e02347eab538b3b263 Mon Sep 17 00:00:00 2001 From: hydrian Date: Sun, 16 Jun 2024 15:51:01 -0400 Subject: [PATCH 1/5] WIP: adding SSH key support --- conf.example.py | 4 + main.py | 24 ++- templates/index.html.orig | 334 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 templates/index.html.orig diff --git a/conf.example.py b/conf.example.py index 12e04fb..4d10032 100644 --- a/conf.example.py +++ b/conf.example.py @@ -5,3 +5,7 @@ ssh_password = 'timekpr-next-remote' ssh_timekpra_bin = '/usr/bin/timekpra' ssh_key = './id_timekpr' + +### If your SSH private key does not have a passphrase, leave +### the ssk_key_passphrase configuration option commented out +#ssk_key_passphrase = 'mypassphrase' \ No newline at end of file diff --git a/main.py b/main.py index dda2b89..b19cd11 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ from fabric import Connection from paramiko.ssh_exception import AuthenticationException from paramiko.ssh_exception import NoValidConnectionsError - +from pathlib import Path def get_config(): return conf.trackme @@ -49,14 +49,26 @@ def get_usage(user, computer, ssh): def get_connection(computer): global connection # todo handle SSH keys instead of forcing it to be passsword only - connect_kwargs = { - 'allow_agent': False, - 'look_for_keys': False, - "password": conf.ssh_password - } + if conf.ssh_key.is_file() : + connect_kwargs = { + 'allow_agent': False, + 'look_for_keys': False, + 'key_filename': conf.ssh_key + } + if conf.ssh_key_passphrase is defined and conf.ssh_key_passphrase is string : + use_passphrase = { 'passphrase': conf.ssh_key_passphrase } + connect_kwargs = connect_kwargs | use_passphrase + + else : + connect_kwargs = { + 'allow_agent': False, + 'look_for_keys': False, + "password": conf.ssh_password + } try: connection = Connection( host=computer, + user=conf.ssh_user, connect_kwargs=connect_kwargs ) diff --git a/templates/index.html.orig b/templates/index.html.orig new file mode 100644 index 0000000..07a1373 --- /dev/null +++ b/templates/index.html.orig @@ -0,0 +1,334 @@ + + + + + + + + + + + + Timekpr Next Remote + + + + + + + Fork me on GitHub + +
+ +
+ + Timekpr Next Remote + + +
+ +
+
+
+ +
+ + + + + + + + + + + From ae38f8c30c01127b334817aaed31ec2c46b18366 Mon Sep 17 00:00:00 2001 From: hydrian Date: Fri, 21 Jun 2024 20:28:44 -0400 Subject: [PATCH 2/5] Added SSH key support --- main.py | 46 ++++++++++++++++++++++++++------------------- requirements.txt | 5 +++-- timekpr-next-web.py | 3 ++- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/main.py b/main.py index b19cd11..1b08193 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,17 @@ # Press Shift+F10 to execute it or replace it with your code. # Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. -import conf, re + +import conf, re, os from fabric import Connection from paramiko.ssh_exception import AuthenticationException from paramiko.ssh_exception import NoValidConnectionsError +from paramiko.ssh_exception import ChannelException +from paramiko.ssh_exception import PasswordRequiredException +from paramiko.ssh_exception import SSHException from pathlib import Path + def get_config(): return conf.trackme @@ -49,36 +54,39 @@ def get_usage(user, computer, ssh): def get_connection(computer): global connection # todo handle SSH keys instead of forcing it to be passsword only - if conf.ssh_key.is_file() : - connect_kwargs = { - 'allow_agent': False, - 'look_for_keys': False, - 'key_filename': conf.ssh_key - } - if conf.ssh_key_passphrase is defined and conf.ssh_key_passphrase is string : - use_passphrase = { 'passphrase': conf.ssh_key_passphrase } - connect_kwargs = connect_kwargs | use_passphrase + + connect_kwargs_common = { + 'allow_agent': False, + 'look_for_keys': False, + } + if hasattr(conf,'ssh_key') and Path(conf.ssh_key).is_file() : + connect_kwargs_merged = connect_kwargs_common | { 'key_filename': conf.ssh_key } + if hasattr(conf, 'ssh_key_passphrase') : + connect_kwargs_merged = connect_kwargs_merged | { 'password': conf.ssh_key_passphrase } + elif hasattr(conf,'ssh_password') : + connect_kwargs_merged = connect_kwargs_common | {"password": conf.ssh_password} else : - connect_kwargs = { - 'allow_agent': False, - 'look_for_keys': False, - "password": conf.ssh_password - } + quit(f"No SSH authentication configured") try: connection = Connection( host=computer, - user=conf.ssh_user, - connect_kwargs=connect_kwargs + forward_agent=False, + connect_kwargs=connect_kwargs_merged ) + return connection except AuthenticationException as e: quit(f"Wrong credentials for user '{conf.ssh_user}' on host '{computer}'. " f"Check `ssh_user` and `ssh_password` credentials in conf.py.") + except ChannelException as e : + quit(f"Could not create a SSH channel for host '{computer}' \n Code: {code} Msg: {text} ") + except PasswordRequiredException as e: + quit(f"SSH Private key passphrase is unset or incorrect") + except SSHException as e : + quit(f"Failed to negotiate SSH connection or logic failure") except Exception as e: quit(f"Error logging in as user '{conf.ssh_user}' on host '{computer}', check conf.py. \n\n\t" + str(e)) - finally: - return connection def adjust_time(up_down_string, seconds, ssh, user): command = conf.ssh_timekpra_bin + ' --settimeleft ' + user + ' ' + up_down_string + ' ' + str(seconds) diff --git a/requirements.txt b/requirements.txt index 230d495..2608c8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ fabric -paramiko -flask \ No newline at end of file +paramiko > 1.6 +flask +pathlib \ No newline at end of file diff --git a/timekpr-next-web.py b/timekpr-next-web.py index 9e48fab..9f2c5e6 100644 --- a/timekpr-next-web.py +++ b/timekpr-next-web.py @@ -1,5 +1,6 @@ import main -import conf, re, os +import conf +import re, os from fabric import Connection from paramiko.ssh_exception import AuthenticationException from flask import Flask, render_template, request, send_from_directory From d43514c31cbe170b63dfefca2d855606d60abc94 Mon Sep 17 00:00:00 2001 From: Ben Tyger <988950+hydrian@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:42:19 -0400 Subject: [PATCH 3/5] Update README.md Fix documentation for now working ssh key support. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a709bdd..2083943 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ https://user-images.githubusercontent.com/8253488/220523348-76c3f5ea-419d-46a7-8 2. copy `conf.example.py` to `conf.py` 3. edit `conf.py`: * change `ssh_password` to be a good password. This will be the password used to log into all the clients + * if you are using an SSH key for authentication, fill out `ssh_key` and optionally `ssh_key_passphrase`. Comment out the `ssh_password` directive. * add key value pairs in `trackme` of the clients and IPs you want to manage. You can use domain names as well. 4. run `docker compose up -d` 5. go to `http://your-server-IP:8080` on your phone or desktop @@ -67,7 +68,8 @@ Follow these steps for on each client you want to control: * `ssh_user` - user to SSH into client machines as. Defaults to `timekpr-next-remote` * `ssh_password` - password to use wen SSHing into client machines. Defaults to `timekpr-next-remote` * `ssh_timekpra_bin` - path on clients where `timekpra` executable is. defaults to `/usr/bin/timekpra` -`ssh_key` - wtf - I don't know, SSH library wouldn't work with out this. don't touch this +* `ssh_key` - file location of SSH private key file. Supports OpenSSH private key format. +* `ssh_key_passphrase` - passphrase for encrypted private key file (optional) ### docker compose From 77bbc51376ca00cdc110a343d8d7676f37dfbff5 Mon Sep 17 00:00:00 2001 From: Ben Tyger <988950+hydrian@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:47:25 -0400 Subject: [PATCH 4/5] Update README.md Fixed feature checklist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2083943..0afc317 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ By design, this system is very secure as far as controlling clients over SSH, bu * [ ] add PIN protection in web GUI * [ ] add "refresh" button per client in web GUI -* [ ] support ssh keys +* [X] support ssh keys * [X] enable more than one user per machine * [X] better error handling when SSH fails etc. * [X] AJAX async loading From c74e55e534ef787a96dd515d72172e3e63668d61 Mon Sep 17 00:00:00 2001 From: Ben Tyger <988950+hydrian@users.noreply.github.com> Date: Fri, 21 Jun 2024 21:16:13 -0400 Subject: [PATCH 5/5] Delete templates/index.html.orig Deleted uneeded file --- templates/index.html.orig | 334 -------------------------------------- 1 file changed, 334 deletions(-) delete mode 100644 templates/index.html.orig diff --git a/templates/index.html.orig b/templates/index.html.orig deleted file mode 100644 index 07a1373..0000000 --- a/templates/index.html.orig +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - - - - - - - Timekpr Next Remote - - - - - - - Fork me on GitHub - -
- -
- - Timekpr Next Remote - - -
- -
-
-
- -
- - - - - - - - - - -