Skip to content

Commit

Permalink
PWX-32188: TLS cipher-suite/version annotations (#1153)
Browse files Browse the repository at this point in the history
* adding annotations to select TLS version and cipher-suites for kube-controller-manager

Signed-off-by: Zoran Rajic <zox@portworx.com>

Manualy fixed Conflicts:
	drivers/storage/portworx/util/util.go
	drivers/storage/portworx/util/util_test.go
  • Loading branch information
zoxpx committed Jul 21, 2023
1 parent 65aaa95 commit 1b0f23e
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
12 changes: 12 additions & 0 deletions drivers/storage/portworx/component/pvccontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,18 @@ func (c *pvcController) createDeployment(
command = append(command, "--secure-port="+AksPVCControllerSecurePort)
}

if min, err := pxutil.GetTLSMinVersion(cluster); err != nil {
return err
} else if min != "" {
command = append(command, "--tls-min-version="+min)
}

if cs, err := pxutil.GetTLSCipherSuites(cluster); err != nil {
return err
} else if cs != "" {
command = append(command, "--tls-cipher-suites="+cs)
}

existingDeployment := &appsv1.Deployment{}
err = c.k8sClient.Get(
context.TODO(),
Expand Down
65 changes: 65 additions & 0 deletions drivers/storage/portworx/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
Expand Down Expand Up @@ -141,6 +142,10 @@ const (
AnnotationPreflightCheck = pxAnnotationPrefix + "/preflight-check"
// AnnotationFACDTopology is added when FACD topology was successfully installed on a *new* cluster (it's blocked for existing clusters)
AnnotationFACDTopology = pxAnnotationPrefix + "/facd-topology"
// AnnotationServerTLSMinVersion sets up TLS-servers w/ requested TLS as minimal version
AnnotationServerTLSMinVersion = pxAnnotationPrefix + "/tls-min-version"
// AnnotationServerTLSCipherSuites sets up TLS-servers w/ requested cipher suites
AnnotationServerTLSCipherSuites = pxAnnotationPrefix + "/tls-cipher-suites"

// EnvKeyPXImage key for the environment variable that specifies Portworx image
EnvKeyPXImage = "PX_IMAGE"
Expand Down Expand Up @@ -1122,3 +1127,63 @@ func IsFreshInstall(cluster *corev1.StorageCluster) bool {
(cluster.Status.Phase == string(corev1.ClusterStateDegraded) &&
util.GetStorageClusterCondition(cluster, PortworxComponentName, corev1.ClusterConditionTypeRuntimeState) == nil)
}

// GetTLSMinVersion gets requested TLS version and validates it
func GetTLSMinVersion(cluster *corev1.StorageCluster) (string, error) {
req := cluster.Annotations[AnnotationServerTLSMinVersion]
if req == "" {
return "", nil
}
req = strings.Trim(req, " \t")

switch strings.ToUpper(req) {
case "VERSIONTLS10":
return "VersionTLS10", nil
case "VERSIONTLS11":
return "VersionTLS11", nil
case "VERSIONTLS12":
return "VersionTLS12", nil
case "VERSIONTLS13":
return "VersionTLS13", nil
}

return "", fmt.Errorf("invalid TLS version: expected one of VersionTLS1{0..3}, got %s", req)
}

// GetTLSCipherSuites gets requested TLS ciphers suites and validates it
// - RETURN: the normalized comma-separated list of cipher suites, or error if requested unknown cipher
func GetTLSCipherSuites(cluster *corev1.StorageCluster) (string, error) {
req := cluster.Annotations[AnnotationServerTLSCipherSuites]
if req == "" {
return "", nil
}

csMap := make(map[string]bool)
for _, c := range tls.CipherSuites() {
csMap[c.Name] = true
}
icsMap := make(map[string]bool)
for _, c := range tls.InsecureCipherSuites() {
icsMap[c.Name] = true
}

req = strings.ToUpper(req)
parts := strings.FieldsFunc(req, func(r rune) bool {
return r == ' ' || r == '\t' || r == ':' || r == ';' || r == ','
})
outList := make([]string, 0, len(parts))

for _, p := range parts {
if p == "" {
// nop..
} else if _, has := csMap[p]; has {
outList = append(outList, p)
} else if _, has = icsMap[p]; has {
logrus.Warnf("Requested insecure cipher suite %s", p)
outList = append(outList, p)
} else {
return "", fmt.Errorf("unknown cipher suite %s", p)
}
}
return strings.Join(outList, ","), nil
}
97 changes: 97 additions & 0 deletions drivers/storage/portworx/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,100 @@ func TestGetPortworxVersion(t *testing.T) {
assert.True(t, GetPortworxVersion(cluster).Equal(pxVer30))

}

func TestGetTLSMinVersion(t *testing.T) {
cluster := &corev1.StorageCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "px-cluster",
Namespace: "kube-system",
Annotations: make(map[string]string),
},
Spec: corev1.StorageClusterSpec{},
}

ret, err := GetTLSMinVersion(cluster)
assert.NoError(t, err)
assert.Empty(t, ret)

data := []struct {
input string
expectOut string
expectErr bool
}{
{"", "", false},
{"VersionTLS10", "VersionTLS10", false},
{" VersionTLS10", "VersionTLS10", false},
{"VersionTLS10\t", "VersionTLS10", false},
{" VersionTLS11 ", "VersionTLS11", false},
{"VersionTLS12", "VersionTLS12", false},
{"VersionTLS13", "VersionTLS13", false},
{"versiontls13", "VersionTLS13", false},
{"VERSIONTLS13", "VersionTLS13", false},
{"VersionTLS14", "", true},
{"VersionTLS3", "", true},
}
for i, td := range data {
cluster.ObjectMeta.Annotations[AnnotationServerTLSMinVersion] = td.input
ret, err = GetTLSMinVersion(cluster)
if td.expectErr {
assert.Error(t, err, "Expecting error for #%d / %v", i+1, td)
} else {
assert.NoError(t, err, "Expecting NO error for #%d / %v", i+1, td)
assert.Equal(t, td.expectOut, ret, "Unexpected result for #%d / %v", i+1, td)
}
}
}

func TestGetTLSCipherSuites(t *testing.T) {
cluster := &corev1.StorageCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "px-cluster",
Namespace: "kube-system",
Annotations: make(map[string]string),
},
Spec: corev1.StorageClusterSpec{},
}

ret, err := GetTLSCipherSuites(cluster)
assert.NoError(t, err)
assert.Empty(t, ret)

data := []struct {
input string
expectOut string
expectErr bool
}{
{"", "", false},
{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
false,
},
{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
false,
},
{
" TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 :: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \t",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
false,
},
{
"tls_ecdhe_ecdsa_with_aes_256_gcm_sha384 tls_ecdhe_rsa_with_aes_256_gcm_sha384 tls_ecdhe_rsa_with_aes_128_gcm_sha256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
false,
},
{"x,y", "", true},
}
for i, td := range data {
cluster.ObjectMeta.Annotations[AnnotationServerTLSCipherSuites] = td.input
ret, err = GetTLSCipherSuites(cluster)
if td.expectErr {
assert.Error(t, err, "Expecting error for #%d / %v", i+1, td)
} else {
assert.NoError(t, err, "Expecting NO error for #%d / %v", i+1, td)
assert.Equal(t, td.expectOut, ret, "Unexpected result for #%d / %v", i+1, td)
}
}
}

0 comments on commit 1b0f23e

Please sign in to comment.