Skip to content

Commit

Permalink
[ic/iso] Support for SELinux (#22)
Browse files Browse the repository at this point in the history
<!-- Description: Please provide a summary of the changes and the
motivation behind them. -->
[ic/iso] Support for SELinux.

To enable SELinux for LiveOS ISOs, we need to:
1. Update the SELinux policies in the selinux-policy package.
2. Update the overlay logic to mark the overlay folders correctly
   in dracut's overlayfs module.

Both these changes require updated versions of corresponding packages.

This change updates MIC such that:
- check if the installed selinux-policy and dracut packages are the
  new packages (or newer).
- if either one is older, selinux is disabled and a warning is printed
  out.

Note that disabling selinux for LiveOS isos has been the default
behavior
in the past.

The changes include the following refactoring:
- moving package related functions to a new packageinformation.go file.
- introducing a new liveosisoutils.go to hold some of the new helper
  functions. This is because the main liveosisobild.go has grown too
  big and we need to start breaking it down.

---

### **Checklist**
- [n/a] Tests added/updated
- [x] Documentation updated (if needed)
- [x] Code conforms to style guidelines
  • Loading branch information
gmileka authored Dec 12, 2024
1 parent 97a3fb6 commit 10a90a1
Show file tree
Hide file tree
Showing 9 changed files with 435 additions and 201 deletions.
3 changes: 0 additions & 3 deletions toolkit/tools/imagecustomizer/docs/iso.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ almost all the artifacts unchanged - some artifacts are changed as follows:
- the root is updated to the LiveOS root file system image.
- the LiveOS dracut parameters are appended.
- the user-specified new parameters are appended.
- SELinux is disabled.
- `/etc/fstab` is dropped from the rootfs as it typically conflicts with the
overlay setup required by the LiveOS.
- `initrd.img` is regenerated to serve the LiveOS boot flow. This should have
Expand All @@ -42,8 +41,6 @@ The current implementation for the LiveOS iso does not support the following:
- disk layout.
- There is always one disk generated when an `iso` output format is
specified.
- SELinux
- No SELinux configuration is supported for the generated iso image.
- non-"Azure Linux toolkit" images
- images generated by tools other than the Azure Linux toolkit may fail to be
converted to a LiveOS iso. This is due to certain assumptions on the input
Expand Down
81 changes: 0 additions & 81 deletions toolkit/tools/pkg/imagecustomizerlib/customizepackages.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package imagecustomizerlib
import (
"fmt"
"regexp"
"strconv"
"strings"

"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
Expand All @@ -26,13 +25,6 @@ var (
tdnfTransactionError = regexp.MustCompile(`^Found \d+ problems$`)
)

type packageInformation struct {
packageVersion string
packageRelease uint32
distroName string
distroVersion uint32
}

func addRemoveAndUpdatePackages(buildDir string, baseConfigPath string, config *imagecustomizerapi.OS,
imageChroot *safechroot.Chroot, rpmsSources []string, useBaseImageRpmRepos bool,
) error {
Expand Down Expand Up @@ -242,79 +234,6 @@ func isPackageInstalled(imageChroot *safechroot.Chroot, packageName string) bool
return true
}

func parseReleaseString(releaseInfo string) (packageRelease uint32, distroName string, distroVersion uint32, err error) {
pattern := `([0-9]+)\.([a-zA-Z]+)([0-9]+)`
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(releaseInfo)

if matches == nil {
return 0, "", 0, fmt.Errorf("failed to parse package release information (%s)\n%w", releaseInfo, err)
}

// package release
packageReleaseString := matches[1]
packageReleaseUint64, err := strconv.ParseUint(packageReleaseString, 10 /*base*/, 32 /*size*/)
if err != nil {
return 0, "", 0, fmt.Errorf("failed to parse package release version (%s) into an unsigned integer:\n%w", packageReleaseString, err)
}
packageRelease = uint32(packageReleaseUint64)

// distro name
distroName = matches[2]

// distro version
distroVersionString := matches[3]
distroVersionUint64, err := strconv.ParseUint(distroVersionString, 10 /*base*/, 32 /*size*/)
if err != nil {
return 0, "", 0, fmt.Errorf("failed to parse distro version (%s) into an unsigned integer:\n%w", distroVersionString, err)
}
distroVersion = uint32(distroVersionUint64)

return packageRelease, distroName, distroVersion, nil
}

func getPackageInformation(imageChroot *safechroot.Chroot, packageName string) (info packageInformation, err error) {
var packageInfo string
err = imageChroot.UnsafeRun(func() error {
packageInfo, _, err = shell.Execute("tdnf", "info", packageName, "--repo", "@system")
return err
})
if err != nil {
return info, fmt.Errorf("failed to query (%s) package information:\n%w", packageName, err)
}

// Regular expressions to match Version and Release
versionRegex := regexp.MustCompile(`(?m)^Version\s+:\s+(\S+)`)
versionMatch := versionRegex.FindStringSubmatch(packageInfo)
var packageVersion string
if len(versionMatch) != 2 {
return info, fmt.Errorf("failed to extract version information from the (%s) package information (\n%s\n):\n%w", packageName, packageInfo, err)
}
packageVersion = versionMatch[1]

// Extract Release
releaseRegex := regexp.MustCompile(`(?m)^Release\s+:\s+(\S+)`)
releaseMatch := releaseRegex.FindStringSubmatch(packageInfo)
var releaseInfo string
if len(releaseMatch) != 2 {
return info, fmt.Errorf("failed to extract release information from the (%s) package information (\n%s\n):\n%w", packageName, packageInfo, err)
}
releaseInfo = releaseMatch[1]

packageRelease, distroName, distroVersion, err := parseReleaseString(releaseInfo)
if err != nil {
return info, fmt.Errorf("failed to parse release information for package (%s)\n%w", packageName, err)
}

// Set return values
info.packageVersion = packageVersion
info.packageRelease = packageRelease
info.distroName = distroName
info.distroVersion = distroVersion

return info, nil
}

func cleanTdnfCache(imageChroot *safechroot.Chroot) error {
logger.Log.Infof("Cleaning up RPM cache")
// Run all cleanup tasks inside the chroot environment
Expand Down
78 changes: 0 additions & 78 deletions toolkit/tools/pkg/imagecustomizerlib/dracututils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,11 @@ import (
"fmt"
"os"
"path/filepath"
"strconv"

"github.com/microsoft/azurelinux/toolkit/tools/internal/file"
"github.com/microsoft/azurelinux/toolkit/tools/internal/safechroot"
)

const (
PxeDracutMinVersion = 102
PxeDracutMinPackageRelease = 7
PxeDracutDistroName = "azl"
PxeDracutMinDistroVersion = 3
)

type DracutPackageInformation struct {
PackageVersion uint32 `yaml:"packageVersion"`
PackageRelease uint32 `yaml:"packageRelease"`
DistroName string `yaml:"distroName"`
DistroVersion uint32 `yaml:"distroVersion"`
}

func addDracutConfig(dracutConfigFile string, lines []string) error {
if _, err := os.Stat(dracutConfigFile); os.IsNotExist(err) {
err := file.WriteLines(lines, dracutConfigFile)
Expand Down Expand Up @@ -63,66 +48,3 @@ func addDracutDriver(dracutDriverName string, imageChroot *safechroot.Chroot) er
}
return addDracutConfig(dracutConfigFile, lines)
}

func getDracutVersion(rootfsSourceDir string) (dracutPackageInfo *DracutPackageInformation, err error) {
chroot := safechroot.NewChroot(rootfsSourceDir, true /*isExistingDir*/)
if chroot == nil {
return nil, fmt.Errorf("failed to create a new chroot object for %s.", rootfsSourceDir)
}
defer chroot.Close(true /*leaveOnDisk*/)

err = chroot.Initialize("", nil, nil, true /*includeDefaultMounts*/)
if err != nil {
return nil, fmt.Errorf("failed to initialize chroot object for %s:\n%w", rootfsSourceDir, err)
}

packageName := "dracut"
packageInfo, err := getPackageInformation(chroot, packageName)
if err != nil {
return nil, fmt.Errorf("failed to get package version for (%s):\n%w", packageName, err)
}
versionUint64, err := strconv.ParseUint(packageInfo.packageVersion, 10 /*base*/, 32 /*size*/)
if err != nil {
return nil, fmt.Errorf("failed to parse package version (%s) for (%s) into an unsigned integer:\n%w", packageInfo.packageVersion, packageName, err)
}

dracutPackageInfo = &DracutPackageInformation{
PackageVersion: uint32(versionUint64),
PackageRelease: packageInfo.packageRelease,
DistroName: packageInfo.distroName,
DistroVersion: packageInfo.distroVersion,
}

return dracutPackageInfo, nil
}

func verifyDracutPXESupport(packageInfo *DracutPackageInformation) error {
if packageInfo == nil {
return fmt.Errorf("no dracut package information provided")
}

if packageInfo.DistroName != PxeDracutDistroName {
return fmt.Errorf("did not find required Azure Linux distro (%s) - found (%s)", PxeDracutDistroName, packageInfo.DistroName)
}

if packageInfo.DistroVersion < PxeDracutMinDistroVersion {
return fmt.Errorf("did not find required Azure Linux distro version (%d) - found (%d)", PxeDracutMinDistroVersion, packageInfo.DistroVersion)
}

// Note that, theoretically, an new distro version could still have an older package version.
// So, it is not sufficient to check that packageInfo.DistroVersion > PxeDracutMinDistroVersion.
// We need to check the package version number.

if packageInfo.PackageVersion < PxeDracutMinVersion {
return fmt.Errorf("did not find required Dracut package version (%d-%d) - found (%d-%d)",
PxeDracutMinVersion, PxeDracutMinPackageRelease, packageInfo.PackageVersion, packageInfo.PackageRelease)
} else if packageInfo.PackageVersion > PxeDracutMinVersion {
return nil
}

if packageInfo.PackageRelease < PxeDracutMinPackageRelease {
return fmt.Errorf("did not find required Dracut package release (%d-%d) - found (%d-%d)",
PxeDracutMinVersion, PxeDracutMinPackageRelease, packageInfo.PackageVersion, packageInfo.PackageRelease)
}
return nil
}
8 changes: 6 additions & 2 deletions toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,12 @@ func convertWriteableFormatToOutputImage(ic *ImageCustomizerParameters, inputIso

case ImageFormatIso:
if ic.customizeOSPartitions || inputIsoArtifacts == nil {
err := createLiveOSIsoImage(ic.buildDir, ic.configPath, inputIsoArtifacts, ic.config.Iso, ic.config.Pxe, ic.rawImageFile,
ic.outputImageDir, ic.outputImageBase, ic.outputPXEArtifactsDir)
requestedSELinuxMode := imagecustomizerapi.SELinuxModeDefault
if ic.config.OS != nil {
requestedSELinuxMode = ic.config.OS.SELinux.Mode
}
err := createLiveOSIsoImage(ic.buildDir, ic.configPath, inputIsoArtifacts, requestedSELinuxMode, ic.config.Iso, ic.config.Pxe,
ic.rawImageFile, ic.outputImageDir, ic.outputImageBase, ic.outputPXEArtifactsDir)
if err != nil {
return fmt.Errorf("failed to create LiveOS iso image:\n%w", err)
}
Expand Down
Loading

0 comments on commit 10a90a1

Please sign in to comment.