tg123/sshpiper

[feature request] Support different upstream keys (and maybe upstream hosts) based on the connection key

callumgare opened this issue · 13 comments

Sorry if this has already been requested and I missed it. Also this might not be possible, I don't know enough about how ssh works, but I thought I may as well ask :) I'd love to be able to use a different key for connecting to the upstream server based on what key was used to connect to sshpiper.

One use case for this is with GitLab. I have sshpiper in front of a GitLab instance and way ssh authentication works in GitLab is that each user uploads their public key in the GitLab UI and GitLab uses the connection key to tell which user has connected rather than the connecting username (the same username is used for all users, by default "git"). Since sshpiper can currently only be configured to use one upstream ssh key per username it's currently not possible to support multiple users in GitLab with sshpiper in a way that is constant with the GitLab UI.
Screen Shot 2022-05-25 at 10 57 13

Another use case where this would be useful is with borg backup which uses an ssh connection for a client to connect to a borg server, typically running under a user account named "borg". To ensure the connection can't be abused for other things it is highly recommended to append command="borg serve --restrict-to-path /repos/name-of-repo",restrict to each authorized_keys entry for the borg user on the borg server. Ideally you'd have multiple authorized keys for each client that are each restrict to their own repo (specified by the /repos/name-of-repo part of the above key prefix). At the moment since sshpiper currently only lets you configure one upstream key per user I have to use /repos instead which means all connecting borg clients can read all available repos (a term borg uses to refer to as a set of backups for a client). An alternative is to have muliple sshpiper users per borg client (e.g. "borg-client1", "borg-client2", etc) which each maps to a different authorized_key entry on the borg server. This works but it would be nice to have sshpiper be "transparent" and just setup borg in the standard way, connecting via the borg username.

I'm using the working Working Directory and the way I'm imaging this might work is by supporting sub-directories inside each user directory, inside of which would be all the normal config that's available at the top-level of a username config directory. E.g.

├── gitlab
│   └── user1
│   │   └── authorized_keys
│   │   └── id_ed25519
│   │   └── id_ed25519.pub
│   │   └── sshpiper_upstream
│   └── user2
│   │   └── authorized_keys
│   │   └── id_ed25519
│   │   └── id_ed25519.pub
│   │   └── sshpiper_upstream
│   └── authorized_keys
│   └── id_ed25519
│   └── id_ed25519.pub
│   └── sshpiper_upstream
└── linode
    └── authorized_keys
    └── id_ed25519
    └── id_ed25519.pub
    └── sshpiper_upstream

sshpiper would look into any available sub-directories under the connecting username directory and would check to see if any of them have a match in their authorized_keys. If there are no matching sub-directories any config at the top level of the username directory would be used as a fallback/behave as it currently does today. This should hopefully be fairly backwards comparable. The names of the sub-directories (in the example above, "user1" and "user2") would be arbitrary as far as sshpiper is concerned, just named by whatever makes the most sense for the person managing the config to keep track of the different sets of configurations. sshpiper would purely use the authorized_keys file to find a matching config set.

Thanks for your consideration!

tg123 commented

is grpc upstream good for you? it can be fully customized

Where would I find the documentation for that? The link in the readme is 404’d

tg123 commented

here is a grpc example https://github.com/tg123/sshpiper/blob/master/sshpiperd/upstream/grpcupstream/simpleserver/impl.go

but I noticed findupstream does not support routing by key at the moment.
I revamp it and add this feature.

please allow me some weekends

Thanks for that! To be honest grpc probably seems a lot more complex than I'm looking for with my setup. The file based driver is easy for me to maintain so I'll probably stick with that.

If you're not interested in supporting key based routing with the file driver that's completely understandable though. Just thought I'd ask. Thanks for making such a great project! 😊

tg123 commented

still working on this
need few more weekends, a big code refactor

tg123 commented

hi @callumgare

here is preview version at https://github.com/tg123/sshpiper/tree/v1
you can build and test, looking forward to see your feedback

note: only yaml and working dir works in preview version
start with --upstream-workingdir-matchpublickeyinsubdir

here is my test folder structure

├── git
│   ├── bitbucket
│   │   └── sshpiper_upstream
│   ├── github
│   │   ├── authorized_keys
│   │   ├── id_rsa
│   │   └── sshpiper_upstream
│   └── gitlab
│       └── sshpiper_upstream

ssh git@sshpiper will search across all sub

the reforge took couple weekends and rewrite most of code to support even more flexible scenerio.
Thanks for the idea

Amazing! Thanks for your had work with that!

Are there instructions for building the docker image (I run sshpiper using the docker image from https://hub.docker.com/r/farmer1992/sshpiperd)? When I try just running docker build . on my x86 home server running proxmox (basically Debian) I get errors:
Screen Shot 2022-07-02 at 15 33 10

tg123 commented
git clone https://github.com/tg123/sshpiper
cd sshpiper
git submodule update --init --recursive
docker build .

Ta. It looks like it's now checking for config in subdirectories which is great but it also seems to be ignoring the top level directory now. Rather than matching the username that is used when connecting to sshpiper with the name of a directory in the top-level, it now seems to ignore the username and check all directories.

For example if I'm connecting to sshpiper from a computer who's ssh public key is in both authorized_keys files in the below example then even if I'm connecting with ssh borg@sshpiper-server the borg username appears to be ignored so if sshpiper checks /git/github/authorized_keys before it checks /borg/backup-server/authorized_keys then it'll connect to /git/github/sshpiper_upstream.

├── git
│   └── github
│       ├── authorized_keys
│       ├── id_rsa
│       └── sshpiper_upstream
└── borg
    └── backup-server
        ├── authorized_keys
        ├── id_rsa
        └── sshpiper_upstream

I'm using /sshpiperd daemon --upstream-workingdir-matchpublickeyinsubdir as the command for the docker container running that uses the image build from the v1 branch.

tg123 commented

good to know it works
anyway seems a bug, I am working on rewriting the plugin system to fit new api
allow me some time to fix it

No worries! Thanks :)

tg123 commented

fixed your bug in https://github.com/tg123/sshpiper/tree/v1plugin/plugin/workingdirbykey

note I am not still not finishing the doc of v1,
but you can test the new plugin based system

here is your plugin

# build plugin/workingdirbykey
cd plugin/workingdirbykey
go build

# in cmd/sshpiperd
go build
./sshpiperd --log-level trace -i ssh_host_rsa_key ../../plugin/workingdirbykey/workingdirbykey --root <your dir>

I am working on more plugins

Sorry I'm just replying to this now. Looks like it works perfectly! Thanks again ❤️