Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify parsing behavior of login credentials to handle Go escape characters #564

Merged
merged 5 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/vmware-tanzu/velero v1.10.2
github.com/vmware/govmomi v0.22.2-0.20200329013745-f2eef8fc745f
github.com/vmware/virtual-disks v0.0.4
gopkg.in/gcfg.v1 v1.2.3
k8s.io/api v0.24.2
k8s.io/apiextensions-apiserver v0.24.2
k8s.io/apimachinery v0.24.2
Expand Down Expand Up @@ -152,6 +153,7 @@ require (
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/cli-runtime v0.24.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
Expand All @@ -1401,6 +1403,8 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
20 changes: 20 additions & 0 deletions pkg/common/config/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package config

type Config struct {
Global struct {
VCenterIP string `gcfg:"ip"`
ClusterID string `gcfg:"cluster-id"`
ClusterDistribution string `gcfg:"cluster-distribution"`
CAFile string `gcfg:"ca-file"`
}
// Virtual Center configurations
VirtualCenter map[string]*VirtualCenterConfig
}

type VirtualCenterConfig struct {
User string `gcfg:"user"`
Password string `gcfg:"password"`
VCenterPort string `gcfg:"port"`
InsecureFlag bool `gcfg:"insecure-flag"`
Datacenters string `gcfg:"datacenters"`
}
65 changes: 34 additions & 31 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ import (
"time"

"github.com/hashicorp/go-version"
vcConfig "github.com/vmware-tanzu/velero-plugin-for-vsphere/pkg/common/config"
"github.com/vmware-tanzu/velero-plugin-for-vsphere/pkg/constants"
"github.com/vmware-tanzu/velero-plugin-for-vsphere/pkg/generated/clientset/versioned/typed/backupdriver/v1alpha1"
"github.com/vmware-tanzu/velero-plugin-for-vsphere/pkg/ivd"
"gopkg.in/gcfg.v1"
"k8s.io/client-go/tools/clientcmd"

"k8s.io/apimachinery/pkg/fields"
Expand Down Expand Up @@ -137,43 +139,44 @@ func RetrieveVcConfigSecret(params map[string]interface{}, config *rest.Config,
return errors.New(errMsg)
}

var sEnc string
var lines []string
for _, value := range secret.Data {
sEnc = string(value)
lines = strings.Split(sEnc, "\n")
logger.Debugf("Successfully retrieved vCenter configuration from secret %s", secret.Name)
break
err = ParseConfig(secret, params, logger)
if err != nil {
logger.WithError(err).Error("Failed to parse vSphere secret data")
return err
}

ParseLines(lines, params, logger)

// If port is missing, add an entry in the params to use the standard https port
if _, ok := params["port"]; !ok {
params["port"] = constants.DefaultVCenterPort
}
return nil
}

func ParseLines(lines []string, params map[string]interface{}, logger logrus.FieldLogger) {
for _, line := range lines {
if strings.Contains(line, "VirtualCenter") {
parts := strings.Split(line, "\"")
params["VirtualCenter"] = parts[1]
} else if strings.Contains(line, "=") {
parts := strings.SplitN(line, "=", 2)
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
// Skip the quotes in the value if present
unquotedValue, err := strconv.Unquote(string(value))
if err != nil {
logger.WithError(err).Debugf("Failed to unquote value %v for key %v. Just store the original value string", value, key)
params[key] = string(value)
continue
}
params[key] = unquotedValue
func ParseConfig(secret *k8sv1.Secret, params map[string]interface{}, logger logrus.FieldLogger) error {
// Setup config
var conf vcConfig.Config
// Read secret data
for _, value := range secret.Data {
confStr := string(value) // Convert the secret data to a string
err := gcfg.FatalOnly(gcfg.ReadStringInto(&conf, confStr))
if err != nil {
logger.WithError(err).Error("Failed to parse vSphere secret data")
return err
}
logger.Debugf("Successfully parsed vCenter configuration from secret %s", secret.Name)
break
}

// Use config data from struct to populate params map passed by RetrieveVcConfigSecret callers
params["cluster-id"] = conf.Global.ClusterID
varunsrinivasan2 marked this conversation as resolved.
Show resolved Hide resolved

for ip, vcConfig := range conf.VirtualCenter {
params["VirtualCenter"] = ip
params["user"] = vcConfig.User
params["password"] = vcConfig.Password
if vcConfig.VCenterPort == "" {
vcConfig.VCenterPort = constants.DefaultVCenterPort
}
params["port"] = vcConfig.VCenterPort
}

return nil
}

func RetrieveParamsFromBSL(repositoryParams map[string]string, bslName string, config *rest.Config,
Expand Down Expand Up @@ -1022,7 +1025,7 @@ func waitForPvSecret(ctx context.Context, clientSet *kubernetes.Clientset, names
}

/*
Adds the Velero label to exclude this K8S resource from the backup
Adds the Velero label to exclude this K8S resource from the backup
*/
func AddVeleroExcludeLabelToObjectMeta(objectMeta *metav1.ObjectMeta) {
objectMeta.Labels = AppendVeleroExcludeLabels(objectMeta.Labels)
Expand Down
60 changes: 40 additions & 20 deletions pkg/utils/utils_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ cluster-distribution = "CSI-Vanilla"

[VirtualCenter "10.182.1.133"]
user = "Administrator@vsphere.local"
password = "Pass"
password = "6^54#,RDvwgJ\\nEdg$2"
datacenters = "VSAN-DC"
port = "443"
`
Expand Down Expand Up @@ -361,7 +361,7 @@ func TestGetBool(t *testing.T) {
}
}

func TestParseLines(t *testing.T) {
func TestParseConfig(t *testing.T) {
// Setup Logger
logger := logrus.New()
formatter := new(logrus.TextFormatter)
Expand All @@ -372,34 +372,32 @@ func TestParseLines(t *testing.T) {

tests := []struct {
name string
sEnc string
vc string
user string
password string
}{
{
name: "Password with special character \\ in it",
sEnc: "[VirtualCenter \"sc-rdops-vm06-dhcp-184-231.eng.vmware.com\"]\npassword = \"GpI4G`OK'?in40Fo/0\\\\;\"",
name: `Password with special character \ in it`,
vc: "sc-rdops-vm06-dhcp-184-231.eng.vmware.com",
password: "GpI4G`OK'?in40Fo/0\\;",
},
{
name: "Password with multiple = in it",
sEnc: "[VirtualCenter \"sc-rdops-vm06-dhcp-184-231.eng.vmware.com\"]\npassword = \"GpI4G`OK'?in40Fo/0\\\\;=h=\"",
vc: "sc-rdops-vm06-dhcp-184-231.eng.vmware.com",
password: "GpI4G`OK'?in40Fo/0\\;=h=",
},
{
name: "Password with special character \\t in it",
sEnc: "[VirtualCenter \"sc-rdops-vm06-dhcp-184-231.eng.vmware.com\"]\npassword = \"G4\\t4t\"",
vc: "sc-rdops-vm06-dhcp-184-231.eng.vmware.com",
password: "G4\t4t",
user: "Administrator@vsphere.local",
password: `6^54#,RDvwgJ\Edg$2`,
},
}
confData := `[Global]
cluster-id = "cluster1"

[VirtualCenter "sc-rdops-vm06-dhcp-184-231.eng.vmware.com"]
user = "Administrator@vsphere.local"
password = "6^54#,RDvwgJ\\Edg$2"
port = "443"`
mockSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "velero-vsphere-config-secret"},
Data: map[string][]byte{"csi-vsphere.conf": []byte(confData)},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
lines := strings.Split(test.sEnc, "\n")
params := make(map[string]interface{})
ParseLines(lines, params, logger)
ParseConfig(mockSecret, params, logger)
assert.Equal(t, test.vc, params["VirtualCenter"])
assert.Equal(t, test.password, params["password"])
})
Expand Down Expand Up @@ -980,12 +978,34 @@ func TestRetrieveVcConfigSecret(t *testing.T) {
formatter.FullTimestamp = true
logger.SetFormatter(formatter)
logger.SetLevel(logrus.DebugLevel)
confData := `[Global]
cluster-id = "cluster1"

[VirtualCenter "10.182.1.133"]
user = "user@vsphere.local"
password = "password"
port = "443"`
mockSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "velero-vsphere-config-secret"},
Data: map[string][]byte{"csi-vsphere.conf": []byte(confData)},
}
tests := []struct {
name string
runtimeObjs []runtime.Object
config *rest.Config
expectError bool
}{
{
name: "Test secret retrieval",
runtimeObjs: []runtime.Object{
decoupleVSphereCSIDriverFeatureDisabled,
csi201VanillaDeployment,
csi201VSphereCredentialSecret,
mockSecret,
varunsrinivasan2 marked this conversation as resolved.
Show resolved Hide resolved
},
config: &rest.Config{},
expectError: false,
},
{
name: "Decouple CSI driver feature enabled, plugin config absent, supervisor csi 2.3, csi secret present",
runtimeObjs: []runtime.Object{
Expand Down
Loading