From 12c957e1873f9738a5a63d55d9b835c68cdd41f0 Mon Sep 17 00:00:00 2001 From: Michel Laterman <82832767+michel-laterman@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:22:08 -0800 Subject: [PATCH] FIPS Build (#6565) Adds FIPS env var to magefile to enable FIPS compliant binary builds using the microsoft/go toolchain. (cherry picked from commit b20efc1801b1b18d9e0b035cea34ff1b942a9066) --- .buildkite/integration.pipeline.yml | 26 +++++++ dev-tools/mage/build.go | 47 ++++++++++++- dev-tools/mage/build_test.go | 59 ++++++++++++++++ dev-tools/mage/crossbuild.go | 5 ++ dev-tools/mage/dockerbuilder.go | 3 + dev-tools/mage/gotest.go | 4 +- dev-tools/mage/pkg.go | 11 ++- dev-tools/mage/pkgtypes.go | 11 +-- dev-tools/mage/settings.go | 7 ++ dev-tools/packaging/package_test.go | 100 ++++++++++++++++++++++----- internal/pkg/release/version.go | 14 ++++ internal/pkg/release/version_test.go | 22 ++++++ magefile.go | 17 +++++ 13 files changed, 298 insertions(+), 28 deletions(-) create mode 100644 dev-tools/mage/build_test.go diff --git a/.buildkite/integration.pipeline.yml b/.buildkite/integration.pipeline.yml index 85d9b6d5672..95601e4aaf4 100644 --- a/.buildkite/integration.pipeline.yml +++ b/.buildkite/integration.pipeline.yml @@ -20,6 +20,19 @@ steps: provider: "gcp" machineType: "n2-standard-8" + - label: "Packaging: Ubuntu x86_64 FIPS" + key: "packaging-ubuntu-x86-64-fips" + env: + PACKAGES: "tar.gz" + PLATFORMS: "linux/amd64" + FIPS: "true" + command: ".buildkite/scripts/steps/integration-package.sh" + artifact_paths: + - build/distributions/** + agents: + provider: "gcp" + machineType: "n2-standard-4" + - label: "Packaging: Ubuntu arm64" key: "packaging-ubuntu-arm64" env: @@ -32,6 +45,19 @@ steps: provider: "gcp" machineType: "n2-standard-8" + - label: "Packaging: Ubuntu arm64 FIPS" + key: "packaging-ubuntu-arm64-fips" + env: + PACKAGES: "tar.gz" + PLATFORMS: "linux/arm64" + FIPS: "true" + command: ".buildkite/scripts/steps/integration-package.sh" + artifact_paths: + - build/distributions/** + agents: + provider: "gcp" + machineType: "n2-standard-4" + - label: "Packaging: Windows" key: "packaging-windows" env: diff --git a/dev-tools/mage/build.go b/dev-tools/mage/build.go index 190efa543ea..92da55273b1 100644 --- a/dev-tools/mage/build.go +++ b/dev-tools/mage/build.go @@ -11,6 +11,7 @@ import ( "log" "os" "path/filepath" + "regexp" "strings" "github.com/josephspurrier/goversioninfo" @@ -34,6 +35,39 @@ type BuildArgs struct { WinMetadata bool // Add resource metadata to Windows binaries (like add the version number to the .exe properties). } +// buildTagRE is a regexp to match strings like "-tags=abcd" +// but does not match "-tags= " +var buildTagRE = regexp.MustCompile(`-tags=([\S]+)?`) + +// ParseBuildTags returns the ExtraFlags param where all flags that are go build tags are joined by a comma. +// +// For example if given -someflag=val1 -tags=buildtag1 -tags=buildtag2 +// It will return -someflag=val1 -tags=buildtag1,buildtag2 +func (b BuildArgs) ParseBuildTags() []string { + flags := make([]string, 0) + if len(b.ExtraFlags) == 0 { + return flags + } + + buildTags := make([]string, 0) + for _, flag := range b.ExtraFlags { + if buildTagRE.MatchString(flag) { + arr := buildTagRE.FindStringSubmatch(flag) + if len(arr) != 2 || arr[1] == "" { + log.Printf("Unexpected format found for buildargs.ExtraFlags, ignoring value %q", flag) + continue + } + buildTags = append(buildTags, arr[1]) + } else { + flags = append(flags, flag) + } + } + if len(buildTags) > 0 { + flags = append(flags, "-tags="+strings.Join(buildTags, ",")) + } + return flags +} + // DefaultBuildArgs returns the default BuildArgs for use in builds. func DefaultBuildArgs() BuildArgs { args := BuildArgs{ @@ -53,6 +87,11 @@ func DefaultBuildArgs() BuildArgs { args.ExtraFlags = append(args.ExtraFlags, "-buildmode", "pie") } + if FIPSBuild { + args.ExtraFlags = append(args.ExtraFlags, "-tags=requirefips") + args.CGO = true + } + if DevBuild { // Disable optimizations (-N) and inlining (-l) for debugging. args.ExtraFlags = append(args.ExtraFlags, `-gcflags=all=-N -l`) @@ -151,6 +190,12 @@ func Build(params BuildArgs) error { if params.CGO { cgoEnabled = "1" } + + if FIPSBuild { + cgoEnabled = "1" + env["GOEXPERIMENT"] = "systemcrypto" + } + env["CGO_ENABLED"] = cgoEnabled // Spec @@ -159,7 +204,7 @@ func Build(params BuildArgs) error { "-o", filepath.Join(params.OutputDir, binaryName), } - args = append(args, params.ExtraFlags...) + args = append(args, params.ParseBuildTags()...) // ldflags ldflags := params.LDFlags diff --git a/dev-tools/mage/build_test.go b/dev-tools/mage/build_test.go new file mode 100644 index 00000000000..e30b5edbc58 --- /dev/null +++ b/dev-tools/mage/build_test.go @@ -0,0 +1,59 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License 2.0; +// you may not use this file except in compliance with the Elastic License 2.0. + +package mage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_BuildArgs_ParseBuildTags(t *testing.T) { + tests := []struct { + name string + input []string + expect []string + }{{ + name: "no flags", + input: nil, + expect: []string{}, + }, { + name: "multiple flags with no tags", + input: []string{"-a", "-b", "-key=value"}, + expect: []string{"-a", "-b", "-key=value"}, + }, { + name: "one build tag", + input: []string{"-tags=example"}, + expect: []string{"-tags=example"}, + }, { + name: "multiple build tags", + input: []string{"-tags=example", "-tags=test"}, + expect: []string{"-tags=example,test"}, + }, { + name: "joined build tags", + input: []string{"-tags=example,test"}, + expect: []string{"-tags=example,test"}, + }, { + name: "multiple build tags with other flags", + input: []string{"-tags=example", "-tags=test", "-key=value", "-a"}, + expect: []string{"-key=value", "-a", "-tags=example,test"}, + }, { + name: "incorrectly formatted tag", + input: []string{"-tags= example"}, + expect: []string{}, + }, { + name: "incorrectly formatted tag with valid tag", + input: []string{"-tags= example", "-tags=test"}, + expect: []string{"-tags=test"}, + }} + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + args := BuildArgs{ExtraFlags: tc.input} + flags := args.ParseBuildTags() + assert.EqualValues(t, tc.expect, flags) + }) + } +} diff --git a/dev-tools/mage/crossbuild.go b/dev-tools/mage/crossbuild.go index 30750602118..a1492902f98 100644 --- a/dev-tools/mage/crossbuild.go +++ b/dev-tools/mage/crossbuild.go @@ -249,6 +249,10 @@ func CrossBuildImage(platform string) (string, error) { return "", err } + if FIPSBuild { + tagSuffix += "-fips" + } + return BeatsCrossBuildImage + ":" + goVersion + "-" + tagSuffix, nil } @@ -332,6 +336,7 @@ func (b GolangCrossBuilder) Build() error { "--env", fmt.Sprintf("SNAPSHOT=%v", Snapshot), "--env", fmt.Sprintf("DEV=%v", DevBuild), "--env", fmt.Sprintf("EXTERNAL=%v", ExternalBuild), + "--env", fmt.Sprintf("FIPS=%v", FIPSBuild), "-v", repoInfo.RootDir+":"+mountPoint, "-w", workDir, image, diff --git a/dev-tools/mage/dockerbuilder.go b/dev-tools/mage/dockerbuilder.go index 0e0bd2078ca..6a0cd8609ac 100644 --- a/dev-tools/mage/dockerbuilder.go +++ b/dev-tools/mage/dockerbuilder.go @@ -182,6 +182,9 @@ func (b *dockerBuilder) dockerBuild() (string, error) { if b.Snapshot { tag = tag + "-SNAPSHOT" } + if b.FIPS { + tag = tag + "-fips" + } if repository := b.ExtraVars["repository"]; repository != "" { tag = fmt.Sprintf("%s/%s", repository, tag) } diff --git a/dev-tools/mage/gotest.go b/dev-tools/mage/gotest.go index 11294caa333..768e2465d77 100644 --- a/dev-tools/mage/gotest.go +++ b/dev-tools/mage/gotest.go @@ -187,9 +187,9 @@ func GoTestBuild(ctx context.Context, params GoTestArgs) error { args := []string{"test", "-c", "-o", params.OutputFile} if len(params.Tags) > 0 { - params := strings.Join(params.Tags, " ") + params := strings.Join(params.Tags, ",") if params != "" { - args = append(args, "-tags", params) + args = append(args, "-tags="+params) } } diff --git a/dev-tools/mage/pkg.go b/dev-tools/mage/pkg.go index a3917ddac1d..6ec09f2e598 100644 --- a/dev-tools/mage/pkg.go +++ b/dev-tools/mage/pkg.go @@ -80,6 +80,7 @@ func Package() error { spec.OS = target.GOOS() spec.Arch = packageArch spec.Snapshot = Snapshot + spec.FIPS = FIPSBuild spec.evalContext = map[string]interface{}{ "GOOS": target.GOOS(), "GOARCH": target.GOARCH(), @@ -148,11 +149,11 @@ type packageBuilder struct { } func (b packageBuilder) Build() error { - fmt.Printf(">> package: Building %v type=%v for platform=%v\n", b.Spec.Name, b.Type, b.Platform.Name) + fmt.Printf(">> package: Building %v type=%v for platform=%v fips=%v\n", b.Spec.Name, b.Type, b.Platform.Name, b.Spec.FIPS) log.Printf("Package spec: %+v", b.Spec) if err := b.Type.Build(b.Spec); err != nil { - return fmt.Errorf("failed building %v type=%v for platform=%v : %w", - b.Spec.Name, b.Type, b.Platform.Name, err) + return fmt.Errorf("failed building %v type=%v for platform=%v fips=%v : %w", + b.Spec.Name, b.Type, b.Platform.Name, b.Spec.FIPS, err) } return nil } @@ -247,6 +248,10 @@ func TestPackages(options ...TestPackagesOption) error { args = append(args, "-root-owner") } + if FIPSBuild { + args = append(args, "-fips") + } + args = append(args, "-files", MustExpand("{{.PWD}}/build/distributions/*")) if out, err := goTest(args...); err != nil { diff --git a/dev-tools/mage/pkgtypes.go b/dev-tools/mage/pkgtypes.go index bdbbc0de097..01f27b57297 100644 --- a/dev-tools/mage/pkgtypes.go +++ b/dev-tools/mage/pkgtypes.go @@ -39,13 +39,13 @@ const ( packageStagingDir = "build/package" // defaultBinaryName specifies the output file for zip and tar.gz. - defaultBinaryName = "{{.Name}}{{if .Qualifier}}-{{.Qualifier}}{{end}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}{{if .OS}}-{{.OS}}{{end}}{{if .Arch}}-{{.Arch}}{{end}}" + defaultBinaryName = "{{.Name}}{{if .Qualifier}}-{{.Qualifier}}{{end}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}{{if .OS}}-{{.OS}}{{end}}{{if .Arch}}-{{.Arch}}{{end}}{{if .FIPS}}-fips{{end}}" // defaultRootDir is the default name of the root directory contained inside of zip and // tar.gz packages. // NOTE: This uses .BeatName instead of .Name because we wanted the internal // directory to not include "-oss". - defaultRootDir = "{{.BeatName}}{{if .Qualifier}}-{{.Qualifier}}{{end}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}{{if .OS}}-{{.OS}}{{end}}{{if .Arch}}-{{.Arch}}{{end}}" + defaultRootDir = "{{.BeatName}}{{if .Qualifier}}-{{.Qualifier}}{{end}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}{{if .OS}}-{{.OS}}{{end}}{{if .Arch}}-{{.Arch}}{{end}}{{if .FIPS}}-fips{{end}}" componentConfigMode os.FileMode = 0600 @@ -92,6 +92,7 @@ type PackageSpec struct { Arch string `yaml:"arch,omitempty"` Vendor string `yaml:"vendor,omitempty"` Snapshot bool `yaml:"snapshot"` + FIPS bool `yaml:"fips"` Version string `yaml:"version,omitempty"` License string `yaml:"license,omitempty"` URL string `yaml:"url,omitempty"` @@ -732,7 +733,7 @@ func runFPM(spec PackageSpec, packageType PackageType) error { } defer os.Remove(inputTar) - outputFile, err := spec.Expand("{{.Name}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}-{{.Arch}}") + outputFile, err := spec.Expand("{{.Name}}-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}-{{.Arch}}{{if .FIPS}}-fips{{end}}") if err != nil { return err } @@ -965,7 +966,7 @@ func addFileToTar(ar *tar.Writer, baseDir string, pkgFile PackageFile) error { } if mg.Verbose() { - log.Println("Adding", os.FileMode(header.Mode), header.Name) + log.Println("Adding", os.FileMode(header.Mode), header.Name) //nolint:gosec // we don't care about an int overflow in a log line } if err := ar.WriteHeader(header); err != nil { return err @@ -1033,7 +1034,7 @@ func addSymlinkToTar(tmpdir string, ar *tar.Writer, baseDir string, pkgFile Pack header.Typeflag = tar.TypeSymlink if mg.Verbose() { - log.Println("Adding", os.FileMode(header.Mode), header.Name) + log.Println("Adding", os.FileMode(header.Mode), header.Name) //nolint:gosec // we don't care about an int overflow in a log line } if err := ar.WriteHeader(header); err != nil { return err diff --git a/dev-tools/mage/settings.go b/dev-tools/mage/settings.go index 32bc62a3e97..b237ebe7900 100644 --- a/dev-tools/mage/settings.go +++ b/dev-tools/mage/settings.go @@ -88,6 +88,7 @@ var ( Snapshot bool DevBuild bool ExternalBuild bool + FIPSBuild bool versionQualified bool versionQualifier string @@ -153,6 +154,11 @@ func initGlobals() { panic(fmt.Errorf("failed to parse EXTERNAL env value: %w", err)) } + FIPSBuild, err = strconv.ParseBool(EnvOr("FIPS", "false")) + if err != nil { + panic(fmt.Errorf("failed to parse FIPS env value: %w", err)) + } + versionQualifier, versionQualified = os.LookupEnv("VERSION_QUALIFIER") agentPackageVersion = EnvOr(agentPackageVersionEnvVar, "") @@ -210,6 +216,7 @@ func varMap(args ...map[string]interface{}) map[string]interface{} { "Snapshot": Snapshot, "DEV": DevBuild, "EXTERNAL": ExternalBuild, + "FIPS": FIPSBuild, "Qualifier": versionQualifier, "CI": CI, } diff --git a/dev-tools/packaging/package_test.go b/dev-tools/packaging/package_test.go index 41104997df1..59801d8cd4f 100644 --- a/dev-tools/packaging/package_test.go +++ b/dev-tools/packaging/package_test.go @@ -14,6 +14,8 @@ import ( "bytes" "compress/gzip" "crypto/sha512" + "debug/buildinfo" + "debug/elf" "encoding/hex" "encoding/json" "errors" @@ -33,7 +35,6 @@ import ( "github.com/cavaliergopher/rpm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" "github.com/elastic/elastic-agent/dev-tools/mage" v1 "github.com/elastic/elastic-agent/pkg/api/v1" @@ -70,6 +71,7 @@ var ( monitorsd = flag.Bool("monitors.d", false, "check monitors.d folder contents") rootOwner = flag.Bool("root-owner", false, "expect root to own package files") rootUserContainer = flag.Bool("root-user-container", false, "expect root in container user") + fips = flag.Bool("fips", false, "check agent binary for FIPS compliance") ) func TestRPM(t *testing.T) { @@ -91,7 +93,15 @@ func TestTar(t *testing.T) { // Regexp matches *-arch.tar.gz, but not *-arch.docker.tar.gz tars := getFiles(t, regexp.MustCompile(`-\w+\.tar\.gz$`)) for _, tar := range tars { - checkTar(t, tar) + checkTar(t, tar, false) + } +} + +func TestFIPSTar(t *testing.T) { + // Regexp matches *-arch-fips.tar.gz, but not *-arch.docker.tar.gz + tars := getFiles(t, regexp.MustCompile(`-\w+-fips\.tar\.gz$`)) + for _, tar := range tars { + checkTar(t, tar, *fips) } } @@ -154,7 +164,7 @@ func checkDeb(t *testing.T, file string, buf *bytes.Buffer) { checkSystemdUnitPermissions(t, p) } -func checkTar(t *testing.T, file string) { +func checkTar(t *testing.T, file string, fipsCheck bool) { p, err := readTar(file) if err != nil { t.Error(err) @@ -176,6 +186,9 @@ func checkTar(t *testing.T, file string) { require.NoError(t, err, "error extracting tar archive") containingDir := strings.TrimSuffix(path.Base(file), ".tar.gz") checkManifestFileContents(t, filepath.Join(tempExtractionPath, containingDir)) + if fipsCheck { + checkFIPS(t, filepath.Join(tempExtractionPath, containingDir)) + } }) checkSha512PackageHash(t, file) @@ -208,20 +221,7 @@ func checkZip(t *testing.T, file string) { func checkManifestFileContents(t *testing.T, extractedPackageDir string) { t.Log("Checking file manifest.yaml") - manifestReadCloser, err := os.Open(filepath.Join(extractedPackageDir, v1.ManifestFileName)) - if err != nil { - t.Errorf("opening manifest %s : %v", v1.ManifestFileName, err) - } - defer func(closer io.ReadCloser) { - err := closer.Close() - assert.NoError(t, err, "error closing manifest file") - }(manifestReadCloser) - - var m v1.PackageManifest - err = yaml.NewDecoder(manifestReadCloser).Decode(&m) - if err != nil { - t.Errorf("unmarshaling package manifest: %v", err) - } + m := parseManifest(t, extractedPackageDir) assert.Equal(t, v1.ManifestKind, m.Kind, "manifest specifies wrong kind") assert.Equal(t, v1.VERSION, m.Version, "manifest specifies wrong api version") @@ -243,6 +243,23 @@ func checkManifestFileContents(t *testing.T, extractedPackageDir string) { } } +func parseManifest(t *testing.T, dir string) v1.PackageManifest { + manifestReadCloser, err := os.Open(filepath.Join(dir, v1.ManifestFileName)) + if err != nil { + t.Errorf("opening manifest %s : %v", v1.ManifestFileName, err) + } + defer func(closer io.ReadCloser) { + err := closer.Close() + assert.NoError(t, err, "error closing manifest file") + }(manifestReadCloser) + + m, err := v1.ParseManifest(manifestReadCloser) + if err != nil { + t.Errorf("unmarshaling package manifest: %v", err) + } + return *m +} + const ( npcapLicense = `Dependency : Npcap \(https://nmap.org/npcap/\)` libpcapLicense = `Dependency : Libpcap \(http://www.tcpdump.org/\)` @@ -584,6 +601,55 @@ func checkDockerUser(t *testing.T, p *packageFile, info *dockerInfo, expectRoot }) } +func checkFIPS(t *testing.T, extractedPackageDir string) { + t.Logf("Checking agent binary in %q for FIPS compliance", extractedPackageDir) + m := parseManifest(t, extractedPackageDir) + versionedHome := m.Package.VersionedHome + require.DirExistsf(t, filepath.Join(extractedPackageDir, versionedHome), " versiondedHome directory %q not found in %q", versionedHome, extractedPackageDir) + binaryPath := filepath.Join(extractedPackageDir, versionedHome, "elastic-agent") // TODO eventually we will need to support .exe as well + require.FileExistsf(t, binaryPath, "Unable to find elastic-agent executable in versioned home in %q", extractedPackageDir) + + info, err := buildinfo.ReadFile(binaryPath) + require.NoError(t, err) + + foundTags := false + foundExperiment := false + for _, setting := range info.Settings { + switch setting.Key { + case "-tags": + foundTags = true + require.Contains(t, setting.Value, "requirefips") + continue + case "GOEXPERIMENT": + foundExperiment = true + require.Contains(t, setting.Value, "systemcrypto") + continue + } + } + + require.True(t, foundTags, "Did not find -tags within binary version information") + require.True(t, foundExperiment, "Did not find GOEXPERIMENT within binary version information") + + // TODO only elf is supported at the moment, in the future we will need to use macho (darwin) and pe (windows) + f, err := elf.Open(binaryPath) + require.NoError(t, err, "unable to open ELF file") + + symbols, err := f.Symbols() + if err != nil { + t.Logf("no symbols present in %q: %v", binaryPath, err) + return + } + + hasOpenSSL := false + for _, symbol := range symbols { + if strings.Contains(symbol.Name, "OpenSSL_version") { + hasOpenSSL = true + break + } + } + require.True(t, hasOpenSSL, "unable to find OpenSSL_version symbol") +} + // ensureNoBuildIDLinks checks for regressions related to // https://github.com/elastic/beats/issues/12956. func ensureNoBuildIDLinks(t *testing.T, p *packageFile) { diff --git a/internal/pkg/release/version.go b/internal/pkg/release/version.go index ba7b01ac657..6b6d44e3686 100644 --- a/internal/pkg/release/version.go +++ b/internal/pkg/release/version.go @@ -20,6 +20,9 @@ const ( // snapshot is a flag marking build as a snapshot. var snapshot = "" +// fips is a flag for marking a FIPS compliant build. +var fips = "false" + // complete is an environment variable marking the image as complete. var complete = "ELASTIC_AGENT_COMPLETE" @@ -77,12 +80,18 @@ func Complete() bool { return ok && isComplete == "true" } +func FIPS() bool { + f, err := strconv.ParseBool(fips) + return err == nil && f +} + // VersionInfo is structure used by `version --yaml`. type VersionInfo struct { Version string `yaml:"version"` Commit string `yaml:"commit"` BuildTime time.Time `yaml:"build_time"` Snapshot bool `yaml:"snapshot"` + FIPS bool `yaml:"fips"` } // Info returns current version information. @@ -92,6 +101,7 @@ func Info() VersionInfo { Commit: Commit(), BuildTime: BuildTime(), Snapshot: Snapshot(), + FIPS: FIPS(), } } @@ -105,8 +115,12 @@ func (v VersionInfo) String() string { } sb.WriteString(" (build: ") sb.WriteString(v.Commit) + if v.FIPS { + sb.WriteString(" fips: true") + } sb.WriteString(" at ") sb.WriteString(v.BuildTime.Format("2006-01-02 15:04:05 -0700 MST")) sb.WriteString(")") + return sb.String() } diff --git a/internal/pkg/release/version_test.go b/internal/pkg/release/version_test.go index 34ca8f45e7b..915550954bc 100644 --- a/internal/pkg/release/version_test.go +++ b/internal/pkg/release/version_test.go @@ -72,3 +72,25 @@ func TestVersion(t *testing.T) { assert.Equal(t, expectedVersion, actualVersion) }) } + +func Test_VersionInfo_WithFIPS(t *testing.T) { + info := Info() + info.FIPS = false + assert.NotContains(t, info.String(), "fips:", "found fips indicator") + info.FIPS = true + assert.Contains(t, info.String(), "fips: true", "did not find fips indicator") +} + +func TestFIPS(t *testing.T) { + oldFips := fips + t.Cleanup(func() { + fips = oldFips + }) + + fips = "" + assert.False(t, FIPS(), "expected FIPS indicator to be false") + fips = "false" + assert.False(t, FIPS(), "expected FIPS indicator to be false") + fips = "true" + assert.True(t, FIPS(), "expected FIPS indicator to be true") +} diff --git a/magefile.go b/magefile.go index 05c0cc32ed3..bdfc9aaf17a 100644 --- a/magefile.go +++ b/magefile.go @@ -82,6 +82,7 @@ const ( metaDir = "_meta" snapshotEnv = "SNAPSHOT" devEnv = "DEV" + fipsEnv = "FIPS" externalArtifacts = "EXTERNAL" platformsEnv = "PLATFORMS" packagesEnv = "PACKAGES" @@ -785,6 +786,9 @@ func (Cloud) Image(ctx context.Context) { variant := os.Getenv(dockerVariants) defer os.Setenv(dockerVariants, variant) + fips := os.Getenv(fipsEnv) + defer os.Setenv(fipsEnv, fips) + os.Setenv(platformsEnv, "linux/amd64") os.Setenv(packagesEnv, "docker") os.Setenv(devEnv, "true") @@ -799,6 +803,13 @@ func (Cloud) Image(ctx context.Context) { devtools.Snapshot = true } + fipsVal, err := strconv.ParseBool(fips) + if err != nil { + fipsVal = false + } + os.Setenv(fipsEnv, strconv.FormatBool(fipsVal)) + devtools.FIPSBuild = fipsVal + devtools.DevBuild = true devtools.Platforms = devtools.Platforms.Filter("linux/amd64") devtools.SelectedPackageTypes = []devtools.PackageType{devtools.Docker} @@ -1742,6 +1753,12 @@ func buildVars() map[string]string { isSnapshot, _ := os.LookupEnv(snapshotEnv) vars["github.com/elastic/elastic-agent/internal/pkg/release.snapshot"] = isSnapshot + if fipsFlag, fipsFound := os.LookupEnv(fipsEnv); fipsFound { + if fips, err := strconv.ParseBool(fipsFlag); err == nil && fips { + vars["github.com/elastic/elastic-agent/internal/pkg/release.fips"] = "true" + } + } + if isDevFlag, devFound := os.LookupEnv(devEnv); devFound { if isDev, err := strconv.ParseBool(isDevFlag); err == nil && isDev { vars["github.com/elastic/elastic-agent/internal/pkg/release.allowEmptyPgp"] = "true"