Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 70788ba261 | |||
| e7994824a5 | 
| @@ -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. | ||||
|  | ||||
| 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 | ||||
| ssh user:myproxy@ssh.example.com | ||||
|   | ||||
| @@ -27,6 +27,8 @@ import ( | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gliderlabs/ssh" | ||||
| @@ -41,8 +43,8 @@ import ( | ||||
|  | ||||
| // proxySettings represents settings for the proxy backend. | ||||
| type proxySettings struct { | ||||
| 	Server      string     `cty:"server"` | ||||
| 	Port        *uint       `cty:"port"` | ||||
| 	Host        *string    `cty:"host"` | ||||
| 	Hosts       *cty.Value `cty:"hosts"` | ||||
| 	User        *string    `cty:"user"` | ||||
| 	PrivkeyPath *string    `cty:"privkey"` | ||||
| 	UserMap     *cty.Value `cty:"user_map"` | ||||
| @@ -53,9 +55,6 @@ type proxySettings struct { | ||||
| func Proxy(route config.Route) router.Handler { | ||||
| 	return func(sess ssh.Session, arg string) error { | ||||
| 		user, _ := sshctx.GetUser(sess.Context()) | ||||
| 		if !route.Permissions.IsAllowed(user, "*") { | ||||
| 			return router.ErrUnauthorized | ||||
| 		} | ||||
|  | ||||
| 		var opts proxySettings | ||||
| 		err := gocty.FromCtyValue(route.Settings, &opts) | ||||
| @@ -78,14 +77,56 @@ func Proxy(route config.Route) router.Handler { | ||||
| 				opts.User = &user.Name | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		if opts.Port == nil { | ||||
| 			port := uint(22) | ||||
| 			opts.Port = &port | ||||
|  | ||||
| 		matched := false | ||||
| 		addr := arg | ||||
| 		var portstr, pattern string | ||||
| 		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{ | ||||
| 			gossh.PasswordCallback(requestPassword(opts, sess)), | ||||
| 			gossh.PasswordCallback(requestPassword(opts, sess, addr)), | ||||
| 		} | ||||
|  | ||||
| 		if opts.PrivkeyPath != nil { | ||||
| @@ -105,8 +146,8 @@ func Proxy(route config.Route) router.Handler { | ||||
| 		c, err := goph.NewConn(&goph.Config{ | ||||
| 			Auth: auth, | ||||
| 			User: *opts.User, | ||||
| 			Addr: opts.Server, | ||||
| 			Port: *opts.Port, | ||||
| 			Addr: addr, | ||||
| 			Port: uint(port), | ||||
| 			Callback: func(host string, remote net.Addr, key gossh.PublicKey) error { | ||||
| 				found, err := goph.CheckKnownHost(host, remote, key, "") | ||||
| 				if !found { | ||||
| @@ -173,9 +214,9 @@ func Proxy(route config.Route) router.Handler { | ||||
| } | ||||
|  | ||||
| // 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) { | ||||
| 		_, 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 { | ||||
| 			return "", err | ||||
| 		} | ||||
|   | ||||
| @@ -20,7 +20,16 @@ route "srv" { | ||||
|     backend = "proxy" | ||||
|     match = "srv" | ||||
|     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" | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user