{"id":13582487,"url":"https://github.com/andrewhamon/pcmd","last_synced_at":"2026-03-13T06:32:10.759Z","repository":{"id":75958775,"uuid":"264090690","full_name":"andrewhamon/pcmd","owner":"andrewhamon","description":"Get creative with your SSH ProxyCommands","archived":false,"fork":false,"pushed_at":"2020-05-17T00:27:05.000Z","size":73,"stargazers_count":34,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-11T11:45:33.343Z","etag":null,"topics":["go","golang","golang-application","proxycommand","ssh","ssh-client","ssh-config"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrewhamon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"publiccode":null,"codemeta":null}},"created_at":"2020-05-15T03:54:49.000Z","updated_at":"2024-12-31T09:06:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"34202091-96fd-4c60-9d7d-52f22959ee56","html_url":"https://github.com/andrewhamon/pcmd","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/andrewhamon/pcmd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhamon%2Fpcmd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhamon%2Fpcmd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhamon%2Fpcmd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhamon%2Fpcmd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewhamon","download_url":"https://codeload.github.com/andrewhamon/pcmd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhamon%2Fpcmd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30460612,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T03:55:51.346Z","status":"ssl_error","status_checked_at":"2026-03-13T03:55:33.055Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["go","golang","golang-application","proxycommand","ssh","ssh-client","ssh-config"],"created_at":"2024-08-01T15:02:45.833Z","updated_at":"2026-03-13T06:32:10.742Z","avatar_url":"https://github.com/andrewhamon.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# pcmd\n\n`pcmd` is a small utility to make SSH `ProxyCommand` more powerful. It might be\nuseful any time you have a `ProxyCommand` that involves a lengthy or expensive\nset-up or tear-down procedure.\n\n- [Rationale](#rationale)\n- [Compatibility and dependencies](#compatibility-and-dependencies)\n- [Installation](#installation)\n  - [Pre-compiled binaries](#pre-compiled-binaries)\n  - [Go get](#go-get)\n- [Examples](#examples)\n  - [Complete example: on-demand VPS with DigitalOcean](#complete-example-on-demand-vps-with-digitalocean)\n  - [Configurable grace period](#configurable-grace-period)\n  - [Preventing concurrent `ProxyCommand` invocations](#preventing-concurrent-proxycommand-invocations)\n  - [Connection sharing](#connection-sharing)\n\n## Rationale\n\nThe primary use case of `pcmd` is to wrap a `ProxyCommand` that might need to\nperform non-trivial set-up or tear-down operations. For example, one might have\na `ProxyCommand` that provisions and deletes a cloud VPS for disposable\ndevelopment environments, accessible via `ssh my.temporary.host`. For such a use\ncase, it is essential to a) not duplicate work and b) ensure that resources get\ncleaned up. Without `pcmd` it can be difficult to reliably meet those\nrequirements.\n\nHere are some of the challenges I faced when implementing on-demand VPS\nprovisioning in a `ProxyCommand`:\n\n1) Tear-down. Some versions and configurations of SSH, such as on macOS, send a\n   hangup signal as soon as the SSH connection is closed. This prevents any\n   cleanup from occurring. That is bad because then you get billed for a VPS\n   that you wanted to be disposable!\n2) Logging. It's nice to be able to see logs to stderr while waiting for the VPS\n   to come up. Once the SSH connection is closed, though, it's very annoying if\n   logs to stderr continue to get dumped into your terminal. That is what would\n   happen if you resolved issue 1) by trapping and ignoring signals.\n3) Connection sharing and concurrency control. If you initiate multiple SSH\n   connections, you probably want to re-use an existing VPS when possible. You\n   might think: doesn't SSH let you configure this? You bet it does! You can set\n   `ControlMaster auto` to make SSH opportunistically re-uses existing\n   connections, skipping the `ProxyCommand` on subsequent SSH invocations.\n   There's a small problem, though: what if the first connection is still in the\n   \"set-up\" phase and hasn't established a connection? It turns out that SSH\n   doesn't do any locking, and, in that case, it executes the `ProxyCommand`\n   twice. One will win and become the master connection, the other will log a\n   warning that the master already exists, and you will be paying for double the\n   compute you thought you were.\n\n`pcmd` solves all of those problems:\n\n1) `pcmd` shields the underlying `ProxyCommand` from interrupt and hangup\n   signals. When a signal is received, pcmd closes stdio and starts a grace\n   period timer (default 5 minutes). During this grace period, the\n   `ProxyCommand` can perform any tear-down it needs. If the command hasn't\n   exited by the end of the grace period, `pcmd` sends a kill signal.\n2) `pcmd` copies stderr of the `ProxyCommand` to a log file as well as to the\n   terminal. That way, while waiting for the connection, you can see any\n   relevant logs. Once the connection is closed, `pcmd` continues copying stderr\n   of the command to the log file but stops copying stderr to the terminal. That\n   way, you don't get annoying logs dumped into the terminal when you move on to\n   something else.\n3) If instructed with the `-lock` flag, `pcmd` ensures only one copy of your\n   `ProxyCommand` runs at once. If are sharing connections using\n   `ControlMaster`, you can additionally specify `-wait-for-master`. If `pcmd`\n   doesn't have a lock, it blocks waiting for the `ControlMaster` to come up. As\n   a nicety, while waiting, it also tails the log file mentioned in 2) to stderr\n   so you can monitor the progress of a lengthy set-up.\n\n## Compatibility and dependencies\n`pcmd` is cross-compiled to [many\ntargets](https://github.com/andrewhamon/pcmd/releases/latest), but has only been\ntested on macOS and Linux (Ubuntu 18.04). Additionally, `pcmd` requires the\n`tail` and, depending on configuration, `ssh` commands to be available. These\nare likely already installed on your system.\n\n## Installation\n\n### Pre-compiled binaries\n`pcmd` is available for download from the [releases\npage](https://github.com/andrewhamon/pcmd/releases/latest). You can also use the\nfollowing snippet to install the latest release of `pcmd`, adjusting the values\naccordingly for your environment:\n\n```sh\n# Possible options: darwin,linux,freebsd,openbsd,netbsd\n# darwin == macOS\nOS=darwin\n\n# Possible options: amd64,386,arm,arm64(linux only)\nARCH=amd64\n\nTARGET=pcmd-$OS-$ARCH\n\n# Download and unzip\ncurl -OL https://github.com/andrewhamon/pcmd/releases/latest/download/$TARGET.zip\nunzip $TARGET.zip\n\n# Copy to somewhere on your $PATH\n# replace ~/bin with something appropriate for your environment\ncp $TARGET/pcmd ~/bin\n```\n\n### Go get\nIf you have Go installed, you can also install `pcmd` using `go get`:\n\n```sh\ngo get github.com/andrewhamon/pcmd\n```\n\n## Examples\nThe easiest way to get started is to wrap your original `ProxyCommand` with\n`pcmd` in your SSH config. For example, if your SSH config looks like this:\n\n```ssh-config\nHost some-host\nProxyCommand original-proxy-command --original-arg foobar\n```\n\nYou can prefix `original-proxy-command` and its arguments with `pcmd`:\n\n```ssh-config\nHost some-host\nProxyCommand pcmd original-proxy-command --original-arg foobar\n```\n\nThis will continue to proxy as before but also ensures `original-proxy-command`\nhas adequate time after the connection closes to perform cleanup.\n\n### Complete example: on-demand VPS with DigitalOcean\nIncluded in this repo is an [example bash\nscript](https://github.com/andrewhamon/pcmd/blob/master/bin/on-demand-proxy)\nthat uses `doctl` to create a VPS on the fly and proxy to it. The following is\nis the resulting SSH config, configured for connection sharing with\n`ControlMaster`. To use, you will need to ensure that the `on-demand-proxy`\nscript is downloaded and available in your `$PATH` and that you have [installed\n`doctl`](https://github.com/digitalocean/doctl#installing-doctl).\n\n```ssh-config\nHost ondemand.dev\nUser root\nProxyCommand pcmd --wait-for-master -r %r -h %h -p %p on-demand-proxy %h\nControlMaster auto\nControlPath ~/.ssh/%r@%h:%p.sock\n\n# Keep the connection alive for 5 minutes after the last connection is closed.\n# This lets you quickly re-connect. This is not the same as the -grace-period\n# flag.\nControlPersist 300\n\n# Even when reviving a snapshot, DigitalOcean seems to generate a new key, which\n# will then cause scary warnings.\nStrictHostKeyChecking no\nUserKnownHostsFile=/dev/null\n```\n\n![pcmd in action](/pcmd-demo.svg?raw=true\u0026sanitize=true)\n\n### Configurable grace period\nYou can configure the grace period (default 5 minutes) with the `-grace-period`\nflag. For example, to set the grace period to 10 minutes:\n\n```\npcmd -grace-period 600 original-proxy-command --original-arg foobar\n```\n\nThe grace period begins only once the SSH connection is closed.\n\n### Preventing concurrent `ProxyCommand` invocations\nIf you add the `-lock` flag, `pcmd` can ensure that only one copy of\n`original-proxy-command` runs at a time. To do so, `pcmd` needs to know the SSH\nremote user and host, which it uses to form a unique key for locking. For\nexample:\n\n```\npcmd -lock -r %r -h %h original-proxy-command --original-arg foobar\n```\n\n`%r` and `%h` are expanded by SSH automatically. See TOKENS in SSH_CONFIG(5) for\nmore details.\n\n### Connection sharing\nSSH provides a native mechanism for connection sharing via the `ControlMaster`\nand `ControlPath` configuration options, which allows you to share a single\nconnection for multiple SSH sessions (see SSH_CONFIG(5) for more details). One\nissue with the native mechanism, however, is that SSH doesn't do any concurrency\ncontrol. That means if you have a lengthy set-up in your proxy command, and then\ntry to establish two concurrent connections, SSH does not block one connection\nwaiting for a master. Instead, it runs both `ProxyCommand`s at the same time.\n\n`pcmd` can help with this, by doing two things:\n\n- Establishing a lock (using the `flock` system call) to ensure only one version\n  of your `ProxyCommand` is running at a time.\n- Waits for the `ControlMaster` to come up, if the lock can not be acquired.\n  `pcmd` checks the status of the control master using `ssh user@host -O check`.\n\nTo set up connection sharing, add the `-wait-for-master` flag, along with the\n`-r` and `-h` flags. For example:\n\n```ssh-config\nHost some-host\nProxyCommand pcmd -wait-for-master -r %r -h %h original-proxy-command --original-arg foobar\nControlMaster auto\nControlPath ~/.ssh/ssh-%r@%h:%p\n\n# Setting ControlPersist to a non-zero value ensures that SSH keeps the master\n# connection running in the background, rather than blocking the first SSH\n# invocation until all child connections also complete. If you want to keep your\n# connection alive in the background for longer, set this to a higher value (in\n# number of seconds)\nControlPersist 1\n```\n\nThe above config will allow only a single master connection, and make any\nsubsequent connections wait for the master to come up before continuing.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewhamon%2Fpcmd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewhamon%2Fpcmd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewhamon%2Fpcmd/lists"}