Wireguard VPN

Introduction

In the aerOS architecture, the Wireguard VPN plays a crucial role in enabling secure communication between aerOS services across different aerOS domains. aerOS services, which are deployed as Kubernetes services or pods, often need to communicate with services in other aerOS domains. Wireguard facilitates this inter-domain communication by creating an overlay network (tunnel) that ensures security and connectivity.

For example, consider a scenario where one service in aerOS Domain A needs to communicate with another service in aerOS Domain B. Using Wireguard, a secure overlay network is established, allowing these services to “speak” with each other seamlessly. This setup ensures that both services can securely reach each other, maintaining the integrity and confidentiality of the transmitted data.

wireguard-network

Features

  • Simplicity: Minimal configuration required compared to traditional VPNs.

  • Speed: High performance with low overhead.

  • Security: Utilizes modern cryptographic techniques.

  • Portability: Runs on various platforms and devices.

  • Scalability: Suitable for both small-scale and large-scale deployments.

Place in architecture

In a Kubernetes cluster, Wireguard can be deployed as a server pod. Containers (services) from other clusters can connect to the Wireguard server, enabling an overlay network across multiple clusters. This setup ensures secure communication between services in different clusters.

wireguard

User guide

Prerequisites

  • A Kubernetes cluster up and running.

  • kubectl configured to interact with your Kubernetes cluster.

  • Basic understanding of Kubernetes and networking concepts.

  • Generate private and public keys for the WireGuard server and for N clients.

Note

To generate the corresponding keys, use apt install wireguard-tools and then:

wg genkey | tee privatekey | wg pubkey > publickey

Installation

1.Server-side (aerOS Domain 1)

To install wireguard-server in an aerOS Domain (K8s cluster), follow these steps:

Create a ConfigMap for Wireguard configuration:

kind: ConfigMap
apiVersion: v1
metadata:
  name: wg-configmap
data:
  wg0.conf: |
    [Interface]
    Address = 10.13.13.1/24
    ListenPort = 51820
    PrivateKey = (Server's private key)
    PreUp = iptables -I FORWARD -i wg0 -j REJECT
    PostDown = iptables -D FORWARD -i wg0 -j REJECT
    PreUp = iptables -I FORWARD -i wg0 -d 10.13.13.0/24 -j ACCEPT
    PostDown = iptables -D FORWARD -i wg0 -d 10.13.13.0/24 -j ACCEPT

    [Peer]
    #client-1
    PublicKey = (Client's public key)
    AllowedIPs = 10.13.13.2/32


# Apply the ConfigMap
kubectl apply -f wg-configmap.yaml

Create a Deployment for the Wireguard server:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wireguard-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wireguard
  template:
    metadata:
      labels:
        app: wireguard
    spec:
      containers:
        - name: wireguard
          image: masipcat/wireguard-go:latest
          command:
          - sh
          - -c
          - /entrypoint.sh
          ports:
          - containerPort: 51820
            protocol: UDP
            name: wireguard
          env:
          - name: LOG_LEVEL
            value: info
          securityContext:
            capabilities:
              add:
                - NET_ADMIN
                - SYS_MODULE
            privileged: true
          resources:
            requests:
              memory: 64Mi
              cpu: "100m"
            limits:
              memory: 256Mi
          volumeMounts:
          - name: cfgmap
            mountPath: /etc/wireguard/wg0.conf
            subPath: wg0.conf
          - name: host-volumes
            mountPath: /lib/modules
      volumes:
      - name: cfgmap
        configMap:
          name: wg-configmap
      - name: host-volumes
        hostPath:
          path: /lib/modules
          type: Directory

# Apply the Deployment
kubectl apply -f wireguard-deployment.yaml

Create a Service for the Wireguard server:

kind: Service
apiVersion: v1
metadata:
  annotations:
    metallb.universe.tf/allow-shared-ip: "sharing-same-ip"
  name: wireguard
  labels:
    app: wireguard
spec:
  type: LoadBalancer
  ports:
  - name: wg
    protocol: UDP
    port: 51820
    targetPort: 51820
  selector:
    app: wireguard

# Apply the Service
kubectl apply -f wg-service.yaml

2.Client-side (aerOS Domain 2)

Create a ConfigMap for Wireguard client configuration:

apiVersion: v1
kind: ConfigMap
metadata:
  name: wireguard-config
data:
  wg0.conf: |
     [Interface]
     Address = 10.13.13.2/32
     PrivateKey = (Client's private key)
     ListenPort = 51820

     [Peer]
     PublicKey = (Server's public key)
     Endpoint =  ncsrd-dev-domain.aeros-project.eu:51820
     AllowedIPs = 10.13.13.0/24
     persistentKeepalive = 25

# Apply the ConfigMap
kubectl apply -f wireguard-config.yaml

Create a Deployment for the Wireguard client:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wireguard-client
  labels:
    app: wireguard-client
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wireguard-client
  template:
    metadata:
      labels:
        app: wireguard-client
    spec:
      initContainers:
      - name: setup-config
        image: busybox
        command: ["sh", "-c", "cp /config-src/* /config-dest/"]
        volumeMounts:
        - name: wg-config
          mountPath: /config-src
          readOnly: true
        - name: wg-temp
          mountPath: /config-dest
      containers:
      - name: wireguard
        image: ghcr.io/linuxserver/wireguard
        securityContext:
          capabilities:
            add: ["NET_ADMIN"]
          privileged: true
        env:
        - name: WG_DISABLE_IPV6
          value: "yes"
        volumeMounts:
        - name: wg-temp
          mountPath: /config/wg_confs
          readOnly: false
      - name: nginx # example app
        image: nginx:latest
        ports:
        - containerPort: 80
      volumes:
      - name: wg-config
        configMap:
          name: wireguard-config
          defaultMode: 0600
      - name: wg-temp
        emptyDir: {}

# Apply the Deployment
kubectl apply -f wireguard-client.yaml

3.Verify Installation/Connection

Once the Wireguard server (Domain 1) and Wireguard client (Domain 2) are correctly installed in both domains, we can verify their overlay network connection.

Method 1:

Check the Wireguard status on the server:

kubectl exec -it wireguard-server-5d845cb9d-bgrhc -- wg

Output:

Defaulted container "wireguard" out of: wireguard
interface: wg0
  public key: w6r8RJaMOkGxdhXkR29jE/MNh5ivgZtfbgXWX/Y4xj0=
  private key: (hidden)
  listening port: 51820

peer: eXf93YG023jt+Srjls43lR81VQ/rXBz+eWv+ewUBHlI=
  endpoint: 10.247.3.156:32796
  allowed ips: 10.13.13.2/32
  latest handshake: 2 minutes, 8 seconds ago  # Handshake created between server and client
  transfer: 642.73 KiB received, 185.65 KiB sent

Method 2:

The Wireguard interface is set to 10.13.13.1/24 and the client (peer) has an IP 10.13.13.2/32. Let’s ping each other to confirm the connection is established.

From Domain 1 (server):

kubectl exec -it wireguard-server-5d845cb9d-bgrhc -- ping -c 4 10.13.13.2

Output:

Defaulted container "wireguard" out of: wireguard
PING 10.13.13.2 (10.13.13.2): 56 data bytes
64 bytes from 10.13.13.2: seq=0 ttl=64 time=0.849 ms
64 bytes from 10.13.13.2: seq=1 ttl=64 time=0.762 ms
64 bytes from 10.13.13.2: seq=2 ttl=64 time=0.741 ms
64 bytes from 10.13.13.2: seq=3 ttl=64 time=0.783 ms

--- 10.13.13.2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.741/0.783/0.849 ms

From Domain 2 (client):

kubectl exec -it wireguard-client-1-c999ffbf7-4mngp -- ping -c 4 10.13.13.1

Output:

Defaulted container "wireguard" out of: wireguard, nginx, setup-config (init)
PING 10.13.13.1 (10.13.13.1) 56(84) bytes of data.
64 bytes from 10.13.13.1: icmp_seq=1 ttl=64 time=0.943 ms
64 bytes from 10.13.13.1: icmp_seq=2 ttl=64 time=0.770 ms
64 bytes from 10.13.13.1: icmp_seq=3 ttl=64 time=0.810 ms
64 bytes from 10.13.13.1: icmp_seq=4 ttl=64 time=0.737 ms

--- 10.13.13.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3046ms
rtt min/avg/max/mdev = 0.737/0.815/0.943/0.078 ms

Method 3:

Let’s trace the network route between the server and the client.

From the server-side:

kubectl exec -it wireguard-server-5d845cb9d-bgrhc -- traceroute 10.13.13.2

Output:

Defaulted container "wireguard" out of: wireguard
traceroute to 10.13.13.2 (10.13.13.2), 30 hops max, 46 byte packets
 1  10.13.13.2 (10.13.13.2)  0.870 ms  1.638 ms  0.596 ms

From the client-side:

kubectl exec -it wireguard-client-1-c999ffbf7-4mngp -- traceroute 10.13.13.1

Output:

Defaulted container "wireguard" out of: wireguard, nginx, setup-config (init)
traceroute to 10.13.13.1 (10.13.13.1), 30 hops max, 46 byte packets
 1  10.13.13.1 (10.13.13.1)  0.877 ms  0.761 ms  0.886 ms

We can see that with one hop they reach each other even while being in separate networks. With the help of Wireguard, the network traffic is securely tunneled directly from one aerOS service to another, effectively creating a seamless and efficient connection between the two services as if they were part of the same local network.

Configuration options

Server-side

Wireguard Configuration File (wg0.conf)

The Wireguard configuration file (wg0.conf) is where you define the interface and peer settings. Here are some key options you can configure:

[Interface] Section:
  • Address: The IP address for the Wireguard interface. - Example: 10.13.13.1/24

  • ListenPort: The port on which Wireguard will listen for incoming connections. - Example: 51820

  • PrivateKey: The private key for the Wireguard server. This key should be kept secret. - Example: iELN6zGpzemkkJyo2jBSDlBuIE/J8PW67Poi66xpFHs=

  • PreUp and PostDown: Commands to be executed before the interface is brought up and after it is brought down. These are typically used for configuring iptables rules. - Examples:

    • PreUp = iptables -I FORWARD -i wg0 -j REJECT

    • PostDown = iptables -D FORWARD -i wg0 -j REJECT

    • PreUp = iptables -I FORWARD -i wg0 -d 10.13.13.0/24 -j ACCEPT

    • PostDown = iptables -D FORWARD -i wg0 -d 10.13.13.0/24 -j ACCEPT

Note

You can adjust the IP tables based on your routing needs.

[Peer] Section:
  • PublicKey: The public key of a Wireguard peer (client). - Example: eXf93YG023jt+Srjls43lR81VQ/rXBz+eWv+ewUBHlI=

  • AllowedIPs: The IP addresses that are allowed to communicate with this peer. - Example: 10.13.13.2/32

Environment Variables in the Deployment

The Wireguard server deployment includes the following environment variables:

  • LOG_LEVEL: Sets the logging level for the Wireguard server. Example: info.

Security Context

The security context in the deployment specifies the following:

  • capabilities: Adds the NET_ADMIN and SYS_MODULE capabilities, which are necessary for Wireguard to function.

  • privileged: The container runs in privileged mode to allow access to necessary system resources.

Resource Requests and Limits

The deployment specifies resource requests and limits to ensure the Wireguard server has the necessary resources:

  • requests: The minimum resources required. Example: memory: 64Mi, cpu: “100m”.

  • limits: The maximum resources allowed. Example: memory: 256Mi.

Volume Mounts

The deployment mounts the following volumes:

  • cfgmap: Mounts the Wireguard configuration file from the ConfigMap.

  • host-volumes: Mounts the /lib/modules directory from the host to the container, necessary for loading kernel modules.

Service Configuration

The service configuration includes the following options:

  • type: The type of service. Example: LoadBalancer.

  • ports: The ports exposed by the service. Example: port: 51820, protocol: UDP.

  • selector: Labels used to identify the pods that the service targets. Example: app: wireguard.

These options provide flexibility in configuring and deploying the Wireguard server in your Kubernetes cluster.

Client-side

Wireguard Client Configuration File

The Wireguard client configuration file is where you define the interface and peer settings. Here are some key options you can configure:

[Interface] Section:
  • Address: The IP address for the Wireguard client interface. - Example: 10.13.13.3/32

  • PrivateKey: The private key for the Wireguard client. This key should be kept secret. - Example: AFXh/ZrI001vUt6Bb+g3TcgkaqUCDrSxTN9JskbM4Hg=

  • ListenPort: The port on which Wireguard will listen for incoming connections. - Example: 51820

[Peer] Section:
  • PublicKey: The public key of the Wireguard server. - Example: w6r8RJaMOkGxdhXkR29jE/MNh5ivgZtfbgXWX/Y4xj0=

  • Endpoint: The endpoint address and port of the Wireguard server. - Example: ncsrd-dev-domain.aeros-project.eu:51820

  • AllowedIPs: The IP addresses that are allowed to communicate with this peer. - Example: 10.13.13.0/24

  • PersistentKeepalive: The interval at which keepalive packets are sent to maintain the connection. - Example: 25

Environment Variables in the Deployment

The Wireguard client deployment includes the following environment variables:

  • WG_DISABLE_IPV6: Disables IPv6 for the Wireguard client. Example: yes.

Security Context

The security context in the deployment specifies the following:

  • capabilities: Adds the NET_ADMIN capability, which is necessary for Wireguard to function.

  • privileged: The container runs in privileged mode to allow access to necessary system resources.

Volume Mounts

The deployment mounts the following volumes:

  • wg-config: Mounts the Wireguard configuration file from the ConfigMap.

  • wg-temp: An empty directory used to hold the temporary configuration files during initialization.

Note

The Wireguard client is deployed alongside an example application (Nginx) as a sidecar container. The Nginx server is exposed on port 80, but this can be replaced with any other application as needed.

Developer guide

Authors

License

Notice (dependencies)