{"id":40827504,"url":"https://github.com/mrzor/gcloud_sync_ssh","last_synced_at":"2026-01-21T22:18:09.512Z","repository":{"id":57433247,"uuid":"288437321","full_name":"mrzor/gcloud_sync_ssh","owner":"mrzor","description":"An improved version of gcloud compute config-ssh, a convenient way to setup connectivity to your virtual machines hosted within GCP.","archived":false,"fork":false,"pushed_at":"2020-09-22T18:57:08.000Z","size":83,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-08T02:25:13.393Z","etag":null,"topics":["gcloud","gcloud-sdk","gcp","sysadmin","sysadmin-tools"],"latest_commit_sha":null,"homepage":"","language":"Python","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/mrzor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-18T11:31:03.000Z","updated_at":"2023-04-11T20:55:41.000Z","dependencies_parsed_at":"2022-08-27T23:52:46.911Z","dependency_job_id":null,"html_url":"https://github.com/mrzor/gcloud_sync_ssh","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mrzor/gcloud_sync_ssh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrzor%2Fgcloud_sync_ssh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrzor%2Fgcloud_sync_ssh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrzor%2Fgcloud_sync_ssh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrzor%2Fgcloud_sync_ssh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrzor","download_url":"https://codeload.github.com/mrzor/gcloud_sync_ssh/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrzor%2Fgcloud_sync_ssh/sbom","scorecard":{"id":665094,"data":{"date":"2025-08-11","repo":{"name":"github.com/mrzor/gcloud_sync_ssh","commit":"f064f524648d42a9199ebc6c916e5f72ca2ca45e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/10 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2022-14 / GHSA-39ph-wr67-j4xq","Warn: Project is vulnerable to: PYSEC-2021-47 / GHSA-5jqp-qgf6-3pvh","Warn: Project is vulnerable to: GHSA-mr82-8j83-vxmv"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T17:44:07.541Z","repository_id":57433247,"created_at":"2025-08-21T17:44:07.541Z","updated_at":"2025-08-21T17:44:07.541Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28645548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T21:29:11.980Z","status":"ssl_error","status_checked_at":"2026-01-21T21:24:31.872Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["gcloud","gcloud-sdk","gcp","sysadmin","sysadmin-tools"],"created_at":"2026-01-21T22:18:07.560Z","updated_at":"2026-01-21T22:18:09.507Z","avatar_url":"https://github.com/mrzor.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `gcloud_sync_ssh`\n\nAn improved version of `gcloud compute config-ssh`, a convenient way to setup connectivity to your virtual machines hosted within GCP.\n\nThis tools has some benefits:\n\n* Can work on several projects at once\n* Only sets up connectivity by setting Hostnames in SSH config.\n  * No side effects in project or instance metadata (madness!)\n  * Works quite faster because updating project metadata is _slow_\n* Can generate user-specified SSH config for all hosts\n  * Host specific config is not handled by the tool. Edit your configuration instead.\n* Can remove stopped ('terminated', in GCP parlance) instances from config\n* Generates smallest possible diff by preserving casing, separators and whitespace\n  * Smallest diff = highest review impact.\n\nAnd some drawbacks:\n\n* If the instance is not configured to let you in, this tool will _not_ fix that.\n\nI've used it for a couple of months for my modest needs and it did the job flawlessly. YMMV - see 'Limitations' below.\n\n## Install\n\n#### With pipx\n\n- `pipx install gcloud_sync_ssh`\n\n#### Run from source\n\n- Clone it\n- Recommended: create a virtualenv with [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv\n  - i.e. `pyenv virtualenv 3.6 gcloud_sync_ssh`\n- `pip install -r requirements.txt`\n  - N.B. This will install development dependencies as well\n- `python -m gcloud_sync_ssh`\n\n## Usage\n\n- `gcloud_sync_ssh --help`\n\nThe tool works in four phases. It optionally changes the gcloud authentication context, then it enumerates projects and instances, then it updates your SSH config in memory and finally saves it after making a backup and showing you the diff.\n\n#### Preliminary: Choose which config file to edit\n\nBy default, `gcloud_sync_ssh` edits the standard `~/.ssh/config`. This will be fine for most uses. It may be changed with `-c/--config`.\n\nSome people prefer the tool to operate on an isolated file, like `~/.ssh/config.auto`. A few things to keep in mind if you go down that path:\n\n- You need to `chmod 0600 ~/.ssh/config.auto` for SSH to read it\n- You need to add `Include ~/.ssh/config.auto` to your existing `~/.ssh/config` _before_ any `Match` or `Host` block.\n- Alternatively, use `ssh -F ~/.ssh/config.auto` instead of setting up the `Include`.\n\n#### First phase: Authentication\n\nFirst it will optionally change your `gcloud` authentication context as desired, using the\n`--login` option for email accounts or `--service-account` option for service accounts.\n\n\nIf none of these options are specified, `gcloud_sync_ssh` will not touch modify your gcloud\nauthentication settings - and will function with your current settings.\n\n\nYour current settings will be restored once the tools exits.\n\n\nBe aware that authentication is a user-level setting. When using special authentication, avoid running `gcloud` while `gcloud_sync_ssh` is running. This also applies to other tools that piggy back on gcloud auth, like Terraform gcloud backend in application-default mode.\n\n#### Second phase: Enumeration\n\nYou may select a project with the `--project` option. This option can be specified several times to select multiple projects. This option accepts `fnmatch`-style globbing (i.e. using `*`, `?` ...).\n\n- Just one project : `--project my-project-name`\n- A couple of projects: `--project project-web-1 --project-web-2`\n- A bunch of projects: `--project 'project-web-*'` (Beware shell quoting rules for globbing characters)\n- All projects : `--all-projects`\n\n#### Third phase: Configuration updates\n\nThere are quite a few cases to consider.\n\n##### Updates\n\nThe simplest case is updates : running instances already in your configuration, but the external IP changes.\n\nYour configuration will be updated in place and nothing else will change. There are no options controlling this behavior.\n\n##### Deletions\n\nThen we have deletions:\n\n- Instances in your configuration that have entered the `STOPPED` status will be removed from your configuration. You can disable this using the `-nrs|--no-remove-stopped` flag.\n- Deleted instances (or instances that have otherwise vanished) will be removed from your SSH config _if and only if_ it's possible to attribute the instance to a project using its hostname i.e. its hostname terminates by `.\u003cproject-name\u003e`. You can disable that using the `-nrv|--no-remove-vanished` flag.\n\n##### Additions\n\nFinally, we have additions. A new `Host` block will be added to your configuration for newly detected instances. The complexity comes from the many ways to specify how that block will be generated. There are two parts to this : the 'template' is the set of options that doesn't change from host to host, and the 'specifics' which are the couple of options that are allowed to vary.\n\n\nThe _`Host` template_ is computed as such:\n\n1. The tool has builtin defaults. You can see them with `--no-inference --debug-template`.\n2. The tool tries to _infer_ a template from your existing configuration. Any keyword-argument pair that is shared by _every_ `Host` block in your config is added to this template. The inferred values override the defaults. You can turn this off by passing the `--no-inference` flag. You can see the results of this stage by passing `-dt|--debug-template`.\n3. You can specify additions to the template using the `-kw/--kwarg` option possibly several times.\n\nHere is an exemple of a Host template that overrides defaults :\n\n    $ gcloud_sync_ssh --debug-template --no-inference -kw IdentityFile=/secret/id_rsa -kw UserKnownHostsFile=/data/known_hosts\n    2020-09-07 20:23:18.290 | INFO     | Displaying host template\n        CheckHostIP no\n        IdentitiesOnly yes\n        IdentityFile /secret/id_rsa\n        UserKnownHostsFile /data/known_hosts\n\n    2020-09-07 20:23:18.291 | INFO     | Done displaying host template\n\nThe 'specifics' are:\n\n- The `Hostname` kwarg, that will be set to the instance external IP (or first external IP, if there are several). This is the whole point of this tool, and it cannot be controlled by an option.\n- The `HostKeyAlias` kwarg, that will be set to `compute.\u003cinstance_id\u003e`. This is what `gcloud compute config-ssh` does. This will prevent warnings because of external IP changes. You can disable generating those with `-nk|--no-host-key-alias`.\n\n#### Fourth phase: Configuration save\n\nYou don't have to blindly trust the tool. By default it will show you the diff and ask for approval before saving - while still saving a backup.\n\nIf you want to use it from `cron` or CI, this behavior might be counterproductive, so it can be disabled:\n\n- Don't show diff and don't ask for approval: `--not-interactive`\n- Don't save a backup: `--no-backup`\n\n## Examples\n\n## Limitations\n\n* Only works with one account at a time (TODO: Support iterating through all accounts exposed by `gcloud auth list`)\n* Can only be setup through commandline options (TODO: Support configuration file on top of gazillion command line options)\n* Doesn't support \"jump box\" setups or VPN setups - where you connect to the private IP address of your instances. (TODO: Support that!)\n* Is single-threaded synchronous (TODO: Support parallelism with either threads or async)\n* Formatting of new hosts is not _exactly_ the same as what `gcloud compute config-ssh` does. Notably, it has consistent space delimiting instead of having `=` on some lines and ` ` on others. (Probably won't fix)\n* There are no ways to setup 'specific' options other than the two builtins for new `Host`. (TODO: Accept Python plugins to allow arbitrarily complex schemes to add/edit SSH config per host)\n* Vanishing/deleted instances can only be removed from your config if their hostname is suffixed by `.\u003cproject-name\u003e`. This is the GCP default. I found no other way to attribute a Host in your SSH config to a given SSH project. Workaround: remove everything with `gcloud compute config-ssh --remove` then use `gcloud_sync_ssh` as usual. (TODO: Support `--overwrite` flag that removes everything in the config block before running)\n* Instances in transitional states and suspended states are completely ignored by the tool.\n\nThe above are roughly sorted given how concerning they are to me. None are preventing me to achieve my goals with the tool - but some may hinder you.\n\n### Alternatives\n\nUsing this script to setup connectivity should work well enough for ~hundreds of instances whose external IPs change ~daily. I use it for ~dozens of instances whose IPs change ~daily.\n\nIf your external IPs change very frequently, or if you have thousands of instances, this tool can still be a useful crutch, but you may want to look into alternatives.\n\nHere are my thoughts on the subject, ordered by effort required.\n\n* Setup a shared private subnetwork between your instances, and you'll get DNS with `.internal` TLD for free. A jumpbox is a way to get access to your instances. [ XXX Other ways ? ]\n* Use some form of service registration/discovery. Hashicorp Consul comes to mind.\n* Use Google Logging to process instance startup/showdown logs. Tap those into Pub/Sub. Write a lambda function that produces a `hosts` file or a SSH config file. Store that in Storage at a well known URI. Download that in whichever way you like, then `Include` it in your SSH config.\n* Same as above, but generate a zone file and feed that to a DNS server you control.\n* Move all your things to K8s and welcome your new YAML overlords. You can't ssh into anything without using `kubectl` but that's okay.\n\n## Contributing\n\nGrab a TODO from the _Limitations_ list, an issue or bring your own issue to solve.\n\nIf it makes sense, please add tests and make sure they pass with `python -m pytest`.\n\n## License\n\nThe code contained in this repository is licensed under the terms of the [MIT license](LICENSE) unless otherwise noted in the source code file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrzor%2Fgcloud_sync_ssh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrzor%2Fgcloud_sync_ssh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrzor%2Fgcloud_sync_ssh/lists"}