Moved quick-unlock+whitelist management to new tweak menu

Former-commit-id: ffb947c22fed9979052aa408db96a403f8654d7f
Former-commit-id: 9d4b8d043245f39c2ac8084f59c9dcfc26185d17
This commit is contained in:
2023-06-15 13:30:23 -04:00
parent f154cbe400
commit 8e72ae1660
4 changed files with 99 additions and 135 deletions
+6 -25
View File
@@ -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 .SH NAME
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store. \fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
.SH SYNOPSIS .SH SYNOPSIS
Client Usage: sshyp [</entry name> [argument] [option]] | [argument] Client Usage: sshyp [</entry name> [argument] [option]] | [argument]
Server Usage: sshyp <argument> [option] [device id] Server Usage: sshyp <argument>
.SH DESCRIPTION .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. 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) .SH EXAMPLES (CLIENT)
@@ -20,6 +20,9 @@ Viewing the entry database:
Reading an existing entry saved as '~/.local/share/sshyp/development/github.gpg': Reading an existing entry saved as '~/.local/share/sshyp/development/github.gpg':
sshyp /development/github sshyp /development/github
Removing an existing folder saved as '~/.local/share/sshyp/social':
sshyp /social shear
Reading the same entry without hiding the password: Reading the same entry without hiding the password:
sshyp /development/github --show 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: Manually syncing entries with the server:
sshyp sync 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) .SH ARGUMENTS (CLIENT)
help/-h bring up the help menu help/-h bring up the help menu
version/-v display sshyp version info version/-v display sshyp version info
@@ -89,13 +77,6 @@ gen:
version/-v display sshyp version info version/-v display sshyp version info
init set up sshyp init set up sshyp
tweak change configuration options/manage extensions and updates 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 .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. 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) Allow remote access to the SSH server w/public key authentication (disabling password-only authentication recommended)
Install sshyp Install sshyp
Run "sshyp init" and set the device type as server 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! Done!
Client setup: Client setup:
+2 -102
View File
@@ -309,20 +309,6 @@ def print_info():
\u001b[1moptions:\u001b[0m \u001b[1moptions:\u001b[0m
gen: gen:
update/-u{14*' '}generate a password for an existing entry\n""") 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: else:
print("\n\u001b[1msshyp ", "copyright (c) 2021-2023 ", """randall winkhart\u001b[0m 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; 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""") \u001b[1mtip 2:\u001b[0m type 'sshyp' to view a list of saved entries\n""")
# PORT START HELP-SERVER # PORT START HELP-SERVER
else: 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 \u001b[1marguments:\u001b[0m
help/-h{17*' '}bring up this menu help/-h{17*' '}bring up this menu
version/-v{14*' '}display sshyp version info version/-v{14*' '}display sshyp version info
init{20*' '}set up sshyp init{20*' '}set up sshyp
tweak{19*' '}change configuration options/manage extensions and updates tweak{19*' '}change configuration options/manage extensions and updates\n""")
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""")
# PORT END HELP-SERVER # PORT END HELP-SERVER
@@ -399,75 +378,6 @@ def sync():
run_profile(f"{home}/.config/sshyp/sshyp.ini", silent_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 # 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): def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
try: try:
@@ -839,20 +749,10 @@ if __name__ == "__main__":
if arg_count < 1: if arg_count < 1:
arguments.append('help') arguments.append('help')
print_info() 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': elif arg_count == 1 and arguments[0] == 'tweak':
success_flag = True success_flag = True
from stweak import global_menu from stweak import global_menu
global_menu('server', 'configuration options:') 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 # PORT END ARGS-SERVER
if arg_count > 0 and success_flag == 0 and arguments[0] != 'sync': if arg_count > 0 and success_flag == 0 and arguments[0] != 'sync':
+90 -7
View File
@@ -276,6 +276,90 @@ def refresh_encryption():
return 2 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 # runs secondary configuration menu
def global_menu(_device_type, _top_message): def global_menu(_device_type, _top_message):
while True: while True:
@@ -294,7 +378,7 @@ def global_menu(_device_type, _top_message):
'[OPTIONAL] re-encrypt/optimize entries', '[OPTIONAL] re-encrypt/optimize entries',
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates']) '[OPTIONAL, NOT IMPLEMENTED] extensions and updates'])
else: else:
_options.extend(['manage quick-unlock']) _options.extend(['manage quick-unlock/whitelist'])
_options.extend(['exit/done']) _options.extend(['exit/done'])
_choice += curses_radio(_options, _top_message) _choice += curses_radio(_options, _top_message)
@@ -312,7 +396,7 @@ def global_menu(_device_type, _top_message):
if _device_type == 'client': if _device_type == 'client':
gpg_config() gpg_config()
else: else:
pass # TODO implement quick-unlock management whitelist_menu()
elif _choice == 2: elif _choice == 2:
if _device_type == 'client': if _device_type == 'client':
ssh_config() ssh_config()
@@ -328,11 +412,10 @@ def global_menu(_device_type, _top_message):
elif _choice == 5: elif _choice == 5:
_enabled = quick_unlock_config(False) _enabled = quick_unlock_config(False)
if _enabled == 'true': 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 '
_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'
"function,\nyou must first log in to the sshyp server and run:\n\nsshyp whitelist " 'from there you can create a quick-unlock pin and add this device to the '
"setup (if not already done)\nsshyp whitelist add " 'whitelist')
f"'{listdir(f'{home}/.config/sshyp/devices')[0].rstrip()}'\n")
elif _choice == 7: elif _choice == 7:
_success = refresh_encryption() _success = refresh_encryption()
if _success == 1: if _success == 1:
+1 -1
View File
@@ -4,7 +4,7 @@ import re
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'), devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
'device + sync type configuration')""" '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), ('TWEAK-DEVTYPE', 'stweak.py', '', '', devtype_replacement),
('ARGS-SERVER', 'sshyp.py', '', '\n\n ', ''), ('HELP-SERVER', 'sshyp.py', '', '\n', '')) ('ARGS-SERVER', 'sshyp.py', '', '\n\n ', ''), ('HELP-SERVER', 'sshyp.py', '', '\n', ''))