Closed arkan closed 2 years ago
How would you expect authentication to work on the second leg? We don't have access to the private key used for the initial leg, so we can't use the same key for the second. We can use the same username, but maybe a single shared keypair for all bastion connections...
@progrium Thanks for the reply!
In this kind of scenario, the Bastion would know the private keys to connect to the servers. The bastion prevents to share private keys with users, and only the bastion knows them.
Maybe https://github.com/kennylevinsen/sshmuxd could help?
@progrium I just published a poc which implements a basic poc of a SSH bastion using this SSH package. I had to tweak a bit the package to export a new field. I'm not satisfied the way it's implemented but before spending some time on it I'd like to know if this is something you'd like to support in this package? If so, would you have any idea on how to achieve this?
Source: https://github.com/arkan/bastion and the relevant commit about the change is https://github.com/arkan/bastion/commit/bab08c812d8a1e021d753184c9338251c76c6ea6
Thanks!
@arkan very nice, I tried to find a solution without modifying gliderlabs/ssh
, your update is generic and working like a charm
@arkan cool, but can you explain your patch? You're just exposing arbitrary requests?
When the channel creation is accepted two values are returned: the channel, but also a Go chan containing SSH requests.
This Go chan is consumed by the handleRequests method and there is currently no way to export those gossh.Request
messages outside of this package.
This is yet required in my usecase to implement a SSH bastion. I need to have access to the 2 SSH channels as well as the two Go chans to get SSH requests.
Then the Bastion does:
I also implemented a TTyRec adapter but this is not useful in our usecase :)
Regarding a possible implementation, I could maybe add a Requests(chan *gossh)
method to the Session
interface.
And then the implementation is pretty straightforward, and will only forward request if the Go chan is specified.
It will become the caller responsibility to close this channel.
What do you think? Does it make sense for you?
That makes sense. It's a reasonable approach.
Though also, and this is more work, but what do you think of a whole different interface for writing a bastion? I've started a sort of lower level generic abstraction for different kinds of handlers different from a session handler. Perhaps something more appropriate for making a gateway/bastion could be made separate from the session handler?
I'm thinking out loud. The Session interface is already quite big and I'm guessing you aren't using a lot of it? I generally like the idea of more specialized interfaces because that also implies the types of things you can build with the library.
I like the idea. This is a good approach especially because exposing raw information is only useful for specific purposes. Bastion is one of them.
So if I correctly understood, your idea would to have something like this? (naming left aside)
type BastionSession interface {
Session
Requests() chan *gossh.Request
}
...
ssh.HandleBastion(func(sess ssh.BastionSession) {
// Bastion implementation goes here
})
Is that what you were thinking about?
Something like this. One question is how much of the Session interface do you use to make a bastion? If you don't use all of it maybe we can disaggregate it into smaller interfaces that this and Session share.
I'm not sure exposing it like this would be the best method. If a server doesn't currently use MaskedReqs, when the buffered channel is full, any new requests will hang and not be handled. It's a little ugly, but possibly adding a callback would be better (HandleChannelReques or something like that)... this means if the user doesn't define it, we can just discard the request.
This does additionally start a discussion on what we should do with requests we already handle internally but the user may want to see (such as window-change)... since these shouldn't be replied to twice.
Also, I'm not sure we're currently handling all messages properly (we should be replying with false to any message we don't recognize/isn't handled).
One question is how much of the Session interface do you use to make a bastion
I clearly don't use all of them.
If you don't use all of it maybe we can disaggregate it into smaller interfaces that this and Session share
Maybe this is something that can be done as a second step? (albeit I agree with you on the concept)
Also, I'm not sure we're currently handling all messages properly (we should be replying with false to any message we don't recognize/isn't handled).
I think (and correctly if I'm wrong @belak) that the replies calls should only be made if req.WantReply
is set to true?
I thought I fixed the message reply issue, but it looks like there are some branches I haven't merged. I'll need to clean that up before we continue.
Second step for the deeper stuff could work. I realize I haven't merged some of the underlying refactoring for channel type handlers. But to avoid making APIs we remove, you should duplicate the methods of Session into your new interface that you use.
I think (and correctly if I'm wrong @belak) that the replies calls should only be made if
req.WantReply
is set to true?
Yep. Though, I think crypto/ssh handles that automatically if you always Reply
.
Hey, just released this https://github.com/moul/sshportal
(I first began to work on this when I read this thread :p)
This looks awesome @moul !
I'll check this in detail - Thanks :)
FYI, I fixed the version of gliderlabs (https://github.com/gliderlabs/ssh/tree/0c9c3575f476a0c2779aafb89d1838ca4ab7ac16 2 commits in the past), and cherry-picked https://github.com/arkan/bastion/commit/bab08c812d8a1e021d753184c9338251c76c6ea6
Will try to switch to newest gliderlabs/ssh
as soon as we choose a solution
Hey everybody, is this issue actionable?
@moul maybe you can make another issue to track anything that needs to be resolved upstream here?
@moul maybe you can make another issue to track anything that needs to be resolved upstream here?
Yep, no problem, will do it now
I think this issue can be closed, as there is an example - sshportal - for bastion servers. If we want something simpler as an example, that could maybe be an addition, but I believe the above works fine?
@progrium Do you think a section for "projects using gliderlabs/ssh" would suffice for for closing? I can PR that if so.
@josegonzalez sounds good
Hi,
Is there any example of SSH bastion created with this package?
Basically I'd like to create a SSH server accepting connections, verifying the identity with Public SSH keys, and then forwarding the connections to another server.
Since I believe this is a pretty standard need, it could be interesting to add such an example in the repo.
Thanks