From 3f18bfc064621719788d72bdccfe53e798010931 Mon Sep 17 00:00:00 2001
From: michel-laterman <michel.laterman@elastic.co>
Date: Fri, 17 Jan 2025 13:13:00 -0800
Subject: [PATCH] WIP try to get fips package available

---
 dev-tools/mage/build.go         | 10 ++++++++++
 dev-tools/mage/crossbuild.go    |  5 +++++
 dev-tools/mage/settings.go      |  9 +++++++++
 internal/pkg/release/version.go | 14 ++++++++++++++
 magefile.go                     | 18 ++++++++++++++++++
 5 files changed, 56 insertions(+)

diff --git a/dev-tools/mage/build.go b/dev-tools/mage/build.go
index 190efa543ea..576035692fd 100644
--- a/dev-tools/mage/build.go
+++ b/dev-tools/mage/build.go
@@ -53,6 +53,10 @@ func DefaultBuildArgs() BuildArgs {
 		args.ExtraFlags = append(args.ExtraFlags, "-buildmode", "pie")
 	}
 
+	if FIPSBuild {
+		args.ExtraFlags = append(args.ExtraFlags, "-tags=fipsrequired")
+	}
+
 	if DevBuild {
 		// Disable optimizations (-N) and inlining (-l) for debugging.
 		args.ExtraFlags = append(args.ExtraFlags, `-gcflags=all=-N -l`)
@@ -151,6 +155,12 @@ func Build(params BuildArgs) error {
 	if params.CGO {
 		cgoEnabled = "1"
 	}
+
+	if FIPSBuild {
+		cgoEnabled = "1"
+		env["GOEXPERIMENT"] = "systemcrypto"
+	}
+
 	env["CGO_ENABLED"] = cgoEnabled
 
 	// Spec
diff --git a/dev-tools/mage/crossbuild.go b/dev-tools/mage/crossbuild.go
index 30750602118..c7ef65b640b 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 {
+		return FIPSBuildImage + ":" + goVersion + "-1-fips-bookworm", nil
+	}
+
 	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/settings.go b/dev-tools/mage/settings.go
index 143baf4dcff..1e1f118e637 100644
--- a/dev-tools/mage/settings.go
+++ b/dev-tools/mage/settings.go
@@ -35,6 +35,8 @@ const (
 	beatsFPMImage = "docker.elastic.co/beats-dev/fpm"
 	// BeatsCrossBuildImage is the image used for crossbuilding Beats.
 	BeatsCrossBuildImage = "docker.elastic.co/beats-dev/golang-crossbuild"
+	//FIPSBuildImage is the image used for building FIPS compliant artifacts
+	FIPSBuildImage = "mcr.microsoft.com/oss/go/microsoft/golang"
 
 	elasticAgentImportPath = "github.com/elastic/elastic-agent"
 
@@ -88,6 +90,7 @@ var (
 	Snapshot      bool
 	DevBuild      bool
 	ExternalBuild bool
+	FIPSBuild     bool
 
 	versionQualified bool
 	versionQualifier string
@@ -153,6 +156,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 +218,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/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/magefile.go b/magefile.go
index cddd7e17466..6f59e4216ff 100644
--- a/magefile.go
+++ b/magefile.go
@@ -81,6 +81,7 @@ const (
 	metaDir           = "_meta"
 	snapshotEnv       = "SNAPSHOT"
 	devEnv            = "DEV"
+	fipsEnv           = "FIPS"
 	externalArtifacts = "EXTERNAL"
 	platformsEnv      = "PLATFORMS"
 	packagesEnv       = "PACKAGES"
@@ -786,6 +787,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")
@@ -800,6 +804,14 @@ func (Cloud) Image(ctx context.Context) {
 		devtools.Snapshot = true
 	}
 
+	if f, err := strconv.ParseBool(fips); err == nil && !f {
+		os.Setenv(fipsEnv, "false")
+		devtools.FIPSBuild = false
+	} else {
+		os.Setenv(fipsEnv, "true")
+		devtools.FIPSBuild = true
+	}
+
 	devtools.DevBuild = true
 	devtools.Platforms = devtools.Platforms.Filter("linux/amd64")
 	devtools.SelectedPackageTypes = []devtools.PackageType{devtools.Docker}
@@ -1756,6 +1768,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"