Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SELinux for tumbleweed and green #2052

Merged
merged 5 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ RUN ARCH=$(uname -m); \
xorriso \
cosign \
gptfdisk \
patterns-microos-selinux \
btrfsprogs \
lvm2 && \
zypper cc -a

Expand Down
6 changes: 5 additions & 1 deletion examples/green/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ RUN ARCH=$(uname -m); \
sudo \
curl \
sed \
patch \
iproute2 \
podman \
audit \
patterns-microos-selinux \
btrfsprogs \
btrfsmaintenance \
snapper \
Expand All @@ -79,6 +80,9 @@ RUN echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/rootlogin.conf
# Add default network configuration
ADD 05_network.yaml /system/oem/05_network.yaml

# SELinux in enforce mode
RUN sed -i "s|SELINUX=.*|SELINUX=enforcing|g" /etc/selinux/config

# Add default snapshotter setup
ADD snapshotter.yaml /etc/elemental/config.d/snapshotter.yaml

Expand Down
22 changes: 1 addition & 21 deletions pkg/action/build-disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ func (b *BuildDiskAction) buildDiskHook(hook string) error {
}

func (b *BuildDiskAction) buildDiskChrootHook(hook string, root string) error {
extraMounts := map[string]string{}
return ChrootHook(&b.cfg.Config, hook, b.cfg.Strict, root, extraMounts, b.cfg.CloudInitPaths...)
return ChrootHook(&b.cfg.Config, hook, b.cfg.Strict, root, nil, b.cfg.CloudInitPaths...)
}

func (b *BuildDiskAction) preparePartitionsRoot() error {
Expand Down Expand Up @@ -219,12 +218,6 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo
return elementalError.NewFromError(err, elementalError.SetDefaultGrubEntry)
}

// Relabel SELinux
err = b.applySelinuxLabels(recRoot, b.spec.Expandable)
if err != nil {
return elementalError.NewFromError(err, elementalError.SelinuxRelabel)
}

// After disk hook happens after deploying the OS tree into a temporary folder
if !b.spec.Expandable {
err = b.buildDiskChrootHook(constants.AfterDiskChrootHook, recRoot)
Expand Down Expand Up @@ -699,19 +692,6 @@ func (b *BuildDiskAction) CreateDiskPartitionTable(disk string) error {
return nil
}

// applySelinuxLabels sets SELinux extended attributes to the root-tree being installed. Swallows errors, label on a best effort
func (b *BuildDiskAction) applySelinuxLabels(root string, unprivileged bool) error {
if unprivileged {
// Swallow errors, label on a best effort when not chrooting
_ = elemental.SelinuxRelabel(b.cfg.Config, root)
return nil
}
binds := map[string]string{}
return utils.ChrootedCallback(
&b.cfg.Config, root, binds, func() error { return elemental.SelinuxRelabel(b.cfg.Config, "/") },
)
}

func (b *BuildDiskAction) createBuildDiskStateYaml(stateRoot, recoveryRoot string) error {
var statePath, recoveryPath string

Expand Down
4 changes: 3 additions & 1 deletion pkg/action/build-iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ func grubCfgTemplate(arch string) string {

menuentry "%s" --class os --unrestricted {
echo Loading kernel...
linux ($root)` + constants.ISOKernelPath(arch) + ` cdroot root=live:CDLABEL=%s rd.live.dir=` + constants.ISOLoaderPath(arch) + ` rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 elemental.disable elemental.setup=` + constants.ISOCloudInitPath + `
linux ($root)` + constants.ISOKernelPath(arch) + ` cdroot root=live:CDLABEL=%s rd.live.dir=` + constants.ISOLoaderPath(arch) +
` rd.live.squashimg=rootfs.squashfs security=selinux enforcing=0 console=tty1 console=ttyS0 elemental.disable elemental.setup=` +
constants.ISOCloudInitPath + `
echo Loading initrd...
initrd ($root)` + constants.ISOInitrdPath(arch) + `
}
Expand Down
7 changes: 0 additions & 7 deletions pkg/action/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,6 @@ func (i *InstallAction) refineDeployment() error { //nolint:dupl
return elementalError.NewFromError(err, elementalError.InstallGrub)
}

// Relabel SELinux
err = elemental.ApplySelinuxLabels(i.cfg.Config, i.spec.Partitions)
if err != nil {
i.cfg.Logger.Errorf("failed setting SELinux labels: %v", err)
return elementalError.NewFromError(err, elementalError.SelinuxRelabel)
}

err = i.installChrootHook(cnst.AfterInstallChrootHook, cnst.WorkingImgDir)
if err != nil {
i.cfg.Logger.Errorf("failed after-install-chroot hook: %v", err)
Expand Down
7 changes: 0 additions & 7 deletions pkg/action/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,6 @@ func (r *ResetAction) refineDeployment() error { //nolint:dupl
return elementalError.NewFromError(err, elementalError.InstallGrub)
}

// Relabel SELinux
err = elemental.ApplySelinuxLabels(r.cfg.Config, r.spec.Partitions)
if err != nil {
r.cfg.Logger.Errorf("failed setting SELinux labels: %v", err)
return elementalError.NewFromError(err, elementalError.SelinuxRelabel)
}

err = r.resetChrootHook(constants.AfterResetChrootHook, constants.WorkingImgDir)
if err != nil {
r.cfg.Logger.Errorf("failed after-reset-chroot hook: %v", err)
Expand Down
7 changes: 0 additions & 7 deletions pkg/action/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,6 @@ func (u *UpgradeAction) refineDeployment() error { //nolint:dupl
}
}

// Relabel SELinux
err = elemental.ApplySelinuxLabels(u.cfg.Config, u.spec.Partitions)
if err != nil {
u.cfg.Logger.Errorf("failed setting SELinux labels: %v", err)
return elementalError.NewFromError(err, elementalError.SelinuxRelabel)
}

err = u.upgradeChrootHook(constants.AfterUpgradeChrootHook, constants.WorkingImgDir)
if err != nil {
u.Error("Error running hook after-upgrade-chroot: %s", err)
Expand Down
60 changes: 44 additions & 16 deletions pkg/elemental/elemental.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ func CreateImageFromTree(c types.Config, img *types.Image, rootDir string, prelo
return err
}

err = SelinuxRelabel(c, rootDir)
if err != nil {
c.Logger.Warnf("failed SELinux labelling at %s: %v", rootDir, err)
}

err = utils.CreateSquashFS(c.Runner, c.Logger, rootDir, img.File, c.SquashFsCompressionConfig)
if err != nil {
c.Logger.Errorf("failed creating squashfs image for %s: %v", img.File, err)
Expand Down Expand Up @@ -398,6 +403,12 @@ func CreateImageFromTree(c types.Config, img *types.Image, rootDir string, prelo
c.Logger.Errorf("failed creating dir structure: %v", err)
return err
}

err = ApplySELinuxLabels(c, img.MountPoint, nil)
if err != nil {
c.Logger.Errorf("failed SELinux labelling at %s: %v", img.MountPoint, err)
return err
}
}
}
return err
Expand All @@ -406,7 +417,7 @@ func CreateImageFromTree(c types.Config, img *types.Image, rootDir string, prelo
// CopyFileImg copies the files target as the source of this image. It also applies the img label over the copied image.
func CopyFileImg(c types.Config, img *types.Image) error {
if !img.Source.IsFile() {
return fmt.Errorf("Copying a file image requires an image source of file type")
return fmt.Errorf("copying a file image requires an image source of file type")
}

err := utils.MkdirAll(c.Fs, filepath.Dir(img.File), cnst.DirPerm)
Expand Down Expand Up @@ -659,19 +670,20 @@ func CopyCloudConfig(c types.Config, path string, cloudInit []string) (err error
}

// SelinuxRelabel will relabel the system if it finds the binary and the context
func SelinuxRelabel(c types.Config, rootDir string) error {
policyFile, err := utils.FindFile(c.Fs, rootDir, filepath.Join(cnst.SELinuxTargetedPolicyPath, "policy.*"))
func SelinuxRelabel(c types.Config, rootDir string, extraPaths ...string) error {
contextFile := filepath.Join(rootDir, cnst.SELinuxTargetedContextFile)
contextExists, _ := utils.Exists(c.Fs, contextFile)

if err == nil && contextExists && c.Runner.CommandExists("setfiles") {
if contextExists && c.Runner.CommandExists("setfiles") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should check that rootDir is RW as well? might be enough to get the recovery test green.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is RW... disable selinux and the test will pass, I believe the snapper error msg is just misleading, it could not write due to some denial, but not because of being RO.

var out []byte
var err error
var args []string
if rootDir == "/" || rootDir == "" {
out, err = c.Runner.Run("setfiles", "-c", policyFile, "-e", "/dev", "-e", "/proc", "-e", "/sys", "-F", contextFile, "/")
args = append([]string{"-e", "/dev", "-e", "/proc", "-e", "/sys", "-i", "-F", contextFile, "/"}, extraPaths...)
} else {
out, err = c.Runner.Run("setfiles", "-c", policyFile, "-F", "-r", rootDir, contextFile, rootDir)
args = append([]string{"-i", "-F", "-r", rootDir, contextFile, rootDir}, extraPaths...)
}
out, err = c.Runner.Run("setfiles", args...)
c.Logger.Debugf("SELinux setfiles output: %s", string(out))
return err
}
Expand All @@ -680,18 +692,34 @@ func SelinuxRelabel(c types.Config, rootDir string) error {
return nil
}

// ApplySelinuxLabels sets SELinux extended attributes to the root-tree being installed
func ApplySelinuxLabels(cfg types.Config, parts types.ElementalPartitions) error {
binds := map[string]string{}
if mnt, _ := IsMounted(cfg, parts.Persistent); mnt {
binds[parts.Persistent.MountPoint] = cnst.PersistentPath
// ApplySELinuxLabels relables with setfiles the given root in a chroot env. Additionaly after the first
// chrooted call it runs a non chrooted call to relabel any mountpoint used within the chroot.
func ApplySELinuxLabels(c types.Config, rootDir string, bind map[string]string) (err error) {
var out []byte
extraPaths := []string{}

for _, v := range bind {
extraPaths = append(extraPaths, v)
}

err = utils.ChrootedCallback(&c, rootDir, bind, func() error { return SelinuxRelabel(c, "/", extraPaths...) })
if err != nil {
return err
}
if mnt, _ := IsMounted(cfg, parts.OEM); mnt {
binds[parts.OEM.MountPoint] = cnst.OEMPath

contextsFile := filepath.Join(rootDir, cnst.SELinuxTargetedContextFile)
existsCon, _ := utils.Exists(c.Fs, contextsFile)

if existsCon && c.Runner.CommandExists("setfiles") {
args := []string{"-r", rootDir, "-i", "-F", contextsFile}
for _, path := range append([]string{"/dev", "/proc", "/sys"}, extraPaths...) {
args = append(args, filepath.Join(rootDir, path))
}
out, err = c.Runner.Run("setfiles", args...)
c.Logger.Debugf("SELinux setfiles output: %s", string(out))
}
return utils.ChrootedCallback(
&cfg, cnst.WorkingImgDir, binds, func() error { return SelinuxRelabel(cfg, "/") },
)

return err
}

// CheckActiveDeployment returns true if at least one of the mode sentinel files is found
Expand Down
6 changes: 3 additions & 3 deletions pkg/elemental/elemental_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,8 @@ var _ = Describe("Elemental", Label("elemental"), func() {
Expect(err).ShouldNot(HaveOccurred())

relabelCmd = []string{
"setfiles", "-c", policyFile, "-e", "/dev", "-e", "/proc", "-e", "/sys",
"-F", constants.SELinuxTargetedContextFile, "/",
"setfiles", "-e", "/dev", "-e", "/proc", "-e", "/sys",
"-i", "-F", constants.SELinuxTargetedContextFile, "/",
}
})
It("does nothing if the context file is not found", func() {
Expand Down Expand Up @@ -929,7 +929,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
Expect(err).ShouldNot(HaveOccurred())

relabelCmd = []string{
"setfiles", "-c", policyFile, "-F", "-r", "/root", contextFile, "/root",
"setfiles", "-i", "-F", "-r", "/root", contextFile, "/root",
}

Expect(elemental.SelinuxRelabel(*config, "/root")).To(BeNil())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
name: "SELinux"
stages:
initramfs:
- name: "Relabelling"
- name: "SELinux labels for targeted policy"
commands:
- |
if grep -q "selinux=1" /proc/cmdline; then
load_policy -i
restorecon -R -i -v /etc /root /opt /srv /var /home /usr/local /oem
contexts="/etc/selinux/targeted/contexts/files/file_contexts"
if grep -qw selinux /sys/kernel/security/lsm && [ -e "${contexts}" ]; then
# Some extended attributes are lost on copy-up bsc#1210690.
# Workaround visit children first, then parents
mkdir -p /run/systemd/relabel-extra.d/
for path in /etc /srv /var /oem /home /root /opt; do
if [ -d "${path}" ]; then
find ${path} -depth -exec /sbin/setfiles -i -F -v "${contexts}" \{\} +
echo "${path}" >> /run/systemd/relabel-extra.d/layout.relabel
fi
done
fi
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ if [ -n "${img}" ]; then
fi

if [ "${mode}" == "recovery" ]; then
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=${recovery_label} ${img_arg} elemental.mode=${mode} elemental.oemlabel=${oem_label} security=selinux selinux=0 rd.neednet=1"
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=${recovery_label} ${img_arg} elemental.mode=${mode} elemental.oemlabel=${oem_label} security=selinux enforcing=0 rd.neednet=1"
else
if [ "${snapshotter}" == "btrfs" ]; then
set snap_arg="elemental.snapshotter=btrfs"
fi
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=${state_label} ${img_arg} ${snap_arg} elemental.mode=${mode} elemental.oemlabel=${oem_label} panic=5 security=selinux selinux=0 rd.neednet=1 fsck.mode=force fsck.repair=yes"
set kernelcmd="console=tty1 console=ttyS0 root=LABEL=${state_label} ${img_arg} ${snap_arg} elemental.mode=${mode} elemental.oemlabel=${oem_label} panic=5 security=selinux rd.neednet=1 fsck.mode=force fsck.repair=yes"
fi

set kernel=/boot/vmlinuz
Expand Down
33 changes: 22 additions & 11 deletions pkg/snapshotter/btrfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,13 @@ func (b *Btrfs) CloseTransaction(snapshot *types.Snapshot) (err error) {
}
}

extraBind := map[string]string{filepath.Join(b.rootDir, snapshotsPath): filepath.Join("/", snapshotsPath)}
err = elemental.ApplySELinuxLabels(b.cfg, snapshot.Path, extraBind)
if err != nil {
b.cfg.Logger.Errorf("failed relabelling snapshot path: %s", snapshot.Path)
return err
}

cmdOut, err = b.cfg.Runner.Run("btrfs", "property", "set", snapshot.Path, "ro", "true")
if err != nil {
b.cfg.Logger.Errorf("failed setting read only property to snapshot %d: %s", snapshot.ID, string(cmdOut))
Expand Down Expand Up @@ -676,20 +683,24 @@ func (b *Btrfs) remountStatePartition(state *types.Partition) error {
return err
}

b.cfg.Logger.Debugf("Mount snapshots subvolume in active snapshot")
if b.activeSnapshotID > 0 {
snapperRoot := filepath.Join(state.MountPoint, fmt.Sprintf(snapshotPathTmpl, b.activeSnapshotID))
mountpoint := filepath.Join(snapperRoot, snapshotsPath)
subvol := fmt.Sprintf("subvol=%s", filepath.Join(rootSubvol, snapshotsPath))
err = b.cfg.Mounter.Mount(state.Path, mountpoint, "btrfs", []string{"rw", subvol})
if err != nil {
b.cfg.Logger.Errorf("failed mounting subvolume %s at %s", subvol, mountpoint)
return err
}
b.snapshotsUmount = func() error { return b.cfg.Mounter.Unmount(mountpoint) }
b.snapperArgs = []string{"--no-dbus", "--root", snapperRoot}
err = b.mountSnapshotsSubvolumeInSnapshot(state.MountPoint, state.Path, b.activeSnapshotID)
b.snapperArgs = []string{"--no-dbus", "--root", filepath.Join(state.MountPoint, fmt.Sprintf(snapshotPathTmpl, b.activeSnapshotID))}
}
b.rootDir = state.MountPoint
return err
}

func (b *Btrfs) mountSnapshotsSubvolumeInSnapshot(root, device string, snapshotID int) error {
b.cfg.Logger.Debugf("Mount snapshots subvolume in active snapshot %d", snapshotID)
mountpoint := filepath.Join(filepath.Join(root, fmt.Sprintf(snapshotPathTmpl, snapshotID)), snapshotsPath)
subvol := fmt.Sprintf("subvol=%s", filepath.Join(rootSubvol, snapshotsPath))
err := b.cfg.Mounter.Mount(device, mountpoint, "btrfs", []string{"rw", subvol})
if err != nil {
b.cfg.Logger.Errorf("failed mounting subvolume %s at %s", subvol, mountpoint)
return err
}
b.snapshotsUmount = func() error { return b.cfg.Mounter.Unmount(mountpoint) }
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/snapshotter/btrfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var _ = Describe("Btrfs", Label("snapshotter", " btrfs"), func() {
var snapCfg types.SnapshotterConfig
var rootDir, efiDir string
var statePart *types.Partition
var syscall *mocks.FakeSyscall

BeforeEach(func() {
rootDir = "/some/root"
Expand All @@ -57,6 +58,7 @@ var _ = Describe("Btrfs", Label("snapshotter", " btrfs"), func() {
efiDir = constants.EfiDir
runner = mocks.NewFakeRunner()
mounter = mocks.NewFakeMounter()
syscall = &mocks.FakeSyscall{}
bootloader = &mocks.FakeBootloader{}
memLog = bytes.NewBuffer(nil)
logger = types.NewBufferLogger(memLog)
Expand All @@ -71,6 +73,7 @@ var _ = Describe("Btrfs", Label("snapshotter", " btrfs"), func() {
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithPlatform("linux/amd64"),
)
snapCfg = types.SnapshotterConfig{
Expand Down
3 changes: 3 additions & 0 deletions pkg/snapshotter/loopdevice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var _ = Describe("LoopDevice", Label("snapshotter", "loopdevice"), func() {
var snapCfg types.SnapshotterConfig
var rootDir, efiDir string
var statePart *types.Partition
var syscall *mocks.FakeSyscall

BeforeEach(func() {
rootDir = "/some/root"
Expand All @@ -55,6 +56,7 @@ var _ = Describe("LoopDevice", Label("snapshotter", "loopdevice"), func() {
efiDir = constants.EfiDir
runner = mocks.NewFakeRunner()
mounter = mocks.NewFakeMounter()
syscall = &mocks.FakeSyscall{}
bootloader = &mocks.FakeBootloader{}
memLog = bytes.NewBuffer(nil)
logger = types.NewBufferLogger(memLog)
Expand All @@ -69,6 +71,7 @@ var _ = Describe("LoopDevice", Label("snapshotter", "loopdevice"), func() {
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithPlatform("linux/amd64"),
)
snapCfg = types.NewLoopDevice()
Expand Down
3 changes: 3 additions & 0 deletions pkg/utils/chroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func NewChroot(path string, config *types.Config) *Chroot {
// ChrootedCallback runs the given callback in a chroot environment
func ChrootedCallback(cfg *types.Config, path string, bindMounts map[string]string, callback func() error) error {
chroot := NewChroot(path, cfg)
if bindMounts == nil {
bindMounts = map[string]string{}
}
chroot.SetExtraMounts(bindMounts)
return chroot.RunCallback(callback)
}
Expand Down
Loading
Loading