/
egresspolicy.go
149 lines (128 loc) · 5.06 KB
/
egresspolicy.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
// Copyright 2021 Authors of Cilium
//
// 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 egresspolicy
import (
"fmt"
"net"
k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
v2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
k8sLabels "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels"
slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
"github.com/cilium/cilium/pkg/logging/logfields"
"github.com/cilium/cilium/pkg/policy"
"github.com/cilium/cilium/pkg/policy/api"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/types"
)
// Config is the internal representation of Cilium Egress NAT Policy.
type Config struct {
// id is the parsed config name and namespace
id types.NamespacedName
endpointSelectors []api.EndpointSelector
dstCIDRs []*net.IPNet
egressIP net.IP
}
// PolicyID includes endpoint name and namespace
type endpointID = types.NamespacedName
// PolicyID includes policy name and namespace
type policyID = types.NamespacedName
// endpointMetadata stores relevant metadata associated with a endpoint that's updated during endpoint
// add/update events
type endpointMetadata struct {
// Endpoint labels
labels map[string]string
// Endpoint ID
id endpointID
// ips are endpoint's unique IPs
ips []string
}
// policyConfigSelectsEndpoint determines if the given endpoint is selected by the policy
// config based on matching labels of config and endpoint.
func (config *Config) policyConfigSelectsEndpoint(endpointInfo *endpointMetadata) bool {
labelsToMatch := k8sLabels.Set(endpointInfo.labels)
for _, selector := range config.endpointSelectors {
if selector.Matches(labelsToMatch) {
return true
}
}
return false
}
// Parse takes a CiliumEgressNATPolicy CR and converts to Config, the internal
// representation of the egress nat policy
func Parse(cenp *v2alpha1.CiliumEgressNATPolicy) (*Config, error) {
var endpointSelectorList []api.EndpointSelector
var dstCidrList []*net.IPNet
allowAllNamespacesRequirement := slim_metav1.LabelSelectorRequirement{
Key: k8sConst.PodNamespaceLabel,
Operator: slim_metav1.LabelSelectorOpExists,
}
name := cenp.ObjectMeta.Name
if name == "" {
return nil, fmt.Errorf("CiliumEgressNATPolicy must have a name")
}
for _, cidrString := range cenp.Spec.DestinationCIDRs {
_, cidr, err := net.ParseCIDR(string(cidrString))
if err != nil {
log.WithError(err).WithFields(logrus.Fields{logfields.CiliumEgressNATPolicyName: name}).Warn("Error parsing cidr.")
return nil, err
}
dstCidrList = append(dstCidrList, cidr)
}
for _, egressRule := range cenp.Spec.Egress {
if egressRule.NamespaceSelector != nil {
prefixedNsSelector := egressRule.NamespaceSelector
matchLabels := map[string]string{}
// We use our own special label prefix for namespace metadata,
// thus we need to prefix that prefix to all NamespaceSelector.MatchLabels
for k, v := range egressRule.NamespaceSelector.MatchLabels {
matchLabels[policy.JoinPath(k8sConst.PodNamespaceMetaLabels, k)] = v
}
prefixedNsSelector.MatchLabels = matchLabels
// We use our own special label prefix for namespace metadata,
// thus we need to prefix that prefix to all NamespaceSelector.MatchLabels
for i, lsr := range egressRule.NamespaceSelector.MatchExpressions {
lsr.Key = policy.JoinPath(k8sConst.PodNamespaceMetaLabels, lsr.Key)
prefixedNsSelector.MatchExpressions[i] = lsr
}
// Empty namespace selector selects all namespaces (i.e., a namespace
// label exists).
if len(egressRule.NamespaceSelector.MatchLabels) == 0 && len(egressRule.NamespaceSelector.MatchExpressions) == 0 {
prefixedNsSelector.MatchExpressions = []slim_metav1.LabelSelectorRequirement{allowAllNamespacesRequirement}
}
endpointSelectorList = append(
endpointSelectorList,
api.NewESFromK8sLabelSelector("", prefixedNsSelector, egressRule.PodSelector))
} else if egressRule.PodSelector != nil {
endpointSelectorList = append(
endpointSelectorList,
api.NewESFromK8sLabelSelector("", egressRule.PodSelector))
} else {
return nil, fmt.Errorf("CiliumEgressNATPolicy cannot have both nil namespace selector and nil pod selector")
}
}
return &Config{
endpointSelectors: endpointSelectorList,
dstCIDRs: dstCidrList,
egressIP: net.ParseIP(cenp.Spec.EgressSourceIP).To4(),
id: types.NamespacedName{
Name: name,
},
}, nil
}
// ParseConfigID takes a CiliumEgressNATPolicy CR and returns only the config id
func ParseConfigID(cenp *v2alpha1.CiliumEgressNATPolicy) types.NamespacedName {
return policyID{
Name: cenp.Name,
}
}