Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding SSHkey support #13

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -88,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
Expand Down
4 changes: 4 additions & 0 deletions conf.example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
32 changes: 26 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# 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():
Expand Down Expand Up @@ -49,24 +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
connect_kwargs = {

connect_kwargs_common = {
'allow_agent': False,
'look_for_keys': False,
"password": conf.ssh_password
}
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 :
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)
Expand Down
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fabric
paramiko
flask
paramiko > 1.6
flask
pathlib
3 changes: 2 additions & 1 deletion timekpr-next-web.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down