tailssh.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build linux
  5. // +build linux
  6. // Package tailssh is an SSH server integrated into Tailscale.
  7. package tailssh
  8. import (
  9. "encoding/json"
  10. "fmt"
  11. "io"
  12. "net"
  13. "os"
  14. "os/exec"
  15. "syscall"
  16. "unsafe"
  17. "github.com/creack/pty"
  18. "github.com/gliderlabs/ssh"
  19. "inet.af/netaddr"
  20. "tailscale.com/envknob"
  21. "tailscale.com/ipn/ipnlocal"
  22. "tailscale.com/net/tsaddr"
  23. "tailscale.com/types/logger"
  24. )
  25. // TODO(bradfitz): this is all very temporary as code is temporarily
  26. // being moved around; it will be restructured and documented in
  27. // following commits.
  28. // Handle handles an SSH connection from c.
  29. func Handle(logf logger.Logf, lb *ipnlocal.LocalBackend, c net.Conn) error {
  30. sshd := &server{lb, logf}
  31. srv := &ssh.Server{
  32. Handler: sshd.handleSSH,
  33. RequestHandlers: map[string]ssh.RequestHandler{},
  34. SubsystemHandlers: map[string]ssh.SubsystemHandler{},
  35. ChannelHandlers: map[string]ssh.ChannelHandler{},
  36. }
  37. for k, v := range ssh.DefaultRequestHandlers {
  38. srv.RequestHandlers[k] = v
  39. }
  40. for k, v := range ssh.DefaultChannelHandlers {
  41. srv.ChannelHandlers[k] = v
  42. }
  43. for k, v := range ssh.DefaultSubsystemHandlers {
  44. srv.SubsystemHandlers[k] = v
  45. }
  46. keys, err := lb.GetSSHHostKeys()
  47. if err != nil {
  48. return err
  49. }
  50. for _, signer := range keys {
  51. srv.AddHostKey(signer)
  52. }
  53. srv.HandleConn(c)
  54. return nil
  55. }
  56. type server struct {
  57. lb *ipnlocal.LocalBackend
  58. logf logger.Logf
  59. }
  60. func (srv *server) handleSSH(s ssh.Session) {
  61. lb := srv.lb
  62. logf := srv.logf
  63. user := s.User()
  64. addr := s.RemoteAddr()
  65. logf("Handling SSH from %v for user %v", addr, user)
  66. ta, ok := addr.(*net.TCPAddr)
  67. if !ok {
  68. logf("tsshd: rejecting non-TCP addr %T %v", addr, addr)
  69. s.Exit(1)
  70. return
  71. }
  72. tanetaddr, ok := netaddr.FromStdIP(ta.IP)
  73. if !ok {
  74. logf("tsshd: rejecting unparseable addr %v", ta.IP)
  75. s.Exit(1)
  76. return
  77. }
  78. if !tsaddr.IsTailscaleIP(tanetaddr) {
  79. logf("tsshd: rejecting non-Tailscale addr %v", ta.IP)
  80. s.Exit(1)
  81. return
  82. }
  83. ptyReq, winCh, isPty := s.Pty()
  84. if !isPty {
  85. fmt.Fprintf(s, "TODO scp etc")
  86. s.Exit(1)
  87. return
  88. }
  89. srcIPP := netaddr.IPPortFrom(tanetaddr, uint16(ta.Port))
  90. node, uprof, ok := lb.WhoIs(srcIPP)
  91. if !ok {
  92. fmt.Fprintf(s, "Hello, %v. I don't know who you are.\n", srcIPP)
  93. s.Exit(0)
  94. return
  95. }
  96. allow := envknob.String("TS_SSH_ALLOW_LOGIN")
  97. if allow == "" || uprof.LoginName != allow {
  98. logf("ssh: access denied for %q (only allowing %q)", uprof.LoginName, allow)
  99. jnode, _ := json.Marshal(node)
  100. jprof, _ := json.Marshal(uprof)
  101. fmt.Fprintf(s, "Access denied.\n\nYou are node: %s\n\nYour profile: %s\n\nYou wanted %+v\n", jnode, jprof, ptyReq)
  102. s.Exit(1)
  103. return
  104. }
  105. var cmd *exec.Cmd
  106. sshUser := s.User()
  107. if os.Getuid() != 0 || sshUser == "root" {
  108. cmd = exec.Command("/bin/bash")
  109. } else {
  110. cmd = exec.Command("/usr/bin/env", "su", "-", sshUser)
  111. }
  112. cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
  113. f, err := pty.Start(cmd)
  114. if err != nil {
  115. logf("running shell: %v", err)
  116. s.Exit(1)
  117. return
  118. }
  119. defer f.Close()
  120. go func() {
  121. for win := range winCh {
  122. setWinsize(f, win.Width, win.Height)
  123. }
  124. }()
  125. go func() {
  126. io.Copy(f, s) // stdin
  127. }()
  128. io.Copy(s, f) // stdout
  129. cmd.Process.Kill()
  130. if err := cmd.Wait(); err != nil {
  131. s.Exit(1)
  132. }
  133. s.Exit(0)
  134. return
  135. }
  136. func setWinsize(f *os.File, w, h int) {
  137. syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
  138. uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
  139. }