Compare commits

..

No commits in common. "master" and "v0.0.1" have entirely different histories.

3 changed files with 16 additions and 66 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.
Here's an example command: The proxy backend takes no extra arguments, so the `ssh` command only requires your username and the routing path:
```bash ```bash
ssh user:myproxy@ssh.example.com ssh user:myproxy@ssh.example.com

View File

@ -27,8 +27,6 @@ import (
"io" "io"
"net" "net"
"os" "os"
"path"
"strconv"
"strings" "strings"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
@ -43,8 +41,8 @@ import (
// proxySettings represents settings for the proxy backend. // proxySettings represents settings for the proxy backend.
type proxySettings struct { type proxySettings struct {
Host *string `cty:"host"` Server string `cty:"server"`
Hosts *cty.Value `cty:"hosts"` Port *uint `cty:"port"`
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"`
@ -55,6 +53,9 @@ type proxySettings struct {
func Proxy(route config.Route) router.Handler { func Proxy(route config.Route) router.Handler {
return func(sess ssh.Session, arg string) error { return func(sess ssh.Session, arg string) error {
user, _ := sshctx.GetUser(sess.Context()) user, _ := sshctx.GetUser(sess.Context())
if !route.Permissions.IsAllowed(user, "*") {
return router.ErrUnauthorized
}
var opts proxySettings var opts proxySettings
err := gocty.FromCtyValue(route.Settings, &opts) err := gocty.FromCtyValue(route.Settings, &opts)
@ -77,56 +78,14 @@ func Proxy(route config.Route) router.Handler {
opts.User = &user.Name opts.User = &user.Name
} }
} }
matched := false if opts.Port == nil {
addr := arg port := uint(22)
var portstr, pattern 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 {
pattern, 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(pattern, 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 !route.Permissions.IsAllowed(user, addr) {
return router.ErrUnauthorized
}
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, addr)), gossh.PasswordCallback(requestPassword(opts, sess)),
} }
if opts.PrivkeyPath != nil { if opts.PrivkeyPath != nil {
@ -146,8 +105,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: addr, Addr: opts.Server,
Port: uint(port), Port: *opts.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 {
@ -214,9 +173,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, addr string) func() (secret string, err error) { func requestPassword(opts proxySettings, sess ssh.Session) 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, addr) _, err = fmt.Fprintf(sess.Stderr(), "Password for %s@%s: ", *opts.User, opts.Server)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -20,16 +20,7 @@ route "srv" {
backend = "proxy" backend = "proxy"
match = "srv" match = "srv"
settings = { settings = {
host = "1.2.3.4" server = "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"
} }
} }