* feat: Decide upstream based on user's unix group membership If a username is not defined, groupname is parsed. Check if the user is part of that group and route them to the associated host defined in the config file for the yaml plugin * style: fix formatting with gofmt * feat: Look up user groups only when groupname defined in yaml config * fix: inefficient assignment because of unused var * feat: fallback to next rule on group lookup failure Instead of failing on group lookup errors, the matcher now skips the groupname rule and proceeds to the next, eventually failing through to the catchall rule. * feat: test cases for group based routing in yaml plugin * Revert "feat: fallback to next rule on group lookup failure" This reverts commit 622ee9f1eb3157d04f57c068179bb74de8db3a1f. Handles the error returned by getUserGroups instead of ignoring it, to prevent potential runtime issues when user lookup fail * feat: Check if a user is known to the system before group lookup This will let the rule matching logic skip to the next pipe in the yaml config when a user is not found on the system. Note the variable name change from user to username to avoid ambiguity dur to name collision with os/user package. * feat: Avoid redundant user lookup * feat: Improve error handling for user and group lookup failures * feat: Use appropriate test user name for group routing |
||
|---|---|---|
| .github | ||
| cmd/sshpiperd | ||
| crypto@8682cc0c5a | ||
| e2e | ||
| libplugin | ||
| plugin | ||
| .devcontainer.json | ||
| .gitignore | ||
| .gitmodules | ||
| .goreleaser.yaml | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| entrypoint.sh | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| README.md | ||
sshpiper 🖇
sshpiper is the reverse proxy for sshd. all protocols, including ssh, scp, port forwarding, running on top of ssh are supported.
Note: this is v1 version, checkout legacy v0 here
Overview and Terminology
downstream: the client side, typically an ssh client.upstream: the server side, typically an ssh server.plugin: handles the routing fromdownstreamtoupstream. Thepluginis also responsible for mapping authentication methods to the upstream server. For example, the downstream may use password authentication, but the upstream server may receive public key authentication mapped bysshpiper.additional challenge: somepluginswill not only perform routing but also add additional challenges to SSH authentication for theupstreamserver. For example, thedownstreammay be asked for two-factor authentication provided by theplugin.
+---------+ +------------------+ +-----------------+
| | | | | |
| Bob +----ssh -l bob----+ | sshpiper +-------------> Bob' machine |
| | | | | | | |
+---------+ | | | | +-----------------+
+---> pipe-by-name--+ |
+---------+ | | | | +-----------------+
| | | | | | | |
| Alice +----ssh -l alice--+ | +-------------> Alice' machine |
| | | | | |
+---------+ +------------------+ +-----------------+
downstream sshpiper upstream
Quick start
Build
git clone https://github.com/tg123/sshpiper
cd sshpiper
git submodule update --init --recursive
mkdir out
go build -tags full -o out ./...
Run simple demo
start dummy sshd server
docker run -d -e USER_NAME=user -e USER_PASSWORD=pass -e PASSWORD_ACCESS=true -p 127.0.0.1:5522:2222 lscr.io/linuxserver/openssh-server
start sshpiperd with fixed plugin targeting the dummy sshd server
./out/sshpiperd -i /tmp/sshpiperkey --server-key-generate-mode notexist --log-level=trace ./out/fixed --target 127.0.0.1:5522
test ssh connection (password: pass)
ssh 127.0.0.1 -l user -p 2222
➕ math before login?
Here illustrates the example of addional challenge before the fixed plugin.
./out/sshpiperd -i /tmp/sshpiperkey --server-key-generate-mode notexist --log-level=trace ./out/simplemath -- ./out/fixed --target 127.0.0.1:5522
Plugins
icons
- 🔀: routing plugin
- 🔒: addtional challenge plugin
Plugin list
- workingdir 🔀:
/home-like directory to managed upstreams routing by sshpiped. - yaml 🔀: config routing with a single yaml file.
- docker 🔀: pipe into docker containers.
- kubernetes 🔀: manage pipes via Kubernetes CRD.
- azdevicecode 🔒: ask user to enter azure device code before login
- fixed 🔀: fixed targeting the dummy sshd server
- simplemath 🔒: ask for very simple math question before login, demo purpose
- githubapp 🔀: login ssh with your github account
- restful by @11notes 🔀🔒: The rest plugin for sshpiperd is a simple plugin that allows you to use a restful backend for authentication and challenge.
- failtoban 🔒: ban ip after failed login attempts
- openpubkey🔀🔒: integrate with openpubkey
Screening recording
asciicast
recording the screen in asciicast format https://docs.asciinema.org/manual/asciicast/v2/
To use it, start sshpiperd with --screen-recording-format asciicast and --screen-recording-dir /path/to/recordingdir
Example:
```
ssh user_name@
... do some commands
exit
asciinema play /path/to/recordingdir/<conn_guid>/shell-channel-0.cast
```
typescript
recording the screen in typescript format (not the lang). The format is compatible with scriptreplay(1)
To use it, start sshpiperd with --screen-recording-format typescript and --screen-recording-dir /path/to/recordingdir
Example:
```
ssh user_name@127.0.0.1 -p 2222
... do some commands
exit
$ cd /path/to/recordingdir/<conn_guid>
$ ls *.timing *.typescript
1472847798.timing 1472847798.typescript
$ scriptreplay -t 1472847798.timing 1472847798.typescript # will replay the ssh session
```
Public key authentication when using sshpiper (Private key remapping)
During SSH publickey auth, RFC 4252 Section 7,
ssh client sign session_id and some other data using private key into a signature sig.
This is for server to verify that the connection is from the client not the man in the middle.
However, sshpiper actually holds two ssh connection, and it is doing what the man in the middle does.
the two ssh connections' session_id will never be the same, because they are hash of the shared secret. RFC 4253 Section 7.2.
To support publickey auth, sshpiper routing plugin must provide a new private key for the upstream to sign the session_id. This new private key is called mapping key.
How this work
+------------+ +------------------------+
| | | |
| client | | sshpiper |
| PK_X +--------> | |
| | | v |
| | | Check Permission |
+------------+ | | |
| | |
| | | +----------------+
| v | | |
| sign again | | server |
| using PK_Y +--------------> check PK_Y |
| | | |
| | | |
+------------------------+ +----------------+
Ports to other platforms
Migrating from v0
What's the major change in v1
- low level sshpiper api is fully redesigned to support more routing protocols.
- plugins system totally redesigned to be more flexible and extensible.
- plugins are now sperated from main process and no longer a single big binary, this allow user to write their own plugins without touching
sshpiperdcode.
- plugins are now sperated from main process and no longer a single big binary, this allow user to write their own plugins without touching
grpcis first class now, the plugins are built on top of it
For plugins already in v1, you need change params to new params. However, not all plugins are migrated to v1 yet, they are being migrated gradually. you can still use the old plugins in v0 branch
Contributing
see CONTRIBUTING.md
License
MIT