Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIPS Build #6565

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion dev-tools/mage/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"log"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/josephspurrier/goversioninfo"
Expand All @@ -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]+)`)

// ParseExtraFlags 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) ParseExtraFlags() []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 {
log.Printf("Parsing buildargs.ExtraFlags found strange flag %q ignoring value", 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{
Expand All @@ -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`)
Expand Down Expand Up @@ -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
Expand All @@ -159,7 +204,7 @@ func Build(params BuildArgs) error {
"-o",
filepath.Join(params.OutputDir, binaryName),
}
args = append(args, params.ExtraFlags...)
args = append(args, params.ParseExtraFlags()...)

// ldflags
ldflags := params.LDFlags
Expand Down
5 changes: 5 additions & 0 deletions dev-tools/mage/crossbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions dev-tools/mage/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently using this image directly fails; i think it's because mage is not installed.
The error when running FIPS=true PACKAGES="tar.gz" PLATFORMS=linux/arm64 mage package is:

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "--build-cmd": executable file not found in $PATH: unknown.
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "--build-cmd": executable file not found in $PATH: unknown.
package ran for 14.597428834s
Error: multiple failures: running "docker run --env EXEC_UID=501 --env EXEC_GID=20 -v /Users/mlaterman/go/pkg/mod:/go/pkg/mod:ro --rm --env GOFLAGS=-mod=readonly --env MAGEFILE_VERBOSE= --env MAGEFILE_TIMEOUT= --env SNAPSHOT=false --env DEV=false --env EXTERNAL=false --env FIPS=true -v /Users/mlaterman/git/elastic-agent:/go/src/github.com/elastic/elastic-agent -w /go/src/github.com/elastic/elastic-agent mcr.microsoft.com/oss/go/microsoft/golang:1.22.10-1-fips-bookworm --build-cmd build/mage-linux-arm64 buildGoDaemon --platforms linux/arm64" failed with exit code 127
multiple failures: running "docker run --env EXEC_UID=501 --env EXEC_GID=20 -v /Users/mlaterman/go/pkg/mod:/go/pkg/mod:ro --rm --env GOFLAGS=-mod=readonly --env MAGEFILE_VERBOSE= --env MAGEFILE_TIMEOUT= --env SNAPSHOT=false --env DEV=false --env EXTERNAL=false --env FIPS=true -v /Users/mlaterman/git/elastic-agent:/go/src/github.com/elastic/elastic-agent -w /go/src/github.com/elastic/elastic-agent mcr.microsoft.com/oss/go/microsoft/golang:1.22.10-1-fips-bookworm --build-cmd build/mage-linux-arm64 golangCrossBuild --platforms linux/arm64" failed with exit code 127


elasticAgentImportPath = "github.com/elastic/elastic-agent"

Expand Down Expand Up @@ -88,6 +90,7 @@ var (
Snapshot bool
DevBuild bool
ExternalBuild bool
FIPSBuild bool

versionQualified bool
versionQualifier string
Expand Down Expand Up @@ -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, "")
Expand Down Expand Up @@ -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,
}
Expand Down
14 changes: 14 additions & 0 deletions internal/pkg/release/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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.
Expand All @@ -92,6 +101,7 @@ func Info() VersionInfo {
Commit: Commit(),
BuildTime: BuildTime(),
Snapshot: Snapshot(),
FIPS: FIPS(),
}
}

Expand All @@ -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()
}
18 changes: 18 additions & 0 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const (
metaDir = "_meta"
snapshotEnv = "SNAPSHOT"
devEnv = "DEV"
fipsEnv = "FIPS"
externalArtifacts = "EXTERNAL"
platformsEnv = "PLATFORMS"
packagesEnv = "PACKAGES"
Expand Down Expand Up @@ -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")
Expand All @@ -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}
Expand Down Expand Up @@ -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"
Expand Down
Loading