diff --git a/cmd/main.go b/cmd/main.go index 03aeda9..f9c80b5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -105,7 +105,6 @@ func main() { "", "Default labels used for all resources, in format key=value,key=value", ) - // Controller generic flags var ( metricsAddr string diff --git a/docs/usage.md b/docs/usage.md index ad6ff94..0825194 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -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. diff --git a/examples/sidecar/example.yaml b/examples/sidecar/example.yaml new file mode 100644 index 0000000..2cd0060 --- /dev/null +++ b/examples/sidecar/example.yaml @@ -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 diff --git a/internal/webhook/v1/pod_webhook.go b/internal/webhook/v1/pod_webhook.go index 0cb4286..5749ce7 100644 --- a/internal/webhook/v1/pod_webhook.go +++ b/internal/webhook/v1/pod_webhook.go @@ -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, + } } diff --git a/internal/webhook/v1/pod_webhook_test.go b/internal/webhook/v1/pod_webhook_test.go index c2003b9..b67c80d 100644 --- a/internal/webhook/v1/pod_webhook_test.go +++ b/internal/webhook/v1/pod_webhook_test.go @@ -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()) + }) + }) }) })