mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
net/tstun: accept peerapi connections through the filter
Fixes tailscale/corp#1545 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
950fc28887
commit
939861773d
@ -1366,7 +1366,7 @@ func (b *LocalBackend) getPeerAPIPortForTSMPPing(ip netaddr.IP) (port uint16, ok
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
for _, pln := range b.peerAPIListeners {
|
for _, pln := range b.peerAPIListeners {
|
||||||
if pln.ip.BitLen() == ip.BitLen() {
|
if pln.ip == ip {
|
||||||
return uint16(pln.port), true
|
return uint16(pln.port), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,9 @@ type Wrapper struct {
|
|||||||
|
|
||||||
// disableFilter disables all filtering when set. This should only be used in tests.
|
// disableFilter disables all filtering when set. This should only be used in tests.
|
||||||
disableFilter bool
|
disableFilter bool
|
||||||
|
|
||||||
|
// disableTSMPRejected disables TSMP rejected responses. For tests.
|
||||||
|
disableTSMPRejected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Wrap(logf logger.Logf, tdev tun.Device) *Wrapper {
|
func Wrap(logf logger.Logf, tdev tun.Device) *Wrapper {
|
||||||
@ -351,13 +354,26 @@ func (t *Wrapper) filterIn(buf []byte) filter.Response {
|
|||||||
return filter.Drop
|
return filter.Drop
|
||||||
}
|
}
|
||||||
|
|
||||||
if filt.RunIn(p, t.filterFlags) != filter.Accept {
|
outcome := filt.RunIn(p, t.filterFlags)
|
||||||
|
|
||||||
|
// Let peerapi through the filter; its ACLs are handled at L7,
|
||||||
|
// not at the packet level.
|
||||||
|
if outcome != filter.Accept &&
|
||||||
|
p.IPProto == ipproto.TCP &&
|
||||||
|
p.TCPFlags&packet.TCPSyn != 0 &&
|
||||||
|
t.PeerAPIPort != nil {
|
||||||
|
if port, ok := t.PeerAPIPort(p.Dst.IP); ok && port == p.Dst.Port {
|
||||||
|
outcome = filter.Accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if outcome != filter.Accept {
|
||||||
|
|
||||||
// Tell them, via TSMP, we're dropping them due to the ACL.
|
// Tell them, via TSMP, we're dropping them due to the ACL.
|
||||||
// Their host networking stack can translate this into ICMP
|
// Their host networking stack can translate this into ICMP
|
||||||
// or whatnot as required. But notably, their GUI or tailscale CLI
|
// or whatnot as required. But notably, their GUI or tailscale CLI
|
||||||
// can show them a rejection history with reasons.
|
// can show them a rejection history with reasons.
|
||||||
if p.IPVersion == 4 && p.IPProto == ipproto.TCP && p.TCPFlags&packet.TCPSyn != 0 {
|
if p.IPVersion == 4 && p.IPProto == ipproto.TCP && p.TCPFlags&packet.TCPSyn != 0 && !t.disableTSMPRejected {
|
||||||
rj := packet.TailscaleRejectedHeader{
|
rj := packet.TailscaleRejectedHeader{
|
||||||
IPSrc: p.Dst.IP,
|
IPSrc: p.Dst.IP,
|
||||||
IPDst: p.Src.IP,
|
IPDst: p.Src.IP,
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -42,6 +43,34 @@ func udp4(src, dst string, sport, dport uint16) []byte {
|
|||||||
return packet.Generate(header, []byte("udp_payload"))
|
return packet.Generate(header, []byte("udp_payload"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tcp4syn(src, dst string, sport, dport uint16) []byte {
|
||||||
|
sip, err := netaddr.ParseIP(src)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dip, err := netaddr.ParseIP(dst)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ipHeader := packet.IP4Header{
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: sip,
|
||||||
|
Dst: dip,
|
||||||
|
IPID: 0,
|
||||||
|
}
|
||||||
|
tcpHeader := make([]byte, 20)
|
||||||
|
binary.BigEndian.PutUint16(tcpHeader[0:], sport)
|
||||||
|
binary.BigEndian.PutUint16(tcpHeader[2:], dport)
|
||||||
|
tcpHeader[13] |= 2 // SYN
|
||||||
|
|
||||||
|
both := packet.Generate(ipHeader, tcpHeader)
|
||||||
|
|
||||||
|
// 20 byte IP4 + 20 byte TCP
|
||||||
|
binary.BigEndian.PutUint16(both[2:4], 40)
|
||||||
|
|
||||||
|
return both
|
||||||
|
}
|
||||||
|
|
||||||
func nets(nets ...string) (ret []netaddr.IPPrefix) {
|
func nets(nets ...string) (ret []netaddr.IPPrefix) {
|
||||||
for _, s := range nets {
|
for _, s := range nets {
|
||||||
if i := strings.IndexByte(s, '/'); i == -1 {
|
if i := strings.IndexByte(s, '/'); i == -1 {
|
||||||
@ -385,3 +414,70 @@ func TestAtomic64Alignment(t *testing.T) {
|
|||||||
c := new(Wrapper)
|
c := new(Wrapper)
|
||||||
atomic.StoreInt64(&c.lastActivityAtomic, 123)
|
atomic.StoreInt64(&c.lastActivityAtomic, 123)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeerAPIBypass(t *testing.T) {
|
||||||
|
wrapperWithPeerAPI := &Wrapper{
|
||||||
|
PeerAPIPort: func(ip netaddr.IP) (port uint16, ok bool) {
|
||||||
|
if ip == netaddr.MustParseIP("100.64.1.2") {
|
||||||
|
return 60000, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
w *Wrapper
|
||||||
|
filter *filter.Filter
|
||||||
|
pkt []byte
|
||||||
|
want filter.Response
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "reject_nil_filter",
|
||||||
|
w: &Wrapper{
|
||||||
|
PeerAPIPort: func(netaddr.IP) (port uint16, ok bool) {
|
||||||
|
return 60000, true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkt: tcp4syn("1.2.3.4", "100.64.1.2", 1234, 60000),
|
||||||
|
want: filter.Drop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reject_with_filter",
|
||||||
|
w: &Wrapper{},
|
||||||
|
filter: filter.NewAllowNone(logger.Discard, new(netaddr.IPSet)),
|
||||||
|
pkt: tcp4syn("1.2.3.4", "100.64.1.2", 1234, 60000),
|
||||||
|
want: filter.Drop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "peerapi_bypass_filter",
|
||||||
|
w: wrapperWithPeerAPI,
|
||||||
|
filter: filter.NewAllowNone(logger.Discard, new(netaddr.IPSet)),
|
||||||
|
pkt: tcp4syn("1.2.3.4", "100.64.1.2", 1234, 60000),
|
||||||
|
want: filter.Accept,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "peerapi_dont_bypass_filter_wrong_port",
|
||||||
|
w: wrapperWithPeerAPI,
|
||||||
|
filter: filter.NewAllowNone(logger.Discard, new(netaddr.IPSet)),
|
||||||
|
pkt: tcp4syn("1.2.3.4", "100.64.1.2", 1234, 60001),
|
||||||
|
want: filter.Drop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "peerapi_dont_bypass_filter_wrong_dst_ip",
|
||||||
|
w: wrapperWithPeerAPI,
|
||||||
|
filter: filter.NewAllowNone(logger.Discard, new(netaddr.IPSet)),
|
||||||
|
pkt: tcp4syn("1.2.3.4", "100.64.1.3", 1234, 60000),
|
||||||
|
want: filter.Drop,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.w.SetFilter(tt.filter)
|
||||||
|
tt.w.disableTSMPRejected = true
|
||||||
|
if got := tt.w.filterIn(tt.pkt); got != tt.want {
|
||||||
|
t.Errorf("got = %v; want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user