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>main
parent
b692985aef
commit
a61825c7b8
@ -0,0 +1,128 @@ |
||||
// 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() |
||||
} |
||||
Loading…
Reference in new issue