package vpnconfig import ( "context" "errors" "net" "sigs.k8s.io/controller-runtime/pkg/client" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "edge-infra.dev/pkg/edge/api/types" v1cluster "edge-infra.dev/pkg/edge/apis/cluster/v1alpha1" v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1" "edge-infra.dev/pkg/sds/remoteaccess/service" "edge-infra.dev/pkg/sds/remoteaccess/wireguard/vpn" ) func Update(ctx context.Context, c client.Client, sm types.SecretManagerService, vpn *vpn.VPN, vpnConfig *v1vpnconfig.VPNConfig, cluster *v1cluster.Cluster) error { // Set relay and client wireguard instances in banner config if err := vpn.UpdateRelay(ctx, c); err != nil { return err } if err := vpn.UpdateClient(ctx, c); err != nil { return err } // retrieve external load balancer ip and reconcile if its not set lbIP, err := service.GetLoadBalancerExternalIP(ctx, c) if err != nil { return err } else if lbIP == nil { return errors.New("load balancer does not have an external IP") } // ensure subnet is configured from up-to-date CIDR from VPN ConfigMap if err := vpn.ConfigureSubnet(ctx, c); err != nil { return err } // ensure VPNConfig has valid IP address if err := updateVPNConfigIPAddress(vpnConfig, vpn); err != nil { return err } // update store config object if err := vpn.UpdateStore(ctx, c, vpnConfig, cluster); err != nil { return err } _, subnet, err := net.ParseCIDR(vpn.GetSubnetCIDR()) if err != nil { return err } // update store wireguard objects if err := vpn.Store(vpnConfig.ClusterEdgeID()).UpdateWireguardSecret( ctx, c, subnet, vpn.Client().GetIPAddress(), lbIP, vpn.Relay().GetPublicKey(), sm, cluster, ); err != nil { return err } // update store emissary CRDs if err := vpn.Store(vpnConfig.ClusterEdgeID()).UpdateEmissaryMapping(ctx, c, cluster.GetName()); err != nil { return err } // update client objects if err := vpn.Client().UpdateWireguardSecret( ctx, c, subnet, lbIP, vpn.Relay().GetPublicKey(), vpn.Stores(), ); err != nil { return err } // update relay objects return vpn.Relay().UpdateWireguardSecret( ctx, c, subnet, vpn.Client().GetIPAddress(), vpn.Client().GetPublicKey(), vpn.Stores(), ) } func updateVPNConfigIPAddress(vpnConfig *v1vpnconfig.VPNConfig, vpn *vpn.VPN) error { if isValid, err := checkIPAddressIsValid(vpnConfig, vpn); err != nil { return err } else if isValid { return nil } // request a new (valid) IP address from the pool ip, err := vpn.RequestAvailableIPAddress(vpnConfig.ClusterEdgeID()) if err != nil { return err } // set the IP address in the VPNConfig status field if vpnConfig.Status == nil { vpnConfig.Status = &v1vpnconfig.VPNConfigStatus{} } vpnConfig.Status.IP = ip.String() return nil } func checkIPAddressIsValid(vpnConfig *v1vpnconfig.VPNConfig, vpn *vpn.VPN) (bool, error) { // IP address is not valid if outside of subnet if isInSubnet, err := vpn.IPAddressIsInSubnet(vpnConfig.IP()); err != nil { return false, err } else if !isInSubnet { return false, nil } // if the IP address unassigned in the pool, IP is valid if vpn.CheckIPAddressIsAvailable(vpnConfig.IP()) { return true, nil } // if the IP address is taken by itself, IP is valid if vpn.CheckIPAddressIsAssignedToStore(vpnConfig.IP(), vpnConfig.ClusterEdgeID()) { return true, nil } return false, nil } func Remove(ctx context.Context, c client.Client, sm types.SecretManagerService, vpn *vpn.VPN, vpnConfig *v1vpnconfig.VPNConfig, cluster *v1cluster.Cluster) error { // create dummy store if it doesn't exist yet so we can delete any objects that may be in the cluster before removing it if !vpn.HasStore(vpnConfig.ClusterEdgeID()) { if err := vpn.UpdateStore(ctx, c, vpnConfig, cluster); err != nil { return err } } // remove store Wireguard objects if err := vpn.Store(vpnConfig.ClusterEdgeID()).RemoveWireguardSecret(ctx, c, sm); err != nil { return err } // remove store Emissary CRDs if err := vpn.Store(vpnConfig.ClusterEdgeID()).RemoveEmissaryMapping(ctx, c); err != nil { return err } // remove the store from banner config vpn.RemoveStore(vpnConfig.ClusterEdgeID()) // retrieve external load balancer ip and reconcile if its not set lbIP, err := service.GetLoadBalancerExternalIP(ctx, c) if err != nil { return err } else if lbIP == nil { return errors.New("load balancer does not have an external IP") } _, subnet, err := net.ParseCIDR(vpn.GetSubnetCIDR()) if err != nil { return err } // update client objects if err := vpn.Client().UpdateWireguardSecret( ctx, c, subnet, lbIP, vpn.Relay().GetPublicKey(), vpn.Stores(), ); err != nil { return err } // update relay objects return vpn.Relay().UpdateWireguardSecret( ctx, c, subnet, vpn.Client().GetIPAddress(), vpn.Client().GetPublicKey(), vpn.Stores(), ) } func AddOwnerReference(vpnConfig *v1vpnconfig.VPNConfig, cluster *v1cluster.Cluster) { vpnConfig.SetOwnerReferences([]metav1.OwnerReference{createOwnerReferenceToCluster(cluster)}) } func createOwnerReferenceToCluster(cluster *v1cluster.Cluster) metav1.OwnerReference { return metav1.OwnerReference{ APIVersion: cluster.APIVersion, Kind: cluster.Kind, Name: cluster.GetName(), UID: cluster.GetUID(), } }