Skip to content

Commit a799c17

Browse files
author
Serhii Zakharov
authored
add egress ips support to anonymizer (#446)
* add egress ips to anonymizer * unit test for cidrs including egress one * calm down linters * fix docs
1 parent 2040a71 commit a799c17

File tree

2 files changed

+127
-8
lines changed

2 files changed

+127
-8
lines changed

pkg/anonymization/anonymizer.go

+29-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// `cluster-api.openshift.example.com` will become `cluster-api.<CLUSTER_BASE_DOMAIN>`
1010
// - IPv4 addresses. Using a config client, it retrieves cluster networks and uses them to anonymize IP addresses
1111
// preserving subnet information. For example, if you have the following networks in your cluster:
12-
// "10.128.0.0/14", "172.30.0.0/16", "127.0.0.1/8"(added by default) the anonymization will handle the IPs like this:
12+
// "10.128.0.0/14", "172.30.0.0/16", "127.0.0.0/8"(added by default) the anonymization will handle the IPs like this:
1313
// - 10.128.0.0 -> 10.128.0.0 // subnetwork itself won't be anonymized
1414
// - 10.128.0.55 -> 10.128.0.1
1515
// - 10.128.0.56 -> 10.128.0.2
@@ -30,6 +30,7 @@ import (
3030
"strings"
3131

3232
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
33+
networkv1client "github.com/openshift/client-go/network/clientset/versioned/typed/network/v1"
3334
corev1 "k8s.io/api/core/v1"
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3536
"k8s.io/client-go/kubernetes"
@@ -83,7 +84,7 @@ type ConfigProvider interface {
8384

8485
// NewAnonymizer creates a new instance of anonymizer with a provided config observer and sensitive data
8586
func NewAnonymizer(clusterBaseDomain string, networks []string, secretsClient corev1client.SecretInterface) (*Anonymizer, error) {
86-
networks = append(networks, "127.0.0.1/8")
87+
networks = append(networks, "127.0.0.0/8")
8788

8889
cidrs, err := k8snet.ParseCIDRs(networks)
8990
if err != nil {
@@ -110,7 +111,10 @@ func NewAnonymizer(clusterBaseDomain string, networks []string, secretsClient co
110111

111112
// NewAnonymizerFromConfigClient creates a new instance of anonymizer with a provided openshift config client
112113
func NewAnonymizerFromConfigClient(
113-
ctx context.Context, kubeClient kubernetes.Interface, configClient configv1client.ConfigV1Interface,
114+
ctx context.Context,
115+
kubeClient kubernetes.Interface,
116+
configClient configv1client.ConfigV1Interface,
117+
networkClient networkv1client.NetworkV1Interface,
114118
) (*Anonymizer, error) {
115119
baseDomain, err := utils.GetClusterBaseDomain(ctx, configClient)
116120
if err != nil {
@@ -136,13 +140,25 @@ func NewAnonymizerFromConfigClient(
136140
return nil, err
137141
}
138142

139-
secretsClient := kubeClient.CoreV1().Secrets(secretNamespace)
140-
141143
if installConfig, exists := clusterConfigV1.Data["install-config"]; exists {
142144
networkRegex := regexp.MustCompile(Ipv4NetworkRegex)
143145
networks = append(networks, networkRegex.FindAllString(installConfig, -1)...)
144146
}
145147

148+
// egress subnets
149+
150+
hostSubnets, err := networkClient.HostSubnets().List(ctx, metav1.ListOptions{})
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
for i := range hostSubnets.Items {
156+
hostSubnet := &hostSubnets.Items[i]
157+
for _, egressCIDR := range hostSubnet.EgressCIDRs {
158+
networks = append(networks, string(egressCIDR))
159+
}
160+
}
161+
146162
// we're sorting by subnet lengths, if they are the same, we use subnet itself
147163
utils.SortAndRemoveDuplicates(&networks, func(i, j int) bool {
148164
if !strings.Contains(networks[i], "/") || !strings.Contains(networks[j], "/") {
@@ -160,6 +176,8 @@ func NewAnonymizerFromConfigClient(
160176
return network1[0] > network2[0]
161177
})
162178

179+
secretsClient := kubeClient.CoreV1().Secrets(secretNamespace)
180+
163181
return NewAnonymizer(baseDomain, networks, secretsClient)
164182
}
165183

@@ -177,7 +195,12 @@ func NewAnonymizerFromConfig(
177195
return nil, err
178196
}
179197

180-
return NewAnonymizerFromConfigClient(ctx, kubeClient, configClient)
198+
networkClient, err := networkv1client.NewForConfig(kubeConfig)
199+
if err != nil {
200+
return nil, err
201+
}
202+
203+
return NewAnonymizerFromConfigClient(ctx, kubeClient, configClient, networkClient)
181204
}
182205

183206
// AnonymizeMemoryRecord takes record.MemoryRecord, removes the sensitive data from it and returns the same object

pkg/anonymization/anonymizer_test.go

+98-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package anonymization
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"testing"
78

9+
configv1 "github.com/openshift/api/config/v1"
10+
networkv1 "github.com/openshift/api/network/v1"
11+
configfake "github.com/openshift/client-go/config/clientset/versioned/fake"
12+
networkfake "github.com/openshift/client-go/network/clientset/versioned/fake"
13+
"github.com/stretchr/testify/assert"
814
corev1 "k8s.io/api/core/v1"
15+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
916
"k8s.io/apimachinery/pkg/runtime"
10-
11-
"github.com/stretchr/testify/assert"
1217
kubefake "k8s.io/client-go/kubernetes/fake"
1318
corefake "k8s.io/client-go/kubernetes/typed/core/v1/fake"
1419
clienttesting "k8s.io/client-go/testing"
@@ -241,3 +246,94 @@ func Test_Anonymizer_StoreTranslationTable(t *testing.T) {
241246
assert.Equal(t, secret.StringData[fmt.Sprintf("192.168.0.%v", 255-i)], fmt.Sprintf("192.168.0.%v", i+1))
242247
}
243248
}
249+
250+
func TestAnonymizer_NewAnonymizerFromConfigClient(t *testing.T) {
251+
const testClusterBaseDomain = "example.com"
252+
localhostCIDR := "127.0.0.0/8"
253+
_, localhostNet, err := net.ParseCIDR(localhostCIDR)
254+
assert.NoError(t, err)
255+
cidr1 := "55.44.0.0/16"
256+
_, net1, err := net.ParseCIDR(cidr1)
257+
assert.NoError(t, err)
258+
cidr2 := "192.168.0.0/16"
259+
_, net2, err := net.ParseCIDR(cidr2)
260+
assert.NoError(t, err)
261+
egressCIDR := "10.0.0.0/8"
262+
_, egressNet, err := net.ParseCIDR(egressCIDR)
263+
assert.NoError(t, err)
264+
testNetworks := []subnetInformation{
265+
{
266+
network: *egressNet,
267+
lastIP: net.IPv4(10, 0, 0, 0),
268+
},
269+
{
270+
network: *net1,
271+
lastIP: net.IPv4(55, 44, 0, 0),
272+
},
273+
{
274+
network: *net2,
275+
lastIP: net.IPv4(192, 168, 0, 0),
276+
},
277+
{
278+
network: *localhostNet,
279+
lastIP: net.IPv4(127, 0, 0, 0),
280+
},
281+
}
282+
283+
kubeClient := kubefake.NewSimpleClientset()
284+
coreClient := kubeClient.CoreV1()
285+
networkClient := networkfake.NewSimpleClientset().NetworkV1()
286+
configClient := configfake.NewSimpleClientset().ConfigV1()
287+
ctx := context.TODO()
288+
289+
// create fake resources
290+
_, err = configClient.DNSes().Create(ctx, &configv1.DNS{
291+
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
292+
Spec: configv1.DNSSpec{BaseDomain: testClusterBaseDomain},
293+
}, metav1.CreateOptions{})
294+
assert.NoError(t, err)
295+
296+
_, err = configClient.Networks().Create(context.TODO(), &configv1.Network{
297+
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
298+
Spec: configv1.NetworkSpec{
299+
ClusterNetwork: []configv1.ClusterNetworkEntry{{CIDR: cidr1}},
300+
ServiceNetwork: []string{cidr2},
301+
ExternalIP: &configv1.ExternalIPConfig{Policy: &configv1.ExternalIPPolicy{}},
302+
},
303+
}, metav1.CreateOptions{})
304+
assert.NoError(t, err)
305+
306+
_, err = coreClient.ConfigMaps("kube-system").Create(ctx, &corev1.ConfigMap{
307+
ObjectMeta: metav1.ObjectMeta{Name: "cluster-config-v1"},
308+
}, metav1.CreateOptions{})
309+
assert.NoError(t, err)
310+
311+
_, err = networkClient.HostSubnets().Create(ctx, &networkv1.HostSubnet{
312+
EgressCIDRs: []networkv1.HostSubnetEgressCIDR{networkv1.HostSubnetEgressCIDR(egressCIDR)},
313+
}, metav1.CreateOptions{})
314+
assert.NoError(t, err)
315+
316+
// test that everything was initialized correctly
317+
318+
anonymizer, err := NewAnonymizerFromConfigClient(
319+
context.TODO(),
320+
kubeClient,
321+
configClient,
322+
networkClient,
323+
)
324+
assert.NoError(t, err)
325+
assert.NotNil(t, anonymizer)
326+
327+
assert.Equal(t, testClusterBaseDomain, anonymizer.clusterBaseDomain)
328+
assert.Empty(t, anonymizer.translationTable)
329+
assert.NotNil(t, anonymizer.ipNetworkRegex)
330+
assert.NotNil(t, anonymizer.secretsClient)
331+
332+
assert.Equal(t, len(testNetworks), len(anonymizer.networks))
333+
// the networks are already sorted in anonymizer
334+
for i, subnetInfo := range anonymizer.networks {
335+
expectedSubnetInfo := testNetworks[i]
336+
assert.Equal(t, expectedSubnetInfo.network.Network(), subnetInfo.network.Network())
337+
assert.Equal(t, expectedSubnetInfo.lastIP.String(), subnetInfo.lastIP.String())
338+
}
339+
}

0 commit comments

Comments
 (0)