Skip to content

Commit 31e9350

Browse files
[9.1] (backport #8383) Adding a unified FIPS integration test (#8876)
* Adding a unified FIPS integration test (#8383) * Adding a unified FIPS integration test * Removing TestFIPSAgentConnectingToFIPSFleetServerInECHFRH * Uncomment build tag * Fix test name * Formatting imports * Wrap health check in require.Eventually * Wrap Agent health and FIPS check in require.Eventually * Fix typo * Fix require.Eventually assertion * Update reference * Add logging for debugging * Fix fleet server URL construction * Move FIPS upgrade test code into unified test * Fix redundant code block left around during conflict resolution * Fixing sudo requirement * Print out verbose output * Use require to fail fast * Use require.Eventually and log status * Add TODOs * Add check for system logs * Refactoring: moving test file into ess package * Removing redundant function declaration * Call testUpgradeFleetManagedElasticAgent instead of testFleetManagedUpgrade * Allow managed fleet upgrade test to skip install * Fix syntax error * Fix path to fixture file * Downgrade instead of upgrade * Remove debugging statement * Query ES directly instead of Kibana API (cherry picked from commit 1cf28a8) # Conflicts: # testing/integration/ess/upgrade_fleet_test.go * Fixing conflicts --------- Co-authored-by: Shaunak Kashyap <[email protected]>
1 parent 4e43b90 commit 31e9350

File tree

5 files changed

+274
-159
lines changed

5 files changed

+274
-159
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License 2.0;
3+
// you may not use this file except in compliance with the Elastic License 2.0.
4+
5+
//go:build integration
6+
7+
package ess
8+
9+
import (
10+
"context"
11+
"encoding/json"
12+
"net/http"
13+
"net/url"
14+
"strings"
15+
"testing"
16+
"time"
17+
18+
"github.com/gofrs/uuid/v5"
19+
"github.com/stretchr/testify/require"
20+
21+
"github.com/elastic/elastic-agent-libs/kibana"
22+
"github.com/elastic/elastic-agent-libs/testing/estools"
23+
atesting "github.com/elastic/elastic-agent/pkg/testing"
24+
"github.com/elastic/elastic-agent/pkg/testing/define"
25+
"github.com/elastic/elastic-agent/pkg/testing/tools"
26+
"github.com/elastic/elastic-agent/pkg/testing/tools/fleettools"
27+
"github.com/elastic/elastic-agent/testing/integration"
28+
"github.com/elastic/elastic-agent/testing/upgradetest"
29+
)
30+
31+
// TestFIPS exercises a FIPS-capable Elastic Agent against a FIPS-capable Fleet Server
32+
// running in ECH. Concretely, it exercises the following functionality:
33+
// - Building a local, FIPS-capable Elastic Agent Docker image that's deployable
34+
// to ECH in a FRH region (this is actually done by the CI pipeline running this integration
35+
// test).
36+
// - Creating a ECH deployment with the FIPS-capable Elastic Agent Docker image, which
37+
// run as Integrations Server / Fleet Server in the deployment (also done by the CI pipeline).
38+
// - Ensure that the ensures that the FIPS-capable Elastic Agent running in ECH is able to
39+
// successfully connect to its own local Fleet Server instance (which, by definition should
40+
// also be FIPS-capable and running in ECH).
41+
// - Installing the local FIPS-capable Elastic Agent artifact locally and enrolling it with the
42+
// ECH deployment's Fleet Server.
43+
// - Adding an integration to the Agent's policy. This has the effect of exercising
44+
// the connectivity between the Fleet UI (Kibana) in the ECH deployment and the Elastic Package
45+
// Registry (EPR) as well as the connectivity between the data collection component run by the
46+
// local Elastic Agent and Elasticsearch. The test checks that data for this integration shows
47+
// up in the Elasticsearch cluster that's part of the ECH deployment.
48+
// - Upgrading the local FIPS-capable Elastic Agent and ensuring that it only upgrades to another
49+
// FIPS-capable Elastic Agent version.
50+
func TestFIPS(t *testing.T) {
51+
info := define.Require(t, define.Requirements{
52+
Group: integration.Fleet,
53+
Stack: &define.Stack{},
54+
OS: []define.OS{
55+
{Type: define.Linux},
56+
},
57+
Sudo: true, // requires Agent installation
58+
Local: true,
59+
60+
// Ensures the test will run in a FIPS-configured environment against a
61+
// deployment in ECH that's running a FIPS-capable integrations server.
62+
FIPS: true,
63+
})
64+
65+
ensureFleetServerInDeploymentIsHealthyAndFIPSCapable(t, info)
66+
fixture, policyID := enrollLocalFIPSAgentInFleetServer(t, info)
67+
addIntegrationAndCheckData(t, info, fixture, policyID)
68+
downgradeFIPSAgent(t, info, fixture) // downgrade exercises same code paths as upgrade
69+
}
70+
71+
func ensureFleetServerInDeploymentIsHealthyAndFIPSCapable(t *testing.T, info *define.Info) {
72+
t.Helper()
73+
74+
// Check that the Fleet Server in the deployment is healthy
75+
fleetServerHost, err := fleettools.DefaultURL(t.Context(), info.KibanaClient)
76+
statusUrl, err := url.JoinPath(fleetServerHost, "/api/status")
77+
t.Logf("statusUrl = %s", statusUrl)
78+
require.NoError(t, err)
79+
80+
require.Eventually(t, func() bool {
81+
resp, err := http.Get(statusUrl)
82+
require.NoError(t, err)
83+
defer resp.Body.Close()
84+
85+
require.Equal(t, http.StatusOK, resp.StatusCode)
86+
87+
var body struct {
88+
Name string `json:"name"`
89+
Status string `json:"status"`
90+
}
91+
decoder := json.NewDecoder(resp.Body)
92+
err = decoder.Decode(&body)
93+
require.NoError(t, err)
94+
95+
t.Logf("body.Status = %s", body.Status)
96+
return body.Status == "HEALTHY"
97+
}, 5*time.Minute, 10*time.Second, "Fleet Server in ECH deployment is not healthy")
98+
99+
require.Eventually(t, func() bool {
100+
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
101+
defer cancel()
102+
103+
// Find Fleet Server's own Agent and get its status and whether it's
104+
// FIPS-capable
105+
searchResp, err := info.ESClient.Search(
106+
info.ESClient.Search.WithContext(ctx),
107+
info.ESClient.Search.WithIndex(".fleet-agents"),
108+
info.ESClient.Search.WithBody(strings.NewReader(`{
109+
"query": {
110+
"term": {
111+
"policy_id": "policy-elastic-agent-on-cloud"
112+
}
113+
}
114+
}`,
115+
)))
116+
require.NoError(t, err)
117+
defer searchResp.Body.Close()
118+
require.Equal(t, http.StatusOK, searchResp.StatusCode)
119+
120+
respObj := struct {
121+
Hits struct {
122+
Total struct {
123+
Value int `json:"value"`
124+
} `json:"total"`
125+
Hits []struct {
126+
Source struct {
127+
LocalMetadata struct {
128+
Elastic struct {
129+
Agent struct {
130+
FIPS bool `json:"fips"`
131+
} `json:"agent"`
132+
} `json:"elastic"`
133+
} `json:"local_metadata"`
134+
LastCheckinStatus string `json:"last_checkin_status"`
135+
} `json:"_source"`
136+
} `json:"hits"`
137+
} `json:"hits"`
138+
}{}
139+
140+
err = json.NewDecoder(searchResp.Body).Decode(&respObj)
141+
require.NoError(t, err)
142+
require.Equal(t, 1, respObj.Hits.Total.Value, "expected only one hit from the ES query")
143+
144+
// Check that this Agent is online (i.e. healthy) and is FIPS-capable. This
145+
// will prove that a FIPS-capable Agent is able to connect to a FIPS-capable
146+
// Fleet Server, with both running in ECH.
147+
t.Logf("FIPS: %v, Status: %s", respObj.Hits.Hits[0].Source.LocalMetadata.Elastic.Agent.FIPS, respObj.Hits.Hits[0].Source.LastCheckinStatus)
148+
return respObj.Hits.Hits[0].Source.LocalMetadata.Elastic.Agent.FIPS && respObj.Hits.Hits[0].Source.LastCheckinStatus == "online"
149+
}, 10*time.Second, 200*time.Millisecond, "Fleet Server's Elastic Agent should be healthy and FIPS-capable")
150+
}
151+
152+
func enrollLocalFIPSAgentInFleetServer(t *testing.T, info *define.Info) (*atesting.Fixture, string) {
153+
t.Helper()
154+
// Select FIPS-capable local Agent artifact
155+
fixture, err := define.NewFixtureFromLocalFIPSBuild(t, define.Version())
156+
require.NoError(t, err)
157+
158+
// Enroll Agent
159+
policyUUID := uuid.Must(uuid.NewV4()).String()
160+
basePolicy := kibana.AgentPolicy{
161+
Name: "test-policy-" + policyUUID,
162+
Namespace: "default",
163+
Description: "Test policy " + policyUUID,
164+
MonitoringEnabled: []kibana.MonitoringEnabledOption{
165+
kibana.MonitoringEnabledLogs,
166+
kibana.MonitoringEnabledMetrics,
167+
},
168+
}
169+
170+
installOpts := atesting.InstallOpts{
171+
NonInteractive: true,
172+
Force: true,
173+
Privileged: true,
174+
}
175+
176+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
177+
defer cancel()
178+
179+
policyResp, _, err := tools.InstallAgentWithPolicy(ctx, t, installOpts, fixture, info.KibanaClient, basePolicy)
180+
require.NoError(t, err)
181+
182+
return fixture, policyResp.ID
183+
}
184+
185+
func addIntegrationAndCheckData(t *testing.T, info *define.Info, fixture *atesting.Fixture, policyID string) {
186+
t.Helper()
187+
188+
// Install system integration
189+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
190+
defer cancel()
191+
_, err := tools.InstallPackageFromDefaultFile(ctx, info.KibanaClient, "system", integration.PreinstalledPackages["system"], "testdata/system_integration_setup.json", uuid.Must(uuid.NewV4()).String(), policyID)
192+
require.NoError(t, err)
193+
194+
// Ensure data from system integration shows up in Elasticsearch
195+
status, err := fixture.ExecStatus(ctx)
196+
require.NoError(t, err)
197+
198+
t.Logf("status: %v", status)
199+
200+
// Check that system metrics show up in Elasticsearch
201+
require.Eventually(t, func() bool {
202+
docs, err := estools.GetResultsForAgentAndDatastream(ctx, info.ESClient, "system.cpu", status.Info.ID)
203+
require.NoError(t, err, "error fetching system metrics")
204+
t.Logf("Generated %d system events", docs.Hits.Total.Value)
205+
206+
return docs.Hits.Total.Value > 0
207+
}, 2*time.Minute, 5*time.Second, "no system.cpu data received in Elasticsearch")
208+
209+
// Check that system logs show up in Elasticsearch
210+
require.Eventually(t, func() bool {
211+
docs, err := estools.GetResultsForAgentAndDatastream(ctx, info.ESClient, "system.syslog", status.Info.ID)
212+
require.NoError(t, err, "error fetching system logs")
213+
t.Logf("Generated %d system events", docs.Hits.Total.Value)
214+
215+
return docs.Hits.Total.Value > 0
216+
}, 2*time.Minute, 5*time.Second, "no system.syslog data received in Elasticsearch")
217+
}
218+
219+
func downgradeFIPSAgent(t *testing.T, info *define.Info, startFixture *atesting.Fixture) {
220+
t.Helper()
221+
222+
startVersion := define.Version()
223+
endVersions := getUpgradeableFIPSVersions(t)
224+
225+
for _, endVersion := range endVersions {
226+
t.Run(startVersion+"_to_"+endVersion.String(), func(t *testing.T) {
227+
ctx, cancel := context.WithCancel(context.TODO())
228+
defer cancel()
229+
230+
// Setup end fixture
231+
endFixture, err := atesting.NewFixture(
232+
t,
233+
endVersion.String(),
234+
atesting.WithFetcher(atesting.ArtifactFetcher(atesting.WithArtifactFIPSOnly())),
235+
)
236+
require.NoError(t, err)
237+
err = endFixture.Prepare(ctx)
238+
require.NoError(t, err)
239+
240+
testUpgradeFleetManagedElasticAgent(ctx, t, info, startFixture, endFixture, defaultPolicy(), false, upgradetest.WithoutInstall())
241+
})
242+
}
243+
}

testing/integration/ess/fleetserver_fips_test.go

Lines changed: 0 additions & 106 deletions
This file was deleted.

0 commit comments

Comments
 (0)