Skip to content

Commit

Permalink
virt-launcher: use our custom SELinux type only when necessary
Browse files Browse the repository at this point in the history
Signed-off-by: Jed Lejosne <[email protected]>
  • Loading branch information
jean-edouard committed Oct 4, 2022
1 parent 32fcba3 commit 3a58db9
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 26 deletions.
4 changes: 3 additions & 1 deletion cmd/virt-handler/virt_launcher.cil
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
(allow process proc_t (dir (mounton)))
(allow process proc_t (filesystem (mount unmount)))
;
; This is needed for passt.
; This is needed for passt when running with this policy.
; container_t is compatible with passt without needing any additional rule.
; This rule is therefore only needed for VMs that use both virtiofs/hugetables and passt.
; The policy will be removed from here once it will be installed via the passt package.
(allow process tmpfs_t (filesystem (mount)))
)
10 changes: 10 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ func IsVFIOVMI(vmi *v1.VirtualMachineInstance) bool {
return false
}

// Check if the VMI includes passt network interface(s)
func IsPasstVMI(vmi *v1.VirtualMachineInstance) bool {
for _, net := range vmi.Spec.Domain.Devices.Interfaces {
if net.Passt != nil {
return true
}
}
return false
}

// Check if a VMI spec requests AMD SEV
func IsSEVVMI(vmi *v1.VirtualMachineInstance) bool {
return vmi.Spec.Domain.LaunchSecurity != nil && vmi.Spec.Domain.LaunchSecurity.SEV != nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/virt-config/virt-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const (
SmbiosConfigDefaultManufacturer = "KubeVirt"
SmbiosConfigDefaultProduct = "None"
DefaultPermitBridgeInterfaceOnPodNetwork = true
DefaultSELinuxLauncherType = "virt_launcher.process"
DefaultSELinuxLauncherType = ""
SupportedGuestAgentVersions = "2.*,3.*,4.*,5.*"
DefaultARCHOVMFPath = "/usr/share/OVMF"
DefaultAARCH64OVMFPath = "/usr/share/AAVMF"
Expand Down
27 changes: 18 additions & 9 deletions pkg/virt-controller/services/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ const (
QemuOverhead = "30Mi" // The `ps` RSS for qemu, minus the RAM of its (stressed) guest, minus the virtual page table
)

const customSELinuxType = "virt_launcher.process"

type TemplateService interface {
RenderMigrationManifest(vmi *v1.VirtualMachineInstance, sourcePod *k8sv1.Pod) (*k8sv1.Pod, error)
RenderLaunchManifest(vmi *v1.VirtualMachineInstance) (*k8sv1.Pod, error)
Expand Down Expand Up @@ -492,11 +494,7 @@ func (t *templateService) renderLaunchManifest(vmi *v1.VirtualMachineInstance, i
pod.Spec.SecurityContext.RunAsNonRoot = &nonRoot
}

// If an SELinux type was specified, use that--otherwise don't set an SELinux type
selinuxType := t.clusterConfig.GetSELinuxLauncherType()
if selinuxType != "" {
alignPodMultiCategorySecurity(&pod, selinuxType, t.clusterConfig.DockerSELinuxMCSWorkaroundEnabled())
}
alignPodMultiCategorySecurity(&pod, vmi, t.clusterConfig.GetSELinuxLauncherType(), t.clusterConfig.DockerSELinuxMCSWorkaroundEnabled())

// If we have a runtime class specified, use it, otherwise don't set a runtimeClassName
runtimeClassName := t.clusterConfig.GetDefaultRuntimeClass()
Expand Down Expand Up @@ -744,7 +742,6 @@ func (t *templateService) RenderHotplugAttachmentPodTemplate(volumes []*v1.Volum
// enter the mount namespace of virt-launcher and check the level of any file/directory.
// We need a way to ask virt-handler to do that.
Level: "s0",
Type: t.clusterConfig.GetSELinuxLauncherType(),
},
},
VolumeMounts: []k8sv1.VolumeMount{
Expand Down Expand Up @@ -825,7 +822,7 @@ func (t *templateService) RenderHotplugAttachmentPodTemplate(volumes []*v1.Volum
return pod, nil
}

func (t *templateService) RenderHotplugAttachmentTriggerPodTemplate(volume *v1.Volume, ownerPod *k8sv1.Pod, _ *v1.VirtualMachineInstance, pvcName string, isBlock bool, tempPod bool) (*k8sv1.Pod, error) {
func (t *templateService) RenderHotplugAttachmentTriggerPodTemplate(volume *v1.Volume, ownerPod *k8sv1.Pod, vmi *v1.VirtualMachineInstance, pvcName string, isBlock bool, tempPod bool) (*k8sv1.Pod, error) {
zero := int64(0)
sharedMount := k8sv1.MountPropagationHostToContainer
var command []string
Expand Down Expand Up @@ -876,7 +873,6 @@ func (t *templateService) RenderHotplugAttachmentTriggerPodTemplate(volume *v1.V
},
SecurityContext: &k8sv1.SecurityContext{
SELinuxOptions: &k8sv1.SELinuxOptions{
Type: t.clusterConfig.GetSELinuxLauncherType(),
Level: "s0",
},
},
Expand Down Expand Up @@ -1115,7 +1111,20 @@ func wrapGuestAgentPingWithVirtProbe(vmi *v1.VirtualMachineInstance, probe *k8sv
return
}

func alignPodMultiCategorySecurity(pod *k8sv1.Pod, selinuxType string, dockerSELinuxMCSWorkaround bool) {
func alignPodMultiCategorySecurity(pod *k8sv1.Pod, vmi *v1.VirtualMachineInstance, selinuxType string, dockerSELinuxMCSWorkaround bool) {
if selinuxType == "" {
if util.HasHugePages(vmi) || util.IsVMIVirtiofsEnabled(vmi) || util.IsPasstVMI(vmi) {
// If no SELinux type was specified, use our custom type for VMIs that need it
selinuxType = customSELinuxType
} else {
// If no SELinux type was specified and the VMI can run as container_t, do nothing
return
}
}

if pod.Spec.SecurityContext == nil {
pod.Spec.SecurityContext = &k8sv1.PodSecurityContext{}
}
pod.Spec.SecurityContext.SELinuxOptions = &k8sv1.SELinuxOptions{Type: selinuxType}
// more info on https://github.com/kubernetes/kubernetes/issues/90759
// Since the compute container needs to be able to communicate with the
Expand Down
33 changes: 32 additions & 1 deletion pkg/virt-controller/services/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ var _ = Describe("Template", func() {
)
})
Context("with SELinux types", func() {
It("should run under the SELinux type virt_launcher.process if none specified", func() {
DescribeTable("should run under the SELinux type virt_launcher.process", func(hugePages, virtiofs bool) {
config, kvInformer, svc = configFactory(defaultArch)
vmi := v1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -437,12 +437,43 @@ var _ = Describe("Template", func() {
},
}},
}
if hugePages {
vmi.Spec.Domain.Memory = &v1.Memory{Hugepages: &v1.Hugepages{PageSize: "2Mi"}}
}
if virtiofs {
vmi.Spec.Domain.Devices.Filesystems = []v1.Filesystem{{
Name: "virtiofs",
Virtiofs: &v1.FilesystemVirtiofs{},
}}
}
pod, err := svc.RenderLaunchManifest(&vmi)
Expect(err).ToNot(HaveOccurred())
if pod.Spec.SecurityContext != nil {
Expect(pod.Spec.SecurityContext.SELinuxOptions).ToNot(BeNil())
Expect(pod.Spec.SecurityContext.SELinuxOptions.Type).To(Equal("virt_launcher.process"))
}
},
Entry("if hugepages are enabled", true, false),
Entry("if virtiofs is enabled", false, true),
Entry("if hugepages and virtiofs are enabled", true, true),
)
It("should be nil if no SELinux type is specified and none is needed", func() {
config, kvInformer, svc = configFactory(defaultArch)
vmi := v1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "testvmi", Namespace: "default", UID: "1234",
},
Spec: v1.VirtualMachineInstanceSpec{Volumes: []v1.Volume{}, Domain: v1.DomainSpec{
Devices: v1.Devices{
DisableHotplug: true,
},
}},
}
pod, err := svc.RenderLaunchManifest(&vmi)
Expect(err).ToNot(HaveOccurred())
if pod.Spec.SecurityContext != nil {
Expect(pod.Spec.SecurityContext.SELinuxOptions).To(BeNil())
}
})
It("should run under the corresponding SELinux type if specified", func() {
config, kvInformer, svc = configFactory(defaultArch)
Expand Down
4 changes: 4 additions & 0 deletions tests/libvmi/vmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ func WithCPUFeature(featureName, policy string) Option {
}
}

func WithPasstInterfaceWithPort() Option {
return WithInterface(InterfaceDeviceWithPasstBinding([]v1.Port{{Port: 1234, Protocol: "TCP"}}...))
}

func baseVmi(name string) *v1.VirtualMachineInstance {
vmi := v1.NewVMIReferenceFromNameWithNS("", name)
vmi.Spec = v1.VirtualMachineInstanceSpec{Domain: v1.DomainSpec{}}
Expand Down
12 changes: 4 additions & 8 deletions tests/network/vmi_passt.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var _ = SIGDescribe("[Serial] Passt", func() {

It("should report the IP to the status", func() {
vmi := libvmi.NewAlpineWithTestTooling(
withPasstInterfaceWithPort(),
libvmi.WithPasstInterfaceWithPort(),
libvmi.WithNetwork(v1.DefaultPodNetwork()),
)

Expand Down Expand Up @@ -126,7 +126,7 @@ var _ = SIGDescribe("[Serial] Passt", func() {

startClientVMI := func() {
clientVMI = libvmi.NewAlpineWithTestTooling(
withPasstInterfaceWithPort(),
libvmi.WithPasstInterfaceWithPort(),
libvmi.WithNetwork(v1.DefaultPodNetwork()),
)

Expand Down Expand Up @@ -248,7 +248,7 @@ EOL`, inetSuffix, serverIP, serverPort)
}

vmi := libvmi.NewAlpineWithTestTooling(
withPasstInterfaceWithPort(),
libvmi.WithPasstInterfaceWithPort(),
libvmi.WithNetwork(v1.DefaultPodNetwork()),
)
vmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(vmi)
Expand All @@ -272,7 +272,7 @@ EOL`, inetSuffix, serverIP, serverPort)
}

vmi := libvmi.NewAlpineWithTestTooling(
withPasstInterfaceWithPort(),
libvmi.WithPasstInterfaceWithPort(),
libvmi.WithNetwork(v1.DefaultPodNetwork()),
)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -295,7 +295,3 @@ func withPasstExtendedResourceMemory(ports ...v1.Port) libvmi.Option {
return func(vmi *v1.VirtualMachineInstance) {
}
}

func withPasstInterfaceWithPort() libvmi.Option {
return libvmi.WithInterface(libvmi.InterfaceDeviceWithPasstBinding([]v1.Port{{Port: 1234, Protocol: "TCP"}}...))
}
8 changes: 6 additions & 2 deletions tests/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ var _ = SIGDescribe("Storage", func() {
libstorage.DeletePVC(pvc2)
})

It("should be successfully started and accessible", func() {
DescribeTable("should be successfully started and accessible", func(option1, option2 libvmi.Option) {

virtiofsMountPath := func(pvcName string) string { return fmt.Sprintf("/mnt/virtiofs_%s", pvcName) }
virtiofsTestFile := func(virtiofsMountPath string) string { return fmt.Sprintf("%s/virtiofs_test", virtiofsMountPath) }
Expand All @@ -442,6 +442,7 @@ var _ = SIGDescribe("Storage", func() {
libvmi.WithCloudInitNoCloudUserData(mountVirtiofsCommands, true),
libvmi.WithFilesystemPVC(pvc1),
libvmi.WithFilesystemPVC(pvc2),
option1, option2,
)

vmi = tests.RunVMIAndExpectLaunchIgnoreWarnings(vmi, 300)
Expand Down Expand Up @@ -473,7 +474,10 @@ var _ = SIGDescribe("Storage", func() {
)
Expect(err).ToNot(HaveOccurred())
Expect(strings.Trim(podVirtioFsFileExist, "\n")).To(Equal("exist"))
})
},
Entry("", func(instance *virtv1.VirtualMachineInstance) {}, func(instance *virtv1.VirtualMachineInstance) {}),
Entry("with passt enabled", libvmi.WithPasstInterfaceWithPort(), libvmi.WithNetwork(v1.DefaultPodNetwork())),
)

})
Context("VirtIO-FS with an empty PVC", func() {
Expand Down
13 changes: 9 additions & 4 deletions tests/vmi_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,10 @@ var _ = Describe("[sig-compute]Configurations", func() {
hugepagesVmi = libvmi.NewCirros()
})

DescribeTable("should consume hugepages ", func(hugepageSize string, memory string, guestMemory string) {
DescribeTable("should consume hugepages ", func(hugepageSize string, memory string, guestMemory string, option1, option2 libvmi.Option) {
if option1 != nil && option2 != nil {
hugepagesVmi = libvmi.NewCirros(option1, option2)
}
hugepageType := kubev1.ResourceName(kubev1.ResourceHugePagesPrefix + hugepageSize)
v, err := cluster.GetKubernetesVersion()
Expect(err).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -1170,9 +1173,11 @@ var _ = Describe("[sig-compute]Configurations", func() {
By("Checking that the VM memory equals to a number of consumed hugepages")
Eventually(func() bool { return verifyHugepagesConsumption() }, 30*time.Second, 5*time.Second).Should(BeTrue())
},
Entry("[Serial][test_id:1671]hugepages-2Mi", "2Mi", "64Mi", "None"),
Entry("[Serial][test_id:1672]hugepages-1Gi", "1Gi", "1Gi", "None"),
Entry("[Serial][test_id:1672]hugepages-2Mi with guest memory set explicitly", "2Mi", "70Mi", "64Mi"),
Entry("[Serial][test_id:1671]hugepages-2Mi", "2Mi", "64Mi", "None", nil, nil),
Entry("[Serial][test_id:1672]hugepages-1Gi", "1Gi", "1Gi", "None", nil, nil),
Entry("[Serial][test_id:1672]hugepages-2Mi with guest memory set explicitly", "2Mi", "70Mi", "64Mi", nil, nil),
Entry("[Serial]hugepages-2Mi with passt enabled", "2Mi", "64Mi", "None",
libvmi.WithPasstInterfaceWithPort(), libvmi.WithNetwork(v1.DefaultPodNetwork())),
)

Context("with unsupported page size", func() {
Expand Down

0 comments on commit 3a58db9

Please sign in to comment.