From e5bf819db34227cc739963059d72582a73ea1613 Mon Sep 17 00:00:00 2001 From: Parfenov Ivan Date: Wed, 23 Jul 2025 14:21:07 +0300 Subject: [PATCH] feat(ccd): support IP conflict validation in Kubernetes Secret mode (#384) Signed-off-by: Paramoshka --- kubernetes.go | 8 +++---- main.go | 65 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/kubernetes.go b/kubernetes.go index 8b4e4ca..05c0c77 100644 --- a/kubernetes.go +++ b/kubernetes.go @@ -329,10 +329,10 @@ func (openVPNPKI *OpenVPNPKI) easyrsaBuildClient(commonName string) (err error) secretMetaData := metav1.ObjectMeta{ Name: fmt.Sprintf(secretClientTmpl, clientCert.SerialNumber), Labels: map[string]string{ - "index.txt": "", - "type": "clientAuth", - "name": commonName, - "app.kubernetes.io/managed-by": "ovpn-admin", + labelKeyIndexTxt: "", + labelKeyType: labelValueClientAuth, + labelKeyName: commonName, + labelKeyManagedBy: labelValueManagedByApp, }, Annotations: map[string]string{ "commonName": commonName, diff --git a/main.go b/main.go index 472a039..a22fe90 100644 --- a/main.go +++ b/main.go @@ -35,14 +35,21 @@ import ( ) const ( - usernameRegexp = `^([a-zA-Z0-9_.\-@])+$` - passwordMinLength = 6 - certsArchiveFileName = "certs.tar.gz" - ccdArchiveFileName = "ccd.tar.gz" - indexTxtDateLayout = "060102150405Z" - stringDateFormat = "2006-01-02 15:04:05" - downloadCertsApiUrl = "api/data/certs/download" - downloadCcdApiUrl = "api/data/ccd/download" + usernameRegexp = `^([a-zA-Z0-9_.\-@])+$` + passwordMinLength = 6 + certsArchiveFileName = "certs.tar.gz" + ccdArchiveFileName = "ccd.tar.gz" + indexTxtDateLayout = "060102150405Z" + stringDateFormat = "2006-01-02 15:04:05" + downloadCertsApiUrl = "api/data/certs/download" + downloadCcdApiUrl = "api/data/ccd/download" + labelKeyIndexTxt = "index.txt" + labelKeyType = "type" + labelKeyName = "name" + labelKeyManagedBy = "app.kubernetes.io/managed-by" + labelValueClientAuth = "clientAuth" + labelValueManagedByApp = "ovpn-admin" + prefixStaticRoute = "ifconfig-push" kubeNamespaceFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" ) @@ -861,11 +868,53 @@ func (oAdmin *OvpnAdmin) getCcd(username string) Ccd { } func checkStaticAddressIsFree(staticAddress string, username string) bool { + + if *storageBackend == "kubernetes.secrets" { + + log.Infof("Static address: %s", staticAddress) + + labelSelector := fmt.Sprintf("%s=%s,%s=%s", + labelKeyType, labelValueClientAuth, + labelKeyManagedBy, labelValueManagedByApp) + + secrets, err := app.secretsGetByLabels(labelSelector) + if err != nil { + log.Error(err) + } + + for _, secret := range secrets.Items { + otherUser := secret.Labels["name"] + if otherUser == username { + continue + } + + dataCCD, ok := secret.Data["ccd"] + if !ok { + continue + } + + lines := strings.Split(string(dataCCD), "\n") + + for _, line := range lines { + if strings.HasPrefix(line, prefixStaticRoute) { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[1] == staticAddress { + log.Warnf("IP %s already assigned to user %s", staticAddress, otherUser) + return false + } + } + } + } + + return true + } + o := runBash(fmt.Sprintf("grep -rl ' %[1]s ' %[2]s | grep -vx %[2]s/%[3]s | wc -l", staticAddress, *ccdDir, username)) if strings.TrimSpace(o) == "0" { return true } + return false }