Skip to content

Commit

Permalink
Add configurable selinux options for datamover backup:
Browse files Browse the repository at this point in the history
none: no selinux changes (for non-selinux env). Default value
no-relabeling: sets spc_t in securityContext to disable selinux
  enforcement in pod and to prevent relabeling on volume mount
no-readonly: removes readOnly=true from pod spec.volumes
Signed-off-by: Scott Seago <[email protected]>
  • Loading branch information
sseago committed Sep 30, 2024
1 parent 42de654 commit dfb6e69
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelogs/unreleased/8255-sseago
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add configurable selinux options for datamover backup
13 changes: 13 additions & 0 deletions pkg/cmd/cli/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
"github.com/vmware-tanzu/velero/pkg/exposer"
"github.com/vmware-tanzu/velero/pkg/install"
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
)
Expand Down Expand Up @@ -89,6 +90,7 @@ type Options struct {
BackupRepoConfigMap string
RepoMaintenanceJobConfigMap string
NodeAgentConfigMap string
SELinuxDatamover string
}

// BindFlags adds command line values to the options struct.
Expand Down Expand Up @@ -182,6 +184,12 @@ func (o *Options) BindFlags(flags *pflag.FlagSet) {
o.NodeAgentConfigMap,
"The name of ConfigMap containing node-agent configurations.",
)
flags.StringVar(
&o.SELinuxDatamover,
"selinux-datamover",
o.SELinuxDatamover,
"Data Mover backup pod options for handling selinux. Supported values are 'none', 'no-relabeling', and 'no-readonly'. Default is 'none'.",
)

Check warning on line 192 in pkg/cmd/cli/install/install.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/install/install.go#L187-L192

Added lines #L187 - L192 were not covered by tests
}

// NewInstallOptions instantiates a new, default InstallOptions struct.
Expand Down Expand Up @@ -283,6 +291,7 @@ func (o *Options) AsVeleroOptions() (*install.VeleroOptions, error) {
BackupRepoConfigMap: o.BackupRepoConfigMap,
RepoMaintenanceJobConfigMap: o.RepoMaintenanceJobConfigMap,
NodeAgentConfigMap: o.NodeAgentConfigMap,
SELinuxDatamover: o.SELinuxDatamover,

Check warning on line 294 in pkg/cmd/cli/install/install.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/install/install.go#L294

Added line #L294 was not covered by tests
}, nil
}

Expand Down Expand Up @@ -421,6 +430,10 @@ func (o *Options) Validate(c *cobra.Command, args []string, f client.Factory) er
fmt.Printf("⚠️ %s\n", msg)
}

if err := exposer.ValidateSELinuxDatamover(o.SELinuxDatamover); err != nil {
return err

Check warning on line 434 in pkg/cmd/cli/install/install.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/install/install.go#L433-L434

Added lines #L433 - L434 were not covered by tests
}

// If we're only installing CRDs, we can skip the rest of the validation.
if o.CRDsOnly {
return nil
Expand Down
13 changes: 13 additions & 0 deletions pkg/cmd/cli/nodeagent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"github.com/vmware-tanzu/velero/pkg/constant"
"github.com/vmware-tanzu/velero/pkg/controller"
"github.com/vmware-tanzu/velero/pkg/datapath"
"github.com/vmware-tanzu/velero/pkg/exposer"
"github.com/vmware-tanzu/velero/pkg/metrics"
"github.com/vmware-tanzu/velero/pkg/nodeagent"
"github.com/vmware-tanzu/velero/pkg/repository"
Expand Down Expand Up @@ -89,6 +90,7 @@ type nodeAgentServerConfig struct {
resourceTimeout time.Duration
dataMoverPrepareTimeout time.Duration
nodeAgentConfig string
selinuxDatamover string
}

func NewServerCommand(f client.Factory) *cobra.Command {
Expand Down Expand Up @@ -126,6 +128,12 @@ func NewServerCommand(f client.Factory) *cobra.Command {
command.Flags().DurationVar(&config.dataMoverPrepareTimeout, "data-mover-prepare-timeout", config.dataMoverPrepareTimeout, "How long to wait for preparing a DataUpload/DataDownload. Default is 30 minutes.")
command.Flags().StringVar(&config.metricsAddress, "metrics-address", config.metricsAddress, "The address to expose prometheus metrics")
command.Flags().StringVar(&config.nodeAgentConfig, "node-agent-configmap", config.nodeAgentConfig, "The name of ConfigMap containing node-agent configurations.")
command.Flags().StringVar(
&config.selinuxDatamover,
"selinux-datamover",
config.selinuxDatamover,
"Data Mover backup pod options for handling selinux. Supported values are 'none', 'no-relabeling', and 'no-readonly'. Default is 'none'.",
)

Check warning on line 136 in pkg/cmd/cli/nodeagent/server.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/nodeagent/server.go#L131-L136

Added lines #L131 - L136 were not covered by tests

return command
}
Expand Down Expand Up @@ -239,6 +247,10 @@ func newNodeAgentServer(logger logrus.FieldLogger, factory client.Factory, confi
s.getDataPathConfigs()
s.dataPathMgr = datapath.NewManager(s.getDataPathConcurrentNum(defaultDataPathConcurrentNum))

if err = exposer.ValidateSELinuxDatamover(config.selinuxDatamover); err != nil {
return nil, err

Check warning on line 251 in pkg/cmd/cli/nodeagent/server.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/nodeagent/server.go#L250-L251

Added lines #L250 - L251 were not covered by tests
}

return s, nil
}

Expand Down Expand Up @@ -330,6 +342,7 @@ func (s *nodeAgentServer) run() {
s.config.dataMoverPrepareTimeout,
s.logger,
s.metrics,
s.config.selinuxDatamover,

Check warning on line 345 in pkg/cmd/cli/nodeagent/server.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/cli/nodeagent/server.go#L345

Added line #L345 was not covered by tests
)
if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil {
s.logger.WithError(err).Fatal("Unable to create the data upload controller")
Expand Down
4 changes: 4 additions & 0 deletions pkg/controller/data_upload_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type DataUploadReconciler struct {
podResources corev1.ResourceRequirements
preparingTimeout time.Duration
metrics *metrics.ServerMetrics
selinuxDatamover string
}

func NewDataUploadReconciler(
Expand All @@ -92,6 +93,7 @@ func NewDataUploadReconciler(
preparingTimeout time.Duration,
log logrus.FieldLogger,
metrics *metrics.ServerMetrics,
selinuxDatamover string,
) *DataUploadReconciler {
return &DataUploadReconciler{
client: client,
Expand All @@ -114,6 +116,7 @@ func NewDataUploadReconciler(
podResources: podResources,
preparingTimeout: preparingTimeout,
metrics: metrics,
selinuxDatamover: selinuxDatamover,
}
}

Expand Down Expand Up @@ -816,6 +819,7 @@ func (r *DataUploadReconciler) setupExposeParam(du *velerov2alpha1api.DataUpload
Affinity: r.loadAffinity,
BackupPVCConfig: r.backupPVCConfig,
Resources: r.podResources,
SELinuxDatamover: r.selinuxDatamover,
}, nil
}
return nil, nil
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/data_upload_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci
time.Minute*5,
velerotest.NewLogger(),
metrics.NewServerMetrics(),
"",
), nil
}

Expand Down
20 changes: 19 additions & 1 deletion pkg/exposer/csi_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ type CSISnapshotExposeParam struct {

// Resources defines the resource requirements of the hosting pod
Resources corev1.ResourceRequirements

// SELinuxDatamover defines how to modify pod definition for SELinux
// none: no changes (won't work in SELinux environments). Default value if field is empty.
// no-relabeling: Sets securityContext.SELinuxOptions.Type=spc_t. Preferred configuration for SELinux with some performance improvements for volumes with many files, but won't work with restricted pods.
// no-readonly: Removes volumes.volumeSource.PVC.ReadOnly=true. For SELinux environments where spc_t is not allowed, but may break shallow copy.
SELinuxDatamover string
}

// CSISnapshotExposeWaitParam define the input param for WaitExposed of CSI snapshots
Expand Down Expand Up @@ -202,6 +208,7 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1.Obje
csiExposeParam.HostingPodLabels,
csiExposeParam.Affinity,
csiExposeParam.Resources,
csiExposeParam.SELinuxDatamover,
)
if err != nil {
return errors.Wrap(err, "error to create backup pod")
Expand Down Expand Up @@ -441,6 +448,7 @@ func (e *csiSnapshotExposer) createBackupPod(
label map[string]string,
affinity *kube.LoadAffinity,
resources corev1.ResourceRequirements,
selinuxDatamover string,
) (*corev1.Pod, error) {
podName := ownerObject.Name

Expand All @@ -461,10 +469,14 @@ func (e *csiSnapshotExposer) createBackupPod(
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: backupPVC.Name,
ReadOnly: true,
},
},
}}

if selinuxDatamover != SELinuxNoReadOnly {
volumes[0].VolumeSource.PersistentVolumeClaim.ReadOnly = true
}

volumes = append(volumes, podInfo.volumes...)

if label == nil {
Expand Down Expand Up @@ -550,5 +562,11 @@ func (e *csiSnapshotExposer) createBackupPod(
},
}

if selinuxDatamover == SELinuxNoRelabeling {
pod.Spec.SecurityContext.SELinuxOptions = &corev1.SELinuxOptions{
Type: "spc_t",

Check warning on line 567 in pkg/exposer/csi_snapshot.go

View check run for this annotation

Codecov / codecov/patch

pkg/exposer/csi_snapshot.go#L566-L567

Added lines #L566 - L567 were not covered by tests
}
}

return e.kubeClient.CoreV1().Pods(ownerObject.Namespace).Create(ctx, pod, metav1.CreateOptions{})
}
17 changes: 17 additions & 0 deletions pkg/exposer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ limitations under the License.
package exposer

import (
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
)

Expand All @@ -25,6 +28,9 @@ const (
AccessModeBlock = "by-block-device"
podGroupLabel = "velero.io/exposer-pod-group"
podGroupSnapshot = "snapshot-exposer"
SELinuxNone = "none"
SELinuxNoRelabeling = "no-relabeling"
SELinuxNoReadOnly = "no-readonly"
)

// ExposeResult defines the result of expose.
Expand All @@ -39,3 +45,14 @@ type ExposeByPod struct {
HostingContainer string
VolumeName string
}

// ValidateSelinuxDatamover validates if the input param is a valid SELinux approach for data mover.
// It will return an error if it's invalid.
func ValidateSELinuxDatamover(t string) error {
t = strings.TrimSpace(t)
if t != "" && t != SELinuxNone && t != SELinuxNoRelabeling && t != SELinuxNoReadOnly {
return fmt.Errorf("invalid SELinux datamover option '%s', valid options are: '%s', '%s', '%s'", t, SELinuxNone, SELinuxNoRelabeling, SELinuxNoReadOnly)

Check warning on line 54 in pkg/exposer/types.go

View check run for this annotation

Codecov / codecov/patch

pkg/exposer/types.go#L51-L54

Added lines #L51 - L54 were not covered by tests
}

return nil
}

Check warning on line 58 in pkg/exposer/types.go

View check run for this annotation

Codecov / codecov/patch

pkg/exposer/types.go#L57-L58

Added lines #L57 - L58 were not covered by tests
4 changes: 4 additions & 0 deletions pkg/install/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet {
daemonSetArgs = append(daemonSetArgs, fmt.Sprintf("--node-agent-configmap=%s", c.nodeAgentConfigMap))
}

if len(c.selinuxDatamover) > 0 {
daemonSetArgs = append(daemonSetArgs, fmt.Sprintf("--selinux-datamover=%s", c.selinuxDatamover))
}

userID := int64(0)
mountPropagationMode := corev1.MountPropagationHostToContainer

Expand Down
7 changes: 7 additions & 0 deletions pkg/install/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type podTemplateConfig struct {
backupRepoConfigMap string
repoMaintenanceJobConfigMap string
nodeAgentConfigMap string
selinuxDatamover string
}

func WithImage(image string) podTemplateOption {
Expand Down Expand Up @@ -183,6 +184,12 @@ func WithNodeAgentConfigMap(nodeAgentConfigMap string) podTemplateOption {
}
}

func WithSELinuxDatamover(selinuxDatamover string) podTemplateOption {
return func(c *podTemplateConfig) {
c.selinuxDatamover = selinuxDatamover

Check warning on line 189 in pkg/install/deployment.go

View check run for this annotation

Codecov / codecov/patch

pkg/install/deployment.go#L187-L189

Added lines #L187 - L189 were not covered by tests
}
}

func WithScheduleSkipImmediately(b bool) podTemplateOption {
return func(c *podTemplateConfig) {
c.scheduleSkipImmediately = b
Expand Down
4 changes: 4 additions & 0 deletions pkg/install/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ type VeleroOptions struct {
BackupRepoConfigMap string
RepoMaintenanceJobConfigMap string
NodeAgentConfigMap string
SELinuxDatamover string
}

func AllCRDs() *unstructured.UnstructuredList {
Expand Down Expand Up @@ -411,6 +412,9 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList {
if len(o.NodeAgentConfigMap) > 0 {
dsOpts = append(dsOpts, WithNodeAgentConfigMap(o.NodeAgentConfigMap))
}
if len(o.SELinuxDatamover) > 0 {
dsOpts = append(dsOpts, WithSELinuxDatamover(o.SELinuxDatamover))

Check warning on line 416 in pkg/install/resources.go

View check run for this annotation

Codecov / codecov/patch

pkg/install/resources.go#L416

Added line #L416 was not covered by tests
}

ds := DaemonSet(o.Namespace, dsOpts...)
if err := appendUnstructured(resources, ds); err != nil {
Expand Down

0 comments on commit dfb6e69

Please sign in to comment.