ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
1 changed files with 145336 additions and 0 deletions
170
services/oci-l4-relay/network_linux_test.go
Normal file
170
services/oci-l4-relay/network_linux_test.go
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/nftables"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
func TestNetworkLifecycle(t *testing.T) {
|
||||
if os.Getenv("RELAY_NETWORK_TEST") != "1" {
|
||||
t.Skip("RELAY_NETWORK_TEST is not enabled")
|
||||
}
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peerKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
privateKeyPath := filepath.Join(t.TempDir(), "private_key")
|
||||
if err := os.WriteFile(privateKeyPath, []byte(privateKey.String()), 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tunnelAddress, err := parseCIDR("198.18.0.2/30")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
localReturnCIDR, err := parseCIDR("203.0.113.0/24")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
targets := []relayTarget{
|
||||
{address: net.ParseIP("198.18.0.10").To4(), ports: []uint16{80, 443}},
|
||||
{address: net.ParseIP("198.18.0.11").To4(), ports: []uint16{25, 465, 993}},
|
||||
}
|
||||
loopback, err := netlink.LinkByName("lo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := netlink.LinkSetUp(loopback); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, target := range targets {
|
||||
if err := netlink.AddrAdd(loopback, &netlink.Addr{
|
||||
IPNet: &net.IPNet{IP: target.address, Mask: net.CIDRMask(32, 32)},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := netlink.RouteAdd(&netlink.Route{
|
||||
LinkIndex: loopback.Attrs().Index,
|
||||
Dst: localReturnCIDR,
|
||||
Scope: netlink.SCOPE_LINK,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
instance := &relay{config: config{
|
||||
privateKeyPath: privateKeyPath,
|
||||
peerPublicKey: peerKey.PublicKey().String(),
|
||||
peerEndpoint: "192.0.2.1:51820",
|
||||
listenPort: 51820,
|
||||
tunnelAddress: tunnelAddress,
|
||||
targets: targets,
|
||||
endpointAddress: net.ParseIP("192.0.2.1").To4(),
|
||||
localReturnCIDRs: []*net.IPNet{localReturnCIDR},
|
||||
}}
|
||||
if err := instance.configure(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := instance.cleanup(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
if err := instance.refreshFence(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := verifyFirewall(instance.config.endpointAddress, instance.config.targets); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := netlink.LinkByName(interfaceName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rules, err := netlink.RuleList(netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, target := range instance.config.targets {
|
||||
foundRule := false
|
||||
for _, rule := range rules {
|
||||
if isRelayPolicyRule(rule, target.address) {
|
||||
foundRule = true
|
||||
}
|
||||
}
|
||||
if !foundRule {
|
||||
t.Fatalf("relay policy rule for %s is absent", target.address)
|
||||
}
|
||||
externalRoutes, err := netlink.RouteGetWithOptions(
|
||||
net.ParseIP("192.0.2.2").To4(),
|
||||
&netlink.RouteGetOptions{SrcAddr: target.address},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(externalRoutes) != 1 || externalRoutes[0].LinkIndex != linkIndex(t, interfaceName) {
|
||||
t.Fatalf("external traffic from %s does not select %s: %+v", target.address, interfaceName, externalRoutes)
|
||||
}
|
||||
localRoutes, err := netlink.RouteGetWithOptions(
|
||||
net.ParseIP("203.0.113.1").To4(),
|
||||
&netlink.RouteGetOptions{SrcAddr: target.address},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(localRoutes) != 1 || localRoutes[0].LinkIndex != loopback.Attrs().Index {
|
||||
t.Fatalf("local traffic from %s does not select the main table: %+v", target.address, localRoutes)
|
||||
}
|
||||
}
|
||||
foundTable := false
|
||||
tables, err := (&nftables.Conn{}).ListTables()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, table := range tables {
|
||||
if table.Family == nftables.TableFamilyINet && table.Name == firewallTable {
|
||||
foundTable = true
|
||||
}
|
||||
}
|
||||
if !foundTable {
|
||||
t.Fatal("relay firewall table is absent")
|
||||
}
|
||||
time.Sleep(16 * time.Second)
|
||||
if err := verifyFirewall(instance.config.endpointAddress, instance.config.targets); err == nil {
|
||||
t.Fatal("expected the unrefreshed ownership fence to expire")
|
||||
}
|
||||
if err := instance.cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := netlink.LinkByName(interfaceName); err == nil {
|
||||
t.Fatalf("%s remains after cleanup", interfaceName)
|
||||
}
|
||||
rules, err = netlink.RuleList(netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, target := range instance.config.targets {
|
||||
for _, rule := range rules {
|
||||
if isRelayPolicyRule(rule, target.address) ||
|
||||
isLocalReturnRule(rule, target.address, localReturnCIDR, localPriority) {
|
||||
t.Fatalf("policy rule for %s remains after cleanup: %+v", target.address, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linkIndex(t *testing.T, name string) int {
|
||||
t.Helper()
|
||||
link, err := netlink.LinkByName(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return link.Attrs().Index
|
||||
}
|
||||
Loading…
Reference in a new issue