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{