Skip to content

Commit 782878b

Browse files
committed
strip whitespace after decoding secret values
We have had several users report problems with credential management that turned out to be caused by extra whitespace in the encoded values placed into the secrets they created. Most often this is caused by using command line tools to encode the values. For example, $ echo -n root | base64 cm9vdA== $ echo root | base64 cm9vdAo= This change assumes that extraneous whitespace in the encoded values is not part of the real username or password, and strips it before trying to use the credentials. The logic to extract the credentials from the secret is moved into its own function so that new unit tests can be added. Signed-off-by: Doug Hellmann <dhellmann@redhat.com>
1 parent 60cbd24 commit 782878b

File tree

3 files changed

+80
-4
lines changed

3 files changed

+80
-4
lines changed

controllers/metal3.io/baremetalhost_controller.go

+15-4
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,20 @@ func (r *BareMetalHostReconciler) getBMCSecretAndSetOwner(request ctrl.Request,
12041204
return bmcCredsSecret, nil
12051205
}
12061206

1207+
func credentialsFromSecret(bmcCredsSecret *corev1.Secret) *bmc.Credentials {
1208+
// We trim surrounding whitespace because those characters are
1209+
// unlikely to be part of the username or password and it is
1210+
// common for users to encode the values with a command like
1211+
//
1212+
// echo "my-password" | base64
1213+
//
1214+
// which introduces a trailing newline.
1215+
return &bmc.Credentials{
1216+
Username: strings.TrimSpace(string(bmcCredsSecret.Data["username"])),
1217+
Password: strings.TrimSpace(string(bmcCredsSecret.Data["password"])),
1218+
}
1219+
}
1220+
12071221
// Make sure the credentials for the management controller look
12081222
// right and manufacture bmc.Credentials. This does not actually try
12091223
// to use the credentials.
@@ -1221,10 +1235,7 @@ func (r *BareMetalHostReconciler) buildAndValidateBMCCredentials(request ctrl.Re
12211235
return nil, nil, &EmptyBMCAddressError{message: "Missing BMC connection detail 'Address'"}
12221236
}
12231237

1224-
bmcCreds = &bmc.Credentials{
1225-
Username: string(bmcCredsSecret.Data["username"]),
1226-
Password: string(bmcCredsSecret.Data["password"]),
1227-
}
1238+
bmcCreds = credentialsFromSecret(bmcCredsSecret)
12281239

12291240
// Verify that the secret contains the expected info.
12301241
err = bmcCreds.Validate()

controllers/metal3.io/baremetalhost_controller_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2424

2525
metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
26+
"github.com/metal3-io/baremetal-operator/pkg/bmc"
2627
"github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture"
2728
"github.com/metal3-io/baremetal-operator/pkg/utils"
2829
)
@@ -2000,3 +2001,63 @@ func TestInvalidBMHCanBeDeleted(t *testing.T) {
20002001
return host.Status.Provisioning.State == metal3v1alpha1.StateDeleting && len(host.Finalizers) == 0
20012002
})
20022003
}
2004+
2005+
func TestCredentialsFromSecret(t *testing.T) {
2006+
cases := []struct {
2007+
name string
2008+
input corev1.Secret
2009+
expected bmc.Credentials
2010+
}{
2011+
{
2012+
name: "empty",
2013+
input: corev1.Secret{},
2014+
expected: bmc.Credentials{},
2015+
},
2016+
{
2017+
name: "clean",
2018+
input: corev1.Secret{
2019+
Data: map[string][]byte{
2020+
"username": []byte("username"),
2021+
"password": []byte("password"),
2022+
},
2023+
},
2024+
expected: bmc.Credentials{
2025+
Username: "username",
2026+
Password: "password",
2027+
},
2028+
},
2029+
{
2030+
name: "newline",
2031+
input: corev1.Secret{
2032+
Data: map[string][]byte{
2033+
"username": []byte("username\n"),
2034+
"password": []byte("password\n"),
2035+
},
2036+
},
2037+
expected: bmc.Credentials{
2038+
Username: "username",
2039+
Password: "password",
2040+
},
2041+
},
2042+
{
2043+
name: "non-newline",
2044+
input: corev1.Secret{
2045+
Data: map[string][]byte{
2046+
"username": []byte(" username\t"),
2047+
"password": []byte(" password\t"),
2048+
},
2049+
},
2050+
expected: bmc.Credentials{
2051+
Username: "username",
2052+
Password: "password",
2053+
},
2054+
},
2055+
}
2056+
2057+
for _, c := range cases {
2058+
t.Run(c.name, func(t *testing.T) {
2059+
actual := credentialsFromSecret(&c.input)
2060+
assert.Equal(t, c.expected, *actual)
2061+
})
2062+
}
2063+
}

docs/api.md

+4
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ data:
475475
password: cGFzc3dvcmQ=
476476
```
477477

478+
NOTE: After decoding the secret content, whitespace is stripped from
479+
the beginning and end before the username and password values are
480+
used.
481+
478482
## Triggering Provisioning
479483

480484
Several conditions must be met in order to initiate provisioning.

0 commit comments

Comments
 (0)