feat: Add username-router plugin for SSHPiper to route connections based on username (#599)

* feat: Add username-router plugin for SSHPiper to route connections based on username

* feat: Add username-router plugin to GoReleaser configuration
This commit is contained in:
Boshi Lian 2025-05-25 04:20:21 -07:00 committed by GitHub
parent 706a36b40e
commit f56c4b67e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 0 deletions

View file

@ -0,0 +1,11 @@
# username-router plugin for sshpiper
Supports routing based on username. This plugin allows you to route connections to different targets based on the username provided during the SSH connection.
The username format is `target+username`, where `target` is the target host and `username` is the username to use for that target.
`target` can be an IP address or a hostname, and it can also include a port number in the format `target:port`.
## Usage
```
sshpiperd username-router
```

View file

@ -0,0 +1,59 @@
//go:build full || e2e
package main
import (
"fmt"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tg123/sshpiper/libplugin"
"github.com/urfave/cli/v2"
)
func parseTargetUser(raw string) (target string, username string, err error) {
// Expect format: [target:port]+user
parts := strings.SplitN(raw, "+", 2)
if len(parts) != 2 {
err = fmt.Errorf("invalid format (expected target:port+user)")
return
}
target = parts[0]
username = parts[1]
return
}
func main() {
libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
Name: "username-router",
Usage: "routing based on target inside username, format: 'target:port+realuser@sshpiper-host'",
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
return &libplugin.SshPiperPluginConfig{
PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) {
address, user, err := parseTargetUser(conn.User())
if err != nil {
return nil, fmt.Errorf("invalid username format %q: %w", conn.User(), err)
}
host, port, err := libplugin.SplitHostPortForSSH(address)
if err != nil {
return nil, fmt.Errorf("invalid target address %q: %w", address, err)
}
log.Info("routing to address ", address, " with user ", user)
return &libplugin.Upstream{
UserName: user,
Host: host,
Port: int32(port),
IgnoreHostKey: true,
Auth: libplugin.CreatePasswordAuth(password),
}, nil
},
}, nil
},
})
}