Skip to content

Commit ea2dafd

Browse files
authored
Add network egress resource (#357)
This change adds a new import resource which enables exposing Netbird resources as Kubernetes services. This remove the need to add sidecars to every pod. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a new `NetworkEgress` custom resource (`netbird.io/v1alpha1`) with CRD, schema validation, and status/conditions. * Extended controller functionality to create egress services and translate egress rules into import `EndpointSlice` resources; egress pods now include a kube-egress-forwarder sidecar. * **Bug Fixes** * Added missing deep-copy and declarative apply support for the new `NetworkEgress` API types. * **Documentation** * Updated README/API reference and added example manifests for `NetworkEgress` (including IP/FQDN target usage). <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Philip Laine <philip.laine@gmail.com>
1 parent 3c1c667 commit ea2dafd

31 files changed

Lines changed: 2218 additions & 81 deletions

.golangci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ linters:
6565
alias: admissionv1
6666
- pkg: k8s.io/api/policy/v1
6767
alias: policyv1
68+
- pkg: "k8s.io/api/discovery/v1"
69+
alias: discoveryv1
6870
- pkg: k8s.io/client-go/applyconfigurations/meta/v1
6971
alias: metav1ac
7072
- pkg: k8s.io/client-go/applyconfigurations/core/v1
@@ -75,6 +77,8 @@ linters:
7577
alias: appsv1ac
7678
- pkg: k8s.io/client-go/applyconfigurations/policy/v1
7779
alias: policyv1ac
80+
- pkg: k8s.io/client-go/applyconfigurations/discovery/v1
81+
alias: discoveryv1ac
7882

7983
- pkg: sigs.k8s.io/gateway-api/apis/v1
8084
alias: gwv1

PROJECT

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,12 @@ resources:
123123
kind: ClusterProxy
124124
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
125125
version: v1alpha1
126+
- api:
127+
crdVersion: v1
128+
namespaced: true
129+
controller: true
130+
domain: netbird.io
131+
kind: NetworkEgress
132+
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
133+
version: v1alpha1
126134
version: "3"

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ helm upgrade --install --create-namespace -n netbird netbird-operator oci://ghcr
2323

2424
| Kind | API Version |
2525
|------|-------------|
26+
| [SetupKey](docs/api-reference.md#setupkey) | `netbird.io/v1alpha1` |
2627
| [Group](docs/api-reference.md#group) | `netbird.io/v1alpha1` |
27-
| [NetworkResource](docs/api-reference.md#networkresource) | `netbird.io/v1alpha1` |
2828
| [NetworkRouter](docs/api-reference.md#networkrouter) | `netbird.io/v1alpha1` |
29-
| [SetupKey](docs/api-reference.md#setupkey) | `netbird.io/v1alpha1` |
29+
| [NetworkResource](docs/api-reference.md#networkresource) | `netbird.io/v1alpha1` |
30+
| [NetworkEgress](docs/api-reference.md#networkegress) | `netbird.io/v1alpha1` |
3031
| [SidecarProfile](docs/api-reference.md#sidecarprofile) | `netbird.io/v1alpha1` |
3132
| [ClusterProxy](docs/api-reference.md#clusterproxy) | `netbird.io/v1alpha1` |
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
package v1alpha1
4+
5+
import (
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
)
8+
9+
// NetworkEgressSpec defines the desired state of NetworkEgress.
10+
type NetworkEgressSpec struct {
11+
// NetworkRouterRef is a reference to the network and router where the resource will be created.
12+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
13+
NetworkRouterRef CrossNamespaceReference `json:"networkRouterRef"`
14+
15+
// Target for egress traffic.
16+
Target NetworkEgressTarget `json:"target"`
17+
18+
// Ports to the resource to route.
19+
// +kubebuilder:validation:MinItems=1
20+
// +kubebuilder:validation:Required
21+
Ports []NetworkEgressPort `json:"ports"`
22+
}
23+
24+
// NetworkEgressTarget describes a single allowed egress destination.
25+
// Exactly one of IP or FQDN must be set.
26+
// +kubebuilder:validation:XValidation:rule="(has(self.ip) ? 1 : 0) + (has(self.fqdn) ? 1 : 0) == 1",message="exactly one of ip or fqdn must be set"
27+
type NetworkEgressTarget struct {
28+
// IP targets a single specific IP address (not a CIDR range).
29+
// +optional
30+
IP *NetworkEgressIPTarget `json:"ip,omitempty"`
31+
32+
// FQDN targets an exact domain name (no wildcards).
33+
// +optional
34+
FQDN *NetworkEgressFQDNTarget `json:"fqdn,omitempty"`
35+
}
36+
37+
// NetworkEgressIPTarget is a single IPv4 or IPv6 address.
38+
type NetworkEgressIPTarget struct {
39+
// Address is a single IP address.
40+
// +kubebuilder:validation:Required
41+
// +kubebuilder:validation:XValidation:rule="isIP(self)",message="address must be a valid IPv4 or IPv6 address"
42+
Address string `json:"address"`
43+
}
44+
45+
// NetworkEgressFQDNTarget matches traffic by an exact domain name (no wildcards).
46+
type NetworkEgressFQDNTarget struct {
47+
// Hostname is a fully qualified domain name to match exactly.
48+
// +kubebuilder:validation:Required
49+
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`
50+
Hostname string `json:"hostname"`
51+
}
52+
53+
type NetworkEgressPort struct {
54+
// Name of the port.
55+
// +required
56+
// +kubebuilder:validation:MinLength=1
57+
// +kubebuilder:validation:MaxLength=15
58+
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
59+
Name string `json:"name,omitempty"`
60+
61+
// The port that will be exposed by this service.
62+
// +required
63+
// +kubebuilder:validation:Minimum=1
64+
// +kubebuilder:validation:Maximum=65535
65+
Port int32 `json:"port"`
66+
}
67+
68+
// NetworkEgressStatus defines the observed state of NetworkEgress.
69+
type NetworkEgressStatus struct {
70+
// ObservedGeneration is the last reconciled generation.
71+
// +optional
72+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
73+
74+
// Conditions holds the conditions for the NetworkEgress.
75+
// +listType=map
76+
// +listMapKey=type
77+
// +optional
78+
Conditions []metav1.Condition `json:"conditions,omitempty"`
79+
}
80+
81+
// +kubebuilder:object:root=true
82+
// +kubebuilder:subresource:status
83+
// +kubebuilder:resource
84+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
85+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
86+
87+
// NetworkEgress is the Schema for the networkegresses API.
88+
type NetworkEgress struct {
89+
metav1.TypeMeta `json:",inline"`
90+
metav1.ObjectMeta `json:"metadata,omitempty"`
91+
92+
// +required
93+
Spec NetworkEgressSpec `json:"spec"`
94+
95+
// +kubebuilder:default={"observedGeneration":-1}
96+
Status NetworkEgressStatus `json:"status,omitempty"`
97+
}
98+
99+
// GetConditions returns the status conditions of the object.
100+
func (n *NetworkEgress) GetConditions() []metav1.Condition {
101+
return n.Status.Conditions
102+
}
103+
104+
// SetConditions sets the status conditions on the object.
105+
func (n *NetworkEgress) SetConditions(conditions []metav1.Condition) {
106+
n.Status.Conditions = conditions
107+
}
108+
109+
// +kubebuilder:object:root=true
110+
111+
// NetworkEgressList contains a list of NetworkEgress
112+
type NetworkEgressList struct {
113+
metav1.TypeMeta `json:",inline"`
114+
metav1.ListMeta `json:"metadata,omitzero"`
115+
Items []NetworkEgress `json:"items"`
116+
}
117+
118+
func init() {
119+
SchemeBuilder.Register(&NetworkEgress{}, &NetworkEgressList{})
120+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 173 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)