diff --git a/Schutzfile b/Schutzfile index d990be4671..a5f2a71df1 100644 --- a/Schutzfile +++ b/Schutzfile @@ -2,7 +2,7 @@ "fedora-38": { "dependencies": { "osbuild": { - "commit": "f3d740aaf8531e55b99632f579f2fae13f1511b7" + "commit": "0767ebccc120eb4a43e274fec64eb95646a66761" } }, "repos": [ @@ -76,4 +76,4 @@ } ] } -} \ No newline at end of file +} diff --git a/pkg/blueprint/customizations.go b/pkg/blueprint/customizations.go index d83ed2ebbd..216f6f21de 100644 --- a/pkg/blueprint/customizations.go +++ b/pkg/blueprint/customizations.go @@ -108,17 +108,6 @@ type ServicesCustomization struct { Disabled []string `json:"disabled,omitempty" toml:"disabled,omitempty"` } -type OpenSCAPCustomization struct { - DataStream string `json:"datastream,omitempty" toml:"datastream,omitempty"` - ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"` - Tailoring *OpenSCAPTailoringCustomizations `json:"tailoring,omitempty" toml:"tailoring,omitempty"` -} - -type OpenSCAPTailoringCustomizations struct { - Selected []string `json:"selected,omitempty" toml:"selected,omitempty"` - Unselected []string `json:"unselected,omitempty" toml:"unselected,omitempty"` -} - type CustomizationError struct { Message string } @@ -316,13 +305,6 @@ func (c *Customizations) GetFDO() *FDOCustomization { return c.FDO } -func (c *Customizations) GetOpenSCAP() *OpenSCAPCustomization { - if c == nil { - return nil - } - return c.OpenSCAP -} - func (c *Customizations) GetIgnition() *IgnitionCustomization { if c == nil { return nil diff --git a/pkg/blueprint/customizations_test.go b/pkg/blueprint/customizations_test.go index d6828113c6..7cc3afdece 100644 --- a/pkg/blueprint/customizations_test.go +++ b/pkg/blueprint/customizations_test.go @@ -371,23 +371,3 @@ func TestGetFilesystemsMinSizeNonSectorSize(t *testing.T) { assert.EqualValues(t, uint64(5632), retFilesystemsSize) } - -func TestGetOpenSCAPConfig(t *testing.T) { - - expectedOscap := OpenSCAPCustomization{ - DataStream: "test-data-stream.xml", - ProfileID: "test_profile", - Tailoring: &OpenSCAPTailoringCustomizations{ - Selected: []string{"quick_rule"}, - Unselected: []string{"very_slow_rule"}, - }, - } - - TestCustomizations := Customizations{ - OpenSCAP: &expectedOscap, - } - - retOpenSCAPCustomiztions := TestCustomizations.GetOpenSCAP() - - assert.EqualValues(t, expectedOscap, *retOpenSCAPCustomiztions) -} diff --git a/pkg/blueprint/openscap_customizations.go b/pkg/blueprint/openscap_customizations.go new file mode 100644 index 0000000000..8eed19b57d --- /dev/null +++ b/pkg/blueprint/openscap_customizations.go @@ -0,0 +1,78 @@ +package blueprint + +import ( + "encoding/json" + "fmt" +) + +type OpenSCAPCustomization struct { + Datastream string `json:"datastream,omitempty" toml:"datastream,omitempty"` + ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"` + Tailoring *OpenSCAPTailoringCustomizations `json:"tailoring,omitempty" toml:"tailoring,omitempty"` +} + +type OpenSCAPTailoringCustomizations struct { + Selected []string `json:"selected,omitempty" toml:"selected,omitempty"` + Unselected []string `json:"unselected,omitempty" toml:"unselected,omitempty"` + Overrides []OpenSCAPTailoringOverride `json:"overrides,omitempty" toml:"overrides,omitempty"` +} + +type OpenSCAPTailoringOverride struct { + Var string `json:"var,omitempty" toml:"var,omitempty"` + Value interface{} `json:"value,omitempty" toml:"value,omitempty"` +} + +func (c *Customizations) GetOpenSCAP() *OpenSCAPCustomization { + if c == nil { + return nil + } + return c.OpenSCAP +} + +func (ot *OpenSCAPTailoringOverride) UnmarshalTOML(data interface{}) error { + d, _ := data.(map[string]interface{}) + + switch d["var"].(type) { + case string: + ot.Var = d["var"].(string) + default: + return fmt.Errorf("TOML unmarshal: override var must be string, got %v of type %T", d["var"], d["var"]) + } + + switch d["value"].(type) { + case int64: + ot.Value = uint64(d["value"].(int64)) + case string: + ot.Value = d["value"].(string) + default: + return fmt.Errorf("TOML unmarshal: override value must be integer or string, got %v of type %T", d["value"], d["value"]) + } + + return nil +} + +func (ot *OpenSCAPTailoringOverride) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + d, _ := v.(map[string]interface{}) + + switch d["var"].(type) { + case string: + ot.Var = d["var"].(string) + default: + return fmt.Errorf("JSON unmarshal: override var must be string, got %v of type %T", d["var"], d["var"]) + } + + switch d["value"].(type) { + case float64: + ot.Value = uint64(d["value"].(float64)) + case string: + ot.Value = d["value"].(string) + default: + return fmt.Errorf("JSON unmarshal: override var must be float64 number or string, got %v of type %T", d["value"], d["value"]) + } + + return nil +} diff --git a/pkg/blueprint/openscap_customizations_test.go b/pkg/blueprint/openscap_customizations_test.go new file mode 100644 index 0000000000..369ec4ec32 --- /dev/null +++ b/pkg/blueprint/openscap_customizations_test.go @@ -0,0 +1,143 @@ +package blueprint + +import ( + "encoding/json" + "testing" + + "github.com/BurntSushi/toml" + "github.com/stretchr/testify/assert" +) + +func TestGetOpenSCAPConfig(t *testing.T) { + + expectedOscap := OpenSCAPCustomization{ + Datastream: "test-data-stream.xml", + ProfileID: "test_profile", + Tailoring: &OpenSCAPTailoringCustomizations{ + Selected: []string{"quick_rule"}, + Unselected: []string{"very_slow_rule"}, + Overrides: []OpenSCAPTailoringOverride{ + OpenSCAPTailoringOverride{ + Var: "rule_id", + Value: 50, + }, + }, + }, + } + + TestCustomizations := Customizations{ + OpenSCAP: &expectedOscap, + } + + retOpenSCAPCustomiztions := TestCustomizations.GetOpenSCAP() + + assert.EqualValues(t, expectedOscap, *retOpenSCAPCustomiztions) +} + +func TestOpenSCAPOverrideTOMLUnmarshaler(t *testing.T) { + tests := []struct { + name string + TOML string + want *OpenSCAPTailoringOverride + wantErr bool + }{ + { + name: "string based rule", + TOML: ` +var = "sshd_idle_timeout_value" +value = "600" + `, + want: &OpenSCAPTailoringOverride{ + Var: "sshd_idle_timeout_value", + Value: "600", + }, + wantErr: false, + }, + { + name: "integer based rule", + TOML: ` +var = "sshd_idle_timeout_value" +value = 600 + `, + want: &OpenSCAPTailoringOverride{ + Var: "sshd_idle_timeout_value", + Value: uint64(600), + }, + wantErr: false, + }, + { + name: "invalid rule", + TOML: ` +var = "sshd_idle_timeout_value" + `, + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + var override OpenSCAPTailoringOverride + err := toml.Unmarshal([]byte(tt.TOML), &override) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, override) + assert.Equal(t, tt.want, &override) + } + } +} + +func TestOpenSCAPOverrideJSONUnmarshaler(t *testing.T) { + tests := []struct { + name string + JSON string + want *OpenSCAPTailoringOverride + wantErr bool + }{ + { + name: "string based rule", + JSON: `{ + "var": "sshd_idle_timeout_value", + "value": "600" + }`, + want: &OpenSCAPTailoringOverride{ + Var: "sshd_idle_timeout_value", + Value: "600", + }, + wantErr: false, + }, + { + name: "integer based rule", + JSON: `{ + "var": "sshd_idle_timeout_value", + "value": 600 + }`, + want: &OpenSCAPTailoringOverride{ + Var: "sshd_idle_timeout_value", + Value: uint64(600), + }, + wantErr: false, + }, + { + name: "invalid rule", + JSON: `{ + "var": "sshd_idle_timeout_value" + }`, + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + var override OpenSCAPTailoringOverride + err := json.Unmarshal([]byte(tt.JSON), &override) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, override) + assert.Equal(t, tt.want, &override) + } + } +} diff --git a/pkg/customizations/oscap/oscap.go b/pkg/customizations/oscap/oscap.go index ffc06db9c6..e840df2829 100644 --- a/pkg/customizations/oscap/oscap.go +++ b/pkg/customizations/oscap/oscap.go @@ -1,11 +1,9 @@ package oscap import ( - "fmt" - "path/filepath" "strings" - "github.com/osbuild/images/pkg/customizations/fsnode" + "github.com/osbuild/images/pkg/distro" ) type Profile string @@ -40,28 +38,42 @@ const ( defaultRHEL8Datastream string = "/usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml" defaultRHEL9Datastream string = "/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml" - // tailoring directory path + // directory paths + dataDirPath string = "/oscap_data" tailoringDirPath string = "/usr/share/xml/osbuild-openscap-data" ) -func DefaultFedoraDatastream() string { - return defaultFedoraDatastream -} +func getDatastream(datastream string, d distro.Distro) string { + if datastream != "" { + return datastream + } -func DefaultRHEL8Datastream(isRHEL bool) string { - if isRHEL { - return defaultRHEL8Datastream + s := strings.ToLower(d.Name()) + if strings.HasPrefix(s, "fedora") { + return defaultFedoraDatastream + } + + if strings.HasPrefix(s, "centos") { + return defaultCentosDatastream(d.Releasever()) } - return defaultCentos8Datastream + + return defaultRHELDatastream(d.Releasever()) } -func DefaultRHEL9Datastream(isRHEL bool) string { - if isRHEL { - return defaultRHEL9Datastream +func defaultCentosDatastream(releaseVer string) string { + if releaseVer == "8" { + return defaultCentos8Datastream } return defaultCentos9Datastream } +func defaultRHELDatastream(releaseVer string) string { + if releaseVer == "8" { + return defaultRHEL8Datastream + } + return defaultRHEL9Datastream +} + func IsProfileAllowed(profile string, allowlist []Profile) bool { for _, a := range allowlist { if a.String() == profile { @@ -77,15 +89,3 @@ func IsProfileAllowed(profile string, allowlist []Profile) bool { return false } - -func GetTailoringFile(profile string) (string, string, *fsnode.Directory, error) { - newProfile := fmt.Sprintf("%s_osbuild_tailoring", profile) - path := filepath.Join(tailoringDirPath, "tailoring.xml") - - tailoringDir, err := fsnode.NewDirectory(tailoringDirPath, nil, nil, nil, true) - if err != nil { - return "", "", nil, err - } - - return newProfile, path, tailoringDir, nil -} diff --git a/pkg/customizations/oscap/stage_options.go b/pkg/customizations/oscap/stage_options.go new file mode 100644 index 0000000000..82be9304b6 --- /dev/null +++ b/pkg/customizations/oscap/stage_options.go @@ -0,0 +1,110 @@ +package oscap + +import ( + "fmt" + "path/filepath" + + "github.com/osbuild/images/pkg/blueprint" + "github.com/osbuild/images/pkg/customizations/fsnode" + "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/osbuild" +) + +func createRequiredDirectories(createTailoring bool) ([]*fsnode.Directory, error) { + var directories []*fsnode.Directory + + // although the osbuild stage will create this directory, + // it's probably better to ensure that it is created here + dataDirNode, err := fsnode.NewDirectory(dataDirPath, nil, nil, nil, true) + if err != nil { + return nil, fmt.Errorf("unexpected error creating OpenSCAP data directory: %s", err) + } + + directories = append(directories, dataDirNode) + + if createTailoring { + tailoringDirNode, err := fsnode.NewDirectory(tailoringDirPath, nil, nil, nil, true) + if err != nil { + return nil, fmt.Errorf("unexpected error creating OpenSCAP tailoring directory: %s", err) + } + + directories = append(directories, tailoringDirNode) + } + + return directories, nil +} + +func getTailoringProfileID(profileID string) string { + return fmt.Sprintf("%s_osbuild_tailoring", profileID) +} + +func CreateTailoringStageOptions(oscapConfig *blueprint.OpenSCAPCustomization, d distro.Distro) *osbuild.OscapAutotailorStageOptions { + if oscapConfig == nil { + return nil + } + + datastream := getDatastream(oscapConfig.Datastream, d) + + tailoringConfig := oscapConfig.Tailoring + if tailoringConfig == nil { + return nil + } + + newProfile := getTailoringProfileID(oscapConfig.ProfileID) + path := filepath.Join(tailoringDirPath, "tailoring.xml") + + var overrides []osbuild.OscapAutotailorOverride + for _, override := range tailoringConfig.Overrides { + overrides = append(overrides, osbuild.OscapAutotailorOverride{ + Var: override.Var, + Value: override.Value, + }) + } + + return osbuild.NewOscapAutotailorStageOptions( + path, + osbuild.OscapAutotailorConfig{ + ProfileID: oscapConfig.ProfileID, + Datastream: datastream, + Selected: tailoringConfig.Selected, + Unselected: tailoringConfig.Unselected, + Overrides: overrides, + NewProfile: newProfile, + }, + ) +} + +func CreateRemediationStageOptions( + oscapConfig *blueprint.OpenSCAPCustomization, + isOSTree bool, + d distro.Distro, +) (*osbuild.OscapRemediationStageOptions, []*fsnode.Directory, error) { + if oscapConfig == nil { + return nil, nil, nil + } + + if isOSTree { + return nil, nil, fmt.Errorf("unexpected oscap options for ostree image type") + } + + datastream := getDatastream(oscapConfig.Datastream, d) + + profileID := oscapConfig.ProfileID + if oscapConfig.Tailoring != nil { + profileID = getTailoringProfileID(profileID) + } + + directories, err := createRequiredDirectories(oscapConfig.Tailoring == nil) + if err != nil { + return nil, nil, err + } + + return osbuild.NewOscapRemediationStageOptions( + dataDirPath, + osbuild.OscapConfig{ + Datastream: datastream, + ProfileID: profileID, + Compression: true, + }, + ), directories, nil +} diff --git a/pkg/distro/fedora/distro.go b/pkg/distro/fedora/distro.go index ec397266a3..c9c78a1c62 100644 --- a/pkg/distro/fedora/distro.go +++ b/pkg/distro/fedora/distro.go @@ -38,9 +38,6 @@ const ( // Added kernel command line options for ami, qcow2, openstack, vhd and vmdk types cloudKernelOptions = "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0" - - // location for saving openscap remediation data - oscapDataDir = "/oscap_data" ) var ( diff --git a/pkg/distro/fedora/images.go b/pkg/distro/fedora/images.go index 6943789c26..d276c3f9b9 100644 --- a/pkg/distro/fedora/images.go +++ b/pkg/distro/fedora/images.go @@ -161,59 +161,22 @@ func osCustomizations( osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos)) } - if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil { - if t.rpmOstree { - panic("unexpected oscap options for ostree image type") - } - - // although the osbuild stage will create this directory, - // it's probably better to ensure that it is created here - dataDirNode, err := fsnode.NewDirectory(oscapDataDir, nil, nil, nil, true) - if err != nil { - panic("unexpected error creating OpenSCAP data directory") - } - - osc.Directories = append(osc.Directories, dataDirNode) - - var datastream = oscapConfig.DataStream - if datastream == "" { - datastream = oscap.DefaultFedoraDatastream() - } - - oscapStageOptions := osbuild.OscapConfig{ - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Compression: true, - } - - if oscapConfig.Tailoring != nil { - newProfile, tailoringFilepath, tailoringDir, err := oscap.GetTailoringFile(oscapConfig.ProfileID) - if err != nil { - panic(fmt.Sprintf("unexpected error creating tailoring file options: %v", err)) - } - - tailoringOptions := osbuild.OscapAutotailorConfig{ - NewProfile: newProfile, - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Selected: oscapConfig.Tailoring.Selected, - Unselected: oscapConfig.Tailoring.Unselected, - } - - osc.OpenSCAPTailorConfig = osbuild.NewOscapAutotailorStageOptions( - tailoringFilepath, - tailoringOptions, - ) - - // overwrite the profile id with the new tailoring id - oscapStageOptions.ProfileID = newProfile - oscapStageOptions.Tailoring = tailoringFilepath - - // add the parent directory for the tailoring file - osc.Directories = append(osc.Directories, tailoringDir) - } + var directories []*fsnode.Directory + osc.OpenSCAPTailorConfig = oscap.CreateTailoringStageOptions( + c.GetOpenSCAP(), + t.arch.distro, + ) + osc.OpenSCAPConfig, directories, err = oscap.CreateRemediationStageOptions( + c.GetOpenSCAP(), + t.rpmOstree, + t.arch.distro, + ) + if err != nil { + panic(err) + } - osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(oscapDataDir, oscapStageOptions) + if len(directories) > 0 { + osc.Directories = append(osc.Directories, directories...) } osc.ShellInit = imageConfig.ShellInit diff --git a/pkg/distro/rhel7/images.go b/pkg/distro/rhel7/images.go index dfc5415313..33c6aa5e83 100644 --- a/pkg/distro/rhel7/images.go +++ b/pkg/distro/rhel7/images.go @@ -133,7 +133,7 @@ func osCustomizations( osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions( oscapDataDir, osbuild.OscapConfig{ - Datastream: oscapConfig.DataStream, + Datastream: oscapConfig.Datastream, ProfileID: oscapConfig.ProfileID, Compression: true, }, diff --git a/pkg/distro/rhel8/images.go b/pkg/distro/rhel8/images.go index 1eeae9a35d..d908aba17f 100644 --- a/pkg/distro/rhel8/images.go +++ b/pkg/distro/rhel8/images.go @@ -182,59 +182,22 @@ func osCustomizations( osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos)) } - if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil { - if t.rpmOstree { - panic("unexpected oscap options for ostree image type") - } - - // although the osbuild stage will create this directory, - // it's probably better to ensure that it is created here - dataDirNode, err := fsnode.NewDirectory(oscapDataDir, nil, nil, nil, true) - if err != nil { - panic("unexpected error creating OpenSCAP data directory") - } - - osc.Directories = append(osc.Directories, dataDirNode) - - var datastream = oscapConfig.DataStream - if datastream == "" { - datastream = oscap.DefaultRHEL8Datastream(t.arch.distro.isRHEL()) - } - - oscapStageOptions := osbuild.OscapConfig{ - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Compression: true, - } - - if oscapConfig.Tailoring != nil { - newProfile, tailoringFilepath, tailoringDir, err := oscap.GetTailoringFile(oscapConfig.ProfileID) - if err != nil { - panic(fmt.Sprintf("unexpected error creating tailoring file options: %v", err)) - } - - tailoringOptions := osbuild.OscapAutotailorConfig{ - NewProfile: newProfile, - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Selected: oscapConfig.Tailoring.Selected, - Unselected: oscapConfig.Tailoring.Unselected, - } - - osc.OpenSCAPTailorConfig = osbuild.NewOscapAutotailorStageOptions( - tailoringFilepath, - tailoringOptions, - ) - - // overwrite the profile id with the new tailoring id - oscapStageOptions.ProfileID = newProfile - oscapStageOptions.Tailoring = tailoringFilepath - - // add the parent directory for the tailoring file - osc.Directories = append(osc.Directories, tailoringDir) - } + var directories []*fsnode.Directory + osc.OpenSCAPTailorConfig = oscap.CreateTailoringStageOptions( + c.GetOpenSCAP(), + t.arch.distro, + ) + osc.OpenSCAPConfig, directories, err = oscap.CreateRemediationStageOptions( + c.GetOpenSCAP(), + t.rpmOstree, + t.arch.distro, + ) + if err != nil { + panic(err) + } - osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(oscapDataDir, oscapStageOptions) + if len(directories) > 0 { + osc.Directories = append(osc.Directories, directories...) } osc.ShellInit = imageConfig.ShellInit diff --git a/pkg/distro/rhel8/imagetype.go b/pkg/distro/rhel8/imagetype.go index 88bd787093..cdf4db8e4e 100644 --- a/pkg/distro/rhel8/imagetype.go +++ b/pkg/distro/rhel8/imagetype.go @@ -37,9 +37,6 @@ const ( // blueprint package set name blueprintPkgsKey = "blueprint" - - // location for saving openscap remediation data - oscapDataDir = "/oscap_data" ) type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error) diff --git a/pkg/distro/rhel9/images.go b/pkg/distro/rhel9/images.go index 225e7a06b3..0dfa75bb81 100644 --- a/pkg/distro/rhel9/images.go +++ b/pkg/distro/rhel9/images.go @@ -179,59 +179,22 @@ func osCustomizations( osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos)) } - if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil { - if t.rpmOstree { - panic("unexpected oscap options for ostree image type") - } - - // although the osbuild stage will create this directory, - // it's probably better to ensure that it is created here - dataDirNode, err := fsnode.NewDirectory(oscapDataDir, nil, nil, nil, true) - if err != nil { - panic("unexpected error creating OpenSCAP data directory") - } - - osc.Directories = append(osc.Directories, dataDirNode) - - var datastream = oscapConfig.DataStream - if datastream == "" { - datastream = oscap.DefaultRHEL9Datastream(t.arch.distro.isRHEL()) - } - - oscapStageOptions := osbuild.OscapConfig{ - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Compression: true, - } - - if oscapConfig.Tailoring != nil { - newProfile, tailoringFilepath, tailoringDir, err := oscap.GetTailoringFile(oscapConfig.ProfileID) - if err != nil { - panic(fmt.Sprintf("unexpected error creating tailoring file options: %v", err)) - } - - tailoringOptions := osbuild.OscapAutotailorConfig{ - NewProfile: newProfile, - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Selected: oscapConfig.Tailoring.Selected, - Unselected: oscapConfig.Tailoring.Unselected, - } - - osc.OpenSCAPTailorConfig = osbuild.NewOscapAutotailorStageOptions( - tailoringFilepath, - tailoringOptions, - ) - - // overwrite the profile id with the new tailoring id - oscapStageOptions.ProfileID = newProfile - oscapStageOptions.Tailoring = tailoringFilepath - - // add the parent directory for the tailoring file - osc.Directories = append(osc.Directories, tailoringDir) - } + var directories []*fsnode.Directory + osc.OpenSCAPTailorConfig = oscap.CreateTailoringStageOptions( + c.GetOpenSCAP(), + t.arch.distro, + ) + osc.OpenSCAPConfig, directories, err = oscap.CreateRemediationStageOptions( + c.GetOpenSCAP(), + t.rpmOstree, + t.arch.distro, + ) + if err != nil { + panic(err) + } - osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(oscapDataDir, oscapStageOptions) + if len(directories) > 0 { + osc.Directories = append(osc.Directories, directories...) } osc.ShellInit = imageConfig.ShellInit diff --git a/pkg/distro/rhel9/imagetype.go b/pkg/distro/rhel9/imagetype.go index 6d8b6e9dc6..f279ca27da 100644 --- a/pkg/distro/rhel9/imagetype.go +++ b/pkg/distro/rhel9/imagetype.go @@ -40,9 +40,6 @@ const ( // blueprint package set name blueprintPkgsKey = "blueprint" - - // location for saving openscap remediation data - oscapDataDir = "/oscap_data" ) type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error) diff --git a/pkg/osbuild/oscap_autotailor_stage.go b/pkg/osbuild/oscap_autotailor_stage.go index 36b3d6544e..d77494f5d2 100644 --- a/pkg/osbuild/oscap_autotailor_stage.go +++ b/pkg/osbuild/oscap_autotailor_stage.go @@ -8,11 +8,17 @@ type OscapAutotailorStageOptions struct { } type OscapAutotailorConfig struct { - NewProfile string `json:"new_profile"` - Datastream string `json:"datastream" toml:"datastream"` - ProfileID string `json:"profile_id" toml:"profile_id"` - Selected []string `json:"selected,omitempty"` - Unselected []string `json:"unselected,omitempty"` + NewProfile string `json:"new_profile"` + Datastream string `json:"datastream" toml:"datastream"` + ProfileID string `json:"profile_id" toml:"profile_id"` + Selected []string `json:"selected,omitempty"` + Unselected []string `json:"unselected,omitempty"` + Overrides []OscapAutotailorOverride `json:"overrides,omitempty"` +} + +type OscapAutotailorOverride struct { + Var string `json:"var"` + Value interface{} `json:"value"` } func (OscapAutotailorStageOptions) isStageOptions() {} @@ -27,6 +33,17 @@ func (c OscapAutotailorConfig) validate() error { if c.NewProfile == "" { return fmt.Errorf("'new_profile' must be specified") } + for _, override := range c.Overrides { + if _, ok := override.Value.(uint64); ok { + continue + } + + if _, ok := override.Value.(string); ok { + continue + } + + return fmt.Errorf("override 'value' must be an integere or a string") + } return nil } @@ -50,6 +67,7 @@ func NewOscapAutotailorStageOptions(filepath string, autotailorOptions OscapAuto ProfileID: autotailorOptions.ProfileID, Selected: autotailorOptions.Selected, Unselected: autotailorOptions.Unselected, + Overrides: autotailorOptions.Overrides, }, } } diff --git a/pkg/osbuild/oscap_autotailor_stage_test.go b/pkg/osbuild/oscap_autotailor_stage_test.go index 166e687996..92f9b8f492 100644 --- a/pkg/osbuild/oscap_autotailor_stage_test.go +++ b/pkg/osbuild/oscap_autotailor_stage_test.go @@ -15,6 +15,12 @@ func TestNewOscapAutotailorStage(t *testing.T) { NewProfile: "test_profile_osbuild_profile", Selected: []string{"fast_rule"}, Unselected: []string{"slow_rule"}, + Overrides: []OscapAutotailorOverride{ + { + Var: "rule1", + Value: "value1", + }, + }, }, } @@ -65,6 +71,56 @@ func TestOscapAutotailorStageOptionsValidate(t *testing.T) { }, err: true, }, + { + name: "invalid-override-value", + options: OscapAutotailorStageOptions{ + Config: OscapAutotailorConfig{ + ProfileID: "test-profile", + Datastream: "test-datastream", + NewProfile: "test-profile-osbuild-profile", + Overrides: []OscapAutotailorOverride{ + { + Var: "test-var", + }, + }, + }, + }, + err: true, + }, + { + name: "invalid-override-value-string", + options: OscapAutotailorStageOptions{ + Config: OscapAutotailorConfig{ + ProfileID: "test-profile", + Datastream: "test-datastream", + NewProfile: "test-profile-osbuild-profile", + Overrides: []OscapAutotailorOverride{ + { + Var: "test-var", + Value: "30", + }, + }, + }, + }, + err: false, + }, + { + name: "valid-override-value-int", + options: OscapAutotailorStageOptions{ + Config: OscapAutotailorConfig{ + ProfileID: "test-profile", + Datastream: "test-datastream", + NewProfile: "test-profile-osbuild-profile", + Overrides: []OscapAutotailorOverride{ + { + Var: "test-var", + Value: uint64(30), + }, + }, + }, + }, + err: false, + }, { name: "valid-data", options: OscapAutotailorStageOptions{ diff --git a/test/configs/all-with-oscap.json b/test/configs/all-with-oscap.json index 05d22c495f..e5a976b890 100644 --- a/test/configs/all-with-oscap.json +++ b/test/configs/all-with-oscap.json @@ -9,6 +9,10 @@ { "name": "bluez", "version": "*" + }, + { + "name": "openscap-utils", + "version": "*" } ], "modules": [], @@ -146,6 +150,12 @@ "unselected": [ "rpm_verify_hashes", "enable_fips_mode" + ], + "overrides": [ + { + "var": "sshd_idle_timeout_value", + "value": 600 + } ] } }