{"id":48431387,"url":"https://github.com/cedws/sshgate","last_synced_at":"2026-04-06T11:01:09.982Z","repository":{"id":310601530,"uuid":"1040531043","full_name":"cedws/sshgate","owner":"cedws","description":"A forwarding-only SSH server with policy-based connection control for restricted jump hosts and bastions","archived":false,"fork":false,"pushed_at":"2026-03-24T21:25:36.000Z","size":86,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-26T02:58:02.371Z","etag":null,"topics":["firewall","ssh","tailscale"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cedws.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-19T05:56:58.000Z","updated_at":"2026-03-25T18:33:46.000Z","dependencies_parsed_at":"2025-08-19T06:20:30.947Z","dependency_job_id":"8ab932c4-8c75-4358-8df6-1c6e170261f2","html_url":"https://github.com/cedws/sshgate","commit_stats":null,"previous_names":["cedws/sshgate"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/cedws/sshgate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedws%2Fsshgate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedws%2Fsshgate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedws%2Fsshgate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedws%2Fsshgate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cedws","download_url":"https://codeload.github.com/cedws/sshgate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedws%2Fsshgate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31469743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T08:36:52.050Z","status":"ssl_error","status_checked_at":"2026-04-06T08:36:51.267Z","response_time":112,"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":["firewall","ssh","tailscale"],"created_at":"2026-04-06T11:01:09.078Z","updated_at":"2026-04-06T11:01:09.973Z","avatar_url":"https://github.com/cedws.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sshgate\n\nA forwarding-only SSH server with policy-based connection control for restricted jump hosts and bastions.\n\nI made this for my experimental [devenv](https://github.com/cedws/devenv) project in which I'm building a locked down development environment with restricted outbound traffic. sshgate facilitates my goal of only allowing outbound SSH traffic to `github.com:22`.\n\n## Installation\n\n### Brew\n\n```bash\nbrew install cedws/tap/sshgate\n```\n\n### Scoop\n\n```powershell\nscoop bucket add cedws https://github.com/cedws/scoop-bucket.git\nscoop install sshgate\n```\n\n### Docker\n\n```bash\ndocker pull ghcr.io/cedws/sshgate:latest\n```\n\n## Usage\n\nsshgate is an SSH server that only handles `direct-tcpip` channels, meaning it will only opaquely forward traffic from a remote host if there's a rule for the connected identity allowing it. It doesn't grant a PTY.\n\nCopy the example `config.json` and add your own SSH public key to the `authorized_keys` array, then start it up:\n\n```\nsshgate --config config.json\n```\n\nYou can now use it as a jump host to reach a remote host:\n\n```\nssh -J localhost:2222 git@github.com\n```\n\nThe example config contains a rule allowing the client to jump to `github.com:22`. If you try some other host or port, you'll see something like this:\n\n```\n$ ssh -J localhost:2222 bitbucket.org\nchannel 0: open failed: administratively prohibited: remote connection denied\nstdio forwarding failed\nConnection closed by UNKNOWN port 65535\n```\n\nsshgate doesn't care about the username used for the jump hop. It doesn't care about the usernames in subsequent hops either; that information is opaque, it simply forwards the traffic to the client.\n\n### Rules\n\nThe default policy is to block all connections. The `rules` array describes what hosts and ports to allow forwarding to. If you just want to use sshgate for a bastion without firewalling you can run it with `--ruleless`, which disables firewall rules.\n\nA host can be any of the following:\n\n* Hostname (e.g. `github.com`, wildcards not supported)\n* IP address (e.g. `1.1.1.1`)\n* CIDR range (e.g. `192.168.1.1/24`)\n\nIf no ports are specified in a rule, port 22 is allowed by default.\n\n### Identity\n\nSince we didn't pass any SSH host keys to sshgate earlier, it generated an ephemeral ED25519 host key on startup. For the server to have a persistent identity, generate an SSH keypair and set `host_key_paths` in the config file accordingly. You may set an ED25519, ECDSA, and RSA host key.\n\nFor example, to set the server's ED25519 identity:\n\n```\nssh-keygen -t ed25519 -f sshgate\n```\n\nAdd to the config:\n\n```json\n{\n  \"host_key_paths\": {\n    \"ed25519\": \"./sshgate\"\n  }\n}\n```\n\n## Tailscale Integration\n\nsshgate can join your Tailscale network (tailnet) directly like an appliance. You can configure the tailnet hostname and port in the JSON config:\n\n```json\n{\n  \"tsnet\": {\n    \"enabled\": true,\n    \"hostname\": \"sshgate\",\n    \"port\": 22\n  }\n}\n```\n\nTo have sshgate join your tailnet, create a `Linux server` device in the Tailscale console and copy the auth key. Export the value as `TS_AUTH_KEY` as an environment variable and start sshgate.\n\n```bash\nexport TS_AUTH_KEY=tskey-auth-xxx sshgate --config config.json\n```\n\nYou should see sshgate join the tailnet with the configured hostname and be able to SSH to it using any authorized key.\n\nUsing Tailscale's [tsnet](https://tailscale.com/kb/1244/tsnet) library, sshgate can authenticate clients by their Tailscale identity, eliminating the need for public key management in the config, while [app capabilities](https://tailscale.com/kb/1537/grants-app-capabilities) enable SSH rules to be directly configured inside the tailnet access controls.\n\nUse this tailnet policy file as a starting point. You'll need to attach the `sshgate` tag to the `sshgate` machine on your network after applying the policy.\n\n```json\n{\n  \"tagOwners\": {\n    \"tag:sshgate\": [\"autogroup:admin\"]\n  },\n\n  \"grants\": [\n    {\n      \"src\": [\"*\"],\n      \"dst\": [\"tag:sshgate\"],\n      \"ip\": [\"22\"],\n      \"app\": {\n        \"github.com/cedws/sshgate\": [\n          {\n            \"hosts\": [\"github.com\"],\n            \"ports\": [22]\n          }\n        ]\n      }\n    }\n  ]\n}\n```\n\nEnsure that the JSON under `\"github.com/cedws/sshgate\"` isn't malformed or clients won't be able to connect.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcedws%2Fsshgate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcedws%2Fsshgate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcedws%2Fsshgate/lists"}