Private Networks

Running ClusterLink in a private network, behind a NAT or firewall.

This task involves connecting ClusterLink behind a NAT or firewall. To connect the ClusterLink gateway, each peer should have a public IP that will be reachable from other peers to enable cross-cluster communications. However, this is not always possible if clusters are behind corporate NAT or firewalls that allow outgoing connections only. In such scenarios, we will use the Fast Reverse Proxy (FRP) open-source project to create reverse tunnels and connect all clusters behind a private network. With FRP, only one IP needs to be public to connect all the clusters in the fabric.

To enable connectivity between the ClusterLink gateways, we need to set up one FRP server with a public IP and create an FRP client for each ClusterLink gateway that connects to the server.

In this task, we will use the FRP Kubernetes image to create the FRP server and clients. We will create one FRP server and two FRP clients: one to create a reverse tunnel and provide access to the server cluster behind a NAT, and another to connect to the FRP server and provide access to the cluster behind the NAT.

drawing

The FRP server can support multiple clusters behind a private network. However, it is also possible to establish multiple FRP servers, with one for each cluster. If a cluster gateway has a public IP, communication can occur without using FRP. This task includes instructions on how to connect the peers using FRP. Instructions for creating full connectivity between applications to remote services can be found in the Nginx tutorial and iPerf3 tutorial.

In this task, we will extend the peer connectivity instructions to use FRP.

Create FRP Server

In this step, we will create the FRP server on the same cluster we use for ClusterLink (the client cluster), but it can be on any peer or Kubernetes cluster.

  1. Create a namespace for all FRP components:

    Client cluster:

    echo " apiVersion: v1 kind: Namespace metadata: name: frp " | kubectl apply -f -

    Server cluster:

    echo " apiVersion: v1 kind: Namespace metadata: name: frp " | kubectl apply -f -
  2. Create a configmap that contains the FRP server configuration:

    Client cluster:

    echo " apiVersion: v1 kind: ConfigMap metadata: name: frps-config namespace: frp data: frps.toml: | bindPort = 4443 " | kubectl apply -f -

    In this setup, we expose the FRP server pod on port 4443.

  3. Create FRP server deployment:

    Client cluster:

    echo " apiVersion: apps/v1 kind: Deployment metadata: name: frps namespace: frp spec: replicas: 1 selector: matchLabels: app: frps template: metadata: labels: app: frps spec: hostNetwork: true containers: - name: frps image: snowdreamtech/frps volumeMounts: - name: frps-config-volume mountPath: /etc/frp/frps.toml subPath: frps.toml volumes: - name: frps-config-volume configMap: name: frps-config restartPolicy: Always " | kubectl apply -f -
  4. Create an ingress service to expose the FRP server:

    Client cluster: v

    echo " apiVersion: v1 kind: Service metadata: name: clusterlink-frps namespace: frp spec: type: NodePort selector: app: frps ports: - port: 4443 targetPort: 4443 nodePort: 30444 " | kubectl apply -f -

    In this case, we use a NodePort service, but it can be other types like LoadBalancer.

Create FRP Clients

  1. Set the FRP_SERVER_IP and FRP_SECRET_KEY variables for each cluster:

    Client cluster:

    export FRP_SERVER_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' client-control-plane` export FRP_SECRET_KEY=`echo $USER | sha256sum | head -c 10`

    Server cluster:

    export FRP_SERVER_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' client-control-plane` export FRP_SECRET_KEY=`echo $USER | sha256sum | head -c 10`

    The FRP_SECRET_KEY should be identical across all clusters.

  2. Deploy FRP client configuration on each cluster:

    Client cluster:

    echo " apiVersion: v1 kind: ConfigMap metadata: name: frpc-config namespace: frp data: frpc.toml: | # Set server address serverAddr = \""${FRP_SERVER_IP}"\" serverPort = 30444 [[visitors]] name = \"clusterlink-client-to-server-visitor\" type = \"stcp\" serverName = \"clusterlink-server\" secretKey = \""${FRP_SECRET_KEY}"\" bindAddr = \"::\" bindPort = 6002 " | kubectl apply -f -

    Server cluster:

    echo " apiVersion: v1 kind: ConfigMap metadata: name: frpc-config namespace: frp data: frpc.toml: | # Set server address serverAddr = \""${FRP_SERVER_IP}"\" serverPort = 30444 [[proxies]] name = \"clusterlink-server\" type = \"stcp\" localIP = \"clusterlink.clusterlink-system.svc.cluster.local\" localPort = 443 secretKey = \""${FRP_SECRET_KEY}"\" " | kubectl apply -f -

    For each configuration, we first set the FRP server’s IP address and port number.

    In the server cluster, we create a proxy that connects to the local ClusterLink gateway and establishes a reverse tunnel to the FRP server, allowing other FRP clients to connect to it. In the client cluster, we create an FRP visitor that specifies which other peers this client wants to connect to. (You need to create a visitor for each peer you want to connect to.) For more details about FRP configuration, you can refer to the FRP configuration documentation. For an example of connecting multiple clusters behind a private network, see the ClusterLink FRP example.

  3. Create a K8s service that connects to the FRP client visitor, allowing ClusterLink to connect to it:

    Client cluster:

    echo ' apiVersion: v1 kind: Service metadata: name: server-peer-clusterlink namespace: frp spec: type: ClusterIP selector: app: frpc ports: - port: 6002 targetPort: 6002 ' | kubectl apply -f -
  4. Create FRP client deployment for each cluster:

    Client cluster:

    echo " apiVersion: apps/v1 kind: Deployment metadata: name: frpc namespace: frp spec: replicas: 1 selector: matchLabels: app: frpc template: metadata: labels: app: frpc spec: containers: - name: frpc image: snowdreamtech/frpc volumeMounts: - name: frpc-config-volume mountPath: /etc/frp volumes: - name: frpc-config-volume configMap: name: frpc-config restartPolicy: Always " | kubectl apply -f -

    Server cluster:

    echo " apiVersion: apps/v1 kind: Deployment metadata: name: frpc namespace: frp spec: replicas: 1 selector: matchLabels: app: frpc template: metadata: labels: app: frpc spec: containers: - name: frpc image: snowdreamtech/frpc volumeMounts: - name: frpc-config-volume mountPath: /etc/frp volumes: - name: frpc-config-volume configMap: name: frpc-config restartPolicy: Always " | kubectl apply -f -

Create Peer CRD

  1. Create a Peer CRD that points to the server cluster. The Peer CRD should connect to a Kubernetes service that points to the visitor port in the frpc client.

    Client cluster:

    echo " apiVersion: clusterlink.net/v1alpha1 kind: Peer metadata: name: server namespace: clusterlink-system spec: gateways: - host: server-peer-clusterlink.frp.svc.cluster.local port: 6002 " | kubectl apply -f -

    To verify that the connectivity between the peers is established correctly, please check if the condition PeerReachable has been added to the peer CR status in each cluster.

    kubectl describe peers.clusterlink.net -A
Sample output
```
Name:         client
Namespace:    clusterlink-system
Labels:       <none>
Annotations:  <none>
API Version:  clusterlink.net/v1alpha1
Kind:         Peer
Metadata:
    Creation Timestamp:  2024-05-28T12:47:33Z
    Generation:          1
    Resource Version:    807
    UID:                 1fdeafff-707a-43e2-bb3a-826f003a42ed
Spec:
    Gateways:
    Host:  172.18.0.4
    Port:  30443
Status:
    Conditions:
    Last Transition Time:  2024-05-28T12:47:33Z
    Message:
    Reason:                Heartbeat
    Status:                True
    Type:                  PeerReachable
```

Connect Application Services

After creating the peer connectivity using FRP, continue to the next step of exporting services, importing services, and creating policies as described in the tutorials Nginx tutorial and iPerf3 tutorial.

Cleanup

To remove all FRP components, delete the frp namespace:

Client cluster:

kubectl delete namespace frp

Server cluster:

kubectl delete namespace frp

This part remove only the FRP components. To remove all ClusterLink components, please refer to the full instructions in the tutorials.