OSModifier: Add support for updating grub (#9874)
Co-authored-by: Ubuntu <azureuser@elaine-dev2.ympr0pkouz1evfbws3zckpfswc.bx.internal.cloudapp.net>
This commit is contained in:
Родитель
a83715e18b
Коммит
449f279ffc
|
@ -1474,7 +1474,7 @@ func installGrubTemplateFile(assetFile, targetFile, installRoot, rootDevice, boo
|
|||
return
|
||||
}
|
||||
|
||||
func CallGrubMkconfig(installChroot *safechroot.Chroot) (err error) {
|
||||
func CallGrubMkconfig(installChroot safechroot.ChrootInterface) (err error) {
|
||||
squashErrors := false
|
||||
|
||||
ReportActionf("Running grub2-mkconfig...")
|
||||
|
@ -1965,7 +1965,7 @@ func SELinuxConfigure(selinuxMode configuration.SELinux, installChroot *safechro
|
|||
return
|
||||
}
|
||||
|
||||
func SELinuxUpdateConfig(selinuxMode configuration.SELinux, installChroot *safechroot.Chroot) (err error) {
|
||||
func SELinuxUpdateConfig(selinuxMode configuration.SELinux, installChroot safechroot.ChrootInterface) (err error) {
|
||||
const (
|
||||
selinuxPattern = "^SELINUX=.*"
|
||||
)
|
||||
|
@ -1986,7 +1986,7 @@ func SELinuxUpdateConfig(selinuxMode configuration.SELinux, installChroot *safec
|
|||
return
|
||||
}
|
||||
|
||||
func SELinuxRelabelFiles(installChroot *safechroot.Chroot, mountPointToFsTypeMap map[string]string, isRootFS bool,
|
||||
func SELinuxRelabelFiles(installChroot safechroot.ChrootInterface, mountPointToFsTypeMap map[string]string, isRootFS bool,
|
||||
) (err error) {
|
||||
const (
|
||||
fileContextBasePath = "etc/selinux/%s/contexts/files/file_contexts"
|
||||
|
|
|
@ -18,10 +18,11 @@ import (
|
|||
var (
|
||||
app = kingpin.New("osmodifier", "Used to modify os")
|
||||
|
||||
configFile = app.Flag("config-file", "Path of the os modification config file.").Required().String()
|
||||
configFile = app.Flag("config-file", "Path of the os modification config file.").String()
|
||||
logFlags = exe.SetupLogFlags(app)
|
||||
profFlags = exe.SetupProfileFlags(app)
|
||||
timestampFile = app.Flag("timestamp-file", "File that stores timestamps for this program.").String()
|
||||
updateGrub = app.Flag("update-grub", "Update default GRUB.").Bool()
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -40,9 +41,19 @@ func main() {
|
|||
timestamp.BeginTiming("osmodifier", *timestampFile)
|
||||
defer timestamp.CompleteTiming()
|
||||
|
||||
err = modifyImage()
|
||||
if err != nil {
|
||||
log.Fatalf("os modification failed: %v", err)
|
||||
// Check if the updateGrub flag is set
|
||||
if *updateGrub {
|
||||
err := osmodifierlib.ModifyDefaultGrub()
|
||||
if err != nil {
|
||||
log.Fatalf("update grub failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*configFile) > 0 {
|
||||
err = modifyImage()
|
||||
if err != nil {
|
||||
log.Fatalf("OS modification failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package osmodifierapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type IdentifiedPartition struct {
|
||||
Id string `yaml:"id"`
|
||||
}
|
||||
|
||||
func (i *IdentifiedPartition) IsValid() error {
|
||||
// Check if Id is not empty
|
||||
if i.Id == "" {
|
||||
return fmt.Errorf("invalid id: empty string")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package osmodifierapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
|
||||
)
|
||||
|
||||
// OS defines how each system present on the image is supposed to be configured.
|
||||
type OS struct {
|
||||
Hostname string `yaml:"hostname"`
|
||||
SELinux imagecustomizerapi.SELinux `yaml:"selinux"`
|
||||
Users []imagecustomizerapi.User `yaml:"users"`
|
||||
Overlays *[]Overlay `yaml:"overlays"`
|
||||
}
|
||||
|
||||
func (s *OS) IsValid() error {
|
||||
var err error
|
||||
|
||||
if s.Hostname != "" {
|
||||
if !govalidator.IsDNSName(s.Hostname) || strings.Contains(s.Hostname, "_") {
|
||||
return fmt.Errorf("invalid hostname (%s)", s.Hostname)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.SELinux.IsValid()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid selinux:\n%w", err)
|
||||
}
|
||||
|
||||
for i, user := range s.Users {
|
||||
err = user.IsValid()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid users item at index %d:\n%w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.Overlays != nil {
|
||||
upperDirs := make(map[string]bool)
|
||||
workDirs := make(map[string]bool)
|
||||
|
||||
for i, overlay := range *s.Overlays {
|
||||
// Validate the overlay itself
|
||||
err := overlay.IsValid()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid overlay at index %d:\n%w", i, err)
|
||||
}
|
||||
|
||||
// Check for unique UpperDir
|
||||
if _, exists := upperDirs[overlay.UpperDir]; exists {
|
||||
return fmt.Errorf("duplicate upperDir (%s) found in overlay at index %d", overlay.UpperDir, i)
|
||||
}
|
||||
upperDirs[overlay.UpperDir] = true
|
||||
|
||||
// Check for unique WorkDir
|
||||
if _, exists := workDirs[overlay.WorkDir]; exists {
|
||||
return fmt.Errorf("duplicate workDir (%s) found in overlay at index %d", overlay.WorkDir, i)
|
||||
}
|
||||
workDirs[overlay.WorkDir] = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package osmodifierapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Overlay struct {
|
||||
LowerDir string `yaml:"lowerDir"`
|
||||
UpperDir string `yaml:"upperDir"`
|
||||
WorkDir string `yaml:"workDir"`
|
||||
Partition *IdentifiedPartition `yaml:"partition"`
|
||||
}
|
||||
|
||||
func (o *Overlay) IsValid() error {
|
||||
// Validate paths for UpperDir, WorkDir, and LowerDir
|
||||
if err := validatePath(o.UpperDir); err != nil {
|
||||
return fmt.Errorf("invalid upperDir (%s):\n%w", o.UpperDir, err)
|
||||
}
|
||||
if err := validatePath(o.WorkDir); err != nil {
|
||||
return fmt.Errorf("invalid workDir (%s):\n%w", o.WorkDir, err)
|
||||
}
|
||||
if err := validatePath(o.LowerDir); err != nil {
|
||||
return fmt.Errorf("invalid lowerDir (%s):\n%w", o.LowerDir, err)
|
||||
}
|
||||
|
||||
// Check if UpperDir and WorkDir are identical
|
||||
if o.UpperDir == o.WorkDir {
|
||||
return fmt.Errorf("upperDir and workDir must be distinct, but both are '%s'", o.UpperDir)
|
||||
}
|
||||
|
||||
// Check if UpperDir is a subdirectory of WorkDir or vice versa
|
||||
if isSubDirString(o.UpperDir, o.WorkDir) {
|
||||
return fmt.Errorf("upperDir (%s) should not be a subdirectory of workDir (%s)", o.UpperDir, o.WorkDir)
|
||||
}
|
||||
if isSubDirString(o.WorkDir, o.UpperDir) {
|
||||
return fmt.Errorf("workDir (%s) should not be a subdirectory of upperDir (%s)", o.WorkDir, o.UpperDir)
|
||||
}
|
||||
|
||||
if o.Partition != nil {
|
||||
if err := o.Partition.IsValid(); err != nil {
|
||||
return fmt.Errorf("invalid partition:\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePath(path string) error {
|
||||
// Check if the path is empty
|
||||
if path == "" {
|
||||
return fmt.Errorf("path cannot be empty")
|
||||
}
|
||||
|
||||
// Check if the path contains spaces
|
||||
if strings.Contains(path, " ") {
|
||||
return fmt.Errorf("path (%s) contains spaces and is invalid", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSubDirString(dir1, dir2 string) bool {
|
||||
// Ensure paths are cleaned and have consistent trailing slashes
|
||||
cleanDir1 := strings.TrimSuffix(dir1, "/") + "/"
|
||||
cleanDir2 := strings.TrimSuffix(dir2, "/") + "/"
|
||||
|
||||
// Check if dir2 starts with dir1 (indicating a subdirectory)
|
||||
return cleanDir1 != cleanDir2 && strings.HasPrefix(cleanDir2, cleanDir1)
|
||||
}
|
|
@ -23,8 +23,8 @@ type BootCustomizer struct {
|
|||
isGrubMkconfig bool
|
||||
}
|
||||
|
||||
func NewBootCustomizer(imageChroot *safechroot.Chroot) (*BootCustomizer, error) {
|
||||
grubCfgContent, err := readGrub2ConfigFile(imageChroot)
|
||||
func NewBootCustomizer(imageChroot safechroot.ChrootInterface) (*BootCustomizer, error) {
|
||||
grubCfgContent, err := ReadGrub2ConfigFile(imageChroot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func (b *BootCustomizer) getSELinuxModeFromGrub() (imagecustomizerapi.SELinuxMod
|
|||
|
||||
// Get the SELinux kernel command-line args.
|
||||
if b.isGrubMkconfig {
|
||||
_, args, _, err = getDefaultGrubFileLinuxArgs(b.defaultGrubFileContent, defaultGrubFileVarNameCmdlineForSELinux)
|
||||
_, args, _, err = GetDefaultGrubFileLinuxArgs(b.defaultGrubFileContent, defaultGrubFileVarNameCmdlineForSELinux)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func (b *BootCustomizer) getSELinuxModeFromGrub() (imagecustomizerapi.SELinuxMod
|
|||
return selinuxMode, nil
|
||||
}
|
||||
|
||||
func (b *BootCustomizer) GetSELinuxMode(imageChroot *safechroot.Chroot) (imagecustomizerapi.SELinuxMode, error) {
|
||||
func (b *BootCustomizer) GetSELinuxMode(imageChroot safechroot.ChrootInterface) (imagecustomizerapi.SELinuxMode, error) {
|
||||
// Get the SELinux mode from the kernel command-line args.
|
||||
selinuxMode, err := b.getSELinuxModeFromGrub()
|
||||
if err != nil {
|
||||
|
@ -163,7 +163,7 @@ func (b *BootCustomizer) UpdateKernelCommandLineArgs(defaultGrubFileVarName defa
|
|||
func (b *BootCustomizer) PrepareForVerity() error {
|
||||
if b.isGrubMkconfig {
|
||||
// Force root command-line arg to be referenced by /dev path instead of by UUID.
|
||||
defaultGrubFileContent, err := updateDefaultGrubFileVariable(b.defaultGrubFileContent, "GRUB_DISABLE_UUID",
|
||||
defaultGrubFileContent, err := UpdateDefaultGrubFileVariable(b.defaultGrubFileContent, "GRUB_DISABLE_UUID",
|
||||
"true")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -171,26 +171,31 @@ func (b *BootCustomizer) PrepareForVerity() error {
|
|||
|
||||
// Disable recovery menu entry, to avoid having more than 1 linux command in the grub.cfg file.
|
||||
// This will make it easier to modify the grub.cfg file to add the verity args.
|
||||
defaultGrubFileContent, err = updateDefaultGrubFileVariable(defaultGrubFileContent, "GRUB_DISABLE_RECOVERY",
|
||||
defaultGrubFileContent, err = UpdateDefaultGrubFileVariable(defaultGrubFileContent, "GRUB_DISABLE_RECOVERY",
|
||||
"true")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For verity, the root device will always be "/dev/mapper/root"
|
||||
defaultGrubFileContent, err = UpdateDefaultGrubFileVariable(defaultGrubFileContent, "GRUB_DEVICE", "/dev/mapper/root")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.defaultGrubFileContent = defaultGrubFileContent
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BootCustomizer) WriteToFile(imageChroot *safechroot.Chroot) error {
|
||||
func (b *BootCustomizer) WriteToFile(imageChroot safechroot.ChrootInterface) error {
|
||||
if b.isGrubMkconfig {
|
||||
// Update /etc/defaukt/grub file.
|
||||
err := writeDefaultGrubFile(b.defaultGrubFileContent, imageChroot)
|
||||
err := WriteDefaultGrubFile(b.defaultGrubFileContent, imageChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update /boot/grub2/grub.cfg file.
|
||||
err = installutils.CallGrubMkconfig(imageChroot)
|
||||
if err != nil {
|
||||
|
|
|
@ -161,9 +161,10 @@ func TestBootCustomizerVerity30(t *testing.T) {
|
|||
err := b.PrepareForVerity()
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedDefaultGrubFileDiff := `6a7,8
|
||||
expectedDefaultGrubFileDiff := `6a7,9
|
||||
> GRUB_DISABLE_UUID="true"
|
||||
> GRUB_DISABLE_RECOVERY="true"
|
||||
> GRUB_DEVICE="/dev/mapper/root"
|
||||
`
|
||||
checkDiffs30(t, b, "", expectedDefaultGrubFileDiff)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func handleSELinux(selinuxMode imagecustomizerapi.SELinuxMode, resetBootLoaderTy
|
|||
}
|
||||
}
|
||||
|
||||
err = updateSELinuxModeInConfigFile(selinuxMode, imageChroot)
|
||||
err = UpdateSELinuxModeInConfigFile(selinuxMode, imageChroot)
|
||||
if err != nil {
|
||||
return imagecustomizerapi.SELinuxModeDefault, err
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func handleSELinux(selinuxMode imagecustomizerapi.SELinuxMode, resetBootLoaderTy
|
|||
return selinuxMode, nil
|
||||
}
|
||||
|
||||
func updateSELinuxModeInConfigFile(selinuxMode imagecustomizerapi.SELinuxMode, imageChroot *safechroot.Chroot) error {
|
||||
func UpdateSELinuxModeInConfigFile(selinuxMode imagecustomizerapi.SELinuxMode, imageChroot safechroot.ChrootInterface) error {
|
||||
imagerSELinuxMode, err := selinuxModeToImager(selinuxMode)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -137,7 +137,7 @@ func findDefaultGrubFileVarAssign(varAssigns []defaultGrubFileVarAssign, name de
|
|||
// - cmdLineVarAssign: The variable assignment that matches 'varName'.
|
||||
// - args: The list of kernel command-line args.
|
||||
// - insertAt: An index that new kernel command-line args can be inserted at.
|
||||
func getDefaultGrubFileLinuxArgs(defaultGrubFileContent string, varName defaultGrubFileVarName,
|
||||
func GetDefaultGrubFileLinuxArgs(defaultGrubFileContent string, varName defaultGrubFileVarName,
|
||||
) (defaultGrubFileVarAssign, []grubConfigLinuxArg, int, error) {
|
||||
varAssigns, err := findDefaultGrubFileVarAssigns(defaultGrubFileContent)
|
||||
if err != nil {
|
||||
|
@ -174,7 +174,7 @@ func getDefaultGrubFileLinuxArgs(defaultGrubFileContent string, varName defaultG
|
|||
insertAt = len(argsString)
|
||||
}
|
||||
|
||||
args, err := parseCommandLineArgs(grubTokens)
|
||||
args, err := ParseCommandLineArgs(grubTokens)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse %s's value args:\n%w", varName, err)
|
||||
return defaultGrubFileVarAssign{}, nil, 0, err
|
||||
|
@ -185,7 +185,7 @@ func getDefaultGrubFileLinuxArgs(defaultGrubFileContent string, varName defaultG
|
|||
|
||||
// Takes the string contents of /etc/default/grub file and inserts the provided command-line args.
|
||||
func addExtraCommandLineToDefaultGrubFile(defaultGrubFileContent string, extraCommandLine string) (string, error) {
|
||||
cmdLineVarAssign, _, insertAt, err := getDefaultGrubFileLinuxArgs(defaultGrubFileContent,
|
||||
cmdLineVarAssign, _, insertAt, err := GetDefaultGrubFileLinuxArgs(defaultGrubFileContent,
|
||||
defaultGrubFileVarNameCmdlineLinuxDefault)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -215,7 +215,7 @@ func addExtraCommandLineToDefaultGrubFile(defaultGrubFileContent string, extraCo
|
|||
func updateDefaultGrubFileKernelCommandLineArgs(defaultGrubFileContent string, varName defaultGrubFileVarName,
|
||||
argsToRemove []string, newArgs []string,
|
||||
) (string, error) {
|
||||
cmdLineVarAssign, args, insertAt, err := getDefaultGrubFileLinuxArgs(defaultGrubFileContent, varName)
|
||||
cmdLineVarAssign, args, insertAt, err := GetDefaultGrubFileLinuxArgs(defaultGrubFileContent, varName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ func insertDefaultGrubFileVarAssign(defaultGrubFileContent string, insertAfterLi
|
|||
|
||||
// Sets the value of a variable in the /etc/default/grub file, either replacing the existing variable value (if one
|
||||
// exists) or adding a new one.
|
||||
func updateDefaultGrubFileVariable(defaultGrubFileContent string, varName string, newValue string) (string, error) {
|
||||
func UpdateDefaultGrubFileVariable(defaultGrubFileContent string, varName string, newValue string) (string, error) {
|
||||
varAssigns, err := findDefaultGrubFileVarAssigns(defaultGrubFileContent)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse %s file:\n%w", installutils.GrubDefFile, err)
|
||||
|
@ -320,7 +320,7 @@ func updateDefaultGrubFileVariable(defaultGrubFileContent string, varName string
|
|||
|
||||
// Checks if the image uses grub-mkconfig.
|
||||
func isGrubMkconfigEnabled(imageChroot *safechroot.Chroot) (bool, error) {
|
||||
grub2ConfigFile, err := readGrub2ConfigFile(imageChroot)
|
||||
grub2ConfigFile, err := ReadGrub2ConfigFile(imageChroot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ func isGrubMkconfigConfig(grub2Config string) bool {
|
|||
}
|
||||
|
||||
// Reads the string contents of the /etc/default/grub file.
|
||||
func readDefaultGrubFile(imageChroot *safechroot.Chroot) (string, error) {
|
||||
func readDefaultGrubFile(imageChroot safechroot.ChrootInterface) (string, error) {
|
||||
logger.Log.Debugf("Reading %s file", installutils.GrubDefFile)
|
||||
|
||||
grub2ConfigFilePath := getDefaultGrubFilePath(imageChroot)
|
||||
|
@ -351,7 +351,7 @@ func readDefaultGrubFile(imageChroot *safechroot.Chroot) (string, error) {
|
|||
}
|
||||
|
||||
// Writes the string contents of the /etc/default/grub file.
|
||||
func writeDefaultGrubFile(grub2Config string, imageChroot *safechroot.Chroot) error {
|
||||
func WriteDefaultGrubFile(grub2Config string, imageChroot safechroot.ChrootInterface) error {
|
||||
logger.Log.Debugf("Writing %s file", installutils.GrubDefFile)
|
||||
|
||||
grub2ConfigFilePath := getDefaultGrubFilePath(imageChroot)
|
||||
|
@ -365,6 +365,6 @@ func writeDefaultGrubFile(grub2Config string, imageChroot *safechroot.Chroot) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func getDefaultGrubFilePath(imageChroot *safechroot.Chroot) string {
|
||||
func getDefaultGrubFilePath(imageChroot safechroot.ChrootInterface) string {
|
||||
return filepath.Join(imageChroot.RootDir(), installutils.GrubDefFile)
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func findLinuxOrInitrdLineAll(inputGrubCfgContent string, commandName string, al
|
|||
}
|
||||
|
||||
// Find the linux command within the grub config file.
|
||||
func findLinuxLine(inputGrubCfgContent string) (grub.Line, error) {
|
||||
func FindLinuxLine(inputGrubCfgContent string) (grub.Line, error) {
|
||||
lines, err := findLinuxOrInitrdLineAll(inputGrubCfgContent, linuxCommand, false /*allowMultiple*/)
|
||||
if err != nil {
|
||||
return grub.Line{}, err
|
||||
|
@ -245,7 +245,7 @@ func getLinuxCommandLineArgs(grub2Config string, requireKernelOpts bool) ([]grub
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
args, err := parseCommandLineArgs(argTokens)
|
||||
args, err := ParseCommandLineArgs(argTokens)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ func findCommandLineInsertAt(argTokens []grub.Token, requireKernelOpts bool) (in
|
|||
}
|
||||
|
||||
// Takes a tokenized grub.cfg file and makes a best effort to extract the kernel command-line args.
|
||||
func parseCommandLineArgs(argTokens []grub.Token) ([]grubConfigLinuxArg, error) {
|
||||
func ParseCommandLineArgs(argTokens []grub.Token) ([]grubConfigLinuxArg, error) {
|
||||
args := []grubConfigLinuxArg(nil)
|
||||
|
||||
for i := range argTokens {
|
||||
|
@ -400,7 +400,7 @@ func replaceKernelCommandLineArgValueAll(inputGrubCfgContent string, name string
|
|||
// Skip the "linux" command and the kernel binary path arg.
|
||||
argTokens := line.Tokens[2:]
|
||||
|
||||
args, err := parseCommandLineArgs(argTokens)
|
||||
args, err := ParseCommandLineArgs(argTokens)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ func updateKernelCommandLineArgsAll(grub2Config string, argsToRemove []string, n
|
|||
return "", err
|
||||
}
|
||||
|
||||
args, err := parseCommandLineArgs(argTokens)
|
||||
args, err := ParseCommandLineArgs(argTokens)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ func updateKernelCommandLineArgs(grub2Config string, argsToRemove []string, newA
|
|||
func updateKernelCommandLineArgsHelper(value string, args []grubConfigLinuxArg, insertAt int,
|
||||
argsToRemove []string, newArgs []string,
|
||||
) (string, error) {
|
||||
newArgsQuoted := grubArgsToString(newArgs)
|
||||
newArgsQuoted := GrubArgsToString(newArgs)
|
||||
foundArgs := findMatchingCommandLineArgs(args, argsToRemove)
|
||||
|
||||
builder := strings.Builder{}
|
||||
|
@ -511,7 +511,7 @@ func updateKernelCommandLineArgsHelper(value string, args []grubConfigLinuxArg,
|
|||
|
||||
// Takes a list of unescaped and unquoted kernel command-line args and combines them into a single string with
|
||||
// appropriate quoting for a grub.cfg file.
|
||||
func grubArgsToString(args []string) string {
|
||||
func GrubArgsToString(args []string) string {
|
||||
builder := strings.Builder{}
|
||||
for i, arg := range args {
|
||||
if i != 0 {
|
||||
|
@ -666,7 +666,7 @@ func getSELinuxModeFromLinuxArgs(args []grubConfigLinuxArg) (imagecustomizerapi.
|
|||
}
|
||||
|
||||
// Gets the SELinux mode set by the /etc/selinux/config file.
|
||||
func getSELinuxModeFromConfigFile(imageChroot *safechroot.Chroot) (imagecustomizerapi.SELinuxMode, error) {
|
||||
func getSELinuxModeFromConfigFile(imageChroot safechroot.ChrootInterface) (imagecustomizerapi.SELinuxMode, error) {
|
||||
selinuxConfigFilePath := filepath.Join(imageChroot.RootDir(), installutils.SELinuxConfigFile)
|
||||
|
||||
// Read the SELinux config file.
|
||||
|
@ -701,7 +701,7 @@ func getSELinuxModeFromConfigFile(imageChroot *safechroot.Chroot) (imagecustomiz
|
|||
}
|
||||
|
||||
// Reads the /boot/grub2/grub.cfg file.
|
||||
func readGrub2ConfigFile(imageChroot *safechroot.Chroot) (string, error) {
|
||||
func ReadGrub2ConfigFile(imageChroot safechroot.ChrootInterface) (string, error) {
|
||||
logger.Log.Debugf("Reading grub.cfg file")
|
||||
|
||||
grub2ConfigFilePath := getGrub2ConfigFilePath(imageChroot)
|
||||
|
@ -716,7 +716,7 @@ func readGrub2ConfigFile(imageChroot *safechroot.Chroot) (string, error) {
|
|||
}
|
||||
|
||||
// Writes the /boot/grub2/grub.cfg file.
|
||||
func writeGrub2ConfigFile(grub2Config string, imageChroot *safechroot.Chroot) error {
|
||||
func writeGrub2ConfigFile(grub2Config string, imageChroot safechroot.ChrootInterface) error {
|
||||
logger.Log.Debugf("Writing grub.cfg file")
|
||||
|
||||
grub2ConfigFilePath := getGrub2ConfigFilePath(imageChroot)
|
||||
|
@ -730,7 +730,7 @@ func writeGrub2ConfigFile(grub2Config string, imageChroot *safechroot.Chroot) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func getGrub2ConfigFilePath(imageChroot *safechroot.Chroot) string {
|
||||
func getGrub2ConfigFilePath(imageChroot safechroot.ChrootInterface) string {
|
||||
return filepath.Join(imageChroot.RootDir(), installutils.GrubCfgFile)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,19 @@
|
|||
package osmodifierlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/internal/safechroot"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/osmodifierapi"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/pkg/imagecustomizerlib"
|
||||
)
|
||||
|
||||
func doModifications(baseConfigPath string, osConfig *imagecustomizerapi.OS) error {
|
||||
func doModifications(baseConfigPath string, osConfig *osmodifierapi.OS) error {
|
||||
var dummyChroot safechroot.ChrootInterface = &safechroot.DummyChroot{}
|
||||
|
||||
err := imagecustomizerlib.AddOrUpdateUsers(osConfig.Users, baseConfigPath, dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -21,5 +27,98 @@ func doModifications(baseConfigPath string, osConfig *imagecustomizerapi.OS) err
|
|||
return err
|
||||
}
|
||||
|
||||
if osConfig.Overlays != nil {
|
||||
bootCustomizer, err := imagecustomizerlib.NewBootCustomizer(dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateGrubConfigForOverlay(*osConfig.Overlays, bootCustomizer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bootCustomizer.WriteToFile(dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if osConfig.SELinux.Mode != "" {
|
||||
bootCustomizer, err := imagecustomizerlib.NewBootCustomizer(dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = handleSELinux(osConfig.SELinux.Mode, bootCustomizer, dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bootCustomizer.WriteToFile(dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateGrubConfigForOverlay(overlays []osmodifierapi.Overlay, bootCustomizer *imagecustomizerlib.BootCustomizer) error {
|
||||
var err error
|
||||
var overlayConfigs []string
|
||||
|
||||
// Iterate over each Overlay configuration
|
||||
for _, overlay := range overlays {
|
||||
// Construct the argument for each Overlay
|
||||
overlayConfig := fmt.Sprintf(
|
||||
"%s,%s,%s,%s",
|
||||
overlay.LowerDir, overlay.UpperDir, overlay.WorkDir, overlay.Partition.Id,
|
||||
)
|
||||
overlayConfigs = append(overlayConfigs, overlayConfig)
|
||||
}
|
||||
|
||||
// Concatenate all overlay configurations with spaces
|
||||
concatenatedOverlays := strings.Join(overlayConfigs, " ")
|
||||
|
||||
// Construct the final cmdline argument
|
||||
newArgs := []string{
|
||||
fmt.Sprintf("rd.overlayfs=%s", concatenatedOverlays),
|
||||
}
|
||||
|
||||
err = bootCustomizer.UpdateKernelCommandLineArgs("GRUB_CMDLINE_LINUX", []string{"rd.overlayfs"},
|
||||
newArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSELinux(selinuxMode imagecustomizerapi.SELinuxMode, bootCustomizer *imagecustomizerlib.BootCustomizer, dummyChroot safechroot.ChrootInterface) error {
|
||||
var err error
|
||||
currentSELinuxMode, err := bootCustomizer.GetSELinuxMode(dummyChroot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current SELinux mode:\n%w", err)
|
||||
}
|
||||
|
||||
if selinuxMode == imagecustomizerapi.SELinuxModeDefault || selinuxMode == currentSELinuxMode {
|
||||
// Don't need to change the configured SELinux mode.
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Infof("Configuring SELinux mode")
|
||||
|
||||
err = bootCustomizer.UpdateSELinuxCommandLine(selinuxMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = imagecustomizerlib.UpdateSELinuxModeInConfigFile(selinuxMode, dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No need to set SELinux labels here as in trident there is reset labels at the end
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package osmodifierlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/internal/safechroot"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/internal/sliceutils"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/pkg/imagecustomizerlib"
|
||||
)
|
||||
|
||||
var grubArgs = []string{
|
||||
"rd.overlayfs",
|
||||
"roothash",
|
||||
"rd.systemd.verity",
|
||||
"systemd.verity_root_data",
|
||||
"systemd.verity_root_hash",
|
||||
"systemd.verity_root_options",
|
||||
"selinux",
|
||||
"enforcing",
|
||||
}
|
||||
|
||||
func modifyDefaultGrub() error {
|
||||
var dummyChroot safechroot.ChrootInterface = &safechroot.DummyChroot{}
|
||||
// Get verity, selinux, overlayfs, and root device values from /boot/grub2/grub.cfg
|
||||
values, err := extractValuesFromGrubConfig(dummyChroot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting verity, selinux and overlayfs values from grub.cfg:\n%w", err)
|
||||
}
|
||||
|
||||
bootCustomizer, err := imagecustomizerlib.NewBootCustomizer(dummyChroot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Stamp root device value to /etc/default/grub
|
||||
err = bootCustomizer.PrepareForVerity()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare grub config files for verity:\n%w", err)
|
||||
}
|
||||
|
||||
// Stamp verity, selinux and overlayfs values to /etc/default/grub
|
||||
err = bootCustomizer.UpdateKernelCommandLineArgs("GRUB_CMDLINE_LINUX", grubArgs, values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bootCustomizer.WriteToFile(dummyChroot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to default grub:\n%w", err)
|
||||
} else {
|
||||
logger.Log.Info("Successfully updated default grub")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractValuesFromGrubConfig(imageChroot safechroot.ChrootInterface) ([]string, error) {
|
||||
grubCfgContent, err := imagecustomizerlib.ReadGrub2ConfigFile(imageChroot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line, err := imagecustomizerlib.FindLinuxLine(grubCfgContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argTokens, err := imagecustomizerlib.ParseCommandLineArgs(line.Tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var values []string
|
||||
for _, arg := range argTokens {
|
||||
if sliceutils.ContainsValue(grubArgs, arg.Name) {
|
||||
if arg.Value != "" {
|
||||
values = append(values, arg.Name+"="+arg.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
|
@ -8,12 +8,13 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
|
||||
"github.com/microsoft/azurelinux/toolkit/tools/osmodifierapi"
|
||||
)
|
||||
|
||||
func ModifyOSWithConfigFile(configFile string) error {
|
||||
var err error
|
||||
|
||||
var osConfig imagecustomizerapi.OS
|
||||
var osConfig osmodifierapi.OS
|
||||
err = imagecustomizerapi.UnmarshalYamlFile(configFile, &osConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -34,7 +35,7 @@ func ModifyOSWithConfigFile(configFile string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ModifyOS(baseConfigPath string, osConfig *imagecustomizerapi.OS) error {
|
||||
func ModifyOS(baseConfigPath string, osConfig *osmodifierapi.OS) error {
|
||||
err := doModifications(baseConfigPath, osConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -42,3 +43,12 @@ func ModifyOS(baseConfigPath string, osConfig *imagecustomizerapi.OS) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ModifyDefaultGrub() error {
|
||||
err := modifyDefaultGrub()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче