{"id":13566763,"url":"https://github.com/pts/ptssh","last_synced_at":"2026-03-01T04:27:05.282Z","repository":{"id":151222401,"uuid":"264549385","full_name":"pts/ptssh","owner":"pts","description":"portable SSH client for Unix","archived":false,"fork":false,"pushed_at":"2022-05-18T11:27:21.000Z","size":96,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-04T21:37:07.850Z","etag":null,"topics":["bourne-shell","openssh","openssl","ssh-client","ssh-key","ssh-keygen","unix"],"latest_commit_sha":null,"homepage":null,"language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pts.png","metadata":{"files":{"readme":"README.txt","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-05-16T23:50:16.000Z","updated_at":"2024-05-21T14:58:47.000Z","dependencies_parsed_at":"2023-06-26T09:00:17.125Z","dependency_job_id":null,"html_url":"https://github.com/pts/ptssh","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pts%2Fptssh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pts%2Fptssh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pts%2Fptssh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pts%2Fptssh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pts","download_url":"https://codeload.github.com/pts/ptssh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247103290,"owners_count":20884023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bourne-shell","openssh","openssl","ssh-client","ssh-key","ssh-keygen","unix"],"created_at":"2024-08-01T13:02:16.216Z","updated_at":"2026-03-01T04:27:05.245Z","avatar_url":"https://github.com/pts.png","language":"Perl","funding_links":[],"categories":["Perl"],"sub_categories":[],"readme":"ptssh: portable SSH client for Unix\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nptssh is and SSH client (with support for ssh, scp, sftp and rsync) for\nUnix with a focus on portability and single-file configs: it has a single\nconfig file (and it ignores ~/.ssh/*), and it works equivalently on any\nUnix system and any OpenSSH version. After configuration, it can be used\nas a fallback to connect if ssh(1) stops working because of a software\nupgrade or a configuration change. It can also be used on public computers\nor loaners where reading and writing OpenSSH config files is not desired.\nptssh is implemented as a Bourne shell script calling OpenSSH ssh(1) with\ncustom options. The config file can be embedded to the ptssh script and\nthe merged standalone script can be copied to a pen drive and used on\nanother computer (running Unix and having OpenSSH installed).\n\nIntroduction\n~~~~~~~~~~~~\nTo start using ptssh, you need to create the config file ~/.ptssh first.\nThis config file contains the user identity (private key), and for each\nserver the server hostkey, server connection information (hostname, port,\nusername) and server X11 forwarding config. The easiest way to create a\nconfig file is importing from ssh(1), e.g. `./ptssh import myserver1' and\n`./ptssh import myuser@myserver2'. It's possible to import multiple times\nif the user identity (private key) is the same; in this case subsequent\nimported server specifications will be appended to the config file\n~/.ptssh. A sample config file is also provided in the file\n`config.ptssh.sample'. (See details below.)\n\nDesign goals of ptssh (all met):\n\n* It should work with old and new versions of OpenSSH. More specifically,\n  OpenSSH 3.9 (released on 2004-08-18) or later, and\n  Debian Etch (released on 2007-04-08) or later should work.\n* It should work with many Bourne shells.\n* The ptssh-keycat tool should work with old and new versions of Perl 5.\n  More specifically, Perl 5.6.1 (released on 2001-04-08) or later should\n  work.\n* It should ignore OpenSSH system config files in /etc/ssh/* .\n* It should ignore OpenSSH user config files in ~/.ssh/* . The reason for\n  this is portability to other systems: by copying the ptssh shell script\n  and the config file ~/.ptssh, it should work equivalently on the target\n  system.\n* It should ignore the ssh-agent(1). This is also part of portability.\n* It should be easy to embed the config into the ptssh script, thus\n  copying one file to the target system should be enough.\n* It shouldn't attempt to run any command (other than ssh, scp, sftp, rsync\n  and chmod) which isn't a shell builtin. (Actually it also runs chmod(1) to\n  pacify permission checks by OpenSSH on user identity files.)\n\nIt's possible to embed the config into the ptssh script by just\nconcatenating them:\n\n  $ cat ptssh ~/.ptssh \u003eptssh_embedded\n  $ chmod +x ptssh_embedded\n  $ ./ptssh_embedded myserver1  # Example call.\n\nHowever, it's better to use ptssh-keycat for concatenation, because it\nalso does the necessary key conversion. Native OpenSSH private key format\n(-----BEGIN OPENSSH PRIVATE KEY-----) only works if the key is at the\nbeginning of the file (ssh(1) error message: `Load key \"...\": invalid\nformat'), and ptssh-keycat converts them to an OpenSSL-compatible format,\nwhich works anywhere. Do it like this:\n\n  $ ./ptssh-keycat -x ptssh_embedded ptssh ~/.ptssh\n  $ ./ptssh_embedded myserver1  # Example call.\n\nThen `./ptssh_embedded ...' won't read ~/.ptssh, but it would use the\nembedded user identity and server specifications.\n\nA limitation of embedding: the user identity must be an RSA (ssh-rsa,\nrecommended with 4096 bits) or DSA (ssh-dss) private key in an\nOpenSSL-compatible format. Other key algorithms (e.g. ssh-ed25519) won't\nwork for embedding, because OpenSSH can't read them in an\nOpenSSL-compatible format.\n\nCompatibility notes\n~~~~~~~~~~~~~~~~~~~\nptssh is very portable: it uses RSA hostkeys by default, and it works with\nOpenSSH \u003e=3.8 (checked with OpenSSH 8.2, OpenSSH 7.4, OpenSSH 3.8.1, and\nOpenSSH_4.3p2 Debian-9etch3, OpenSSL 0.9.8c 05 Sep 2006 on Debian Etch,\nOpenSSL 0.9.7e on Debian Sarge), no matter what the system defaults and\nuser defaults for the SSH client are. It also works with various Bourne\nshells: bash, zsh, dash, pdfksh, busybox sh (ash), posh.\n\nptssh is compatible with OpenSSH \u003e=3.8 client (released 2004-02-34), but\nit's recommended to upgrade to OpenSSH \u003e=3.9 client (released 2004-08-18),\nbecause recent OpenSSH server versions don't support any kex (key\nexchange) algorithm compatible with the OpenSSH 3.8 client, with the\nclient failing with error `no kex alg'. ptssh is also compatible\nwith newer OpenSSH clients, e.g. 8.2 (released 2020-02-14).\n\nptssh ignores the ssh-agent, and asks for the identity file passphrase\neach time. This is on purpose. The goal is to make to ptssh run\nindependently and identically on any system and environment (given the\nsame config file), and the use of ssh-agent would introduce variance.\n\nptssh is also compatible with Debian Sarge (2005-06-06) or later, but\nrecent OpenSSH server versions don't support any kex algorithm compatible\nwith the OpenSSH 3.8.1 client in Debian Sarge, so it's recommended to\nupgrade to Debian Etch (2007-04-08) instead, which has OpenSSH 4.3.\n\nptssh is compatible with rsync \u003e=2.6.4 client in Debian Sarge\n(2005-06-06). Maybe it works with even older versions of rsync.\n\nptssh can't run from a FAT filesystem (or other non-Unix filesystems),\nbecause `chmod' below isn't able to remove rwx permissions for group and\nother there. This can be a problem if it's run from USB pen drives.\n\nPrivate key generation and conversion\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nThe RSA public key algorithm is recommended for maximum backwards\ncompatibility with OpenSSH clients and servers. The RSA key size of 4096\nbits is high enough to withstand cryptographic attacks in 2021.\n\nYou need an OpenSSL-compatible private key if you wish to embed the config\nfile to the shell script later. OpenSSH only supports RSA and DSA as public\nkey algorithms in OpenSSL-compatible format. The RSA algorithm is\nrecommended. The DSA algorithm, as used with by OpenSSH, is now considered\ninsecure, it's too easy to crack the key on modern hardware. If you don't\nneed want to embed, any public key algorithm (such as ED25519) will work,\nprovided that both the OpenSSH client and server support it.\n\nKey generation commands:\n\n* Run this to generate a new, OpenSSH-format, strongly passphrase-protected\n  RSA private key (user identity) into file id_rsa and public key into file\n  id_rsa.pub (it won't work when embedded to the shell script):\n\n    $ ssh-keygen -t rsa -b 4096 -o -C '' -f id_rsa\n\n  Above the `-o' flag of ssh-keygen selects the new, OpenSSH format.\n\n* Alternatively, run this to generate a new, OpenSSH-format, strongly\n  passphrase-protected ED25519 private key (user identity) into file\n  id_ed25519 and public key into file id_ed25519.pub (it won't work when\n  embedded to the shell script):\n\n    $ ssh-keygen -t ed25519 -o -C '' -f id_rsa\n\n* Run this to generate a new, OpenSSL-compatible, strongly\n  passphrase-protected RSA private key (user identity) into file id_rsa and\n  public key into file id_rsa.pub (it will work when embedded to the shell\n  script):\n\n    $ ssh-keygen -t rsa -b 4096 -C '' -P '' -f id_rsa.unprotected\n    $ ./ptssh-keycat --protect -o id_rsa id_rsa.unprotected\n    $ rm -f id_rsa.unprotected\n\n  Alternatively, you can do it with openssl(1):\n\n    $ openssl genrsa 4096 | ./ptssh-keycat --protect -o id_rsa\n    $ ssh-keygen -y -f id_rsa \u003eid_rsa.pub\n\n  Alternatively, you can do it with openssl(1) only, without using ptssh-keycat:\n\n    $ openssl genrsa 4096 |\n      openssl pkcs8 -out id_rsa -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA1 -iter 4200000\n    $ ssh-keygen -y -f id_rsa \u003eid_rsa.pub\n\n  The private key file id_rsa will start with `-----BEGIN ENCRYPTED PRIVATE\n  KEY-----'. It will work with both OpenSSH (\u003e=3.8, probably even earlier)\n  and OpenSSL.\n\n  Please note that it's possible to have password-protected RSA and DSA\n  private keys in the traditional OpenSSL formats (`----BEGIN RSA PRIVATE\n  KEY-----'; and `----BEGIN DSA PRIVATE KEY-----'), but those are insecure\n  (weak), because they generate the decryption key by doing a single MD5\n  digest call, and thus the attacker can try (brute-force) millions of\n  passphrases per second. The alternative above (`openssl pkcs8\n  ... -iter 4200000') uses PBKDF2 with a sufficiently large iteration count\n  (equivalent to `ssh-keygen -a 400') to protect against brute-force\n  attacks.\n\nKey conversion commands:\n\n* Run this to convert a private key in file id_rsa from any format (e.g.\n  OpenSSL-compatible) to the new, OpenSSH format:\n\n    $ ssh-keygen -p -o -f id_rsa\n\n  You will have to type the old passphrase (if any) and the new passphrase.\n  To get an unprotected private key, set the new passphrase to empty.\n\n  Above the `-o' flag of ssh-keygen selects the new, OpenSSH format.\n\n* Run this to convert a private key in file id_rsa from any format (e.g.\n  new, OpenSSH) with public key algorithm RSA or DSA to the\n  OpenSSL-compatible format, with strong passphrase protection:\n\n    $ ./ptssh-keycat --protect -o id_rsa.protected id_rsa\n    $ mv id_rsa.protected id_rsa\n\n  ptssh-keycat is a Perl script which implements most of the conversion\n  steps natively in Perl, and it uses ssh-keygen(1) and openssl(1) for\n  passphrase-protection.\n\n  Alternatively, it's possble to convert an unprotected private key from the\n  new, OpenSSH format to the OpenSSL-compatible format with strong\n  passphrase protection, using puttygen(1) instead of ptssh-keycat. (Please\n  note that openssl(1) and ssh-keygen(1) together can't do it, either\n  puttygen(1) or ptssh-keycat is also needed.) (Please note that puttygen(1)\n  cannot read passphrase-protected private keys in the new, OpenSSH format,\n  but ptssh-keycat can.) On Debian and Ubuntu, install puttygen(1) with\n  `sudo apt-get install putty-tools'. Probably it's more convenient to use\n  ptssh-keycat, because that works without installation. Here is how to\n  convert using puttygen(1):\n\n    $ puttygen -P -O private-openssh -o id_rsa.unprotected id_rsa\n    Enter passphrase to load key:\n    Enter passphrase to save key:    (specify empty passphrase)\n    Re-enter passphrase to verify:   (specify empty passphrase)\n    $ openssl pkcs8 -out embed.pem -in id_rsa.unprotected -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA1 -iter 4200000\n    Enter Encryption Password:\n    Verifying - Enter Encryption Password:\n    $ rm -f id_rsa.unprotected\n    $ chmod 600 id_rsa\n\n* Run this to convert a private key in file id_rsa from any format (e.g.\n  new, OpenSSH) with public key algorithm RSA or DSA to the\n  OpenSSL-compatible format, without passphrase protection (unprotected):\n\n    $ ./ptssh-keycat --unprotect -o id_rsa.unprotected id_rsa\n    $ mv id_rsa.unprotected id_rsa\n\n  Alternatively, run this to convert an unprotected private key from the\n  new, OpenSSH format to the OpenSSL-compatible format:\n\n    $ puttygen -P -O private-openssh -o id_rsa.unprotected id_rsa\n    Enter passphrase to load key:\n    Enter passphrase to save key:    (specify empty passphrase)\n    Re-enter passphrase to verify:   (specify empty passphrase)\n    $ mv id_rsa.unprotected id_rsa\n    $ chmod 600 id_rsa\n\n* Run this to convert a private key in file id_rsa from any format (e.g.\n  new, OpenSSH) with public key algorithm RSA or DSA to the\n  OpenSSL-compatible format, keeping protection unchanged:\n\n    $ ./ptssh-keycat -o id_rsa.converted id_rsa\n    $ mv id_rsa.converted id_rsa\n\nSome other useful commands:\n\n* Run this to replace the private key in the config file ~/.ptssh with the\n  private key in the file id_rsa:\n\n    $ (cat id_rsa \u0026\u0026\n       perl -ne 'if(/^-----(?:BEGIN|(END)) /){$c-=1-2*!$1}else{print if!$c}' ~/.ptssh\n      ) \u003e~/.ptssh.new\n    $ cat ~/.ptssh.new \u003e~./ptssh\n    $ rm -f ~./ptssh.new\n\n  Alternatively, you can do it in a text editor. The old private key starts\n  with the first `----BEGIN ' in ~/.ptssh .\n\n* Run this to enable a private key id_rsa (also works with ~/.ptssh) on the\n  OpenSSH server:\n\n   $ (PK=\"$(ssh-keygen -y -f id_rsa)\"; echo \"$PK\" |\n      ssh myserver1 'test -d .ssh || mkdir .ssh; cat \u003e\u003e.ssh/authorized_keys')\n\nThe config file\n~~~~~~~~~~~~~~~\nThe config file can be embedded in a merged standalone script by\nconcatenating the ptssh script with the config file, or it can\nbe in a separate file, ~/.ptssh.\n\nThe format of the config file is the following:\n\n* (Most users don't need to care about these details, because they have\n  created the config file with `./ptssh import ...'.)\n* (Use `./ptssh-keycat --check ~/.ptssh' to check the config for common\n  errors and incompatibilities with OpenSSH.)\n* The beginning of the config file is ignored. This enables embedding the\n  config to the shell script. Please note that for this to work with the\n  OpenSSH client, the key type must be OpenSSL-compatible (i.e. anything\n  other than `-----BEGIN OPENSSH PRIVATE KEY-----'). Only lines starting\n  with `-----BEGIN ' and `ptssh-hostkey-' are recognized, everything else is\n  ignored.\n* A line of the form `-----BEGIN ... PRIVATE KEY-----' (including\n  `-----BEGIN PRIVATE KEY-----') indicating the\n  beginning of the user identity (private key). Any format (`...')\n  supported by OpenSSH works. It can be protected (encrypted with a\n  passphrase) or unprotected. Any key algorithm supported by OpenSSH (e.g.\n  ssh-rsa, ssh-dss, ssh-ed25519) works.\n* Optional `Key: value' lines if it's an encrypted traditional OpenSSL RSA\n  or DSA private key. Example key: `Proc-Type' and `DEK-Info'.\n* Base64-encoded private key material.\n* A line of the form `-----END ... PRIVATE KEY-----'.\n* Comment lines and server specification lines. At least 1 server\n  specification line. Each line which doesn't start with `ptssh-hostkey-'\n  is a comment line.\n* A server specification line is a whitespace-separated (can be space, tab\n  or combination) of these items:\n  * `ptssh-hostkey-ID'. `ID' is an arbitrary unique ID within the file.\n  * hostkey algorithm. Examples: ssh-rsa, ssh-dss, ssh-ed25519,\n    ecdsa-sha2-nistp521, ecdsa-sha2-nistp384, ecdsa-sha2-nistp256.\n  * hostkey public key, Base64-encoded. Starts with `AAAA'.\n  * server username.\n  * server port number. The default, 22, also has to be specified.\n  * server hostname (or IP address).\n  * X11 forwaring specification. One of: -x (disabled), -X (enabled,\n    untrusted), -Y (enabled, trusted). If you don't want to run GUI programs\n    on the server over the SSH connection, specify `-x'.\n  * host aliases. Comma-separated list of aliases (`Host' lines in\n    ~/.ssh/config) ptssh should recognize in the command-line. By default,\n    there is only 1 alias: `ID' above.\n\nThe config file format was designed with the following requirements:\n\n* It is a structured text file which is easy to generate and parse\n  from shell scripts.\n* It is easy to edit manually, including the copy-pasting of keys\n  from other files. However, manual config file editing is only an expert\n  use case: for most uses the config file created by tools ptssh and\n  ptssh-keycat should be sufficient.\n* It can be used for `ssh -o IdentityFile=...' (and `ssh -i ...') without\n  modification, thus it starts with a private key understood by\n  OpenSSH, thus starting with `-----BEGIN ... PRIVATE KEY-----'.\n  Please note that if the config file is embedded within the ptssh script,\n  then the script (starting with `#! /bin/sh --') comes first. This is\n  not a problem, because for OpenSSL-compatible private key formats\n  (i.e. anything other than ``-----BEGIN OPENSSH PRIVATE KEY-----'),\n  OpenSSH accepts a private key starting anywhere in the file.\n* It can be used for `ssh -o UserKnownHostsFile=...' without modification.\n  This isn't an important restriction, because OpenSSH ignores lines\n  not starting with with `...' specified in `ssh -o HostKeyAlias=...'.\n\nTODOs\n~~~~~\n* Use a one-off ssh-agent with the private key piped using ssh-add. This\n  will lift the following restrictions:\n\n  * The new, OpenSSH-format private keys cannot be embedded (because OpenSSH\n    client only looks at the beginning of the file).\n\n  * The ~/.ptssh config file and the embedded shell script must be chmodded\n    to remove all group and other permission bits. (This will also make the\n    embedded ptssh shell script work on FAT and exFAT filesystems.)\n\n* Add support for multiple ptssh config files (`ptssh -F \u003cconfig-file\u003e\n  ...').\n\n* Add support for convenient and permanent ssh-agent dedicated to ptssh,\n  one ssh-agent per private key (config file). Agent forwarding can be\n  enabled, but keys in the ssh-agent will be immutable after the first\n  key has been added.\n\n__END__\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpts%2Fptssh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpts%2Fptssh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpts%2Fptssh/lists"}