...

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

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

     1  package vpn
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/netip"
     9  
    10  	corev1 "k8s.io/api/core/v1"
    11  	"sigs.k8s.io/controller-runtime/pkg/client"
    12  
    13  	"edge-infra.dev/pkg/sds/remoteaccess/constants"
    14  	v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1"
    15  )
    16  
    17  var ErrNoIPAddressesAvailable = errors.New("no IP addresses available in subnet")
    18  var ErrSubnetNotConfigured = errors.New("subnet has not been configured yet, run `ConfigureSubnet()`")
    19  
    20  // map of IP addresses to ClusterEdgeID
    21  type IPAddressPool map[string]string
    22  
    23  func (v *VPN) ConfigureSubnet(ctx context.Context, c client.Client) error {
    24  	subnetCIDR, err := getSubnetCIDRFromConfigMap(ctx, c)
    25  	if err != nil {
    26  		return err
    27  	}
    28  
    29  	// do nothing if address pool is populated and subnet has not changed
    30  	if v.AvailableIPAddressPool != nil {
    31  		if subnetCIDR == v.GetSubnetCIDR() {
    32  			return nil
    33  		}
    34  	}
    35  
    36  	// update subnet CIDR and repopulate IP address pool from current VPNConfig IP addresses
    37  	v.SubnetCIDR = subnetCIDR
    38  	return v.populateAvailableIPAddressPool(ctx, c)
    39  }
    40  
    41  func getSubnetCIDRFromConfigMap(ctx context.Context, c client.Client) (string, error) {
    42  	configMap := &corev1.ConfigMap{}
    43  	if err := c.Get(ctx, vpnConfigMapKey, configMap); err != nil {
    44  		return "", err
    45  	}
    46  
    47  	subnetCIDR, ok := configMap.Data[constants.VPNConfigMapCIDRKey]
    48  	if !ok {
    49  		return "", fmt.Errorf("expected key '%s' in ConfigMap %s/%s", constants.VPNConfigMapCIDRKey, constants.VPNNamespace, vpnConfigMapKey.String())
    50  	}
    51  	return subnetCIDR, nil
    52  }
    53  
    54  func (v *VPN) populateAvailableIPAddressPool(ctx context.Context, c client.Client) error {
    55  	if err := v.createInitialIPAddressPool(ctx, c); err != nil {
    56  		return err
    57  	}
    58  
    59  	vpnConfigs, err := getVPNConfigList(ctx, c)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	// set (in-subnet) IP addresses from all VPNConfigs as unavailable
    65  	for _, vpnConfig := range vpnConfigs.Items {
    66  		isInSubnet, err := v.IPAddressIsInSubnet(vpnConfig.IP())
    67  		if err != nil {
    68  			return err
    69  		}
    70  		if isInSubnet && v.CheckIPAddressIsAvailable(vpnConfig.IP()) {
    71  			v.setIPAddressUnavailable(vpnConfig.IP(), vpnConfig.ClusterEdgeID())
    72  		}
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func (v *VPN) createInitialIPAddressPool(ctx context.Context, c client.Client) error {
    79  	v.AvailableIPAddressPool = IPAddressPool{}
    80  
    81  	subnet, err := netip.ParsePrefix(v.GetSubnetCIDR())
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	// set all IP addresses in pool to available, but reserve first two addresses for relay and client
    87  	idx := 0
    88  	for nextIP := subnet.Addr(); subnet.Contains(nextIP); nextIP = nextIP.Next() {
    89  		ip := net.IP(nextIP.AsSlice())
    90  		v.setIPAddressAvailable(ip)
    91  		if err := v.reserveIPAddresses(ctx, c, ip, idx); err != nil {
    92  			return err
    93  		}
    94  		idx++
    95  	}
    96  	return nil
    97  }
    98  
    99  // Reserves the first two IP addresses for the relay and client
   100  func (v *VPN) reserveIPAddresses(ctx context.Context, c client.Client, ip net.IP, idx int) error {
   101  	if idx == 0 {
   102  		if err := v.Relay().UpdateIPAddress(ctx, c, constants.RelayName, "cluster-infra", ip); err != nil {
   103  			return err
   104  		}
   105  		v.setIPAddressUnavailable(ip, "relay")
   106  	} else if idx == 1 {
   107  		if err := v.Client().UpdateIPAddress(ctx, c, constants.ClientName, "cluster-infra", ip); err != nil {
   108  			return err
   109  		}
   110  		v.setIPAddressUnavailable(ip, "client")
   111  	}
   112  	return nil
   113  }
   114  
   115  // Returns a set of IP addresses assigned to the current VPNConfig objects
   116  func getVPNConfigList(ctx context.Context, c client.Client) (*v1vpnconfig.VPNConfigList, error) {
   117  	vpnConfigs := &v1vpnconfig.VPNConfigList{}
   118  	if err := c.List(ctx, vpnConfigs, &client.ListOptions{Namespace: constants.VPNNamespace}); err != nil {
   119  		return nil, err
   120  	}
   121  	return vpnConfigs, nil
   122  }
   123  
   124  // Check if a given IP address is in the subnet
   125  func (v *VPN) IPAddressIsInSubnet(ip net.IP) (bool, error) {
   126  	if ip == nil {
   127  		return false, nil
   128  	}
   129  
   130  	subnetCIDR := v.GetSubnetCIDR()
   131  	subnet, err := netip.ParsePrefix(subnetCIDR)
   132  	if err != nil {
   133  		return false, err
   134  	}
   135  
   136  	address, err := netip.ParseAddr(ip.String())
   137  	if err != nil {
   138  		return false, err
   139  	}
   140  
   141  	return subnet.Contains(address), nil
   142  }
   143  
   144  // Return an available IP address from the address pool and set as unavailable
   145  func (v *VPN) RequestAvailableIPAddress(clusterEdgeID string) (net.IP, error) {
   146  	if v.AvailableIPAddressPool == nil {
   147  		return nil, ErrSubnetNotConfigured
   148  	}
   149  
   150  	for nextIP := range v.AvailableIPAddressPool {
   151  		ip := net.ParseIP(nextIP)
   152  		if v.CheckIPAddressIsAvailable(ip) {
   153  			v.setIPAddressUnavailable(ip, clusterEdgeID)
   154  			return ip, nil
   155  		}
   156  	}
   157  	return nil, ErrNoIPAddressesAvailable
   158  }
   159  
   160  func (v *VPN) setIPAddressUnavailable(ip net.IP, clusterEdgeID string) {
   161  	if v.AvailableIPAddressPool != nil {
   162  		v.AvailableIPAddressPool[ip.String()] = clusterEdgeID
   163  	}
   164  }
   165  
   166  func (v *VPN) setIPAddressAvailable(ip net.IP) {
   167  	if v.AvailableIPAddressPool != nil {
   168  		v.AvailableIPAddressPool[ip.String()] = ""
   169  	}
   170  }
   171  
   172  func (v *VPN) CheckIPAddressIsAvailable(ip net.IP) bool {
   173  	if v.AvailableIPAddressPool == nil {
   174  		return false
   175  	}
   176  	return v.AvailableIPAddressPool[ip.String()] == ""
   177  }
   178  
   179  func (v *VPN) CheckIPAddressIsAssignedToStore(ip net.IP, clusterEdgeID string) bool {
   180  	if v.AvailableIPAddressPool == nil {
   181  		return false
   182  	}
   183  	return v.AvailableIPAddressPool[ip.String()] == clusterEdgeID
   184  }
   185  

View as plain text