Skip to content

Commit 1fa6ffe

Browse files
authored
Merge pull request #371 from tam7t/tam7t/targetPath
fix: validate SPCPS targetPaths match Pod UIDs
2 parents 69c2fd5 + c2cbb19 commit 1fa6ffe

File tree

13 files changed

+645
-133
lines changed

13 files changed

+645
-133
lines changed

controllers/secretproviderclasspodstatus_controller.go

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package controllers
1919
import (
2020
"context"
2121
"fmt"
22-
"regexp"
2322
"strings"
2423
"sync"
2524
"time"
@@ -35,6 +34,7 @@ import (
3534
"sigs.k8s.io/secrets-store-csi-driver/apis/v1alpha1"
3635
"sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/scheme"
3736
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/fileutil"
37+
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/k8sutil"
3838
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/secretutil"
3939

4040
ctrl "sigs.k8s.io/controller-runtime"
@@ -227,18 +227,34 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
227227
return ctrl.Result{}, nil
228228
}
229229

230-
// podObjectReference is an object reference to the pod that spc pod status
231-
// is created for. The object reference is created with minimal required fields
232-
// name, namespace and UID. By doing this we can skip an additional client call
233-
// to fetch the pod object
234-
podObjectReference, err := getPodObjectReference(spcPodStatus)
235-
if err != nil {
236-
logger.Errorf("failed to get pod object reference, error: %+v", err)
230+
// Obtain the full pod metadata. An object reference is needed for sending
231+
// events and the UID is helpful for validating the SPCPS TargetPath.
232+
pod := &v1.Pod{}
233+
if err := r.reader.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: spcPodStatus.Status.PodName}, pod); err != nil {
234+
logger.Errorf("failed to get pod %s/%s, err: %+v", req.Namespace, spcPodStatus.Status.PodName, err)
235+
if apierrors.IsNotFound(err) {
236+
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
237+
}
238+
return ctrl.Result{}, err
239+
}
240+
241+
// determine which pod volume this is associated with
242+
podVol := k8sutil.SPCVolume(pod, spc.Name)
243+
if podVol == nil {
244+
return ctrl.Result{}, fmt.Errorf("failed to find secret provider class pod status volume for pod %s/%s", req.Namespace, spcPodStatus.Status.PodName)
245+
}
246+
247+
// validate TargetPath
248+
if fileutil.GetPodUIDFromTargetPath(spcPodStatus.Status.TargetPath) != string(pod.UID) {
249+
return ctrl.Result{}, fmt.Errorf("secret provider class pod status targetPath did not match pod UID for pod %s/%s", req.Namespace, spcPodStatus.Status.PodName)
250+
}
251+
if fileutil.GetVolumeNameFromTargetPath(spcPodStatus.Status.TargetPath) != podVol.Name {
252+
return ctrl.Result{}, fmt.Errorf("secret provider class pod status volume name did not match pod Volume for pod %s/%s", req.Namespace, spcPodStatus.Status.PodName)
237253
}
238254

239255
files, err := fileutil.GetMountedFiles(spcPodStatus.Status.TargetPath)
240256
if err != nil {
241-
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get mounted files, err: %+v", err))
257+
r.generateEvent(pod, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get mounted files, err: %+v", err))
242258
logger.Errorf("failed to get mounted files, err: %+v", err)
243259
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
244260
}
@@ -265,7 +281,7 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
265281

266282
datamap := make(map[string][]byte)
267283
if datamap, err = secretutil.GetSecretData(secretObj.Data, secretType, files); err != nil {
268-
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err))
284+
r.generateEvent(pod, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err))
269285
log.Errorf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err)
270286
errs = append(errs, fmt.Errorf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err))
271287
continue
@@ -297,7 +313,7 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
297313
Factor: 1.0,
298314
Jitter: 0.1,
299315
}, f); err != nil {
300-
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, err.Error())
316+
r.generateEvent(pod, corev1.EventTypeWarning, secretCreationFailedReason, err.Error())
301317
return ctrl.Result{RequeueAfter: 5 * time.Second}, err
302318
}
303319
}
@@ -401,31 +417,6 @@ func (r *SecretProviderClassPodStatusReconciler) secretExists(ctx context.Contex
401417
return false, err
402418
}
403419

404-
// getPodObjectReference returns a v1.ObjectReference for the pod object
405-
func getPodObjectReference(spcPodStatus v1alpha1.SecretProviderClassPodStatus) (*v1.ObjectReference, error) {
406-
podName := spcPodStatus.Status.PodName
407-
podNamespace := spcPodStatus.Namespace
408-
podUID := getPodUIDFromTargetPath(spcPodStatus.Status.TargetPath)
409-
if podUID == "" {
410-
return nil, fmt.Errorf("failed to get pod UID from target path")
411-
}
412-
return &v1.ObjectReference{
413-
Name: podName,
414-
Namespace: podNamespace,
415-
UID: types.UID(podUID),
416-
}, nil
417-
}
418-
419-
// getPodUIDFromTargetPath returns podUID from targetPath
420-
func getPodUIDFromTargetPath(targetPath string) string {
421-
re := regexp.MustCompile(`[\\|\/]+pods[\\|\/]+(.+?)[\\|\/]+volumes`)
422-
match := re.FindStringSubmatch(targetPath)
423-
if len(match) < 2 {
424-
return ""
425-
}
426-
return match[1]
427-
}
428-
429420
// generateEvent generates an event
430421
func (r *SecretProviderClassPodStatusReconciler) generateEvent(obj runtime.Object, eventType, reason, message string) {
431422
if obj != nil {

controllers/secretproviderclasspodstatus_controller_test.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -172,49 +172,6 @@ func TestCreateK8sSecret(t *testing.T) {
172172
g.Expect(secret.Name).To(Equal("my-secret2"))
173173
}
174174

175-
func TestGetPodUIDFromTargetPath(t *testing.T) {
176-
g := NewWithT(t)
177-
178-
cases := []struct {
179-
targetPath string
180-
expectedPodUID string
181-
}{
182-
{
183-
targetPath: "/var/lib/kubelet/pods/7e7686a1-56c4-4c67-a6fd-4656ac484f0a/volumes/",
184-
expectedPodUID: "7e7686a1-56c4-4c67-a6fd-4656ac484f0a",
185-
},
186-
{
187-
targetPath: `c:\var\lib\kubelet\pods\d4fd876f-bdb3-11e9-a369-0a5d188d99c0\volumes`,
188-
expectedPodUID: "d4fd876f-bdb3-11e9-a369-0a5d188d99c0",
189-
},
190-
{
191-
targetPath: `c:\\var\\lib\\kubelet\\pods\\d4fd876f-bdb3-11e9-a369-0a5d188d9934\\volumes`,
192-
expectedPodUID: "d4fd876f-bdb3-11e9-a369-0a5d188d9934",
193-
},
194-
{
195-
targetPath: "/var/lib/",
196-
expectedPodUID: "",
197-
},
198-
{
199-
targetPath: "/var/lib/kubelet/pods",
200-
expectedPodUID: "",
201-
},
202-
{
203-
targetPath: "/opt/new/var/lib/kubelet/pods/456457fc-d980-4191-b5eb-daf70c4ff7c1/volumes/kubernetes.io~csi/secrets-store-inline/mount",
204-
expectedPodUID: "456457fc-d980-4191-b5eb-daf70c4ff7c1",
205-
},
206-
{
207-
targetPath: "data/kubelet/pods/456457fc-d980-4191-b5eb-daf70c4ff7c1/volumes/kubernetes.io~csi/secrets-store-inline/mount",
208-
expectedPodUID: "456457fc-d980-4191-b5eb-daf70c4ff7c1",
209-
},
210-
}
211-
212-
for _, tc := range cases {
213-
actualPodUID := getPodUIDFromTargetPath(tc.targetPath)
214-
g.Expect(actualPodUID).To(Equal(tc.expectedPodUID))
215-
}
216-
}
217-
218175
func TestGenerateEvent(t *testing.T) {
219176
g := NewWithT(t)
220177

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/blang/semver v3.5.0+incompatible
77
github.com/container-storage-interface/spec v1.0.0
88
github.com/golang/protobuf v1.4.2
9+
github.com/google/go-cmp v0.5.0
910
github.com/kubernetes-csi/csi-lib-utils v0.6.1
1011
github.com/kubernetes-csi/csi-test v1.1.0
1112
github.com/onsi/gomega v1.8.1

pkg/errors/errors.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,9 @@ const (
4040
// NodePublishSecretRefNotFound error
4141
// #nosec G101 (Ref: https://github.com/securego/gosec/issues/295)
4242
NodePublishSecretRefNotFound = "NodePublishSecretRefNotFound"
43+
// UnexpectedTargetPath error
44+
// Indicated SecretProviderClassPodStatus status.targetPath is an invalid value.
45+
UnexpectedTargetPath = "UnexpectedTargetPath"
46+
// PodVolumeNotFound error
47+
PodVolumeNotFound = "PodVolumeNotFound"
4348
)

pkg/rotation/reconciler.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030

3131
internalerrors "sigs.k8s.io/secrets-store-csi-driver/pkg/errors"
3232
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/fileutil"
33+
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/k8sutil"
3334
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/secretutil"
3435

3536
"k8s.io/client-go/tools/cache"
@@ -196,6 +197,23 @@ func (r *Reconciler) reconcile(ctx context.Context, spcps *v1alpha1.SecretProvid
196197
return fmt.Errorf("failed to get pod %s/%s, err: %+v", podNamespace, podName, err)
197198
}
198199

200+
// determine which pod volume this is associated with
201+
podVol := k8sutil.SPCVolume(pod, spc.Name)
202+
if podVol == nil {
203+
errorReason = internalerrors.PodVolumeNotFound
204+
return fmt.Errorf("could not find secret provider class pod status volume for pod %s/%s", podNamespace, podName)
205+
}
206+
207+
// validate TargetPath
208+
if fileutil.GetPodUIDFromTargetPath(spcps.Status.TargetPath) != string(pod.UID) {
209+
errorReason = internalerrors.UnexpectedTargetPath
210+
return fmt.Errorf("secret provider class pod status targetPath did not match pod UID for pod %s/%s", podNamespace, podName)
211+
}
212+
if fileutil.GetVolumeNameFromTargetPath(spcps.Status.TargetPath) != podVol.Name {
213+
errorReason = internalerrors.UnexpectedTargetPath
214+
return fmt.Errorf("secret provider class pod status volume name did not match pod Volume for pod %s/%s", podNamespace, podName)
215+
}
216+
199217
parameters := make(map[string]string)
200218
if spc.Spec.Parameters != nil {
201219
parameters = spc.Spec.Parameters
@@ -217,20 +235,7 @@ func (r *Reconciler) reconcile(ctx context.Context, spcps *v1alpha1.SecretProvid
217235

218236
// check if the volume pertaining to the current spc is using nodePublishSecretRef for
219237
// accessing external secrets store
220-
var nodePublishSecretRef *v1.LocalObjectReference
221-
for _, vol := range pod.Spec.Volumes {
222-
if vol.CSI == nil {
223-
continue
224-
}
225-
if vol.CSI.Driver != "secrets-store.csi.k8s.io" {
226-
continue
227-
}
228-
if vol.CSI.VolumeAttributes["secretProviderClass"] != spc.Name {
229-
continue
230-
}
231-
nodePublishSecretRef = vol.CSI.NodePublishSecretRef
232-
break
233-
}
238+
nodePublishSecretRef := podVol.CSI.NodePublishSecretRef
234239

235240
var secretsJSON []byte
236241
nodePublishSecretData := make(map[string]string)

0 commit comments

Comments
 (0)