<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Misha Bragin</title>
    <description>The latest articles on DEV Community by Misha Bragin (@braginini).</description>
    <link>https://dev.to/braginini</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F930725%2Fe3f9eca6-a149-45b9-b921-fd197e5926c6.jpeg</url>
      <title>DEV Community: Misha Bragin</title>
      <link>https://dev.to/braginini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/braginini"/>
    <language>en</language>
    <item>
      <title>AWS Lambda Serverless Security. Mistakes, Oversights, and Potential Vulnerabilities</title>
      <dc:creator>Misha Bragin</dc:creator>
      <pubDate>Fri, 03 May 2024 09:07:15 +0000</pubDate>
      <link>https://dev.to/braginini/aws-lambda-serverless-security-mistakes-oversights-and-potential-vulnerabilities-3240</link>
      <guid>https://dev.to/braginini/aws-lambda-serverless-security-mistakes-oversights-and-potential-vulnerabilities-3240</guid>
      <description>&lt;p&gt;&lt;a href="https://aws.amazon.com/lambda/"&gt;Amazon Web Services (AWS) Lambda&lt;/a&gt; is a serverless function-as-a-service (FaaS) platform that lets you deploy, run, and scale code in the cloud as self-contained functions without having to manually configure any infrastructure. Lambda runs your functions on demand in response to specific events, such as an HTTP request from the internet or activity in another AWS service.&lt;/p&gt;

&lt;p&gt;Using Lambda helps accelerate your development cycles by letting engineers stay focused on code, but going serverless is also a potential security risk. Incorrectly configured functions can harbor several kinds of vulnerabilities that can be exploited to allow unauthorized access or cause instability in your deployments.&lt;/p&gt;

&lt;p&gt;In this guide, you'll explore some common Lambda security pitfalls and learn how to properly protect your functions. You need to understand how serverless changes the cloud security model so that you can confidently adopt the architecture while keeping your apps, data, and users safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does AWS Lambda Serverless Security Matter
&lt;/h2&gt;

&lt;p&gt;Security should be a primary concern when deploying a new AWS Lambda serverless function. Although Lambda handles infrastructure management for you, this doesn't mean you can trust its default configuration to automatically secure your functions and related resources.&lt;/p&gt;

&lt;p&gt;Lambda operates under a &lt;a href="https://aws.amazon.com/lambda/security-overview-of-aws-lambda"&gt;shared responsibility&lt;/a&gt; security model. AWS secures the physical infrastructure that runs the service—including software components, such as the operating system and Lambda platform—but you're responsible for protecting your functions, their code, the generated data, and network routes to other stack components.&lt;/p&gt;

&lt;p&gt;Correctly configuring serverless security is vital so you can safely use functions in business-critical scenarios, such as when your code processes sensitive or proprietary data. There are two main aspects to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Code security:&lt;/strong&gt; This relates to vulnerabilities that exist within your function's code. It includes areas such as dependency &lt;a href="https://cloud.google.com/software-supply-chain-security/docs/overview"&gt;supply chain security&lt;/a&gt; and the use of effective &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html"&gt;user input validation&lt;/a&gt; and sanitization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function security:&lt;/strong&gt; This covers the security of your Lambda functions and AWS environment, including the correct configuration of rate-limiting controls, resource constraints, and authorization requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, it's easy to make errors in both of these areas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common AWS Lambda Security Mistakes and Oversights
&lt;/h2&gt;

&lt;p&gt;Now that you've seen why it's important to secure your Lambda functions, let's analyze some of the most commonly experienced mistakes and oversights. Your functions could be vulnerable to these threats if you haven't already taken action to address them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inadequate Function-Level Permission Restrictions
&lt;/h3&gt;

&lt;p&gt;Inadequate permission checks within your function's code can allow attackers to gain unauthorized access to your data. This risk can be present in any service, but the fast-paced Lambda development cycle can mean the threat easily goes unnoticed. Developers may also have a false sense of security that resources running within AWS or Lambda are already being protected.&lt;/p&gt;

&lt;p&gt;You can address this problem by auditing all code for security issues before deployment. Additionally, you should &lt;a href="https://www.checkpoint.com/cyber-hub/cloud-security/what-is-shift-left-security/"&gt;shift security left&lt;/a&gt; and identify any privileged actions before you start development. This ensures that engineers know how to implement relevant permission checks as they build their functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insufficient Error Handling and/or Logging
&lt;/h3&gt;

&lt;p&gt;Functions should be observable so that you can spot errors and analyze suspected attack activity. Codes with insufficient error reporting and logging mask security incidents, making it easier for threat actors to acquire long-term access.&lt;/p&gt;

&lt;p&gt;Log collection issues can be easily solved by instrumenting your function to emit logs on its standard output and error streams. Lambda automatically &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html"&gt;sends this output&lt;/a&gt; to the &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html"&gt;AWS CloudWatch Logs&lt;/a&gt; management service, allowing you to monitor log output and verify your function is working as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insufficient Resource Allocation
&lt;/h3&gt;

&lt;p&gt;Lambda functions that have been allocated insufficient memory or CPU resources can cause resource exhaustion. At best, this leads to instability; at worst, malicious users could exploit it to achieve a denial-of-service (DoS) attack against your functions.&lt;/p&gt;

&lt;p&gt;Resource constraints are controlled by &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/computing-power.html"&gt;setting the memory available&lt;/a&gt; to your Lambda functions. The available CPU capacity is then scaled automatically based on the amount of memory you've requested. You should assess your function's memory requirement before you deploy, then select an allocation that provides enough headroom for real-world usage. It's also important to set up &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/monitoring-observability.html"&gt;CloudWatch alerts&lt;/a&gt; to receive notifications when a function invocation approaches its memory limit or exceeds defined thresholds for CPU or IO activity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Excessive Resource Allocation
&lt;/h3&gt;

&lt;p&gt;It's not just inadequate resource allocation that poses a security threat—assigning too much memory can be equally impactful. Because Lambda pricing is determined based on the amount of memory allocated and the total duration of each invocation, excessive allocation causes an unnecessary increase in your operating costs. Attackers could abuse this by directing malicious traffic to your services, forcing you to accept a higher bill.&lt;/p&gt;

&lt;p&gt;To mitigate this threat, ensure you regularly monitor the actual resource utilization of your functions using the metrics available in CloudWatch. You can then rightsize your memory allocations to achieve an optimal balance of performance and cost efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use of Insecure Dependencies
&lt;/h3&gt;

&lt;p&gt;Functions that depend on vulnerable or outdated third-party packages pose security risks when running in Lambda, just like when any other deployment method is used.&lt;/p&gt;

&lt;p&gt;Using unmaintained packages exposes you to actively exploited Common Vulnerabilities and Exposures (CVEs), insecure or buggy code, and the prospect of backdoors or intentional weaknesses inserted by malicious authors. You should practice safe supply chain security to identify these problems before you deploy, such as by &lt;a href="https://anchore.com/sbom/what-is-an-sbom/"&gt;generating a software bill of materials (SBOM)&lt;/a&gt; that lists your dependencies and any known issues they contain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Injection
&lt;/h3&gt;

&lt;p&gt;Unprotected Lambda functions can be vulnerable to data injection issues, such as improper user input sanitization or acceptance of malformed commands generated by attacks against other AWS services that can trigger your function. These weaknesses give threat actors the opportunity to execute code while making it difficult for you to detect them. Practicing robust code reviews, static and dynamic source security scans (static/dynamic application security testing [SAST/DAST]), and fuzz testing can help you uncover potential weaknesses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improper Access Control
&lt;/h3&gt;

&lt;p&gt;Missing or incorrectly configured access controls allow unauthorized users to invoke your functions. You can prevent this by creating appropriate AWS Identity and Access Management (IAM) policies to precisely define which users and applications can interact with your functions.&lt;/p&gt;

&lt;p&gt;It's also important to assign your functions a narrowly scoped &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html"&gt;execution role&lt;/a&gt;. This configures the AWS resources that your function's code can access using the credentials that are injected into its environment. Functions should be assigned the minimum privileges they require, such as permission to access files in a specific Amazon Simple Storage Service (Amazon S3) bucket but not any other type of resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secure AWS Lambda: Protect Your Functions with a Mesh Network
&lt;/h2&gt;

&lt;p&gt;Lambda security issues often stem from functions being unnecessarily publicly exposed. Although this can be unavoidable in some situations, many functions interact only with other components in your stack. Setting up private networking flows for these functions removes the need to expose them on the internet, reducing your attack surface.&lt;/p&gt;

&lt;p&gt;The best way to securely connect your functions to your other resources is with an encrypted mesh network like &lt;a href="https://netbird.io"&gt;NetBird&lt;/a&gt;. NetBird lets you link your infrastructure together using a zero-config private &lt;a href="https://www.wireguard.com"&gt;WireGuard&lt;/a&gt; network that works across cloud, serverless, and on-premise infrastructure.&lt;/p&gt;

&lt;p&gt;Joining serverless environments like AWS Lambda to a mesh network has traditionally been difficult because you can't directly access the network interfaces on the hosts that run your functions. The NetBird &lt;a href="https://docs.netbird.io/how-to/netbird-on-faas"&gt;netstack mode&lt;/a&gt; addresses this by providing a simulated &lt;a href="https://www.wireguard.com"&gt;TUN device&lt;/a&gt; and a SOCKS5 proxy that targets that device. This allows your Lambda function to access other services in your NetBird network via the proxy.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Securely Run Lambda Functions with NetBird
&lt;/h3&gt;

&lt;p&gt;To use NetBird with your Lambda functions, you need to follow a &lt;a href="https://docs.netbird.io/how-to/netbird-on-faas"&gt;few simple steps&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write your function's code:&lt;/strong&gt; Create your function in the usual way, including any network connections you require. However, you should ensure the networking libraries you use are configured to connect using the SOCKS5 proxy that NetBird provides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Docker image for your function:&lt;/strong&gt; Your Lambda functions must be &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/images-create.html"&gt;deployed as container images&lt;/a&gt; so the NetBird binary can be run alongside your code. Within your container image's build, you should select a relevant AWS Lambda image as your base image, then add your code and the &lt;code&gt;/go/bin/netbird&lt;/code&gt; binary from the &lt;a href="https://hub.docker.com/r/netbirdio/netbird"&gt;NetBird official image&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable the NetBird netstack mode:&lt;/strong&gt; You need to set the &lt;code&gt;NB_USE_NETSTACK_MODE&lt;/code&gt; and &lt;code&gt;NB_FOREGROUND_MODE&lt;/code&gt; environment variables inside your container image to ensure NetBird is correctly configured for in-container netstack operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a command script that starts NetBird before your function runs:&lt;/strong&gt; You can achieve this by setting a custom script as your container's entry point. It should start the NetBird binary and then invoke your code using the AWS Lambda platform binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set your NetBird setup key as a Lambda environment variable:&lt;/strong&gt; Before running your function, you should set the &lt;code&gt;NB_SETUP_KEY&lt;/code&gt; environment variable using the Lambda console or API. Setup keys connect your apps to your NetBird network. You &lt;a href="https://docs.netbird.io/ipa/resources/setup-keys"&gt;can generate one&lt;/a&gt; by logging into your NetBird account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After following these steps, you can deploy your Lambda project. NetBird will register your function in the network and assign it a private NetBird IP address.&lt;br&gt;
You can use this IP to call your function. It executes inside the container using the image you've created.&lt;br&gt;
Because the NetBird binary is configured to run inside the container, too, your code can access the other resources in&lt;br&gt;
your network without being exposed to the public internet. You can even connect your Lambda to on-premise resources like databases or legacy systems,&lt;br&gt;
or even multiple serverless functions running in different cloud providers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flimfdsy7ivtizd5zmjrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flimfdsy7ivtizd5zmjrj.png" alt="NetBird connects lambda" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://docs.netbird.io/how-to/netbird-on-faas"&gt;NetBird documentation&lt;/a&gt; for more information on how to set up your Lambda functions with NetBird.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for AWS Lambda Security
&lt;/h2&gt;

&lt;p&gt;AWS Lambda simplifies cloud deployment by letting you launch services as serverless functions that run on demand with high availability and scalability. Although this can help you deliver code more quickly and cost-effectively, it also makes you vulnerable to new security threats if you don't properly harden your AWS accounts and the functions within them. Here's a recap of the best practices you should follow to defend your serverless functions against these risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure correct &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html"&gt;IAM policies&lt;/a&gt; to restrict access to your functions.&lt;/li&gt;
&lt;li&gt;Allocate appropriate resources to your Lambda functions to avoid resource exhaustion or overprovisioning.&lt;/li&gt;
&lt;li&gt;Practice secure coding methods, including proper input sanitization and safe supply chain hygiene.&lt;/li&gt;
&lt;li&gt;Ensure that activities in functions are observable through logs and error reports.&lt;/li&gt;
&lt;li&gt;Avoid exposing functions on the public internet.&lt;/li&gt;
&lt;li&gt;Connect functions to other infrastructure using a private VPN.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps ensure your Lambda functions are performant, safe, and secure, letting you fully benefit from the development efficiency improvements that serverless computing allows.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>Using NetBird for Kubernetes Access</title>
      <dc:creator>Misha Bragin</dc:creator>
      <pubDate>Mon, 29 Apr 2024 18:24:12 +0000</pubDate>
      <link>https://dev.to/braginini/using-netbird-for-kubernetes-access-3fc2</link>
      <guid>https://dev.to/braginini/using-netbird-for-kubernetes-access-3fc2</guid>
      <description>&lt;p&gt;Securing access to your &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; clusters is crucial as inadequate security measures can lead to unauthorized access and potential data breaches. However, navigating the complexities of Kubernetes access security, especially when setting up strong authentication, authorization, and network policies, can be challenging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://netbird.io/" rel="noopener noreferrer"&gt;NetBird&lt;/a&gt; simplifies Kubernetes access with its zero-configuration approach, leveraging &lt;a href="https://www.wireguard.com" rel="noopener noreferrer"&gt;WireGuard's&lt;/a&gt; simplicity and strength. It seamlessly integrates with various tools, offering transparency and high reliability as an open source solution.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn how to set up the &lt;a href="https://docs.netbird.io/how-to/cli" rel="noopener noreferrer"&gt;NetBird CLI&lt;/a&gt; to ensure a secure connection to a Kubernetes cluster on the &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Platform (GCP)&lt;/a&gt;, complete with a fail-safe route for uninterrupted access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using NetBird for Kubernetes Access
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll follow these steps to enable secure access to your Kubernetes cluster using NetBird:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a NetBird agent on your local machine.&lt;/li&gt;
&lt;li&gt;Create NetBird setup keys to be used in the Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;Set up a remote Kubernetes cluster. For this tutorial, &lt;a href="https://cloud.google.com/kubernetes-engine/" rel="noopener noreferrer"&gt;Google Kubernetes Engine (GKE)&lt;/a&gt; was chosen; however, feel free to use any remote Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;Deploy the setup key in the remote Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;Configure a highly available (HA) network route in NetBird for uninterrupted access to the Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;Test secure access from the local client to the remote Kubernetes cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note that to follow this tutorial, you need &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/" rel="noopener noreferrer"&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;&lt;code&gt;gcloud&lt;/code&gt;&lt;/a&gt; command line tools installed and properly configured on your local machine.&lt;/p&gt;

&lt;p&gt;With the procedure outlined, let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up the NetBird Agent
&lt;/h2&gt;

&lt;p&gt;If you haven't already, &lt;a href="https://app.netbird.io/" rel="noopener noreferrer"&gt;sign up for a free NetBird account&lt;/a&gt;. After confirming your email,&lt;br&gt;
you'll receive a message with instructions on how to install the NetBird agent on your local machine.&lt;br&gt;
This agent includes NetBird's CLI, which is used to control it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FIOiWtcj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FIOiWtcj.png" alt="NetBird client setup instructions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions for your operating system and finish by authorizing the NetBird app to access the NetBird dashboard using the command &lt;code&gt;sudo netbird up&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FoK44D9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FoK44D9j.png" alt="Authorize the NetBird app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see your local machine in the &lt;strong&gt;Peers&lt;/strong&gt; dashboard, which means that it's now connected to the NetBird peer-to-peer (P2P) network:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FDRoMOPe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FDRoMOPe.png" alt="Peers: local machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Setup Keys
&lt;/h2&gt;

&lt;p&gt;Now that you're connected to the network, it's time to add a Kubernetes cluster to this network. To do that, you need to &lt;a href="https://docs.netbird.io/how-to/register-machines-using-setup-keys#ephemeral-peers" rel="noopener noreferrer"&gt;create a setup key&lt;/a&gt; that your Kubernetes cluster can use to authenticate with NetBird.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Setup Keys&lt;/strong&gt; tab on the left-hand side of your screen and then click on the &lt;strong&gt;Create Setup Key&lt;/strong&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FXmOfhc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FXmOfhc3.png" alt="Creating a setup key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pop-up window opens where you can name the key, set the maximum number of peers that can use it, and determine its expiration date.&lt;br&gt;
Additionally, you can also select &lt;a href="https://docs.netbird.io/how-to/register-machines-using-setup-keys#peer-auto-grouping" rel="noopener noreferrer"&gt;auto-assigned groups&lt;/a&gt;.&lt;br&gt;
With this option, any peer that registers with the key will be automatically added to these groups, meaning that the access control&lt;br&gt;
rules set for these groups will automatically apply to them. Set it to &lt;code&gt;gke-cluster&lt;/code&gt;, it will help you to avoid adding&lt;br&gt;
each peer to the group manually later in the tutorial.&lt;/p&gt;

&lt;p&gt;You'll also see two toggle switches there: one for making the key reusable and another to mark a peer as&lt;br&gt;
&lt;a href="https://docs.netbird.io/how-to/register-machines-using-setup-keys#ephemeral-peers" rel="noopener noreferrer"&gt;ephemeral&lt;/a&gt;.&lt;br&gt;
Reusable setup keys are useful when there is no human behind a machine that can use SSO and MFA, e.g.,&lt;br&gt;
to &lt;a href="https://docs.netbird.io/how-to/register-machines-using-setup-keys" rel="noopener noreferrer"&gt;associate servers with a NetBird account&lt;/a&gt;,&lt;br&gt;
so they can authenticate to the network automatically. On the other hand, ephemeral keys automatically remove machines&lt;br&gt;
from NetBird if they are offline for more than 10 minutes. This feature helps prevent offline machines from cluttering&lt;br&gt;
the NetBird peer dashboard, such as when a Kubernetes pod gets replaced by a new one.&lt;/p&gt;

&lt;p&gt;The following screenshot shows the creation of an ephemeral key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FSf8NBDF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FSf8NBDF.png" alt="Ephemeral setup key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Likewise, this screenshot shows how to create a reusable key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FKWk6tRX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FKWk6tRX.png" alt="Reusable setup key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Given the reasons mentioned earlier, using ephemeral keys for this tutorial is recommended. This is because Kubernetes&lt;br&gt;
might restart services, causing machines to go offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up a Remote Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;To ensure a highly available network route with NetBird, there are some considerations to keep in mind when deploying the GKE cluster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should create, at a bare minimum, a &lt;a href="https://cloud.google.com/kubernetes-engine/docs/concepts/regional-clusters" rel="noopener noreferrer"&gt;regional cluster&lt;/a&gt; or a &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-zonal-cluster" rel="noopener noreferrer"&gt;multizonal cluster&lt;/a&gt; with nodes distributed across multiple &lt;a href="https://cloud.google.com/compute/docs/regions-zones" rel="noopener noreferrer"&gt;zones within a region&lt;/a&gt; to ensure that the cluster is resilient to zonal failures.&lt;/li&gt;
&lt;li&gt;You need to deploy at least three nodes to ensure high availability.&lt;/li&gt;
&lt;li&gt;You are highly recommended to enable cluster autoscaling to ensure that there are always at least three nodes running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use the Google Cloud console UI to set up a Kubernetes cluster like the one described earlier, or you can adapt this &lt;code&gt;gcloud&lt;/code&gt; command to fit your needs:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

gcloud container &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_PROJECT_ID"&lt;/span&gt; clusters create &lt;span class="s2"&gt;"netbird-cluster-1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--no-enable-basic-auth&lt;/span&gt; &lt;span class="nt"&gt;--release-channel&lt;/span&gt; &lt;span class="s2"&gt;"regular"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--machine-type&lt;/span&gt; &lt;span class="s2"&gt;"e2-small"&lt;/span&gt; &lt;span class="nt"&gt;--image-type&lt;/span&gt; &lt;span class="s2"&gt;"COS_CONTAINERD"&lt;/span&gt; &lt;span class="nt"&gt;--disk-type&lt;/span&gt; &lt;span class="s2"&gt;"pd-standard"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--disk-size&lt;/span&gt; &lt;span class="s2"&gt;"100"&lt;/span&gt; &lt;span class="nt"&gt;--metadata&lt;/span&gt; disable-legacy-endpoints&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--num-nodes&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;--logging&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SYSTEM,WORKLOAD &lt;span class="nt"&gt;--monitoring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SYSTEM &lt;span class="nt"&gt;--enable-ip-alias&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autoscaling&lt;/span&gt; &lt;span class="nt"&gt;--min-nodes&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;--max-nodes&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--network&lt;/span&gt; &lt;span class="s2"&gt;"projects/YOUR_PROJECT_ID/global/networks/default"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--subnetwork&lt;/span&gt; &lt;span class="s2"&gt;"projects/YOUR_PROJECT_ID/regions/us-east1/subnetworks/default"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--no-enable-intra-node-visibility&lt;/span&gt; &lt;span class="nt"&gt;--default-max-pods-per-node&lt;/span&gt; &lt;span class="s2"&gt;"110"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--security-posture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;standard &lt;span class="nt"&gt;--workload-vulnerability-scanning&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;disabled &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--no-enable-master-authorized-networks&lt;/span&gt; &lt;span class="nt"&gt;--addons&lt;/span&gt; HorizontalPodAutoscaling,HttpLoadBalancing,GcePersistentDiskCsiDriver &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt; &lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="nt"&gt;--max-surge-upgrade&lt;/span&gt; 1 &lt;span class="nt"&gt;--max-unavailable-upgrade&lt;/span&gt; 0 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-managed-prometheus&lt;/span&gt; &lt;span class="nt"&gt;--enable-shielded-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"us-east1"&lt;/span&gt; &lt;span class="nt"&gt;--node-locations&lt;/span&gt; &lt;span class="s2"&gt;"us-east1-b,us-east1-c,us-east1-d"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;YOUR_PROJECT_ID&lt;/code&gt; with the ID of your project. Additionally, replace the region &lt;code&gt;us-east1&lt;/code&gt; and the zones &lt;code&gt;us-east1-b&lt;/code&gt;, &lt;code&gt;us-east1-c&lt;/code&gt;, and &lt;code&gt;us-east1-d&lt;/code&gt; according to your preferences.&lt;/p&gt;

&lt;p&gt;If you want to create a cluster with a specific version, use the &lt;code&gt;--cluster-version&lt;/code&gt; flag to match the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/release-notes" rel="noopener noreferrer"&gt;latest available version&lt;/a&gt; in your region.&lt;/p&gt;

&lt;p&gt;Pay attention to the instance type configuration. In this case, &lt;code&gt;e2-small&lt;/code&gt; guarantees NetBird has sufficient CPU and memory resources. &lt;code&gt;--num-nodes&lt;/code&gt; is set to &lt;code&gt;1&lt;/code&gt; since it deploys one node per zone (three nodes in total), and the &lt;code&gt;--enable-autoscaling&lt;/code&gt; flag is set to allow a maximum of two nodes per zone or a maximum of six nodes in total.&lt;/p&gt;

&lt;p&gt;Once the cluster is deployed, log in to the Google Cloud console and check that everything is working as expected (cluster status is &lt;code&gt;Ok&lt;/code&gt; and status of the nodes' is &lt;code&gt;Ready&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FrlMBnjD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FrlMBnjD.png" alt="GKE cluster in the Google Cloud console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also verify the nodes using &lt;code&gt;kubectl&lt;/code&gt; from your local machine:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes
NAME                                               STATUS   ROLES    AGE     VERSION
gke-netbird-cluster-1-default-pool-6969d6cf-w5fd   Ready    &amp;lt;none&amp;gt;   5m13s   v1.27.8-gke.1067004
gke-netbird-cluster-1-default-pool-7ddbb961-8rkf   Ready    &amp;lt;none&amp;gt;   5m14s   v1.27.8-gke.1067004
gke-netbird-cluster-1-default-pool-a7de6516-rbb0   Ready    &amp;lt;none&amp;gt;   5m14s   v1.27.8-gke.1067004


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the GKE cluster up and running, it's time to add the NetBird ephemeral key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the NetBird Ephemeral Setup Key to the Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;To register your Kubernetes cluster to the NetBird secure P2P network, create a YAML file on your local machine called &lt;code&gt;netbird-client.yaml&lt;/code&gt; and paste the following content in it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbirdio/netbird:latest&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NB_SETUP_KEY&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR_SETUP_KEY&amp;gt;&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NB_HOSTNAME&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;netbird-cluster-1"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NB_LOG_LEVEL&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info"&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird-client&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/netbird&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500m"&lt;/span&gt;
          &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
            &lt;span class="na"&gt;runAsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
            &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NET_ADMIN&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NET_RESOURCE&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SYS_ADMIN&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;netbird-client&lt;/span&gt;
          &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's break down the key arguments of this deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;replicas: 3&lt;/code&gt;&lt;/strong&gt; ensures that at least one replica is deployed per node and is necessary to secure HA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;image: netbirdio/netbird:latest&lt;/code&gt;&lt;/strong&gt; uses the latest available image. For production, it's best practice to use a specific version to avoid conflicts during updates.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/strong&gt;  parameter ensures that NetBird has a minimum guaranteed amount of resources to run properly. It's best practice to assign requests and/or limits to any app or service running on your cluster, which includes the NetBird client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;privileged: true&lt;/code&gt;&lt;/strong&gt; is required since, generally, modifying the routing table requires &lt;code&gt;root&lt;/code&gt; permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to replace &lt;code&gt;YOUR_SETUP_KEY&lt;/code&gt; with the previously created ephemeral setup key. Once you're ready, add the ephemeral key to the cluster by running the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; netbird-client.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can check the status of the deployment using &lt;code&gt;kubectl get pods&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
netbird-7dc884d77f-285dh   1/1     Running   0          96s
netbird-7dc884d77f-cfq69   1/1     Running   0          96s
netbird-7dc884d77f-x56nh   1/1     Running   0          96s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point, if you go to the NetBird &lt;strong&gt;Peers&lt;/strong&gt; tab, you should see the GKE nodes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FQxqj62H.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FQxqj62H.png" alt="GKE Peers view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With your local machine and GKE nodes now part of the NetBird P2P network, you're ready to establish a secure HA network route for communication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up an HA Network Route in NetBird
&lt;/h2&gt;

&lt;p&gt;Simply having all GKE nodes on the same peer-to-peer network doesn't guarantee accessibility. For instance, if you attempt to ping a node, you'll receive a "request timeout…" message. This happens because you need to set up a network route and define an access policy before you can connect to the nodes.&lt;/p&gt;

&lt;p&gt;To facilitate the process of &lt;a href="https://docs.netbird.io/how-to/routing-traffic-to-private-networks" rel="noopener noreferrer"&gt;creating network routes&lt;/a&gt; and &lt;a href="https://docs.netbird.io/how-to/manage-network-access" rel="noopener noreferrer"&gt;access policies&lt;/a&gt;, NetBird lets you create peer groups.&lt;br&gt;
Do you remember that you created and assigned the &lt;code&gt;gke-cluster&lt;/code&gt; group to the GKE nodes when you created the ephemeral key?&lt;br&gt;
If you haven't done this then, you can create and assign the group manually to the peers now.&lt;/p&gt;

&lt;p&gt;To do so, click on the first GKE node in the &lt;strong&gt;Peers&lt;/strong&gt; view. This takes you to a screen with the peer details. On the right side, you should see &lt;strong&gt;Assigned Groups&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FYEKbikr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FYEKbikr.png" alt="**Assigned Groups**"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start typing a name (&lt;em&gt;ie&lt;/em&gt; &lt;code&gt;gke-cluster&lt;/code&gt;) to create a new group. After you've entered the name, click &lt;strong&gt;Save Changes&lt;/strong&gt; to apply the new configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2F2rWwxVn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2F2rWwxVn.png" alt="Create a new group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat the procedure for the remaining two GKE nodes; remember to choose the same group and save the changes.&lt;/p&gt;

&lt;p&gt;Once finished, hover the mouse pointer over the &lt;strong&gt;GROUPS&lt;/strong&gt; column on any of the nodes to verify that they belong to the desired group:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FVDYr7Rb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FVDYr7Rb.png" alt="Peers added to a group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, to simplify setting a route, you can assign a group to your local machine, such as &lt;code&gt;local&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FzJmMb2I.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FzJmMb2I.png" alt="Local machine group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the peer groups ready, it's time to move to the &lt;strong&gt;Network Routes&lt;/strong&gt; tab, which you can find on the left-hand side of your screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FDXzUAhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FDXzUAhb.png" alt="Network routes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before setting up your first network route, you need to determine which network you wish to connect with. For instance, if you want to access pods and services in your Kubernetes cluster, you'll need the GKE cluster Pod IPv4 and IPv4 service ranges. To find these, navigate to the &lt;strong&gt;Networking&lt;/strong&gt; section in the Google Cloud console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FyEt7fww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FyEt7fww.png" alt="Google console **Networking**"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Add Route&lt;/strong&gt; button, and a pop-up opens, allowing you to create a new route.&lt;/p&gt;

&lt;p&gt;Input the &lt;strong&gt;Network Range&lt;/strong&gt;, then click on &lt;strong&gt;Peer Group&lt;/strong&gt; and choose &lt;strong&gt;gke-cluster&lt;/strong&gt; (or the group you've established for your nodes). Under &lt;strong&gt;Distribution Groups&lt;/strong&gt;, pick the &lt;code&gt;local&lt;/code&gt; group you just created to make this route known to your local machine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FIWCdHBS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FIWCdHBS.png" alt="Create network route"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Name &amp;amp; Description&lt;/strong&gt; tab, provide a name (network identifier) for the route and click &lt;strong&gt;Add Route&lt;/strong&gt; to finalize its creation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FILIrb3K.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FILIrb3K.png" alt="Naming a network route"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following is an example of two networks: one for the GKE pods and another for the GKE services. Note that the &lt;strong&gt;HIGH AVAILABILITY&lt;/strong&gt; column shows &lt;strong&gt;3 Peer(s)&lt;/strong&gt; in green to indicate that it is a highly available route:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FH5ZA9Ns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FH5ZA9Ns.png" alt="Network routes view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While these routes create a link between your local machine and the GKE cluster, you still need one more component to enable communication: an access control policy (ACP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up an Access Control Policy to Access Your Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;To create an ACP, go to &lt;strong&gt;Access Control &amp;gt; Policies&lt;/strong&gt; and click on &lt;strong&gt;Add Policy&lt;/strong&gt;. A pop-up appears where you can define a new ACP. Select the &lt;code&gt;local&lt;/code&gt; group as &lt;strong&gt;Source&lt;/strong&gt; and the &lt;code&gt;gke-cluster&lt;/code&gt; group as &lt;strong&gt;Destination&lt;/strong&gt;. This allows bidirectional communication between both peers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FsMx5LJX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FsMx5LJX.png" alt="New ACP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.netbird.io/how-to/manage-posture-checks" rel="noopener noreferrer"&gt;Configuring security posture checks&lt;/a&gt; is beyond the scope of this guide, but in a nutshell, the &lt;strong&gt;Posture Checks&lt;/strong&gt; tab lets you enhance access control by blocking peers that fail to meet certain conditions, such as client version, country of origin, and operating system.&lt;br&gt;
If you are interested in this and other Zero Trust Security features, check the &lt;a href="https://netbird.io/knowledge-hub/open-source-zero-trust-networking" rel="noopener noreferrer"&gt;Open-Source Zero Trust Networking Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To create the new policy, go to the &lt;strong&gt;Name &amp;amp; Description&lt;/strong&gt; tab and enter an appropriate name for the rule (&lt;em&gt;eg&lt;/em&gt; &lt;strong&gt;Local to GKE&lt;/strong&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2Fqb3WF10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2Fqb3WF10.png" alt="Name new ACP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To finish setting up the new ACP, select &lt;strong&gt;Add Policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The following are two example policies: &lt;strong&gt;Local to GKE&lt;/strong&gt; and the NetBird &lt;strong&gt;Default&lt;/strong&gt; policy. In this example, the default policy has been turned off, so there's only &lt;strong&gt;Local to GKE&lt;/strong&gt; active:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FztfuitR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnetbird.io%2Fimg%2Fusing-netbird-for-kubernetes-access%2FztfuitR.png" alt="ACP view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You just configured secure access to Kubernetes using NetBird P2P. To check the connection, navigate to the &lt;strong&gt;Peers&lt;/strong&gt; tab and take note of the IP address or domain assigned by NetBird to any of the nodes.&lt;/p&gt;

&lt;p&gt;The following image shows the response when pinging one of the GKE nodes:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 netbird-cluster-1.netbird.cloud&lt;br&gt;
PING netbird-cluster-1.netbird.cloud &lt;span class="o"&gt;(&lt;/span&gt;100.74.176.22&lt;span class="o"&gt;)&lt;/span&gt;: 56 data bytes&lt;br&gt;
64 bytes from 100.74.176.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;106.287 ms&lt;br&gt;
64 bytes from 100.74.176.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;107.516 ms&lt;br&gt;
64 bytes from 100.74.176.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;105.674 ms&lt;/p&gt;

&lt;p&gt;&lt;span class="nt"&gt;---&lt;/span&gt; netbird-cluster-1.netbird.cloud ping statistics &lt;span class="nt"&gt;---&lt;/span&gt;&lt;br&gt;
3 packets transmitted, 3 packets received, 0.0% packet loss&lt;br&gt;
round-trip min/avg/max/stddev &lt;span class="o"&gt;=&lt;/span&gt; 105.674/106.492/107.516/0.766 ms&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you installed the NetBird CLI, configured a Kubernetes cluster on GCP, integrated a NetBird ephemeral key, created a high-availability route, and set an access control policy for secure local access.&lt;/p&gt;

&lt;p&gt;Take your network management to the next level—explore more features and enhance your setup with the &lt;a href="https://netbird.io/use-cases/site-to-site-connectivity" rel="noopener noreferrer"&gt;NetBird cutting-edge peer-to-peer network&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>Serverless Security Vulnerabilities and Best Practices to Mitigate Them</title>
      <dc:creator>Misha Bragin</dc:creator>
      <pubDate>Tue, 23 Apr 2024 15:49:19 +0000</pubDate>
      <link>https://dev.to/braginini/serverless-security-vulnerabilities-and-best-practices-to-mitigate-them-3og0</link>
      <guid>https://dev.to/braginini/serverless-security-vulnerabilities-and-best-practices-to-mitigate-them-3og0</guid>
      <description>&lt;p&gt;The cloud has revolutionized application development, and serverless computing is its latest evolution. It offers an entire paradigm shift, allowing developers to focus solely on code functionality without having to manage servers. However, this convenience comes with a twist: new security challenges.&lt;/p&gt;

&lt;p&gt;Traditional security practices are centered on securing underlying infrastructure, but serverless computing lacks dedicated servers and introduces a new attack surface. Malicious actors can exploit vulnerabilities in code, permissions, and event data to compromise serverless applications. This can lead to data breaches, unauthorized access, and financial loss.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn all about security vulnerabilities that can occur in serverless environments and how to prevent them. By the end of the article, you'll be able to build and deploy secure serverless applications confidently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Need to Secure Your Serverless Functions
&lt;/h2&gt;

&lt;p&gt;Serverless security goes beyond traditional application security. It focuses on protecting the code within your serverless functions, the data those functions handle, and the overall execution environment provided by the cloud platform. This shift in focus is crucial because serverless functions often become the primary targets for attackers due to accessibility, heavy reliance on third-party code/services, shared resources, and limited control over infrastructure.&lt;/p&gt;

&lt;p&gt;If your serverless functions process any personally identifiable information (PII), financial transactions, or health information or manage intellectual property, even a minor security breach can have devastating consequences. Strong serverless security safeguards this sensitive information and ensures the integrity of your functions. Additionally, many applications must comply with regulations, such as the &lt;a href="https://gdpr-info.eu/"&gt;General Data Protection Regulation (GDPR)&lt;/a&gt; or the &lt;a href="https://privacy.ca.gov/about-us/about-the-cppa/"&gt;California Consumer Privacy Act (CCPA)&lt;/a&gt;. That means securing your functions and your data is a necessity.&lt;/p&gt;

&lt;p&gt;In addition, serverless applications often interact with data residing outside the cloud platform, such as on-premise databases or storage systems. This hybrid environment creates additional security considerations. For example, a function might need to securely access a customer database located in your corporate data center. Mitigating unauthorized access to this data requires robust security measures at the function level to ensure proper authentication and authorization protocols are followed during communication.&lt;/p&gt;

&lt;p&gt;Data migration projects are another compelling reason to prioritize serverless security. Imagine a scenario where you're migrating a workload (functions) to the cloud while the data (database) remains on-premise. In this case, ensuring secure communication between the functions and the on-premise data is critical. Serverless security best practices, such as &lt;a href="https://cloudsecurityalliance.org/blog/2023/06/27/why-you-should-use-the-principle-of-least-privilege-to-secure-serverless-applications"&gt;least privilege access&lt;/a&gt; and &lt;a href="https://cloudian.com/guides/data-protection/data-encryption-the-ultimate-guide/"&gt;data encryption&lt;/a&gt;, can help prevent unauthorized access during the migration process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices to Secure Your Serverless Functions
&lt;/h2&gt;

&lt;p&gt;Now that you know why you need to secure your serverless functions, let's take a look at a few best practices that you can implement to help you secure them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement an Authentication and Authorization Solution That Integrates with Your Function
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.techtarget.com/searchsecurity/definition/authentication"&gt;Authentication (authn)&lt;/a&gt; and &lt;a href="https://auth0.com/intro-to-iam/what-is-authorization"&gt;authorization (authz)&lt;/a&gt; are the cornerstones of web application security. Authentication verifies a user's identity, while authorization determines their access rights to specific functions or resources. In a serverless environment, these mechanisms are typically integrated via an &lt;a href="https://www.nginx.com/learn/api-gateway"&gt;API gateway&lt;/a&gt;, which acts as the entry point for your functions, or through &lt;a href="https://www.techtarget.com/searchsecurity/definition/identity-access-management-IAM-system"&gt;identity and access management (IAM)&lt;/a&gt; rules specific to a cloud platform that allow the functions to interact with other cloud resources.&lt;/p&gt;

&lt;p&gt;Without proper controls, any person or service could trigger your functions and steal data or disrupt operations. Authentication ensures that only authorized users can interact with your functions, while authorization restricts their actions based on predefined roles or permissions.&lt;/p&gt;

&lt;p&gt;Depending on the cloud platform, there are multiple ways you could set up authn/authz for your serverless functions. For instance, Amazon Web Services (AWS) provides user authentication through &lt;a href="https://aws.amazon.com/cognito/"&gt;Amazon Cognito&lt;/a&gt;. The &lt;a href="https://repost.aws/knowledge-center/api-gateway-cognito-user-pool-authorizer"&gt;API gateway can be configured to integrate with Cognito&lt;/a&gt; to require users to sign in before invoking any &lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda functions&lt;/a&gt;. Cognito verifies user identities and passes relevant tokens to your functions for authorization decisions.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;a href="https://aws.amazon.com/iam/"&gt;AWS IAM&lt;/a&gt; allows you to define roles with specific permissions. You can associate an IAM role with your Lambda function, granting it access to specific resources, such as Amazon Simple Storage Service (Amazon S3) buckets or DynamoDB tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perform Input Validation and Sanitization
&lt;/h3&gt;

&lt;p&gt;Serverless functions often accept input from various sources, including user requests, API calls, and event triggers. Malicious actors can exploit improperly validated or sanitized input to inject code or manipulate data. This is why input validation and sanitization are critical security practices.&lt;/p&gt;

&lt;p&gt;Unvalidated or unsanitized input can lead to vulnerabilities, such as &lt;a href="https://owasp.org/www-community/attacks/SQL_Injection"&gt;SQL injection&lt;/a&gt;, &lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;cross-site scripting (XSS)&lt;/a&gt;, and command injection. These attacks can compromise your functions, steal data, and disrupt operations.&lt;/p&gt;

&lt;p&gt;Following are a few ways you can implement proper input sanitization and validation in your serverless functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate input types and formats:&lt;/strong&gt; Ensure user input conforms to expected data types (&lt;em&gt;eg&lt;/em&gt; integer or string) and formats (&lt;em&gt;eg&lt;/em&gt; email address or date). AWS Lambda supports libraries such as &lt;a href="https://github.com/ibm-cloud-architecture/terraform-icp-aws/blob/master/autoscaling/lambda/node_modules/ajv/README.md"&gt;Ajv&lt;/a&gt; or native JavaScript functions for data type validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitize user input:&lt;/strong&gt; Remove potentially harmful characters or code from user input before processing it. You can consider setting up a security handler using &lt;a href="http://vandium.io/"&gt;Vanadium&lt;/a&gt; to add custom, reusable logic for handling input validation and sanitization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encode special characters:&lt;/strong&gt; Make sure to encode special characters when sending user input to prevent them from being misinterpreted. For instance, if you're sending input to functions through HTTP parameters, then you need to encode &lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; characters because they have special meanings in URLs. JavaScript's &lt;code&gt;encodeURIComponent&lt;/code&gt; and &lt;code&gt;encodeURI&lt;/code&gt; functions can be used for this purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Validate Third-Party Library Dependencies in Your Code
&lt;/h3&gt;

&lt;p&gt;When writing serverless apps, developers often use third-party libraries to implement commonly used solutions and logic. However, the convenience of third-party libraries can come with hidden security risks. If not carefully vetted and maintained, these libraries can introduce vulnerabilities and create openings for attackers. A compromised library can be used to steal data, inject malicious code, or disrupt your functions. Validating your third-party dependencies is essential for robust serverless security.&lt;/p&gt;

&lt;p&gt;Following are a few tips you can implement to help you carefully validate and pick the right library dependencies for your serverless app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use trusted sources and stay updated:&lt;/strong&gt; Download libraries only from official repositories, such as &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;, or maintain them in private repositories with proper access controls. Additionally, keep your libraries up-to-date to benefit from security patches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review library security reports:&lt;/strong&gt; Many popular libraries have publicly available security reports detailing known vulnerabilities. Review these reports before using the library and consider alternatives if critical vulnerabilities exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage dependency scanning tools:&lt;/strong&gt; Several tools, such as &lt;a href="https://snyk.io/"&gt;Snyk&lt;/a&gt; or &lt;a href="https://docs.npmjs.com/cli/v10/commands/npm-audit/"&gt;npm-audit&lt;/a&gt;, can scan your Lambda function dependencies for known vulnerabilities. These tools can identify potential security risks and recommend mitigation steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While libraries offer convenience, make sure you use only essential libraries to reduce your attack surface and simplify security management. Additionally, consider building custom functionality if a readily available library has a questionable security track record and there are no secure alternatives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable Centralized Monitoring and Logging
&lt;/h3&gt;

&lt;p&gt;Centralized monitoring and logging provide a comprehensive view of your functions' health and security posture and can help you maintain a watchful eye over your serverless functions. Without proper monitoring, you could miss critical security events, such as unauthorized access attempts or function errors. Centralized logs allow you to analyze function behavior, identify anomalies, and detect potential threats.&lt;/p&gt;

&lt;p&gt;Most cloud platform vendors offer services to help you with active monitoring and alerts. For instance, &lt;a href="https://aws.amazon.com/cloudwatch/"&gt;Amazon CloudWatch&lt;/a&gt; provides a centralized logging service for your AWS Lambda functions. You can configure your Lambda functions to send logs to CloudWatch, allowing you to aggregate and analyze logs from all your functions in one place.&lt;/p&gt;

&lt;p&gt;Several third-party tools, such as &lt;a href="https://www.splunk.com/"&gt;Splunk&lt;/a&gt; or &lt;a href="https://www.sumologic.com/"&gt;Sumo Logic&lt;/a&gt;, can be integrated with CloudWatch to provide advanced log analytics and visualization. These tools can help you identify trends, filter logs for specific events, and set up alerts for suspicious activity.&lt;/p&gt;

&lt;p&gt;CloudWatch also provides monitoring capabilities. &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-monitoring.html"&gt;Lambda automatically reports metrics&lt;/a&gt; related to basic performance, duration, and resource utilization through CloudWatch, and you can configure Lambda functions to emit custom metrics that provide insights into &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/custom-metrics.html"&gt;statistics local to the app domain&lt;/a&gt;. You can then use these metrics to identify potential performance bottlenecks and diagnose function errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review Your Security Configurations to Remove Overly Permissive or Stale Configurations
&lt;/h3&gt;

&lt;p&gt;A large part of web security hinges on proper configuration management. Permissions granted to functions and access controls for resources need to be continuously reviewed and adjusted to minimize the attack surface.&lt;/p&gt;

&lt;p&gt;Overly permissive configurations or outdated permissions can create security gaps. An unused function with excessive IAM permissions or an API gateway endpoint with unrestricted access can be exploited.&lt;/p&gt;

&lt;p&gt;Here's how you can ensure your serverless functions benefit from secure and up-to-date configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implement least privilege:&lt;/strong&gt; Grant your serverless functions only the minimum permissions required to perform their tasks. Avoid using wildcards or overly broad IAM policies. The principle of least privilege minimizes the potential damage if a function is compromised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review and remove unused resources:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/devops-guidance/ag.sad.6-conduct-periodic-identity-and-access-management-reviews.html"&gt;Periodically review&lt;/a&gt; IAM roles, API gateway permissions, and other security configurations associated with your serverless functions. Identify and remove unused resources to eliminate potential attack vectors and streamline your security posture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utilize infrastructure as code (IaC)&lt;/strong&gt;: Leverage IaC tools, such as &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; or AWS CloudFormation (in the case of AWS), to manage your serverless infrastructure configurations. IaC allows you to version control your configurations, track changes, and automate deployments. This ensures consistency and reduces the risk of manual configuration errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable security best practices:&lt;/strong&gt; Many cloud providers offer security best practices recommendations for serverless functions. In AWS, the &lt;a href="https://aws.amazon.com/security-hub/"&gt;AWS Security Hub&lt;/a&gt; can identify potential security issues in your Lambda function configurations and suggest remediation steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consult the OWASP Serverless Top Ten Documentation
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://owasp.org/"&gt;Open Worldwide Application Security Project (OWASP)&lt;/a&gt; is a renowned community for web application security. The &lt;a href="https://owasp.org/www-project-serverless-top-10/"&gt;OWASP Serverless Top Ten project&lt;/a&gt; addresses specific security challenges for serverless environments. This resource catalogs the ten most critical security risks specific to serverless functions. Understanding these threats allows you to prioritize your security efforts and implement effective mitigation strategies.&lt;/p&gt;

&lt;p&gt;The documentation discusses the various ways in which injection attacks can impact a serverless app and lists ways that you can safeguard your functions against them. Similarly, it talks about broken authentication, sensitive data exposure, XML external entities, broken access control, security misconfiguration, XSS, insecure deserialization, use of components with known vulnerabilities, and insufficient logging and monitoring in depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Serverless security demands a proactive approach. You need to understand the unique threat landscape and implement best practices, such as robust authentication/authorization, meticulous input validation, and secure coding practices, to help you reduce your attack surface. Remember to leverage cloud provider security features, monitor your functions vigilantly, and keep configurations up-to-date.&lt;/p&gt;

&lt;p&gt;For a deeper dive into serverless vulnerabilities, explore the OWASP Serverless Top Ten documentation. When you prioritize serverless security, you can build and deploy applications with confidence, ensuring the integrity of your data and the smooth operation of your serverless environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.netbird.io/how-to/netbird-on-faas"&gt;NetBird&lt;/a&gt; secures access and connectivity to serverless functions on platforms like AWS and Microsoft Azure. NetBird leverages netstack from the &lt;a href="https://github.com/google/gvisor"&gt;gVisor Go package&lt;/a&gt; (part of &lt;a href="https://github.com/netbirdio/wireguard-go"&gt;wireguard-go&lt;/a&gt;) and enables WireGuard to run entirely in the user space. This method alleviates the need for network-level or kernel-level access.&lt;/p&gt;

&lt;p&gt;To learn how to configure NetBird, check out this &lt;a href="https://github.com/netbirdio/azure-functions-python-db-access"&gt;Python database access example on Azure functions&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using eBPF and XDP to Share Default DNS Port Between Multiple Resolvers</title>
      <dc:creator>Misha Bragin</dc:creator>
      <pubDate>Tue, 23 Apr 2024 15:41:32 +0000</pubDate>
      <link>https://dev.to/braginini/using-ebpf-and-xdp-to-share-default-dns-port-between-multiple-resolvers-17j6</link>
      <guid>https://dev.to/braginini/using-ebpf-and-xdp-to-share-default-dns-port-between-multiple-resolvers-17j6</guid>
      <description>&lt;p&gt;Have you tried configuring a local DNS resolver to use a port different from the default one?&lt;br&gt;
Changing port &lt;code&gt;53&lt;/code&gt; can be tricky since the DNS protocol is usually bound to this port.&lt;br&gt;
This is especially true if you need the software you develop to support as many operating systems as possible with various versions,&lt;br&gt;
ensuring it works on different platforms, too.&lt;/p&gt;

&lt;p&gt;The NetBird team recently faced this and other challenges while working on the &lt;a href="https://docs.netbird.io/how-to/manage-dns-in-your-network"&gt;DNS feature&lt;/a&gt; that makes managing DNS in private networks easy.&lt;br&gt;
We spent a few days on a solution involving Go, eBPF, and XDP "magic" that allows sharing a single port between multiple DNS resolver processes.&lt;br&gt;
We also created a DNS manager for WireGuard® networks that universally works on macOS, Linux, Windows, Docker, and mobile phones.&lt;br&gt;
Now is the time to share our journey. Traditionally, as with the whole NetBird platform, the code is &lt;a href="https://github.com/netbirdio/netbird/tree/main/client/internal/dns"&gt;open-source&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  About NetBird
&lt;/h2&gt;

&lt;p&gt;Before jumping into the details, it is worth mentioning why we've dived into this topic and how NetBird simplifies DNS management in private networks.&lt;/p&gt;

&lt;p&gt;NetBird is a &lt;a href="https://netbird.io/connect"&gt;zero-configuration overlay network&lt;/a&gt; and &lt;a href="https://netbird.io/use-cases/remote-access"&gt;remote access&lt;/a&gt; solution that automatically connects your servers, containers,&lt;br&gt;
cloud resources, and remote teams over an encrypted point-to-point WireGuard tunnel.&lt;br&gt;
There is also something special about NetBird that gave us a "little" headache while working on the DNS support and forced us to use XDP.&lt;br&gt;
I'm talking about kernel WireGuard. If available, NetBird uses the kernel WireGuard module shipped with Linux.&lt;br&gt;
Otherwise, it falls back to the userspace &lt;a href="https://github.com/WireGuard/wireguard-go"&gt;wireguard-go&lt;/a&gt; implementation,&lt;br&gt;
a common case for non-Linux operating systems.&lt;/p&gt;

&lt;p&gt;To minimize the configuration effort on the administrator side, NetBird allocates network IPs from the &lt;code&gt;CGNAT 100.64.0.0/10&lt;/code&gt;&lt;br&gt;
range and distributes them to machines from a central place. Then, it configures a WireGuard interface locally on every machine,&lt;br&gt;
assigning IPs to create direct connections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhx9mcmhy3asqx0dkna3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhx9mcmhy3asqx0dkna3.png" alt="NetBird WireGuard IP management" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how the WireGuard interface looks on my Ubuntu laptop after NetBird has done its configuration job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;ip address show wt0
5: wt0: &amp;lt;POINTOPOINT,NOARP,UP,LOWER_UP&amp;gt; mtu 1280 qdisc noqueue state UNKNOWN group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/none
    inet 100.109.71.139/16 brd 100.109.255.255 scope global wt0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NetBird assigned the private IP &lt;code&gt;100.127.136.241&lt;/code&gt; to the WireGuard interface &lt;code&gt;wt0&lt;/code&gt; on my machine. Similarly, it assigned private IPs to remote machines that my laptop is allowed to connect to (&lt;code&gt;allowed ips&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;wg show
interface: wt0
public key: LAo1oYyliEnvENFRZJSVb93K1kOZFu627/KSxSYjWAw&lt;span class="o"&gt;=&lt;/span&gt;
private key: &lt;span class="o"&gt;(&lt;/span&gt;hidden&lt;span class="o"&gt;)&lt;/span&gt;
listening port: 51820

peer: HzjQ27XQHhm3Ky2jrCDOOF63hgop/GS6zfzYEQmH6VA&lt;span class="o"&gt;=&lt;/span&gt;
endpoint: 35.191.145.11:62519
allowed ips: 100.127.78.76/32
latest handshake: 24 seconds ago
transfer: 124 B received, 180 B sent
persistent keepalive: every 25 seconds

peer: &lt;span class="nv"&gt;Fz30t36HImRdt1nwVslVFxy2Q96OEB2SwCFYGeIHdmA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
endpoint: 34.173.121.152:35895
allowed ips: 100.127.58.115/32, 172.17.0.0/16
latest handshake: 25 seconds ago
transfer: 124 B received, 180 B sent
persistent keepalive: every 25 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Need for DNS in private networks
&lt;/h2&gt;

&lt;p&gt;Now, what about DNS? Wouldn't it be much more convenient to have a meaningful and easily memorable name instead of the&lt;br&gt;
&lt;code&gt;100.127.16.22&lt;/code&gt; IP address to access a privately hosted Postgres database hidden behind this IP?&lt;br&gt;
To address this, NetBird automatically assigns a domain name to each peer in the private &lt;code&gt;netbird.cloud&lt;/code&gt; space that one can use for remote access.&lt;/p&gt;

&lt;p&gt;Running the &lt;code&gt;netbird status -d&lt;/code&gt; command shows that my machine has connected to &lt;code&gt;postgres.netbird.cloud&lt;/code&gt; and &lt;code&gt;berlin-office.netbird.cloud&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;netbird status &lt;span class="nt"&gt;-d&lt;/span&gt;
Peers detail:
postgres.netbird.cloud:
NetBird IP: 100.127.16.22
Public key: 99W1Y7p3QPesQpEYu7i0mXlvo+jBhv4DzvY7apPDDBM&lt;span class="o"&gt;=&lt;/span&gt;
Status: Connected
&lt;span class="nt"&gt;--&lt;/span&gt; detail &lt;span class="nt"&gt;--&lt;/span&gt;
Connection &lt;span class="nb"&gt;type&lt;/span&gt;:P2P
Last connection update: 2023-12-13 11:47:56

berlin-office.netbird.cloud:
NetBird IP: 100.127.58.115
Public key: &lt;span class="nv"&gt;Fz30t36HImRdt1nwVslVFxy2Q96OEB2SwCFYGeIHdmA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
Status: Connected
&lt;span class="nt"&gt;--&lt;/span&gt; detail &lt;span class="nt"&gt;--&lt;/span&gt;
Connection &lt;span class="nb"&gt;type&lt;/span&gt;: P2P
Last connection update: 2023-12-13 11:43:32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ping command successfully resolves &lt;code&gt;postgres.netbird.cloud&lt;/code&gt; to &lt;code&gt;100.127.16.22&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 postgres.netbird.cloud
PING postgres.netbird.cloud &lt;span class="o"&gt;(&lt;/span&gt;100.127.16.22&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from 100.127.16.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;34.6 ms
64 bytes from 100.127.16.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;35.6 ms
64 bytes from 100.127.16.22: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33.0 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Local DNS resolution implementation
&lt;/h2&gt;

&lt;p&gt;How does NetBird do it so that DNS resolution works on Linux, Mac, Windows, mobile phones, and even inside Docker? While&lt;br&gt;
working on the feature, we had a few options. One was to set up a centralized DNS server, e.g., &lt;code&gt;dns.netbird.io&lt;/code&gt;,&lt;br&gt;
and configure local resolvers on all connected machines to point to it.&lt;br&gt;
However, such an approach brings scalability, performance, and, more importantly, privacy issues.&lt;br&gt;
Like network traffic, we prefer our users' DNS queries not to go through our servers.&lt;/p&gt;

&lt;p&gt;Another approach is to modify the local &lt;code&gt;/etc/hosts&lt;/code&gt; file on every machine, specifying the remote machines' name-IP pairs.&lt;br&gt;
The hosts file can quickly outgrow when dealing with large networks, and who would like it if NetBird modified this file?&lt;/p&gt;

&lt;p&gt;We took the locality approach and avoided significant system configuration changes. Every NetBird agent starts an embedded&lt;br&gt;
DNS resolver that holds the NetBird name-IP pairs of accessible machines in memory and resolves the names when requested.&lt;br&gt;
The centralized NetBird management service sends DNS updates (essentially name-IP pairs) through the control channel&lt;br&gt;
when new machines join or leave the network or the administrator changes DNS settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo48rj9xzccksuaerz5zn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo48rj9xzccksuaerz5zn.png" alt="NetBird DNS management" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We applied this approach to all supported operating systems. However, there is a caveat.&lt;br&gt;
The NetBird agent must also configure the machine's operating system to point all &lt;code&gt;netbird.cloud&lt;/code&gt; queries to the local NetBird resolver.&lt;/p&gt;

&lt;p&gt;Configuring DNS management varies from operating system to operating system and even version to version.&lt;br&gt;
Add kernel WireGuard to it, and you will get some interesting implementation differences. Read on, and you will finally get to the port issue!&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring DNS on Linux
&lt;/h2&gt;

&lt;p&gt;Linux is the most used OS in NetBird. The great variety of Linux flavors and versions make the administration,&lt;br&gt;
for lack of a better word, unpleasant. DNS configuration is not the exception - Linux offers different ways to do it.&lt;br&gt;
Depending on the system setup, you could use the &lt;a href="https://wiki.gnome.org/Projects/NetworkManager"&gt;NetworkManager&lt;/a&gt; service,&lt;br&gt;
&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-resolved.service.html"&gt;systemd-resolved&lt;/a&gt; service,&lt;a href="https://manpages.ubuntu.com/manpages/trusty/man8/resolvconf.8.html"&gt;resolvconf&lt;/a&gt; command line tool, and a direct modification of the &lt;a href="https://manpages.ubuntu.com/manpages/focal/man8/resolvconf.8.html"&gt;resolv.conf&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Which one does NetBird use? As mentioned earlier, we avoid causing significant changes to the system configuration.&lt;br&gt;
Therefore, NetBird checks the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file that usually contains a comment indicating how the system manages DNS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 /etc/resolv.conf
&lt;span class="c"&gt;# This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).&lt;/span&gt;
&lt;span class="c"&gt;# Do not edit.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My Ubuntu system uses the systemd-resolved service. There is also a clear warning not to edit; we follow the advice.&lt;/p&gt;

&lt;p&gt;With a bunch of &lt;a href="https://github.com/netbirdio/netbird/blob/f73a2e284840eff247de097bdd4e65c486019f7d/client/internal/dns/host_linux.go#L64"&gt;"if a string contains" conditions&lt;/a&gt;, NetBird chooses the right manager.&lt;/p&gt;

&lt;p&gt;After picking the manager, NetBird starts configuring DNS.&lt;br&gt;
It uses D-Bus for communicating with NetworkManager and systemd-resolved, &lt;a href="https://pkg.go.dev/os/exec"&gt;os.exec&lt;/a&gt; with resolvconf, and &lt;a href="https://pkg.go.dev/os#example-WriteFile"&gt;os.WriteFile&lt;/a&gt; for a direct &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file modification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugzfiopyw88vkbug6ev8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugzfiopyw88vkbug6ev8.png" alt="DNS configuration tools" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are familiar with the Go programming language, look at the &lt;a href="https://github.com/netbirdio/netbird/blob/9fa0fbda0db01ba047781eaee7494361edc18f14/client/internal/dns/systemd_linux.go#L84"&gt;open-sourced code&lt;/a&gt; calling the systemd-resolved service via D-Bus.&lt;/p&gt;

&lt;p&gt;Below is the command-line equivalent of the Go code that uses Ubuntu's default &lt;code&gt;bustctl&lt;/code&gt; utility to call D-Bus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;busctl call org.freedesktop.resolve1 &lt;span class="se"&gt;\&lt;/span&gt;
  /org/freedesktop/resolve1/link/_312 &lt;span class="se"&gt;\&lt;/span&gt;
  org.freedesktop.resolve1.Link &lt;span class="se"&gt;\&lt;/span&gt;
  SetDNS &lt;span class="s1"&gt;'a(iay)'&lt;/span&gt; 1 2 &lt;span class="se"&gt;\&lt;/span&gt;
  4 100 127 136 241
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does the configuration result look like? When I run the &lt;code&gt;resolvectl status&lt;/code&gt; command on my machine,&lt;br&gt;
the applied DNS configuration appears as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# wt0 is the WireGuard interface that NetBird created on the machine&lt;/span&gt;
misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;resolvectl status wt0
Link 12 &lt;span class="o"&gt;(&lt;/span&gt;wt0&lt;span class="o"&gt;)&lt;/span&gt;
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR &lt;span class="nt"&gt;-mDNS&lt;/span&gt; &lt;span class="nt"&gt;-DNSOverTLS&lt;/span&gt; &lt;span class="nv"&gt;DNSSEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no/unsupported
Current DNS Server: 100.127.136.241
       DNS Servers: 100.127.136.241
        DNS Domain: netbird.cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent reused its NetBird private IP &lt;code&gt;100.127.136.241&lt;/code&gt; and configured the system's DNS to resolve &lt;code&gt;netbird.cloud&lt;/code&gt;&lt;br&gt;
with the embedded resolver listening on the default DNS port &lt;code&gt;53&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Port 53 issue
&lt;/h2&gt;

&lt;p&gt;The setup described above looks straightforward, doesn't it? The NetBird agent started a local resolver,&lt;br&gt;
received a configuration from the management server, and applied it to the system. However, there are always exceptions.&lt;br&gt;
Some of them are very common. Other processes like dnsmasq, Unbound, or Pi-hole might already occupy the default DNS port.&lt;br&gt;
On top of that, some systems won't allow administrators to specify a custom port. Support for custom DNS ports was added&lt;br&gt;
to the &lt;a href="https://github.com/systemd/systemd/blob/v246/NEWS#L285"&gt;systemd-resolved service only in 2020&lt;/a&gt;,&lt;br&gt;
while &lt;code&gt;resolv.conf&lt;/code&gt; file doesn't support it to date. What to do in this situation?&lt;br&gt;
NetBird pursues the goal of a  networking platform that works anywhere, universally, and without complex configurations.&lt;br&gt;
Therefore, we had to apply some logic to work around the issue and came up with two solutions to most of the corner cases.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution: unassigned loopback address
&lt;/h2&gt;

&lt;p&gt;The NetBird-embedded DNS resolver tries to listen for incoming DNS queries on the private NetBird IP or on the loopback address and port &lt;code&gt;53&lt;/code&gt;,&lt;br&gt;
e.g., &lt;code&gt;100.127.136.241:53&lt;/code&gt; and &lt;code&gt;127.0.0.1:53&lt;/code&gt;, respectively. As we discovered, this may not be possible due to some other process occupying port &lt;code&gt;53&lt;/code&gt;.&lt;br&gt;
NetBird yields to an unassigned loopback address, &lt;code&gt;127.0.0.153:53&lt;/code&gt; in this case.&lt;br&gt;
The Linux operating system will allow a combination of an unassigned loopback address and port even though it is occupied.&lt;/p&gt;

&lt;p&gt;Below is a small Go program that simulates the situation and tries listening on &lt;code&gt;127.0.0.1:53&lt;/code&gt; and then on &lt;code&gt;127.0.0.153:53&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// array of IP:port combinations&lt;/span&gt;
    &lt;span class="n"&gt;addresses&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:53"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.153:53"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// create a listener for each address&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;addresses&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;packetConn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenPacket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"udp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed creating UDP listener on %s: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;packetConn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"successfully created UDP listener on %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="c"&gt;// handle packets&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packetConn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error reading packet: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
             &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Keep the main goroutine running&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After compiling and running the code, we see that it worked without issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux: go build &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./test
successfully created a UDP listener on 127.0.0.1:53
successfully created a UDP listener on 127.0.0.153:53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Netstat also indicates that there are two go processes listening on port 53:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netstat &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;53
udp        0      0 127.0.0.153:53          0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;          24356/./test
udp        0      0 127.0.0.1:53            0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;          24356/./test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, the systemd-resolved service has a similar behavior and listens on &lt;code&gt;127.0.0.53:53&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That was a theory, but what does it look like in real life?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;100.127.136.241:53&lt;/code&gt; address, a private NetBird IP, may not be available because of the &lt;code&gt;dnsmasq&lt;/code&gt; service that listens&lt;br&gt;
on all interfaces by default (omitting ipv6 for simplicity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tcp        0      0 172.17.0.1:53           0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;         LISTEN      26136/dnsmasq
tcp        0      0 127.0.0.1:53            0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;         LISTEN      26136/dnsmasq
tcp        0      0 100.127.136.241:53      0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;         LISTEN      26136/dnsmasq
udp        0      0 127.0.0.1:53            0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;                     26136/dnsmasq
udp        0      0 100.127.136.241:53      0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;                     26136/dnsmasq
udp        0      0 172.17.0.1:53           0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;                     26136/dnsmasq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How about &lt;code&gt;127.0.0.1:53&lt;/code&gt;? Similar to &lt;code&gt;dnsmasq&lt;/code&gt; another DNS service &lt;a href="https://github.com/NLnetLabs/unbound"&gt;unbound&lt;/a&gt; occupies this address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tcp        0      0 127.0.0.1:8953          0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;         LISTEN      27023/unbound
tcp        0      0 127.0.0.1:53            0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;         LISTEN      27023/unbound
udp        0      0 127.0.0.1:53            0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;                     27023/unbound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching to &lt;code&gt;127.0.0.153:53&lt;/code&gt; should do the trick in most cases. But what if some process listens on all interfaces?&lt;/p&gt;

&lt;p&gt;When a process listens on all interfaces, in other words, &lt;code&gt;0.0.0.0:53&lt;/code&gt;,&lt;br&gt;
Linux won't allow using unassigned loopback addresses.&lt;br&gt;
We can quickly test this scenario by modifying the address parameter in the Go testing code by placing &lt;code&gt;0.0.0.0:53&lt;/code&gt; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;addresses&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:53"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.1:53"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.153:53"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code fails with the &lt;code&gt;address already in use&lt;/code&gt; error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux: go build &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./test
successfully created a UDP listener on 0.0.0.0:53
failed creating a UDP listener on 127.0.0.1:53: &lt;span class="nb"&gt;bind&lt;/span&gt;: address already &lt;span class="k"&gt;in &lt;/span&gt;use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That could happen when network administrators apply custom configurations to the system.&lt;br&gt;
NetBird uses eBPF with &lt;a href="https://www.iovisor.org/technology/xdp"&gt;XDP (eXpress Data Path)&lt;/a&gt; to work around this issue and share the port with the other process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution: port forwarding with XDP and eBPF
&lt;/h2&gt;

&lt;p&gt;From the documentation: XDP is a framework that enables high-speed packet processing within eBPF applications.&lt;br&gt;
To enable faster response to network operations,&lt;br&gt;
XDP runs a eBPF program as soon as possible, immediately as the network interface receives a packet. Here is how we use it in NetBird:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ms6L45oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://netbird.io/img/xdp-ebpf-dns-port-forwarding-dark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ms6L45oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://netbird.io/img/xdp-ebpf-dns-port-forwarding-dark.png" alt="DNS port forwarding in NetBird with XDP" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shortly, the agent initiates an embedded NetBird DNS listener on a custom port &lt;code&gt;5053&lt;/code&gt; and NetBird IP &lt;code&gt;100.127.136.241&lt;/code&gt;,&lt;br&gt;
that the NetBird management service assigned to the peer.&lt;br&gt;
However, due to issues encountered when using custom ports, the agent configures the system to reroute all&lt;br&gt;
&lt;code&gt;netbird.cloud&lt;/code&gt; queries to a "fake" resolver address using the peer's NetBird IP &lt;code&gt;100.127.136.241:53&lt;/code&gt;.&lt;br&gt;
This address is referred to as "fake" because there is no active listener on it. Subsequently,&lt;br&gt;
the eBPF program intercepts network packets destined for the fake address and&lt;br&gt;
 forwards them to port &lt;code&gt;5053&lt;/code&gt;, where the real embedded NetBird DNS resolver listens.&lt;/p&gt;

&lt;p&gt;We developed the eBPF program in C with a core function &lt;code&gt;xdp_dns_fwd&lt;/code&gt; shown below and attached it&lt;br&gt;
to the loopback interface &lt;code&gt;lo&lt;/code&gt; with XDP.&lt;br&gt;
This function modifies the destination address of incoming DNS packets when it matches the &lt;code&gt;dns_ip&lt;/code&gt;.&lt;br&gt;
Specifically, it transforms the address &lt;code&gt;100.127.136.241:53&lt;/code&gt; to &lt;code&gt;100.127.136.241:5053&lt;/code&gt;. Additionally,&lt;br&gt;
it handles outgoing DNS packets returning from the NetBird resolver to DNS clients by altering the source address.&lt;br&gt;
In this case, &lt;code&gt;100.127.136.241:5053&lt;/code&gt; is changed to &lt;code&gt;100.127.136.241:53&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;xdp_dns_fwd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;iphdr&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;udphdr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dns_port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;read_settings&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// For DNS request processing:&lt;/span&gt;
    &lt;span class="c"&gt;// - Incoming DNS packets going to,&lt;/span&gt;
    &lt;span class="c"&gt;//   for example, 100.127.136.241:53 (a 'fake' address),&lt;/span&gt;
    &lt;span class="c"&gt;//   are redirected to the NetBird resolver at 100.127.136.241:5053.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;GENERAL_DNS_PORT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;daddr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;dns_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;udp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dns_port&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// For DNS response processing:&lt;/span&gt;
    &lt;span class="c"&gt;// - The source port 5053 of the NetBird resolver's response&lt;/span&gt;
    &lt;span class="c"&gt;//   is replaced with port 53.&lt;/span&gt;
    &lt;span class="c"&gt;//   E.g., 100.127.136.241:5053 becomes 100.127.136.241:53.&lt;/span&gt;
    &lt;span class="c"&gt;//   This is necessary for the client to accept the response.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;dns_port&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;saddr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;dns_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;udp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GENERAL_DNS_PORT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why did we attach the XDP program to the loopback interface but not the WireGuard interface &lt;code&gt;wt0&lt;/code&gt;?&lt;br&gt;
The reason is simple: the DNS resolution we are performing is local. The system understands this and optimizes the flow by using the loopback interface.&lt;br&gt;
That is also why we can modify the DNS response even though XDP only works on ingress traffic.&lt;br&gt;
Running &lt;code&gt;tcpdump&lt;/code&gt; while pinging &lt;code&gt;postgres.netbird.cloud&lt;/code&gt; demonstrates this behaviour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;misha@misha-linux:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'udp and (port 53 or port 5053)'&lt;/span&gt;
15:17:53.581324 lo    In  IP 100.127.136.241.59049 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 100.127.136.241.5053: UDP, length 44
15:17:53.581675 lo    In  IP 100.127.136.241.53 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 100.127.136.241.59049: 34021 0/0/0 &lt;span class="o"&gt;(&lt;/span&gt;44&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have also noticed a call to the &lt;code&gt;read_settings()&lt;/code&gt; function. We use it to make the function more flexible and pass parameters from the Go code. It helps us configure the "fake" address with the &lt;code&gt;dns_ip&lt;/code&gt; and &lt;code&gt;dns_port&lt;/code&gt; properties that the eBPF program uses to "catch" DNS packets.&lt;br&gt;
We achieved this by using &lt;a href="https://docs.kernel.org/bpf/maps.html"&gt;BPF maps&lt;/a&gt;&lt;br&gt;
to pass these parameters from the userspace Go application to the eBPF program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;read_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;__u16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;port_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;__u32&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ip_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;// read dns ip (configured fake address)&lt;/span&gt;
    &lt;span class="n"&gt;ip_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_map_lookup_elem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nb_map_dns_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;map_key_dns_ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ip_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;dns_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htonl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ip_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// read dns port (configured real active listener port. e.g. 5053)&lt;/span&gt;
    &lt;span class="n"&gt;port_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_map_lookup_elem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nb_map_dns_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;map_key_dns_port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;port_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;dns_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;port_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To put it all together and use the function in Go, we've built and compiled the program with &lt;a href="https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go"&gt;bpf2go&lt;/a&gt;,&lt;br&gt;
which is a component of the &lt;a href="https://cilium.io/"&gt;Cilium&lt;/a&gt; ecosystem.&lt;br&gt;
The command generates Go helper files that contain the eBPF program's bytecode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# required packages libbpf-dev, libc6-dev-i386-amd64-cross&lt;/span&gt;
go run github.com/cilium/ebpf/cmd/bpf2go &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-cc&lt;/span&gt; clang-14 &lt;span class="se"&gt;\&lt;/span&gt;
    bpf src/prog.c &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt; /usr/x86_64-linux-gnu/include
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated files can be used to load eBPF functions and pass parameters via a BPF map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;GeneralManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;LoadDNSFwd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dnsPort&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debugf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"load ebpf DNS forwarder, watching addr: %s:53, redirect to port: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dnsPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadXdp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bpfObjs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NbMapDnsIp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapKeyDNSIP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip2int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bpfObjs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NbMapDnsPort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapKeyDNSPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsPort&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;featureFlagDnsForwarder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bpfObjs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NbFeatures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapKeyFeatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;featureFlags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the complete eBPF code in our GitHub repository at the following link: &lt;a href="https://github.com/netbirdio/netbird/tree/main/client/internal/ebpf"&gt;GitHub Repo - netbirdio/netbird&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We didn't expect to use technologies like eBPF and XDP when we started working on the DNS feature, nor did we think there would be so many edge cases.&lt;br&gt;
The main reason for the complexity was our integration with the kernel WireGuard, where NetBird configures the interface and steps aside.&lt;br&gt;
NetBird doesn't have direct access to network packets in this mode, unlike the userspace wireguard-go implementation,&lt;br&gt;
where the DNS packets can be processed directly in Go. Therefore, the userspace mode doesn't require port forwarding with XDP and eBPF.&lt;/p&gt;

&lt;p&gt;We intentionally picked the harder path, not just because we enjoy challenges but also because the kernel WireGuard mode&lt;br&gt;
offers high network performance, security, and efficiency. We are committed to bringing this value to our cloud and open-source users.&lt;/p&gt;

&lt;p&gt;As for the userspace implementation, the NetBird DNS feature also works on Windows, macOS, and mobile phones where we used wireguard-go.&lt;br&gt;
How does it work there? We will cover this in a separate article.&lt;br&gt;
Meanwhile, try NetBird in the cloud with the &lt;a href="https://app.netbird.io/install"&gt;QuickStart Guide&lt;/a&gt; or self-host it on your servers with the &lt;a href="https://github.com/netbirdio/netbird?tab=readme-ov-file#quickstart-with-netbird-cloud"&gt;Self-hosting Quickstart Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dns</category>
      <category>networking</category>
      <category>wireguard</category>
      <category>security</category>
    </item>
  </channel>
</rss>
