Skip to main content

Kubernetes: Netbird Operator

Kubernetes - This article is part of a series.
Part 6: This Article

Accessing the Cluster Remotely with Netbird
#

To access the cluster from outside of the home network we can deploy the Netbird operator for Kubernetes. Similar to tailscale, this allows us to access all of our services through a private mesh network. The operator handles the creation of network router nodes to create secure Wireguard tunnels to our exposed services.

Prerequisites
#

Using the Netbird dashboard, a DNS zone, named “homelab” in this case, must be creted along with a srevice user api key with admin permissions.

Deploying Netbird
#

Helm
#

We can use the official Helm chart to install the Netbird Operator:

{
  self,
  inputs,
  ...
}:
{
  flake.modules.nixos.netbird-operator-charts =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    let
      netbirdOperatorChart = {
        name = "netbird-operator";
        repo = "oci://ghcr.io/netbirdio/helm-charts/netbird-operator";
        version = "0.7.0";
        hash = "sha256-5/uW/ufWfSKT8ZlLZEVY2pXqxAS8es0L1VV5/xd8byo=";
      };
    in
    {
      config = lib.mkIf config.netbird-operator.enable {
        services.k3s.autoDeployCharts = {
          netbird-operator = netbirdOperatorChart // {
            targetNamespace = "netbird";
            createNamespace = true;
          };
        };
      };
    };
}

Preloading the Operator and Router Images
#

The images can be preloaded as usual with their corresponding helm values set:

{
  self,
  inputs,
  ...
}:
{
  flake.modules.nixos.netbird-operator-images =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    let
      operatorImage = pkgs.dockerTools.pullImage {
        imageName = "ghcr.io/netbirdio/netbird-operator";
        imageDigest = "sha256:4ac288a3c2534553dc5cef02b6fd258673cf82822c5d90a06f16eaaffbe70bf8";
        hash = "sha256-KkQM0EbNzX2hCjiZ/EGcXMdS9ZutkiEYy689yhPoS9g=";
        finalImageTag = "v0.7.0";
        arch = "amd64";
      };
      routerImage = pkgs.dockerTools.pullImage {
        imageName = "ghcr.io/netbirdio/netbird";
        imageDigest = "sha256:b41e7f4197e4e7fde09effc6722e182cc96b39625114e0830227d407670db607";
        hash = "sha256-KFm29OcXByByzkZ9kbDmtLq++IbxpI1e9LUNjz6bhNI=";
        finalImageTag = "0.71.4-rootless";
        arch = "amd64";
      };
    in
    {
      config = lib.mkIf config.netbird-operator.enable {
        services.k3s.images = [
          operatorImage
          routerImage
        ];
      };
    };
}

Configuring the Network Router
#

Using the netbird.io api we can deploy our network routers:

{
  self,
  inputs,
  ...
}:
{
  flake.modules.nixos.netbird-operator-router =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    {
      config = lib.mkIf config.netbird-operator.enable {
        services.k3s.autoDeployCharts.netbird-operator.extraDeploy = [
          {
            apiVersion = "netbird.io/v1alpha1";
            kind = "NetworkRouter";
            metadata = {
              name = "homelab";
              namespace = "netbird";
            };
            spec = {
              dnsZoneRef.name = "homelab";
              workloadOverride = {
                replicas = 1;
              };
            };
          }
        ];
      };
    };
}

Adding the Netbird Secret
#

Using sops-nix we can add a separate manifest to deploy the required Kubernetes secrets:

{
  self,
  inputs,
  ...
}:
{
  flake.modules.nixos.netbird-operator-secrets =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    {
      config =
        lib.mkIf
          (config.netbird-operator.enable && config.secrets.enable && config.secrets.netbird-operator.enable)
          {
            sops = {
              secrets = {
                "netbird/key" = { };
              };
              templates = {
                netbirdMgmtApiKey = {
                  content = builtins.toJSON {
                    apiVersion = "v1";
                    kind = "Secret";
                    metadata = {
                      name = "netbird-mgmt-api-key";
                      namespace = "netbird";
                    };
                    type = "Opaque";
                    immutable = true;
                    stringData = {
                      NB_API_KEY = config.sops.placeholder."netbird/key";
                    };
                  };
                  path = "/var/lib/rancher/k3s/server/manifests/netbird-mgmt-api-key.json";
                };
              };
            };
          };
    };
}

Exposing Services
#

Once installed, we can deploy NetworkResource manifests to expose Kubernetes services:

{
  self,
  inputs,
  ...
}:
{
  flake.modules.nixos.immich-services =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    {
      config = lib.mkIf config.immich.enable {
        services.k3s.autoDeployCharts.immich.extraDeploy = [
          {
            apiVersion = "v1";
            kind = "Service";
            metadata = {
              name = "immich-lb";
              namespace = "immich";
            };
            spec = {
              type = "LoadBalancer";
              loadBalancerIP = "192.168.1.205";
              selector = {
                "app.kubernetes.io/controller" = "main";
                "app.kubernetes.io/instance" = "immich";
                "app.kubernetes.io/name" = "server";
              };
              ports = [
                {
                  name = "http";
                  port = 2283;
                  targetPort = 2283;
                  protocol = "TCP";
                }
              ];
            };
          }
          {
            apiVersion = "v1";
            kind = "Service";
            metadata = {
              name = "immich";
              namespace = "immich";
            };
            spec = {
              type = "ClusterIP";
              selector = {
                "app.kubernetes.io/controller" = "main";
                "app.kubernetes.io/instance" = "immich";
                "app.kubernetes.io/name" = "server";
              };
              ports = [
                {
                  name = "http";
                  port = 80;
                  targetPort = 2283;
                  protocol = "TCP";
                }
              ];
            };
          }
          {
            apiVersion = "netbird.io/v1alpha1";
            kind = "NetworkResource";
            metadata = {
              name = "immich";
              namespace = "immich";
            };
            spec = {
              networkRouterRef = {
                name = "homelab";
                namespace = "netbird";
              };
              serviceRef.name = "immich";
              groups = [ { name = "All"; } ];
            };
          }
        ];
      };
    };
}

Managing the Cluster Network in Netbird
#

With our services deployed, we can manage the generate resources in the “homelab” network on the Netbird dashboard:

Netbird Dashboard

Kubernetes - This article is part of a series.
Part 6: This Article