...

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

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

     1  package wireguard
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"time"
     7  
     8  	corev1 "k8s.io/api/core/v1"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  
    11  	v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1"
    12  
    13  	"k8s.io/apimachinery/pkg/api/errors"
    14  
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  
    17  	"edge-infra.dev/pkg/edge/k8objectsutils"
    18  	"edge-infra.dev/pkg/lib/crypto"
    19  	"edge-infra.dev/pkg/sds/remoteaccess/constants"
    20  	secrets "edge-infra.dev/pkg/sds/remoteaccess/wireguard/secret"
    21  )
    22  
    23  const (
    24  	SecretPrefix     string = "keys-"
    25  	SecretExpires    string = "expires"
    26  	SecretPublicKey  string = "publicKey"
    27  	SecretPrivateKey string = "privateKey"
    28  	SecretIPAddress  string = "ipAddress"
    29  )
    30  
    31  type Instance struct {
    32  	IPAddress  net.IP
    33  	PublicKey  string
    34  	PrivateKey string
    35  }
    36  
    37  func (i *Instance) GetIPAddress() net.IP {
    38  	return i.IPAddress
    39  }
    40  
    41  func (i *Instance) GetPublicKey() string {
    42  	return i.PublicKey
    43  }
    44  
    45  func (i *Instance) GetPrivateKey() string {
    46  	return i.PrivateKey
    47  }
    48  
    49  // Create a new wireguard instance and K8s secret
    50  func New(ctx context.Context, c client.Client, name, clusterEdgeID string, vpnConfig *v1vpnconfig.VPNConfig) (*Instance, error) {
    51  	wgKey, err := crypto.GenerateWireguardKeyPair()
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	instance := &Instance{
    56  		PublicKey:  wgKey.PublicKey(),
    57  		PrivateKey: wgKey.PrivateKey(),
    58  	}
    59  	if err := createInstanceSecret(ctx, c, name, clusterEdgeID, instance, vpnConfig); err != nil {
    60  		return nil, err
    61  	}
    62  	return instance, nil
    63  }
    64  
    65  // Upon updating the IP address we update the K8s secret
    66  func (i *Instance) UpdateIPAddress(ctx context.Context, c client.Client, name, clusterEdgeID string, ip net.IP) error {
    67  	i.IPAddress = ip
    68  	return updateInstanceSecret(ctx, c, name, clusterEdgeID, i)
    69  }
    70  
    71  // Checks for existing wireguard instance secret
    72  // If no secret exists or has expired, create new instance, marshal to json, save as K8s secret, return instance
    73  // If secret exists, unmarshal secret and return instance
    74  func GetInstance(ctx context.Context, c client.Client, name, clusterEdgeID string, vpnConfig *v1vpnconfig.VPNConfig) (*Instance, error) {
    75  	secret := &corev1.Secret{}
    76  	secretName := k8objectsutils.NameWithPrefix(SecretPrefix+name, clusterEdgeID)
    77  
    78  	err := c.Get(ctx, client.ObjectKey{Namespace: constants.VPNNamespace, Name: secretName}, secret)
    79  	if err != nil && !errors.IsNotFound(err) {
    80  		return nil, err
    81  	}
    82  	if errors.IsNotFound(err) {
    83  		return New(ctx, c, name, clusterEdgeID, vpnConfig)
    84  	}
    85  
    86  	// check if secret has expired
    87  	expires, err := time.Parse(time.RFC3339, secret.Annotations[SecretExpires])
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	if time.Now().After(expires) {
    92  		return rotateInstanceSecret(ctx, c, name, clusterEdgeID, secret, vpnConfig)
    93  	}
    94  
    95  	return instanceFromSecret(secret), nil
    96  }
    97  
    98  func rotateInstanceSecret(ctx context.Context, c client.Client, name, clusterEdgeID string, secret *corev1.Secret, vpnConfig *v1vpnconfig.VPNConfig) (*Instance, error) {
    99  	// we need to ensure that we use the previously set IP address when rotating instance secrets
   100  	old := instanceFromSecret(secret)
   101  
   102  	if err := c.Delete(ctx, secret); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	instance, err := New(ctx, c, name, clusterEdgeID, vpnConfig)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	err = instance.UpdateIPAddress(ctx, c, name, clusterEdgeID, old.IPAddress)
   112  	return instance, err
   113  }
   114  
   115  func instanceFromSecret(secret *corev1.Secret) *Instance {
   116  	return &Instance{
   117  		PublicKey:  string(secret.Data[SecretPublicKey]),
   118  		PrivateKey: string(secret.Data[SecretPrivateKey]),
   119  		IPAddress:  net.ParseIP(string(secret.Data[SecretIPAddress])),
   120  	}
   121  }
   122  
   123  func createInstanceSecret(ctx context.Context, c client.Client, name, clusterEdgeID string, instance *Instance, vpnConfig *v1vpnconfig.VPNConfig) error {
   124  	secretName := k8objectsutils.NameWithPrefix(SecretPrefix+name, clusterEdgeID)
   125  	secret := &corev1.Secret{
   126  		ObjectMeta: metav1.ObjectMeta{
   127  			Name:      secretName,
   128  			Namespace: constants.VPNNamespace,
   129  			Annotations: map[string]string{
   130  				SecretExpires: secrets.ExpireAt().Format(time.RFC3339),
   131  			},
   132  			OwnerReferences: createOwnerReferences(name, vpnConfig),
   133  		},
   134  		Data: map[string][]byte{
   135  			SecretPublicKey:  []byte(instance.GetPublicKey()),
   136  			SecretPrivateKey: []byte(instance.GetPrivateKey()),
   137  			SecretIPAddress:  []byte(instance.GetIPAddress().String()),
   138  		},
   139  	}
   140  	return c.Create(ctx, secret)
   141  }
   142  
   143  func updateInstanceSecret(ctx context.Context, c client.Client, name, clusterEdgeID string, instance *Instance) error {
   144  	secretName := k8objectsutils.NameWithPrefix(SecretPrefix+name, clusterEdgeID)
   145  	secret := &corev1.Secret{}
   146  	if err := c.Get(ctx, client.ObjectKey{Namespace: constants.VPNNamespace, Name: secretName}, secret); err != nil {
   147  		return err
   148  	}
   149  	secret.Data[SecretPublicKey] = []byte(instance.GetPublicKey())
   150  	secret.Data[SecretPrivateKey] = []byte(instance.GetPrivateKey())
   151  	secret.Data[SecretIPAddress] = []byte(instance.GetIPAddress().String())
   152  	return c.Update(ctx, secret)
   153  }
   154  
   155  func createOwnerReferences(name string, vpnConfig *v1vpnconfig.VPNConfig) []metav1.OwnerReference {
   156  	if name != constants.StoreName || vpnConfig == nil {
   157  		return []metav1.OwnerReference{}
   158  	}
   159  	return []metav1.OwnerReference{
   160  		{
   161  			APIVersion: vpnConfig.APIVersion,
   162  			Kind:       vpnConfig.Kind,
   163  			Name:       vpnConfig.GetName(),
   164  			UID:        vpnConfig.GetUID(),
   165  		},
   166  	}
   167  }
   168  

View as plain text