Skip to content

Commit 7738141

Browse files
michel-latermanmergify[bot]
authored andcommitted
Add Pipeline to deploy custom agent image for FIPS testing (#8035)
Add a new buildkite pipeline to build a custom agent image and use it in an ECH deployment for testing. Run FIPS integration tests on VMs with a FIPS provider. (cherry picked from commit 5908ed2) # Conflicts: # testing/integration/fleetserver_fips_test.go
1 parent a914f66 commit 7738141

File tree

8 files changed

+301
-3
lines changed

8 files changed

+301
-3
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/buildkite/pipeline-schema/main/schema.json
2+
3+
env:
4+
DOCKER_REGISTRY: "docker.elastic.co"
5+
ASDF_MAGE_VERSION: 1.14.0
6+
7+
IMAGE_UBUNTU_2404_X86_64: "platform-ingest-elastic-agent-ubuntu-2404-1749258065"
8+
IMAGE_UBUNTU_X86_64_FIPS: "platform-ingest-elastic-agent-ubuntu-2204-fips-1748955449"
9+
IMAGE_UBUNTU_ARM64_FIPS: "platform-ingest-elastic-agent-ubuntu-2204-fips-aarch64-1748955449"
10+
11+
steps:
12+
- label: Build and push custom elastic-agent image
13+
depends_on:
14+
- 'packaging-containers-x86-64-fips' # Reuse artifacts produced in .buildkite/integration.pipeline.yml
15+
key: integration-fips-cloud-image
16+
env:
17+
FIPS: "true"
18+
CUSTOM_IMAGE_TAG: "git-${BUILDKITE_COMMIT:0:12}"
19+
CI_ELASTIC_AGENT_DOCKER_IMAGE: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud"
20+
TF_VAR_integration_server_docker_image: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud:git-${BUILDKITE_COMMIT:0:12}"
21+
command: |
22+
buildkite-agent artifact download build/distributions/elastic-agent-fips-cloud-*-linux-amd64.docker.tar.gz . --step 'packaging-containers-x86-64-fips'
23+
mage cloud:load
24+
mage cloud:push
25+
agents:
26+
provider: "gcp"
27+
machineType: "n1-standard-8"
28+
image: "${IMAGE_UBUNTU_2404_X86_64}"
29+
30+
- label: Start ESS stack for FIPS integration tests
31+
key: integration-fips-ess
32+
depends_on:
33+
- integration-fips-cloud-image
34+
env:
35+
ASDF_TERRAFORM_VERSION: 1.9.2
36+
CUSTOM_IMAGE_TAG: "git-${BUILDKITE_COMMIT:0:12}"
37+
CI_ELASTIC_AGENT_DOCKER_IMAGE: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud"
38+
TF_VAR_integration_server_docker_image: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud:git-${BUILDKITE_COMMIT:0:12}"
39+
command: |
40+
source .buildkite/scripts/steps/ess_start.sh
41+
artifact_paths:
42+
- test_infra/ess/*.tfstate
43+
- test_infra/ess/*.lock.hcl
44+
agents:
45+
image: "docker.elastic.co/ci-agent-images/platform-ingest/buildkite-agent-beats-ci-with-hooks:0.5"
46+
useCustomGlobalHooks: true
47+
48+
- group: "fips:Stateful:Ubuntu"
49+
key: integration-tests-ubuntu-fips
50+
depends_on:
51+
- integration-fips-ess
52+
steps:
53+
- label: "fips:x86_64:sudo-{{matrix.sudo}}:{{matrix.groups}}"
54+
depends_on:
55+
- packaging-ubuntu-x86-64-fips # Reuse artifacts produced in .buildkite/integration.pipeline.yml
56+
env:
57+
FIPS: "true"
58+
CUSTOM_IMAGE_TAG: "git-${BUILDKITE_COMMIT:0:12}"
59+
CI_ELASTIC_AGENT_DOCKER_IMAGE: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud"
60+
TF_VAR_integration_server_docker_image: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud:git-${BUILDKITE_COMMIT:0:12}"
61+
command: |
62+
buildkite-agent artifact download build/distributions/** . --step 'packaging-ubuntu-x86-64-fips'
63+
.buildkite/scripts/steps/integration_tests_tf.sh {{matrix.groups}} {{matrix.sudo}}
64+
artifact_paths:
65+
- build/**
66+
- build/diagnostics/**
67+
retry:
68+
automatic:
69+
limit: 1
70+
agents:
71+
provider: "aws"
72+
image: "${IMAGE_UBUNTU_X86_64_FIPS}"
73+
instanceType: "m5.2xlarge"
74+
matrix:
75+
setup:
76+
sudo:
77+
- "false"
78+
- "true"
79+
groups:
80+
- fleet # currently there is only a single test in the fleet group, add more tests once they have been defined
81+
82+
- label: "fips:arm64:sudo-{{matrix.sudo}}:{{matrix.groups}}"
83+
depends_on:
84+
- packaging-ubuntu-arm64-fips
85+
env:
86+
FIPS: "true"
87+
CUSTOM_IMAGE_TAG: "git-${BUILDKITE_COMMIT:0:12}"
88+
CI_ELASTIC_AGENT_DOCKER_IMAGE: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud"
89+
TF_VAR_integration_server_docker_image: "docker.elastic.co/beats-ci/elastic-agent-fips-cloud:git-${BUILDKITE_COMMIT:0:12}"
90+
command: |
91+
buildkite-agent artifact download build/distributions/** . --step 'packaging-ubuntu-arm64-fips'
92+
.buildkite/scripts/steps/integration_tests_tf.sh {{matrix.groups}} {{matrix.sudo}}
93+
artifact_paths:
94+
- build/**
95+
- build/diagnostics/**
96+
retry:
97+
automatic:
98+
limit: 1
99+
agents:
100+
provider: "aws"
101+
image: "${IMAGE_UBUNTU_ARM64_FIPS}"
102+
instanceType: "m6g.2xlarge"
103+
matrix:
104+
setup:
105+
sudo:
106+
- "false"
107+
- "true"
108+
groups:
109+
- fleet
110+
111+
- label: ESS FIPS stack cleanup
112+
depends_on:
113+
- integration-tests-ubuntu-fips
114+
allow_dependency_failure: true
115+
command: |
116+
buildkite-agent artifact download "test_infra/ess/**" . --step "integration-fips-ess"
117+
ls -lah test_infra/ess
118+
.buildkite/scripts/steps/ess_down.sh
119+
agents:
120+
image: "docker.elastic.co/ci-agent-images/platform-ingest/buildkite-agent-beats-ci-with-hooks:0.5"
121+
useCustomGlobalHooks: true
122+
123+
- label: Aggregate test reports
124+
depends_on:
125+
- integration-tests-ubuntu-fips
126+
allow_dependency_failure: true
127+
command: |
128+
buildkite-agent artifact download "build/*.xml" .
129+
agents:
130+
image: "docker.elastic.co/ci-agent-images/platform-ingest/buildkite-agent-beats-ci-with-hooks:0.5"
131+
useCustomGlobalHooks: true
132+
soft_fail:
133+
- exit_status: "*"
134+
plugins:
135+
- elastic/vault-secrets#v0.1.0:
136+
path: "kv/ci-shared/platform-ingest/buildkite_analytics_token"
137+
field: "token"
138+
env_var: "BUILDKITE_ANALYTICS_TOKEN"
139+
- test-collector#v1.11.0:
140+
files: "build/*.xml"
141+
format: "junit"
142+
branches: "main"
143+
debug: true

.buildkite/hooks/pre-command

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,7 @@ if [[ "$BUILDKITE_PIPELINE_SLUG" == "elastic-agent-binary-dra" ]]; then
7272
release_manager_login
7373
fi
7474
fi
75+
76+
if [[ "$BUILDKITE_PIPELINE_SLUG" == "elastic-agent" && "$BUILDKITE_STEP_KEY" == "integration-fips-cloud-image" ]]; then
77+
docker_login
78+
fi

.buildkite/integration.pipeline.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,8 @@ steps:
177177
depends_on:
178178
- int-packaging
179179
command: "buildkite-agent pipeline upload .buildkite/bk.integration.pipeline.yml"
180+
181+
- label: "Triggering custom FIPS integration tests"
182+
depends_on:
183+
- int-packaging
184+
command: "buildkite-agent pipeline upload .buildkite/bk.integration-fips.pipeline.yml"

.buildkite/scripts/buildkite-integration-tests.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ if [ -z "$TEST_SUDO" ]; then
1515
exit 1
1616
fi
1717

18+
if [ "${FIPS:-false}" == "true" ]; then
19+
echo "~~~FIPS: Checking msft-go is installed"
20+
GOEXPERIMENT=systemcrypto go version
21+
fi
22+
1823
if [ "$TEST_SUDO" == "true" ]; then
1924
echo "Re-initializing ASDF. The user is changed to root..."
2025
export ASDF_DATA_DIR="/opt/buildkite-agent/.asdf"
@@ -50,7 +55,7 @@ GOTEST_ARGS=(-tags integration -test.shuffle on -test.timeout 2h0m0s)
5055
if [ -n "$TEST_NAME_PATTERN" ]; then
5156
GOTEST_ARGS+=(-run="${TEST_NAME_PATTERN}")
5257
fi
53-
GOTEST_ARGS+=("github.com/elastic/elastic-agent/testing/integration" -v -args "-integration.groups=${GROUP_NAME}" "-integration.sudo=${TEST_SUDO}")
58+
GOTEST_ARGS+=("github.com/elastic/elastic-agent/testing/integration" -v -args "-integration.groups=${GROUP_NAME}" "-integration.sudo=${TEST_SUDO}" "-integration.fips=${FIPS:-false}")
5459

5560
set +e
5661
TEST_BINARY_NAME="elastic-agent" AGENT_VERSION="${AGENT_VERSION}" SNAPSHOT=true \

.ci/updatecli/updatecli-bump-vm-images.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,13 @@ targets:
6363
file: .buildkite/bk.integration.pipeline.yml
6464
matchpattern: '(IMAGE_.+): "platform-ingest-elastic-agent-(.+)-(.+)"'
6565
replacepattern: '$1: "platform-ingest-elastic-agent-$2-{{ source "latestVersion" }}"'
66+
67+
update-buildkite-bk.integration-fips.pipeline:
68+
name: "Update .buildkite/bk.integration-fips.pipeline.yml"
69+
sourceid: latestVersion
70+
scmid: githubConfig
71+
kind: file
72+
spec:
73+
file: .buildkite/bk.integration-fips.pipeline.yml
74+
matchpattern: '(IMAGE_.+): "platform-ingest-elastic-agent-(.+)-(.+)"'
75+
replacepattern: '$1: "platform-ingest-elastic-agent-$2-{{ source "latestVersion" }}"'

.tool-versions

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
mage 1.14.0
2-
golang 1.24.0
32
terraform 1.9.3

magefile.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,37 @@ func (Cloud) Image(ctx context.Context) {
979979
Package(ctx)
980980
}
981981

982+
// Load loads an artifact as a docker image.
983+
// Looks in build/distributions for an elastic-agent-cloud*.docker.tar.gz artifact and imports it as docker.elastic.co/beats-ci/elastic-agent-cloud:$VERSION
984+
// DOCKER_IMPORT_SOURCE - override source for import
985+
func (Cloud) Load() error {
986+
snapshot := os.Getenv(snapshotEnv)
987+
defer os.Setenv(snapshotEnv, snapshot)
988+
os.Setenv(snapshotEnv, "true")
989+
990+
version := getVersion()
991+
992+
// Need to get the FIPS env var flag to see if we are using the normal source cloud image name, or the FIPS variant
993+
fips := os.Getenv(fipsEnv)
994+
defer os.Setenv(fipsEnv, fips)
995+
fipsVal, err := strconv.ParseBool(fips)
996+
if err != nil {
997+
fipsVal = false
998+
}
999+
os.Setenv(fipsEnv, strconv.FormatBool(fipsVal))
1000+
devtools.FIPSBuild = fipsVal
1001+
1002+
source := "build/distributions/elastic-agent-cloud-" + version + "-linux-" + runtime.GOARCH + ".docker.tar.gz"
1003+
if fipsVal {
1004+
source = "build/distributions/elastic-agent-fips-cloud-" + version + "-linux-" + runtime.GOARCH + ".docker.tar.gz"
1005+
}
1006+
if envSource, ok := os.LookupEnv("DOCKER_IMPORT_SOURCE"); ok && envSource != "" {
1007+
source = envSource
1008+
}
1009+
1010+
return sh.RunV("docker", "image", "load", "-i", source)
1011+
}
1012+
9821013
// Push builds a cloud image tags it correctly and pushes to remote image repo.
9831014
// Previous login to elastic registry is required!
9841015
func (Cloud) Push() error {
@@ -998,7 +1029,20 @@ func (Cloud) Push() error {
9981029
tag = fmt.Sprintf("%s-%s-%d", version, commit, time)
9991030
}
10001031

1032+
// Need to get the FIPS env var flag to see if we are using the normal source cloud image name, or the FIPS variant
1033+
fips := os.Getenv(fipsEnv)
1034+
defer os.Setenv(fipsEnv, fips)
1035+
fipsVal, err := strconv.ParseBool(fips)
1036+
if err != nil {
1037+
fipsVal = false
1038+
}
1039+
os.Setenv(fipsEnv, strconv.FormatBool(fipsVal))
1040+
devtools.FIPSBuild = fipsVal
1041+
10011042
sourceCloudImageName := fmt.Sprintf("docker.elastic.co/beats-ci/elastic-agent-cloud:%s", version)
1043+
if fipsVal {
1044+
sourceCloudImageName = fmt.Sprintf("docker.elastic.co/beats-ci/elastic-agent-fips-cloud:%s", version)
1045+
}
10021046
var targetCloudImageName string
10031047
if customImage, isPresent := os.LookupEnv("CI_ELASTIC_AGENT_DOCKER_IMAGE"); isPresent && len(customImage) > 0 {
10041048
targetCloudImageName = fmt.Sprintf("%s:%s", customImage, tag)
@@ -1007,7 +1051,7 @@ func (Cloud) Push() error {
10071051
}
10081052

10091053
fmt.Printf(">> Setting a docker image tag to %s\n", targetCloudImageName)
1010-
err := sh.RunV("docker", "tag", sourceCloudImageName, targetCloudImageName)
1054+
err = sh.RunV("docker", "tag", sourceCloudImageName, targetCloudImageName)
10111055
if err != nil {
10121056
return fmt.Errorf("Failed setting a docker image tag: %w", err)
10131057
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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 integration
8+
9+
import (
10+
"context"
11+
"encoding/json"
12+
"net/http"
13+
"net/url"
14+
"testing"
15+
"time"
16+
17+
"github.com/stretchr/testify/require"
18+
19+
"github.com/elastic/elastic-agent-libs/kibana"
20+
"github.com/elastic/elastic-agent/pkg/testing/define"
21+
"github.com/elastic/elastic-agent/pkg/testing/tools/fleettools"
22+
)
23+
24+
const cloudAgentPolicyID = "policy-elastic-agent-on-cloud"
25+
26+
// TestFIPSAgentConnectingToFIPSFleetServerInECHFRH ensures that a FIPS-capable Elastic Agent
27+
// running in an ECH FRH (FedRamp High) environment is able to successfully connect to its
28+
// own local Fleet Server instance (which, by definition should also be FIPS-capable and
29+
// running in the ECH FRH environment).
30+
func TestFIPSAgentConnectingToFIPSFleetServerInECHFRH(t *testing.T) {
31+
info := define.Require(t, define.Requirements{
32+
Group: Fleet,
33+
Stack: &define.Stack{},
34+
OS: []define.OS{
35+
{Type: define.Linux},
36+
},
37+
Sudo: false,
38+
Local: true,
39+
40+
// Ensures the test will run in a FIPS-configured environment against a
41+
// deployment in ECH that's running a FIPS-capable integrations server.
42+
FIPS: true,
43+
})
44+
45+
fleetServerHost, err := fleettools.DefaultURL(t.Context(), info.KibanaClient)
46+
require.NoError(t, err)
47+
statusUrl, err := url.JoinPath(fleetServerHost, "/api/status")
48+
require.NoError(t, err)
49+
50+
resp, err := http.Get(statusUrl)
51+
require.NoError(t, err)
52+
defer resp.Body.Close()
53+
54+
var body struct {
55+
Name string `json:"name"`
56+
Status string `json:"status"`
57+
}
58+
decoder := json.NewDecoder(resp.Body)
59+
err = decoder.Decode(&body)
60+
require.NoError(t, err)
61+
62+
require.Equalf(t, "HEALTHY", body.Status, "response status code: %d", resp.StatusCode)
63+
64+
// Get all Agents
65+
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
66+
defer cancel()
67+
agents, err := info.KibanaClient.ListAgents(ctx, kibana.ListAgentsRequest{})
68+
require.NoError(t, err)
69+
70+
// Find Fleet Server's own Agent and get its status and whether it's
71+
// FIPS-capable
72+
//var agentStatus string
73+
var agentIsFIPS bool
74+
for _, item := range agents.Items {
75+
if item.PolicyID == cloudAgentPolicyID {
76+
t.Logf("Found fleet-server entry: %+v", item)
77+
//agentStatus = item.Status
78+
agentIsFIPS = item.LocalMetadata.Elastic.Agent.FIPS
79+
break
80+
}
81+
}
82+
83+
// Check that this Agent is online (i.e. healthy) and is FIPS-capable. This
84+
// will prove that a FIPS-capable Agent is able to connect to a FIPS-capable
85+
// Fleet Server, with both running in ECH.
86+
require.Equal(t, true, agentIsFIPS)
87+
//require.Equal(t, "online", agentStatus) // FIXME: Uncomment after https://github.com/elastic/apm-server/issues/17063 is resolved
88+
}

0 commit comments

Comments
 (0)