-
Notifications
You must be signed in to change notification settings - Fork 573
/
Copy pathimageconfigvalidator.go
179 lines (145 loc) · 5.75 KB
/
imageconfigvalidator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// An image configuration validator
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/configuration"
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/installutils"
"github.com/microsoft/azurelinux/toolkit/tools/internal/exe"
"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
"github.com/microsoft/azurelinux/toolkit/tools/internal/timestamp"
"github.com/microsoft/azurelinux/toolkit/tools/pkg/profile"
"gopkg.in/alecthomas/kingpin.v2"
)
var (
app = kingpin.New("imageconfigvalidator", "A tool for validating image configuration files")
logFlags = exe.SetupLogFlags(app)
profFlags = exe.SetupProfileFlags(app)
input = exe.InputStringFlag(app, "Path to the image config file.")
baseDirPath = exe.InputDirFlag(app, "Base directory for relative file paths from the config.")
timestampFile = app.Flag("timestamp-file", "File that stores timestamps for this program.").String()
)
func main() {
const returnCodeOnError = 1
app.Version(exe.ToolkitVersion)
kingpin.MustParse(app.Parse(os.Args[1:]))
logger.InitBestEffort(logFlags)
prof, err := profile.StartProfiling(profFlags)
if err != nil {
logger.Log.Warnf("Could not start profiling: %s", err)
}
defer prof.StopProfiler()
timestamp.BeginTiming("config validator", *timestampFile)
defer timestamp.CompleteTiming()
inPath, err := filepath.Abs(*input)
logger.PanicOnError(err, "Error when calculating input path")
baseDir, err := filepath.Abs(*baseDirPath)
logger.PanicOnError(err, "Error when calculating input directory")
logger.Log.Infof("Reading configuration file (%s)", inPath)
config, err := configuration.LoadWithAbsolutePaths(inPath, baseDir)
if err != nil {
logger.Log.Fatalf("Failed while loading image configuration '%s': %s", inPath, err)
}
// Basic validation will occur during load, but we can add additional checking here.
err = ValidateConfiguration(config)
if err != nil {
// Log an error here as opposed to panicing to keep the output simple
// and only contain the error with the config file.
logger.Log.Fatalf("Invalid configuration '%s': %s", inPath, err)
}
return
}
// ValidateConfiguration will run sanity checks on a configuration structure
func ValidateConfiguration(config configuration.Config) (err error) {
timestamp.StartEvent("validating config", nil)
defer timestamp.StopEvent(nil)
err = config.IsValid()
if err != nil {
return
}
err = validatePackages(config)
if err != nil {
return
}
err = validateKickStartInstall(config)
return
}
func validateKickStartInstall(config configuration.Config) (err error) {
timestamp.StartEvent("validate kickstart", nil)
defer timestamp.StopEvent(nil)
// If doing a kickstart-style installation, then the image config file
// must not have any partitioning info because that will be provided
// by the preinstall script
for _, systemConfig := range config.SystemConfigs {
if systemConfig.IsKickStartBoot {
if len(config.Disks) > 0 || len(systemConfig.PartitionSettings) > 0 {
return fmt.Errorf("partition should not be specified in image config file when performing kickstart installation")
}
}
}
return
}
func validatePackages(config configuration.Config) (err error) {
timestamp.StartEvent("validate packages", nil)
defer timestamp.StopEvent(nil)
const (
validateError = "failed to validate package lists in config"
kernelPkgName = "kernel"
dracutFipsPkgName = "dracut-fips"
fipsKernelCmdLine = "fips=1"
userAddPkgName = "shadow-utils"
)
for _, systemConfig := range config.SystemConfigs {
packageList, err := installutils.PackageNamesFromSingleSystemConfig(systemConfig)
if err != nil {
return fmt.Errorf("%s: %w", validateError, err)
}
foundSELinuxPackage := false
foundDracutFipsPackage := false
foundUserAddPackage := false
kernelCmdLineString := systemConfig.KernelCommandLine.ExtraCommandLine
selinuxPkgName := systemConfig.KernelCommandLine.SELinuxPolicy
if selinuxPkgName == "" {
selinuxPkgName = configuration.SELinuxPolicyDefault
}
foundKernelPackage, err := installutils.PackagelistContainsPackage(packageList, kernelPkgName)
if err != nil {
return fmt.Errorf("%s: %w", validateError, err)
}
foundDracutFipsPackage, err = installutils.PackagelistContainsPackage(packageList, dracutFipsPkgName)
if err != nil {
return fmt.Errorf("%s: %w", validateError, err)
}
foundSELinuxPackage, err = installutils.PackagelistContainsPackage(packageList, selinuxPkgName)
if err != nil {
return fmt.Errorf("%s: %w", validateError, err)
}
foundUserAddPackage, err = installutils.PackagelistContainsPackage(packageList, userAddPkgName)
if err != nil {
return fmt.Errorf("%s: %w", validateError, err)
}
if foundKernelPackage {
return fmt.Errorf("%s: kernel should not be included in a package list, add via config file's [KernelOptions] entry", validateError)
}
if strings.Contains(kernelCmdLineString, fipsKernelCmdLine) || systemConfig.KernelCommandLine.EnableFIPS {
if !foundDracutFipsPackage {
return fmt.Errorf("%s: 'fips=1' provided on kernel cmdline, but '%s' package is not included in the package lists", validateError, dracutFipsPkgName)
}
}
if systemConfig.KernelCommandLine.SELinux != configuration.SELinuxOff {
if !foundSELinuxPackage {
return fmt.Errorf("%s: [SELinux] selected, but '%s' package is not included in the package lists", validateError, selinuxPkgName)
}
}
if len(systemConfig.Users) > 0 || len(systemConfig.Groups) > 0 {
if !foundUserAddPackage {
return fmt.Errorf("%s: the '%s' package must be included in the package lists when the image is configured to add users or groups", validateError, userAddPkgName)
}
}
}
return
}