Skip to content

Commit

Permalink
Adding support for enabling FIPS mode via an operating system boolean (
Browse files Browse the repository at this point in the history
  • Loading branch information
rdoxenham authored Aug 27, 2024
1 parent b184f7c commit fe47532
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 2 deletions.
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

### Image Definition Changes

* Introduced a dedicated FIPS mode option, adding the required packages, kernel arguments, and crypto selection

### Image Configuration Directory Changes

## Bug Fixes
Expand Down
2 changes: 2 additions & 0 deletions docs/building-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ operatingSystem:
kernelArgs:
- arg1
- arg2
enableFIPS: true
groups:
- name: group1
- name: group2
Expand Down Expand Up @@ -153,6 +154,7 @@ The remainder of the operating system customizations may be applied regardless o
parameter is omitted. If this option is set, the default entries will need to be manually added if they are
still in use.
* `kernelArgs` - Provides a list of flags that should be passed to the kernel on boot.
* `enableFIPS` - Specifies whether to setup the operating system for FIPS mode (Default: false)
* `groups` - Defines a list of operating system groups to create. This will not fail if the
group already exists. Each entry is made up of the following fields:
* `name` - Required; Name of the group to create.
Expand Down
4 changes: 4 additions & 0 deletions pkg/combustion/combustion.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ func (c *Combustion) Configure(ctx *image.Context) error {
name: systemdComponentName,
runnable: configureSystemd,
},
{
name: fipsComponentName,
runnable: configureFips,
},
{
name: elementalComponentName,
runnable: configureElemental,
Expand Down
49 changes: 49 additions & 0 deletions pkg/combustion/fips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package combustion

import (
_ "embed"
"fmt"
"os"
"path/filepath"

"github.com/suse-edge/edge-image-builder/pkg/fileio"
"github.com/suse-edge/edge-image-builder/pkg/image"
"github.com/suse-edge/edge-image-builder/pkg/log"
)

const (
fipsComponentName = "fips"
fipsScriptName = "15-fips-setup.sh"
)

var (
//go:embed templates/15-fips-setup.sh
fipsScript string
FipsPackages = []string{"patterns-base-fips"}
FipsKernelArgs = []string{"fips=1"}
)

func configureFips(ctx *image.Context) ([]string, error) {
fips := ctx.ImageDefinition.OperatingSystem.EnableFips
if !fips {
log.AuditComponentSkipped(fipsComponentName)
return nil, nil
}

if err := writeFipsCombustionScript(ctx); err != nil {
log.AuditComponentFailed(fipsComponentName)
return nil, err
}

log.AuditComponentSuccessful(fipsComponentName)
return []string{fipsScriptName}, nil
}

func writeFipsCombustionScript(ctx *image.Context) error {
fipsScriptFilename := filepath.Join(ctx.CombustionDir, fipsScriptName)

if err := os.WriteFile(fipsScriptFilename, []byte(fipsScript), fileio.ExecutablePerms); err != nil {
return fmt.Errorf("writing file %s: %w", fipsScriptFilename, err)
}
return nil
}
62 changes: 62 additions & 0 deletions pkg/combustion/fips_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package combustion

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/suse-edge/edge-image-builder/pkg/fileio"
"github.com/suse-edge/edge-image-builder/pkg/image"
)

func TestConfigureFips_NoConf(t *testing.T) {
// Setup
var ctx image.Context

ctx.ImageDefinition = &image.Definition{
OperatingSystem: image.OperatingSystem{},
}

// Test
scripts, err := configureFips(&ctx)

// Verify
require.NoError(t, err)
assert.Nil(t, scripts)
}

func TestConfigureFips_Enabled(t *testing.T) {
// Setup
ctx, teardown := setupContext(t)
defer teardown()

ctx.ImageDefinition = &image.Definition{
OperatingSystem: image.OperatingSystem{
EnableFips: true,
},
}

// Test
scripts, err := configureFips(ctx)

// Verify
require.NoError(t, err)

require.Len(t, scripts, 1)
assert.Equal(t, fipsScriptName, scripts[0])

expectedFilename := filepath.Join(ctx.CombustionDir, fipsScriptName)
foundBytes, err := os.ReadFile(expectedFilename)
require.NoError(t, err)

stats, err := os.Stat(expectedFilename)
require.NoError(t, err)
assert.Equal(t, fileio.ExecutablePerms, stats.Mode())

foundContents := string(foundBytes)

// - Ensure that we have the fips setup script defined
assert.Contains(t, foundContents, "fips-mode-setup --enable", "fips setup script missing")
}
4 changes: 4 additions & 0 deletions pkg/combustion/templates/15-fips-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
set -euo pipefail

fips-mode-setup --enable
16 changes: 16 additions & 0 deletions pkg/eib/eib.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Run(ctx *image.Context, rootBuildDir string) error {
}

appendElementalRPMs(ctx)
appendFips(ctx)
appendHelm(ctx)

c, err := buildCombustion(ctx, rootBuildDir)
Expand Down Expand Up @@ -107,6 +108,15 @@ func appendElementalRPMs(ctx *image.Context) {
}, combustion.ElementalPackages...)
}

func appendFips(ctx *image.Context) {
fips := ctx.ImageDefinition.OperatingSystem.EnableFips
if fips {
log.AuditInfo("FIPS mode is configured. The necessary RPM packages will be downloaded.")
appendRPMs(ctx, nil, combustion.FipsPackages...)
appendKernelArgs(ctx, combustion.FipsKernelArgs...)
}
}

func appendRPMs(ctx *image.Context, repos []image.AddRepo, packages ...string) {
repositories := ctx.ImageDefinition.OperatingSystem.Packages.AdditionalRepos
repositories = append(repositories, repos...)
Expand All @@ -125,6 +135,12 @@ func appendHelm(ctx *image.Context) {
ctx.ImageDefinition.Kubernetes.Helm.Repositories = append(ctx.ImageDefinition.Kubernetes.Helm.Repositories, componentRepos...)
}

func appendKernelArgs(ctx *image.Context, kernelArgs ...string) {
kernelArgList := ctx.ImageDefinition.OperatingSystem.KernelArgs
kernelArgList = append(kernelArgList, kernelArgs...)
ctx.ImageDefinition.OperatingSystem.KernelArgs = kernelArgList
}

func buildCombustion(ctx *image.Context, rootDir string) (*combustion.Combustion, error) {
cacheDir := filepath.Join(rootDir, "cache")
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/image/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type OperatingSystem struct {
Time Time `yaml:"time"`
Proxy Proxy `yaml:"proxy"`
Keymap string `yaml:"keymap"`
EnableFips bool `yaml:"enableFIPS"`
}

type IsoConfiguration struct {
Expand Down
4 changes: 4 additions & 0 deletions pkg/image/definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func TestParse(t *testing.T) {
}
assert.Equal(t, expectedKernelArgs, definition.OperatingSystem.KernelArgs)

// Operating System -> FIPS
enableFIPS := definition.OperatingSystem.EnableFips
assert.Equal(t, true, enableFIPS)

// Operating System -> Groups
groupConfigs := definition.OperatingSystem.Groups
require.Len(t, groupConfigs, 2)
Expand Down
1 change: 1 addition & 0 deletions pkg/image/testdata/full-valid-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ operatingSystem:
- alpha=foo
- beta=bar
- baz
enableFIPS: true
systemd:
enable:
- enable0
Expand Down
6 changes: 6 additions & 0 deletions pkg/image/validation/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func validateKernelArgs(os *image.OperatingSystem) []FailedValidation {
UserMessage: "Kernel arguments must be specified as 'key=value'.",
})
}

if (key == "fips" && value == "1") && !os.EnableFips {
failures = append(failures, FailedValidation{
UserMessage: "FIPS mode has been specified via kernel arguments, please use the 'enableFIPS: true' option instead.",
})
}
}

if _, exists := seenKeys[key]; exists {
Expand Down
5 changes: 3 additions & 2 deletions pkg/image/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestValidateDefinition(t *testing.T) {
},
},
},
`one error from each`: {
`invalid in each`: {
Definition: image.Definition{
APIVersion: "1.0",
Image: image.Image{
Expand All @@ -49,7 +49,7 @@ func TestValidateDefinition(t *testing.T) {
OutputImageName: "output.iso",
},
OperatingSystem: image.OperatingSystem{
KernelArgs: []string{"foo="},
KernelArgs: []string{"foo=", "fips=1"},
},
EmbeddedArtifactRegistry: image.EmbeddedArtifactRegistry{
ContainerImages: []image.ContainerImage{
Expand Down Expand Up @@ -78,6 +78,7 @@ func TestValidateDefinition(t *testing.T) {
},
osComponent: {
"Kernel arguments must be specified as 'key=value'.",
"FIPS mode has been specified via kernel arguments, please use the 'enableFIPS: true' option instead.",
},
registryComponent: {
"The 'name' field is required for each entry in 'images'.",
Expand Down
6 changes: 6 additions & 0 deletions pkg/image/validation/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ func validateVersion(ctx *image.Context) []FailedValidation {
})
}

if definition.APIVersion == "1.0" && definition.OperatingSystem.EnableFips {
failures = append(failures, FailedValidation{
UserMessage: "Automated FIPS configuration is not supported in EIB version 1.0, please use EIB version >= 1.1",
})
}

return failures
}
11 changes: 11 additions & 0 deletions pkg/image/validation/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ func TestValidateVersion(t *testing.T) {
"Helm chart APIVersions field is not supported in EIB version 1.0, must use EIB version 1.1",
},
},
`invalid version with FIPS enabled`: {
ImageDefinition: image.Definition{
APIVersion: "1.0",
OperatingSystem: image.OperatingSystem{
EnableFips: true,
},
},
ExpectedFailedMessages: []string{
"Automated FIPS configuration is not supported in EIB version 1.0, please use EIB version >= 1.1",
},
},
}

for name, test := range tests {
Expand Down

0 comments on commit fe47532

Please sign in to comment.