Skip to content

Commit 64eb7cf

Browse files
committed
Disallow fetching secrets from namespaces different from the host's one
The BareMetalHost CRD allows the UserData, MetaData, and NetworkData for the provisioned host to be specified as links to k8s Secrets. There are fields for both the Name and Namespace of the Secret, meaning that the baremetal-operator will read a Secret from any namespace. If a Secret contains the key "value" (or "userData", "metaData", or "networkData"), its corresponding value can be exfiltrated by a user provisioning a Host pointing to that Secret, then retrieving that data from the provisioned host. Authored-by: Zane Bitter <[email protected]> Co-Authored-By: Dmitry Tantsur <[email protected]> Signed-off-by: Tuomo Tanskanen <[email protected]>
1 parent 1941c2a commit 64eb7cf

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

controllers/metal3.io/baremetalhost_controller_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const (
3737
)
3838

3939
func newSecret(name string, data map[string]string) *corev1.Secret {
40+
return newSecretInNamespace(name, namespace, data)
41+
}
42+
43+
func newSecretInNamespace(name, namespace string, data map[string]string) *corev1.Secret {
4044
secretData := make(map[string][]byte)
4145
for k, v := range data {
4246
secretData[k] = []byte(base64.StdEncoding.EncodeToString([]byte(v)))

controllers/metal3.io/host_config_data.go

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/go-logr/logr"
55
metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
66
"github.com/metal3-io/baremetal-operator/pkg/secretutils"
7+
"github.com/pkg/errors"
78
corev1 "k8s.io/api/core/v1"
89
"k8s.io/apimachinery/pkg/types"
910
)
@@ -20,6 +21,10 @@ type hostConfigData struct {
2021
// parameter to detirmine which data to return in case secret contins multiple
2122
// keys.
2223
func (hcd *hostConfigData) getSecretData(name, namespace, dataKey string) (string, error) {
24+
if namespace != hcd.host.Namespace {
25+
return "", errors.Errorf("%s secret must be in same namespace as host %s/%s", dataKey, hcd.host.Namespace, hcd.host.Name)
26+
}
27+
2328
key := types.NamespacedName{
2429
Name: name,
2530
Namespace: namespace,

controllers/metal3.io/host_config_data_test.go

+49-1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,54 @@ func TestProvisionWithHostConfig(t *testing.T) {
323323
ExpectedNetworkData: "",
324324
ErrNetworkData: true,
325325
},
326+
{
327+
Scenario: "user-data secret in different namespace",
328+
Host: newHost("host-user-data",
329+
&metal3api.BareMetalHostSpec{
330+
BMC: metal3api.BMCDetails{
331+
Address: "ipmi://192.168.122.1:6233",
332+
CredentialsName: defaultSecretName,
333+
},
334+
UserData: &corev1.SecretReference{
335+
Name: "user-data",
336+
Namespace: "other-namespace",
337+
},
338+
}),
339+
UserDataSecret: newSecretInNamespace("user-data", "other-namespace", map[string]string{"userData": "somedata"}),
340+
ErrUserData: true,
341+
},
342+
{
343+
Scenario: "meta-data secret in different namespace",
344+
Host: newHost("host-user-data",
345+
&metal3api.BareMetalHostSpec{
346+
BMC: metal3api.BMCDetails{
347+
Address: "ipmi://192.168.122.1:6233",
348+
CredentialsName: defaultSecretName,
349+
},
350+
MetaData: &corev1.SecretReference{
351+
Name: "meta-data",
352+
Namespace: "other-namespace",
353+
},
354+
}),
355+
NetworkDataSecret: newSecretInNamespace("meta-data", "other-namespace", map[string]string{"metaData": "key: value"}),
356+
ErrMetaData: true,
357+
},
358+
{
359+
Scenario: "network-data secret in different namespace",
360+
Host: newHost("host-user-data",
361+
&metal3api.BareMetalHostSpec{
362+
BMC: metal3api.BMCDetails{
363+
Address: "ipmi://192.168.122.1:6233",
364+
CredentialsName: defaultSecretName,
365+
},
366+
NetworkData: &corev1.SecretReference{
367+
Name: "net-data",
368+
Namespace: "other-namespace",
369+
},
370+
}),
371+
NetworkDataSecret: newSecretInNamespace("net-data", "other-namespace", map[string]string{"networkData": "key: value"}),
372+
ErrNetworkData: true,
373+
},
326374
}
327375

328376
for _, tc := range testCases {
@@ -378,7 +426,7 @@ func TestProvisionWithHostConfig(t *testing.T) {
378426
}
379427

380428
if actualMetaData != tc.ExpectedMetaData {
381-
t.Fatal(fmt.Errorf("Failed to assert MetaData. Expected '%s' got '%s'", actualMetaData, tc.ExpectedMetaData))
429+
t.Fatal(fmt.Errorf("Failed to assert MetaData. Expected '%s' got '%s'", tc.ExpectedMetaData, actualMetaData))
382430
}
383431
})
384432
}

0 commit comments

Comments
 (0)