mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-18 12:32:13 +00:00
ipn/ipnlocal, wgengine/netstack: start handling ports for future serving
Updates tailscale/corp#7515 Change-Id: I966e936e72a2ee99be8d0f5f16872b48cc150258 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
acf5839dd2
commit
2daf0f146c
@ -568,5 +568,13 @@ func runDevStoreSet(ctx context.Context, args []string) error {
|
|||||||
if !devStoreSetArgs.danger {
|
if !devStoreSetArgs.danger {
|
||||||
return errors.New("this command is dangerous; use --danger to proceed")
|
return errors.New("this command is dangerous; use --danger to proceed")
|
||||||
}
|
}
|
||||||
return localClient.SetDevStoreKeyValue(ctx, args[0], args[1])
|
key, val := args[0], args[1]
|
||||||
|
if val == "-" {
|
||||||
|
valb, err := io.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
val = string(valb)
|
||||||
|
}
|
||||||
|
return localClient.SetDevStoreKeyValue(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/util/singleflight from tailscale.com/control/controlclient+
|
tailscale.com/util/singleflight from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/strs from tailscale.com/hostinfo+
|
tailscale.com/util/strs from tailscale.com/hostinfo+
|
||||||
tailscale.com/util/systemd from tailscale.com/control/controlclient+
|
tailscale.com/util/systemd from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/uniq from tailscale.com/wgengine/magicsock
|
tailscale.com/util/uniq from tailscale.com/wgengine/magicsock+
|
||||||
💣 tailscale.com/util/winutil from tailscale.com/cmd/tailscaled+
|
💣 tailscale.com/util/winutil from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/version from tailscale.com/derp+
|
tailscale.com/version from tailscale.com/derp+
|
||||||
tailscale.com/version/distro from tailscale.com/hostinfo+
|
tailscale.com/version/distro from tailscale.com/hostinfo+
|
||||||
|
18
ipn/ipnlocal/cert_js.go
Normal file
18
ipn/ipnlocal/cert_js.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ipnlocal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TLSCertKeyPair struct {
|
||||||
|
CertPEM, KeyPEM []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) GetCertPEM(ctx context.Context, domain string) (*TLSCertKeyPair, error) {
|
||||||
|
return nil, errors.New("not implemented for js/wasm")
|
||||||
|
}
|
@ -6,10 +6,13 @@ package ipnlocal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -25,6 +28,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go4.org/mem"
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
"tailscale.com/client/tailscale/apitype"
|
"tailscale.com/client/tailscale/apitype"
|
||||||
@ -62,6 +66,7 @@ import (
|
|||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/util/osshare"
|
"tailscale.com/util/osshare"
|
||||||
"tailscale.com/util/systemd"
|
"tailscale.com/util/systemd"
|
||||||
|
"tailscale.com/util/uniq"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
@ -135,8 +140,9 @@ type LocalBackend struct {
|
|||||||
sshAtomicBool atomic.Bool
|
sshAtomicBool atomic.Bool
|
||||||
shutdownCalled bool // if Shutdown has been called
|
shutdownCalled bool // if Shutdown has been called
|
||||||
|
|
||||||
filterAtomic atomic.Pointer[filter.Filter]
|
filterAtomic atomic.Pointer[filter.Filter]
|
||||||
containsViaIPFuncAtomic syncs.AtomicValue[func(netip.Addr) bool]
|
containsViaIPFuncAtomic syncs.AtomicValue[func(netip.Addr) bool]
|
||||||
|
shouldInterceptTCPPortAtomic syncs.AtomicValue[func(uint16) bool]
|
||||||
|
|
||||||
// The mutex protects the following elements.
|
// The mutex protects the following elements.
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@ -192,6 +198,10 @@ type LocalBackend struct {
|
|||||||
directFileDoFinalRename bool // false on macOS, true on several NAS platforms
|
directFileDoFinalRename bool // false on macOS, true on several NAS platforms
|
||||||
componentLogUntil map[string]componentLogState
|
componentLogUntil map[string]componentLogState
|
||||||
|
|
||||||
|
// ServeConfig fields. (also guarded by mu)
|
||||||
|
lastServeConfJSON mem.RO // last JSON that was parsed into serveConfig
|
||||||
|
serveConfig ipn.ServeConfig
|
||||||
|
|
||||||
// statusLock must be held before calling statusChanged.Wait() or
|
// statusLock must be held before calling statusChanged.Wait() or
|
||||||
// statusChanged.Broadcast().
|
// statusChanged.Broadcast().
|
||||||
statusLock sync.Mutex
|
statusLock sync.Mutex
|
||||||
@ -257,6 +267,8 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, diale
|
|||||||
// Default filter blocks everything and logs nothing, until Start() is called.
|
// Default filter blocks everything and logs nothing, until Start() is called.
|
||||||
b.setFilter(filter.NewAllowNone(logf, &netipx.IPSet{}))
|
b.setFilter(filter.NewAllowNone(logf, &netipx.IPSet{}))
|
||||||
|
|
||||||
|
b.setTCPPortsIntercepted(nil)
|
||||||
|
|
||||||
b.statusChanged = sync.NewCond(&b.statusLock)
|
b.statusChanged = sync.NewCond(&b.statusLock)
|
||||||
b.e.SetStatusCallback(b.setWgengineStatus)
|
b.e.SetStatusCallback(b.setWgengineStatus)
|
||||||
|
|
||||||
@ -1142,6 +1154,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.setAtomicValuesFromPrefs(b.prefs)
|
b.setAtomicValuesFromPrefs(b.prefs)
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
wantRunning := b.prefs.WantRunning()
|
wantRunning := b.prefs.WantRunning()
|
||||||
@ -1906,6 +1919,7 @@ func (b *LocalBackend) loadStateLocked(key ipn.StateKey, prefs *ipn.Prefs) (err
|
|||||||
// value instead of making up a new one.
|
// value instead of making up a new one.
|
||||||
b.logf("using frontend prefs: %s", prefs.Pretty())
|
b.logf("using frontend prefs: %s", prefs.Pretty())
|
||||||
b.prefs = prefs.Clone().View()
|
b.prefs = prefs.Clone().View()
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
b.writeServerModeStartState(b.userID, b.prefs)
|
b.writeServerModeStartState(b.userID, b.prefs)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1926,6 +1940,7 @@ func (b *LocalBackend) loadStateLocked(key ipn.StateKey, prefs *ipn.Prefs) (err
|
|||||||
prefs.WantRunning = false
|
prefs.WantRunning = false
|
||||||
b.logf("using backend prefs; created empty state for %q: %s", key, prefs.Pretty())
|
b.logf("using backend prefs; created empty state for %q: %s", key, prefs.Pretty())
|
||||||
b.prefs = prefs.View()
|
b.prefs = prefs.View()
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
return nil
|
return nil
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return fmt.Errorf("backend prefs: store.ReadState(%q): %v", key, err)
|
return fmt.Errorf("backend prefs: store.ReadState(%q): %v", key, err)
|
||||||
@ -1952,10 +1967,49 @@ func (b *LocalBackend) loadStateLocked(key ipn.StateKey, prefs *ipn.Prefs) (err
|
|||||||
b.prefs = prefs.View()
|
b.prefs = prefs.View()
|
||||||
|
|
||||||
b.setAtomicValuesFromPrefs(b.prefs)
|
b.setAtomicValuesFromPrefs(b.prefs)
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setTCPPortsIntercepted populates b.shouldInterceptTCPPortAtomic with an
|
||||||
|
// efficient func for ShouldInterceptTCPPort to use, which is called on every
|
||||||
|
// incoming packet.
|
||||||
|
func (b *LocalBackend) setTCPPortsIntercepted(ports []uint16) {
|
||||||
|
slices.Sort(ports)
|
||||||
|
uniq.ModifySlice(&ports)
|
||||||
|
b.logf("localbackend: handling TCP ports = %v", ports)
|
||||||
|
var f func(uint16) bool
|
||||||
|
switch len(ports) {
|
||||||
|
case 0:
|
||||||
|
f = func(uint16) bool { return false }
|
||||||
|
case 1:
|
||||||
|
f = func(p uint16) bool { return ports[0] == p }
|
||||||
|
case 2:
|
||||||
|
f = func(p uint16) bool { return ports[0] == p || ports[1] == p }
|
||||||
|
case 3:
|
||||||
|
f = func(p uint16) bool { return ports[0] == p || ports[1] == p || ports[2] == p }
|
||||||
|
default:
|
||||||
|
if len(ports) > 16 {
|
||||||
|
m := map[uint16]bool{}
|
||||||
|
for _, p := range ports {
|
||||||
|
m[p] = true
|
||||||
|
}
|
||||||
|
f = func(p uint16) bool { return m[p] }
|
||||||
|
} else {
|
||||||
|
f = func(p uint16) bool {
|
||||||
|
for _, x := range ports {
|
||||||
|
if p == x {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.shouldInterceptTCPPortAtomic.Store(f)
|
||||||
|
}
|
||||||
|
|
||||||
// setAtomicValuesFromPrefs populates sshAtomicBool and containsViaIPFuncAtomic
|
// setAtomicValuesFromPrefs populates sshAtomicBool and containsViaIPFuncAtomic
|
||||||
// from the prefs p, which may be nil.
|
// from the prefs p, which may be nil.
|
||||||
func (b *LocalBackend) setAtomicValuesFromPrefs(p ipn.PrefsView) {
|
func (b *LocalBackend) setAtomicValuesFromPrefs(p ipn.PrefsView) {
|
||||||
@ -1963,6 +2017,7 @@ func (b *LocalBackend) setAtomicValuesFromPrefs(p ipn.PrefsView) {
|
|||||||
|
|
||||||
if !p.Valid() {
|
if !p.Valid() {
|
||||||
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(nil))
|
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(nil))
|
||||||
|
b.setTCPPortsIntercepted(nil)
|
||||||
} else {
|
} else {
|
||||||
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(p.AdvertiseRoutes().Filter(tsaddr.IsViaPrefix)))
|
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(p.AdvertiseRoutes().Filter(tsaddr.IsViaPrefix)))
|
||||||
}
|
}
|
||||||
@ -2283,8 +2338,8 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
|
|||||||
// anyway. No-op if no exit node resolution is needed.
|
// anyway. No-op if no exit node resolution is needed.
|
||||||
findExitNodeIDLocked(newp, netMap)
|
findExitNodeIDLocked(newp, netMap)
|
||||||
b.prefs = newp.View()
|
b.prefs = newp.View()
|
||||||
|
|
||||||
b.setAtomicValuesFromPrefs(b.prefs)
|
b.setAtomicValuesFromPrefs(b.prefs)
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
b.inServerMode = b.prefs.ForceDaemon()
|
b.inServerMode = b.prefs.ForceDaemon()
|
||||||
// We do this to avoid holding the lock while doing everything else.
|
// We do this to avoid holding the lock while doing everything else.
|
||||||
|
|
||||||
@ -3281,6 +3336,7 @@ func (b *LocalBackend) ResetForClientDisconnect() {
|
|||||||
b.authURLSticky = ""
|
b.authURLSticky = ""
|
||||||
b.activeLogin = ""
|
b.activeLogin = ""
|
||||||
b.setAtomicValuesFromPrefs(b.prefs)
|
b.setAtomicValuesFromPrefs(b.prefs)
|
||||||
|
b.setTCPPortsIntercepted(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Load() && envknob.CanSSHD() }
|
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Load() && envknob.CanSSHD() }
|
||||||
@ -3402,6 +3458,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
|||||||
}
|
}
|
||||||
b.capFileSharing = fs
|
b.capFileSharing = fs
|
||||||
|
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
if nm == nil {
|
if nm == nil {
|
||||||
b.nodeByAddr = nil
|
b.nodeByAddr = nil
|
||||||
return
|
return
|
||||||
@ -3436,6 +3493,41 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setTCPPortsInterceptedFromNetmapAndPrefsLocked calls setTCPPortsIntercepted with
|
||||||
|
// the ports that tailscaled should handle as a function of b.netMap and b.prefs.
|
||||||
|
//
|
||||||
|
// b.mu must be held.
|
||||||
|
func (b *LocalBackend) setTCPPortsInterceptedFromNetmapAndPrefsLocked() {
|
||||||
|
handlePorts := make([]uint16, 0, 4)
|
||||||
|
|
||||||
|
prefs := b.prefs
|
||||||
|
if prefs.Valid() && prefs.RunSSH() && envknob.CanSSHD() {
|
||||||
|
handlePorts = append(handlePorts, 22)
|
||||||
|
}
|
||||||
|
|
||||||
|
nm := b.netMap
|
||||||
|
if nm != nil && nm.SelfNode != nil {
|
||||||
|
profileID := fmt.Sprintf("node-%s", nm.SelfNode.StableID) // TODO(maisem,bradfitz): something else?
|
||||||
|
confKey := ipn.ServeConfigKey(profileID)
|
||||||
|
if confj, err := b.store.ReadState(confKey); err == nil {
|
||||||
|
if !b.lastServeConfJSON.Equal(mem.B(confj)) {
|
||||||
|
b.lastServeConfJSON = mem.B(confj)
|
||||||
|
var conf ipn.ServeConfig
|
||||||
|
if err := json.Unmarshal(confj, &conf); err != nil {
|
||||||
|
b.logf("invalid ServeConfig %q in StateStore: %v", confKey, err)
|
||||||
|
}
|
||||||
|
b.serveConfig = conf
|
||||||
|
}
|
||||||
|
for p := range b.serveConfig.TCP {
|
||||||
|
if p > 0 && p <= math.MaxUint16 {
|
||||||
|
handlePorts = append(handlePorts, uint16(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.setTCPPortsIntercepted(handlePorts)
|
||||||
|
}
|
||||||
|
|
||||||
// operatorUserName returns the current pref's OperatorUser's name, or the
|
// operatorUserName returns the current pref's OperatorUser's name, or the
|
||||||
// empty string if none.
|
// empty string if none.
|
||||||
func (b *LocalBackend) operatorUserName() string {
|
func (b *LocalBackend) operatorUserName() string {
|
||||||
@ -3976,5 +4068,59 @@ func (b *LocalBackend) SetDevStateStore(key, value string) error {
|
|||||||
}
|
}
|
||||||
err := b.store.WriteState(ipn.StateKey(key), []byte(value))
|
err := b.store.WriteState(ipn.StateKey(key), []byte(value))
|
||||||
b.logf("SetDevStateStore(%q, %q) = %v", key, value, err)
|
b.logf("SetDevStateStore(%q, %q) = %v", key, value, err)
|
||||||
return err
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldInterceptTCPPort reports whether the given TCP port number to a
|
||||||
|
// Tailscale IP (not a subnet router, service IP, etc) should be intercepted by
|
||||||
|
// Tailscaled and handled in-process.
|
||||||
|
func (b *LocalBackend) ShouldInterceptTCPPort(port uint16) bool {
|
||||||
|
return b.shouldInterceptTCPPortAtomic.Load()(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
var runDevWebServer = envknob.RegisterBool("TS_DEV_WEBSERVER")
|
||||||
|
|
||||||
|
func (b *LocalBackend) HandleInterceptedTCPConn(c net.Conn) {
|
||||||
|
if !runDevWebServer() {
|
||||||
|
b.logf("localbackend: closing TCP conn from %v to %v", c.RemoteAddr(), c.LocalAddr())
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bradfitz): look up how; sniff SNI if ambiguous
|
||||||
|
hs := &http.Server{
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
GetCertificate: b.getTLSServeCert,
|
||||||
|
},
|
||||||
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
io.WriteString(w, "<h1>hello world</h1>this is tailscaled")
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
hs.ServeTLS(netutil.NewOneConnListener(c, nil), "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) getTLSServeCert(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
if hi == nil || hi.ServerName == "" {
|
||||||
|
return nil, errors.New("no SNI ServerName")
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
pair, err := b.GetCertPEM(ctx, hi.ServerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cert, nil
|
||||||
}
|
}
|
||||||
|
51
ipn/store.go
51
ipn/store.go
@ -64,3 +64,54 @@ func ReadStoreInt(store StateStore, id StateKey) (int64, error) {
|
|||||||
func PutStoreInt(store StateStore, id StateKey, val int64) error {
|
func PutStoreInt(store StateStore, id StateKey, val int64) error {
|
||||||
return store.WriteState(id, fmt.Appendf(nil, "%d", val))
|
return store.WriteState(id, fmt.Appendf(nil, "%d", val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeConfigKey returns a StateKey that stores the
|
||||||
|
// JSON-encoded ServeConfig for a config profile.
|
||||||
|
func ServeConfigKey(profileID string) StateKey {
|
||||||
|
return StateKey("_serve/" + profileID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeConfig is the JSON type stored in the StateStore for
|
||||||
|
// StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
|
||||||
|
type ServeConfig struct {
|
||||||
|
// TCP are the list of TCP port numbers that tailscaled should handle for
|
||||||
|
// the Tailscale IP addresses. (not subnet routers, etc)
|
||||||
|
TCP map[int]*TCPPortHandler `json:",omitempty"`
|
||||||
|
|
||||||
|
// Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
|
||||||
|
// keyed by mount point ("/", "/foo", etc)
|
||||||
|
Web map[string]map[string]*HTTPHandler `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPPortHandler describes what to do when handling a TCP
|
||||||
|
// connection.
|
||||||
|
type TCPPortHandler struct {
|
||||||
|
// HTTPS, if true, means that tailscaled should handle this connection as an
|
||||||
|
// HTTPS request as configured by ServeConfig.Web.
|
||||||
|
//
|
||||||
|
// It is mutually exclusive with TCPForward.
|
||||||
|
HTTPS bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// TCPForward is the IP:port to forward TCP connections to.
|
||||||
|
// Whether or not TLS is terminated by tailscaled depends on
|
||||||
|
// TerminateTLS.
|
||||||
|
//
|
||||||
|
// It is mutually exclusive with HTTPS.
|
||||||
|
TCPForward string `json:",omitempty"`
|
||||||
|
|
||||||
|
// TerminateTLS is whether tailscaled should terminate TLS
|
||||||
|
// connections before forwarding them to TCPForward. It is only
|
||||||
|
// used if TCPForward is non-empty. (the HTTPS mode )
|
||||||
|
TerminateTLS bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPHandler is either a path or a proxy to serve.
|
||||||
|
type HTTPHandler struct {
|
||||||
|
// Exactly one of the following may be set.
|
||||||
|
|
||||||
|
Path string `json:",omitempty"` // absolute path to directory or file to serve
|
||||||
|
Proxy string `json:",omitempty"` // http://localhost:3000/, localhost:3030, 3030
|
||||||
|
|
||||||
|
// TODO(bradfitz): bool to not enumerate directories? TTL on mapping for
|
||||||
|
// temporary ones? Error codes? Redirects?
|
||||||
|
}
|
||||||
|
@ -554,9 +554,8 @@ func (ns *Impl) shouldProcessInbound(p *packet.Parsed, t *tstun.Wrapper) bool {
|
|||||||
if dport == peerAPIPort {
|
if dport == peerAPIPort {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// Also handle SSH connections, webserver, etc, if enabled:
|
||||||
// Also handle SSH connections, if enabled.
|
if ns.lb.ShouldInterceptTCPPort(dport) {
|
||||||
if dport == 22 && ns.lb.ShouldRunSSH() {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -894,6 +893,14 @@ func (ns *Impl) acceptTCP(r *tcp.ForwarderRequest) {
|
|||||||
ns.lb.HandleQuad100Port80Conn(c)
|
ns.lb.HandleQuad100Port80Conn(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if ns.lb.ShouldInterceptTCPPort(reqDetails.LocalPort) && ns.isLocalIP(dialIP) {
|
||||||
|
c := createConn()
|
||||||
|
if c == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ns.lb.HandleInterceptedTCPConn(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ns.ForwardTCPIn != nil {
|
if ns.ForwardTCPIn != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user