Add support for running as a init sidecar container (#99)

Adds support for injecting the client as a [sidecar
container](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/)
using the `netbird.io/init-sidecar: "true"` pod annotation.
This commit is contained in:
Bethuel Mmbaga
2026-02-11 15:20:52 +03:00
committed by GitHub
parent d548240320
commit 5fb144559c
5 changed files with 123 additions and 17 deletions
-1
View File
@@ -105,7 +105,6 @@ func main() {
"",
"Default labels used for all resources, in format key=value,key=value",
)
// Controller generic flags
var (
metricsAddr string
+17 -8
View File
@@ -49,6 +49,15 @@ Since v0.27.0, NetBird supports extra DNS labels, which extends the DNS names th
```
With this setup, all peers with the same extra label would be used in a DNS round-robin fashion.
### Init Sidecar Mode
By default, the NetBird container is injected as a regular sidecar container. For workloads like Jobs and CronJobs where the pod should terminate after the main container completes, you can use init sidecar mode. This injects NetBird as an init container with `restartPolicy: Always`.
To enable init sidecar mode, add the following annotation:
```yaml
netbird.io/init-sidecar: "true"
```
## Provisioning Networks (Ingress Functionality)
### Granting controller access to NetBird Management
@@ -89,10 +98,10 @@ cluster:
```yaml
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/user/.minikube/ca.crt
server: https://kubernetes.default.svc.cluster.local
name: minikube
- cluster:
certificate-authority: /home/user/.minikube/ca.crt
server: https://kubernetes.default.svc.cluster.local
name: minikube
```
### Exposing a Service
@@ -175,12 +184,12 @@ ingress:
name: Kubernetes Default Policy # Required, name of policy in NetBird console
description: Default # Optional
sourceGroups: # Required, name of groups to assign as source in Policy.
- All
- All
ports: # Optional, resources annotated 'netbird.io/policy=default' will append to this.
- 443
- 443
protocols: # Optional, restricts protocols allowed to resources, defaults to ['tcp', 'udp'].
- tcp
- udp
- tcp
- udp
bidirectional: true # Optional, defaults to true
```
2. Reference policies in Services using `netbird.io/policy=default,otherpolicy,...`, this will add relevant ports and destination groups to policies.
+55
View File
@@ -0,0 +1,55 @@
apiVersion: v1
kind: Secret
metadata:
name: test
namespace: default
stringData:
SETUP_KEY: 50445ABC-8901-4050-8047-0A390658A79B # Replace with valid setup key
---
apiVersion: netbird.io/v1
kind: NBSetupKey
metadata:
name: test
namespace: default
spec:
secretKeyRef:
name: test
key: SETUP_KEY
---
apiVersion: batch/v1
kind: Job
metadata:
name: job
spec:
template:
metadata:
annotations:
netbird.io/setup-key: test
netbird.io/init-sidecar: "true"
spec:
containers:
- name: worker
image: curlimages/curl:latest
command: ["sh", "-c", "curl -s https://example.com && echo done && sleep 60"]
restartPolicy: Never
backoffLimit: 3
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob
spec:
schedule: "*/30 * * * *"
jobTemplate:
spec:
template:
metadata:
annotations:
netbird.io/setup-key: test
netbird.io/init-sidecar: "true"
spec:
containers:
- name: worker
image: curlimages/curl:latest
command: ["sh", "-c", "curl -s https://example.com && echo done"]
restartPolicy: Never
+24 -8
View File
@@ -33,6 +33,7 @@ import (
const (
setupKeyAnnotation = "netbird.io/setup-key"
sidecarAnnotation = "netbird.io/init-sidecar"
)
// nolint:unused
@@ -121,8 +122,27 @@ func (d *PodNetbirdInjector) Default(ctx context.Context, obj runtime.Object) er
}
}
// Append the netbird container with the constructed env vars.
pod.Spec.Containers = append(pod.Spec.Containers, corev1.Container{
// Build the netbird container spec.
nbContainer := d.buildNetbirdContainer(envVars, nbSetupKey.Spec.VolumeMounts)
// If sidecar mode is requested, inject as a sidecar (init container with restartPolicy: Always).
if pod.Annotations[sidecarAnnotation] == "true" {
restartPolicy := corev1.ContainerRestartPolicyAlways
nbContainer.RestartPolicy = &restartPolicy
pod.Spec.InitContainers = append(pod.Spec.InitContainers, nbContainer)
} else {
pod.Spec.Containers = append(pod.Spec.Containers, nbContainer)
}
pod.Spec.Volumes = append(pod.Spec.Volumes, nbSetupKey.Spec.Volumes...)
return nil
}
// buildNetbirdContainer constructs the NetBird container spec with the given
// environment variables and volume mounts.
func (d *PodNetbirdInjector) buildNetbirdContainer(envVars []corev1.EnvVar, volumeMounts []corev1.VolumeMount) corev1.Container {
return corev1.Container{
Name: "netbird",
Image: d.clientImage,
Env: envVars,
@@ -131,10 +151,6 @@ func (d *PodNetbirdInjector) Default(ctx context.Context, obj runtime.Object) er
Add: []corev1.Capability{"NET_ADMIN"},
},
},
VolumeMounts: nbSetupKey.Spec.VolumeMounts,
})
pod.Spec.Volumes = append(pod.Spec.Volumes, nbSetupKey.Spec.Volumes...)
return nil
VolumeMounts: volumeMounts,
}
}
+27
View File
@@ -125,6 +125,33 @@ var _ = Describe("Pod Webhook", func() {
Expect(obj.Spec.Containers).To(HaveLen(2))
Expect(obj.Spec.Containers[1].Name).To(Equal("netbird"))
})
It("Should inject NB container as native sidecar when init-sidecar annotation is true", func() {
obj.Annotations[sidecarAnnotation] = "true"
Expect(defaulter.Default(context.Background(), obj)).NotTo(HaveOccurred())
Expect(obj.Spec.Containers).To(HaveLen(1), "original containers should be unchanged")
Expect(obj.Spec.InitContainers).To(HaveLen(1))
Expect(obj.Spec.InitContainers[0].Name).To(Equal("netbird"))
Expect(obj.Spec.InitContainers[0].RestartPolicy).NotTo(BeNil())
Expect(*obj.Spec.InitContainers[0].RestartPolicy).To(Equal(corev1.ContainerRestartPolicyAlways))
})
It("Should inject NB as regular container when init-sidecar annotation is false", func() {
obj.Annotations[sidecarAnnotation] = "false"
Expect(defaulter.Default(context.Background(), obj)).NotTo(HaveOccurred())
Expect(obj.Spec.Containers).To(HaveLen(2))
Expect(obj.Spec.Containers[1].Name).To(Equal("netbird"))
Expect(obj.Spec.InitContainers).To(BeEmpty())
})
It("Should inject NB as regular container when init-sidecar annotation is absent", func() {
delete(obj.Annotations, sidecarAnnotation)
Expect(defaulter.Default(context.Background(), obj)).NotTo(HaveOccurred())
Expect(obj.Spec.Containers).To(HaveLen(2))
Expect(obj.Spec.Containers[1].Name).To(Equal("netbird"))
Expect(obj.Spec.InitContainers).To(BeEmpty())
})
})
})
})