summaryrefslogtreecommitdiffhomepage
path: root/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'ssh')
-rw-r--r--ssh/tailssh/tailssh.go33
-rw-r--r--ssh/tailssh/tailssh_test.go47
2 files changed, 49 insertions, 31 deletions
diff --git a/ssh/tailssh/tailssh.go b/ssh/tailssh/tailssh.go
index cffd7016b..add5a8d61 100644
--- a/ssh/tailssh/tailssh.go
+++ b/ssh/tailssh/tailssh.go
@@ -87,7 +87,7 @@ func init() {
// HandleSSHConn handles a Tailscale SSH connection from c.
func (srv *server) HandleSSHConn(c net.Conn) error {
- ss, err := srv.newSSHServer()
+ ss, err := srv.newSSHServer(c)
if err != nil {
return err
}
@@ -109,8 +109,28 @@ func (srv *server) OnPolicyChange() {
}
}
-func (srv *server) newSSHServer() (*ssh.Server, error) {
+func (srv *server) newSSHServer(c net.Conn) (*ssh.Server, error) {
ss := &ssh.Server{
+ ServerConfigCallback: func(ctx ssh.Context) *gossh.ServerConfig {
+ conf := &gossh.ServerConfig{
+ NoClientAuth: true,
+ NoClientAuthCallback: func(m gossh.ConnMetadata) (*gossh.Permissions, error) {
+ if srv.requiresPubKey(m.User(), toIPPort(m.LocalAddr()), toIPPort(m.RemoteAddr())) {
+ return nil, errors.New("public key required") // any non-nil error will do
+ }
+ return nil, nil
+ },
+ BannerCallback: func(m gossh.ConnMetadata) string {
+ // TODO(bradfitz): make this be a "you are rejected, contact
+ // your Tailnet admin etc etc" message or or the
+ // SSHAction.Message from the rejecting SSHAction if
+ // matched.
+ return "# Tailscale SSH server\n"
+ },
+ }
+ return conf
+ },
+
Handler: srv.handleSSH,
RequestHandlers: map[string]ssh.RequestHandler{},
SubsystemHandlers: map[string]ssh.SubsystemHandler{},
@@ -122,12 +142,9 @@ func (srv *server) newSSHServer() (*ssh.Server, error) {
},
Version: "SSH-2.0-Tailscale",
LocalPortForwardingCallback: srv.mayForwardLocalPortTo,
- NoClientAuthCallback: func(m gossh.ConnMetadata) (*gossh.Permissions, error) {
- if srv.requiresPubKey(m.User(), toIPPort(m.LocalAddr()), toIPPort(m.RemoteAddr())) {
- return nil, errors.New("public key required") // any non-nil error will do
- }
- return nil, nil
- },
+
+ // TODO(bradfitz,maisem): don't register this hook if the policy doesn't
+ // involve any pubkey stuff at all for the user identified by c.
PublicKeyHandler: func(ctx ssh.Context, key ssh.PublicKey) bool {
if srv.acceptPubKey(ctx.User(), toIPPort(ctx.LocalAddr()), toIPPort(ctx.RemoteAddr()), key) {
srv.logf("accepting SSH public key %s", bytes.TrimSpace(gossh.MarshalAuthorizedKey(key)))
diff --git a/ssh/tailssh/tailssh_test.go b/ssh/tailssh/tailssh_test.go
index 7fed58c05..09d6ed6f6 100644
--- a/ssh/tailssh/tailssh_test.go
+++ b/ssh/tailssh/tailssh_test.go
@@ -211,34 +211,11 @@ func TestSSH(t *testing.T) {
dir := t.TempDir()
lb.SetVarRoot(dir)
- srv := &server{
- lb: lb,
- logf: logf,
- }
- ss, err := srv.newSSHServer()
- if err != nil {
- t.Fatal(err)
- }
-
u, err := user.Current()
if err != nil {
t.Fatal(err)
}
- ci := &sshConnInfo{
- sshUser: "test",
- src: netaddr.MustParseIPPort("1.2.3.4:32342"),
- dst: netaddr.MustParseIPPort("1.2.3.5:22"),
- node: &tailcfg.Node{},
- uprof: &tailcfg.UserProfile{},
- }
-
- ss.Handler = func(s ssh.Session) {
- ss := srv.newSSHSession(s, ci, u)
- ss.action = &tailcfg.SSHAction{Accept: true}
- ss.run()
- }
-
ln, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
@@ -255,6 +232,30 @@ func TestSSH(t *testing.T) {
}
return
}
+
+ srv := &server{
+ lb: lb,
+ logf: logf,
+ }
+ ss, err := srv.newSSHServer(c)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ ci := &sshConnInfo{
+ sshUser: "test",
+ src: netaddr.MustParseIPPort("1.2.3.4:32342"),
+ dst: netaddr.MustParseIPPort("1.2.3.5:22"),
+ node: &tailcfg.Node{},
+ uprof: &tailcfg.UserProfile{},
+ }
+
+ ss.Handler = func(s ssh.Session) {
+ ss := srv.newSSHSession(s, ci, u)
+ ss.action = &tailcfg.SSHAction{Accept: true}
+ ss.run()
+ }
go ss.HandleConn(c)
}
}()