...

Source file src/edge-infra.dev/pkg/sds/remoteaccess/wireguard/client/client.go

Documentation: edge-infra.dev/pkg/sds/remoteaccess/wireguard/client

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"strings"
     8  
     9  	appsv1 "k8s.io/api/apps/v1"
    10  	corev1 "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/labels"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  
    15  	"edge-infra.dev/pkg/k8s/runtime/objectrestarter"
    16  	"edge-infra.dev/pkg/sds/ingress/emissary"
    17  	"edge-infra.dev/pkg/sds/remoteaccess/constants"
    18  	wg "edge-infra.dev/pkg/sds/remoteaccess/wireguard"
    19  	secrets "edge-infra.dev/pkg/sds/remoteaccess/wireguard/secret"
    20  	"edge-infra.dev/pkg/sds/remoteaccess/wireguard/store"
    21  )
    22  
    23  type Client struct {
    24  	*wg.Instance
    25  }
    26  
    27  // Retrieves the client wireguard instance and creates a new one if it does not exist
    28  func Get(ctx context.Context, c client.Client) (*Client, error) {
    29  	wg, err := wg.GetInstance(ctx, c, constants.ClientName, "cluster-infra", nil)
    30  	return &Client{Instance: wg}, err
    31  }
    32  
    33  var emissaryIngressLabelSelector = labels.Set{constants.K8sNameLabel: emissary.IngressName}
    34  
    35  func (cl *Client) UpdateWireguardSecret(ctx context.Context, c client.Client, subnetCIDR *net.IPNet, relayExternalIPAddress net.IP, relayPublicKey string, stores map[string]*store.Store) error {
    36  	secret := cl.GenerateConfigurationSecret(subnetCIDR, relayExternalIPAddress, relayPublicKey, stores)
    37  	if err := secrets.CreateOrPatchSecret(ctx, c, secret); err != nil {
    38  		return err
    39  	}
    40  	return cl.updateEmissaryDeployment(ctx, c)
    41  }
    42  
    43  func (cl *Client) GenerateConfigurationSecret(subnetCIDR *net.IPNet, relayExternalIPAddress net.IP, relayPublicKey string, storeConfigs map[string]*store.Store) *corev1.Secret {
    44  	wg0ConfigString := cl.wg0ConfigString(subnetCIDR, relayExternalIPAddress, relayPublicKey, storeConfigs)
    45  	return &corev1.Secret{
    46  		ObjectMeta: metav1.ObjectMeta{
    47  			Namespace: emissary.IngressNamespace,
    48  			Name:      constants.ClientName,
    49  		},
    50  		StringData: map[string]string{constants.WireguardSecretField: wg0ConfigString},
    51  	}
    52  }
    53  
    54  func (cl *Client) wg0ConfigString(subnetCIDR *net.IPNet, relayExternalIP net.IP, relayPublicKey string, storeConfigs map[string]*store.Store) string {
    55  	allowedIPs := getAllowedIPs(storeConfigs)
    56  	interfaceConfig := cl.interfaceConfigString(subnetCIDR)
    57  	clientConfig := cl.peerConfigString(relayExternalIP, relayPublicKey, allowedIPs)
    58  	return interfaceConfig + clientConfig
    59  }
    60  
    61  func getAllowedIPs(storeConfigs map[string]*store.Store) []string {
    62  	var allowedIPs []string
    63  	for _, storeConfig := range storeConfigs {
    64  		if storeConfig.IsEnabled {
    65  			allowedIPs = append(allowedIPs, fmt.Sprintf("%s/32", storeConfig.GetIPAddress()))
    66  		}
    67  	}
    68  	return allowedIPs
    69  }
    70  
    71  func (cl *Client) interfaceConfigString(subnetCIDR *net.IPNet) string {
    72  	prefLen, _ := subnetCIDR.Mask.Size()
    73  	return fmt.Sprintf(
    74  		"[Interface]\nPrivateKey = %s\nAddress = %s/%d\nMTU = %s\n",
    75  		cl.GetPrivateKey(),
    76  		cl.GetIPAddress(),
    77  		prefLen,
    78  		constants.MTU,
    79  	)
    80  }
    81  
    82  func (cl *Client) peerConfigString(relayExternalIP net.IP, relayPublicKey string, allowedIPs []string) string {
    83  	peerConfigString := fmt.Sprintf(
    84  		"\n[Peer]\nPersistentKeepalive = 25\nEndpoint = %s:51820\nPublicKey = %s\n",
    85  		relayExternalIP,
    86  		relayPublicKey,
    87  	)
    88  	if len(allowedIPs) > 0 {
    89  		peerConfigString = fmt.Sprintf("%sAllowedIPs = %s\n", peerConfigString, strings.Join(allowedIPs, ", "))
    90  	}
    91  	return peerConfigString
    92  }
    93  
    94  // A mutating webhook will add a Wireguard container to the Emissary pod on creation. If the emissary pod was created before the webhook
    95  // configuration this will not happen. This method deletes the pod if only one container exists so the webhook configuration can apply.
    96  func (cl *Client) updateEmissaryDeployment(ctx context.Context, c client.Client) error {
    97  	if injected, err := emissaryDeploymentIsInjected(ctx, c); err != nil {
    98  		return err
    99  	} else if injected {
   100  		return nil // do nothing if already injected
   101  	}
   102  
   103  	// restart deployment so Wireguard can be injected
   104  	deployment := &appsv1.Deployment{}
   105  	if err := c.Get(ctx, client.ObjectKey{Namespace: emissary.IngressNamespace, Name: emissary.IngressName}, deployment); err != nil {
   106  		return err
   107  	}
   108  	return objectrestarter.Restart(ctx, c, deployment)
   109  }
   110  
   111  func emissaryDeploymentIsInjected(ctx context.Context, c client.Client) (bool, error) {
   112  	podList := &corev1.PodList{}
   113  	if err := c.List(ctx, podList, &client.ListOptions{
   114  		LabelSelector: labels.SelectorFromSet(emissaryIngressLabelSelector),
   115  		Namespace:     emissary.IngressNamespace,
   116  	}); err != nil {
   117  		return false, err
   118  	}
   119  
   120  	// delete any emissary-ingress pods without a Wireguard container so it can be added by the webhook on re-creation
   121  	for _, pod := range podList.Items {
   122  		if !hasWireguardContainer(pod) {
   123  			return false, nil
   124  		}
   125  	}
   126  
   127  	return true, nil
   128  }
   129  
   130  func hasWireguardContainer(pod corev1.Pod) bool {
   131  	for _, container := range pod.Spec.Containers {
   132  		if container.Name == constants.WireguardContainerName {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }
   138  

View as plain text