Skip to content

Commit

Permalink
pb-6681: Add support for PSA in kdmp job
Browse files Browse the repository at this point in the history
- Added check and collected psa info from the namespace
- if psa enabled then extracted the uid and gid used by the POD.
  here the pod is choosen based on whichever PVC is used by that pod
- applied those uid and GID to all relevant job pod spec
- this is done only if the PSA mode enforced with "restricted" value
- for baseline and privilege mode no restriction on UID/GID and
  default setting of SElinux and secomp is adopted in the POD spec

Signed-off-by: Lalatendu Das <[email protected]>
  • Loading branch information
lalat-das committed May 16, 2024
1 parent 52b07b4 commit 567add1
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 2 deletions.
31 changes: 31 additions & 0 deletions pkg/drivers/kopiabackup/kopiabackup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
)

var backupJobLock sync.Mutex
Expand Down Expand Up @@ -318,6 +319,17 @@ func jobFor(
logrus.Errorf("failed to get the toleration details: %v", err)
return nil, fmt.Errorf("failed to get the toleration details for job [%s/%s]", jobOption.Namespace, jobName)
}
// get dataexport CR and read psa specific labels.
de, err := kdmpops.Instance().GetDataExport(context.Background(), jobOption.DataExportName, jobOption.Namespace)
if err != nil {
errMsg := fmt.Sprintf("failed to get dataexport CR %s/%s: %v", jobOption.Namespace, jobOption.DataExportName, err)
logrus.Errorf("%v", errMsg)
return nil, fmt.Errorf(errMsg)
}
psaIsEnabled := de.Annotations[utils.PsaEnabledKey]
podUserId := de.Annotations[utils.PsaUIDKey]
podGroupId := de.Annotations[utils.PsaGIDKey]

job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: jobName,
Expand Down Expand Up @@ -359,6 +371,18 @@ func jobFor(
ReadOnly: true,
},
},
SecurityContext: &corev1.SecurityContext{
RunAsNonRoot: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
},
},
},
Tolerations: tolerations,
Expand All @@ -384,6 +408,13 @@ func jobFor(
},
},
}
// Add security Context only if the PSA is enabled.
if psaIsEnabled == "true" {
job, err = utils.AddSecurityContextToJob(job, podUserId, podGroupId)
if err != nil {
return nil, err
}
}

if len(nodeName) != 0 {
job.Spec.Template.Spec.NodeName = nodeName
Expand Down
33 changes: 32 additions & 1 deletion pkg/drivers/kopiarestore/kopiarestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
)

var restoreJobLock sync.Mutex
Expand Down Expand Up @@ -219,6 +220,18 @@ func jobFor(
logrus.Errorf("failed to get the toleration details: %v", err)
return nil, fmt.Errorf("failed to get the toleration details for job [%s/%s]", jobOption.Namespace, jobName)
}

// get dataexport CR from itds name and namespace
de, err := kdmpops.Instance().GetDataExport(context.Background(), jobOption.DataExportName, jobOption.Namespace)
if err != nil {
errMsg := fmt.Sprintf("failed to get dataexport CR %s/%s: %v", jobOption.Namespace, jobOption.DataExportName, err)
logrus.Errorf("%v", errMsg)
return nil, fmt.Errorf(errMsg)
}
psaIsEnabled := de.Annotations[utils.PsaEnabledKey]
podUserId := de.Annotations[utils.PsaUIDKey]
podGroupId := de.Annotations[utils.PsaGIDKey]

job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: jobName,
Expand Down Expand Up @@ -260,6 +273,18 @@ func jobFor(
ReadOnly: true,
},
},
SecurityContext: &corev1.SecurityContext{
RunAsNonRoot: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
},
},
},
Tolerations: tolerations,
Expand All @@ -285,7 +310,13 @@ func jobFor(
},
},
}

// Add security Context only if the PSA is enabled.
if psaIsEnabled == "true" {
job, err = utils.AddSecurityContextToJob(job, podUserId, podGroupId)
if err != nil {
return nil, err
}
}
// Add the image secret in job spec only if it is present in the stork deployment.
if len(imageRegistrySecret) != 0 {
job.Spec.Template.Spec.ImagePullSecrets = utils.ToImagePullSecret(utils.GetImageSecretName(jobName))
Expand Down
31 changes: 30 additions & 1 deletion pkg/drivers/nfsbackup/nfsbackup.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
)

// Driver is a nfsbackup implementation of the data export interface.
Expand Down Expand Up @@ -199,7 +200,6 @@ func jobForBackupResource(
}, " ")

labels := addJobLabels(jobOption)

nfsExecutorImage, imageRegistrySecret, err := utils.GetExecutorImageAndSecret(drivers.NfsExecutorImage,
jobOption.NfsImageExecutorSource,
jobOption.NfsImageExecutorSourceNs,
Expand All @@ -216,6 +216,7 @@ func jobForBackupResource(
logrus.Errorf("failed to get the toleration details: %v", err)
return nil, fmt.Errorf("failed to get the toleration details for job [%s/%s]", jobOption.Namespace, jobOption.RestoreExportName)
}

job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: jobOption.RestoreExportName,
Expand Down Expand Up @@ -253,6 +254,18 @@ func jobForBackupResource(
ReadOnly: true,
},
},
SecurityContext: &corev1.SecurityContext{
RunAsNonRoot: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
},
},
},
Tolerations: tolerations,
Expand All @@ -270,6 +283,22 @@ func jobForBackupResource(
},
},
}
// get dataexport CR and read psa specific labels.
re, err := kdmp.Instance().GetResourceExport(jobOption.RestoreExportName, jobOption.Namespace)
if err != nil {
errMsg := fmt.Sprintf("failed to get resourceExport CR %s/%s: %v", jobOption.Namespace,jobOption.RestoreExportName, err)
logrus.Errorf("%v", errMsg)
return nil, fmt.Errorf(errMsg)
}
psaIsEnabled := re.Annotations[utils.PsaEnabledKey]
// Add security Context only if the PSA is enabled.
if psaIsEnabled == "true" {
job, err = utils.AddSecurityContextToJob(job, "", "")
if err != nil {
return nil, err
}
}

// Add the image secret in job spec only if it is present in the stork deployment.
if len(imageRegistrySecret) != 0 {
job.Spec.Template.Spec.ImagePullSecrets = utils.ToImagePullSecret(utils.GetImageSecretName(jobOption.RestoreExportName))
Expand Down
32 changes: 32 additions & 0 deletions pkg/drivers/nfscsirestore/nfscsirestore.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package nfscsirestore

import (
"context"
"fmt"
"strings"

"github.com/portworx/kdmp/pkg/drivers"
"github.com/portworx/kdmp/pkg/drivers/utils"
"github.com/portworx/sched-ops/k8s/batch"

kdmpops "github.com/portworx/kdmp/pkg/util/ops"
"github.com/sirupsen/logrus"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
)

// Driver is a nfsbackup implementation of the data export interface.
Expand Down Expand Up @@ -211,6 +214,16 @@ func jobForRestoreCSISnapshot(
logrus.Errorf("failed to get the toleration details: %v", err)
return nil, fmt.Errorf("failed to get the toleration details for job [%s/%s]", jobOption.Namespace, jobOption.DataExportName)
}
// get dataexport CR and read psa specific labels.
de, err := kdmpops.Instance().GetDataExport(context.Background(), jobOption.DataExportName, jobOption.Namespace)
if err != nil {
errMsg := fmt.Sprintf("failed to get dataexport CR %s/%s: %v", jobOption.Namespace, jobOption.DataExportName, err)
logrus.Errorf("%v", errMsg)
return nil, fmt.Errorf(errMsg)
}
psaIsEnabled := de.Annotations[utils.PsaEnabledKey]
podUserId := de.Annotations[utils.PsaUIDKey]
podGroupId := de.Annotations[utils.PsaGIDKey]

job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -249,6 +262,18 @@ func jobForRestoreCSISnapshot(
ReadOnly: true,
},
},
SecurityContext: &corev1.SecurityContext{
RunAsNonRoot: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
},
},
},
Tolerations: tolerations,
Expand All @@ -266,6 +291,13 @@ func jobForRestoreCSISnapshot(
},
},
}
// Add security Context only if the PSA is enabled.
if psaIsEnabled == "true" {
job, err = utils.AddSecurityContextToJob(job, podUserId, podGroupId)
if err != nil {
return nil, err
}
}
// Add the image secret in job spec only if it is present in the stork deployment.
if len(imageRegistrySecret) != 0 {
job.Spec.Template.Spec.ImagePullSecrets = utils.ToImagePullSecret(utils.GetImageSecretName(jobName))
Expand Down
48 changes: 48 additions & 0 deletions pkg/drivers/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/utils/pointer"
)

const (
Expand Down Expand Up @@ -67,6 +68,11 @@ const (
IstioInjectLabel = "sidecar.istio.io/inject"
// ProcessVMResourceSuccessMsg - vm resources processed successfully
ProcessVMResourceSuccessMsg = "vm resources processed successfully"
PsaEnabledKey = "portworx.io/psa-enabled"
PsaUIDKey = "portworx.io/psa-uid"
PsaGIDKey = "portworx.io/psa-gid"
kdmpJobUid = 1013
kdmpJobGid = 1013
)

var (
Expand Down Expand Up @@ -966,3 +972,45 @@ func GetShortUID(uid string) string {
}
return uid[:8]
}

// Add container security Context to job pod if the PSA is enabled.
func AddSecurityContextToJob(job *batchv1.Job, podUserId, podGroupId string) (*batchv1.Job, error) {
if job != nil {
if podUserId != "" {
uid, err := strconv.ParseInt(podUserId, 10, 64)
if err != nil {
logrus.Errorf("failed to convert the UID to int: %v", err)
return nil, fmt.Errorf("failed to convert the UID to int: %v", err)
}
job.Spec.Template.Spec.SecurityContext.RunAsUser = &uid
} else {
// we need to add a default user id if the user id is not provided
job.Spec.Template.Spec.SecurityContext.RunAsUser = pointer.Int64(kdmpJobUid)
}
if podGroupId != "" {
gid, err := strconv.ParseInt(podGroupId, 10, 64)
if err != nil {
logrus.Errorf("failed to convert the GID to int: %v", err)
return nil, fmt.Errorf("failed to convert the GID to int: %v", err)
}
job.Spec.Template.Spec.SecurityContext.RunAsGroup = &gid
} else {
// we need to add a default group id if the group id is not provided
job.Spec.Template.Spec.SecurityContext.RunAsGroup = pointer.Int64(kdmpJobGid)
}
// Add RunAsNonRoot to true and drop all capabilities and seccomp profile and allowPrivilegeEscalation to false
job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot = pointer.Bool(true)
job.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
job.Spec.Template.Spec.Containers[0].SecurityContext.SeccompProfile = &corev1.SeccompProfile{
Type: "RuntimeDefault",
}
job.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
}
} else {
return job, fmt.Errorf("recieved a nil job object to add security context")
}
return job, nil
}

0 comments on commit 567add1

Please sign in to comment.