Compare commits

..
5 Commits
Author SHA1 Message Date
RandyTheSilly 5cd50c7ad2 Backport /lib/sshyp fix
Former-commit-id: 21f1705abc3581a6317cd4a390324a97de9ba1b9
Former-commit-id: 56ccd20bef6587cc3b185c6260b4328001eb541c
2023-06-08 19:44:22 -04:00
RandyTheSilly c48722f04f Update documentation for release v1.4.3
Former-commit-id: 7805e0878e776dfc321bba12ed8ecf26f94d594c
Former-commit-id: f3a9f1ea37a35c5d6607d3f3bc33e7da9799ffe4
2023-06-08 19:12:56 +00:00
RandyTheSilly d32bd72d68 Backport multi-version Powershell fix
Former-commit-id: 7e82515f1146e2551eb887cf2513fa7e7f6fc082
Former-commit-id: 827531aca1be7bfe18cfb194e5ce9fcb124099b8
2023-06-08 15:48:43 -04:00
RandyTheSilly 59d0844d77 Backport IPv6 sync fix
Former-commit-id: 231005bca34d7fd8984749f2bbaadd5ea375bd63
Former-commit-id: d7021e0135afd97802a421726e79ef45fbf970ac
2023-06-08 15:39:16 -04:00
RandyTheSilly 0eb283892f Disabled folder renaming to prevent data loss bug - real fix in v1.5.0
Former-commit-id: 3577551b710064fc3aa079f598394f0456b2c622
Former-commit-id: 0c0be57ae8a4549848b67c7b244cdc35e35a1c58
2023-06-08 15:30:34 -04:00
35 changed files with 1126 additions and 2038 deletions
+72
View File
@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '34 22 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+1 -2
View File
@@ -1,2 +1 @@
/output
/port-jobs/working
*
+51 -39
View File
@@ -1,35 +1,48 @@
![sshyp](https://github.com/rwinkhart/sshyp-labs/blob/main/extra/artwork/sshyp-banner.png)
[![release](https://img.shields.io/github/v/release/rwinkhart/sshyp)](https://github.com/rwinkhart/sshyp/releases)
![python](https://img.shields.io/badge/python-3.8--3.12-yellow)
![python](https://img.shields.io/badge/python-3.7--3.11-yellow)
[![downloads](https://img.shields.io/github/downloads/rwinkhart/sshyp/total)](https://github.com/rwinkhart/sshyp/releases)
[![CodeQL](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
pronounced as: 'sheep', 'shēp'
sshyp is a self-hosted, synchronized password manager for UNIX(-like) systems (currently Haiku/FreeBSD/Linux). It has been succeeded by [MUTN](https://github.com/rwinkhart/MUTN) due to its many [flaws](https://github.com/rwinkhart/sshyp/blob/main/wiki/postmortem.md).
sshyp is a very simple self-hosted, synchronized password manager for UNIX(-like) systems (currently Haiku/FreeBSD/Linux).
sshyp is compatible with entries created by pass/password-store, as its original goal was to be like pass/password-store, but far more user-friendly to synchronize with a self-hosted server.
sshyp makes use of a custom sftp wrapper, called sshync (ssh+sync), to reliably sync user entries with a local or remote server.
The name "sshyp" is a combination of its synchronization library, "sshync", and "passwords".
The name "sshyp" is a combination of its syncing library, "sshync", and "passwords".
# WARNING
It is your responsibility to assess the security and stability of sshyp and to ensure it meets your needs before using it.
I am not responsible for any data loss or breaches of your information resulting from the use of sshyp.
sshyp has not been extensively tested by the public; safety and security are priorities, but they cannot be guaranteed.
It is your responsibility to assess the security and stability of "sshyp" before using it and ensure it meets your needs.
I am not responsible for any data loss or breaches of your information resulting from the use of "sshyp".
"sshyp" is a new project that is constantly being updated, and though safety and security are priorities, they cannot be guaranteed.
# Mission Statement
sshyp aims to make it as simple as possible to manage passwords and notes via CLI across multiple devices in a secure, self-hosted fashion.
What sshyp can do:
- securely manage a collection of encrypted passwords and notes via CLI
- generate new, secure passwords to the user's choice in length and complexity
- securely sync said passwords and notes seamlessly between devices (or just manage them offline)
- utilize [extensions](https://github.com/rwinkhart/sshyp-labs) to interact with your entries is additional ways (such as generating TOTP keys or managing your entries in a GUI)
- everything above with entries created by pass/password-store!
- everything above on Haiku, FreeBSD, Linux, and Termux!
# Installation
**Important:** *Shell completions (both Bash and ZSH) may require additional configuration on some distributions - please see [this page](https://github.com/rwinkhart/sshyp/blob/main/wiki/completions.md) of the wiki for support.*
**Important:** *Shell completions (both Bash and ZSH) may require additional configuration on some distributions - please see [this page](https://github.com/rwinkhart/sshyp/wiki/Completions) of the wiki for support.*
Please see the [installation guide](https://github.com/rwinkhart/sshyp/blob/main/wiki/install.md) in the sshyp wiki for directions specific to your distribution/OS.
Please see the [installation guide](https://github.com/rwinkhart/sshyp/wiki/Installation) in the sshyp wiki for directions specific to your distribution/OS.
Pre-built packages exist for Haiku, FreeBSD, Alpine Linux, Debian/Ubuntu Linux, Fedora Linux, Termux, and WSL. These can be downloaded from the releases page.
Pre-built packages exist for Haiku, FreeBSD, Alpine Linux, Arch Linux, Debian/Ubuntu Linux, Fedora Linux, and Termux. These can be downloaded from the releases page.
Additionally, sshyp is distributed on the AUR.
Support for additional environments, such as MacOS and OpenBSD, is coming soon.
Extensions can be installed from the `sshyp tweak` menu on most supported platforms.
For Haiku and Termux, extensions must instead be installed through the [system package manager](https://github.com/rwinkhart/sshyp-labs/releases).
Extensions are available in the [sshyp-labs](https://github.com/rwinkhart/sshyp-labs) repository.
# Building
A packaging script is included in the root directory of the repo in order to package sshyp for your distribution. To package sshyp from source, simply run:
@@ -46,40 +59,39 @@ Haiku and Fedora packaging must be done on their own respective distributions.
The AUR version and the packages attached to the release tags were already packaged using this script.
Currently, the script can create packages for Haiku, FreeBSD, Alpine Linux (APKBUILD), Arch Linux (PKGBUILD), Debian/Ubuntu Linux, Fedora Linux, Termux, and WSL.
Currently, the script can create packages for Haiku, FreeBSD, Alpine Linux (APKBUILD), Arch Linux (PKGBUILD), Debian/Ubuntu Linux, Fedora Linux, Termux, and generic.
# Usage
Upon initial installation (on both the server and client devices), run `sshyp init` to configure the settings necessary for sshyp to function.
Upon initial installation (on both the server and client devices), be sure to run:
In order to configure optional settings or change already configured settings, run `sshyp tweak`.
```
sshyp tweak
```
This command will allow you to configure the settings necessary for sshyp to function. To ensure configuration compatibility, it is a good idea to run 'sshyp tweak' after each major update.
Please note that decrypting and reading entries is disabled on server devices for security reasons. Only devices configured as clients can use the GPG key to decrypt entries.
All available options can be found with `sshyp help`, or alternatively, in the man page.
All available options can be found with:
# Roadmap (successor)
```
sshyp help
```
[MUTN](https://github.com/rwinkhart/MUTN) has been developed as a direct successor to sshyp. It is not yet at release v1.0.0, therefore breaking changes will be made.
Or alternatively, in the man page:
If you're interested in migrating to MUTN early, please see the [migration guide](https://github.com/rwinkhart/libmutton/blob/main/wiki/migration.md).
```
man sshyp
```
Some key differences are:
- Modularity/Maintainability
- MUTN is based off of [libmutton](https://github.com/rwinkhart/libmutton), enabling third-party clients
- Due to the modularity of the code, there will be no more "extension" support
- sshyp-mfa functionality is built into the successor
- Server and client code are now two completely separate projects
- This greatly simplifies the code and makes it easier to maintain
- This also means that third-party clients do not need to maintain separate server code
- Stability
- sshyp has a track record of making breaking changes in most of its updates
- This will not be the case with MUTN (starting with release v1.0.0)
- Efficiency
- MUTN is written in a compiled language (Go)
- This means that encryption and SSH-sync can be done natively in Go rather than relying on GnuPG/OpenSSH (this is possible with Python, but it would require users to install third-party libraries)
- MUTN makes small tweaks to the design established by sshyp to make user interactions less frustrating
- Platform support
- MUTN was built from the ground-up to support both UNIX-like platforms AND Windows
- Unfortunately, using Go means dropping Haiku support, as newer versions of Go do not (yet) support Haiku
# Roadmap
Short-term Goals:
From this point forward, sshyp will only receive minimal support (as needed) and transitional updates.
- MacOS support
- improved temporary directory security
- a minimal GUI app - being made as an [extension](https://github.com/rwinkhart/sshyp-labs)
Long-term Goals:
- a fun surprise
- seize the thrones, shear the humans
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/sh
sed -i 's/[[:space:]]*$//' ./lib/* ./port-jobs/* ./package.sh
git commit -am "$1"
git add -f extra lib/sshyp.py lib/sshync.py port-jobs share LICENSE README.md package.sh commit.sh .gitignore
git commit -m "$1"
git push
-132
View File
@@ -1,135 +1,3 @@
sshyp v1.5.1
10/24/2023
the fortified flock update - patch one
this release fixes both long and short-standing bugs that managed to escape discovery in testing;
it also addresses some issues that first-time users may be put-off by upon encountering
user-facing features:
- the clipboard is now only cleared if the contents of the clipboard still match what was placed onto it by sshyp
^ this means that sshyp will no longer remove data from the clipboard that it did not place there
- a warning/explanation is now provided when the user attempts to sync in offline mode
- a readable error is now provided if the user attempts to edit a note and the specified text editor cannot be found
- the optional configuration menu is now explained when encountered during "sshyp init"
- the man page now includes common troubleshooting information
fixes/optimizations:
- custom, user-provided ssh keyfiles are now properly utilized in all cases
^ previously, using custom ssh keyfiles would break various features
- clipboard contents are no longer leaked via the process list on Wayland
- interpolation has been disabled for ConfigParser, allowing "%" to be stored in configuration files
- gpg keys with ":" or "\" in their names are now properly written to the configuration file
- copy_id_check() and whitelist_verify() are now safely callable from extensions
- the ssh key selection will no longer see "known_hosts.old" as a valid keyfile
- when possible, data from the configuration file is now read as boolean values
- the Haiku package has been updated to use Python 3.11
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.5.0
07/12/2023
the fortified flock update
this release implements new curses-based TUI configuration menus and improves the security of sshyp
compatibility-breaking changes:
- a completely new configuration system calls for a new configuration file
^ before using sshyp v1.5.0 for the first time, 'sshyp init' will need to be ran to create the
new configuration file - the old configuration files can be safely deleted
^ 'sshyp tweak' has been repurposed as the configuration menu for changing individual options,
rather than re-doing setup entirely - after initialization, this is the one you want to use
- the new extension manager is replacing the old method of installing extensions as system packages
^ any older extensions you have installed should be uninstalled to prevent conflicts
^ extensions are now installed and removed through the 'sshyp tweak' menu's extension manager
^ this does not yet apply to Haiku and Termux, which will continue to install extensions using
the previous method
- all clients and servers must be updated to this release for the folder renaming bug fix to work
^ failure to update all devices will result in errors and/or potential data loss
- various packaging changes
^ x-clip and wl-clipboard are now marked as optional dependencies, so the correct tool will
need to be installed as needed - a warning has been added for this if neither package is installed
^ the changelog no longer ships with sshyp - it is still available on GitHub
user-facing features:
- the old configuration menu, 'sshyp tweak', has been split into two new curses-based TUI menus
^ 'sshyp init' is for first-time setup/initialization
^ 'sshyp tweak' can be used at any time to quickly adjust individual settings
^ whitelist management tools have been moved to the new 'sshyp tweak' menu
^ clients and servers now each have their own dedicated 'sshyp tweak' menu
^ the new config file is in .INI format, making it easier to edit the config without sshyp
- added a 'sshyp tweak' option for re-encrypting all entries with a new gpg key
- added a security advisory when enabling quick-unlock to ensure the user understands potential risks
- the user is now warned if the clipboard tool relevant to their platform is not installed
- passwords are now hidden by default in the entry reader
^ they can be displayed by appending '--show' or '-s' to the end of the command
- input is now hidden when adding/editing a password in an entry
- entries are no longer re-encrypted and synced if the note editor is quit without saving
fixes/optimizations:
- a tmpfs is no longer used for decrypting entries unless editing notes
^ data is now decrypted directly into sshyp and written to entries pre-encrypted
^ this makes reading entries much more secure
^ editing entries is similarly secure to before - greater improvements coming in a future update
- folder renaming has been re-enabled and now functions as intended
- some unnecessarily verbose outputs were silenced and/or made to appear more cleanly
- FreeBSD packaging fixes
^ no longer incorrectly includes and uses the logic for the Termux clipboard
^ now specifies 'python3' dependency instead of 'python'
- many lists provided to the subprocess module have been swapped with tuples
- more correct and clear language is used to describe options and arguments in the help menus
- properly display an error when attempting to copy blank fields from entries
- shebangs have been removed from libraries not meant to be run directly
other factors of note:
- with the release of Debian 12 Bookworm, sshyp is once again fully supported on vanilla Debian
^ previously, it was only working correctly in offline mode due to Debian 11's old OpenSSH package
- this is the biggest release of sshyp ever
^ it includes LOTS of minor changes and optimizations not included in the patch notes summary
^ as such, keep an eye out for new bugs!
- the next major release of sshyp may be even larger...
^ but it is also likely very far away
^ GnuPG is great, but it is inherently incompatible with sshyp's future security model
^ if all goes according to plan, GnuPG will be replaced in the next release
^ assuming this happens, this next release will be crowned v2.0.0
^ sshyp v1.5.X is expected to be the latest stable release for a longer amount of time than usual
^ sshyp v1.5.X may receive security/bug fix patches after the release of v2.0.0, as needed
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.4.3
06/08/2023
the argumentative agronomist update - patch three
IMPORTANT: the next major release of sshyp, v1.5.0, will make breaking changes that will require
running through 'sshyp tweak' again - also starting with v1.5.0, a copy of the changelog will no
longer be packaged with sshyp (it will still be available on GitHub) and clipboard tools
(x-clip and wl-clipboard) will be set to optional dependencies ('sshyp tweak' will warn of this)
this is a highly unconventional release featuring backports to fix critical bugs found during the
development of the upcoming v1.5.0
fixes:
- folder renaming has been disabled to prevent a severe data loss bug from occuring
^ this has been properly fixed for v1.5.0, but the fix requires all clients and servers
to be updated - to avoid breaking compatibility with 1.4.X, folder renaming was simply disabled
for the time being
- backported fix for syncing over IPv6
- backported fix for multiple versions of Powershell being called under WSL
- backported fix for checking /lib/sshyp instead of /usr/lib/sshyp
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.4.2
04/30/2023
-82
View File
@@ -1,82 +0,0 @@
sshyp v1.5.3
01/08/2024
the fortified flock update - patch three
this release is a quick hot-fix for some bugs that were noticed after the release of v1.5.2
fixes/optimizations:
- fixed a regression with the entry list causing entries not part of a subdirectory to appear
in-line with the entry list header
- fixed extension group ownership under FreeBSD
^ the "wheel" group is now used where previously the non-existent "root" group was used
- fixed notes sometimes being appended in the wrong position after editing an incomplete entry
- addressed flake8 suggestions (slight optimization)
- removed unnecessary empty variable declarations
^ these were previously included to silence PyCharm's PEP8 warnings
^ the project is now primarily developed in VSCodium with the flake8 linter
^ the only change to flake8 defaults in the line length limit (79->120)
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.5.2
01/07/2024
the fortified flock update - patch two
this release focuses on fixing various bugs, crashes, and visual oddities;
additionally, the new-user setup experience has been vastly improved
compatibility-breaking changes:
- this release is fully compatible with sshyp v1.5.0-v1.5.1
- the minimum Python version has been raised from Python 3.7 to Python 3.8
^ this was needed for the new device id changing behavior
^ Python 3.7 no longer receives security updates, so this should have minimal impact
^ please create an issue on GitHub if any Python version (3.8 or newer) does not work as intended
user-facing features:
- registered device ids can now be removed from the server tweak menu
^ all device id menus now stay open until the user manually exits
- changing a client's device id now attempts to automatically remove the previous id from the server
- the extension downloader now supports showing multiple lines of usage information
^ sshyp-mfa is taking advantage of this to display the info from the old man page
- basic tweak/init menu text wrapping and terminal-resizing has been added
^ the tweak/init radio menus now warn if the terminal needs to be resized to see all content;
this resizing can be done in real time without restarting sshyp (layout dynamically adjusts)
^ this avoids crashes on large radio menus in small terminals (crashes are still possible, but
only on unreasonably small terminals - this will probably stay the way it is now)
- ssh connections are no longer made from the init menu
^ this is to give the user time to register their ssh pubkey with the server
- the user is now warned that gpg key generation may take time and that sshyp should not be terminated
- the user is now warned about risks associated with changing or removing active device ids
- back buttons have been added to menus that were missing a safe way to exit
fixes/optimizations:
- quick-unlock has been fixed - invalid pins have been generated since v1.5.0
^ if quick-unlock is not working for you, re-generate your pin after this update!
- extension removal now works on Busybox environments (Alpine Linux)
- instead of crashing with an unhelpful error, sshyp now warns if a valid pinentry program is not found (gpg)
^ affects some Alpine Linux 3.19 base installs (pinentry does not always get installed with gpg)
- a recursive curses.wrapper() function is no longer used (fixes menu crash on Alpine Linux 3.19)
^ the new method of transitioning menus is more efficient on all platforms
- the wl-paste empty clipboard warning is no longer shown when multiple copies are done within 30 seconds (Wayland)
- quotes are no longer copied to the Termux clipboard on clear
- the entry reader and entry list should now be more visible in light terminals
^ in testing, I noticed some solarized dark themes bind color 8 to the same color as the terminal background,
leading to some invisible text - this will not be fixed (unless custom accent colors are added), as
I believe this is poor theme design and the fault of the particular solarized dark themes I tested
- entry list text wrapping has been vastly improved
^ better utilization of full terminal width
^ less prone to splitting entry names in half (should be impossible now)
^ no longer crashes on small terminals
- multi-word device ids can now verify via quick-unlock
- various language standardization changes
^ all back buttons in menus are now named "BACK"
^ all mentions of device ids are now referred to as "ids", rather than "names"
^ various non-user-facing variable renamings have also been made
- various menu helptext have been made more clear
^ this includes quick-unlock configuration, ssh configuration, device id configuration, and more
+57 -74
View File
@@ -1,17 +1,14 @@
.TH sshyp 1 "08 January 2024" "v1.5.3" "sshyp man page"
.TH sshyp 1 "08 June 2023" "v1.4.3" "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]
Client Usage: sshyp [</entry name> [option] [flag]] [option]
Server Usage: sshyp <argument>
Server Usage: sshyp <option> [flag] [device id]
.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)
Setting up sshyp for the first time:
sshyp init
Altering the existing sshyp configuration:
Setting up sshyp for the first time or altering its configuration (also recommended after major updates):
sshyp tweak
Viewing the entry database:
@@ -20,12 +17,6 @@ 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
Copying the password of an existing entry saved as '~/.local/share/sshyp/financial/bank.gpg':
sshyp /financial/bank copy -p
@@ -38,45 +29,65 @@ Making a new entry saved as '~/.local/share/sshyp/school/university.gpg' using t
Creating a note-only entry saved as '~/.local/share/sshyp/notes/test note.gpg':
sshyp /notes/test\ note add -n
Manually synchronizing entries with the server:
Manually syncing entries with the server:
sshyp sync
.SH ARGUMENTS (CLIENT)
help/-h bring up the help menu
version/-v display sshyp version info
init set up sshyp
tweak change configuration options/manage extensions and updates
add add an entry
gen generate a new password
edit edit an existing entry
copy copy details of an entry to your clipboard
shear delete an existing entry
sync manually sync the entry directory via sshync
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 OPTIONS (CLIENT)
help/-h bring up the help menu
version/-v display sshyp version info
tweak configure sshyp
add add an entry
gen generate a new password
edit edit an existing entry
copy copy details of an entry to your clipboard
shear delete an existing entry
sync manually sync the entry directory via sshync
.SH FLAGS (CLIENT)
add:
password/-p add a password entry
note/-n add a note entry
folder/-f add a new folder for entries
password/-p add a password entry
note/-n add a note entry
folder/-f add a new folder for entries
edit:
rename/relocate/-r rename or relocate an entry
username/-u change the username of an entry
password/-p change the password of an entry
note/-n change the note attached to an entry
url/-l change the url attached to an entry
rename/relocate/-r rename or relocate an entry
username/-u change the username of an entry
password/-p change the password of an entry
note/-n change the note attached to an entry
url/-l change the url attached to an entry
copy:
username/-u copy the username of an entry to your clipboard
password/-p copy the password of an entry to your clipboard
url/-l copy the URL of an entry to your clipboard
note/-n copy the note of an entry to your clipboard
username/-u copy the username of an entry to your clipboard
password/-p copy the password of an entry to your clipboard
url/-l copy the URL of an entry to your clipboard
note/-n copy the note of an entry to your clipboard
gen:
update/-u generate a password for an existing entry
.SH ARGUMENTS (SERVER)
help/-h bring up this menu
version/-v display sshyp version info
init set up sshyp
tweak change configuration options/manage extensions and updates
update/-u generate a password for an existing entry
.SH OPTIONS (SERVER)
help/-h bring up this menu
version/-v display sshyp version info
tweak configure sshyp
whitelist manage the quick-unlock whitelist
.SH FLAGS (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.
@@ -86,45 +97,19 @@ Server setup:
Configure an OpenSSH server (if sshyp was installed from the Debian package, openssh-server and openssh-sftp-server must be installed manually - if sshyp was installed from the Fedora package, openssh-server must be installed manually)
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, configure a quick-unlock pin from the tweak menu
Run "sshyp tweak" and set the device type as server
Optionally, run "sshyp whitelist setup" and configure quick-unlock
Done!
Client setup:
Install sshyp
Run "sshyp init" and follow the prompts
Run "sshyp tweak" and follow the prompts
Copy the generated public SSH key (located at ~/.ssh/sshyp.pub) to the server using preferred method (manually adding to authorized_keys, ssh-copy-id, etc.)
If you already have entries on the server, sync them using "sshyp sync"
Optionally, shell completions (both Bash and ZSH) can be enabled with your shell's respective method
Done!
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
.SH TROUBLESHOOTING
.B Clipboard not clearing:
The intended way for sshyp to interact with the system clipboard is for it to clear it 30 seconds after copying a field. Unfortunately, this does not work by default on all systems due to the prevalence of clipboard managers.
Clipboard managers save a history of what has been copied to the clipboard, which is already a big enough issue on its own for people who copy sensitive information to their clipboard. Some clipboard managers simply will not allow the clipboard to be empty and will replace its contents with the last copied item if you attempt to clear it. One such naughty clipboard manager is KDE Klipper, which comes packaged into KDE Plasma and is typically enabled by default on most distributions. Due to this behavior, KDE Klipper breaks sshyp's clipboard clearing functionality and should not be left enabled.
It is likely other popular clipboard managers exhibit this behavior as well (Klipper is just the one I first noticed it with). Clipboard managers should not be enabled by default in any environment or distribution due to their potential security implications.
.B Shell completions not working:
ZSH:
Make sure your ~/.zshrc contains the following:
autoload -Uz compinit && compinit
Restart your shell
Bash:
Install your distribution's 'bash-completion' package and restart your shell
OR
Source the completion script manually by adding the following to your ~/.bashrc (then restart your shell):
Linux:
source /usr/share/bash-completion/completions/sshyp
FreeBSD:
source /usr/local/share/bash-completion/completions/sshyp
Haiku:
source /system/data/bash-completion/completions/sshyp
.SH EXIT CODES
0 - no error
@@ -137,7 +122,5 @@ Please note that the server setup intentionally does not allow the reading of en
4 - decryption/encryption error
5 - server connection error
6 - other error
.SH AUTHOR
Randall Winkhart (https://github.com/rwinkhart)
-42
View File
@@ -1,42 +0,0 @@
#!/usr/bin/env python3
from hashlib import sha512
from subprocess import DEVNULL, PIPE, run
from sys import argv
from time import sleep
# set period of time to wait before attempting to clear clipboard
sleep(30)
# enable the storing of clipboard contents hash for later comparison
hash_paste = sha512()
# PORT START CLIPCLEAR
if argv[2] == 'wsl':
hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('powershell.exe', '-c', 'Set-Clipboard'))
elif argv[2] == 'wayland':
hash_paste.update(run('wl-paste', stdout=PIPE, stderr=DEVNULL).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('wl-copy', '-c'))
elif argv[2] == 'haiku':
hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('clipboard', '-r'))
elif argv[2] == 'mac':
hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run('pbcopy', input=b'')
elif argv[2] == 'termux':
hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(("termux-clipboard-set", ''))
elif argv[2] == 'x11':
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('xclip', '-i', '/dev/null', '-sel', 'c'))
# PORT END CLIPCLEAR
Regular → Executable
+41 -45
View File
@@ -1,19 +1,20 @@
#!/usr/bin/env python3
from os import listdir, remove, walk
from os.path import expanduser, isdir, isfile, getmtime, join
from os.path import expanduser, isdir, getmtime, join
from subprocess import CalledProcessError, PIPE, run
from sys import exit as s_exit
home = expanduser('~')
home = expanduser("~")
# PORT START SSHYNC-REMOTE
# REMOTE
# prints all necessary remote data to stdout
def remote_list_gen(_client_device_id, _remote_dir):
def remote_list_gen(_client_device_name, _remote_dir):
# deletions
for _file in listdir(f"{home}/.config/sshyp/deleted"):
_file_path, _sep, _device = _file.partition('\x1f')
_file_path = _file_path.replace('\x1e', '/')
if _device == _client_device_id:
if _device == _client_device_name:
print(_file_path)
try:
remove(f"{home}/.config/sshyp/deleted/{_file}")
@@ -31,8 +32,8 @@ def remote_list_gen(_client_device_id, _remote_dir):
# HYBRID
# deletes a file or folder and/or marks it for deletion upon synchronizing
def delete(_file_path, _target_database, _silent):
# deletes a file or folder and/or marks it for deletion upon syncing
def delete(_file_path, _target_database):
from shutil import rmtree
_directory = f"{home}/.local/share/sshyp/"
try:
@@ -41,11 +42,10 @@ def delete(_file_path, _target_database, _silent):
else:
remove(f"{_directory}{_file_path}.gpg")
except FileNotFoundError:
if not _silent:
print(f"location does not exist {_target_database}")
print(f"location does not exist {_target_database}")
if _target_database == 'remotely':
for _device_id in listdir(f"{home}/.config/sshyp/devices"):
open(f"{home}/.config/sshyp/deleted/" + _file_path.replace('/', '\x1e') + '\x1f' + _device_id, 'w')
for _device_name in listdir(f"{home}/.config/sshyp/devices"):
open(f"{home}/.config/sshyp/deleted/" + _file_path.replace('/', '\x1e') + '\x1f' + _device_name, 'w')
# retrieves and returns titles and mod times from the local device
@@ -67,9 +67,9 @@ def get_local_data(_directory, _device):
# captures and returns all necessary data from the remote server
def remote_list_fetch(_user_data):
try:
_remote_data = run(('ssh', '-i', _user_data[5], '-p', _user_data[2], f"{_user_data[0]}@{_user_data[1]}",
_remote_data = run(['ssh', '-i', _user_data[5], '-p', _user_data[2], f"{_user_data[0]}@{_user_data[1]}",
f'cd /usr/lib/sshyp; python3 -c \'from sshync import remote_list_gen; remote_list_gen'
f'("{_user_data[6]}", "{_user_data[4]}")\''), stdout=PIPE, text=True, check=True
f'("{_user_data[6]}", "{_user_data[4]}")\''], stdout=PIPE, text=True, check=True
).stdout.split('\x1d')
except CalledProcessError:
print('\n\u001b[38;5;9merror: failed to connect to the remote server\u001b[0m\n')
@@ -78,21 +78,17 @@ def remote_list_fetch(_user_data):
_deletion_database = _remote_data[0].strip().splitlines()
_folder_database = _remote_data[1].strip().splitlines()
_titles_mods = _remote_data[2].strip().splitlines()
return _deletion_database, _folder_database, _titles_mods[:len(_titles_mods) // 2], \
_titles_mods[len(_titles_mods) // 2:]
return _deletion_database, _folder_database, _titles_mods[:len(_titles_mods)//2], \
_titles_mods[len(_titles_mods)//2:]
# checks for and acts upon files and folders marked for deletion
def deletion_sync(_deletion_database, _silent):
_synced = False
for _file in _deletion_database:
if _file != '':
if not _silent:
_synced = True
print(f"\u001b[38;5;208m{_file}\u001b[0m has been sheared, removing...")
delete(_file, 'locally', _silent)
if _synced:
print()
delete(_file, 'locally')
# creates matches of remote folders on the local client
@@ -121,21 +117,25 @@ def sort_titles_mods(_list_1, _list_2):
return _title_list_2_sorted, _mod_list_2_sorted
# creates a sshync job profile
def make_profile(_profile_dir, _local_dir, _remote_dir, _identity, _ip, _port, _user):
open(_profile_dir, 'w').write(f"{_user}\n{_ip}\n{_port}\n{_local_dir}\n{_remote_dir}\n{_identity}\n")
# returns a list of data read from a sshync job profile
def get_profile(_profile_dir):
from configparser import ConfigParser
_profile_data = ConfigParser(interpolation=None)
if isfile(_profile_dir):
_profile_data.read(_profile_dir)
else:
try:
_profile_data = open(_profile_dir).readlines()
except (FileNotFoundError, IndexError):
print('\n\u001b[38;5;9merror: the profile does not exist or is corrupted\u001b[0m\n')
_profile_data = None
s_exit(2)
_user = _profile_data.get('SSHYNC', 'user')
_ip = _profile_data.get('SSHYNC', 'ip')
_port = _profile_data.get('SSHYNC', 'port')
_local_dir = _profile_data.get('SSHYNC', 'local_dir')
_remote_dir = _profile_data.get('SSHYNC', 'remote_dir')
_identity = _profile_data.get('SSHYNC', 'identity_file')
_user = _profile_data[0].rstrip()
_ip = _profile_data[1].rstrip()
_port = _profile_data[2].rstrip()
_local_dir = _profile_data[3].rstrip()
_remote_dir = _profile_data[4].rstrip()
_identity = _profile_data[5].rstrip()
_client_device_id = listdir(f"{home}/.config/sshyp/devices")[0].rstrip()
return _user, _ip, _port, _local_dir, _remote_dir, _identity, _client_device_id
@@ -154,31 +154,27 @@ def run_profile(_profile_dir, _silent):
_index_local = sort_titles_mods(_remote_titles_mods, get_local_data(_user_data[3], 'client'))
_index_remote = sort_titles_mods(_index_local, _remote_titles_mods)
# sync new and updated files
_i, _synced = -1, False
_i = -1
for _title in _index_local[0]:
_i += 1
if _title in _index_remote[0]:
# compare mod times and sync
if int(_index_local[1][_i]) > int(_index_remote[1][_i]):
_synced = True
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is newer locally, uploading...")
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}"))
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
elif int(_index_local[1][_i]) < int(_index_remote[1][_i]):
_synced = True
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is newer remotely, downloading...")
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}", f"{_user_data[3]}{_title}"))
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}",
f"{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
else:
_synced = True
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is not on remote server, uploading...")
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}"))
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
for _title in _index_remote[0]:
if _title not in _index_local[0]:
_synced = True
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is not in local directory, downloading...")
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}", f"{_user_data[3]}{_title}"))
if _synced:
print()
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}",
f"{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
+633 -452
View File
File diff suppressed because it is too large Load Diff
-734
View File
@@ -1,734 +0,0 @@
from configparser import ConfigParser
from curses import A_REVERSE, KEY_DOWN, KEY_UP, curs_set, newwin
from curses.textpad import rectangle, Textbox
from os import environ, listdir, remove
from os.path import exists, expanduser, isfile
from pathlib import Path
from random import randint
from shutil import which
from subprocess import CalledProcessError, DEVNULL, PIPE, run
# PORT START UNAME-IMPORT-STWEAK
from os import uname
# PORT END UNAME-IMPORT-STWEAK
home, sshyp_data, stdscr, gm_device_type = expanduser('~'), ConfigParser(interpolation=None), None, None
if isfile(f"{home}/.config/sshyp/sshyp.ini"):
_exists_flag = True
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
else:
_exists_flag = False
# writes data stored in ConfigParser to the correct config file
def write_config(_sshyp_data=sshyp_data):
with open(f"{home}/.config/sshyp/sshyp.ini", 'w') as configfile:
_sshyp_data.write(configfile)
# creates a radio selection between the provided options
def curses_radio(_options, _pretext):
curs_set(0)
_selected = 0
while True:
# clear curses window
stdscr.clear()
# get terminal size
_height, _width = stdscr.getmaxyx()
# track whether to generate interactive buttons (depending on terminal size)
_button_gen = False
# split text based on new lines
_pretext_lines = _pretext.split('\n')
# iterate through lines, splitting further (wrapping) as needed, to add to curses window
_current_line = 0
for _line in _pretext_lines:
if _current_line <= _height - 4:
_button_gen = True
while len(_line) > _width:
stdscr.addstr(_current_line, 0, _line[:_width])
_line = _line[_width:]
_current_line += 1
stdscr.addstr(_current_line, 0, _line)
_current_line += 1
else:
_button_gen = False
stdscr.addstr(_current_line, 0, '# warning: re-size terminal to see more information'[:_width - 1])
_current_line += 1
break
# create user-interactive options if all information has been displayed to the user
if _button_gen:
for _i, _option in enumerate(_options):
_y = _i + _current_line + 1
if _y < _height:
if _i == _selected:
stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE)
else:
stdscr.addstr(_y, 0, "[ ] " + _option)
else:
stdscr.addstr(_y - 1, 0, '# warning: re-size terminal to see more information')
break
# update _selected based on user input
_key = stdscr.getch()
if _button_gen:
if _key == KEY_UP:
_selected = (_selected - 1) % len(_options)
elif _key == KEY_DOWN:
_selected = (_selected + 1) % len(_options)
# ord('\n') == 10
elif _key == 10:
break
stdscr.refresh()
curs_set(1)
return _selected
# creates a text-box input
def curses_text(_pretext):
stdscr.clear()
stdscr.addstr(0, 0, _pretext)
_width = stdscr.getmaxyx()[1]
_editwin = newwin(1, _width - 2, 3, 1)
rectangle(stdscr, 2, 0, 4, _width - 1)
stdscr.refresh()
_box = Textbox(_editwin)
# let the user edit until ctrl+g/enter is struck
_box.edit()
# return resulting contents
return _box.gather().strip()
# device+sync type selection
def install_type():
_offline_mode = 'false'
# PORT START TWEAK-DEVTYPE
_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)', 'server'),
'device + sync type configuration')
# PORT END TWEAK-DEVTYPE
if _install_type == 2:
_dev_type = 'server'
Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True)
Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True)
curses_radio(['okay'], 'make sure the ssh service is running and properly configured')
else:
_dev_type = 'client'
if _install_type == 1:
_offline_mode = 'true'
if not sshyp_data.has_section('CLIENT-GENERAL'):
sshyp_data.add_section('CLIENT-GENERAL')
sshyp_data.set('CLIENT-GENERAL', 'offline_mode_enabled', _offline_mode)
if not sshyp_data.has_section('GENERAL'):
sshyp_data.add_section('GENERAL')
sshyp_data.set('GENERAL', 'device_type', _dev_type)
write_config()
return _dev_type, _offline_mode
# gpg configuration
def gpg_config():
# gpg key selection
_uid_list = [_item for _item in run(('gpg', '-k', '--with-colons'),
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
_named_uid_list = []
for _uid in _uid_list:
_named_uid_list.append(_uid.split(':')[9].replace('\\x3a', ':').replace('\\x5c', '\\'))
_named_uid_list.append('auto-generate')
_gpg_id_sel = curses_radio(_named_uid_list, 'gpg key selection')
if _gpg_id_sel == len(_named_uid_list) - 1:
if not isfile(f"{home}/.config/sshyp/gpg-gen"):
open(f"{home}/.config/sshyp/gpg-gen", 'w').writelines([
'Key-Type: eddsa\n', 'Key-Curve: ed25519\n', 'Key-Usage: sign\n', 'Subkey-Type: ecdh\n',
'Subkey-Curve: cv25519\n', 'Subkey-Usage: encrypt\n', 'Name-Real: sshyp\n',
'Name-Comment: gpg-sshyp\n', 'Name-Email: github.com/rwinkhart/sshyp\n', 'Expire-Date: 0'])
curses_radio(['okay'], 'gpg key generation may take some time (especially on slower devices)\n\nselect "okay" '
'to start\n\ndo not terminate this process!')
try:
run(('gpg', '-q', '--batch', '--generate-key', f"{home}/.config/sshyp/gpg-gen"), stderr=PIPE, check=True)
except CalledProcessError as e:
if 'No pinentry' in e.stderr.decode("utf-8"):
curses_radio(['okay'], 'either a valid pinentry program is missing or gpg is not configured to use an '
'available pinentry program\n\nsshyp will now exit')
from sys import exit as s_exit
s_exit(6)
remove(f"{home}/.config/sshyp/gpg-gen")
_gpg_id = run(('gpg', '-k', '--with-colons'), stdout=PIPE, text=True).stdout.splitlines()[-1].split(':')[9]
else:
_gpg_id = _named_uid_list[_gpg_id_sel]
# lock file generation
if isfile(f"{home}/.config/sshyp/lock.gpg"):
remove(f"{home}/.config/sshyp/lock.gpg")
open(f"{home}/.config/sshyp/lock", 'w')
run(('gpg', '-qr', _gpg_id, '-e', f"{home}/.config/sshyp/lock"))
remove(f"{home}/.config/sshyp/lock")
if not sshyp_data.has_section('CLIENT-GENERAL'):
sshyp_data.add_section('CLIENT-GENERAL')
sshyp_data.set('CLIENT-GENERAL', 'gpg_id', _gpg_id)
write_config()
# text editor configuration
def editor_config(_env_mode):
if _env_mode:
# set default text editor to value of EDITOR environment variable, otherwise default to vi
if 'EDITOR' in environ:
_editor = environ['EDITOR']
else:
_editor = 'vi'
else:
_editor = curses_text('enter the name of your preferred text editor:\n\n\n\n\n(ctrl+g/enter to confirm)'
'\n\nthis will be used for writing notes\n\nexample input: vim')
if not sshyp_data.has_section('CLIENT-GENERAL'):
sshyp_data.add_section('CLIENT-GENERAL')
sshyp_data.set('CLIENT-GENERAL', 'text_editor', _editor)
write_config()
# ssh+sshync configuration
def ssh_config(_reconfig=False):
# private key selection/generation
_keys = []
# ensure ~/.ssh directory exists
Path(f"{home}/.ssh").mkdir(mode=0o700, exist_ok=True)
for _file in listdir(f"{home}/.ssh"):
if not _file.startswith('.') and _file not in ('known_hosts', 'known_hosts.old', 'authorized_keys') \
and not _file.endswith('.pub') and isfile(f"{home}/.ssh/{_file}"):
_keys.append(f"{home}/.ssh/{_file}")
_keys.extend(['auto-generate', 'other (type the location)'])
# append a back button if launched optionally
if _reconfig:
_keys.append('BACK')
_key_selected_num = curses_radio(_keys, 'which private ssh key would you like to use for sshyp?')
if _reconfig and _key_selected_num == len(_keys) - 1:
return
_gen_index = len(_keys) - 2
if _key_selected_num >= _gen_index:
_ssh_key = expanduser(curses_text('enter the location for your private ssh key:\n\n\n\n\n(ctrl+g/enter to '
'confirm)\n\nexample input:\n\n~/.ssh/privkey'))
if _key_selected_num == _gen_index:
_passphrase = curses_text('enter your desired ssh keyfile passphrase:\n\n\n\n\n(ctrl+g/enter to confirm)'
'\n\nnote: it is recommended to leave this blank and to use the keyfile without '
'a passphrase (the keyfile itself serves as your identity)')
run(('ssh-keygen', '-q', '-t', 'ed25519', '-N', _passphrase, '-f', _ssh_key))
else:
_ssh_key = _keys[_key_selected_num]
# ssh+sshync configuration
_uiport = curses_text('enter the username, ip, and ssh port of your sshyp server:\n\n\n\n\n(ctrl+g/enter to '
'confirm)\n\nexample inputs:\n\n ipv4: user@10.10.10.10:22\n ipv6: user@[2000:2000:2000:2000'
':2000:2000:2000:2000]:22\n domain: user@mydomain.com:22')
_uiport_split = _uiport.split('@')
_username_ssh = _uiport_split[0]
_iport = _uiport_split[1].lstrip('[').replace(']', '').rsplit(':', 1)
if not sshyp_data.has_section('SSHYNC'):
sshyp_data.add_section('SSHYNC')
sshyp_data.set('SSHYNC', 'user', _username_ssh)
sshyp_data.set('SSHYNC', 'ip', _iport[0])
sshyp_data.set('SSHYNC', 'port', _iport[1])
sshyp_data.set('SSHYNC', 'local_dir', f"{home}/.local/share/sshyp/")
sshyp_data.set('SSHYNC', 'remote_dir', f"/home/{_username_ssh}/.local/share/sshyp/")
sshyp_data.set('SSHYNC', 'identity_file', _ssh_key)
write_config()
return _iport[1], _username_ssh, _iport[0], _ssh_key
# device id configuration
def dev_id_config(_port, _username_ssh, _ip, _identity, _reconfig=False):
if _reconfig:
_sure = curses_radio(('no', 'yes'), "WARNING: ensure this sshyp client is synchronized (up-to-date) before "
"changing the device id\n\nfailure to do so may result in sync"
"hronization issues\n\nare you sure you wish to change the device id?")
if _sure != 1:
return
from sshyp import copy_id_check, string_gen
_device_id_prefix = curses_text('set this device\'s id:\n\n\n\n\n(ctrl+g/enter to confirm)\n\nimportant: this '
'id must be unique amongst your client devices\n\nthis is used to keep track of '
'database synchronization and quick-unlock permissions\n')
_device_id_suffix = string_gen('f', randint(24, 48))
_device_id = _device_id_prefix + '-' + _device_id_suffix
# remove existing device ids
_device_id_list = listdir(f"{home}/.config/sshyp/devices")
for _id in _device_id_list:
remove(f"{home}/.config/sshyp/devices/{_id}")
open(f"{home}/.config/sshyp/devices/{_device_id}", 'w')
# test server connection and attempt to register device id - only run if _reconfig is True, since the keyfile
# needs to be registered with the server before a successful connection can be made
if _reconfig:
# copy_id_check() returns false if successful
if not copy_id_check(_port, _username_ssh, _ip, _device_id, _identity, sshyp_data):
# remove old device id from registered pool and whitelist
run(('ssh', '-o', 'ConnectTimeout=3', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
'python3 -c \'from pathlib import Path; '
f'Path("/home/{_username_ssh}/.config/sshyp/devices/{_device_id_list[0]}").unlink(missing_ok=True); '
f'Path("/home/{_username_ssh}/.config/sshyp/whitelist/{_device_id_list[0]}")'
f'.unlink(missing_ok=True)\''), stderr=DEVNULL, stdout=DEVNULL)
else:
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'true')
write_config()
curses_radio(['okay'], 'this device will be registered with the server upon the first successful sync\n\n'
'you can force this now by running "sshyp sync"\n\nif you have not done so already, '
'ensure your ssh pubkey has been appended to the server\'s authorized_keys file!')
# quick-unlock configuration
def quick_unlock_config(_default):
if _default:
_enabled = 'false'
else:
_quick_unlock_sel = curses_radio(('no', 'yes'), 'WARNING: quick-unlock is only as secure as the environment you'
' use it in\n\nquick-unlock allows you to use a shorter version'
' of your gpg key passphrase and\nrequires a constant '
'connection to your sshyp server to authenticate\n\na '
'compromised program on your computer could scan the process '
'list in the\nbrief period during decryption to retrieve the '
'necessary information to decrypt your entries\n\nenable '
'quick-unlock?')
if _quick_unlock_sel == 1:
_enabled = 'true'
else:
_enabled = 'false'
if not sshyp_data.has_section('CLIENT-ONLINE'):
sshyp_data.add_section('CLIENT-ONLINE')
sshyp_data.set('CLIENT-ONLINE', 'quick_unlock_enabled', _enabled)
write_config()
return _enabled
# re-encrypt/optimize all entries
def refresh_encryption():
_directory = f"{home}/.local/share/sshyp"
# warn the user of potential data loss and prompt to continue
_sure = curses_radio(('no', 'yes'), "WARNING: proceeding with this action will remove/overwrite any directories"
f" matching the following:\n\n{home}/.local/share/sshyp.old\n{home}/.local/"
"share/sshyp.new\n\nare you sure you wish to re-encrypt all entries?")
if _sure != 1:
return 3
# set new gpg key
gpg_config()
from os import walk
from os.path import isdir
from shutil import move, rmtree
from sshyp import decrypt, encrypt
# prompt for unlock and display do not close warning
decrypt(None)
curses_radio(['okay'], 'entry optimization may take some time (especially on slower devices)\n\nselect "okay" '
'to start\n\ndo not terminate this process!')
# remove existing conflicts
for _extension in ('.new', '.old'):
if exists(f"{_directory}{_extension}"):
rmtree(f"{_directory}{_extension}")
# decrypt, optimize, and re-encrypt each entry with the newly selected key
if isdir(_directory):
_gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
for _root, _dirs, _files in sorted(walk(_directory, topdown=True)):
for _filename in _files:
Path(_root.replace(_directory, _directory + '.new', 1)).mkdir(0o700, parents=True, exist_ok=True)
encrypt(decrypt(f"{_root}/{_filename[:-4]}"),
f"{_root.replace(_directory, _directory + '.new', 1)}/{_filename[:-4]}", _gpg_id)
# create a backup of the original version and activate the new version
move(_directory, _directory + '.old')
move(_directory + '.new', _directory)
return 1
else:
return 2
# PORT START WHITELIST-SERVER
# removes a registered device id from the server-side pool and prunes the quick-unlock whitelist
def registered_dev_id_remover(_back=False):
_device_ids = listdir(f"{home}/.config/sshyp/devices") + ['BACK']
_whitelisted_ids = listdir(f"{home}/.config/sshyp/whitelist")
while not _back:
_del_id = curses_radio(_device_ids, 'WARNING: only remove registered device ids if they are no longer in use'
'\n\nthe removal of an active device id will result in the malfunction of '
'the device still using the removed id\n\nregistered device id to remove:')
if _del_id == len(_device_ids) - 1:
_back = True
else:
# remove deleted device id from whitelist
if _device_ids[_del_id] in _whitelisted_ids:
remove(f"{home}/.config/sshyp/whitelist/{_whitelisted_ids[_del_id]}")
_whitelisted_ids = [_id for _id in _whitelisted_ids if _id != _device_ids[_del_id]]
# remove deleted device id from device pool
remove(f"{home}/.config/sshyp/devices/{_device_ids[_del_id]}")
del _device_ids[_del_id]
# 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 = 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\nnote: do NOT enter your desired pin - this is simply "
"an integer used to determine the length of the auto-generated pin"
"\n\npin must be half the length of the gpg passphrase or less and "
"must be greater than 0"))
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'])
curses_radio(['okay'], 'gpg key generation may take some time (especially on slower devices)\n\nselect "okay" '
'to start\n\ndo not terminate this process!')
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
encrypt([_quick_unlock_password_excluded], f"{home}/.config/sshyp/excluded", _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, _back=False):
_whitelisted_ids = listdir(f"{home}/.config/sshyp/whitelist") + ['BACK']
_device_ids = listdir(f"{home}/.config/sshyp/devices")
_unwhitelisted_ids = []
for _id in _device_ids:
if _id not in _whitelisted_ids:
_unwhitelisted_ids.append(_id)
_unwhitelisted_ids.append('BACK')
while not _back:
# _action == True indicates adding
if _action:
_add_id = curses_radio(_unwhitelisted_ids, 'id to add to whitelist:')
if _add_id == len(_unwhitelisted_ids) - 1:
_back = True
else:
open(f"{home}/.config/sshyp/whitelist/{_unwhitelisted_ids[_add_id]}", 'w').write('')
del _unwhitelisted_ids[_add_id]
# _action == False indicates removing
else:
_del_id = curses_radio(_whitelisted_ids, 'id to remove from whitelist:')
if _del_id == len(_whitelisted_ids) - 1:
_back = True
else:
remove(f"{home}/.config/sshyp/whitelist/{_whitelisted_ids[_del_id]}")
del _whitelisted_ids[_del_id]
# runs quick-unlock configuration menu
def whitelist_menu():
while True:
_choice = curses_radio(('setup/create pin', 'add to whitelist', 'remove from whitelist',
'BACK'), '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
# PORT START TWEAK-EXTEND-FUNCTIONS
# downloads/updates extensions
def extension_downloader():
from os import chmod
from tempfile import gettempdir
from urllib.request import urlopen, urlretrieve
# the version listed below will NOT always match the version of sshyp being used
# it is only updated if new extensions are incompatible with previous sshyp versions
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.2").read()
_pointer = ConfigParser(interpolation=None)
_pointer.read_string(_file_data.decode('utf-8'))
_extensions = _pointer.sections()
_extensions.append('BACK')
_choice = curses_radio(_extensions, 'select an extension for more info')
if _choice == len(_extensions) - 1:
return False
_selected = _extensions[_choice]
_divider = (stdscr.getmaxyx()[1]) * '-'
_choice = curses_radio(('no', 'yes'), '# description\n' + _divider + '\n\n' + _pointer.get(_selected, 'desc') +
'\n\n# usage\n' + _divider + '\n\n' + _pointer.get(_selected, 'usage').replace('<br>', '\n')
+ '\n\n' + _divider + '\n\ninstall ' + _selected + '?')
# if installing the extension...
if _choice == 1:
# download extension files to temporary directory
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
urlretrieve(_pointer.get(_selected, 'exe'), _exe_dir)
urlretrieve(_pointer.get(_selected, 'ini'), _ini_dir)
# set permissions under active user
chmod(_exe_dir, 0o755)
chmod(_ini_dir, 0o644)
return _selected
return False
# uninstalls/deletes selected extensions
def extension_remover():
_installed = []
for _extension in listdir('/usr/lib/sshyp/extensions'):
_installed.append(_extension[:-4])
_installed.append('BACK')
_choice = curses_radio(_installed, 'select an extension to uninstall')
if _choice == len(_installed) - 1:
return False
_sure = curses_radio(('no', 'yes'), f"are you sure you want to remove {_installed[_choice]}?")
if _sure == 0:
return False
return _installed[_choice]
# provides options for managing extensions
def extension_menu():
_ext_name, _action = False, False
# determine which of the supported privilege escalation utilities is installed
if which('doas') is not None:
_escalator = 'doas'
elif which('sudo') is not None:
_escalator = 'sudo'
else:
# throw an error if no supported privilege escalation utility is found
raise ChildProcessError
while True:
_choice = curses_radio(('download/update extensions', 'remove extensions', 'BACK'), 'extension management')
if _choice == 0:
_ext_name = extension_downloader()
if _ext_name:
# True represents installation
_action = True
break
elif _choice == 1:
_ext_name = extension_remover()
if _ext_name:
break
else:
break
return _ext_name, _escalator, _action
# PORT END TWEAK-EXTEND-FUNCTIONS
# runs secondary configuration menu
def global_menu(_scr, _device_type, _top_message):
global stdscr
# only set global stdscr if running as entry point
if not isinstance(_scr, bool):
stdscr = _scr
while True:
_options, _choice, _exit_signal = ['change device/synchronization types'], 0, False
if _device_type == 'client':
_options.extend(['change gpg key', 're-configure ssh(ync)', 'change device id',
'[OPTIONAL, RECOMMENDED] set custom text editor',
'[OPTIONAL] enable/disable quick-unlock',
'[OPTIONAL] re-encrypt/optimize entries',
'[OPTIONAL] extension management'])
else:
_options.extend(['remove registered device ids', 'manage quick-unlock/whitelist'])
_options.append('EXIT/DONE')
_choice += curses_radio(_options, _top_message)
if _choice == 0:
_dev_sync_types = install_type()
# if switching to client mode...
if _dev_sync_types[0] == 'client':
# ...and gpg settings are missing
if not sshyp_data.has_option('CLIENT-GENERAL', 'gpg_id'):
gpg_config()
# ...and text editor settings are missing
if not sshyp_data.has_option('CLIENT-GENERAL', 'text_editor'):
editor_config(True)
# ...and online (synchronized) mode is enabled...
if _dev_sync_types[1] == 'false':
# ...and quick-unlock settings are missing
if not sshyp_data.has_option('CLIENT-ONLINE', 'quick_unlock_enabled'):
quick_unlock_config(True)
# set to None to check if modified later
_ip, _username_ssh, _port, _identity = None, None, None, None
# ...and there is no sshync config present
if not sshyp_data.has_section('SSHYNC'):
_ip, _username_ssh, _port, _identity = ssh_config()
# ...and there is no device ID present
if not listdir(f"{home}/.config/sshyp/devices"):
if None in (_ip, _username_ssh, _port):
_ip, _username_ssh, _port, _identity = ssh_config()
dev_id_config(_port, _username_ssh, _ip, _identity)
# ...or ssh_error is missing
elif not sshyp_data.has_option('CLIENT-ONLINE', 'ssh_error'):
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
write_config()
_device_type = _dev_sync_types[0]
elif _choice == 1:
if _device_type == 'client':
gpg_config()
else:
registered_dev_id_remover()
elif _choice == 2:
if _device_type == 'client':
ssh_config(True)
else:
whitelist_menu()
elif _choice == 3:
if _device_type == 'client':
if not sshyp_data.has_section('SSHYNC'):
ssh_config()
dev_id_config(sshyp_data.get('SSHYNC', 'port'), sshyp_data.get('SSHYNC', 'user'),
sshyp_data.get('SSHYNC', 'ip'), sshyp_data.get('SSHYNC', 'identity_file'), True)
else:
_exit_signal = True
elif _choice == 4:
editor_config(False)
elif _choice == 5:
_enabled = quick_unlock_config(False)
if _enabled == 'true':
curses_radio(['okay'], 'quick-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'
'\nfrom there you can create a quick-unlock pin and add this device to the '
'whitelist')
elif _choice == 6:
_success = refresh_encryption()
if _success == 1:
curses_radio(['okay'], "a backup of your previous entry directory has been created:\n\n"
f"{home}/.local/share/sshyp.old")
elif _success == 2:
curses_radio(['okay'], '\u001b[38;5;9merror: re-encryption failed: entry directory not found\u001b[0m')
elif _choice == 7:
# PORT START TWEAK-EXTEND-OPTION
_ext_name, _escalator, _action = extension_menu()
# if root is needed for extension management...
if _ext_name:
return _ext_name, _escalator, _action
# PORT END TWEAK-EXTEND-OPTION
else:
_exit_signal = True
if _exit_signal:
break
return None, None, None
# runs initial configuration wizard
def initial_setup(_scr):
global stdscr
stdscr = _scr
# required directory creation
Path(f"{home}/.config/sshyp/devices").mkdir(mode=0o700, parents=True, exist_ok=True)
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)
# removal of old config files
if _exists_flag:
sshyp_data.clear()
# curses menu tree
# device+sync type selection
_dev_sync_types = install_type()
if _dev_sync_types[0] == 'client':
# gpg configuration
gpg_config()
# text editor configuration (automated)
editor_config(True)
# quick-unlock configuration (disabled by default)
quick_unlock_config(True)
# online (synchronized mode) configuration
if _dev_sync_types[1] != 'true':
# ssh+sshync configuration
_ip, _username_ssh, _port, _identity = ssh_config()
# device id configuration
dev_id_config(_port, _username_ssh, _ip, _identity)
# PORT START CLIPTOOL
# check for clipboard tool and display warning if missing
if uname()[0] in ('Linux', 'FreeBSD') and 'WSL_DISTRO_NAME' not in environ \
and not exists("/data/data/com.termux"):
_display_server, _clipboard_tool, _clipboard_package = None, None, None
if 'WAYLAND_DISPLAY' in environ:
_display_server, _clipboard_tool, _clipboard_package = 'Wayland', 'wl-copy', 'wl-clipboard'
elif 'DISPLAY' in environ:
_display_server, _clipboard_tool, _clipboard_package = 'X11', 'xclip', 'xclip'
if _display_server is not None and which(_clipboard_tool) is None:
curses_radio(['okay'], f'WARNING: you are using {_display_server} and "{_clipboard_tool}" is not '
'present - \ncopying entry fields will not function until '
f'"{_clipboard_package}" is installed')
# PORT END CLIPTOOL
# run optional configuration menu
curses_radio(['okay'], 'required configuration complete\n\na menu for additional (optional) configuration will be '
'displayed\n\nthis menu can be safely exited at any time')
# set gm_device_type so that after the init menu is terminated the global menu knows the device type
global gm_device_type
gm_device_type = _dev_sync_types[0]
return
# runs the specified entry function (menu start point) within a curses wrapper
def wrapped_entry(_gm_device_type, _gm_top_message='configuration options:'):
from curses import wrapper, use_default_colors
global gm_device_type
gm_device_type = _gm_device_type
# a boolean value represents init
if isinstance(gm_device_type, bool):
wrapper(lambda _wrap_stdscr: (use_default_colors(), initial_setup(_wrap_stdscr)))
# any other value will be interpreted as the global menu device type
# this code still runs when called for init once the init menu terminates
_repeat = True
while _repeat:
try:
_ext_name, _escalator, _action = \
wrapper(lambda _wrap_stdscr: (use_default_colors(),
global_menu(_wrap_stdscr, gm_device_type, _gm_top_message)))[1]
except ChildProcessError:
print("\n\u001b[38;5;9merror: privilege escalation required\n\nneither 'doas' nor 'sudo' were found in "
"the system's $PATH\u001b[0m\n")
return
# only run if privilege escalation is needed
if _action is not None:
if _action:
# install with privilege escalation (outside of curses)
from tempfile import gettempdir
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
# PORT START TWEAK-EXT-CHOWN
if uname()[0] == 'FreeBSD':
run((_escalator, 'chown', 'root:wheel', _exe_dir, _ini_dir))
else:
run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir))
# PORT END TWEAK-EXT-CHOWN
run((_escalator, 'mv', _exe_dir, f"/usr/lib/sshyp/{_ext_name}"))
run((_escalator, 'mv', _ini_dir, f"/usr/lib/sshyp/extensions/{_ext_name}.ini"))
else:
# uninstall with privilege escalation (outside of curses)
run((_escalator, 'rm', f"/usr/lib/sshyp/{_ext_name}",
f"/usr/lib/sshyp/extensions/{_ext_name}.ini"))
else:
_repeat = False
+84 -104
View File
@@ -1,14 +1,12 @@
#!/bin/sh
version=$(head -n1 extra/changelog-archive/2024 | cut -c8-)
version=$(sed -n '1{p;q}' share/doc/sshyp/changelog | cut -c8-)
if [ -z "$2" ]; then
revision=1
else
revision="$2"
fi
mkdir -p port-jobs/working
_create_generic_linux() {
printf '\npackaging as generic (Linux)...\n'
mkdir -p output/linuxtemp/usr/bin \
@@ -17,11 +15,9 @@ _create_generic_linux() {
output/linuxtemp/usr/share/bash-completion/completions \
output/linuxtemp/usr/share/zsh/functions/Completion/Unix
# START PORT
cp lib/* port-jobs/working/
cp -r lib/. port-jobs/working/
cd port-jobs
./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX
./CHOWN.py
./UNAME.py LINUX
./COMMENTS.py ALL
./BLANKS.py
@@ -37,14 +33,14 @@ _create_generic_linux() {
gzip output/linuxtemp/usr/share/man/man1/sshyp.1
XZ_OPT=-e6 tar -C output/linuxtemp -cvJf output/GENERIC-LINUX-sshyp-"$version".tar.xz usr/
rm -rf output/linuxtemp
sha512="$(sha512sum output/GENERIC-LINUX-sshyp-"$version".tar.xz | cut -d' ' -f1)"
sha512="$(sha512sum output/GENERIC-LINUX-sshyp-"$version".tar.xz | awk '{print $1;}')"
printf '\ngeneric (Linux) packaging complete\n\n'
} &&
_create_pkgbuild() {
printf '\ngenerating PKGBUILD...\n'
if [ "$1" = 'Deb' ]; then
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/DEBIAN-sshyp_"$pkgver"-"$pkgrel"_all.deb'
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/UBUNTU-sshyp_"$pkgver"-"$pkgrel"_all.deb'
local decomp_target='data.tar.xz'
else
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/GENERIC-LINUX-sshyp-"$pkgver".tar.xz'
@@ -58,12 +54,8 @@ pkgdesc='A light-weight, self-hosted, synchronized password manager'
url='https://github.com/rwinkhart/sshyp'
arch=('any')
license=('GPL-3.0-only')
depends=(python gnupg openssh)
optdepends=(
'wl-clipboard: wayland clipboard support'
'xclip: x11 clipboard support'
'bash-completion: bash completion support'
)
depends=(python gnupg openssh xclip wl-clipboard)
optdepends=('bash-completion: bash completion support')
source=(\""$source"\")
sha512sums=('"$sha512"')
@@ -77,8 +69,8 @@ package() {
_create_apkbuild() {
printf '\ngenerating APKBUILD...\n'
if [ "$1" = 'Deb' ]; then
local source="https://github.com/rwinkhart/sshyp/releases/download/v\"\$pkgver\"/DEBIAN-sshyp_\"\$pkgver\"-"$revision"_all.deb"
local sumsname="DEBIAN-sshyp_\"\$pkgver\"-"$revision"_all.deb"
local source="https://github.com/rwinkhart/sshyp/releases/download/v\"\$pkgver\"/UBUNTU-sshyp_\"\$pkgver\"-"$revision"_all.deb"
local sumsname="UBUNTU-sshyp_\"\$pkgver\"-"$revision"_all.deb"
local processing='mkdir -p "$pkgdir"
7z x "$srcdir"/* -o"$srcdir"
tar -xf "$srcdir"/data.tar -C "$pkgdir"
@@ -103,7 +95,7 @@ options=!check
url='https://github.com/rwinkhart/sshyp'
arch='noarch'
license='GPL-3.0-only'
depends='python3 gnupg openssh'
depends='python3 gnupg openssh xclip wl-clipboard'
source=\""$source"\"
package() {
@@ -136,7 +128,7 @@ licenses {
\"GNU GPL v3\"
}
copyrights {
\"2021-2024 Randall Winkhart\"
\"2021-2023 Randall Winkhart\"
}
provides {
sshyp_client = "$version"
@@ -145,39 +137,37 @@ provides {
requires {
gnupg
openssh
python3.11
python310
}
urls {
\"https://github.com/rwinkhart/sshyp\"
}
" > output/haikutemp/.PackageInfo
# START PORT
cp lib/* port-jobs/working/
cp -r lib/. port-jobs/working/
cd port-jobs
./SHEBANG.sh
./RMSERVER.py
./RMEXTMAN.py
./CLIPTOOL.py
./CLIPBOARD.py HAIKU
./CHOWN.py
./UNAME.py TMP
./COMMENTS.py ALL
./UNAME.py TMP
./BLANKS.py
./TABS.sh TABS
cd ..
mv port-jobs/working/* output/haikutemp/lib/sshyp/
# END PORT
ln -s /system/lib/sshyp/sshyp.py output/haikutemp/bin/sshyp
cp -r share/doc/sshyp/. output/haikutemp/documentation/packages/sshyp/
cp -r share/licenses/sshyp/. output/haikutemp/documentation/packages/sshyp/
cp extra/completion.bash output/haikutemp/data/bash-completion/completions/sshyp
cp extra/completion.zsh output/haikutemp/data/zsh/site-functions/_sshyp
cp extra/manpage output/haikutemp/documentation/man/man1/sshyp.1
gzip output/haikutemp/documentation/man/man1/sshyp.1
cd output/haikutemp
package create -b HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg
package add HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg bin lib documentation data
package create -b HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg
package add HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg bin lib documentation data
cd ../..
mv output/haikutemp/HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg output/
mv output/haikutemp/HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg output/
rm -rf output/haikutemp
printf '\nHaiku packaging complete\n\n'
} &&
@@ -190,19 +180,34 @@ _create_deb() {
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1 \
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/bash-completion/completions \
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/zsh/functions/Completion/Unix
printf "Package: sshyp
Version: $version
Section: utils
Architecture: all
Maintainer: Randall Winkhart <idgr at tutanota dot com>
Description: A light-weight, self-hosted, synchronized password manager
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
if [ "$1" = 'Debian' ]; then
printf "Depends: python3, gnupg, openssh-client, xclip, wl-clipboard
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
else
printf "Depends: python3, gnupg, openssh-client
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
fi
printf "Suggests: bash-completion
Priority: optional
Installed-Size: 71680
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
# START PORT
cp lib/* port-jobs/working/
cp -r lib/. port-jobs/working/
cd port-jobs
if [ "$1" = 'Debian' ]; then
./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX
special=DEBIAN
special=UBUNTU
else
./CLIPTOOL.py
./CLIPBOARD.py WSL
special=WSL-ONLY-DEBIAN
special=WSL-ONLY-UBUNTU
fi
./CHOWN.py
./UNAME.py LINUX
./COMMENTS.py ALL
./BLANKS.py
@@ -216,62 +221,22 @@ _create_deb() {
cp extra/completion.zsh output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/zsh/functions/Completion/Unix/_sshyp
cp extra/manpage output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
gzip output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
size=$(du --block-size=1024 -s output/debiantemp/sshyp_"$version"-"$revision"_all | cut -f1)
printf "Package: sshyp
Version: $version
Section: utils
Architecture: all
Maintainer: Randall Winkhart <idgr at tutanota dot com>
Description: A light-weight, self-hosted, synchronized password manager
Priority: optional
Installed-Size: $size
Depends: python3, gnupg, openssh-client
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
if [ "$1" = 'Debian' ]; then
printf "Suggests: wl-clipboard, xclip, bash-completion, openssh-server
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
else
printf "Suggests: bash-completion, openssh-server
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
fi
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp_"$version"-"$revision"_all/
mv output/debiantemp/sshyp_"$version"-"$revision"_all.deb output/"$special"-sshyp_"$version"-"$revision"_all.deb
rm -rf output/debiantemp
sha512="$(sha512sum output/"$special"-sshyp_"$version"-"$revision"_all.deb | cut -d' ' -f1)"
sha512="$(sha512sum output/"$special"-sshyp_"$version"-"$revision"_all.deb | awk '{print $1;}')"
printf "\n$1 packaging complete\n\n"
} &&
_create_termux() {
printf '\npackaging for Termux...\n'
mkdir -p output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN \
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions \
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin \
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1 \
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions \
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions
# START PORT
cp lib/* port-jobs/working/
cd port-jobs
./RMSERVER.py
./RMEXTMAN.py
./CLIPTOOL.py
./CLIPBOARD.py TERMUX
./CHOWN.py
./UNAME.py TERMUX
./COMMENTS.py ALL
./BLANKS.py
./TABS.sh TABS
cd ..
mv port-jobs/working/* output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/
# END PORT
ln -s /data/data/com.termux/files/usr/lib/sshyp/sshyp.py output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp
cp -r share output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
cp extra/completion.bash output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions/sshyp
cp extra/completion.zsh output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions/_sshyp
cp extra/manpage output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
gzip output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
size=$(du --block-size=1024 -s output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux | cut -f1)
printf "Package: sshyp-client
mkdir -p output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN \
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions \
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin \
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1 \
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions \
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions
printf "Package: sshyp
Version: $version
Section: utils
Architecture: all
@@ -280,10 +245,27 @@ Description: A light-weight, self-hosted, synchronized password manager
Depends: python, gnupg, openssh, termux-api, termux-am
Suggests: bash-completion
Priority: optional
Installed-Size: $size
" > output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN/control
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/
mv output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux.deb output/
Installed-Size: 71680
" > output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN/control
# START PORT
cp -r lib/. port-jobs/working/
cd port-jobs
./CLIPBOARD.py TERMUX
./UNAME.py TERMUX
./COMMENTS.py ALL
./BLANKS.py
./TABS.sh TABS
cd ..
mv port-jobs/working/* output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/
# END PORT
ln -s /data/data/com.termux/files/usr/lib/sshyp/sshyp.py output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp
cp -r share output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
cp extra/completion.bash output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions/sshyp
cp extra/completion.zsh output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions/_sshyp
cp extra/manpage output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
gzip output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/
mv output/termuxtemp/sshyp_"$version"-"$revision"_all_termux.deb output/TERMUX-sshyp_"$version"-"$revision"_all_termux.deb
rm -rf output/termuxtemp
printf '\nTermux packaging complete\n\n'
} &&
@@ -305,8 +287,8 @@ BuildArch: noarch
License: GPL-3.0-only
URL: https://github.com/rwinkhart/sshyp
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
Requires: python gnupg openssh-clients
Recommends: wl-clipboard xclip bash-completion openssh-server
Requires: python gnupg openssh-clients wl-clipboard
Recommends: bash-completion
%%description
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
%%install
@@ -316,21 +298,17 @@ cp -r %%{_sourcedir}/usr %%{buildroot}
/usr/bin/sshyp
/usr/lib/sshyp/sshyp.py
/usr/lib/sshyp/sshync.py
/usr/lib/sshyp/stweak.py
/usr/lib/sshyp/clipclear.py
/usr/lib/sshyp/extensions/
/usr/share/bash-completion/completions/sshyp
/usr/share/zsh/site-functions/_sshyp
%%license /usr/share/licenses/sshyp/license
%%doc
/usr/share/doc/sshyp/changelog
/usr/share/man/man1/sshyp.1.gz
" > ~/rpmbuild/SPECS/sshyp.spec
# START PORT
cp lib/* port-jobs/working/
cp -r lib/. port-jobs/working/
cd port-jobs
./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX
./CHOWN.py
./UNAME.py LINUX
./COMMENTS.py ALL
./BLANKS.py
@@ -371,31 +349,33 @@ maintainer: <idgr at tutanota dot com>
www: https://github.com/rwinkhart/sshyp
prefix: /
\"deps\" : {
\"python3\" : {
\"origin\" : \"lang/python3\"
\"python\" : {
\"origin\" : \"lang/python\"
},
\"gnupg\" : {
\"origin\" : \"security/gnupg\"
},
\"xclip\" : {
\"origin\" : \"x11/xclip\"
},
\"wl-clipboard\" : {
\"origin\" : \"x11/wl-clipboard\"
},
},
" > output/freebsdtemp/+MANIFEST
printf "/usr/bin/sshyp
/usr/lib/sshyp/sshyp.py
/usr/lib/sshyp/sshync.py
/usr/lib/sshyp/stweak.py
/usr/lib/sshyp/clipclear.py
/usr/lib/sshyp/sshyp.py
/usr/local/share/bash-completion/completions/sshyp
/usr/local/share/zsh/site-functions/_sshyp
/usr/share/doc/sshyp/changelog
/usr/share/licenses/sshyp/license
/usr/share/man/man1/sshyp.1.gz
@dir /usr/lib/sshyp/extensions/
" > output/freebsdtemp/plist
# START PORT
cp lib/* port-jobs/working/
cp -r lib/. port-jobs/working/
cd port-jobs
./CLIPTOOL.py LINUX
./CLIPBOARD.py BSD
./CHOWN.py BSD
./CLIPBOARD.py TERMUX
./UNAME.py TMP
./COMMENTS.py ALL
./BLANKS.py
@@ -480,6 +460,6 @@ case "$1" in
esac
;;
*)
printf '\nusage: package.sh [target] <revision>\n\ntargets:\n full support: pkgbuild pkgbuild-deb apkbuild apkbuild-deb fedora debian wsl freebsd\n client-only: haiku termux\n groups: buildable-arch buildable-arch-deb\n\n'
printf '\nusage: package.sh [target] <revision>\n\ntargets:\n mainline: pkgbuild pkgbuild-deb apkbuild apkbuild-deb fedora debian wsl haiku freebsd\n experimental: termux\n groups: buildable-arch buildable-arch-deb\n\n'
;;
esac
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/env python3
import re
from sys import argv
# read arguments
arguments = argv[1:]
# define PORT target
string1 = '# PORT START TWEAK-EXT-CHOWN'
string2 = '# PORT END TWEAK-EXT-CHOWN'
# define replacement text depending on arguments
if len(arguments) > 0 and arguments[0] == 'BSD':
replacement = "run((_escalator, 'chown', 'root:wheel', _exe_dir, _ini_dir))"
else:
replacement = "run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir))"
# read input file
text = open('working/stweak.py', 'r').read()
# compile regex and modify text
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
# find and replace the defined PORT target
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/stweak.py', 'w').write(new_text)
+33 -45
View File
@@ -3,60 +3,48 @@ import re
from sys import argv, exit as s_exit
# read arguments
arguments, clip_replacement, clear_replacement = argv[1:], None, None
arguments = argv[1:]
# define replacement text depending on arguments
if len(arguments) > 0:
if arguments[0] == 'WSL':
clip_replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL, stderr=DEVNULL)"""
clear_replacement = """hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('powershell.exe', '-c', 'Set-Clipboard'))"""
replacement = """run(['powershell.exe', '-c', "Set-Clipboard '" + _copy_line[_index].rstrip().replace("'", "''") + "'"])
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DENVULL)"""
elif arguments[0] == 'MAC':
clip_replacement = """run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))"""
clear_replacement = """hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run('pbcopy', input=b'')"""
replacement = """run(['pbcopy'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\\\\\', '\\\\\\\\\\\\\\').replace('%', '%%')],
stdout=PIPE).stdout)
Popen("sleep 30; printf '' | pbcopy", shell=True)"""
elif arguments[0] == 'HAIKU':
clip_replacement = """run(('clipboard', '-c', _copy_subject))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))"""
clear_replacement = """hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('clipboard', '-r'))"""
replacement = """run(['clipboard', '-c', _copy_line[_index].rstrip()])
Popen('sleep 30; clipboard -r', shell=True)"""
elif arguments[0] == 'TERMUX':
clip_replacement = """run(('termux-clipboard-set', _copy_subject))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))"""
clear_replacement = """hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(("termux-clipboard-set", "''"))"""
elif arguments[0] in ('LINUX', 'BSD'):
clip_replacement = """if 'WAYLAND_DISPLAY' in environ:
run('wl-copy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wayland'))
replacement = """run(['termux-clipboard-set', _copy_line[_index].rstrip()])
Popen("sleep 30; termux-clipboard-set ''", shell=True)"""
elif arguments[0] == 'BSD':
replacement = """run(['xclip', '-sel', 'c'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\', '\\\\')
.replace('%', '%%')], stdout=PIPE).stdout)
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
elif arguments[0] == 'LINUX':
replacement = """if 'WAYLAND_DISPLAY' in environ:
run(['wl-copy', _copy_line[_index].rstrip()])
Popen('sleep 30; wl-copy -c', shell=True)
else:
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))"""
clear_replacement = """if argv[2] == 'wayland':
hash_paste.update(run('wl-paste', stdout=PIPE, stderr=DEVNULL).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('wl-copy', '-c'))
else:
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('xclip', '-i', '/dev/null', '-sel', 'c'))"""
run(['xclip', '-sel', 'c'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\\\\\', '\\\\\\\\\\\\\\')
.replace('%', '%%')], stdout=PIPE).stdout)
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
else:
s_exit()
targets = (('CLIPBOARD', 'sshyp.py', '', '', clip_replacement),
('CLIPCLEAR', 'clipclear.py', '\n', '', clear_replacement))
# define PORT target
string1 = '# PORT START CLIPBOARD'
string2 = '# PORT END CLIPBOARD'
for target in targets:
# read text from target file
text = open(f"working/{target[1]}", 'r').read()
# compile regex and modify text
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
new_text = re.sub(regex, target[4], text)
# write updated text to target file
open(f"working/{target[1]}", 'w').write(new_text)
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/env python3
import re
from sys import argv
# read arguments
arguments = argv[1:]
# define PORT target
string1 = '# PORT START CLIPTOOL'
string2 = '# PORT END CLIPTOOL'
# define replacement text depending on arguments
if len(arguments) > 0:
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
replacement = """# check for clipboard tool and display warning if missing
_display_server, _clipboard_tool, _clipboard_package = None, None, None
if 'WAYLAND_DISPLAY' in environ:
_display_server, _clipboard_tool, _clipboard_package = 'Wayland', 'wl-copy', 'wl-clipboard'
elif 'DISPLAY' in environ:
_display_server, _clipboard_tool, _clipboard_package = 'X11', 'xclip', 'xclip'
if _display_server is not None and which(_clipboard_tool) is None:
curses_radio(['okay'], f'''WARNING: you are using {_display_server} and "{_clipboard_tool}" is not present -
copying entry fields will not function until "{_clipboard_package}" is installed'''
)"""
else:
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
replacement = ''
# read input file
text = open('working/stweak.py', 'r').read()
# find and replace the defined PORT target
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/stweak.py', 'w').write(new_text)
-17
View File
@@ -1,17 +0,0 @@
#!/usr/bin/env python3
import re
targets = (('TWEAK-EXTEND-FUNCTIONS', 'stweak.py', '\n', '\n\n', ''),
('TWEAK-EXTEND-OPTION', 'stweak.py', '', '', "curses_radio(['okay'], "
"'extension management is not supported on this platform\\\\n\\\\ninstead, you may install and manage "
"extensions through your system package manager\\\\n\\\\nofficial extension packages are available "
"at https://github.com/rwinkhart/sshyp-labs/releases')"))
for target in targets:
# read text from target file
text = open(f"working/{target[1]}", 'r').read()
# compile regex and modify text
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
new_text = re.sub(regex, target[4], text)
# write updated text to target file
open(f"working/{target[1]}", 'w').write(new_text)
+77 -13
View File
@@ -1,18 +1,82 @@
#!/usr/bin/env python3
import re
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
'device + sync type configuration')"""
# SSHYNC-REMOTE
# define PORT target
string1 = '# PORT START SSHYNC-REMOTE'
string2 = '# PORT END SSHYNC-REMOTE'
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', ''))
# read input file
text = open('working/sshync.py', 'r').read()
for target in targets:
# read text from target file
text = open(f"working/{target[1]}", 'r').read()
# compile regex and modify text
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
new_text = re.sub(regex, target[4], text)
# write updated text to target file
open(f"working/{target[1]}", 'w').write(new_text)
# find and replace the defined PORT target
regex = re.compile(f"\n{string1}.*?{string2}\n\n", re.DOTALL)
new_text = re.sub(regex, '', text)
# write updated text
open('working/sshync.py', 'w').write(new_text)
# WHITELIST-SERVER
# define PORT target
string1 = '# PORT START WHITELIST-SERVER'
string2 = '# PORT END WHITELIST-SERVER'
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"\n{string1}.*?{string2}\n\n", re.DOTALL)
new_text = re.sub(regex, '', text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
# TWEAK-DEVTYPE
# define PORT target
string1 = '# PORT START TWEAK-DEVTYPE'
string2 = '# PORT END TWEAK-DEVTYPE'
# set replacement text
replacement = """if True:
_sshyp_data = ['client']
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)"""
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
# ARGS-SERVER
# define PORT target
string1 = '# PORT START ARGS-SERVER'
string2 = '# PORT END ARGS-SERVER'
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}\n\n ", re.DOTALL)
new_text = re.sub(regex, '', text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
# HELP-SERVER
# define PORT target
string1 = '# PORT START HELP-SERVER'
string2 = '# PORT END HELP-SERVER'
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
new_text = re.sub(regex, '', text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
+3 -4
View File
@@ -1,6 +1,5 @@
#!/bin/sh
if [ "$1" = 'PREP' ]; then
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./*.py
else
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./working/sshyp.py ./working/clipclear.py
if [ "$1" = 'HAIKU' ]; then
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshync.py
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshyp.py
fi
+1 -3
View File
@@ -2,11 +2,9 @@
if [ "$1" = 'TABS' ]; then
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/sshyp.py > working/sshyp.py.new
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/sshync.py > working/sshync.py.new
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/stweak.py > working/stweak.py.new
mv working/sshyp.py.new working/sshyp.py
mv working/sshync.py.new working/sshync.py
mv working/stweak.py.new working/stweak.py
chmod +x working/sshyp.py working/sshync.py working/stweak.py
chmod +x working/sshyp.py working/sshync.py
elif [ "$(grep -qP '\t' "$1" && echo TABS)" = 'TABS' ]; then
printf "$1 contains tab characters...\n"
read -rp 'replace with (4) spaces? (Y/n) ' replace
+43 -9
View File
@@ -1,13 +1,47 @@
#!/usr/bin/env python3
import re
from sys import argv, exit as s_exit
targets = (('UNAME-IMPORT-SSHYP', 'sshyp.py', ''), ('UNAME-IMPORT-STWEAK', 'stweak.py', ''))
# read arguments
arguments = argv[1:]
for target in targets:
# read text from target file
text, special = open(f"working/{target[1]}", 'r').read(), ''
# compile regex and modify text
regex = re.compile(f"# PORT START {target[0]}.*?# PORT END {target[0]}\n", re.DOTALL)
new_text = re.sub(regex, target[2], text)
# write updated text to target file
open(f"working/{target[1]}", 'w').write(new_text)
# define replacement text depending on arguments
if len(arguments) > 0:
if arguments[0] == 'TMP':
replacement = 'symlink("/tmp", f"{home}/.config/sshyp/tmp")'
elif arguments[0] == 'TERMUX':
replacement = 'symlink("/data/data/com.termux/files/usr/tmp", f"{home}/.config/sshyp/tmp")'
elif arguments[0] == 'LINUX':
replacement = 'symlink("/dev/shm", f"{home}/.config/sshyp/tmp")'
else:
s_exit()
# UNAME-IMPORT
# define PORT target
string1 = '# PORT START UNAME-IMPORT'
string2 = '# PORT END UNAME-IMPORT'
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
new_text = re.sub(regex, '', text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
# UNAME-TMP
# define PORT target
string1 = '# PORT START UNAME-TMP'
string2 = '# PORT END UNAME-TMP'
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
+27
View File
@@ -0,0 +1,27 @@
sshyp v1.4.3
06/08/2023
the argumentative agronomist update - patch three
IMPORTANT: the next major release of sshyp, v1.5.0, will make breaking changes that will require
running through 'sshyp tweak' again - also starting with v1.5.0, a copy of the changelog will no
longer be packaged with sshyp (it will still be available on GitHub) and clipboard tools
(x-clip and wl-clipboard) will be set to optional dependencies ('sshyp tweak' will warn of this)
this is a highly unconventional release featuring backports to fix critical bugs found during the
development of the upcoming v1.5.0
fixes:
- folder renaming has been disabled to prevent a severe data loss bug from occuring
^ this has been properly fixed for v1.5.0, but the fix requires all clients and servers
to be updated - to avoid breaking compatibility with 1.4.X, folder renaming was simply disabled
for the time being
- backported fix for syncing over IPv6
- backported fix for multiple versions of Powershell being called under WSL
- backported fix for checking /lib/sshyp instead of /usr/lib/sshyp
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
The full history of changes to "sshyp" can be found on the following page:
https://github.com/rwinkhart/sshyp/tree/main/extra/changelog-archive/
+1 -1
View File
@@ -1,5 +1,5 @@
sshyp is a FOSS password manager that uses an sftp-based syncing back-end.
Copyright (C) 2021-2024 Randall Winkhart idgr@tutanota.com
Copyright (C) 2021-2023 Randall Winkhart idgr@tutanota.com
This program is free software: you can redistribute it and/or modify
it under the terms of version 3 (only) of the GNU General Public License
-4
View File
@@ -1,4 +0,0 @@
## Known Bugs
No bugs are currently documented within sshyp, but this is due to a lack of current testing (bugs most certainly exist).
Development and testing efforts have been re-focused on [MUTN](https://github.com/rwinkhart/MUTN) and [libmutton](https://github.com/rwinkhart/libmutton).
-13
View File
@@ -1,13 +0,0 @@
## Clipboard Troubleshooting
### Clipboard managers can break sshyp
***
The intended way for sshyp to interact with the system clipboard is for it to clear it 30 seconds after copying a field. **Unfortunately, this does not work by default on all systems due to the prevalence of clipboard managers.**
Clipboard managers save a history of what has been copied to the clipboard, which is already a big enough issue on its own for people who copy sensitive information to their clipboard. Some **clipboard managers simply will not allow the clipboard to be empty** and will replace its contents with the last copied item if you attempt to clear it. One such naughty clipboard manager is **KDE Klipper**, which comes **packaged into KDE Plasma** and is typically **enabled by default** on most distributions. Due to this behavior, **KDE Klipper breaks sshyp's clipboard clearing functionality** and should not be left enabled.
It is likely other popular clipboard managers exhibit this behavior. I noticed it with KDE Klipper, which is what prompted me to create this wiki page. **Clipboard managers should not be enabled by default in any environment** or distribution due to their **potential security implications**.
### Termux cannot clear the clipboard from the background
***
If using the Termux (Android) version of sshyp, the clipboard may not successfully be cleared after the 30 second timeout period if Termux is not actively in the foreground when the sleep timer expires. This is an unfortunate side-effect of running on Android and cannot be easily fixed. Due to Termux being at the bottom of the platform support priority list, I will not be investing time into working around this.
-26
View File
@@ -1,26 +0,0 @@
## Shell Completions Troubleshooting
ZSH completions not working? Make sure your ~/.zshrc contains the following:
```
autoload -Uz compinit && compinit
```
...and then restart your shell.
***
Bash completions not working? Install your distribution's 'bash-completion' package or source the completion script manually.
For most environments, this would mean adding the following to your ~/.bashrc:
```
source /usr/share/bash-completion/completions/sshyp
```
Note that this directory is different on FreeBSD and Haiku.
FreeBSD:
```
source /usr/local/share/bash-completion/completions/sshyp
```
Haiku:
```
source /system/data/bash-completion/completions/sshyp
```
...and then restart your shell.
*Please note that Bash completions are slightly more limited than ZSH completions, and as such, new entries will not be auto-completed until the completions script is re-sourced.*
-15
View File
@@ -1,15 +0,0 @@
**Note:** [Shell completions may require additional configuration to work](https://github.com/rwinkhart/sshyp/blob/main/wiki/completions.md).
[Haiku](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/haiku.md)
[FreeBSD](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/freebsd.md)
[Alpine Linux](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/alpine.md)
[Arch Linux](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/arch.md)
[Debian/Ubuntu Linux (or WSL)](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/debian.md)
[Fedora Linux](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/fedora.md)
[Termux (Android)](https://github.com/rwinkhart/sshyp/blob/main/wiki/installationGuides/termux.md)
-11
View File
@@ -1,11 +0,0 @@
## Installation (Alpine Linux)
The Alpine Linux package is actively tested on the latest stable release of Alpine Linux.
***
1. Download the *.apk file from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
2. Run `doas apk add --allow-untrusted <path/to/*.apk`
3. sshyp is now installed - run `sshyp init` to get started!
***
Creating a package from an APKBUILD? Just use `abuild -r` in the same directory as the APKBUILD.
-10
View File
@@ -1,10 +0,0 @@
## Installation (Arch Linux)
The Arch package is actively tested on up-to-date installations of Arch and Artix Linux.
***
The easiest method of installation is via the AUR. Search for "sshyp" using your favorite AUR helper or run:
```
git clone https://aur.archlinux.org/sshyp.git
cd sshyp
makepkg -si
```
sshyp is now installed - run `sshyp init` to get started!
-12
View File
@@ -1,12 +0,0 @@
## Installation (Debian/Ubuntu)
The Debian/Ubuntu package is actively tested on Debian 12 (standard) and Ubuntu 22.04 (WSL).
_OpenSSH warning:_ Sync support requires OpenSSH >= 8.7, meaning **Debian 12 (Bookworm)** is the minimum supported version.
***
1. Download the *.deb file from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
^ do not download the version ending in *_termux.deb by mistake!
2. Run `sudo dpkg -i <path/to/*.deb>; sudo apt install -f`
3. sshyp is now installed - run `sshyp init` to get started!
-10
View File
@@ -1,10 +0,0 @@
## Installation (Fedora)
The Fedora package is actively tested on the latest version of Fedora Linux.
This package may be install-able on other Red Hat-based distributions, but it is only tested with the latest version of Fedora.
***
1. Download the *.rpm file from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
2. Run `sudo dnf localinstall <path/to/*.rpm>`
3. sshyp is now installed - run `sshyp init` to get started!
-10
View File
@@ -1,10 +0,0 @@
## Installation (FreeBSD)
The FreeBSD package is actively tested on the latest version of FreeBSD 14 and should be fully functional on at least FreeBSD 13+.
***
1. Download the *.pkg file from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
2. Run `sudo pkg add <path/to/*.pkg>`
^ the install may fail if you are missing any dependencies - please install the dependencies reported by the package manager and try again
3. sshyp is now installed - run `sshyp init` to get started!
-14
View File
@@ -1,14 +0,0 @@
## Installation (Haiku)
**Missing features:** For security and technical reasons, the Haiku version of sshyp cannot be used as a server (it only functions as a client). Additionally, the Haiku package does not support the extension management system, so extensions still must be installed as separate packages.
Please note that sshyp for Haiku is targeting release R1 Beta 4 (64-bit). Previous releases have significant show-stopping bugs, while stability on the nightly releases cannot be guaranteed.
***
1. Download the *.hpkg file from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
2. Open the downloaded file and click "Install sshyp" in the top right of the window
**or**
Run `pkgman install <path/to/*.hpkg>`
3. sshyp is now installed - run `sshyp init` to get started!
-16
View File
@@ -1,16 +0,0 @@
## Installation (Termux)
**Missing features:** For technical reasons, the Termux version of sshyp cannot be used as a server (it only functions as a client). Additionally, the Termux package does not support the extension management system, so extensions still must be installed as separate packages.
The Termux package is minimally tested. Attempts to maintain compatibility are made and it should be fully functional on the latest versions of Termux+Termux:API (F-Droid versions), but newer features are more likely to be broken than on other platforms.
***
1. Install the Termux application from [F-Droid](https://f-droid.org/en/packages/com.termux/)
2. Install the Termux API from [F-Droid](https://f-droid.org/en/packages/com.termux.api/) (for clipboard support)
3. From within Termux, run `curl -L <github/link/to/latest/*_termux.deb> -o sshyp.deb`
^ copy the required download link from the [latest tagged release of sshyp](https://github.com/rwinkhart/sshyp/releases)
4. From within Termux, run `dpkg -i sshyp.deb; pkg install -f`
5. sshyp is now installed - run `sshyp init` to get started!
-27
View File
@@ -1,27 +0,0 @@
## Postmortem
### Reflecting on the Flaws of sshyp
Due to being my introduction to the world of programming, sshyp contains many flaws that have been addressed with the creation of [MUTN](https://github.com/rwinkhart/MUTN).
Many of these flaws are the result of a lack of direction for the project.
In the beginning, sshyp was actually called "rpass" and was meant to be a simple wrapper for pass/password-store with rsync integration for synchronization.
I quickly ran into issues using rsync and pivoted to using sftp, thus necessitating the rebrand to "sshyp".
sshyp grew to become its own standalone password manager with no relation to pass/password-store (except for entry import compatibility).
In terms of technical flaws resulting from a lack of experience, here is a non-exhaustive list:
- Port jobs are unnecessarily complex and difficult to maintain; they made working on the project into a chore
- Extensions are unnecessarily complex and their functionality is better left to third-party clients
- Delegating functionality to extensions meant poor integration with the entry format, help menus, and shell completions
- Due to sshyp being a program with no underlying library, third-party clients are not very feasible
- Not having a library with the goal of change stability led to breaking changes in nearly every release of sshyp
- Python was a poor choice for my personal desire of portability, as dependencies must be independently installed on each system
- This led to me avoiding all non-standard libraries, which meant relying on system binaries for things like GPG and SSH
- This meant taking into account the versions of these binaries shipped by each distribution
- Because of this, sshyp was tied to OpenSSH (no support for other SSH implementations)
- This also meant launching separate SSH processes for each item being synchronized, making sshyp's synchronization _very_ slow
- Dynamically typed languages are a poor choice for beginners, as they allow for poor programming practices that create bugs
- With sshyp, I attempted to "save memory" by re-using variables for multiple purposes
- sshyp also checks variable types, rather than values, as a shortcut for determining a function's exit status
- sshyp combined the client and server into one package, resulting in lots of unreadable spaghetti code (especially with argument parsing)
- sshyp was designed in a way where it would be very difficult to port to non-UNIX-like platforms (it would basically necessitate a complete rewrite)
Developing [the successor to sshyp](https://github.com/rwinkhart/MUTN) forced me to confront these flaws and make a better product. It also allowed me the chance to re-consider each design decision; I found a better way of doing nearly everything.
-6
View File
@@ -1,6 +0,0 @@
## Usage
**Note:** [Shell completions may require additional configuration to work](https://github.com/rwinkhart/sshyp/blob/main/wiki/completions.md).
Read the man page with `man sshyp`, or [preview it on GitHub](https://github.com/rwinkhart/sshyp/blob/main/extra/manpage).
Please note that the version of the man page on GitHub may not be accurate to the version of sshyp you have installed, as the git version is kept up to date with the source, rather than the latest tagged release.