diff --git a/.buildkite/pipelines/pull_request/fips.yml b/.buildkite/pipelines/pull_request/fips.yml new file mode 100644 index 0000000000000..dac30490b3733 --- /dev/null +++ b/.buildkite/pipelines/pull_request/fips.yml @@ -0,0 +1,14 @@ +steps: + - command: .buildkite/scripts/steps/fips/build.sh + label: 'Build FIPS Image' + agents: + queue: n2-2-spot + depends_on: + - build + - quick_checks + timeout_in_minutes: 60 + soft_fail: true + retry: + automatic: + - exit_status: '-1' + limit: 3 diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 66e53f80bdf5a..537bee3a290a2 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -31,6 +31,7 @@ if is_pr_with_label "ci:build-cloud-image"; then --docker-tag-qualifier="$GIT_COMMIT" \ --docker-push \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-ubuntu \ --skip-docker-serverless \ --skip-docker-contexts diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 07bd9472f5242..25539aecb0707 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -139,6 +139,10 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/deploy_cloud.yml')); } + if (GITHUB_PR_LABELS.includes('ci:build-docker-fips')) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/fips.yml')); + } + if ( GITHUB_PR_LABELS.includes('ci:project-deploy-elasticsearch') || GITHUB_PR_LABELS.includes('ci:project-deploy-observability') || diff --git a/.buildkite/scripts/steps/artifacts/docker_image.sh b/.buildkite/scripts/steps/artifacts/docker_image.sh index f6ed33bbce0d3..6e07cec0947ba 100755 --- a/.buildkite/scripts/steps/artifacts/docker_image.sh +++ b/.buildkite/scripts/steps/artifacts/docker_image.sh @@ -36,6 +36,7 @@ node scripts/build \ --docker-tag="$KIBANA_IMAGE_TAG" \ --skip-docker-ubuntu \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-cloud \ --skip-docker-contexts diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 15de3aa8614b8..6a7a95f8eaf10 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -42,6 +42,7 @@ else --docker-tag-qualifier="$GIT_COMMIT" \ --docker-push \ --skip-docker-ubi \ + --skip-docker-fips \ --skip-docker-ubuntu \ --skip-docker-serverless \ --skip-docker-contexts diff --git a/.buildkite/scripts/steps/demo_env/kibana.sh b/.buildkite/scripts/steps/demo_env/kibana.sh index 0483c038aaac4..e8dacdfb94e53 100755 --- a/.buildkite/scripts/steps/demo_env/kibana.sh +++ b/.buildkite/scripts/steps/demo_env/kibana.sh @@ -9,7 +9,7 @@ source "$(dirname "${0}")/config.sh" export KIBANA_IMAGE="gcr.io/elastic-kibana-184716/demo/kibana:$DEPLOYMENT_NAME-$(git rev-parse HEAD)" echo '--- Build Kibana' -node scripts/build --debug --docker-images --example-plugins --skip-docker-ubi --skip-docker-cloud --skip-docker-serverless --skip-docker-contexts +node scripts/build --debug --docker-images --example-plugins --skip-docker-ubi --skip-docker-fips --skip-docker-cloud --skip-docker-serverless --skip-docker-contexts echo '--- Build Docker image with example plugins' cd target/example_plugins diff --git a/.buildkite/scripts/steps/fips/build.sh b/.buildkite/scripts/steps/fips/build.sh new file mode 100644 index 0000000000000..dcd3cc3b0bb2f --- /dev/null +++ b/.buildkite/scripts/steps/fips/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +.buildkite/scripts/bootstrap.sh + +source .buildkite/scripts/common/util.sh +source .buildkite/scripts/steps/artifacts/env.sh + +echo "$KIBANA_DOCKER_PASSWORD" | docker login -u "$KIBANA_DOCKER_USERNAME" --password-stdin docker.elastic.co +mkdir -p target +download_artifact "kibana-$FULL_VERSION-linux-x86_64.tar.gz" ./target --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" + +echo "--- Build FIPS image" +node scripts/build \ + --skip-initialize \ + --skip-generic-folders \ + --skip-platform-folders \ + --skip-cdn-assets \ + --skip-archives \ + --docker-images \ + --docker-namespace="kibana-ci" \ + --docker-tag-qualifier="$BUILDKITE_COMMIT" \ + --docker-push \ + --skip-docker-ubi \ + --skip-docker-ubuntu \ + --skip-docker-cloud \ + --skip-docker-serverless \ + --skip-docker-contexts + +docker logout docker.elastic.co + +# Moving to `target/` first will keep `buildkite-agent` from including directories in the artifact name +cd "$KIBANA_DIR/target" +buildkite-agent artifact upload "./*docker-image*.tar.gz" diff --git a/.buildkite/scripts/steps/package_testing/build.sh b/.buildkite/scripts/steps/package_testing/build.sh index 892b8bf312d51..9cd03ac82ac0f 100755 --- a/.buildkite/scripts/steps/package_testing/build.sh +++ b/.buildkite/scripts/steps/package_testing/build.sh @@ -4,7 +4,7 @@ set -euo pipefail .buildkite/scripts/bootstrap.sh -node scripts/build --all-platforms --debug --skip-docker-cloud --skip-docker-serverless --skip-docker-ubi --skip-docker-contexts --skip-cdn-assets +node scripts/build --all-platforms --debug --skip-docker-cloud --skip-docker-serverless --skip-docker-ubi --skip-docker-fips --skip-docker-contexts --skip-cdn-assets DOCKER_FILE="kibana-$KIBANA_PKG_VERSION-SNAPSHOT-docker-image.tar.gz" diff --git a/dev_docs/tutorials/ci.mdx b/dev_docs/tutorials/ci.mdx index 2234143c32664..2c093169e8b3d 100644 --- a/dev_docs/tutorials/ci.mdx +++ b/dev_docs/tutorials/ci.mdx @@ -51,6 +51,10 @@ Build an archive that can be used to serve Kibana's static assets. Build cloud Docker images that can be used for testing deployments on Elastic Cloud. +#### `ci:build-docker-fips` + +Build Docker UBI x64 image with FIPS enabled. + #### `ci:build-os-packages` Build Docker images, and Debian and RPM packages. diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index e379ae02ceba3..1bb50cf3cd9c1 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -33,6 +33,7 @@ it('build default and oss dist for current platform, without packages, by defaul "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -72,6 +73,7 @@ it('builds packages if --all-platforms is passed', () => { "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, @@ -111,6 +113,7 @@ it('limits packages if --rpm passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -150,6 +153,7 @@ it('limits packages if --deb passed with --all-platforms', () => { "createDebPackage": true, "createDockerCloud": false, "createDockerContexts": true, + "createDockerFIPS": false, "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, @@ -190,6 +194,7 @@ it('limits packages if --docker passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, @@ -237,6 +242,7 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": false, "createDockerUbuntu": true, @@ -277,6 +283,7 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerFIPS": true, "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": false, @@ -305,3 +312,44 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => } `); }); + +it('limits packages if --all-platforms passed with --skip-docker-fips', () => { + expect(readCliArgs(['node', 'scripts/build', '--all-platforms', '--skip-docker-fips'])) + .toMatchInlineSnapshot(` + Object { + "buildOptions": Object { + "buildCanvasShareableRuntime": true, + "createArchives": true, + "createCdnAssets": true, + "createDebPackage": true, + "createDockerCloud": true, + "createDockerContexts": true, + "createDockerFIPS": false, + "createDockerServerless": true, + "createDockerUBI": true, + "createDockerUbuntu": true, + "createGenericFolders": true, + "createPlatformFolders": true, + "createRpmPackage": true, + "dockerContextUseLocalArtifact": null, + "dockerCrossCompile": false, + "dockerNamespace": null, + "dockerPush": false, + "dockerTag": null, + "dockerTagQualifier": null, + "downloadCloudDependencies": true, + "downloadFreshNode": true, + "eprRegistry": "snapshot", + "initialize": true, + "isRelease": false, + "targetAllPlatforms": true, + "versionQualifier": "", + "withExamplePlugins": false, + "withTestPlugins": false, + }, + "log": , + "showHelp": false, + "unknownFlags": Array [], + } + `); +}); diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index 8f16d5ce571c0..0996a8688ef22 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -33,6 +33,7 @@ export function readCliArgs(argv: string[]) { 'skip-docker-ubuntu', 'skip-docker-cloud', 'skip-docker-serverless', + 'skip-docker-fips', 'release', 'skip-node-download', 'skip-cloud-dependencies-download', @@ -143,6 +144,7 @@ export function readCliArgs(argv: string[]) { isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-serverless']), createDockerUBI: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubi']), createDockerContexts: !Boolean(flags['skip-docker-contexts']), + createDockerFIPS: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-fips']), targetAllPlatforms: Boolean(flags['all-platforms']), eprRegistry: flags['epr-registry'], buildCanvasShareableRuntime: !Boolean(flags['skip-canvas-shareable-runtime']), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index b324780e15672..ab9731e4ba112 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -34,6 +34,7 @@ export interface BuildOptions { createDockerCloud: boolean; createDockerServerless: boolean; createDockerContexts: boolean; + createDockerFIPS: boolean; versionQualifier: string | undefined; targetAllPlatforms: boolean; withExamplePlugins: boolean; @@ -163,6 +164,11 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreateDockerServerless); } + if (options.createDockerFIPS) { + // control w/ --docker-images or --skip-docker-fips or --skip-os-packages + await run(Tasks.CreateDockerFIPS); + } + if (options.createDockerContexts) { // control w/ --skip-docker-contexts await run(Tasks.CreateDockerContexts); diff --git a/src/dev/build/cli.ts b/src/dev/build/cli.ts index 09fd081136786..9a967e1cc85c9 100644 --- a/src/dev/build/cli.ts +++ b/src/dev/build/cli.ts @@ -45,6 +45,7 @@ if (showHelp) { --skip-canvas-shareable-runtime {dim Don't build the Canvas shareable runtime} --skip-docker-ubi {dim Don't build the docker ubi image} --skip-docker-ubuntu {dim Don't build the docker ubuntu image} + --skip-docker-fips {dim Don't build the docker fips image} --release {dim Produce a release-ready distributable} --version-qualifier {dim Suffix version with a qualifier} --skip-node-download {dim Reuse existing downloads of node.js} diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index 7cb9697364c75..e623dd86b9d6f 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -137,6 +137,20 @@ export const CreateDockerCloud: Task = { }, }; +export const CreateDockerFIPS: Task = { + description: 'Creating Docker FIPS image', + + async run(config, log, build) { + await runDockerGenerator(config, log, build, { + architecture: 'x64', + baseImage: 'ubi', + context: false, + image: true, + fips: true, + }); + }, +}; + export const CreateDockerContexts: Task = { description: 'Creating Docker build contexts', @@ -170,5 +184,11 @@ export const CreateDockerContexts: Task = { context: true, image: false, }); + await runDockerGenerator(config, log, build, { + baseImage: 'ubi', + context: true, + image: false, + fips: true, + }); }, }; diff --git a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts index 7df5b21bd562a..0dac00084df0e 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/bundle_dockerfiles.ts @@ -54,6 +54,12 @@ export async function bundleDockerFiles(config: Config, log: ToolingLog, scope: await write(resolve(dockerFilesBuildDir, template), output); } } + if (scope.fips) { + await copyAll( + resolve(scope.dockerBuildDir, 'openssl'), + resolve(dockerFilesBuildDir, 'openssl') + ); + } // Compress dockerfiles dir created inside // docker build dir as output it as a target diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf b/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf new file mode 100644 index 0000000000000..bd8fece6674d7 --- /dev/null +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/fips/openssl/nodejs.cnf @@ -0,0 +1,28 @@ +########################################################################## +## ## +## This OpenSSL config is only loaded when running Kibana in FIPS mode. ## +## ## +## See: ## +## https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md ## +## https://www.openssl.org/docs/man3.0/man7/fips_module.html ## +## ## +########################################################################## + +nodejs_conf = nodejs_init +.include /usr/local/ssl/fipsmodule.cnf + +[nodejs_init] +providers = provider_sect +alg_section = algorithm_sect + +[provider_sect] +default = default_sect +# The fips section name should match the section name inside the +# included fipsmodule.cnf. +fips = fips_sect + +[default_sect] +activate = 1 + +[algorithm_sect] +default_properties = fips=yes \ No newline at end of file diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index 16c48ad492187..cf2ddd34913b8 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -36,6 +36,7 @@ export async function runDockerGenerator( cloud?: boolean; serverless?: boolean; dockerBuildDate?: string; + fips?: boolean; } ) { let baseImageName = ''; @@ -47,6 +48,7 @@ export async function runDockerGenerator( if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; if (flags.serverless) imageFlavor += '-serverless'; + if (flags.fips) imageFlavor += '-fips'; // General docker var config const license = 'Elastic License'; @@ -111,6 +113,7 @@ export async function runDockerGenerator( architecture: flags.architecture, revision: config.getBuildSha(), publicArtifactSubdomain, + fips: flags.fips, }; type HostArchitectureToDocker = Record; @@ -148,6 +151,14 @@ export async function runDockerGenerator( dockerBuildDir ); + // Copy fips related resources + if (flags.fips) { + await copyAll( + config.resolveFromRepo('src/dev/build/tasks/os_packages/docker_generator/resources/fips'), + dockerBuildDir + ); + } + // Build docker image into the target folder // In order to do this we just call the file we // created from the templates/build_docker_sh.template.js diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index edd0aed87e281..7734c347edfaa 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -33,4 +33,5 @@ export interface TemplateContext { ironbank?: boolean; revision: string; architecture?: string; + fips?: boolean; } diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 25f4345ffbcc3..b02efe3f5b5b3 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -61,7 +61,7 @@ EXPOSE 5601 RUN for iter in {1..10}; do \ {{packageManager}} update --setopt=tsflags=nodocs -y && \ {{packageManager}} install --setopt=tsflags=nodocs -y \ - fontconfig freetype shadow-utils nss findutils && \ + fontconfig freetype shadow-utils nss findutils {{#fips}}perl make gcc tar {{/fips}}&& \ {{packageManager}} clean all && exit_code=0 && break || exit_code=$? && echo "{{packageManager}} error: retry $iter in 10s" && \ sleep 10; \ done; \ @@ -112,6 +112,37 @@ COPY --from=builder --chown=1000:0 /usr/share/kibana /usr/share/kibana COPY --from=builder --chown=0:0 /opt /opt {{/cloud}} WORKDIR /usr/share/kibana +{{#fips}} + +# OpenSSL requires specific versions that are FIPS certified. Further, the FIPS modules +# need to be compiled on the machine to pass its own self validation on startup. +# +# See: +# https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md +# https://www.openssl.org/docs/man3.0/man7/fips_module.html + +# Ideally we would handle this in the builder step, but make is installing over the OS version +# of OpenSSL and requires linking of many submodules. +RUN set -e ; \ + curl --retry 8 -S -L -O https://www.openssl.org/source/openssl-3.0.8.tar.gz ; \ + curl --retry 8 -S -L -O https://www.openssl.org/source/openssl-3.0.8.tar.gz.sha256 ; \ + echo "$(cat openssl-3.0.8.tar.gz.sha256) openssl-3.0.8.tar.gz" | sha256sum -c ; \ + tar -zxf openssl-3.0.8.tar.gz ; \ + rm -rf openssl-3.0.8.tar* ; \ + cd /usr/share/kibana/openssl-3.0.8 ; \ + ./Configure enable-fips ; \ + make -j $(nproc) ; \ + make install ; \ + ldconfig /usr/local/lib64/ ; \ + chown -R 1000:0 /usr/share/kibana/openssl-3.0.8 + +# Enable FIPS for Kibana only. In the future we can override OS wide with ENV OPENSSL_CONF +RUN echo -e '\n--enable-fips' >> config/node.options +RUN echo '--openssl-config=/usr/share/kibana/openssl-3.0.8/nodejs.cnf' >> config/node.options +COPY --chown=1000:0 openssl/nodejs.cnf /usr/share/kibana/openssl-3.0.8/nodejs.cnf +ENV OPENSSL_MODULES=/usr/local/lib64/ossl-modules + +{{/fips}} RUN ln -s /usr/share/kibana /opt/kibana {{! Please notify @elastic/kibana-security if you want to remove or change this environment variable. }} @@ -127,7 +158,7 @@ COPY --chown=1000:0 config/serverless.es.yml /usr/share/kibana/config/serverless COPY --chown=1000:0 config/serverless.oblt.yml /usr/share/kibana/config/serverless.oblt.yml COPY --chown=1000:0 config/serverless.security.yml /usr/share/kibana/config/serverless.security.yml # Supportability enhancement: enable capturing heap snapshots. See https://nodejs.org/api/cli.html#--heapsnapshot-signalsignal -RUN echo '\n--heapsnapshot-signal=SIGUSR2' >> config/node.options +RUN echo -e '\n--heapsnapshot-signal=SIGUSR2' >> config/node.options RUN echo '--diagnostic-dir=./data' >> config/node.options {{/serverless}} {{^opensslLegacyProvider}} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts index 57fc4d93a760a..dd35323808d51 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts @@ -19,7 +19,7 @@ function generator(options: TemplateContext) { packageManager: options.baseImage === 'ubi' ? 'microdnf' : 'apt-get', ubi: options.baseImage === 'ubi', ubuntu: options.baseImage === 'ubuntu', - opensslLegacyProvider: !(options.cloud || options.serverless), + opensslLegacyProvider: !(options.cloud || options.serverless || options.fips), ...options, }); } diff --git a/x-pack/plugins/profiling/server/lib/setup/README.md b/x-pack/plugins/profiling/server/lib/setup/README.md index b1eb7dc241075..dfab97d508c74 100644 --- a/x-pack/plugins/profiling/server/lib/setup/README.md +++ b/x-pack/plugins/profiling/server/lib/setup/README.md @@ -26,7 +26,7 @@ Build and push a Kibana image with the latest changes. Choose a unique identifier for the build, then: ``` -node scripts/build --docker-images --skip-docker-ubi --skip-docker-ubuntu +node scripts/build --docker-images --skip-docker-ubi --skip-docker-ubuntu --skip-docker-fips docker tag docker.elastic.co/kibana-ci/kibana-cloud:8.7.0-SNAPSHOT docker.elastic.co/observability-ci/kibana: docker push docker.elastic.co/observability-ci/kibana: ```