Add the ability to specify multiple proxy hosts
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Elara 2024-08-12 17:20:14 -07:00
parent 1d292ec21a
commit e7994824a5
3 changed files with 61 additions and 13 deletions

View File

@ -66,7 +66,7 @@ See the [serial](https://gitea.elara.ws/Elara6331/seashell/wiki/Backends#serial)
Seashell can proxy another SSH server. In this case, your client will authenticate to seashell and then seashell will authenticate to the target server, so you should provide seashell with a private key to use for authentication and encryption. If you don't provide this, seashell will ask the authenticating user for the target server's password. Seashell can proxy another SSH server. In this case, your client will authenticate to seashell and then seashell will authenticate to the target server, so you should provide seashell with a private key to use for authentication and encryption. If you don't provide this, seashell will ask the authenticating user for the target server's password.
The proxy backend takes no extra arguments, so the `ssh` command only requires your username and the routing path: Here's an example command:
```bash ```bash
ssh user:myproxy@ssh.example.com ssh user:myproxy@ssh.example.com

View File

@ -27,6 +27,8 @@ import (
"io" "io"
"net" "net"
"os" "os"
"path"
"strconv"
"strings" "strings"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
@ -41,8 +43,8 @@ import (
// proxySettings represents settings for the proxy backend. // proxySettings represents settings for the proxy backend.
type proxySettings struct { type proxySettings struct {
Server string `cty:"server"` Host *string `cty:"host"`
Port *uint `cty:"port"` Hosts *cty.Value `cty:"hosts"`
User *string `cty:"user"` User *string `cty:"user"`
PrivkeyPath *string `cty:"privkey"` PrivkeyPath *string `cty:"privkey"`
UserMap *cty.Value `cty:"user_map"` UserMap *cty.Value `cty:"user_map"`
@ -78,14 +80,51 @@ func Proxy(route config.Route) router.Handler {
opts.User = &user.Name opts.User = &user.Name
} }
} }
if opts.Port == nil { var matched bool
port := uint(22) var addr, portstr string
opts.Port = &port if opts.Host == nil {
hosts := ctyTupleToStrings(opts.Hosts)
if len(hosts) == 0 {
return errors.New("no host configuration provided")
}
for _, hostPattern := range hosts {
addr, portstr, ok = strings.Cut(hostPattern, ":")
if !ok {
// addr is already set by the above statement, so just set the default port
portstr = "22"
}
matched, err = path.Match(addr, arg)
if err != nil {
return err
}
if matched {
addr = arg
break
}
}
} else {
addr, portstr, ok = strings.Cut(*opts.Host, ":")
if !ok {
// addr is already set by the above statement, so just set the default port
portstr = "22"
}
}
if !matched {
return errors.New("provided argument doesn't match any host patterns in configuration")
}
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
return err
} }
auth := goph.Auth{ auth := goph.Auth{
gossh.PasswordCallback(requestPassword(opts, sess)), gossh.PasswordCallback(requestPassword(opts, sess, addr)),
} }
if opts.PrivkeyPath != nil { if opts.PrivkeyPath != nil {
@ -105,8 +144,8 @@ func Proxy(route config.Route) router.Handler {
c, err := goph.NewConn(&goph.Config{ c, err := goph.NewConn(&goph.Config{
Auth: auth, Auth: auth,
User: *opts.User, User: *opts.User,
Addr: opts.Server, Addr: addr,
Port: *opts.Port, Port: uint(port),
Callback: func(host string, remote net.Addr, key gossh.PublicKey) error { Callback: func(host string, remote net.Addr, key gossh.PublicKey) error {
found, err := goph.CheckKnownHost(host, remote, key, "") found, err := goph.CheckKnownHost(host, remote, key, "")
if !found { if !found {
@ -173,9 +212,9 @@ func Proxy(route config.Route) router.Handler {
} }
// requestPassword asks the client for the remote server's password // requestPassword asks the client for the remote server's password
func requestPassword(opts proxySettings, sess ssh.Session) func() (secret string, err error) { func requestPassword(opts proxySettings, sess ssh.Session, addr string) func() (secret string, err error) {
return func() (secret string, err error) { return func() (secret string, err error) {
_, err = fmt.Fprintf(sess.Stderr(), "Password for %s@%s: ", *opts.User, opts.Server) _, err = fmt.Fprintf(sess.Stderr(), "Password for %s@%s: ", *opts.User, addr)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -20,7 +20,16 @@ route "srv" {
backend = "proxy" backend = "proxy"
match = "srv" match = "srv"
settings = { settings = {
server = "1.2.3.4" host = "1.2.3.4"
privkey = "/home/elara/.ssh/id_ed25519"
}
}
route "cluster" {
backend = "proxy"
match = "cluster\\.(.+)"
settings = {
hosts = ["node*", "nas", "192.168.1.*"]
privkey = "/home/elara/.ssh/id_ed25519" privkey = "/home/elara/.ssh/id_ed25519"
} }
} }