From ba6ec42f6d5558d04b683755aca63f88b1f2d5fc Mon Sep 17 00:00:00 2001 From: James Tucker Date: Thu, 24 Aug 2023 18:15:03 -0700 Subject: [PATCH] util/linuxfw: add missing input rule to the tailscale tun Add an explicit accept rule for input to the tun interface, as a mirror to the explicit rule to accept output from the tun interface. The rule matches any packet in to our tun interface and accepts it, and the rule is positioned and prioritized such that it should be evaluated prior to conventional ufw/iptables/nft rules. Updates #391 Fixes #7332 Updates #9084 Signed-off-by: James Tucker --- util/linuxfw/iptables_runner.go | 14 ++++++++- util/linuxfw/iptables_runner_test.go | 1 + util/linuxfw/nftables_runner.go | 43 ++++++++++++++++++++++++++++ util/linuxfw/nftables_runner_test.go | 32 +++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/util/linuxfw/iptables_runner.go b/util/linuxfw/iptables_runner.go index 14f2fa536..3c4199ece 100644 --- a/util/linuxfw/iptables_runner.go +++ b/util/linuxfw/iptables_runner.go @@ -254,6 +254,12 @@ func (i *iptablesRunner) addBase4(tunname string) error { return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err) } + // Explicitly allow all other inbound traffic to the tun interface + args = []string{"-i", tunname, "-j", "ACCEPT"} + if err := i.ipt4.Append("filter", "ts-input", args...); err != nil { + return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err) + } + // Forward all traffic from the Tailscale interface, and drop // traffic to the tailscale interface by default. We use packet // marks here so both filter/FORWARD and nat/POSTROUTING can match @@ -291,7 +297,13 @@ func (i *iptablesRunner) addBase6(tunname string) error { // TODO: only allow traffic from Tailscale's ULA range to come // from tailscale0. - args := []string{"-i", tunname, "-j", "MARK", "--set-mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask} + // Explicitly allow all other inbound traffic to the tun interface + args := []string{"-i", tunname, "-j", "ACCEPT"} + if err := i.ipt6.Append("filter", "ts-input", args...); err != nil { + return fmt.Errorf("adding %v in v6/filter/ts-input: %w", args, err) + } + + args = []string{"-i", tunname, "-j", "MARK", "--set-mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask} if err := i.ipt6.Append("filter", "ts-forward", args...); err != nil { return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err) } diff --git a/util/linuxfw/iptables_runner_test.go b/util/linuxfw/iptables_runner_test.go index e294f064b..d4c7c95f4 100644 --- a/util/linuxfw/iptables_runner_test.go +++ b/util/linuxfw/iptables_runner_test.go @@ -261,6 +261,7 @@ func TestAddAndDeleteBase(t *testing.T) { } tsRulesCommon := []fakeRule{ // table/chain/rule + {"filter", "ts-input", []string{"-i", tunname, "-j", "ACCEPT"}}, {"filter", "ts-forward", []string{"-i", tunname, "-j", "MARK", "--set-mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask}}, {"filter", "ts-forward", []string{"-m", "mark", "--mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask, "-j", "ACCEPT"}}, {"filter", "ts-forward", []string{"-o", tunname, "-j", "ACCEPT"}}, diff --git a/util/linuxfw/nftables_runner.go b/util/linuxfw/nftables_runner.go index 519725792..d7588107c 100644 --- a/util/linuxfw/nftables_runner.go +++ b/util/linuxfw/nftables_runner.go @@ -877,6 +877,38 @@ func addAcceptOutgoingPacketRule(conn *nftables.Conn, table *nftables.Table, cha return nil } +// createAcceptIncomingPacketRule creates a rule to accept incoming packets to +// the given interface. +func createAcceptIncomingPacketRule(table *nftables.Table, chain *nftables.Chain, tunname string) *nftables.Rule { + return &nftables.Rule{ + Table: table, + Chain: chain, + Exprs: []expr.Any{ + &expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte(tunname), + }, + &expr.Counter{}, + &expr.Verdict{ + Kind: expr.VerdictAccept, + }, + }, + } +} + +func addAcceptIncomingPacketRule(conn *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error { + rule := createAcceptIncomingPacketRule(table, chain, tunname) + _ = conn.AddRule(rule) + + if err := conn.Flush(); err != nil { + return fmt.Errorf("flush add rule: %w", err) + } + + return nil +} + // AddBase adds some basic processing rules. func (n *nftablesRunner) AddBase(tunname string) error { if err := n.addBase4(tunname); err != nil { @@ -904,6 +936,9 @@ func (n *nftablesRunner) addBase4(tunname string) error { if err = addDropCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil { return fmt.Errorf("add drop cgnat range rule v4: %w", err) } + if err = addAcceptIncomingPacketRule(conn, n.nft4.Filter, inputChain, tunname); err != nil { + return fmt.Errorf("add accept incoming packet rule v4: %w", err) + } forwardChain, err := getChainFromTable(conn, n.nft4.Filter, chainNameForward) if err != nil { @@ -937,6 +972,14 @@ func (n *nftablesRunner) addBase4(tunname string) error { func (n *nftablesRunner) addBase6(tunname string) error { conn := n.conn + inputChain, err := getChainFromTable(conn, n.nft6.Filter, chainNameInput) + if err != nil { + return fmt.Errorf("get input chain v4: %v", err) + } + if err = addAcceptIncomingPacketRule(conn, n.nft6.Filter, inputChain, tunname); err != nil { + return fmt.Errorf("add accept incoming packet rule v6: %w", err) + } + forwardChain, err := getChainFromTable(conn, n.nft6.Filter, chainNameForward) if err != nil { return fmt.Errorf("get forward chain v6: %w", err) diff --git a/util/linuxfw/nftables_runner_test.go b/util/linuxfw/nftables_runner_test.go index ad068957e..b8c66363f 100644 --- a/util/linuxfw/nftables_runner_test.go +++ b/util/linuxfw/nftables_runner_test.go @@ -375,6 +375,38 @@ func TestAddAcceptOutgoingPacketRule(t *testing.T) { } } +func TestAddAcceptIncomingPacketRule(t *testing.T) { + proto := nftables.TableFamilyIPv4 + want := [][]byte{ + // batch begin + []byte("\x00\x00\x00\x0a"), + // nft add table ip ts-filter-test + []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"), + // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority 0\; } + []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"), + // nft add rule ip ts-filter-test ts-input-test iifname "testTunn" counter accept + []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\xb4\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"), + // batch end + []byte("\x00\x00\x00\x0a"), + } + testConn := newTestConn(t, want) + table := testConn.AddTable(&nftables.Table{ + Family: proto, + Name: "ts-filter-test", + }) + chain := testConn.AddChain(&nftables.Chain{ + Name: "ts-input-test", + Table: table, + Type: nftables.ChainTypeFilter, + Hooknum: nftables.ChainHookInput, + Priority: nftables.ChainPriorityFilter, + }) + err := addAcceptIncomingPacketRule(testConn, table, chain, "testTunn") + if err != nil { + t.Fatal(err) + } +} + func TestAddMatchSubnetRouteMarkRuleMasq(t *testing.T) { proto := nftables.TableFamilyIPv4 want := [][]byte{