From 890a2c4bd3e4eab30ff51344fd6a33e5eddac6c4 Mon Sep 17 00:00:00 2001 From: wenxuanW Date: Fri, 6 Feb 2026 15:44:36 -0800 Subject: [PATCH] Squash --- aks-flex-node-sudoers | 17 +- assets/img/README/image.png | Bin 0 -> 199015 bytes go.mod | 3 + go.sum | 6 + pkg/bootstrapper/bootstrapper.go | 3 + pkg/components/cni/cni_setup_installer.go | 7 - pkg/components/kubelet/kubelet_installer.go | 103 ++- pkg/components/vpn_gateway/README.md | 29 + pkg/components/vpn_gateway/consts.go | 96 +++ .../vpn_gateway/vpn_gateway_installer.go | 674 +++++++++++++++ .../vpn_gateway/vpn_gateway_uninstaller.go | 409 +++++++++ pkg/components/vpn_gateway/vpn_operations.go | 794 ++++++++++++++++++ pkg/config/structs.go | 45 +- pkg/utils/utils.go | 146 +++- 14 files changed, 2290 insertions(+), 42 deletions(-) create mode 100644 assets/img/README/image.png create mode 100644 pkg/components/vpn_gateway/README.md create mode 100644 pkg/components/vpn_gateway/consts.go create mode 100644 pkg/components/vpn_gateway/vpn_gateway_installer.go create mode 100644 pkg/components/vpn_gateway/vpn_gateway_uninstaller.go create mode 100644 pkg/components/vpn_gateway/vpn_operations.go diff --git a/aks-flex-node-sudoers b/aks-flex-node-sudoers index f830e4d..2a27817 100644 --- a/aks-flex-node-sudoers +++ b/aks-flex-node-sudoers @@ -38,6 +38,11 @@ aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl status node-problem-det aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl check kubelet aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl check containerd aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl check node-problem-detector +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl enable openvpn@* +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl start openvpn@* +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl stop openvpn@* +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl restart openvpn@* +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl status openvpn@* aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl is-active * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl is-enabled * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/systemctl list-unit-files * @@ -82,10 +87,11 @@ aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/apt -y remove * aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/dpkg -i * aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/dpkg --purge * aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/lsof * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/apt install -y openvpn # Directory and file operations for Kubernetes paths - simplified for compatibility aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/mkdir *, /usr/bin/mkdir * -aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/mkdir -p /etc/kubernetes/*, /bin/mkdir -p /var/lib/kubelet/*, /bin/mkdir -p /var/lib/cni/*, /bin/mkdir -p /etc/containerd/*, /bin/mkdir -p /opt/cni/bin, /bin/mkdir -p /etc/cni/net.d, /bin/mkdir -p /etc/systemd/system/kubelet.service.d, /bin/mkdir -p /etc/sysctl.d, /bin/mkdir -p /etc/modules-load.d +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/mkdir -p /etc/kubernetes/*, /bin/mkdir -p /var/lib/kubelet/*, /bin/mkdir -p /var/lib/cni/*, /bin/mkdir -p /etc/containerd/*, /bin/mkdir -p /opt/cni/bin, /bin/mkdir -p /etc/cni/net.d, /bin/mkdir -p /etc/systemd/system/kubelet.service.d, /bin/mkdir -p /etc/sysctl.d, /bin/mkdir -p /etc/modules-load.d, /bin/mkdir -p /etc/aks-flex-node/*, /bin/mkdir -p /etc/openvpn aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/cp *, /bin/mv *, /bin/rm * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/chmod *, /bin/chown * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/ln *, /usr/bin/ln * @@ -94,6 +100,7 @@ aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/curl *, /usr/bin/wget * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/tar *, /usr/bin/unzip * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/ls *, /usr/bin/ls * aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/bin/test *, /bin/test * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/cat *, /usr/bin/cat * # System configuration for Kubernetes aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/sysctl --system @@ -117,9 +124,15 @@ aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/cat /etc/kubernetes/* aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/cat /var/lib/kubelet/kubeconfig -# Network operations for troubleshooting +# Network operations for troubleshooting and VPN gateway management aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip route aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip addr +aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip route add * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip route del * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip route delete * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/ip route show * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /usr/sbin/iptables * +aks-flex-node ALL=(root) NOPASSWD:SETENV: /sbin/iptables * aks-flex-node ALL=(root) NOPASSWD:SETENV: /bin/netstat -rn # Read-only Kubernetes API check for node readiness (used by status collector) diff --git a/assets/img/README/image.png b/assets/img/README/image.png new file mode 100644 index 0000000000000000000000000000000000000000..13bea376091ad5aa09bc18b5df12d6bcbb999b6d GIT binary patch literal 199015 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 a9)jHC8kUrqL>ZujKu${OQnrNA-Twoi9=}!q literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod index 6cecbc6..264017a 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,9 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/go.sum b/go.sum index 2a8019e..e350db6 100644 --- a/go.sum +++ b/go.sum @@ -8,12 +8,18 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDo github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 h1:qiir/pptnHqp6hV8QwV+IExYIf6cPsXBfUDUXQ27t2Y= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2/go.mod h1:jVRrRDLCOuif95HDYC23ADTMlvahB7tMdl519m9Iyjc= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/hybridcompute/armhybridcompute v1.2.0 h1:7UuAn4ljE+H3GQ7qts3c7oAaMRvge68EgyckoNP/1Ro= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/hybridcompute/armhybridcompute v1.2.0/go.mod h1:F2eDq/BGK2LOEoDtoHbBOphaPqcjT0K/Y5Am8vf7+0w= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 h1:bXwSugBiSbgtz7rOtbfGf+woewp4f06orW9OP5BjHLA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0/go.mod h1:Y/HgrePTmGy9HjdSGTqZNa+apUpTVIEVKXJyARP2lrk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= diff --git a/pkg/bootstrapper/bootstrapper.go b/pkg/bootstrapper/bootstrapper.go index d719a4c..776a67a 100644 --- a/pkg/bootstrapper/bootstrapper.go +++ b/pkg/bootstrapper/bootstrapper.go @@ -14,6 +14,7 @@ import ( "go.goms.io/aks/AKSFlexNode/pkg/components/runc" "go.goms.io/aks/AKSFlexNode/pkg/components/services" "go.goms.io/aks/AKSFlexNode/pkg/components/system_configuration" + "go.goms.io/aks/AKSFlexNode/pkg/components/vpn_gateway" "go.goms.io/aks/AKSFlexNode/pkg/config" ) @@ -34,6 +35,7 @@ func (b *Bootstrapper) Bootstrap(ctx context.Context) (*ExecutionResult, error) // Define the bootstrap steps in order - using modules directly steps := []Executor{ arc.NewInstaller(b.logger), // Setup Arc + vpn_gateway.NewInstaller(b.logger), // Setup VPN Gateway (if enabled) services.NewUnInstaller(b.logger), // Stop kubelet before setup system_configuration.NewInstaller(b.logger), // Configure system (early) runc.NewInstaller(b.logger), // Install runc @@ -59,6 +61,7 @@ func (b *Bootstrapper) Unbootstrap(ctx context.Context) (*ExecutionResult, error containerd.NewUnInstaller(b.logger), // Uninstall containerd binary runc.NewUnInstaller(b.logger), // Uninstall runc binary system_configuration.NewUnInstaller(b.logger), // Clean system settings + vpn_gateway.NewUnInstaller(b.logger), // Clean VPN Gateway arc.NewUnInstaller(b.logger), // Uninstall Arc (after cleanup) } diff --git a/pkg/components/cni/cni_setup_installer.go b/pkg/components/cni/cni_setup_installer.go index e7445c5..8429f29 100644 --- a/pkg/components/cni/cni_setup_installer.go +++ b/pkg/components/cni/cni_setup_installer.go @@ -89,13 +89,6 @@ func (i *Installer) IsCompleted(ctx context.Context) bool { } } - // Validate Step 3: Bridge configuration - configPath := filepath.Join(DefaultCNIConfDir, bridgeConfigFile) - if !utils.FileExistsAndValid(configPath) { - i.logger.Debug("Bridge configuration file not found") - return false - } - i.logger.Debug("CNI setup validation passed - all components properly configured") return true } diff --git a/pkg/components/kubelet/kubelet_installer.go b/pkg/components/kubelet/kubelet_installer.go index 3239a9d..00febbe 100644 --- a/pkg/components/kubelet/kubelet_installer.go +++ b/pkg/components/kubelet/kubelet_installer.go @@ -208,42 +208,58 @@ func (i *Installer) createKubeletDefaultsFile() error { labels = append(labels, fmt.Sprintf("%s=%s", key, value)) } + // Build kubelet flags dynamically + kubeletFlags := []string{ + fmt.Sprintf("--v=%d", i.config.Node.Kubelet.Verbosity), + "--address=0.0.0.0", + "--anonymous-auth=false", + "--authentication-token-webhook=true", + "--authorization-mode=Webhook", + "--cgroup-driver=systemd", + "--cgroups-per-qos=true", + "--enforce-node-allocatable=pods", + fmt.Sprintf("--cluster-dns=%s", i.config.Node.Kubelet.DNSServiceIP), + "--cluster-domain=cluster.local", + "--event-qps=0", + fmt.Sprintf("--eviction-hard=%s", mapToEvictionThresholds(i.config.Node.Kubelet.EvictionHard, ",")), + fmt.Sprintf("--kube-reserved=%s", mapToKeyValuePairs(i.config.Node.Kubelet.KubeReserved, ",")), + fmt.Sprintf("--image-gc-high-threshold=%d", i.config.Node.Kubelet.ImageGCHighThreshold), + fmt.Sprintf("--image-gc-low-threshold=%d", i.config.Node.Kubelet.ImageGCLowThreshold), + fmt.Sprintf("--max-pods=%d", i.config.Node.MaxPods), + "--node-status-update-frequency=10s", + fmt.Sprintf("--pod-infra-container-image=%s", i.config.Containerd.PauseImage), + "--pod-max-pids=-1", + "--protect-kernel-defaults=true", + "--read-only-port=0", + "--resolv-conf=/run/systemd/resolve/resolv.conf", + "--streaming-connection-idle-timeout=4h", + "--tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256", + } + + // Add VPN node IP if VPN gateway is enabled and connected + if vpnIP := i.getVPNInterfaceIP(); vpnIP != "" { + kubeletFlags = append(kubeletFlags, fmt.Sprintf("--node-ip=%s", vpnIP)) + i.logger.Infof("Configuring kubelet to use VPN interface IP: %s", vpnIP) + } + + // Format flags with proper line continuation + flagsFormatted := make([]string, len(kubeletFlags)) + for i, flag := range kubeletFlags { + flagsFormatted[i] = fmt.Sprintf(" %s \\", flag) + } + // Remove trailing backslash from last flag + if len(flagsFormatted) > 0 { + lastFlag := flagsFormatted[len(flagsFormatted)-1] + flagsFormatted[len(flagsFormatted)-1] = strings.TrimSuffix(lastFlag, " \\") + } + kubeletDefaults := fmt.Sprintf(`KUBELET_NODE_LABELS="%s" KUBELET_CONFIG_FILE_FLAGS="" KUBELET_FLAGS="\ - --v=%d \ - --address=0.0.0.0 \ - --anonymous-auth=false \ - --authentication-token-webhook=true \ - --authorization-mode=Webhook \ - --cgroup-driver=systemd \ - --cgroups-per-qos=true \ - --enforce-node-allocatable=pods \ - --cluster-dns=%s \ - --cluster-domain=cluster.local \ - --event-qps=0 \ - --eviction-hard=%s \ - --kube-reserved=%s \ - --image-gc-high-threshold=%d \ - --image-gc-low-threshold=%d \ - --max-pods=%d \ - --node-status-update-frequency=10s \ - --pod-max-pids=-1 \ - --protect-kernel-defaults=true \ - --read-only-port=0 \ - --resolv-conf=/run/systemd/resolve/resolv.conf \ - --streaming-connection-idle-timeout=4h \ - --rotate-certificates=true \ - --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256 \ +%s \ "`, strings.Join(labels, ","), - i.config.Node.Kubelet.Verbosity, - i.config.Node.Kubelet.DNSServiceIP, - mapToEvictionThresholds(i.config.Node.Kubelet.EvictionHard, ","), - mapToKeyValuePairs(i.config.Node.Kubelet.KubeReserved, ","), - i.config.Node.Kubelet.ImageGCHighThreshold, - i.config.Node.Kubelet.ImageGCLowThreshold, - i.config.Node.MaxPods) + strings.Join(flagsFormatted, "\n")) // Ensure /etc/default directory exists if err := utils.RunSystemCommand("mkdir", "-p", etcDefaultDir); err != nil { @@ -709,3 +725,28 @@ func mapToEvictionThresholds(m map[string]string, separator string) string { } return strings.Join(pairs, separator) } + +// getVPNInterfaceIP returns the IP address of the VPN interface if VPN is enabled and connected +func (i *Installer) getVPNInterfaceIP() string { + // Check if VPN gateway is enabled in configuration + if !i.config.IsVPNGatewayEnabled() { + return "" + } + + // Get VPN interface using the generic utility function + vpnInterface, err := utils.GetVPNInterface() + if err != nil { + i.logger.Debugf("VPN interface not found: %v", err) + return "" + } + + // Get IP address of the VPN interface + ip, err := utils.GetVPNInterfaceIP(vpnInterface) + if err != nil { + i.logger.Debugf("Failed to get VPN interface IP: %v", err) + return "" + } + + i.logger.Infof("Found VPN interface %s with IP: %s", vpnInterface, ip) + return ip +} diff --git a/pkg/components/vpn_gateway/README.md b/pkg/components/vpn_gateway/README.md new file mode 100644 index 0000000..a4da174 --- /dev/null +++ b/pkg/components/vpn_gateway/README.md @@ -0,0 +1,29 @@ +# VPN Gateway Component + +This component provides VPN connectivity for AKS Flex Node using OpenVPN over Point-to-Site (P2S) connections. It's designed for scenarios where a limited number of clients need secure access to a virtual network. + +![VPN Gateway Architecture](../../../assets/img/README/image.png) + +## Overview + +The VPN Gateway component enables secure connectivity between AKS Flex Nodes and Azure Virtual Networks through: + +- **Certificate-based authentication** using self-generated root and client certificates +- **OpenVPN SSL tunnel** for encrypted communication +- **Automatic IP management** to update node IPs when VPN interface changes +- **Azure integration** for seamless VPN gateway configuration + +## Steps +1. Prepare Azure Resources +- Create a GatewaySubnet within the AKS VNet +- Deploy a Route-based Azure VPN Gateway into the GatewaySubnet + +2. Prepare Certificates +- root certificate: will be uploaded to Azure as a "trusted" cert (a Base64 encoded X.509 .cer file.) +- client certificates: generated from the root certificate and to be installed on each client computer for client authentication + +3. Configure VPN client profile + +## References + +[Configure server settings for P2S VPN Gateway certificate authentication](https://learn.microsoft.com/en-us/azure/vpn-gateway/point-to-site-certificate-gateway) \ No newline at end of file diff --git a/pkg/components/vpn_gateway/consts.go b/pkg/components/vpn_gateway/consts.go new file mode 100644 index 0000000..c1367e1 --- /dev/null +++ b/pkg/components/vpn_gateway/consts.go @@ -0,0 +1,96 @@ +package vpn_gateway + +import ( + "path/filepath" + "time" +) + +const ( + // VPN Gateway default name + defaultVPNGatewayName = "vpn-gateway" + + // Azure VPN Gateway configuration + vpnClientRootCertName = "VPNClientRootCert" + gatewaySubnetName = "GatewaySubnet" + gatewaySubnetPrefix = 27 // /27 subnet for GatewaySubnet + + // Directory paths + systemConfigDir = "/etc/aks-flex-node" + certificatesDir = "/etc/aks-flex-node/certs" + openVPNConfigDir = "/etc/openvpn" + + // File names + vpnConfigFileName = "vpn-config.ovpn" + vpnClientCertFileName = "vpn-client.crt" + vpnClientKeyFileName = "vpn-client.key" + vpnRootCertFileName = "vpn-root-ca.crt" + openVPNConfigFileName = "vpnconfig.conf" + + // File permissions + certificatesDirPerm = 0700 + configDirPerm = 0755 + privateKeyFilePerm = 0600 + certificateFilePerm = 0644 + + // Certificate configuration + certificateKeySize = 2048 + certificateValidYears = 10 + certificateCommonName = "VPN CA" + + // PEM block types + rsaPrivateKeyType = "RSA PRIVATE KEY" + certificateType = "CERTIFICATE" + + // Timeouts and intervals + gatewayProvisioningTimeout = 30 * time.Minute // VPN Gateway provisioning timeout + gatewayStatusCheckInterval = 30 * time.Second // Polling interval for gateway status + vpnConnectionTimeout = 1 * time.Minute // VPN connection establishment timeout + vpnConnectionCheckInterval = 2 * time.Second // Interval for VPN connection checks + + // System paths for validation + systemEtcPrefix = "/etc/" + systemUsrPrefix = "/usr/" + systemVarPrefix = "/var/" + + // Temporary file patterns + tempVPNConfigPattern = "vpnconfig-*.ovpn" + tempVPNCertPattern = "vpn-cert-*.tmp" + tempVPNZipPattern = "vpnconfig-*.zip" + tempVPNExtractPrefix = "vpnconfig-" + + // OpenVPN service template + openVPNServiceTemplate = "openvpn@vpnconfig" + openVPNServiceName = "vpnconfig" + + // Public IP naming pattern + gatewayPublicIPName = "vpn-gateway-ip" + vpnGatewayName = "vpn-gateway" + + // Point-to-Site configuration name + p2sConfigName = "P2SConfig" +) + +// GetVPNClientCertPath returns the full path to the VPN client certificate file +func GetVPNClientCertPath() string { + return filepath.Join(certificatesDir, vpnClientCertFileName) +} + +// GetVPNClientKeyPath returns the full path to the VPN client private key file +func GetVPNClientKeyPath() string { + return filepath.Join(certificatesDir, vpnClientKeyFileName) +} + +// GetVPNRootCertPath returns the full path to the VPN root CA certificate file +func GetVPNRootCertPath() string { + return filepath.Join(certificatesDir, vpnRootCertFileName) +} + +// GetOpenVPNConfigPath returns the full path to the OpenVPN configuration file +func GetOpenVPNConfigPath() string { + return filepath.Join(openVPNConfigDir, openVPNConfigFileName) +} + +// GetVPNConfigPath returns the full path to the VPN configuration file in system config directory +func GetVPNConfigPath() string { + return filepath.Join(systemConfigDir, vpnConfigFileName) +} diff --git a/pkg/components/vpn_gateway/vpn_gateway_installer.go b/pkg/components/vpn_gateway/vpn_gateway_installer.go new file mode 100644 index 0000000..b8eca47 --- /dev/null +++ b/pkg/components/vpn_gateway/vpn_gateway_installer.go @@ -0,0 +1,674 @@ +package vpn_gateway + +import ( + "context" + "errors" + "fmt" + "net" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" + "github.com/Azure/go-autorest/autorest/to" + "github.com/sirupsen/logrus" + + "go.goms.io/aks/AKSFlexNode/pkg/auth" + "go.goms.io/aks/AKSFlexNode/pkg/config" + "go.goms.io/aks/AKSFlexNode/pkg/utils" +) + +// Installer handles VPN Gateway installation operations +type Installer struct { + config *config.Config + logger *logrus.Logger + vnetClient *armnetwork.VirtualNetworksClient + subnetsClient *armnetwork.SubnetsClient + vgwClient *armnetwork.VirtualNetworkGatewaysClient + publicIPClient *armnetwork.PublicIPAddressesClient +} + +// NewInstaller creates a new VPN Gateway installer +func NewInstaller(logger *logrus.Logger) *Installer { + return &Installer{ + config: config.GetConfig(), + logger: logger, + } +} + +// Validate validates prerequisites for VPN Gateway installation +func (i *Installer) Validate(ctx context.Context) error { + if !i.config.IsVPNGatewayEnabled() { + i.logger.Info("VPN Gateway setup is not enabled in configuration, skipping Validate...") + return nil + } + + if i.config.Azure.VPNGateway.P2SGatewayCIDR == "" { + return fmt.Errorf("P2S Gateway CIDR is not configured") + } + + if i.config.Azure.VPNGateway.PodCIDR == "" { + return fmt.Errorf("pod CIDR is not configured - this is required for VPN network routing") + } + + if i.config.Azure.VPNGateway.VNetID == "" { + return fmt.Errorf("VNet ID for VPN Gateway is not configured") + } + + // Validate that VNet ID is a proper Azure resource ID + if err := utils.ValidateAzureResourceID(i.config.Azure.VPNGateway.VNetID, "virtualNetworks"); err != nil { + return fmt.Errorf("invalid VNet ID: %w", err) + } + + return nil +} + +// GetName returns the step name +func (i *Installer) GetName() string { + return "VPNGatewayInstaller" +} + +type vnetResourceInfo struct { + vnetID string + location string + resourceGroupName string + subscriptionID string + vnet *armnetwork.VirtualNetwork +} + +// Execute performs VPN Gateway setup as part of the bootstrap process +// This method handles the whole VPN Gateway creation and configuration flow: +// 1. VPN Gateway provisioning +// 2. Certificate generation and upload +// 3. VPN client configuration download +// 4. VPN connection establishment +func (i *Installer) Execute(ctx context.Context) error { + i.logger.Info("Starting VPN Gateway setup for bootstrap process") + + // Set up Azure clients + if err := i.setUpClients(ctx); err != nil { + i.logger.Errorf("Failed to set up Azure clients: %v", err) + return fmt.Errorf("vpn gateway setup failed at client setup: %w", err) + } + + // Discover the VNet used by AKS cluster nodes - it can be either BYO VNet or AKS managed VNet + // The VPN Gateway will be created in this VNet to establish connectivity between the flex node and AKS cluster nodes + vnetInfo, err := i.getNodeVNet(ctx) + if err != nil { + i.logger.Errorf("Failed to get AKS managed VNet: %v", err) + return fmt.Errorf("vpn gateway setup failed at VNet discovery: %w", err) + } + + // Provision VPN Gateway in the AKS Node VNet + _, err = i.provisionGateway(ctx, vnetInfo) + if err != nil { + i.logger.Errorf("Failed to provision VPN Gateway: %v", err) + return fmt.Errorf("vpn gateway setup failed at gateway provisioning: %w", err) + } + + // Check if VPN connection is already working before setting up certificates + if i.isVPNConnected() { + i.logger.Info("VPN connection is already established, skipping certificate setup and connection establishment") + } else { + // Setup VPN Gateway certificates (root and client) + i.logger.Info("Setting up VPN certificates") + if err := i.setupCertificates(ctx, vnetInfo); err != nil { + i.logger.Errorf("Failed to setup certificates: %v", err) + return fmt.Errorf("vpn gateway setup failed at certificate setup: %w", err) + } + i.logger.Info("VPN certificates setup completed") + + // Download VPN configuration + i.logger.Info("Downloading VPN client configuration") + configPath, err := i.downloadVPNConfig(ctx, vnetInfo) + if err != nil { + i.logger.Errorf("Failed to download VPN configuration: %v", err) + return fmt.Errorf("vpn gateway setup failed at config download: %w", err) + } + i.logger.Infof("VPN configuration downloaded to: %s", configPath) + + // Establish VPN connection using the downloaded configuration + i.logger.Info("Establishing VPN connection") + connected, err := i.establishVPNConnection(ctx, configPath) + if err != nil { + i.logger.Errorf("Failed to establish VPN connection: %v", err) + return fmt.Errorf("vpn gateway setup failed at connection establishment: %w", err) + } + if !connected { + return fmt.Errorf("vpn gateway setup failed: VPN connection could not be established") + } + i.logger.Info("VPN connection established successfully") + } + + // Always configure network routes and iptables rules + i.logger.Info("Configuring VPN network routing") + if err := i.configureVPNNetworking(ctx, vnetInfo); err != nil { + i.logger.Errorf("Failed to configure VPN networking: %v", err) + return fmt.Errorf("vpn gateway setup failed at network configuration: %w", err) + } + i.logger.Info("VPN networking configuration completed") + + i.logger.Info("VPN Gateway setup completed successfully") + return nil +} + +// setUpAKSClients sets up Azure Container Service clients using the target cluster subscription ID +func (i *Installer) setUpClients(ctx context.Context) error { + cred, err := auth.NewAuthProvider().UserCredential(config.GetConfig()) + if err != nil { + return fmt.Errorf("failed to get authentication credential: %w", err) + } + + vnetID := i.config.GetVPNGatewayVNetID() + if vnetID == "" { + return fmt.Errorf("failed to get VNet ID from configuration") + } + vnetSub := utils.GetSubscriptionIDFromResourceID(vnetID) + + clientFactory, err := armnetwork.NewClientFactory(vnetSub, cred, nil) + if err != nil { + return fmt.Errorf("failed to create Azure Network client factory: %w", err) + } + + i.vnetClient = clientFactory.NewVirtualNetworksClient() + i.subnetsClient = clientFactory.NewSubnetsClient() + i.vgwClient = clientFactory.NewVirtualNetworkGatewaysClient() + i.publicIPClient = clientFactory.NewPublicIPAddressesClient() + return nil +} + +// IsCompleted checks if VPN Gateway setup has been completed +func (i *Installer) IsCompleted(ctx context.Context) bool { + if !i.config.IsVPNGatewayEnabled() { + i.logger.Info("VPN Gateway setup is disabled in configuration, skipping installation...") + return true + } + + i.logger.Debug("Checking VPN Gateway setup completion status") + + // Check if VPN is connected + if !i.isVPNConnected() { + i.logger.Debug("VPN is not connected") + return false + } + + // // Check if network configuration is applied (automatically discovered) + // if !i.isNetworkConfigured(ctx) { + // i.logger.Debug("VPN network configuration not applied") + // return false + // } + + // // Check if VPN Gateway exists in Azure + // if gateway, err := i.getVPNGateway(ctx); err != nil || gateway == nil { + // i.logger.Debugf("VPN Gateway not found or not accessible: %v", err) + // return false + // } + + // i.logger.Debug("VPN Gateway setup appears to be completed") + return false +} + +// provisionGateway handles VPN Gateway provisioning with idempotency +func (i *Installer) provisionGateway(ctx context.Context, vnetInfo vnetResourceInfo) (*armnetwork.VirtualNetworkGateway, error) { + // Check if VPN Gateway already exists + if gateway, err := i.getVPNGateway(ctx, vnetInfo); err == nil && gateway != nil { + i.logger.Infof("VPN Gateway already exists: %s", to.String(gateway.Name)) + return gateway, nil + } + + i.logger.Infof("Provisioning VPN Gateway in VNet: %s", vnetInfo.vnetID) + + // Ensure GatewaySubnet exists + if err := i.ensureGatewaySubnet(ctx, vnetInfo); err != nil { + return nil, fmt.Errorf("failed to ensure gateway subnet: %w", err) + } + + // Create Public IP for VPN Gateway + publicIP, err := i.createPublicIPForVPNGateway(ctx, vnetInfo) + if err != nil { + return nil, fmt.Errorf("failed to create public IP: %w", err) + } + + // Create VPN Gateway in the GatewaySubnet + gateway, err := i.createVPNGateway(ctx, vnetInfo, publicIP) + if err != nil { + return nil, fmt.Errorf("failed to create VPN Gateway: %w", err) + } + + i.logger.Infof("Successfully provisioned VPN Gateway: %s", to.String(gateway.Name)) + return gateway, nil +} + +// createPublicIPForVPNGateway creates a public IP for the VPN Gateway +func (i *Installer) createPublicIPForVPNGateway(ctx context.Context, vnetInfo vnetResourceInfo) (string, error) { + i.logger.Infof("Ensuring public IP exists: %s", gatewayPublicIPName) + + // Prepare Public IP parameters + allocationMethod := armnetwork.IPAllocationMethodStatic + skuName := armnetwork.PublicIPAddressSKUNameStandard + skuTier := armnetwork.PublicIPAddressSKUTierRegional + + publicIPParams := armnetwork.PublicIPAddress{ + Location: &vnetInfo.location, + SKU: &armnetwork.PublicIPAddressSKU{ + Name: &skuName, + Tier: &skuTier, + }, + Properties: &armnetwork.PublicIPAddressPropertiesFormat{ + PublicIPAllocationMethod: &allocationMethod, + }, + Zones: []*string{ + &[]string{"1"}[0], + }, + } + + // Create Public IP - this is a long-running operation + poller, err := i.publicIPClient.BeginCreateOrUpdate(ctx, vnetInfo.resourceGroupName, gatewayPublicIPName, publicIPParams, nil) + if err != nil { + return "", fmt.Errorf("failed to start public IP creation: %w", err) + } + + i.logger.Info("Public IP creation initiated. Waiting for completion...") + + // Wait for completion + result, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return "", fmt.Errorf("failed to create public IP: %w", err) + } + + i.logger.Infof("Successfully created public IP: %s", to.String(result.ID)) + return to.String(result.ID), nil +} + +// setupCertificates handles certificate generation and upload +func (i *Installer) setupCertificates(ctx context.Context, vnetInfo vnetResourceInfo) error { + i.logger.Info("Setting up VPN root certificates...") + certData, err := i.generateCertificates() + if err != nil { + return fmt.Errorf("failed to generate VPN certificates: %w", err) + } + + i.logger.Info("Uploading VPN root certificate to Azure VPN Gateway...") + if err := i.uploadCertificateToAzure(ctx, certData, vnetInfo); err != nil { + i.logger.Warnf("Certificate upload failed: %v", err) + return fmt.Errorf("failed to upload certificate to Azure: %w", err) + } + i.logger.Info("Certificate uploaded to Azure VPN Gateway successfully") + + return nil +} + +// downloadVPNConfig downloads and saves the VPN configuration +func (i *Installer) downloadVPNConfig(ctx context.Context, vnetInfo vnetResourceInfo) (string, error) { + i.logger.Info("Downloading VPN client configuration...") + configData, err := i.downloadVPNClientConfig(ctx, defaultVPNGatewayName, vnetInfo.resourceGroupName) + if err != nil { + return "", fmt.Errorf("failed to download VPN client configuration: %w", err) + } + + // Save configuration to file + configPath, err := i.saveVPNConfig(configData) + if err != nil { + return "", fmt.Errorf("failed to save VPN config: %w", err) + } + + return configPath, nil +} + +// establishVPNConnection establishes the VPN connection +func (i *Installer) establishVPNConnection(ctx context.Context, configPath string) (bool, error) { + i.logger.Info("Setting up OpenVPN with downloaded configuration...") + if err := i.setupOpenVPN(configPath); err != nil { + return false, fmt.Errorf("failed to setup OpenVPN: %w", err) + } + + i.logger.Info("Waiting for VPN connection to establish...") + if err := i.waitForVPNConnection(vpnConnectionTimeout); err != nil { + return false, fmt.Errorf("VPN connection failed to establish: %w", err) + } + + i.logger.Info("VPN connection established successfully") + return true, nil +} + +// waitForVPNConnection waits for VPN connection to be established +func (i *Installer) waitForVPNConnection(timeout time.Duration) error { + i.logger.Infof("Waiting up to %v for VPN connection...", timeout) + + start := time.Now() + for time.Since(start) < timeout { + if i.isVPNConnected() { + i.logger.Info("VPN connection established successfully") + return nil + } + + i.logger.Debug("VPN not connected yet, waiting...") + time.Sleep(vpnConnectionCheckInterval) + } + + return fmt.Errorf("VPN connection timeout after %v", timeout) +} + +// saveVPNConfig saves VPN configuration to the appropriate directory +func (i *Installer) saveVPNConfig(configData string) (string, error) { + configPath := GetVPNConfigPath() + + // Save VPN config to the persistent location atomically + if err := utils.WriteFileAtomicSystem(configPath, []byte(configData), certificateFilePerm); err != nil { + return "", fmt.Errorf("failed to save VPN config file: %w", err) + } + + i.logger.Infof("VPN configuration saved to: %s", configPath) + return configPath, nil +} + +// calculateGatewaySubnetCIDR calculates an appropriate GatewaySubnet CIDR +func (i *Installer) calculateGatewaySubnetCIDR(ctx context.Context, vnetInfo vnetResourceInfo) (string, error) { + i.logger.Infof("Calculating GatewaySubnet CIDR for VNet: %s", vnetInfo.vnetID) + + // proactive checks, should not happen + if vnetInfo.vnet.Properties == nil || + vnetInfo.vnet.Properties.AddressSpace == nil || + len(vnetInfo.vnet.Properties.AddressSpace.AddressPrefixes) == 0 { + return "", fmt.Errorf("VNet has no address prefixes") + } + + // Try each address prefix until we find one with available space + var lastErr error + for idx, prefix := range vnetInfo.vnet.Properties.AddressSpace.AddressPrefixes { + if prefix == nil { + continue + } + + vnetCIDR := *prefix + i.logger.Infof("Trying VNet address prefix %d: %s", idx+1, vnetCIDR) + + // Calculate an available /27 subnet for GatewaySubnet in this address prefix + gatewaySubnetCIDR, err := i.calculateAvailableSubnetInRange(vnetCIDR, vnetInfo.vnet.Properties.Subnets, gatewaySubnetPrefix) + if err != nil { + i.logger.Warnf("No available space in address prefix %s: %v", vnetCIDR, err) + lastErr = err + continue + } + + i.logger.Infof("Successfully calculated GatewaySubnet CIDR: %s in address prefix: %s", gatewaySubnetCIDR, vnetCIDR) + return gatewaySubnetCIDR, nil + } + + // If we get here, no address prefix had available space + return "", fmt.Errorf("no available space for GatewaySubnet in any VNet address prefix. Last error: %w", lastErr) +} + +// calculateAvailableSubnetInRange finds an available subnet within the VNet address space +func (i *Installer) calculateAvailableSubnetInRange(vnetCIDR string, existingSubnets []*armnetwork.Subnet, prefixLength int) (string, error) { + // Parse VNet CIDR + _, vnetNet, err := net.ParseCIDR(vnetCIDR) + if err != nil { + return "", fmt.Errorf("failed to parse VNet CIDR %s: %w", vnetCIDR, err) + } + + // Convert existing subnets to IPNet for overlap checking + var existingNets []*net.IPNet + for _, subnet := range existingSubnets { + if subnet.Properties.AddressPrefix != nil && *subnet.Properties.AddressPrefix != "" { + _, subnetNet, err := net.ParseCIDR(*subnet.Properties.AddressPrefix) + if err != nil { + i.logger.Warnf("Failed to parse existing subnet CIDR %s: %v", *subnet.Properties.AddressPrefix, err) + continue + } + existingNets = append(existingNets, subnetNet) + } + } + + // Calculate subnet size + subnetSize := uint32(1 << (32 - prefixLength)) + + // Try to find an available subnet range + vnetIP := vnetNet.IP.To4() + if vnetIP == nil { + return "", fmt.Errorf("only IPv4 networks are supported") + } + + // Convert IP to uint32 for easier calculation + vnetStart := uint32(vnetIP[0])<<24 | uint32(vnetIP[1])<<16 | uint32(vnetIP[2])<<8 | uint32(vnetIP[3]) + prefixLen := i.getNetworkPrefixLength(vnetNet) + if prefixLen < 0 || prefixLen > 32 { + return "", fmt.Errorf("invalid network prefix length: %d", prefixLen) + } + vnetPrefixLength := uint32(prefixLen) + vnetMask := uint32(0xFFFFFFFF) << (32 - vnetPrefixLength) + vnetEnd := vnetStart | (^vnetMask) + + // Start from a high address in the VNet range to avoid conflicts with existing subnets + startAddress := vnetEnd - subnetSize + 1 + startAddress = startAddress &^ (subnetSize - 1) // Align to subnet boundary + + for currentAddr := startAddress; currentAddr >= vnetStart; currentAddr -= subnetSize { + // Create candidate subnet + candidateIP := net.IPv4( + byte(currentAddr>>24), + byte(currentAddr>>16), + byte(currentAddr>>8), + byte(currentAddr), + ) + + candidateNet := &net.IPNet{ + IP: candidateIP, + Mask: net.CIDRMask(prefixLength, 32), + } + + // Check if this subnet overlaps with any existing subnet + overlaps := false + for _, existing := range existingNets { + if i.subnetsOverlap(candidateNet, existing) { + overlaps = true + break + } + } + + if !overlaps { + return candidateNet.String(), nil + } + } + + return "", fmt.Errorf("no available /%d subnet found in VNet %s", prefixLength, vnetCIDR) +} + +// getNetworkPrefixLength returns the prefix length of a network +func (i *Installer) getNetworkPrefixLength(network *net.IPNet) int { + ones, _ := network.Mask.Size() + return ones +} + +// subnetsOverlap checks if two subnets overlap +func (i *Installer) subnetsOverlap(subnet1, subnet2 *net.IPNet) bool { + return subnet1.Contains(subnet2.IP) || subnet2.Contains(subnet1.IP) || + subnet1.Contains(i.getLastIP(subnet2)) || subnet2.Contains(i.getLastIP(subnet1)) +} + +// getLastIP returns the last IP address in a subnet +func (i *Installer) getLastIP(network *net.IPNet) net.IP { + ip := network.IP.To4() + if ip == nil { + return nil + } + + // Convert to uint32 + ipInt := uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) + + // Get network mask + ones, bits := network.Mask.Size() + mask := uint32(0xFFFFFFFF) << (bits - ones) + + // Calculate last IP + lastIPInt := ipInt | (^mask) + + return net.IPv4( + byte(lastIPInt>>24), + byte(lastIPInt>>16), + byte(lastIPInt>>8), + byte(lastIPInt), + ) +} + +// AKS nodes can be in either BYO VNet or AKS managed VNet +func (i *Installer) getNodeVNet(ctx context.Context) (vnetResourceInfo, error) { + // First try to discover BYO VNet from agent pools + vnetID := i.config.GetVPNGatewayVNetID() + // Get VNet details + vnetResp, err := i.vnetClient.Get(ctx, + utils.GetResourceGroupFromResourceID(vnetID), + utils.GetResourceNameFromResourceID(vnetID), nil) + if err != nil { + return vnetResourceInfo{}, fmt.Errorf("failed to get VNet details for VNet ID %s: %w", vnetID, err) + } + + vnet := &vnetResp.VirtualNetwork + vnetInfo := vnetResourceInfo{ + vnetID: to.String(vnet.ID), + location: to.String(vnet.Location), + resourceGroupName: utils.GetResourceGroupFromResourceID(to.String(vnet.ID)), + subscriptionID: utils.GetSubscriptionIDFromResourceID(to.String(vnet.ID)), + vnet: vnet, + } + + return vnetInfo, nil +} + +// getVPNGateway finds a VPN Gateway by name using Azure SDK +func (i *Installer) getVPNGateway(ctx context.Context, vnetInfo vnetResourceInfo) (*armnetwork.VirtualNetworkGateway, error) { + // Get the specific VPN Gateway by name + resp, err := i.vgwClient.Get(ctx, vnetInfo.resourceGroupName, defaultVPNGatewayName, nil) + if err != nil { + if strings.Contains(err.Error(), "NotFound") { + i.logger.Infof("VPN Gateway '%s' not found in resource group '%s'", defaultVPNGatewayName, vnetInfo.resourceGroupName) + return nil, errors.New("NotFound") // VPN Gateway not found + } + return nil, fmt.Errorf("failed to get VPN Gateway '%s' in resource group '%s': %w", defaultVPNGatewayName, vnetInfo.resourceGroupName, err) + } + + // Verify it's a VPN Gateway (GatewayType == "Vpn") + if resp.Properties != nil && + resp.Properties.GatewayType != nil && + *resp.Properties.GatewayType == armnetwork.VirtualNetworkGatewayTypeVPN { + + i.logger.Infof("Found VPN Gateway '%s' with GatewayType 'Vpn' in resource group '%s'", defaultVPNGatewayName, vnetInfo.resourceGroupName) + return &resp.VirtualNetworkGateway, nil + } + + i.logger.Infof("Gateway '%s' found but is not a VPN Gateway (GatewayType: %v)", defaultVPNGatewayName, resp.Properties.GatewayType) + return nil, errors.New("NotFound") // Gateway exists but is not a VPN Gateway +} + +// createVPNGateway creates a VPN Gateway +func (i *Installer) createVPNGateway(ctx context.Context, vnetInfo vnetResourceInfo, publicIPID string) (*armnetwork.VirtualNetworkGateway, error) { + i.logger.Infof("Creating VPN Gateway: %s in resource group: %s", vpnGatewayName, vnetInfo.resourceGroupName) + + // Construct gateway subnet ID + gatewaySubnetID := fmt.Sprintf("%s/subnets/%s", vnetInfo.vnetID, gatewaySubnetName) + + // Prepare VPN Gateway configuration + vpnGwSKU := armnetwork.VirtualNetworkGatewaySKUNameVPNGw2AZ + vpnGwTier := armnetwork.VirtualNetworkGatewaySKUTierVPNGw2AZ + gatewayType := armnetwork.VirtualNetworkGatewayTypeVPN + vpnType := armnetwork.VPNTypeRouteBased + enableBgp := false + activeActive := false + + // IP Configuration name + ipConfigName := p2sConfigName + + // VPN Client Configuration + p2sGatewayCIDR := i.config.Azure.VPNGateway.P2SGatewayCIDR + vpnClientProtocol := armnetwork.VPNClientProtocolOpenVPN + + gatewayParams := armnetwork.VirtualNetworkGateway{ + Location: &vnetInfo.location, + Properties: &armnetwork.VirtualNetworkGatewayPropertiesFormat{ + SKU: &armnetwork.VirtualNetworkGatewaySKU{ + Name: &vpnGwSKU, + Tier: &vpnGwTier, + }, + GatewayType: &gatewayType, + VPNType: &vpnType, + EnableBgp: &enableBgp, + Active: &activeActive, + IPConfigurations: []*armnetwork.VirtualNetworkGatewayIPConfiguration{ + { + Name: &ipConfigName, + Properties: &armnetwork.VirtualNetworkGatewayIPConfigurationPropertiesFormat{ + PublicIPAddress: &armnetwork.SubResource{ + ID: &publicIPID, + }, + Subnet: &armnetwork.SubResource{ + ID: &gatewaySubnetID, + }, + }, + }, + }, + VPNClientConfiguration: &armnetwork.VPNClientConfiguration{ + VPNClientAddressPool: &armnetwork.AddressSpace{ + AddressPrefixes: []*string{&p2sGatewayCIDR}, + }, + VPNClientProtocols: []*armnetwork.VPNClientProtocol{&vpnClientProtocol}, + }, + }, + } + + // Create VPN Gateway - this is a long-running operation + poller, err := i.vgwClient.BeginCreateOrUpdate(ctx, vnetInfo.resourceGroupName, vpnGatewayName, gatewayParams, nil) + if err != nil { + return nil, fmt.Errorf("failed to start VPN Gateway creation: %w", err) + } + + i.logger.Info("VPN Gateway creation initiated. Waiting for completion (this may take 20-30 minutes)...") + + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, fmt.Errorf("failed to create VPN Gateway: %w", err) + } + + i.logger.Infof("VPN Gateway creation completed: %s", *resp.Name) + return &resp.VirtualNetworkGateway, nil +} + +// ensureGatewaySubnet creates GatewaySubnet if it doesn't exist +func (i *Installer) ensureGatewaySubnet(ctx context.Context, vnetInfo vnetResourceInfo) error { + // Check if GatewaySubnet already exists + for _, subnet := range vnetInfo.vnet.Properties.Subnets { + if strings.EqualFold(to.String(subnet.Name), gatewaySubnetName) { + i.logger.Infof("GatewaySubnet already exists in VNet %s", vnetInfo.vnetID) + return nil + } + } + + // Calculate a CIDR for GatewaySubnet to ensure no + gatewaySubnetCIDR, err := i.calculateGatewaySubnetCIDR(ctx, vnetInfo) + if err != nil { + return fmt.Errorf("failed to calculate gateway subnet CIDR: %w", err) + } + + i.logger.Infof("Creating GatewaySubnet with CIDR: %s", gatewaySubnetCIDR) + + gatewaySubnetParams := armnetwork.Subnet{ + Properties: &armnetwork.SubnetPropertiesFormat{ + AddressPrefix: &gatewaySubnetCIDR, + }, + } + + // Create the subnet - this is a long-running operation + poller, err := i.subnetsClient.BeginCreateOrUpdate(ctx, vnetInfo.resourceGroupName, to.String(vnetInfo.vnet.Name), gatewaySubnetName, gatewaySubnetParams, nil) + if err != nil { + return fmt.Errorf("failed to start GatewaySubnet creation: %w", err) + } + + i.logger.Info("GatewaySubnet creation initiated. Waiting for completion...") + + // Wait for completion + result, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("failed to create GatewaySubnet: %w", err) + } + + i.logger.Infof("Successfully created GatewaySubnet: %s", *result.Name) + return nil +} diff --git a/pkg/components/vpn_gateway/vpn_gateway_uninstaller.go b/pkg/components/vpn_gateway/vpn_gateway_uninstaller.go new file mode 100644 index 0000000..6dd5dfe --- /dev/null +++ b/pkg/components/vpn_gateway/vpn_gateway_uninstaller.go @@ -0,0 +1,409 @@ +package vpn_gateway + +import ( + "context" + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" + "github.com/sirupsen/logrus" + + "go.goms.io/aks/AKSFlexNode/pkg/auth" + "go.goms.io/aks/AKSFlexNode/pkg/config" + "go.goms.io/aks/AKSFlexNode/pkg/utils" +) + +// UnInstaller handles VPN Gateway cleanup operations +type UnInstaller struct { + config *config.Config + logger *logrus.Logger + vnetClient *armnetwork.VirtualNetworksClient + subnetsClient *armnetwork.SubnetsClient + vgwClient *armnetwork.VirtualNetworkGatewaysClient + publicIPClient *armnetwork.PublicIPAddressesClient +} + +// NewUnInstaller creates a new VPN Gateway uninstaller +func NewUnInstaller(logger *logrus.Logger) *UnInstaller { + cfg := config.GetConfig() + return &UnInstaller{ + config: cfg, + logger: logger, + } +} + +// GetName returns the cleanup step name +func (u *UnInstaller) GetName() string { + return "VPNGatewayCleanup" +} + +// Execute performs VPN Gateway cleanup as part of the unbootstrap process +// This method is resilient to failures and continues cleanup even if some operations fail +func (u *UnInstaller) Execute(ctx context.Context) error { + u.logger.Info("Starting VPN Gateway cleanup for unbootstrap process") + + // Set up Azure clients + if err := u.setUpClients(ctx); err != nil { + u.logger.Errorf("Failed to set up Azure clients: %v", err) + return fmt.Errorf("vpn gateway setup failed at client setup: %w", err) + } + + // Step 1: Disconnect VPN connection + u.logger.Info("Step 1: Disconnecting VPN connection") + if err := u.disconnectVPN(); err != nil { + u.logger.Warnf("Failed to disconnect VPN (continuing cleanup): %v", err) + } else { + u.logger.Info("Successfully disconnected VPN connection") + } + + // Step 2: Clean up VPN networking (routes and iptables rules) + u.logger.Info("Step 2: Cleaning up VPN networking configuration") + if err := u.cleanupVPNNetworking(); err != nil { + u.logger.Warnf("Failed to cleanup VPN networking (continuing cleanup): %v", err) + } else { + u.logger.Info("Successfully cleaned up VPN networking configuration") + } + + // Step 3: Clean up VPN configuration files and certificates + u.logger.Info("Step 3: Cleaning up VPN configuration files and certificates") + if err := u.cleanupVPNFiles(); err != nil { + u.logger.Warnf("Failed to cleanup VPN files (continuing cleanup): %v", err) + } else { + u.logger.Info("Successfully cleaned up VPN configuration files and certificates") + } + + // Note: We don't delete the VPN Gateway from Azure as it's expensive to recreate + // and might be shared with other resources. The VPN Gateway will be left in Azure. + u.logger.Info("VPN Gateway resources in Azure are preserved for potential reuse") + + if err := u.cleanupAzureResources(ctx); err != nil { + u.logger.Warnf("Failed to cleanup Azure VPN Gateway resources: %v", err) + } else { + u.logger.Info("Successfully cleaned up Azure VPN Gateway resources") + } + + u.logger.Info("VPN Gateway cleanup for unbootstrap completed") + return nil +} + +// setUpAKSClients sets up Azure Container Service clients using the target cluster subscription ID +func (u *UnInstaller) setUpClients(ctx context.Context) error { + cred, err := auth.NewAuthProvider().UserCredential(config.GetConfig()) + if err != nil { + return fmt.Errorf("failed to get authentication credential: %w", err) + } + + vnetID := u.config.GetVPNGatewayVNetID() + if vnetID == "" { + return fmt.Errorf("failed to get VNet ID from configuration") + } + vnetSub := utils.GetSubscriptionIDFromResourceID(vnetID) + + clientFactory, err := armnetwork.NewClientFactory(vnetSub, cred, nil) + if err != nil { + return fmt.Errorf("failed to create Azure Network client factory: %w", err) + } + + u.vnetClient = clientFactory.NewVirtualNetworksClient() + u.subnetsClient = clientFactory.NewSubnetsClient() + u.vgwClient = clientFactory.NewVirtualNetworkGatewaysClient() + u.publicIPClient = clientFactory.NewPublicIPAddressesClient() + return nil +} + +// IsCompleted checks if VPN Gateway cleanup has been completed +func (u *UnInstaller) IsCompleted(ctx context.Context) bool { + if !u.config.IsVPNGatewayEnabled() { + u.logger.Info("VPN Gateway is not enabled in configuration; skipping cleanup") + return true + } + return false +} + +// disconnectVPN stops the VPN connection and OpenVPN service +func (u *UnInstaller) disconnectVPN() error { + u.logger.Info("Disconnecting VPN connection") + + // Stop OpenVPN service + if err := utils.StopService(openVPNServiceTemplate); err != nil { + u.logger.Warnf("Failed to stop OpenVPN service: %v", err) + // Continue with other cleanup steps + } + + // Kill any remaining OpenVPN processes + if err := utils.RunSystemCommand("pkill", "-f", "openvpn"); err != nil { + u.logger.Warnf("Failed to kill OpenVPN processes: %v", err) + // Continue with other cleanup steps + } + + u.logger.Info("VPN disconnection completed") + return nil +} + +// cleanupVPNFiles removes VPN configuration files and certificates +func (u *UnInstaller) cleanupVPNFiles() error { + u.logger.Info("Cleaning up VPN configuration files and certificates") + + // Use the utility function for file removal + filesToCleanup := []string{ + GetVPNConfigPath(), + GetVPNClientCertPath(), + GetVPNClientKeyPath(), + GetVPNRootCertPath(), + } + + if errors := utils.RemoveFiles(filesToCleanup, u.logger); len(errors) > 0 { + for _, err := range errors { + u.logger.Warnf("File removal error: %v", err) + } + } + + // Try to remove the certificates directory using the utility function + if errors := utils.RemoveDirectories([]string{certificatesDir}, u.logger); len(errors) > 0 { + for _, err := range errors { + u.logger.Debugf("Directory removal error: %v", err) + } + } + + u.logger.Info("VPN files and certificates cleanup completed") + return nil +} + +// cleanupVPNNetworking removes IP routes and iptables rules configured for VPN +func (u *UnInstaller) cleanupVPNNetworking() error { + u.logger.Info("Cleaning up VPN networking configuration (routes and iptables rules)") + + // Get VPN interface to clean up routes + vpnInterface, err := utils.GetVPNInterface() + if err != nil { + u.logger.Infof("No VPN interface found, skipping route cleanup: %v", err) + // Continue with iptables cleanup even if no VPN interface + } else { + u.logger.Infof("Found VPN interface: %s, cleaning up routes", vpnInterface) + + // Get all routes via the VPN interface and remove them + if err := u.cleanupVPNRoutes(vpnInterface); err != nil { + u.logger.Warnf("Failed to clean up VPN routes: %v", err) + } + } + + // Clean up iptables MASQUERADE rules + if err := u.cleanupIPTablesRules(); err != nil { + u.logger.Warnf("Failed to clean up iptables rules: %v", err) + } + + u.logger.Info("VPN networking cleanup completed") + return nil +} + +// cleanupVPNRoutes removes all routes that go through the VPN interface +func (u *UnInstaller) cleanupVPNRoutes(vpnInterface string) error { + u.logger.Infof("Cleaning up routes via interface: %s", vpnInterface) + + // Get current routing table + output, err := utils.RunCommandWithOutput("ip", "route", "show") + if err != nil { + return fmt.Errorf("failed to get current routes: %w", err) + } + + // Parse routes and find ones using our VPN interface + routes := strings.Split(output, "\n") + routesRemoved := 0 + + for _, route := range routes { + route = strings.TrimSpace(route) + if route == "" { + continue + } + + // Check if this route uses our VPN interface + if strings.Contains(route, "dev "+vpnInterface) { + // Extract the destination from the route (first part before whitespace) + parts := strings.Fields(route) + if len(parts) > 0 { + dest := parts[0] + u.logger.Infof("Removing route: %s", dest) + + // Remove the route + if err := utils.RunSystemCommand("ip", "route", "del", dest); err != nil { + u.logger.Warnf("Failed to remove route %s: %v", dest, err) + // Continue with other routes + } else { + routesRemoved++ + u.logger.Infof("Removed route: %s", dest) + } + } + } + } + + u.logger.Infof("Removed %d VPN routes", routesRemoved) + return nil +} + +// cleanupIPTablesRules removes iptables MASQUERADE rules added for VPN +func (u *UnInstaller) cleanupIPTablesRules() error { + u.logger.Info("Cleaning up iptables MASQUERADE rules") + + // Get current NAT table rules + output, err := utils.RunCommandWithOutput("iptables", "-t", "nat", "-L", "POSTROUTING", "-n", "--line-numbers") + if err != nil { + return fmt.Errorf("failed to list iptables rules: %w", err) + } + + // Parse output to find MASQUERADE rules + lines := strings.Split(output, "\n") + rulesRemoved := 0 + + // Process lines in reverse order to maintain line numbers when deleting + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line == "" || strings.HasPrefix(line, "Chain") || strings.HasPrefix(line, "num") { + continue + } + + // Look for MASQUERADE rules (likely involving our VPN interface or VNet CIDRs) + if strings.Contains(line, "MASQUERADE") { + // Extract the line number (first field) + parts := strings.Fields(line) + if len(parts) > 0 { + lineNum := parts[0] + + // Check if this rule involves VPN (look for tun interface references) + if strings.Contains(line, "tun") { + u.logger.Infof("Removing iptables MASQUERADE rule: %s", line) + + // Remove the rule by line number + if err := utils.RunSystemCommand("iptables", "-t", "nat", "-D", "POSTROUTING", lineNum); err != nil { + u.logger.Warnf("Failed to remove iptables rule %s: %v", lineNum, err) + // Continue with other rules + } else { + rulesRemoved++ + u.logger.Infof("Removed iptables rule: %s", line) + } + } + } + } + } + + u.logger.Infof("Removed %d iptables MASQUERADE rules", rulesRemoved) + return nil +} + +// cleanupAzureResources removes VPN Gateway resources from Azure +// This includes: VPN Gateway, Public IP, and GatewaySubnet +func (u *UnInstaller) cleanupAzureResources(ctx context.Context) error { + u.logger.Info("Cleaning up VPN Gateway resources from Azure") + + vnetID := u.config.GetVPNGatewayVNetID() + resourceGroupName := utils.GetResourceGroupFromResourceID(vnetID) + vnetName := utils.GetResourceNameFromResourceID(vnetID) + + // Step 1: Delete VPN Gateway (this must be done first as it depends on other resources) + u.logger.Infof("Deleting VPN Gateway: %s (this may take 10-20 minutes)", defaultVPNGatewayName) + if err := u.deleteVPNGateway(ctx, resourceGroupName); err != nil { + u.logger.Warnf("Failed to delete VPN Gateway: %v", err) + // Continue with other cleanup even if VPN Gateway deletion fails + } else { + u.logger.Info("VPN Gateway successfully deleted from Azure") + } + + // Step 2: Delete Public IP + u.logger.Infof("Deleting Public IP: %s", gatewayPublicIPName) + if err := u.deletePublicIP(ctx, resourceGroupName); err != nil { + u.logger.Warnf("Failed to delete Public IP: %v", err) + // Continue with other cleanup + } else { + u.logger.Info("Public IP successfully deleted from Azure") + } + + // Step 3: Delete GatewaySubnet (this should be done last) + u.logger.Infof("Deleting GatewaySubnet: %s", gatewaySubnetName) + if err := u.deleteGatewaySubnet(ctx, resourceGroupName, vnetName); err != nil { + u.logger.Warnf("Failed to delete GatewaySubnet: %v", err) + // Continue - this is not critical + } else { + u.logger.Info("GatewaySubnet successfully deleted from Azure") + } + + u.logger.Info("Azure VPN Gateway resources cleanup completed") + return nil +} + +// deleteVPNGateway deletes the VPN Gateway +func (u *UnInstaller) deleteVPNGateway(ctx context.Context, resourceGroupName string) error { + // Check if VPN Gateway exists before trying to delete + gateway, err := u.vgwClient.Get(ctx, resourceGroupName, defaultVPNGatewayName, nil) + if err != nil { + // If gateway doesn't exist, consider it already deleted + u.logger.Infof("VPN Gateway %s not found, may already be deleted", defaultVPNGatewayName) + return nil + } + + if gateway.Properties == nil || gateway.Properties.ProvisioningState == nil { + u.logger.Warn("VPN Gateway found but has incomplete properties") + return nil + } + + u.logger.Infof("Found VPN Gateway %s in state: %s", defaultVPNGatewayName, *gateway.Properties.ProvisioningState) + + poller, err := u.vgwClient.BeginDelete(ctx, resourceGroupName, defaultVPNGatewayName, nil) + if err != nil { + return fmt.Errorf("failed to start VPN Gateway deletion: %w", err) + } + + // Wait for deletion to complete + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("VPN Gateway deletion failed: %w", err) + } + + return nil +} + +// deletePublicIP deletes the Public IP used by VPN Gateway +func (u *UnInstaller) deletePublicIP(ctx context.Context, resourceGroupName string) error { + // Check if Public IP exists before trying to delete + _, err := u.publicIPClient.Get(ctx, resourceGroupName, gatewayPublicIPName, nil) + if err != nil { + // If Public IP doesn't exist, consider it already deleted + u.logger.Infof("Public IP %s not found, may already be deleted", gatewayPublicIPName) + return nil + } + + poller, err := u.publicIPClient.BeginDelete(ctx, resourceGroupName, gatewayPublicIPName, nil) + if err != nil { + return fmt.Errorf("failed to start Public IP deletion: %w", err) + } + + // Wait for deletion to complete + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("public IP deletion failed: %w", err) + } + + return nil +} + +// deleteGatewaySubnet deletes the GatewaySubnet +func (u *UnInstaller) deleteGatewaySubnet(ctx context.Context, resourceGroupName, vnetName string) error { + // Check if GatewaySubnet exists before trying to delete + _, err := u.subnetsClient.Get(ctx, resourceGroupName, vnetName, gatewaySubnetName, nil) + if err != nil { + // If subnet doesn't exist, consider it already deleted + u.logger.Infof("GatewaySubnet %s not found, may already be deleted", gatewaySubnetName) + return nil + } + + poller, err := u.subnetsClient.BeginDelete(ctx, resourceGroupName, vnetName, gatewaySubnetName, nil) + if err != nil { + return fmt.Errorf("failed to start GatewaySubnet deletion: %w", err) + } + + // Wait for deletion to complete + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("GatewaySubnet deletion failed: %w", err) + } + + return nil +} diff --git a/pkg/components/vpn_gateway/vpn_operations.go b/pkg/components/vpn_gateway/vpn_operations.go new file mode 100644 index 0000000..9f0cd03 --- /dev/null +++ b/pkg/components/vpn_gateway/vpn_operations.go @@ -0,0 +1,794 @@ +package vpn_gateway + +import ( + "archive/zip" + "bytes" + "context" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "fmt" + "io" + "math/big" + "net" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" + "go.goms.io/aks/AKSFlexNode/pkg/utils" +) + +// VPN certificate and connection management functions for the Installer + +// generateCertificates generates VPN certificates for P2S connection +func (i *Installer) generateCertificates() (string, error) { + _, err := i.setupCertificateDirectory() + if err != nil { + return "", err + } + + // Check if certificates already exist + if certBase64, exists := i.loadExistingCertificate(); exists { + return certBase64, nil + } + + i.logger.Info("Generating new VPN certificates...") + certBase64, err := i.generateNewCertificate() + if err != nil { + return "", err + } + return certBase64, nil // new certificate generated +} + +// setupCertificateDirectory creates and configures the certificate directory +func (i *Installer) setupCertificateDirectory() (string, error) { + certDir := certificatesDir + if err := utils.RunSystemCommand("mkdir", "-p", certDir); err != nil { + return "", fmt.Errorf("failed to create certificates directory: %w", err) + } + + // Set proper permissions on the directory + if err := utils.RunSystemCommand("chmod", fmt.Sprintf("%o", certificatesDirPerm), certDir); err != nil { + return "", fmt.Errorf("failed to set permissions on certificates directory: %w", err) + } + + i.logger.Infof("Using system certificate directory: %s", certDir) + + return certDir, nil +} + +// loadExistingCertificate checks for existing certificates and returns root certificate base64 data if found +func (i *Installer) loadExistingCertificate() (string, bool) { + certPath := GetVPNClientCertPath() + keyPath := GetVPNClientKeyPath() + rootCertPath := GetVPNRootCertPath() + + // Check if all required files exist + if _, err := os.Stat(certPath); err == nil { + if _, err := os.Stat(keyPath); err == nil { + if _, err := os.Stat(rootCertPath); err == nil { + i.logger.Info("VPN certificates already exist, using existing certificates") + + // Read existing root certificate and return base64 data for Azure comparison + rootCertData, err := os.ReadFile(rootCertPath) + if err != nil { + i.logger.Warnf("Failed to read existing root certificate: %v", err) + return "", false + } + + // Parse certificate to get DER data for base64 encoding + block, _ := pem.Decode(rootCertData) + if block == nil { + i.logger.Warnf("Failed to parse existing root certificate PEM") + return "", false + } + + rootCertBase64 := base64.StdEncoding.EncodeToString(block.Bytes) + i.logger.Info("Using existing VPN certificates") + return rootCertBase64, true + } + } + } + + return "", false +} + +// generateNewCertificate creates a proper CA hierarchy with root CA and client certificate +func (i *Installer) generateNewCertificate() (string, error) { + // Generate root CA private key + rootPrivateKey, err := rsa.GenerateKey(rand.Reader, certificateKeySize) + if err != nil { + return "", fmt.Errorf("failed to generate root CA private key: %w", err) + } + + // Create root CA certificate + rootCertDER, err := i.createRootCACertificate(rootPrivateKey) + if err != nil { + return "", fmt.Errorf("failed to create root CA certificate: %w", err) + } + + // Generate client private key + clientPrivateKey, err := rsa.GenerateKey(rand.Reader, certificateKeySize) + if err != nil { + return "", fmt.Errorf("failed to generate client private key: %w", err) + } + + // Create client certificate signed by root CA + clientCertDER, err := i.createClientCertificate(clientPrivateKey, rootPrivateKey, rootCertDER) + if err != nil { + return "", fmt.Errorf("failed to create client certificate: %w", err) + } + + // Save certificate and key to files + clientKeyPath := GetVPNClientKeyPath() + clientCertPath := GetVPNClientCertPath() + rootCertPath := GetVPNRootCertPath() + + // Save the client private key + if err := i.savePrivateKey(clientKeyPath, clientPrivateKey); err != nil { + return "", err + } + + // Save the client certificate + if err := i.saveCertificate(clientCertPath, clientCertDER); err != nil { + return "", err + } + + // Save the root CA certificate (for Azure upload) + if err := i.saveCertificate(rootCertPath, rootCertDER); err != nil { + return "", err + } + + // Return base64-encoded root certificate for upload to Azure + rootCertBase64 := base64.StdEncoding.EncodeToString(rootCertDER) + + i.logger.Info("VPN certificate hierarchy generated successfully (root CA + client cert)") + + return rootCertBase64, nil +} + +// createRootCACertificate generates a root CA certificate +func (i *Installer) createRootCACertificate(privateKey *rsa.PrivateKey) ([]byte, error) { + // Generate SubjectKeyIdentifier for the root CA + publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to marshal public key: %w", err) + } + subjectKeyID := sha256.Sum256(publicKeyBytes) + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: certificateCommonName, + }, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: time.Now().Add(certificateValidYears * 365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + IsCA: true, // This is a CA certificate + SubjectKeyId: subjectKeyID[:], // Required for chain validation + } + + // Self-signed root CA: template is both the certificate to create and the issuer + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return nil, fmt.Errorf("failed to create root CA certificate: %w", err) + } + + return certDER, nil +} + +// createClientCertificate generates a client certificate signed by the root CA +func (i *Installer) createClientCertificate(clientPrivateKey, rootPrivateKey *rsa.PrivateKey, rootCertDER []byte) ([]byte, error) { + // Parse the root certificate to use as issuer + rootCert, err := x509.ParseCertificate(rootCertDER) + if err != nil { + return nil, fmt.Errorf("failed to parse root certificate: %w", err) + } + + // Ensure the root certificate has SubjectKeyId (should be set from createRootCACertificate) + if len(rootCert.SubjectKeyId) == 0 { + return nil, fmt.Errorf("root certificate is missing SubjectKeyId") + } + + // Generate SubjectKeyIdentifier for the client certificate + clientPublicKeyBytes, err := x509.MarshalPKIXPublicKey(&clientPrivateKey.PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to marshal client public key: %w", err) + } + clientSubjectKeyID := sha256.Sum256(clientPublicKeyBytes) + + template := x509.Certificate{ + SerialNumber: big.NewInt(2), // Different serial number for client cert + Subject: pkix.Name{ + CommonName: "VPN Client", + }, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: time.Now().Add(certificateValidYears * 365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + IsCA: false, // This is NOT a CA certificate + SubjectKeyId: clientSubjectKeyID[:], // Required for chain validation + AuthorityKeyId: rootCert.SubjectKeyId, // MUST match root's SubjectKeyId exactly + } + + i.logger.Infof("Creating client certificate with AuthorityKeyId matching root SubjectKeyId: %x", rootCert.SubjectKeyId) + + // Client certificate signed by root CA + certDER, err := x509.CreateCertificate(rand.Reader, &template, rootCert, &clientPrivateKey.PublicKey, rootPrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to create client certificate: %w", err) + } + + return certDER, nil +} + +// savePrivateKey saves the private key to file with proper permissions +func (i *Installer) savePrivateKey(keyPath string, privateKey *rsa.PrivateKey) error { + privateKeyPEM := &pem.Block{ + Type: rsaPrivateKeyType, + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + } + + // Encode PEM to bytes + var keyBuffer bytes.Buffer + if err := pem.Encode(&keyBuffer, privateKeyPEM); err != nil { + return fmt.Errorf("failed to encode private key: %w", err) + } + + // Write file using system-level file operations + if err := utils.WriteFileAtomicSystem(keyPath, keyBuffer.Bytes(), privateKeyFilePerm); err != nil { + return fmt.Errorf("failed to create key file: %w", err) + } + + return nil +} + +// saveCertificate saves the certificate to file with proper permissions +func (i *Installer) saveCertificate(certPath string, certDER []byte) error { + certPEM := &pem.Block{ + Type: certificateType, + Bytes: certDER, + } + + // Encode PEM to bytes + var certBuffer bytes.Buffer + if err := pem.Encode(&certBuffer, certPEM); err != nil { + return fmt.Errorf("failed to encode certificate: %w", err) + } + + // Write file using system-level file operations + if err := utils.WriteFileAtomicSystem(certPath, certBuffer.Bytes(), certificateFilePerm); err != nil { + return fmt.Errorf("failed to create certificate file: %w", err) + } + + return nil +} + +// processVPNConfig processes VPN config file and embeds certificates using sed (matching working implementation) +func (i *Installer) processVPNConfig(sourcePath, destPath string) error { + i.logger.Info("Processing VPN configuration with certificate data...") + + // Ensure certificates exist - use the same directory logic as GenerateCertificates + _, err := i.setupCertificateDirectory() + if err != nil { + return fmt.Errorf("failed to setup certificate directory: %w", err) + } + + clientCertPath := GetVPNClientCertPath() + clientKeyPath := GetVPNClientKeyPath() + + // Check if certificates exist, if not generate them + if _, err := os.Stat(clientCertPath); os.IsNotExist(err) { + i.logger.Info("Client certificates not found, generating new certificates...") + if _, err := i.generateCertificates(); err != nil { + return fmt.Errorf("failed to generate certificates: %w", err) + } + } + + // Create a temporary working copy of the VPN config + tempConfig, err := os.CreateTemp("", tempVPNConfigPattern) + if err != nil { + return fmt.Errorf("failed to create temporary config file: %w", err) + } + tempConfigPath := tempConfig.Name() + _ = tempConfig.Close() + defer func() { + if err := utils.RunCleanupCommand(tempConfigPath); err != nil { + i.logger.Warnf("Failed to clean up temp config file %s: %v", tempConfigPath, err) + } + }() + + // Copy source to temp file + if err := utils.RunSystemCommand("cp", sourcePath, tempConfigPath); err != nil { + return fmt.Errorf("failed to copy VPN config to temp file: %w", err) + } + + // Read certificate and key content using system commands + certContent, err := utils.RunCommandWithOutput("cat", clientCertPath) + if err != nil { + return fmt.Errorf("failed to read certificate file: %w", err) + } + + keyContent, err := utils.RunCommandWithOutput("cat", clientKeyPath) + if err != nil { + return fmt.Errorf("failed to read private key file: %w", err) + } + + // Read the config file content + configContent, err := utils.RunCommandWithOutput("cat", tempConfigPath) + if err != nil { + return fmt.Errorf("failed to read temp config file: %w", err) + } + + // Replace placeholders with actual content (trim to remove any trailing whitespace) + processedConfig := strings.ReplaceAll(configContent, "$CLIENTCERTIFICATE", strings.TrimSpace(certContent)) + processedConfig = strings.ReplaceAll(processedConfig, "$PRIVATEKEY", strings.TrimSpace(keyContent)) + + // Write processed config back to temp file using system command + if err := utils.WriteFileAtomicSystem(tempConfigPath, []byte(processedConfig), 0600); err != nil { + return fmt.Errorf("failed to write processed config: %w", err) + } + + // Copy processed config to final destination + if strings.HasPrefix(destPath, systemEtcPrefix) || strings.HasPrefix(destPath, systemUsrPrefix) || strings.HasPrefix(destPath, systemVarPrefix) { + // Create destination directory if it doesn't exist + destDir := filepath.Dir(destPath) + if err := utils.RunSystemCommand("mkdir", "-p", destDir); err != nil { + return fmt.Errorf("failed to create destination directory: %w", err) + } + + // Copy temp file to destination with sudo + if err := utils.RunSystemCommand("cp", tempConfigPath, destPath); err != nil { + return fmt.Errorf("failed to copy VPN config to %s: %w", destPath, err) + } + + // Set proper permissions and ownership with sudo + if err := utils.RunSystemCommand("chmod", "600", destPath); err != nil { + i.logger.Warnf("Failed to set permissions on VPN config: %v", err) + } + + if err := utils.RunSystemCommand("chown", "root:root", destPath); err != nil { + i.logger.Warnf("Failed to set ownership on VPN config: %v", err) + } + } else { + // Destination is in user directory, copy directly + if err := utils.RunSystemCommand("cp", tempConfigPath, destPath); err != nil { + return fmt.Errorf("failed to copy VPN config to %s: %w", destPath, err) + } + } + + i.logger.Info("VPN configuration processed successfully") + return nil +} + +// setupOpenVPN installs and configures OpenVPN +func (i *Installer) setupOpenVPN(configPath string) error { + i.logger.Info("Setting up OpenVPN...") + + // Check if VPN connection is already established + if i.isVPNConnected() { + i.logger.Info("VPN connection is already established, skipping OpenVPN setup") + return nil + } + + // Install OpenVPN + // Check if OpenVPN is already installed + if err := utils.RunSystemCommand("which", "openvpn"); err == nil { + i.logger.Info("OpenVPN is already installed, skipping installation") + } else { + i.logger.Info("Installing OpenVPN...") + if err := utils.RunSystemCommand("apt", "install", "-y", "openvpn"); err != nil { + return fmt.Errorf("failed to install OpenVPN: %w", err) + } + } + + // Always ensure certificates are embedded in the OpenVPN config + destPath := GetOpenVPNConfigPath() + + // If configPath is provided, process it; otherwise process existing config + sourceConfigPath := configPath + if sourceConfigPath == "" { + sourceConfigPath = destPath // Process existing config in place + } + + // Copy and process VPN config file + if sourceConfigPath != "" { + vpnConfigDir := openVPNConfigDir + if err := utils.RunSystemCommand("mkdir", "-p", vpnConfigDir); err != nil { + return fmt.Errorf("failed to create OpenVPN config directory: %w", err) + } + + // Process VPN config with certificate data + if err := i.processVPNConfig(sourceConfigPath, destPath); err != nil { + return fmt.Errorf("failed to process VPN config: %w", err) + } + + // Enable and restart OpenVPN service to ensure it uses the updated configuration + if err := utils.EnableService(openVPNServiceTemplate); err != nil { + return fmt.Errorf("failed to enable OpenVPN service: %w", err) + } + + i.logger.Info("Restarting OpenVPN service to apply updated configuration...") + if err := utils.RestartService(openVPNServiceTemplate); err != nil { + return fmt.Errorf("failed to restart OpenVPN service: %w", err) + } + + // Give OpenVPN a moment to start before checking status + time.Sleep(2 * time.Second) + + // Check if OpenVPN service started successfully + if !utils.IsServiceActive(openVPNServiceTemplate) { + i.logger.Warn("OpenVPN service is not active, please check the service status for details") + } else { + i.logger.Info("OpenVPN service restarted successfully") + } + } + return nil +} + +// configureVPNNetworking configures routes and iptables rules for VPN gateway connectivity +func (i *Installer) configureVPNNetworking(ctx context.Context, vnetInfo vnetResourceInfo) error { + i.logger.Info("Configuring VPN network routes and iptables rules...") + + // Get Pod CIDR from user configuration and extract all AKS VNet CIDRs from vnetInfo + podCIDR, aksVNetCIDRs, err := i.getNetworkConfiguration(vnetInfo) + if err != nil { + return fmt.Errorf("failed to get network configuration: %w", err) + } + + i.logger.Infof("Using AKS VNet CIDRs: %v, Pod CIDR: %s", aksVNetCIDRs, podCIDR) + + // Get VPN interface + vpnInterface, err := utils.GetVPNInterface() + if err != nil { + return fmt.Errorf("failed to get VPN interface: %w", err) + } + + i.logger.Infof("Configuring networking for VPN interface: %s", vpnInterface) + + // Add route for AKS VNet via VPN gateway + // The gateway IP is typically the first IP in the P2S CIDR range + 1 + gatewayIP, err := i.calculateGatewayIP() + if err != nil { + return fmt.Errorf("failed to calculate gateway IP: %w", err) + } + + // Add routes for all VNet CIDRs + for _, vnetCIDR := range aksVNetCIDRs { + i.logger.Infof("Adding route: %s via %s dev %s", vnetCIDR, gatewayIP, vpnInterface) + if err := i.addIPRoute(vnetCIDR, gatewayIP, vpnInterface); err != nil { + return fmt.Errorf("failed to add route for AKS VNet CIDR %s: %w", vnetCIDR, err) + } + } + + // Add route for AKS pod network (required for flex pod to aks pod communication) + // This enables Flex node pods to reach AKS cluster pods (like DNS services) + i.logger.Infof("Adding route for AKS pod network: %s via %s dev %s", podCIDR, gatewayIP, vpnInterface) + if err := i.addIPRoute(podCIDR, gatewayIP, vpnInterface); err != nil { + return fmt.Errorf("failed to add route for AKS pod CIDR %s: %w", podCIDR, err) + } + + i.logger.Info("VPN network configuration completed successfully") + return nil +} + +// getNetworkConfiguration gets Pod CIDR from user config and extracts AKS VNet CIDRs from vnetInfo +func (i *Installer) getNetworkConfiguration(vnetInfo vnetResourceInfo) (string, []string, error) { + // Get Pod CIDR from user configuration (required) + if i.config.Azure.VPNGateway == nil || i.config.Azure.VPNGateway.PodCIDR == "" { + return "", nil, fmt.Errorf("pod CIDR is required in VPN configuration when enabled, please set it") + } + podCIDR := i.config.GetVPNGatewayPodCIDR() + + // Extract all AKS VNet CIDRs from the already discovered VNet info + // Using all VNet CIDRs ensures we can reach all subnets including AKS nodes + aksVNetCIDRs, err := i.getVNetCIDRsFromInfo(vnetInfo) + if err != nil { + return "", nil, fmt.Errorf("failed to get AKS VNet CIDRs: %w", err) + } + + return podCIDR, aksVNetCIDRs, nil +} + +// getVNetCIDRsFromInfo extracts all VNet CIDRs from vnetInfo +func (i *Installer) getVNetCIDRsFromInfo(vnetInfo vnetResourceInfo) ([]string, error) { + // Extract all VNet CIDRs from the address space + if vnetInfo.vnet == nil || + vnetInfo.vnet.Properties == nil || + vnetInfo.vnet.Properties.AddressSpace == nil || + len(vnetInfo.vnet.Properties.AddressSpace.AddressPrefixes) == 0 { + return nil, fmt.Errorf("VNet has no address prefixes") + } + + // Extract all address prefixes as VNet CIDRs + var vnetCIDRs []string + for _, prefix := range vnetInfo.vnet.Properties.AddressSpace.AddressPrefixes { + if prefix != nil { + vnetCIDRs = append(vnetCIDRs, *prefix) + } + } + + if len(vnetCIDRs) == 0 { + return nil, fmt.Errorf("VNet has no valid address prefixes") + } + + i.logger.Infof("Using VNet CIDRs: %v", vnetCIDRs) + return vnetCIDRs, nil +} + +// calculateGatewayIP calculates the gateway IP from P2S CIDR +func (i *Installer) calculateGatewayIP() (string, error) { + p2sCIDR := i.config.Azure.VPNGateway.P2SGatewayCIDR + if p2sCIDR == "" { + return "", fmt.Errorf("P2S Gateway CIDR not configured") + } + + // Parse the P2S CIDR (e.g., "192.168.100.0/24") + _, network, err := net.ParseCIDR(p2sCIDR) + if err != nil { + return "", fmt.Errorf("failed to parse P2S CIDR %s: %w", p2sCIDR, err) + } + + // Gateway IP is typically the first usable IP in the range + // For 192.168.100.0/24, the gateway would be 192.168.100.1 + ip := network.IP.To4() + if ip == nil { + return "", fmt.Errorf("only IPv4 networks are supported") + } + + // Increment the network address by 1 to get the gateway IP + gatewayIP := net.IPv4(ip[0], ip[1], ip[2], ip[3]+1) + return gatewayIP.String(), nil +} + +// addIPRoute adds an IP route if it doesn't already exist +func (i *Installer) addIPRoute(vnetCIDR, gatewayIP, vpnInterface string) error { + // Try to add the route, capture combined output to check for "File exists" error + output, err := utils.RunCommandWithOutput("ip", "route", "add", vnetCIDR, "via", gatewayIP, "dev", vpnInterface) + if err != nil { + // Check if route already exists by looking for "File exists" in the output or error + if strings.Contains(output, "File exists") || strings.Contains(err.Error(), "File exists") { + i.logger.Infof("Route to %s already exists, skipping", vnetCIDR) + return nil + } + return fmt.Errorf("failed to add route for AKS VNet CIDR %s: %s (exit code: %v)", vnetCIDR, strings.TrimSpace(output), err) + } + + i.logger.Infof("Added route: %s via %s dev %s", vnetCIDR, gatewayIP, vpnInterface) + return nil +} + +// isVPNConnected checks if VPN connection is active +func (i *Installer) isVPNConnected() bool { + iface, err := utils.GetVPNInterface() + if err != nil { + return false + } + + ip, err := utils.GetVPNInterfaceIP(iface) + return err == nil && ip != "" +} + +// uploadCertificateToAzure uploads the root certificate to Azure VPN Gateway using Azure SDK +func (i *Installer) uploadCertificateToAzure(ctx context.Context, certData string, vnetInfo vnetResourceInfo) error { + // Get the current VPN Gateway to update its configuration + gateway, err := i.vgwClient.Get(ctx, vnetInfo.resourceGroupName, defaultVPNGatewayName, nil) + if err != nil { + return fmt.Errorf("failed to get VPN Gateway: %w", err) + } + + // Check if VPN client configuration exists + // Look for our specific certificate by name and data + var existingCertFound bool + var existingCertMatches bool + + // Check if VPN client configuration exists and has certificates + if gateway.Properties.VPNClientConfiguration != nil && + gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates != nil { + for _, cert := range gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates { + if cert.Properties != nil && cert.Properties.PublicCertData != nil { + // Compare certificate data directly + if *cert.Properties.PublicCertData == certData { + i.logger.Infof("VPN certificate already exists on Azure VPN Gateway with name '%s', skipping upload", *cert.Name) + return nil // Certificate already exists and matches, no need to upload + } + } + // Track if any certificate exists (regardless of name) + if cert.Name != nil { + existingCertFound = true + } + } + } + + if existingCertFound && !existingCertMatches { + i.logger.Info("Adding new VPN root certificate alongside existing certificates") + } else if !existingCertFound { + i.logger.Info("No existing VPN root certificates found, uploading first certificate") + } + + // Ensure the VPN client configuration section exists with required address pool + if gateway.Properties.VPNClientConfiguration == nil { + p2sGatewayCIDR := i.config.Azure.VPNGateway.P2SGatewayCIDR + vpnClientProtocol := armnetwork.VPNClientProtocolOpenVPN + + gateway.Properties.VPNClientConfiguration = &armnetwork.VPNClientConfiguration{ + VPNClientAddressPool: &armnetwork.AddressSpace{ + AddressPrefixes: []*string{&p2sGatewayCIDR}, + }, + VPNClientProtocols: []*armnetwork.VPNClientProtocol{&vpnClientProtocol}, + } + } + + // Create root certificate parameters with unique name based on certificate data + certHash := fmt.Sprintf("%x", sha256.Sum256([]byte(certData)))[:8] // Use first 8 chars of hash + certName := fmt.Sprintf("%s-%s", vpnClientRootCertName, certHash) + + i.logger.Infof("Adding VPN root certificate with name: %s", certName) + + rootCert := armnetwork.VPNClientRootCertificate{ + Name: &certName, + Properties: &armnetwork.VPNClientRootCertificatePropertiesFormat{ + PublicCertData: &certData, + }, + } + + // Append the new certificate to existing certificates instead of replacing them + if gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates == nil { + gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates = []*armnetwork.VPNClientRootCertificate{} + } + + // Always append the new certificate (with unique name, no conflicts) + gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates = + append(gateway.Properties.VPNClientConfiguration.VPNClientRootCertificates, &rootCert) + + // Update the VPN Gateway with the new certificate configuration + poller, err := i.vgwClient.BeginCreateOrUpdate(ctx, vnetInfo.resourceGroupName, defaultVPNGatewayName, gateway.VirtualNetworkGateway, nil) + if err != nil { + return fmt.Errorf("failed to start VPN Gateway update: %w", err) + } + + // Wait for the operation to complete + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("failed to update VPN Gateway with certificate: %w", err) + } + + if existingCertFound && !existingCertMatches { + i.logger.Info("VPN certificate added successfully - now have multiple certificates available") + } else { + i.logger.Info("VPN certificate uploaded to Azure successfully") + } + return nil + +} + +// downloadVPNClientConfig downloads the VPN client configuration from Azure using Azure SDK +func (i *Installer) downloadVPNClientConfig(ctx context.Context, gatewayName, resourceGroup string) (string, error) { + i.logger.Info("Downloading VPN client configuration from Azure VPN Gateway...") + + // Generate VPN client configuration + authMethod := armnetwork.AuthenticationMethodEAPTLS + req := armnetwork.VPNClientParameters{ + AuthenticationMethod: &authMethod, + } + + poller, err := i.vgwClient.BeginGenerateVPNProfile(ctx, resourceGroup, gatewayName, req, nil) + if err != nil { + return "", fmt.Errorf("failed to start VPN client config generation: %w", err) + } + + // Wait for the operation to complete + result, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return "", fmt.Errorf("failed to generate VPN client config: %w", err) + } + + if result.Value == nil || *result.Value == "" { + return "", fmt.Errorf("no VPN client configuration URL returned from Azure") + } + + downloadURL := *result.Value + i.logger.Infof("VPN client configuration URL: %s", downloadURL) + + // Download the configuration file + configData, err := i.downloadConfigFromURL(downloadURL) + if err != nil { + return "", fmt.Errorf("failed to download VPN client configuration: %w", err) + } + + return configData, nil + +} + +// downloadConfigFromURL downloads the VPN client configuration from the provided URL +func (i *Installer) downloadConfigFromURL(urlStr string) (string, error) { + // Validate URL to prevent SSRF attacks + parsedURL, err := url.Parse(urlStr) + if err != nil { + return "", fmt.Errorf("invalid URL: %w", err) + } + + // Only allow HTTPS URLs for security + if parsedURL.Scheme != "https" { + return "", fmt.Errorf("only HTTPS URLs are allowed, got: %s", parsedURL.Scheme) + } + + // Validate that this is an Azure Blob Storage URL for VPN configuration + host := strings.ToLower(parsedURL.Host) + if !strings.HasSuffix(host, ".blob.core.windows.net") { + return "", fmt.Errorf("URL must be from Azure Blob Storage: %s", parsedURL.Host) + } + + // Create temporary file for ZIP download + tempZipFile, err := os.CreateTemp("", tempVPNZipPattern) + if err != nil { + return "", fmt.Errorf("failed to create temporary ZIP file: %w", err) + } + tempZipPath := tempZipFile.Name() + _ = tempZipFile.Close() // Close file handle so utils.DownloadFile can write to it + + defer func() { + if err := utils.RunCleanupCommand(tempZipPath); err != nil { + i.logger.Warnf("Failed to clean up temp ZIP file %s: %v", tempZipPath, err) + } + }() + + // Download the ZIP file using utils.DownloadFile + i.logger.Info("Downloading VPN configuration ZIP file...") + if err := utils.DownloadFile(urlStr, tempZipPath); err != nil { + return "", fmt.Errorf("failed to download VPN config ZIP: %w", err) + } + + // Extract and return the OpenVPN configuration + return i.extractOpenVPNConfig(tempZipPath) +} + +// extractOpenVPNConfig extracts the OpenVPN configuration from a ZIP file +func (i *Installer) extractOpenVPNConfig(zipPath string) (string, error) { + // Open the ZIP file + reader, err := zip.OpenReader(zipPath) + if err != nil { + return "", fmt.Errorf("failed to open VPN config ZIP: %w", err) + } + defer func() { _ = reader.Close() }() + + // Look for any .ovpn file in the ZIP (handle different path separators) + i.logger.Info("Examining ZIP contents:") + for _, file := range reader.File { + i.logger.Infof(" File: %s", file.Name) + fileName := strings.ToLower(file.Name) + // Check for .ovpn files in OpenVPN directory (handle both / and \ separators) + if strings.HasSuffix(fileName, ".ovpn") && + (strings.Contains(fileName, "openvpn/") || strings.Contains(fileName, "openvpn\\")) { + // Extract and read the config file with size limits + fileReader, err := file.Open() + if err != nil { + return "", fmt.Errorf("failed to open OpenVPN config: %w", err) + } + defer func() { _ = fileReader.Close() }() + + // Add size limit of 1MB (config files are typically small) + const maxFileSize = 1024 * 1024 // 1MB + limitedReader := io.LimitReader(fileReader, maxFileSize) + + configData, err := io.ReadAll(limitedReader) + if err != nil { + return "", fmt.Errorf("failed to read OpenVPN config: %w", err) + } + + i.logger.Info("VPN configuration extracted successfully") + return string(configData), nil + } + } + + return "", fmt.Errorf("OpenVPN configuration file (.ovpn) not found in ZIP") +} diff --git a/pkg/config/structs.go b/pkg/config/structs.go index 3c1cc4a..609a361 100644 --- a/pkg/config/structs.go +++ b/pkg/config/structs.go @@ -30,6 +30,7 @@ type AzureConfig struct { ManagedIdentity *ManagedIdentityConfig `json:"managedIdentity,omitempty"` // Optional managed identity authentication BootstrapToken *BootstrapTokenConfig `json:"bootstrapToken,omitempty"` // Optional bootstrap token authentication Arc *ArcConfig `json:"arc"` // Azure Arc machine configuration + VPNGateway *VPNConfig `json:"vpnGateway"` // Azure VPN gateway configuration for P2S connectivity TargetCluster *TargetClusterConfig `json:"targetCluster"` // Target AKS cluster configuration } @@ -72,6 +73,15 @@ type ArcConfig struct { Location string `json:"location"` // Azure region for Arc machine } +// VPNConfig holds configuration settings for the VPN gateway. +type VPNConfig struct { + Enabled bool `json:"enabled"` + P2SGatewayCIDR string `json:"p2sGatewayCIDR"` + GatewaySKU string `json:"gatewaySKU"` + PodCIDR string `json:"podCIDR,omitempty"` // Pod network CIDR (e.g., "10.244.0.0/16") - required for routing + VNetID string `json:"vnetID,omitempty"` // Azure VNet resource ID (AKS managed or BYO VNet where AKS nodes reside) +} + // AgentConfig holds agent-specific operational configuration. type AgentConfig struct { LogLevel string `json:"logLevel"` // Logging level: debug, info, warning, error @@ -130,7 +140,7 @@ type KubernetesPathsConfig struct { KubeletDir string `json:"kubeletDir"` } -// CNIPathsConfig holds file system paths related to CNI plugins and configurations. +// CNIConfig holds configuration settings for CNI plugins and networking type CNIConfig struct { Version string `json:"version"` } @@ -196,6 +206,14 @@ func (cfg *Config) GetTargetClusterResourceGroup() string { return "" } +// GetTargetClusterNodeResourceGroup returns the target AKS cluster node resource group from configuration +func (cfg *Config) GetTargetClusterNodeResourceGroup() string { + if cfg.Azure.TargetCluster != nil && cfg.Azure.TargetCluster.NodeResourceGroup != "" { + return cfg.Azure.TargetCluster.NodeResourceGroup + } + return "" +} + // GetTargetClusterLocation returns the target AKS cluster location from configuration func (cfg *Config) GetTargetClusterLocation() string { if cfg.Azure.TargetCluster != nil && cfg.Azure.TargetCluster.Location != "" { @@ -256,3 +274,28 @@ func (cfg *Config) GetKubernetesVersion() string { func (cfg *Config) IsARCEnabled() bool { return cfg.Azure.Arc != nil && cfg.Azure.Arc.Enabled } + +// IsVPNGatewayEnabled checks if VPN Gateway is enabled in the configuration +func (cfg *Config) IsVPNGatewayEnabled() bool { + if cfg.Azure.VPNGateway != nil && + cfg.Azure.VPNGateway.Enabled { + return true + } + return false +} + +// GetVPNGatewayVNetID returns the VNet ID for the VPN Gateway from configuration +func (cfg *Config) GetVPNGatewayVNetID() string { + if cfg.Azure.VPNGateway != nil { + return cfg.Azure.VPNGateway.VNetID + } + return "" +} + +// GetVPNGatewayPodCIDR returns the Pod CIDR for the VPN Gateway from configuration +func (cfg *Config) GetVPNGatewayPodCIDR() string { + if cfg.Azure.VPNGateway != nil { + return cfg.Azure.VPNGateway.PodCIDR + } + return "" +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 80fe053..9e4f976 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -20,7 +20,7 @@ import ( // sudoCommandLists holds the command lists for sudo determination var ( - alwaysNeedsSudo = []string{"apt", "apt-get", "dpkg", "systemctl", "mount", "umount", "modprobe", "sysctl", "azcmagent", "usermod", "kubectl", "swapoff"} + alwaysNeedsSudo = []string{"apt", "apt-get", "dpkg", "systemctl", "mount", "umount", "modprobe", "sysctl", "azcmagent", "usermod", "kubectl", "pkill", "swapoff", "iptables", "ip"} conditionalSudo = []string{"mkdir", "cp", "chmod", "chown", "mv", "tar", "rm", "bash", "install", "ln", "cat"} systemPaths = []string{"/etc/", "/usr/", "/var/", "/opt/", "/boot/", "/sys/"} ) @@ -98,6 +98,15 @@ func IsServiceActive(serviceName string) bool { return strings.TrimSpace(output) == "active" } +// IsServiceEnabled checks if a systemd service is enabled +func IsServiceEnabled(serviceName string) bool { + output, err := RunCommandWithOutput("systemctl", "is-enabled", serviceName) + if err != nil { + return false + } + return strings.TrimSpace(output) == "enabled" +} + // ServiceExists checks if a systemd service unit file exists func ServiceExists(serviceName string) bool { err := RunSystemCommand("systemctl", "list-unit-files", serviceName+".service") @@ -114,6 +123,11 @@ func DisableService(serviceName string) error { return RunSystemCommand("systemctl", "disable", serviceName) } +// EnableService enables a systemd service +func EnableService(serviceName string) error { + return RunSystemCommand("systemctl", "enable", serviceName) +} + // EnableAndStartService enables and starts a systemd service func EnableAndStartService(serviceName string) error { return RunSystemCommand("systemctl", "enable", "--now", serviceName) @@ -468,3 +482,133 @@ func ExtractClusterInfo(kubeconfigData []byte) (string, string, error) { caCertDataB64 := base64.StdEncoding.EncodeToString(cluster.CertificateAuthorityData) return cluster.Server, caCertDataB64, nil } + +// GetVPNInterface returns the first available VPN interface (tun0, tun1, etc.) +func GetVPNInterface() (string, error) { + const vpnInterfacePrefix = "tun" + const maxVPNInterfaces = 10 + + // Check for tun interfaces + for j := 0; j < maxVPNInterfaces; j++ { + iface := fmt.Sprintf("%s%d", vpnInterfacePrefix, j) + if _, err := os.Stat(fmt.Sprintf("/sys/class/net/%s", iface)); err == nil { + return iface, nil + } + } + return "", fmt.Errorf("no VPN interface found") +} + +// GetVPNInterfaceIP returns the IP address of the given VPN interface +func GetVPNInterfaceIP(iface string) (string, error) { + output, err := RunCommandWithOutput("ip", "addr", "show", iface) + if err != nil { + return "", fmt.Errorf("failed to get interface %s info: %w", iface, err) + } + + // Parse IP address from output + lines := strings.Split(output, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.Contains(line, "inet ") && !strings.Contains(line, "inet6") { + fields := strings.Fields(line) + if len(fields) >= 2 { + ip := strings.Split(fields[1], "/")[0] + return ip, nil + } + } + } + return "", fmt.Errorf("no IP address found for interface %s", iface) +} + +// ValidateAzureResourceID validates that the provided resource ID follows Azure resource ID format +// Expected format: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProvider}/{resourceType}/{resourceName} +func ValidateAzureResourceID(resourceID, expectedResourceType string) error { + if resourceID == "" { + return fmt.Errorf("resource ID cannot be empty") + } + + // Azure resource IDs must start with /subscriptions/ + if !strings.HasPrefix(resourceID, "/subscriptions/") { + return fmt.Errorf("resource ID must start with '/subscriptions/', got: %s", resourceID) + } + + // Split the resource ID into parts + parts := strings.Split(resourceID, "/") + + // Azure resource ID should have at least 9 parts: + // ["", "subscriptions", "{subscriptionId}", "resourceGroups", "{resourceGroupName}", "providers", "{resourceProvider}", "{resourceType}", "{resourceName}"] + if len(parts) < 9 { + return fmt.Errorf("resource ID has invalid format, expected at least 9 segments, got %d: %s", len(parts), resourceID) + } + + // Validate the fixed parts of the resource ID format + expectedSegments := map[int]string{ + 1: "subscriptions", + 3: "resourceGroups", + 5: "providers", + } + + for index, expectedValue := range expectedSegments { + if index >= len(parts) || parts[index] != expectedValue { + return fmt.Errorf("resource ID segment %d should be '%s', got '%s': %s", index, expectedValue, parts[index], resourceID) + } + } + + // Validate that required segments are not empty + requiredSegments := map[int]string{ + 2: "subscription ID", + 4: "resource group name", + 6: "resource provider", + 7: "resource type", + 8: "resource name", + } + + for index, segmentName := range requiredSegments { + if index >= len(parts) || strings.TrimSpace(parts[index]) == "" { + return fmt.Errorf("resource ID %s cannot be empty: %s", segmentName, resourceID) + } + } + + // Validate the resource type matches expected type + if expectedResourceType != "" && parts[7] != expectedResourceType { + return fmt.Errorf("expected resource type '%s', got '%s': %s", expectedResourceType, parts[7], resourceID) + } + + // Validate that it's a Microsoft.Network provider for VNet + if expectedResourceType == "virtualNetworks" && parts[6] != "Microsoft.Network" { + return fmt.Errorf("VNet resource must use Microsoft.Network provider, got '%s': %s", parts[6], resourceID) + } + + return nil +} + +// GetResourceGroupFromResourceID extracts the resource group name from an Azure resource ID +func GetResourceGroupFromResourceID(resourceID string) string { + parts := strings.Split(resourceID, "/") + for i, part := range parts { + if strings.EqualFold(part, "resourceGroups") && i+1 < len(parts) { + return parts[i+1] + } + } + return "" +} + +// GetResourceNameFromResourceID extracts the resource name from an Azure resource ID +func GetResourceNameFromResourceID(resourceID string) string { + parts := strings.Split(resourceID, "/") + if len(parts) > 0 { + return parts[len(parts)-1] + } + return "" +} + +// GetSubscriptionIDFromResourceID extracts the subscription ID from an Azure resource ID +func GetSubscriptionIDFromResourceID(resourceID string) string { + parts := strings.Split(resourceID, "/") + for i, part := range parts { + if strings.EqualFold(part, "subscriptions") && i+1 < len(parts) { + return parts[i+1] + } + } + return "" +}