...

Source file src/edge-infra.dev/pkg/sds/remoteaccess/wireguard/relay/integration/integration_test.go

Documentation: edge-infra.dev/pkg/sds/remoteaccess/wireguard/relay/integration

     1  package integration
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"net"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  
    18  	v1cluster "edge-infra.dev/pkg/edge/apis/cluster/v1alpha1"
    19  	"edge-infra.dev/pkg/sds/remoteaccess/constants"
    20  	v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1"
    21  	r "edge-infra.dev/pkg/sds/remoteaccess/wireguard/relay"
    22  	"edge-infra.dev/pkg/sds/remoteaccess/wireguard/store"
    23  	"edge-infra.dev/test/f2"
    24  	"edge-infra.dev/test/f2/x/ktest"
    25  )
    26  
    27  var f f2.Framework
    28  
    29  var (
    30  	projectID     = "ret-edge-b79we3ikmc7j9mihuwst2"
    31  	testPublicKey = "wbWuHrJEPC2Ui7XVQoWuM/8HZAG1FlLC/08L2vvEEgw="
    32  
    33  	outOfSubnetIPAddress = "179.16.17.3"
    34  	inSubnetIPAddress    = "172.16.16.12"
    35  
    36  	clusterAName = "cluster-a"
    37  	clusterBName = "cluster-b"
    38  	clusterCName = "cluster-c"
    39  	testClusterA = createCluster(clusterAName)
    40  	testClusterB = createCluster(clusterBName)
    41  	testClusterC = createCluster(clusterCName)
    42  
    43  	testVPNConfig                     = createVPNConfig(clusterAName, inSubnetIPAddress)
    44  	testVPNConfigNoIPAddress          = createVPNConfig(clusterCName, "")
    45  	testVPNConfigOutOfSubnetIPAddress = createVPNConfig(clusterBName, outOfSubnetIPAddress)
    46  
    47  	expectedRelaySecretInterfaceLines = []string{
    48  		"[Interface]",
    49  		"ListenPort = 51820",
    50  		"PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE",
    51  		"PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE",
    52  	}
    53  	expectedRelaySecretPeerLines = []string{
    54  		"",
    55  		"[Peer]",
    56  	}
    57  
    58  	vpnNamespace = &corev1.Namespace{
    59  		ObjectMeta: metav1.ObjectMeta{
    60  			Name: constants.VPNNamespace,
    61  		},
    62  	}
    63  )
    64  
    65  func TestMain(m *testing.M) {
    66  	f = f2.New(context.Background(),
    67  		f2.WithExtensions(
    68  			ktest.New(),
    69  		)).
    70  		Setup(func(ctx f2.Context) (f2.Context, error) {
    71  			k, err := ktest.FromContext(ctx)
    72  			if err != nil {
    73  				return ctx, err
    74  			}
    75  			// Override timeouts if we aren't using a live cluster
    76  			if !*k.Env.UseExistingCluster {
    77  				k.Timeout = 5 * time.Second
    78  				k.Tick = 10 * time.Millisecond
    79  			}
    80  			return ctx, nil
    81  		}).Teardown()
    82  	os.Exit(f.Run(m))
    83  }
    84  
    85  func TestRelayWireguard(t *testing.T) {
    86  	var (
    87  		relay    *r.Relay
    88  		storeAWg *store.Store
    89  		storeBWg *store.Store
    90  		storeCWg *store.Store
    91  		subnet   *net.IPNet
    92  		clientIP net.IP
    93  	)
    94  	feature := f2.NewFeature("RelayWireguard").
    95  		Setup("create wireguard instances", func(ctx f2.Context, t *testing.T) f2.Context {
    96  			var err error
    97  			k := ktest.FromContextT(ctx, t)
    98  			require.NoError(t, k.Client.Create(ctx, vpnNamespace.DeepCopy()))
    99  
   100  			relay, err = r.Get(ctx, k.Client)
   101  			require.NoError(t, err)
   102  
   103  			storeAWg, err = store.Get(ctx, k.Client, testVPNConfigNoIPAddress.DeepCopy(), testClusterA)
   104  			require.NoError(t, err)
   105  
   106  			storeBWg, err = store.Get(ctx, k.Client, testVPNConfigOutOfSubnetIPAddress.DeepCopy(), testClusterB)
   107  			require.NoError(t, err)
   108  
   109  			storeCWg, err = store.Get(ctx, k.Client, testVPNConfig.DeepCopy(), testClusterC)
   110  			require.NoError(t, err)
   111  			return ctx
   112  		}).
   113  		Setup("create subnet and client IP", func(ctx f2.Context, t *testing.T) f2.Context {
   114  			var err error
   115  			_, subnet, err = net.ParseCIDR("172.16.16.0/28")
   116  			require.NoError(t, err)
   117  
   118  			clientIP = net.ParseIP("172.16.16.1")
   119  			return ctx
   120  		}).
   121  		Test("wireguard relay secret contains expected data", func(ctx f2.Context, t *testing.T) f2.Context {
   122  			storeConfigs := map[string]*store.Store{clusterAName: storeAWg}
   123  			secret := relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   124  			secretLines := getLinesFromWireguardSecretData(secret)
   125  
   126  			// assert expected lines appear in interface and peer sections
   127  			assert.Equal(t, expectedRelaySecretInterfaceLines, secretLines[:4])
   128  			assert.Equal(t, expectedRelaySecretPeerLines, secretLines[7:9])
   129  
   130  			// check relay interface address is set
   131  			expectedInterfaceLine := fmt.Sprintf("Address = %s", subnet)
   132  			assert.Equal(t, expectedInterfaceLine, secretLines[4])
   133  
   134  			// private key is not exposed, so check string is present and base64 encoded
   135  			assert.Equal(t, "PrivateKey = ", secretLines[5][:13])
   136  			privatekey := secretLines[5][13:]
   137  			_, err := base64.StdEncoding.DecodeString(privatekey)
   138  			assert.NoError(t, err)
   139  
   140  			// check relay interface MTU is set
   141  			expectedMTULine := fmt.Sprintf("MTU = %s", constants.MTU)
   142  			assert.Equal(t, expectedMTULine, secretLines[6])
   143  
   144  			// check friendly name is set for peer config
   145  			expectedClientFriendlyName := "# friendly_json={\"cluster_name\":\"cluster_infra\"}"
   146  			assert.Equal(t, expectedClientFriendlyName, secretLines[9])
   147  
   148  			// check client public key is set in peer config
   149  			expectedClientPublicKeyLine := fmt.Sprintf("PublicKey = %s", testPublicKey)
   150  			assert.Equal(t, expectedClientPublicKeyLine, secretLines[11])
   151  
   152  			// check store friendly name is set in peer config
   153  			expectedStoreFriendlyName := fmt.Sprintf("# friendly_json={\"cluster_name\":\"%s\",\"cluster\":\"%s\",\"vpn_enabled\":\"%t\"}", "4c4d-30-05-22", clusterAName, true)
   154  			assert.Equal(t, expectedStoreFriendlyName, secretLines[14])
   155  
   156  			// check store ip address is set in peer config
   157  			expectedStoreAllowedIPsLine := fmt.Sprintf("AllowedIPs = %s/32", storeConfigs[clusterAName].GetIPAddress())
   158  			assert.Equal(t, expectedStoreAllowedIPsLine, secretLines[15])
   159  
   160  			// check store public key is set in peer config
   161  			expectedStorePublicKeyline := fmt.Sprintf("PublicKey = %s", storeConfigs[clusterAName].GetPublicKey())
   162  			assert.Equal(t, expectedStorePublicKeyline, secretLines[16])
   163  			return ctx
   164  		}).
   165  		Test("multiple stores are added to relay secret", func(ctx f2.Context, t *testing.T) f2.Context {
   166  			// add store one and check peer is added to relay config
   167  			storeConfigs := map[string]*store.Store{clusterAName: storeAWg}
   168  			secret := relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   169  			secretLines := getLinesFromWireguardSecretData(secret)
   170  			assert.Len(t, secretLines, 18)
   171  			assert.Equal(t, "[Peer]", secretLines[8])
   172  
   173  			// add store two and check another peer is added to relay config
   174  			storeConfigs = map[string]*store.Store{clusterAName: storeAWg, clusterBName: storeBWg}
   175  			secret = relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   176  			secretLines = getLinesFromWireguardSecretData(secret)
   177  			assert.Len(t, secretLines, 23)
   178  			assert.Equal(t, "[Peer]", secretLines[13])
   179  
   180  			// add store three and check another peer is added to relay config
   181  			storeConfigs = map[string]*store.Store{clusterAName: storeAWg, clusterBName: storeBWg, clusterCName: storeCWg}
   182  			secret = relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   183  			secretLines = getLinesFromWireguardSecretData(secret)
   184  			assert.Len(t, secretLines, 28)
   185  			assert.Equal(t, "[Peer]", secretLines[18])
   186  			return ctx
   187  		}).
   188  		Test("store can be removed from relay secret", func(ctx f2.Context, t *testing.T) f2.Context {
   189  			// add store and check peer is added to relay config
   190  			storeConfigs := map[string]*store.Store{clusterAName: storeAWg}
   191  			secret := relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   192  			secretLines := getLinesFromWireguardSecretData(secret)
   193  			assert.Len(t, secretLines, 18)
   194  			assert.Equal(t, "[Peer]", secretLines[8])
   195  
   196  			// remove store and check peer is removed from relay config
   197  			storeConfigs = map[string]*store.Store{}
   198  			secret = relay.GenerateConfigurationSecret(subnet, clientIP, testPublicKey, storeConfigs)
   199  			secretLines = getLinesFromWireguardSecretData(secret)
   200  			assert.Len(t, secretLines, 13)
   201  			return ctx
   202  		}).Feature()
   203  	f.Test(t, feature)
   204  }
   205  
   206  func getLinesFromWireguardSecretData(secret *corev1.Secret) []string {
   207  	secretData := secret.StringData[constants.WireguardSecretField]
   208  	return strings.Split(secretData, "\n")
   209  }
   210  
   211  func createCluster(name string) *v1cluster.Cluster {
   212  	return &v1cluster.Cluster{
   213  		ObjectMeta: metav1.ObjectMeta{Name: name},
   214  		Spec: v1cluster.ClusterSpec{
   215  			Banner:       "dev0-zynstra",
   216  			Fleet:        "store",
   217  			Location:     "us-east1-c",
   218  			Name:         "4c4d-30-05-22",
   219  			Organization: "edge-dev0-retail-gmi062",
   220  			ProjectID:    projectID,
   221  			Type:         "sds",
   222  		},
   223  	}
   224  }
   225  
   226  func createVPNConfig(name, ip string) *v1vpnconfig.VPNConfig {
   227  	return &v1vpnconfig.VPNConfig{
   228  		TypeMeta:   metav1.TypeMeta{Kind: "VPNConfig", APIVersion: "remoteaccess.edge.ncr.com"},
   229  		ObjectMeta: metav1.ObjectMeta{Namespace: constants.VPNNamespace, Name: name, UID: "1234"},
   230  		Spec: v1vpnconfig.VPNConfigSpec{
   231  			Enabled: true,
   232  		},
   233  		Status: &v1vpnconfig.VPNConfigStatus{
   234  			IP: ip,
   235  		},
   236  	}
   237  }
   238  

View as plain text