[Yandex Cloud documentation](../../index.md) > [Yandex Managed Service for Kubernetes](../index.md) > [Step-by-step guides](index.md) > Working with external nodes > Configuring WireGuard gateways to connect external nodes to a cluster

# Configuring WireGuard gateways to connect external nodes to a cluster

With Yandex Managed Service for Kubernetes, you can connect servers from outside Yandex Cloud as [Kubernetes cluster](../concepts/index.md#kubernetes-cluster) nodes. To connect one, first, set up network connectivity between the remote network hosting the external server and the [cloud network](../../vpc/concepts/network.md#network) hosting your Managed Service for Kubernetes cluster. You can do this using a VPN.

Below is an example of establishing network connectivity over the [WireGuard](https://www.wireguard.com) protocol.

You can see the solution architecture in the diagram below:

![external-nodes-connect](../../_assets/tutorials/external-nodes-connect/external-nodes-connect.svg)

As you see in the diagram, the external Kubernetes cluster node and VPN client reside in a remote network outside Yandex Cloud.

To facilitate testing, the solution below uses a separate Yandex Virtual Private Cloud [cloud network](../../vpc/concepts/network.md#network) as the remote site, and a Yandex Compute Cloud [VM instance](../../compute/concepts/vm.md) located in that cloud network, as the VPN client.

## Getting started {#before-you-begin}

1. [Create](../../vpc/operations/network-create.md) your main cloud network with three [subnets](../../vpc/concepts/network.md#subnet) in different availability zones.

1. In the main network, [create](kubernetes-cluster/kubernetes-cluster-create.md) a Managed Service for Kubernetes cluster with a [highly available](../concepts/index.md#master) master.

    To create an external node group, the Managed Service for Kubernetes cluster must operate in [tunnel mode](../concepts/network-policy.md#cilium). You can only enable this mode when creating the cluster.

1. [Install kubect](https://kubernetes.io/docs/tasks/tools/install-kubectl) and [configure it to work with the new cluster](connect/index.md#kubectl-connect).

1. In the main network, [create](../../compute/operations/vm-create/create-linux-vm.md) a Compute Cloud VM with a [public IP address](../../vpc/concepts/address.md#public-addresses); name it `VM-1`. On this VM, you will set up the main WireGuard gateway.

1. [Create](../../vpc/operations/network-create.md) an additional cloud network with one subnet.

1. In the additional network, [create](../../compute/operations/vm-create/create-linux-vm.md) a Compute Cloud VM with a public IP address; name it `VM-2`. On this VM, you will set up the additional WireGuard gateway.

## Configuring security groups {#sg}

1. In the main network, [create](../../vpc/operations/security-group-create.md) a security group and associate it with `VM-1`. [Add](../../vpc/operations/security-group-add-rule.md) the following rules to the group:

    {% list tabs group=traffic %}

    - Outgoing traffic {#outgoing}

      Description | Port range | Protocol | Destination name | CIDR blocks
      --- | --- | --- | --- | ---
      `any` | `0-65535` | `Any` | `CIDR` | `0.0.0.0/0`

    - Incoming traffic {#incoming}

      Description | Port range | Protocol | Source | CIDR blocks
      --- | --- | --- | --- | ---
      `icmp` | `0-65535` | `ICMP` | `CIDR` | `0.0.0.0/0`
      `ssh` | `22` | `TCP` | `CIDR` | `0.0.0.0/0`
      `wireguard` | `51821` | `UDP` | `CIDR` | `<VM-2_public_address>/32`
      `VM-2-subnet` | `0-65535` | `Any` | `CIDR` | `<VM-2_subnet_CIDR>`
      
    {% endlist %}

1. In the additional network, [create](../../vpc/operations/security-group-create.md) a security group and associate it with `VM-2`. [Add](../../vpc/operations/security-group-add-rule.md) the following rules to the group:

    {% list tabs group=traffic %}

    - Outgoing traffic {#outgoing}

      Description | Port range | Protocol | Destination name | CIDR blocks
      --- | --- | --- | --- | ---
      `any` | `0-65535` | `Any` | `CIDR` | `0.0.0.0/0`
      
    - Incoming traffic {#incoming}
      
      Description | Port range | Protocol | Source | CIDR blocks
      --- | --- | --- | --- | ---
      `icmp` | `0-65535` | `ICMP` | `CIDR` | `0.0.0.0/0`
      `ssh` | `22` | `TCP` | `CIDR` | `0.0.0.0/0`
      `wireguard` | `51822` | `UDP` | `CIDR` | `<VM-1_public_address>/32`
      `k8s-VM-1-subnets` | `0-65535` | `Any` | `CIDR` | `<main_subnet1_CIDR>`, `<main_subnet2_CIDR>`, `<<main_subnet3_CIDR>`
      `cluster&services` | `0-65535` | `Any` | `CIDR` | `<cluster_CIDR>`, `<CIDRs_of_services>`

    {% endlist %}    

1. [Add](../../vpc/operations/security-group-add-rule.md) the following rule to the security group of the Managed Service for Kubernetes cluster and node groups:

    {% list tabs group=traffic %}

    - Incoming traffic {#incoming}

      Description | Port range | Protocol | Source | CIDR blocks
      --- | --- | --- | --- | ---
      `VM-2-subnet` | `0-65535` | `Any` | `CIDR` | `<VM-2_subnet_CIDR>`

    {% endlist %}

## Configuring routing {#rt}

1. Configure routing for the main WireGuard gateway:

   1. In the main network, [create a route table and add a static route to it](../../vpc/operations/static-route-create.md):

      * **Destination prefix**: Specify the CIDR of the subnet hosting `VM-2`.
      * **IP address**: Specify the `VM-1` internal IP address.

   1. Associate the route table with all subnets in your main network.

1. Configure routing for the additional WireGuard gateway:

   1. In the additional network, create a route table.

   1. Add a static route for the route table:

      * **Destination prefix**: Specify the CIDR of the subnet hosting `VM-1`.
      * **IP address**: Specify the `VM-2` internal IP address.

      Repeat this step for each subnet in your main network.

   1. Associate the route table with the `VM-2` subnet.

## Setting up WireGuard gateways {#gw}

1. Set up the main WireGuard gateway:

    1. [Connect](../../compute/operations/vm-connect/ssh.md) to `VM-1` over SSH.

    1. Install WireGuard:

        ```bash
        sudo apt update && sudo apt install wireguard
        ```

    1. Generate and save the encryption keys:

        ```
        wg genkey | sudo tee vm1_private.key | wg pubkey | sudo tee vm1_public.key > /dev/null
        wg genkey | sudo tee vm2_private.key | wg pubkey | sudo tee vm2_public.key > /dev/null
        ```
       In the current directory, the system will create these four files:

          * `vm1_private.key`: Contains the private encryption key for `VM-1`.
          * `vm1_public.key`: Contains the public encryption key for `VM-1`.
          * `vm2_private.key`: Contains the private encryption key for `VM-2`.
          * `vm2_public.key`: Contains the public encryption key for `VM-2`.

    1. Create a configuration file named `wg0.conf`:

        ```bash
        sudo nano /etc/wireguard/wg0.conf
        ```

    1. Add the following configuration to it:

        ```text
        [Interface]
        PrivateKey = <vm1_private.key_file_contents>
        Address = 10.0.0.1/32
        ListenPort = 51821

        PreUp = sysctl -w net.ipv4.ip_forward=1

        [Peer]
        PublicKey = <vm2_public.key_file_contents>
        Endpoint = <VM-2_public_address>:51822
        AllowedIPs = <VM-2_subnet_CIDR>, 10.0.0.2/32
        PersistentKeepalive = 15
        ```
  
       [Learn more about the configuration parameters](https://www.procustodibus.com/blog/2020/12/wireguard-site-to-site-config).

       Save the changes and close the file.

    1. Apply the configuration:

        ```bash
        sudo systemctl restart wg-quick@wg0
        ```

1. Set up the additional WireGuard gateway:

    1. [Connect](../../compute/operations/vm-connect/ssh.md) to `VM-2` over SSH.

    1. Install WireGuard:

        ```bash
        sudo apt update && sudo apt install wireguard
        ```

    1. Create a configuration file named `wg0.conf`:

        ```bash
        sudo nano /etc/wireguard/wg0.conf
        ```

    1. Add the following configuration to it:

        ```text
        [Interface]
        PrivateKey = <vm2_private.key_file_contents>
        Address = 10.0.0.2/32
        ListenPort = 51822

        PreUp = sysctl -w net.ipv4.ip_forward=1

        [Peer]
        PublicKey = <vm1_public.key_file_contents>
        Endpoint = <VM-1_public_address>:51821
        AllowedIPs = <main_subnet1_CIDR>, <main_subnet2_CIDR>, <main_subnet3_CIDR>, 10.0.0.1/32
        PersistentKeepalive = 15
        ```

       [Learn more about the configuration parameters](https://www.procustodibus.com/blog/2020/12/wireguard-site-to-site-config).

       Save the changes and close the file.

    1. Apply the configuration:

        ```bash
        sudo systemctl restart wg-quick@wg0
        ```

1. Check the connection status on both VMs:

   ```bash
   sudo wg show
   ```

    You should see `latest handshake` in the command output, indicating a successfully established connection: 

    ```text
    ...
    latest handshake: 3 seconds ago
    ...
    ```

1. Configure [MTU](../../vpc/concepts/mtu-mss.md) on both VMs:
   
      ```bash
      ETH_NIC=eth0
      sudo iptables -t mangle -A FORWARD -i ${ETH_NIC} -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
      sudo iptables -t mangle -A FORWARD -o ${ETH_NIC} -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
      echo "net.ipv4.ip_no_pmtu_disc = 1" | sudo tee -a /etc/sysctl.conf
      sudo sysctl -p /etc/sysctl.conf
      ``` 
   
     {% note warning %}
   
     If you keep the default MTU value, you may lose network traffic.
   
     {% endnote %}

1. [Connect](external-nodes-connect.md) `VM-2` to the Managed Service for Kubernetes cluster as its external node.

## Troubleshooting {#troubleshooting}

### Error when using the docker-ce and containerd packages on an external node {#docker-containerd}

Follow these steps for troubleshooting:

1. View the list of services that are not running properly:

   ```bash
   sudo systemctl --failed
   ```

   Result:

   ```text
   UNIT LOAD ACTIVE SUB DESCRIPTION
   docker.socket loaded failed failed Docker Socket for the API
   LOAD = Reflects whether the unit definition was properly loaded.
   ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
   SUB = The low-level unit activation state, values depend on unit type.
   1 loaded units listed.
   ```

1. Check the `docker.socket` status:

   ```bash
   sudo systemctl status docker.socket
   ```

   Result:

   ```text
   docker.socket - Docker Socket for the API
   Loaded: loaded (/lib/systemd/system/docker.socket; disabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Tue 2024-02-10 09:53:37 UTC; 6s ago
   Triggers: ● docker.service
   Listen: /run/docker.sock (Stream)
   CPU: 1ms
   Feb 10 09:53:37 ext-node systemd[1]: Starting Docker Socket for the API...
   Feb 10 09:53:37 ext-node systemd[7052]: docker.socket: Failed to resolve group docker: No such process
   Feb 10 09:53:37 ext-node systemd[1]: docker.socket: Control process exited, code=exited, status=216/GROUP
   Feb 10 09:53:37 ext-node systemd[1]: docker.socket: Failed with result 'exit-code'.
   Feb 10 09:53:37 ext-node systemd[1]: Failed to listen on Docker Socket for the API.
   ```

1. Look up errors in the system logs:

   ```bash
   sudo journalctl -xe
   ```

   Result:

   ```bash
   ...
   Feb 10 09:56:40 ext-node maintainer[19298]: E: Sub-process /usr/bin/dpkg returned an error code (1)
   ...
   ```

1. Reinstall the packages and fix the errors:

   ```bash
   sudo apt install -f
   ```

1. When the installer prompts you for action with the `config.toml` file, enter `N` to keep the current version of the file.