mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
ipn/ipnlocal: push down a user-specific root dir to peerapi handler
And add a put handler. Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
662fbd4a09
commit
35596ae5ce
@ -11,6 +11,7 @@
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
|
"tailscale.com/paths"
|
||||||
"tailscale.com/portlist"
|
"tailscale.com/portlist"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/empty"
|
"tailscale.com/types/empty"
|
||||||
@ -1448,7 +1450,22 @@ func (b *LocalBackend) initPeerAPIListener() {
|
|||||||
}
|
}
|
||||||
b.peerAPIListeners = nil
|
b.peerAPIListeners = nil
|
||||||
|
|
||||||
if len(b.netMap.Addresses) == 0 || b.netMap.SelfNode == nil {
|
selfNode := b.netMap.SelfNode
|
||||||
|
if len(b.netMap.Addresses) == 0 || selfNode == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stateFile := paths.DefaultTailscaledStateFile()
|
||||||
|
if stateFile == "" {
|
||||||
|
b.logf("peerapi disabled; no state directory")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
baseDir := fmt.Sprintf("%s-uid-%d",
|
||||||
|
strings.ReplaceAll(b.activeLogin, "@", "-"),
|
||||||
|
selfNode.User)
|
||||||
|
dir := filepath.Join(filepath.Dir(stateFile), "files", baseDir)
|
||||||
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
|
b.logf("peerapi disabled; error making directory: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1458,16 +1475,23 @@ func (b *LocalBackend) initPeerAPIListener() {
|
|||||||
tunName, _ = tunDev.Name()
|
tunName, _ = tunDev.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ps := &peerAPIServer{
|
||||||
|
b: b,
|
||||||
|
rootDir: dir,
|
||||||
|
tunName: tunName,
|
||||||
|
selfNode: selfNode,
|
||||||
|
}
|
||||||
|
|
||||||
for _, a := range b.netMap.Addresses {
|
for _, a := range b.netMap.Addresses {
|
||||||
ln, err := peerAPIListen(a.IP, b.prevIfState, tunName)
|
ln, err := ps.listen(a.IP, b.prevIfState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.logf("[unexpected] peerAPI listen(%q) error: %v", a.IP, err)
|
b.logf("[unexpected] peerAPI listen(%q) error: %v", a.IP, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pln := &peerAPIListener{
|
pln := &peerAPIListener{
|
||||||
ln: ln,
|
ps: ps,
|
||||||
lb: b,
|
ln: ln,
|
||||||
selfNode: b.netMap.SelfNode,
|
lb: b,
|
||||||
}
|
}
|
||||||
pln.urlStr = "http://" + net.JoinHostPort(a.IP.String(), strconv.Itoa(pln.Port()))
|
pln.urlStr = "http://" + net.JoinHostPort(a.IP.String(), strconv.Itoa(pln.Port()))
|
||||||
|
|
||||||
|
@ -13,8 +13,13 @@
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
@ -23,7 +28,14 @@
|
|||||||
|
|
||||||
var initListenConfig func(*net.ListenConfig, netaddr.IP, *interfaces.State, string) error
|
var initListenConfig func(*net.ListenConfig, netaddr.IP, *interfaces.State, string) error
|
||||||
|
|
||||||
func peerAPIListen(ip netaddr.IP, ifState *interfaces.State, tunIfName string) (ln net.Listener, err error) {
|
type peerAPIServer struct {
|
||||||
|
b *LocalBackend
|
||||||
|
rootDir string
|
||||||
|
tunName string
|
||||||
|
selfNode *tailcfg.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net.Listener, err error) {
|
||||||
ipStr := ip.String()
|
ipStr := ip.String()
|
||||||
|
|
||||||
var lc net.ListenConfig
|
var lc net.ListenConfig
|
||||||
@ -31,7 +43,7 @@ func peerAPIListen(ip netaddr.IP, ifState *interfaces.State, tunIfName string) (
|
|||||||
// On iOS/macOS, this sets the lc.Control hook to
|
// On iOS/macOS, this sets the lc.Control hook to
|
||||||
// setsockopt the interface index to bind to, to get
|
// setsockopt the interface index to bind to, to get
|
||||||
// out of the network sandbox.
|
// out of the network sandbox.
|
||||||
if err := initListenConfig(&lc, ip, ifState, tunIfName); err != nil {
|
if err := initListenConfig(&lc, ip, ifState, s.tunName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||||
@ -67,10 +79,10 @@ func peerAPIListen(ip netaddr.IP, ifState *interfaces.State, tunIfName string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
type peerAPIListener struct {
|
type peerAPIListener struct {
|
||||||
ln net.Listener
|
ps *peerAPIServer
|
||||||
lb *LocalBackend
|
ln net.Listener
|
||||||
urlStr string
|
lb *LocalBackend
|
||||||
selfNode *tailcfg.Node
|
urlStr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pln *peerAPIListener) Port() int {
|
func (pln *peerAPIListener) Port() int {
|
||||||
@ -112,7 +124,8 @@ func (pln *peerAPIListener) serve() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
h := &peerAPIHandler{
|
h := &peerAPIHandler{
|
||||||
isSelf: pln.selfNode.User == peerNode.User,
|
ps: pln.ps,
|
||||||
|
isSelf: pln.ps.selfNode.User == peerNode.User,
|
||||||
remoteAddr: ipp,
|
remoteAddr: ipp,
|
||||||
peerNode: peerNode,
|
peerNode: peerNode,
|
||||||
peerUser: peerUser,
|
peerUser: peerUser,
|
||||||
@ -145,6 +158,7 @@ func (l *oneConnListener) Close() error { return nil }
|
|||||||
|
|
||||||
// peerAPIHandler serves the Peer API for a source specific client.
|
// peerAPIHandler serves the Peer API for a source specific client.
|
||||||
type peerAPIHandler struct {
|
type peerAPIHandler struct {
|
||||||
|
ps *peerAPIServer
|
||||||
remoteAddr netaddr.IPPort
|
remoteAddr netaddr.IPPort
|
||||||
isSelf bool // whether peerNode is owned by same user as this node
|
isSelf bool // whether peerNode is owned by same user as this node
|
||||||
peerNode *tailcfg.Node // peerNode is who's making the request
|
peerNode *tailcfg.Node // peerNode is who's making the request
|
||||||
@ -152,7 +166,15 @@ type peerAPIHandler struct {
|
|||||||
lb *LocalBackend
|
lb *LocalBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *peerAPIHandler) logf(format string, a ...interface{}) {
|
||||||
|
h.ps.b.logf("peerapi: "+format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasPrefix(r.URL.Path, "/v0/put/") {
|
||||||
|
h.put(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
who := h.peerUser.DisplayName
|
who := h.peerUser.DisplayName
|
||||||
fmt.Fprintf(w, `<html>
|
fmt.Fprintf(w, `<html>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@ -165,3 +187,56 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprintf(w, "<p>You are the owner of this node.\n")
|
fmt.Fprintf(w, "<p>You are the owner of this node.\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !h.isSelf {
|
||||||
|
http.Error(w, "not owner", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Method != "PUT" {
|
||||||
|
http.Error(w, "not method PUT", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.ps.rootDir == "" {
|
||||||
|
http.Error(w, "no rootdir", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := path.Base(r.URL.Path)
|
||||||
|
if name == "." || name == "/" {
|
||||||
|
http.Error(w, "bad filename", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileBase := strings.ReplaceAll(url.PathEscape(name), ":", "%3a")
|
||||||
|
dstFile := filepath.Join(h.ps.rootDir, fileBase)
|
||||||
|
f, err := os.Create(dstFile)
|
||||||
|
if err != nil {
|
||||||
|
h.logf("put Create error: %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var success bool
|
||||||
|
defer func() {
|
||||||
|
if !success {
|
||||||
|
os.Remove(dstFile)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
n, err := io.Copy(f, r.Body)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
h.logf("put Copy error: %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
h.logf("put Close error: %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.logf("put(%q): %d bytes from %v/%v", name, n, h.remoteAddr.IP, h.peerNode.ComputedName)
|
||||||
|
|
||||||
|
// TODO: set modtime
|
||||||
|
// TODO: some real response
|
||||||
|
success = true
|
||||||
|
io.WriteString(w, "{}\n")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user