mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-03 16:31:20 +00:00 
			
		
		
		
	In particular, tests showing that #3824 works. But that test doesn't actually work yet; it only gets a DERP connection. (why?) Updates #13038 Change-Id: Ie1fd1b6a38d4e90fae7e72a0b9a142a95f0b2e8f Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			129 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
 | 
						|
	"github.com/google/nftables"
 | 
						|
	"github.com/google/nftables/expr"
 | 
						|
	"tailscale.com/types/ptr"
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	addFirewall = addFirewallLinux
 | 
						|
}
 | 
						|
 | 
						|
func addFirewallLinux() error {
 | 
						|
	c, err := nftables.New()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// Create a new table
 | 
						|
	table := &nftables.Table{
 | 
						|
		Family: nftables.TableFamilyIPv4, // TableFamilyINet doesn't work (why?. oh well.)
 | 
						|
		Name:   "filter",
 | 
						|
	}
 | 
						|
	c.AddTable(table)
 | 
						|
 | 
						|
	// Create a new chain for incoming traffic
 | 
						|
	inputChain := &nftables.Chain{
 | 
						|
		Name:     "input",
 | 
						|
		Table:    table,
 | 
						|
		Type:     nftables.ChainTypeFilter,
 | 
						|
		Hooknum:  nftables.ChainHookInput,
 | 
						|
		Priority: nftables.ChainPriorityFilter,
 | 
						|
		Policy:   ptr.To(nftables.ChainPolicyDrop),
 | 
						|
	}
 | 
						|
	c.AddChain(inputChain)
 | 
						|
 | 
						|
	// Allow traffic from the loopback interface
 | 
						|
	c.AddRule(&nftables.Rule{
 | 
						|
		Table: table,
 | 
						|
		Chain: inputChain,
 | 
						|
		Exprs: []expr.Any{
 | 
						|
			&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
 | 
						|
			&expr.Cmp{
 | 
						|
				Op:       expr.CmpOpEq,
 | 
						|
				Register: 1,
 | 
						|
				Data:     []byte("lo"),
 | 
						|
			},
 | 
						|
			&expr.Verdict{
 | 
						|
				Kind: expr.VerdictAccept,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	// Accept established and related connections
 | 
						|
	c.AddRule(&nftables.Rule{
 | 
						|
		Table: table,
 | 
						|
		Chain: inputChain,
 | 
						|
		Exprs: []expr.Any{
 | 
						|
			&expr.Ct{
 | 
						|
				Register: 1,
 | 
						|
				Key:      expr.CtKeySTATE,
 | 
						|
			},
 | 
						|
			&expr.Bitwise{
 | 
						|
				SourceRegister: 1,
 | 
						|
				DestRegister:   1,
 | 
						|
				Len:            4,
 | 
						|
				Mask:           binary.NativeEndian.AppendUint32(nil, 0x06), // CT_STATE_BIT_ESTABLISHED | CT_STATE_BIT_RELATED
 | 
						|
				Xor:            binary.NativeEndian.AppendUint32(nil, 0),
 | 
						|
			},
 | 
						|
			&expr.Cmp{
 | 
						|
				Op:       expr.CmpOpNeq,
 | 
						|
				Register: 1,
 | 
						|
				Data:     binary.NativeEndian.AppendUint32(nil, 0x00),
 | 
						|
			},
 | 
						|
			&expr.Verdict{
 | 
						|
				Kind: expr.VerdictAccept,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	// Allow TCP packets in that don't have the SYN bit set, even if they're not
 | 
						|
	// ESTABLISHED or RELATED. This is because the test suite gets TCP
 | 
						|
	// connections up & idle (for HTTP) before it conditionally installs these
 | 
						|
	// firewall rules. But because conntrack wasn't previously active, existing
 | 
						|
	// TCP flows aren't ESTABLISHED and get dropped. So this rule allows
 | 
						|
	// previously established TCP connections that predates the firewall rules
 | 
						|
	// to continue working, as they don't have conntrack state.
 | 
						|
	c.AddRule(&nftables.Rule{
 | 
						|
		Table: table,
 | 
						|
		Chain: inputChain,
 | 
						|
		Exprs: []expr.Any{
 | 
						|
			&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
 | 
						|
			&expr.Cmp{
 | 
						|
				Op:       expr.CmpOpEq,
 | 
						|
				Register: 1,
 | 
						|
				Data:     []byte{0x06}, // TCP
 | 
						|
			},
 | 
						|
			&expr.Payload{ // get TCP flags
 | 
						|
				DestRegister: 1,
 | 
						|
				Base:         2,
 | 
						|
				Offset:       13, // flags
 | 
						|
				Len:          1,
 | 
						|
			},
 | 
						|
			&expr.Bitwise{
 | 
						|
				SourceRegister: 1,
 | 
						|
				DestRegister:   1,
 | 
						|
				Len:            1,
 | 
						|
				Mask:           []byte{2}, // TCP_SYN
 | 
						|
				Xor:            []byte{0},
 | 
						|
			},
 | 
						|
			&expr.Cmp{
 | 
						|
				Op:       expr.CmpOpNeq,
 | 
						|
				Register: 1,
 | 
						|
				Data:     []byte{2}, // TCP_SYN
 | 
						|
			},
 | 
						|
			&expr.Verdict{
 | 
						|
				Kind: expr.VerdictAccept,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	return c.Flush()
 | 
						|
}
 |