mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-08-30 13:56:31 -04:00
Moved quick-unlock+whitelist management to new tweak menu
Former-commit-id: ffb947c22fed9979052aa408db96a403f8654d7f Former-commit-id: 9d4b8d043245f39c2ac8084f59c9dcfc26185d17
This commit is contained in:
+6
-25
@@ -1,10 +1,10 @@
|
||||
.TH sshyp 1 "15 May 2023" "v1.5.0" "sshyp man page"
|
||||
.TH sshyp 1 "15 June 2023" "v1.5.0" "sshyp man page"
|
||||
.SH NAME
|
||||
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
||||
.SH SYNOPSIS
|
||||
Client Usage: sshyp [</entry name> [argument] [option]] | [argument]
|
||||
|
||||
Server Usage: sshyp <argument> [option] [device id]
|
||||
Server Usage: sshyp <argument>
|
||||
.SH DESCRIPTION
|
||||
sshyp is a lightweight password and note management program written in Python. sshyp is used via CLI and is self-hosted with a client-server model. sshyp expects that you have access to a personal server with a properly configured ssh server, ideally accepting remote connections.
|
||||
.SH EXAMPLES (CLIENT)
|
||||
@@ -20,6 +20,9 @@ Viewing the entry database:
|
||||
Reading an existing entry saved as '~/.local/share/sshyp/development/github.gpg':
|
||||
sshyp /development/github
|
||||
|
||||
Removing an existing folder saved as '~/.local/share/sshyp/social':
|
||||
sshyp /social shear
|
||||
|
||||
Reading the same entry without hiding the password:
|
||||
sshyp /development/github --show
|
||||
|
||||
@@ -37,21 +40,6 @@ Creating a note-only entry saved as '~/.local/share/sshyp/notes/test note.gpg':
|
||||
|
||||
Manually syncing entries with the server:
|
||||
sshyp sync
|
||||
|
||||
Removing an existing folder saved as '~/.local/share/sshyp/social':
|
||||
sshyp /social/ shear
|
||||
.SH EXAMPLES (SERVER)
|
||||
Setting up the quick-unlock whitelist:
|
||||
sshyp whitelist setup
|
||||
|
||||
Checking the quick-unlock whitelist status of all registered client devices:
|
||||
sshyp whitelist list
|
||||
|
||||
Adding a device with the id 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_' to the quick-unlock whitelist:
|
||||
sshyp whitelist add 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_'
|
||||
|
||||
Removing a device with the id 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_' from the quick-unlock whitelist:
|
||||
sshyp whitelist del 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_'
|
||||
.SH ARGUMENTS (CLIENT)
|
||||
help/-h bring up the help menu
|
||||
version/-v display sshyp version info
|
||||
@@ -89,13 +77,6 @@ gen:
|
||||
version/-v display sshyp version info
|
||||
init set up sshyp
|
||||
tweak change configuration options/manage extensions and updates
|
||||
whitelist manage the quick-unlock whitelist
|
||||
.SH OPTIONS (SERVER)
|
||||
whitelist:
|
||||
setup set up the quick-unlock whitelist
|
||||
list/-l view all registered device ids and their quick-unlock whitelist status
|
||||
add whitelist a device id for quick-unlock
|
||||
del remove a device id from the quick-unlock whitelist
|
||||
.SH SETUP
|
||||
sshyp operates on a client-server model, and thus requires you to have access to your own server (whether it be a physical home server or a cloud rental) with remote access via SSH.
|
||||
|
||||
@@ -106,7 +87,7 @@ Server setup:
|
||||
Allow remote access to the SSH server w/public key authentication (disabling password-only authentication recommended)
|
||||
Install sshyp
|
||||
Run "sshyp init" and set the device type as server
|
||||
Optionally, run "sshyp whitelist setup" and configure quick-unlock
|
||||
Optionally, configure a quick-unlock pin from the tweak menu
|
||||
Done!
|
||||
|
||||
Client setup:
|
||||
|
||||
+2
-102
@@ -309,20 +309,6 @@ def print_info():
|
||||
\u001b[1moptions:\u001b[0m
|
||||
gen:
|
||||
update/-u{14*' '}generate a password for an existing entry\n""")
|
||||
elif arguments[0] == 'whitelist':
|
||||
if device_type == 'server':
|
||||
if arg_count > 1 and arguments[1] in ('add', 'del'):
|
||||
print("\nwhen adding or deleting devices from the whitelist,\nthe device ID must be specified as an"
|
||||
" argument\n\nexample: sshyp whitelist add 'this-is-a-quoted-device-id'")
|
||||
print(f"""\n\u001b[1musage:\u001b[0m sshyp whitelist <option> [device id]\u001b[0m\n
|
||||
\u001b[1moptions:\u001b[0m
|
||||
whitelist:
|
||||
setup{18*' '}set up the quick-unlock whitelist
|
||||
list/-l{16*' '}view all registered device ids and their quick-unlock whitelist status
|
||||
add{20*' '}whitelist a device id for quick-unlock
|
||||
del{20*' '}remove a device id from the quick-unlock whitelist\n""")
|
||||
else:
|
||||
print('\n\u001b[38;5;9merror: argument (whitelist) only available on server\u001b[0m\n')
|
||||
else:
|
||||
print("\n\u001b[1msshyp ", "copyright (c) 2021-2023 ", """randall winkhart\u001b[0m
|
||||
this is free software, and you are welcome to redistribute it under certain conditions;
|
||||
@@ -362,19 +348,12 @@ this program comes with absolutely no warranty; type 'sshyp license' for details
|
||||
\u001b[1mtip 2:\u001b[0m type 'sshyp' to view a list of saved entries\n""")
|
||||
# PORT START HELP-SERVER
|
||||
else:
|
||||
print(f"""\n\u001b[1musage:\u001b[0m sshyp <argument> [option] [device id]\n
|
||||
print(f"""\n\u001b[1musage:\u001b[0m sshyp <argument>\n
|
||||
\u001b[1marguments:\u001b[0m
|
||||
help/-h{17*' '}bring up this menu
|
||||
version/-v{14*' '}display sshyp version info
|
||||
init{20*' '}set up sshyp
|
||||
tweak{19*' '}change configuration options/manage extensions and updates
|
||||
whitelist{15*' '}manage the quick-unlock whitelist
|
||||
\n\u001b[1moptions:\u001b[0m
|
||||
whitelist:
|
||||
setup{18*' '}set up the quick-unlock whitelist
|
||||
list/-l{16*' '}view all registered device ids and their quick-unlock whitelist status
|
||||
add{20*' '}whitelist a device id for quick-unlock
|
||||
del{20*' '}remove a device id from the quick-unlock whitelist\n""")
|
||||
tweak{19*' '}change configuration options/manage extensions and updates\n""")
|
||||
# PORT END HELP-SERVER
|
||||
|
||||
|
||||
@@ -399,75 +378,6 @@ def sync():
|
||||
run_profile(f"{home}/.config/sshyp/sshyp.ini", silent_sync)
|
||||
|
||||
|
||||
# PORT START WHITELIST-SERVER
|
||||
# takes input from the user to set up quick-unlock password
|
||||
def whitelist_setup():
|
||||
from getpass import getpass
|
||||
_gpg_password_temp = str(getpass(prompt='\nfull gpg passphrase: '))
|
||||
_half_length = int(len(_gpg_password_temp)/2)
|
||||
try:
|
||||
_short_password_length = int(input("\nquick unlock pin length (must be half the length "
|
||||
f"of the gpg passphrase or less) ({_half_length}): "))
|
||||
if not 0 <= _short_password_length <= _half_length:
|
||||
_short_password_length = _half_length
|
||||
except ValueError:
|
||||
_short_password_length = _half_length
|
||||
_i, _quick_unlock_password, _quick_unlock_password_excluded = 0, '', ''
|
||||
for _char in _gpg_password_temp:
|
||||
if _i % 2 == 1 and _i < _short_password_length*2:
|
||||
_quick_unlock_password += _char
|
||||
else:
|
||||
_quick_unlock_password_excluded += _char
|
||||
_i += 1
|
||||
|
||||
# create assembly key
|
||||
open(f"{home}/.config/sshyp/gpg-gen", 'w').writelines([
|
||||
'Key-Type: 1\n', 'Key-Length: 4096\n', 'Key-Usage: sign encrypt\n', 'Name-Real: sshyp\n',
|
||||
'Name-Comment: gpg-sshyp-whitelist\n', 'Name-Email: github.com/rwinkhart/sshyp\n', 'Expire-Date: 0'])
|
||||
run(('gpg', '-q', '--pinentry-mode', 'loopback', '--batch', '--generate-key', '--passphrase',
|
||||
_quick_unlock_password, f"{home}/.config/sshyp/gpg-gen"))
|
||||
remove(f"{home}/.config/sshyp/gpg-gen")
|
||||
_gpg_id = run(('gpg', '-k', '--with-colons'), stdout=PIPE, text=True).stdout.splitlines()[-1].split(':')[9]
|
||||
|
||||
# encrypt excluded with the assembly key
|
||||
_shm_folder, _shm_entry = shm_gen()
|
||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').write(_quick_unlock_password_excluded)
|
||||
encrypt(f"{home}/.config/sshyp/excluded", _shm_folder, _shm_entry, _gpg_id)
|
||||
print(f"\nyour quick-unlock pin: {_quick_unlock_password}")
|
||||
|
||||
|
||||
# shows the quick-unlock whitelist status of device ids
|
||||
def whitelist_list():
|
||||
_whitelisted_ids = listdir(f"{home}/.config/sshyp/whitelist")
|
||||
_device_ids = listdir(f"{home}/.config/sshyp/devices")
|
||||
print('\n\u001b[1mquick-unlock whitelisted device ids:\u001b[0m')
|
||||
for _id in _whitelisted_ids:
|
||||
print(_id)
|
||||
print('\n\u001b[1mother registered device ids:\u001b[0m')
|
||||
for _id in _device_ids:
|
||||
if _id not in _whitelisted_ids:
|
||||
print(_id)
|
||||
print()
|
||||
|
||||
|
||||
# adds or removes quick-unlock whitelisted device ids
|
||||
def whitelist_manage(_device_id):
|
||||
if arguments[1] == 'add':
|
||||
if _device_id in listdir(f"{home}/.config/sshyp/devices"):
|
||||
open(f"{home}/.config/sshyp/whitelist/{_device_id}", 'w').write('')
|
||||
whitelist_list()
|
||||
else:
|
||||
print(f"\n\u001b[38;5;9merror: device id ({_device_id}) is not registered\u001b[0m\n")
|
||||
s_exit(1)
|
||||
elif isfile(f"{home}/.config/sshyp/whitelist/{_device_id}"):
|
||||
remove(f"{home}/.config/sshyp/whitelist/{_device_id}")
|
||||
whitelist_list()
|
||||
else:
|
||||
print(f"\n\u001b[38;5;9merror: device id ({_device_id}) is not whitelisted\u001b[0m\n")
|
||||
s_exit(1)
|
||||
# PORT END WHITELIST-SERVER
|
||||
|
||||
|
||||
# checks the user's whitelist status and fetches the full gpg key password if possible
|
||||
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
|
||||
try:
|
||||
@@ -839,20 +749,10 @@ if __name__ == "__main__":
|
||||
if arg_count < 1:
|
||||
arguments.append('help')
|
||||
print_info()
|
||||
elif arg_count == 2 and arguments[0] == 'whitelist':
|
||||
if arguments[1] in ('list', '-l'):
|
||||
success_flag = True
|
||||
whitelist_list()
|
||||
elif arguments[1] == 'setup':
|
||||
success_flag = True
|
||||
whitelist_setup()
|
||||
elif arg_count == 1 and arguments[0] == 'tweak':
|
||||
success_flag = True
|
||||
from stweak import global_menu
|
||||
global_menu('server', 'configuration options:')
|
||||
elif arg_count > 2 and arguments[1] in ('add', 'del'):
|
||||
success_flag = True
|
||||
whitelist_manage(arguments[2])
|
||||
# PORT END ARGS-SERVER
|
||||
|
||||
if arg_count > 0 and success_flag == 0 and arguments[0] != 'sync':
|
||||
|
||||
+90
-7
@@ -276,6 +276,90 @@ def refresh_encryption():
|
||||
return 2
|
||||
|
||||
|
||||
# PORT START WHITELIST-SERVER
|
||||
# takes input from the user to set up quick-unlock pin
|
||||
def whitelist_setup():
|
||||
_gpg_password_temp = str(curses_text('full gpg passphrase:\n\n\n\n\n(ctrl+g/enter to confirm)'))
|
||||
_half_length = int(len(_gpg_password_temp)/2)
|
||||
try:
|
||||
_short_password_length = int(curses_text(f"quick unlock pin length ({_half_length}):\n\n\n\n\n(ctrl+g/enter "
|
||||
"to confirm)\n\npin must be half the length of the gpg passphrase "
|
||||
"or less\n\ncannot be a negative number"))
|
||||
if not 0 <= _short_password_length <= _half_length:
|
||||
_short_password_length = _half_length
|
||||
except ValueError:
|
||||
_short_password_length = _half_length
|
||||
_i, _quick_unlock_password, _quick_unlock_password_excluded = 0, '', ''
|
||||
for _char in _gpg_password_temp:
|
||||
if _i % 2 == 1 and _i < _short_password_length*2:
|
||||
_quick_unlock_password += _char
|
||||
else:
|
||||
_quick_unlock_password_excluded += _char
|
||||
_i += 1
|
||||
|
||||
# create assembly key
|
||||
open(f"{home}/.config/sshyp/gpg-gen", 'w').writelines([
|
||||
'Key-Type: 1\n', 'Key-Length: 4096\n', 'Key-Usage: sign encrypt\n', 'Name-Real: sshyp\n',
|
||||
'Name-Comment: gpg-sshyp-whitelist\n', 'Name-Email: github.com/rwinkhart/sshyp\n', 'Expire-Date: 0'])
|
||||
run(('gpg', '-q', '--pinentry-mode', 'loopback', '--batch', '--generate-key', '--passphrase',
|
||||
_quick_unlock_password, f"{home}/.config/sshyp/gpg-gen"))
|
||||
remove(f"{home}/.config/sshyp/gpg-gen")
|
||||
_gpg_id = run(('gpg', '-k', '--with-colons'), stdout=PIPE, text=True).stdout.splitlines()[-1].split(':')[9]
|
||||
|
||||
# encrypt excluded with the assembly key
|
||||
from sshyp import encrypt, shm_gen
|
||||
_shm_folder, _shm_entry = shm_gen()
|
||||
open(f"{home}/.config/sshyp/tmp/{_shm_folder}/{_shm_entry}", 'w').write(_quick_unlock_password_excluded)
|
||||
encrypt(f"{home}/.config/sshyp/excluded", _shm_folder, _shm_entry, _gpg_id)
|
||||
curses_radio(['okay, I have it memorized'], f"your quick-unlock pin: {_quick_unlock_password}")
|
||||
|
||||
|
||||
# adds or removes quick-unlock whitelisted device ids
|
||||
def whitelist_manage(_action):
|
||||
_whitelisted_ids = listdir(f"{home}/.config/sshyp/whitelist")
|
||||
_device_ids = listdir(f"{home}/.config/sshyp/devices")
|
||||
|
||||
# a value of True indicates adding
|
||||
if _action:
|
||||
_unwhitelisted_ids = []
|
||||
for _id in _device_ids:
|
||||
if _id not in _whitelisted_ids:
|
||||
_unwhitelisted_ids.append(_id)
|
||||
_unwhitelisted_ids.append('cancel')
|
||||
_add_id = curses_radio(_unwhitelisted_ids, 'id to add to whitelist:')
|
||||
if _add_id == len(_unwhitelisted_ids)-1:
|
||||
return
|
||||
open(f"{home}/.config/sshyp/whitelist/{_unwhitelisted_ids[_add_id]}", 'w').write('')
|
||||
else:
|
||||
_whitelisted_choices = _whitelisted_ids + ['cancel']
|
||||
_del_id = curses_radio(_whitelisted_choices, 'id to remove from whitelist:')
|
||||
if _del_id == len(_whitelisted_choices)-1:
|
||||
return
|
||||
remove(f"{home}/.config/sshyp/whitelist/{_whitelisted_ids[_del_id]}")
|
||||
|
||||
# prune deleted device ids from whitelist
|
||||
for _id in _whitelisted_ids:
|
||||
if _id not in _device_ids:
|
||||
remove(f"{home}/.config/sshyp/whitelist/{_id}")
|
||||
|
||||
|
||||
# runs quick-unlock configuration menu
|
||||
def whitelist_menu():
|
||||
while True:
|
||||
_choice = curses_radio(('setup/create pin', 'add to whitelist', 'remove from whitelist',
|
||||
'exit/done'), 'quick-unlock/whitelist management')
|
||||
|
||||
if _choice == 0:
|
||||
whitelist_setup()
|
||||
elif _choice == 1:
|
||||
whitelist_manage(True)
|
||||
elif _choice == 2:
|
||||
whitelist_manage(False)
|
||||
else:
|
||||
break
|
||||
# PORT END WHITELIST-SERVER
|
||||
|
||||
|
||||
# runs secondary configuration menu
|
||||
def global_menu(_device_type, _top_message):
|
||||
while True:
|
||||
@@ -294,7 +378,7 @@ def global_menu(_device_type, _top_message):
|
||||
'[OPTIONAL] re-encrypt/optimize entries',
|
||||
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates'])
|
||||
else:
|
||||
_options.extend(['manage quick-unlock'])
|
||||
_options.extend(['manage quick-unlock/whitelist'])
|
||||
_options.extend(['exit/done'])
|
||||
_choice += curses_radio(_options, _top_message)
|
||||
|
||||
@@ -312,7 +396,7 @@ def global_menu(_device_type, _top_message):
|
||||
if _device_type == 'client':
|
||||
gpg_config()
|
||||
else:
|
||||
pass # TODO implement quick-unlock management
|
||||
whitelist_menu()
|
||||
elif _choice == 2:
|
||||
if _device_type == 'client':
|
||||
ssh_config()
|
||||
@@ -328,11 +412,10 @@ def global_menu(_device_type, _top_message):
|
||||
elif _choice == 5:
|
||||
_enabled = quick_unlock_config(False)
|
||||
if _enabled == 'true':
|
||||
# TODO update for future menu-based quick-unlock management
|
||||
_term_message = ("\nquick-unlock has been enabled client-side - in order for this feature to "
|
||||
"function,\nyou must first log in to the sshyp server and run:\n\nsshyp whitelist "
|
||||
"setup (if not already done)\nsshyp whitelist add "
|
||||
f"'{listdir(f'{home}/.config/sshyp/devices')[0].rstrip()}'\n")
|
||||
_term_message = ('\nquick-unlock has been enabled client-side - in order for this feature to '
|
||||
'function,\nyou must first log in to the sshyp server and run:\n\nsshyp tweak\n\n'
|
||||
'from there you can create a quick-unlock pin and add this device to the '
|
||||
'whitelist')
|
||||
elif _choice == 7:
|
||||
_success = refresh_encryption()
|
||||
if _success == 1:
|
||||
|
||||
@@ -4,7 +4,7 @@ import re
|
||||
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
|
||||
'device + sync type configuration')"""
|
||||
|
||||
targets = (('SSHYNC-REMOTE', 'sshync.py', '\n', '\n\n', ''), ('WHITELIST-SERVER', 'sshyp.py', '\n', '\n\n', ''),
|
||||
targets = (('SSHYNC-REMOTE', 'sshync.py', '\n', '\n\n', ''), ('WHITELIST-SERVER', 'stweak.py', '\n', '\n\n', ''),
|
||||
('TWEAK-DEVTYPE', 'stweak.py', '', '', devtype_replacement),
|
||||
('ARGS-SERVER', 'sshyp.py', '', '\n\n ', ''), ('HELP-SERVER', 'sshyp.py', '', '\n', ''))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user