-
Notifications
You must be signed in to change notification settings - Fork 376
/
Copy pathipsec_test.go
204 lines (182 loc) · 7.8 KB
/
ipsec_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright 2019 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package e2e
import (
"context"
"fmt"
"regexp"
"strconv"
"testing"
"k8s.io/apimachinery/pkg/util/wait"
"antrea.io/antrea/pkg/agent/util"
agentconfig "antrea.io/antrea/pkg/config/agent"
"antrea.io/antrea/pkg/features"
)
// TestIPSec is the top-level test which contains all subtests for
// IPsec related test cases so they can share setup, teardown.
func TestIPSec(t *testing.T) {
skipIfIPv6Cluster(t)
skipIfNumNodesLessThan(t, 2)
skipIfHasWindowsNodes(t)
skipIfAntreaIPAMTest(t)
skipIfProviderIs(t, "kind", "IPsec tests take too long to run and do not work with multiple Docker bridges")
data, err := setupTest(t)
if err != nil {
t.Fatalf("Error when setting up test: %v", err)
}
defer teardownTest(t, data)
skipIfMulticastEnabled(t, data)
t.Logf("Redeploy Antrea with IPsec tunnel enabled")
data.redeployAntrea(t, deployAntreaIPsec)
// Restore normal Antrea deployment with IPsec disabled.
defer data.redeployAntrea(t, deployAntreaDefault)
t.Run("testIPSecPSKAuth", func(t *testing.T) {
conf, err := data.getAgentConf(antreaNamespace)
failOnError(err, t)
if conf.IPsec.AuthenticationMode != "psk" {
t.Logf("Restarting Antrea Agent with IPsec PSK authentication mode. Current mode: %s", conf.IPsec.AuthenticationMode)
ac := func(config *agentconfig.AgentConfig) {
config.IPsec.AuthenticationMode = "psk"
}
if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil {
t.Fatalf("Failed to change IPsec authentication mode to PSK: %v", err)
}
}
t.Run("testIPSecTunnelConnectivity", func(t *testing.T) { testIPSecTunnelConnectivity(t, data, false) })
})
t.Run("testIPSecCertificateAuth", func(t *testing.T) {
skipIfFeatureDisabled(t, features.IPsecCertAuth, true, true)
conf, err := data.getAgentConf(antreaNamespace)
failOnError(err, t)
if conf.IPsec.AuthenticationMode != "cert" {
t.Logf("Restarting Antrea Agent with IPsec Certificate authentication mode. Current mode: %s", conf.IPsec.AuthenticationMode)
ac := func(config *agentconfig.AgentConfig) {
config.IPsec.AuthenticationMode = "cert"
}
if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil {
t.Fatalf("Failed to change IPsec authentication mode to Certificate: %v", err)
}
}
t.Run("testIPSecTunnelConnectivity", func(t *testing.T) { testIPSecTunnelConnectivity(t, data, true) })
})
t.Run("testIPSecDeleteStaleTunnelPorts", func(t *testing.T) { testIPSecDeleteStaleTunnelPorts(t, data) })
}
func (data *TestData) readSecurityAssociationsStatus(nodeName string) (up int, connecting int, isCertAuth bool, err error) {
antreaPodName, err := data.getAntreaPodOnNode(nodeName)
if err != nil {
return 0, 0, false, err
}
cmd := []string{"ipsec", "statusall"}
stdout, stderr, err := data.RunCommandFromPod(antreaNamespace, antreaPodName, "antrea-ipsec", cmd)
if err != nil {
return 0, 0, false, fmt.Errorf("error when running 'ipsec status' on '%s': %v - stdout: %s - stderr: %s", nodeName, err, stdout, stderr)
}
re := regexp.MustCompile(`Security Associations \((\d+) up, (\d+) connecting\)`)
matches := re.FindStringSubmatch(stdout)
if len(matches) == 0 {
return 0, 0, false, fmt.Errorf("unexpected 'ipsec statusall' output: %s", stdout)
}
v, err := strconv.ParseUint(matches[1], 10, 32)
if err != nil {
return 0, 0, false, fmt.Errorf("error when retrieving 'up' SAs from 'ipsec statusall' output: %v", err)
}
up = int(v)
v, err = strconv.ParseUint(matches[2], 10, 32)
if err != nil {
return 0, 0, false, fmt.Errorf("error when retrieving 'connecting' SAs from 'ipsec statusall' output: %v", err)
}
connecting = int(v)
re = regexp.MustCompile(`uses ([a-z-]+) key authentication`)
match := re.FindStringSubmatch(stdout)
if len(match) == 0 {
return 0, 0, false, fmt.Errorf("failed to determine authentication method from 'ipsec statusall' output: %s", stdout)
}
if match[1] == "pre-shared" {
isCertAuth = false
} else if match[1] == "public" {
isCertAuth = true
} else {
return 0, 0, false, fmt.Errorf("unknown key authentication mode %q", match[1])
}
return up, connecting, isCertAuth, nil
}
// testIPSecTunnelConnectivity checks that Pod traffic across two Nodes over
// the IPsec tunnel, by creating multiple Pods across distinct Nodes and having
// them ping each other.
func testIPSecTunnelConnectivity(t *testing.T, data *TestData, certAuth bool) {
var tag string
if certAuth {
tag = "ipsec-cert"
} else {
tag = "ipsec-psk"
}
podInfos, deletePods := createPodsOnDifferentNodes(t, data, data.testNamespace, tag)
defer deletePods()
t.Logf("Executing ping tests across Nodes: '%s' <-> '%s'", podInfos[0].NodeName, podInfos[1].NodeName)
// PMTU is wrong when using GRE+IPsec with some Linux kernel versions, do not set DF to work around.
// See https://github.com/antrea-io/antrea/issues/5922 for more details.
data.runPingMesh(t, podInfos[:2], toolboxContainerName, false)
// Check that there is at least one 'up' Security Association on the Node
nodeName := podInfos[0].NodeName
if up, _, isCertAuth, err := data.readSecurityAssociationsStatus(nodeName); err != nil {
t.Errorf("Error when reading Security Associations: %v", err)
} else if up == 0 {
t.Errorf("Expected at least one 'up' Security Association, but got %d", up)
} else if isCertAuth != certAuth {
t.Errorf("Expected certificate authentication to be %t, got %t", certAuth, isCertAuth)
} else {
t.Logf("Found %d 'up' SecurityAssociation(s) for Node '%s', certificate auth: %t", up, nodeName, isCertAuth)
}
}
// testIPSecDeleteStaleTunnelPorts checks that when switching from IPsec mode to
// non-encrypted mode, the previously created tunnel ports are deleted
// correctly.
func testIPSecDeleteStaleTunnelPorts(t *testing.T, data *TestData) {
nodeName0 := nodeName(0)
nodeName1 := nodeName(1)
antreaPodName := func() string {
antreaPodName, err := data.getAntreaPodOnNode(nodeName0)
if err != nil {
t.Fatalf("Error when retrieving the name of the Antrea Pod running on Node '%s': %v", nodeName0, err)
}
t.Logf("The Antrea Pod for Node '%s' is '%s'", nodeName0, antreaPodName)
return antreaPodName
}
portName := util.GenerateNodeTunnelInterfaceName(nodeName1)
doesOVSPortExist := func() bool {
exists, err := data.doesOVSPortExist(antreaPodName(), portName)
if err != nil {
t.Fatalf("Cannot determine if OVS port exists: %v", err)
}
return exists
}
t.Logf("Checking that tunnel port has been created")
if err := wait.PollUntilContextTimeout(context.Background(), defaultInterval, defaultTimeout, true, func(ctx context.Context) (found bool, err error) {
return doesOVSPortExist(), nil
}); wait.Interrupted(err) {
t.Fatalf("Timed out while waiting for OVS tunnel port to be created")
} else if err != nil {
t.Fatalf("Error while waiting for OVS tunnel port to be created")
}
t.Logf("Redeploy Antrea with IPsec tunnel disabled")
data.redeployAntrea(t, deployAntreaDefault)
t.Logf("Checking that tunnel port has been deleted")
if err := wait.PollUntilContextTimeout(context.Background(), defaultInterval, defaultTimeout, true, func(ctx context.Context) (found bool, err error) {
return !doesOVSPortExist(), nil
}); wait.Interrupted(err) {
t.Fatalf("Timed out while waiting for OVS tunnel port to be deleted")
} else if err != nil {
t.Fatalf("Error while waiting for OVS tunnel port to be deleted")
}
}