From f2f122a3a5d77f8e1680e277f9db911554788d2d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 16:59:46 -0700 Subject: [PATCH 01/11] Add ability for Filebeat plugins (inputs) to specify that they should be excluded from FIPS builds --- filebeat/input/v2/plugin.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/filebeat/input/v2/plugin.go b/filebeat/input/v2/plugin.go index 15a8b0f8ebed..0cd714f2f748 100644 --- a/filebeat/input/v2/plugin.go +++ b/filebeat/input/v2/plugin.go @@ -60,6 +60,10 @@ type Plugin struct { // Manager MUST be configured. The manager is used to create the inputs. Manager InputManager + + // ExcludeForFIPS marks the plugin as being excluded in FIPS-capable artifacts. If + // a plugin sets this to true and that plugin is configured, a message is logged. + ExcludeForFIPS bool } func (p Plugin) validate() error { @@ -75,5 +79,9 @@ func (p Plugin) validate() error { if p.Manager == nil { return fmt.Errorf("invalid plugin (%v) structure detected", p.Name) } + if p.ExcludeForFIPS { + return fmt.Errorf("plugin [%s] is not available as it is not FIPS-capable", p.Name) + } + return nil } From 75fc147f35b4b6f5d7bfc66c8c44a2b404d6e586 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 23:22:50 -0700 Subject: [PATCH 02/11] Remove ExcludeForFIPS implementation --- filebeat/beater/crawler_fips.go | 1 + filebeat/beater/crawler_nofips.go | 1 + filebeat/input/v2/plugin.go | 8 -------- x-pack/filebeat/input/o365audit/input.go | 1 + 4 files changed, 3 insertions(+), 8 deletions(-) create mode 100644 filebeat/beater/crawler_fips.go create mode 100644 filebeat/beater/crawler_nofips.go diff --git a/filebeat/beater/crawler_fips.go b/filebeat/beater/crawler_fips.go new file mode 100644 index 000000000000..e9c8e8fa7a00 --- /dev/null +++ b/filebeat/beater/crawler_fips.go @@ -0,0 +1 @@ +package beater diff --git a/filebeat/beater/crawler_nofips.go b/filebeat/beater/crawler_nofips.go new file mode 100644 index 000000000000..e9c8e8fa7a00 --- /dev/null +++ b/filebeat/beater/crawler_nofips.go @@ -0,0 +1 @@ +package beater diff --git a/filebeat/input/v2/plugin.go b/filebeat/input/v2/plugin.go index 0cd714f2f748..15a8b0f8ebed 100644 --- a/filebeat/input/v2/plugin.go +++ b/filebeat/input/v2/plugin.go @@ -60,10 +60,6 @@ type Plugin struct { // Manager MUST be configured. The manager is used to create the inputs. Manager InputManager - - // ExcludeForFIPS marks the plugin as being excluded in FIPS-capable artifacts. If - // a plugin sets this to true and that plugin is configured, a message is logged. - ExcludeForFIPS bool } func (p Plugin) validate() error { @@ -79,9 +75,5 @@ func (p Plugin) validate() error { if p.Manager == nil { return fmt.Errorf("invalid plugin (%v) structure detected", p.Name) } - if p.ExcludeForFIPS { - return fmt.Errorf("plugin [%s] is not available as it is not FIPS-capable", p.Name) - } - return nil } diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index dd8623406f3b..99df9d6c589a 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -53,6 +53,7 @@ type apiEnvironment struct { } func Plugin(log *logp.Logger, store statestore.States) v2.Plugin { + fmt.Println("in o365audit plugin") return v2.Plugin{ Name: pluginName, Stability: feature.Experimental, From f879dc4c50c6b537d97a2545761a163adda6ef04 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 23:23:16 -0700 Subject: [PATCH 03/11] Create and check FIPSAwareInput interface --- filebeat/beater/crawler.go | 4 ++++ filebeat/beater/crawler_fips.go | 24 ++++++++++++++++++++++++ filebeat/beater/crawler_nofips.go | 10 ++++++++++ filebeat/input/v2/compat/compat.go | 9 +++++++++ filebeat/input/v2/input-cursor/input.go | 7 +++++++ filebeat/input/v2/input.go | 7 +++++++ 6 files changed, 61 insertions(+) diff --git a/filebeat/beater/crawler.go b/filebeat/beater/crawler.go index 525ab544141f..a551cd27afa6 100644 --- a/filebeat/beater/crawler.go +++ b/filebeat/beater/crawler.go @@ -140,6 +140,10 @@ func (c *crawler) startInput( inputRunner.Once = c.once } + if err := checkFIPSCapability(runner); err != nil { + return err + } + c.inputs[id] = runner c.log.Infof("Starting input (ID: %d)", id) diff --git a/filebeat/beater/crawler_fips.go b/filebeat/beater/crawler_fips.go index e9c8e8fa7a00..2b384053abd3 100644 --- a/filebeat/beater/crawler_fips.go +++ b/filebeat/beater/crawler_fips.go @@ -1 +1,25 @@ +//go:build requirefips + package beater + +import ( + "fmt" + v2 "github.com/elastic/beats/v7/filebeat/input/v2" + "github.com/elastic/beats/v7/libbeat/cfgfile" +) + +func checkFIPSCapability(runner cfgfile.Runner) error { + fipsAwareInput, ok := runner.(v2.FIPSAwareInput) + if !ok { + // Input is not FIPS-aware; assume it's FIPS capable and proceed + // without error + return nil + } + + if fipsAwareInput.IsFIPSCapable() { + // Input is FIPS-capable, proceed without error + return nil + } + + return fmt.Errorf("running a FIPS-capable distribution but input %s is not FIPS capable", runner.String()) +} diff --git a/filebeat/beater/crawler_nofips.go b/filebeat/beater/crawler_nofips.go index e9c8e8fa7a00..109ea3807898 100644 --- a/filebeat/beater/crawler_nofips.go +++ b/filebeat/beater/crawler_nofips.go @@ -1 +1,11 @@ +//go:build !requirefips + package beater + +import "github.com/elastic/beats/v7/libbeat/cfgfile" + +func checkFIPSCapability(_ cfgfile.Runner) error { + // In non-FIPS builds, we assume all inputs are FIPS capable + // and proceed without error + return nil +} diff --git a/filebeat/input/v2/compat/compat.go b/filebeat/input/v2/compat/compat.go index 7a73def43ed3..1d807fbf1310 100644 --- a/filebeat/input/v2/compat/compat.go +++ b/filebeat/input/v2/compat/compat.go @@ -229,3 +229,12 @@ func (f *factory) generateCheckConfig(config *conf.C) (*conf.C, error) { return testCfg, nil } + +func (r *runner) IsFIPSCapable() bool { + if fipsAwareInput, ok := r.input.(v2.FIPSAwareInput); ok { + return fipsAwareInput.IsFIPSCapable() + } + + // Input does not implement FIPSAwareInput, assume it is FIPS-capable + return true +} diff --git a/filebeat/input/v2/input-cursor/input.go b/filebeat/input/v2/input-cursor/input.go index 57f0a7d40f06..eba5b55f136c 100644 --- a/filebeat/input/v2/input-cursor/input.go +++ b/filebeat/input/v2/input-cursor/input.go @@ -87,6 +87,13 @@ func (inp *managedInput) Test(ctx input.TestContext) error { return nil } +func (inp *managedInput) IsFIPSCapable() bool { + if fipsAware, ok := inp.input.(input.FIPSAwareInput); ok { + return fipsAware.IsFIPSCapable() + } + return true +} + func (inp *managedInput) testSource(ctx input.TestContext, source Source) (err error) { defer func() { if v := recover(); v != nil { diff --git a/filebeat/input/v2/input.go b/filebeat/input/v2/input.go index cf7d2fff2dbb..f35438eb7940 100644 --- a/filebeat/input/v2/input.go +++ b/filebeat/input/v2/input.go @@ -78,6 +78,13 @@ type Input interface { Run(Context, beat.PipelineConnector) error } +// FIPSAwareInput is able to report if it is FIPS capable or not. +type FIPSAwareInput interface { + // IsFIPSCapable returns true if the input is capable of running with + // FIPS-compliant algorithms; false, otherwise. + IsFIPSCapable() bool +} + // Context provides the Input Run function with common environmental // information and services. type Context struct { From e930db28790738fcc1cb2c1fab6be13aa31daf82 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 23:23:48 -0700 Subject: [PATCH 04/11] Make o365 input use FIPSAwareInput interface --- x-pack/filebeat/input/o365audit/input.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index 99df9d6c589a..ae76c3a92288 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -94,6 +94,10 @@ func (s *stream) Name() string { func (inp *o365input) Name() string { return pluginName } +func (inp *o365input) IsFIPSCapable() bool { + return false +} + func (inp *o365input) Test(src cursor.Source, ctx v2.TestContext) error { tenantID := src.(*stream).tenantID auth, err := inp.config.NewTokenProvider(tenantID) From af83f369f5205f245b36b45bfeb351f0cdb31a92 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 23:25:05 -0700 Subject: [PATCH 05/11] Running mage fmt --- filebeat/beater/crawler_fips.go | 18 ++++++++++++++++++ filebeat/beater/crawler_nofips.go | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/filebeat/beater/crawler_fips.go b/filebeat/beater/crawler_fips.go index 2b384053abd3..3ca9203e7449 100644 --- a/filebeat/beater/crawler_fips.go +++ b/filebeat/beater/crawler_fips.go @@ -1,9 +1,27 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + //go:build requirefips package beater import ( "fmt" + v2 "github.com/elastic/beats/v7/filebeat/input/v2" "github.com/elastic/beats/v7/libbeat/cfgfile" ) diff --git a/filebeat/beater/crawler_nofips.go b/filebeat/beater/crawler_nofips.go index 109ea3807898..3309df43f008 100644 --- a/filebeat/beater/crawler_nofips.go +++ b/filebeat/beater/crawler_nofips.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + //go:build !requirefips package beater From bb0799e1cecc48daced21f9ff4e866734daafe54 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Jun 2025 23:46:30 -0700 Subject: [PATCH 06/11] Remove debugging statement --- x-pack/filebeat/input/o365audit/input.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index ae76c3a92288..13814ce6889d 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -53,7 +53,6 @@ type apiEnvironment struct { } func Plugin(log *logp.Logger, store statestore.States) v2.Plugin { - fmt.Println("in o365audit plugin") return v2.Plugin{ Name: pluginName, Stability: feature.Experimental, From f71dec544e5c3a0580d6dd7dfc358fd3ed06e7e4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 24 Jun 2025 12:17:51 -0700 Subject: [PATCH 07/11] Explain behavior for types not implementing the interface --- filebeat/input/v2/input.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filebeat/input/v2/input.go b/filebeat/input/v2/input.go index f35438eb7940..2585dd259107 100644 --- a/filebeat/input/v2/input.go +++ b/filebeat/input/v2/input.go @@ -78,7 +78,8 @@ type Input interface { Run(Context, beat.PipelineConnector) error } -// FIPSAwareInput is able to report if it is FIPS capable or not. +// FIPSAwareInput is able to report if it is FIPS capable or not. If a type does +// not implement this interface, that type will be considered to be FIPS capable. type FIPSAwareInput interface { // IsFIPSCapable returns true if the input is capable of running with // FIPS-compliant algorithms; false, otherwise. From 3b05db89d53c9bd004f8081556a1fe250e8ff572 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 24 Jun 2025 12:22:26 -0700 Subject: [PATCH 08/11] Adding godoc comments for implementations of the IsFIPSCapable() method --- filebeat/input/v2/compat/compat.go | 2 ++ filebeat/input/v2/input-cursor/input.go | 2 ++ x-pack/filebeat/input/o365audit/input.go | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/filebeat/input/v2/compat/compat.go b/filebeat/input/v2/compat/compat.go index 1d807fbf1310..5d29727613da 100644 --- a/filebeat/input/v2/compat/compat.go +++ b/filebeat/input/v2/compat/compat.go @@ -230,6 +230,8 @@ func (f *factory) generateCheckConfig(config *conf.C) (*conf.C, error) { return testCfg, nil } +// IsFIPSCapable returns true if the input is capable of running with +// FIPS-compliant algorithms; false, otherwise. func (r *runner) IsFIPSCapable() bool { if fipsAwareInput, ok := r.input.(v2.FIPSAwareInput); ok { return fipsAwareInput.IsFIPSCapable() diff --git a/filebeat/input/v2/input-cursor/input.go b/filebeat/input/v2/input-cursor/input.go index eba5b55f136c..5b1c5fcd8773 100644 --- a/filebeat/input/v2/input-cursor/input.go +++ b/filebeat/input/v2/input-cursor/input.go @@ -87,6 +87,8 @@ func (inp *managedInput) Test(ctx input.TestContext) error { return nil } +// IsFIPSCapable returns true if the input is capable of running with +// FIPS-compliant algorithms; false, otherwise. func (inp *managedInput) IsFIPSCapable() bool { if fipsAware, ok := inp.input.(input.FIPSAwareInput); ok { return fipsAware.IsFIPSCapable() diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index 13814ce6889d..511bb2f8f86f 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -93,6 +93,11 @@ func (s *stream) Name() string { func (inp *o365input) Name() string { return pluginName } +// IsFIPSCapable returns false because the o365 input indirectly does +// not use FIPS-compliant algorithms. Specifically, the input depends on +// the github.com/Azure/azure-sdk-for-go/sdk/azidentity package which, in +// turn, depends on the golang.org/x/crypto/pkcs12 package, which is not +// FIPS-compliant func (inp *o365input) IsFIPSCapable() bool { return false } From fe9973ee9c645b2beff8f9713f55ab92bf20407d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 24 Jun 2025 12:46:43 -0700 Subject: [PATCH 09/11] Add unit test for checkFIPSCapability() --- filebeat/beater/crawler_fips.go | 2 +- filebeat/beater/crawler_fips_test.go | 74 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 filebeat/beater/crawler_fips_test.go diff --git a/filebeat/beater/crawler_fips.go b/filebeat/beater/crawler_fips.go index 3ca9203e7449..f43fe9f13781 100644 --- a/filebeat/beater/crawler_fips.go +++ b/filebeat/beater/crawler_fips.go @@ -39,5 +39,5 @@ func checkFIPSCapability(runner cfgfile.Runner) error { return nil } - return fmt.Errorf("running a FIPS-capable distribution but input %s is not FIPS capable", runner.String()) + return fmt.Errorf("running a FIPS-capable distribution but input [%s] is not FIPS capable", runner.String()) } diff --git a/filebeat/beater/crawler_fips_test.go b/filebeat/beater/crawler_fips_test.go new file mode 100644 index 000000000000..2cd401f33c02 --- /dev/null +++ b/filebeat/beater/crawler_fips_test.go @@ -0,0 +1,74 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build requirefips + +package beater + +import ( + "github.com/elastic/beats/v7/libbeat/cfgfile" + "github.com/stretchr/testify/require" + "testing" +) + +type fipsUnawareInput struct{} + +func newFIPSUnawareInput() *fipsUnawareInput { return &fipsUnawareInput{} } +func (f *fipsUnawareInput) String() string { return "fips_unaware_input" } +func (f *fipsUnawareInput) Start() {} +func (f *fipsUnawareInput) Stop() {} + +type fipsAwareInput struct{ isFIPSCapable bool } + +func newFIPSAwareInput(isFIPSCapable bool) *fipsAwareInput { + return &fipsAwareInput{isFIPSCapable: isFIPSCapable} +} +func (f *fipsAwareInput) String() string { return "fips_aware_input" } +func (f *fipsAwareInput) Start() {} +func (f *fipsAwareInput) Stop() {} +func (f *fipsAwareInput) IsFIPSCapable() bool { return f.isFIPSCapable } + +func TestCheckFIPSCapability(t *testing.T) { + tests := map[string]struct { + runner cfgfile.Runner + expectedErr string + }{ + "input_is_not_fips_aware": { + runner: newFIPSUnawareInput(), + expectedErr: "", + }, + "input_is_fips_aware_but_not_fips_capable": { + runner: newFIPSAwareInput(false), + expectedErr: "running a FIPS-capable distribution but input [fips_aware_input] is not FIPS capable", + }, + "input_is_fips_aware_and_fips_capable": { + runner: newFIPSAwareInput(true), + expectedErr: "", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + err := checkFIPSCapability(test.runner) + if test.expectedErr == "" { + require.NoError(t, err) + } else { + require.EqualError(t, err, test.expectedErr) + } + }) + } +} From 72cf251238abe7e067365a12bbafb7bb428494d6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 24 Jun 2025 13:17:09 -0700 Subject: [PATCH 10/11] Running mage fmt --- filebeat/beater/crawler_fips_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filebeat/beater/crawler_fips_test.go b/filebeat/beater/crawler_fips_test.go index 2cd401f33c02..b8e4dccc3af6 100644 --- a/filebeat/beater/crawler_fips_test.go +++ b/filebeat/beater/crawler_fips_test.go @@ -20,9 +20,11 @@ package beater import ( - "github.com/elastic/beats/v7/libbeat/cfgfile" - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/cfgfile" ) type fipsUnawareInput struct{} From ecfdbb809d889f184f144cf801849cb97f2ed0ac Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 24 Jun 2025 13:18:16 -0700 Subject: [PATCH 11/11] Remove example implementation --- x-pack/filebeat/input/o365audit/input.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index 511bb2f8f86f..dd8623406f3b 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -93,15 +93,6 @@ func (s *stream) Name() string { func (inp *o365input) Name() string { return pluginName } -// IsFIPSCapable returns false because the o365 input indirectly does -// not use FIPS-compliant algorithms. Specifically, the input depends on -// the github.com/Azure/azure-sdk-for-go/sdk/azidentity package which, in -// turn, depends on the golang.org/x/crypto/pkcs12 package, which is not -// FIPS-compliant -func (inp *o365input) IsFIPSCapable() bool { - return false -} - func (inp *o365input) Test(src cursor.Source, ctx v2.TestContext) error { tenantID := src.(*stream).tenantID auth, err := inp.config.NewTokenProvider(tenantID)