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
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
127 assert.Equal(t, expectedRelaySecretInterfaceLines, secretLines[:4])
128 assert.Equal(t, expectedRelaySecretPeerLines, secretLines[7:9])
129
130
131 expectedInterfaceLine := fmt.Sprintf("Address = %s", subnet)
132 assert.Equal(t, expectedInterfaceLine, secretLines[4])
133
134
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
141 expectedMTULine := fmt.Sprintf("MTU = %s", constants.MTU)
142 assert.Equal(t, expectedMTULine, secretLines[6])
143
144
145 expectedClientFriendlyName := "# friendly_json={\"cluster_name\":\"cluster_infra\"}"
146 assert.Equal(t, expectedClientFriendlyName, secretLines[9])
147
148
149 expectedClientPublicKeyLine := fmt.Sprintf("PublicKey = %s", testPublicKey)
150 assert.Equal(t, expectedClientPublicKeyLine, secretLines[11])
151
152
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
157 expectedStoreAllowedIPsLine := fmt.Sprintf("AllowedIPs = %s/32", storeConfigs[clusterAName].GetIPAddress())
158 assert.Equal(t, expectedStoreAllowedIPsLine, secretLines[15])
159
160
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
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
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
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
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
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