diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2266b84ef3560..936abc4efddfb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -173,49 +173,49 @@ variables: # To use images from datadog-agent-buildimages dev branches, set the corresponding # SUFFIX variable to _test_only DATADOG_AGENT_BUILDIMAGES_SUFFIX: "" - DATADOG_AGENT_BUILDIMAGES: v48372186-ff395e52 + DATADOG_AGENT_BUILDIMAGES: v48815877-9bfad02c DATADOG_AGENT_WINBUILDIMAGES_SUFFIX: "" - DATADOG_AGENT_WINBUILDIMAGES: v48372186-ff395e52 + DATADOG_AGENT_WINBUILDIMAGES: v48815877-9bfad02c DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX: "" - DATADOG_AGENT_ARMBUILDIMAGES: v48372186-ff395e52 + DATADOG_AGENT_ARMBUILDIMAGES: v48815877-9bfad02c DATADOG_AGENT_SYSPROBE_BUILDIMAGES_SUFFIX: "" - DATADOG_AGENT_SYSPROBE_BUILDIMAGES: v48372186-ff395e52 + DATADOG_AGENT_SYSPROBE_BUILDIMAGES: v48815877-9bfad02c DATADOG_AGENT_BTF_GEN_BUILDIMAGES_SUFFIX: "" - DATADOG_AGENT_BTF_GEN_BUILDIMAGES: v48372186-ff395e52 + DATADOG_AGENT_BTF_GEN_BUILDIMAGES: v48815877-9bfad02c # New images to enable different version per image - not used yet - CI_IMAGE_BTF_GEN: v48372186-ff395e52 + CI_IMAGE_BTF_GEN: v48815877-9bfad02c CI_IMAGE_BTF_GEN_SUFFIX: "" - CI_IMAGE_DEB_X64: v48372186-ff395e52 + CI_IMAGE_DEB_X64: v48815877-9bfad02c CI_IMAGE_DEB_X64_SUFFIX: "" - CI_IMAGE_DEB_ARM64: v48372186-ff395e52 + CI_IMAGE_DEB_ARM64: v48815877-9bfad02c CI_IMAGE_DEB_ARM64_SUFFIX: "" - CI_IMAGE_DEB_ARMHF: v48372186-ff395e52 + CI_IMAGE_DEB_ARMHF: v48815877-9bfad02c CI_IMAGE_DEB_ARMHF_SUFFIX: "" - CI_IMAGE_DD_AGENT_TESTING: v48372186-ff395e52 + CI_IMAGE_DD_AGENT_TESTING: v48815877-9bfad02c CI_IMAGE_DD_AGENT_TESTING_SUFFIX: "" - CI_IMAGE_DOCKER_X64: v48372186-ff395e52 + CI_IMAGE_DOCKER_X64: v48815877-9bfad02c CI_IMAGE_DOCKER_X64_SUFFIX: "" - CI_IMAGE_DOCKER_ARM64: v48372186-ff395e52 + CI_IMAGE_DOCKER_ARM64: v48815877-9bfad02c CI_IMAGE_DOCKER_ARM64_SUFFIX: "" - CI_IMAGE_GITLAB_AGENT_DEPLOY: v48372186-ff395e52 + CI_IMAGE_GITLAB_AGENT_DEPLOY: v48815877-9bfad02c CI_IMAGE_GITLAB_AGENT_DEPLOY_SUFFIX: "" - CI_IMAGE_LINUX_GLIBC_2_17_X64: v48372186-ff395e52 + CI_IMAGE_LINUX_GLIBC_2_17_X64: v48815877-9bfad02c CI_IMAGE_LINUX_GLIBC_2_17_X64_SUFFIX: "" - CI_IMAGE_LINUX_GLIBC_2_23_ARM64: v48372186-ff395e52 + CI_IMAGE_LINUX_GLIBC_2_23_ARM64: v48815877-9bfad02c CI_IMAGE_LINUX_GLIBC_2_23_ARM64_SUFFIX: "" - CI_IMAGE_SYSTEM_PROBE_X64: v48372186-ff395e52 + CI_IMAGE_SYSTEM_PROBE_X64: v48815877-9bfad02c CI_IMAGE_SYSTEM_PROBE_X64_SUFFIX: "" - CI_IMAGE_SYSTEM_PROBE_ARM64: v48372186-ff395e52 + CI_IMAGE_SYSTEM_PROBE_ARM64: v48815877-9bfad02c CI_IMAGE_SYSTEM_PROBE_ARM64_SUFFIX: "" - CI_IMAGE_RPM_X64: v48372186-ff395e52 + CI_IMAGE_RPM_X64: v48815877-9bfad02c CI_IMAGE_RPM_X64_SUFFIX: "" - CI_IMAGE_RPM_ARM64: v48372186-ff395e52 + CI_IMAGE_RPM_ARM64: v48815877-9bfad02c CI_IMAGE_RPM_ARM64_SUFFIX: "" - CI_IMAGE_RPM_ARMHF: v48372186-ff395e52 + CI_IMAGE_RPM_ARMHF: v48815877-9bfad02c CI_IMAGE_RPM_ARMHF_SUFFIX: "" - CI_IMAGE_WIN_1809_X64: v48372186-ff395e52 + CI_IMAGE_WIN_1809_X64: v48815877-9bfad02c CI_IMAGE_WIN_1809_X64_SUFFIX: "" - CI_IMAGE_WIN_LTSC2022_X64: v48372186-ff395e52 + CI_IMAGE_WIN_LTSC2022_X64: v48815877-9bfad02c CI_IMAGE_WIN_LTSC2022_X64_SUFFIX: "" DATADOG_AGENT_EMBEDDED_PATH: /opt/datadog-agent/embedded diff --git a/.gitlab/container_build/docker_linux.yml b/.gitlab/container_build/docker_linux.yml index 5f5c83c0dce67..773f91fd1ca3a 100644 --- a/.gitlab/container_build/docker_linux.yml +++ b/.gitlab/container_build/docker_linux.yml @@ -89,6 +89,33 @@ docker_build_agent7_arm64: TAG_SUFFIX: -7 BUILD_ARG: --target test --build-arg DD_AGENT_ARTIFACT=datadog-agent-7*-arm64.tar.xz +# build agent7 fips image +docker_build_fips_agent7: + extends: [.docker_build_job_definition_amd64, .docker_build_artifact] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: + - job: datadog-agent-7-x64-fips + variables: + IMAGE: registry.ddbuild.io/ci/datadog-agent/agent + BUILD_CONTEXT: Dockerfiles/agent + TAG_SUFFIX: -7-fips + BUILD_ARG: --target test --build-arg DD_AGENT_ARTIFACT=datadog-fips-agent-7*-amd64.tar.xz + +docker_build_fips_agent7_arm64: + extends: [.docker_build_job_definition_arm64, .docker_build_artifact] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: + - job: datadog-agent-7-arm64-fips + variables: + IMAGE: registry.ddbuild.io/ci/datadog-agent/agent + BUILD_CONTEXT: Dockerfiles/agent + TAG_SUFFIX: -7-fips + BUILD_ARG: --target test --build-arg DD_AGENT_ARTIFACT=datadog-fips-agent-7*-arm64.tar.xz + # build agent7 jmx image docker_build_agent7_jmx: extends: [.docker_build_job_definition_amd64, .docker_build_artifact] @@ -116,6 +143,32 @@ docker_build_agent7_jmx_arm64: TAG_SUFFIX: -7-jmx BUILD_ARG: --target test --build-arg WITH_JMX=true --build-arg DD_AGENT_ARTIFACT=datadog-agent-7*-arm64.tar.xz +docker_build_fips_agent7_jmx: + extends: [.docker_build_job_definition_amd64, .docker_build_artifact] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: + - job: datadog-agent-7-x64-fips + variables: + IMAGE: registry.ddbuild.io/ci/datadog-agent/agent + BUILD_CONTEXT: Dockerfiles/agent + TAG_SUFFIX: -7-fips-jmx + BUILD_ARG: --target test --build-arg DD_AGENT_ARTIFACT=datadog-fips-agent-7*-amd64.tar.xz + +docker_build_fips_agent7_arm64_jmx: + extends: [.docker_build_job_definition_arm64, .docker_build_artifact] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: + - job: datadog-agent-7-arm64-fips + variables: + IMAGE: registry.ddbuild.io/ci/datadog-agent/agent + BUILD_CONTEXT: Dockerfiles/agent + TAG_SUFFIX: -7-fips-jmx + BUILD_ARG: --target test --build-arg DD_AGENT_ARTIFACT=datadog-fips-agent-7*-arm64.tar.xz + # build agent7 UA image docker_build_ot_agent7: extends: [.docker_build_job_definition_amd64, .docker_build_artifact] diff --git a/.gitlab/deploy_packages/nix.yml b/.gitlab/deploy_packages/nix.yml index 8f16b27a38b55..314fcb4961fe7 100644 --- a/.gitlab/deploy_packages/nix.yml +++ b/.gitlab/deploy_packages/nix.yml @@ -14,6 +14,18 @@ deploy_packages_deb-arm64-7: variables: PACKAGE_ARCH: arm64 +deploy_packages_deb-x64-7-fips: + extends: .deploy_packages_deb-7 + needs: [ agent_deb-x64-a7-fips ] + variables: + PACKAGE_ARCH: amd64 + +deploy_packages_deb-arm64-7-fips: + extends: .deploy_packages_deb-7 + needs: [ agent_deb-arm64-a7-fips ] + variables: + PACKAGE_ARCH: arm64 + deploy_packages_heroku_deb-x64-7: extends: .deploy_packages_deb-7 needs: [ agent_heroku_deb-x64-a7 ] @@ -62,6 +74,18 @@ deploy_packages_rpm-arm64-7: variables: PACKAGE_ARCH: aarch64 +deploy_packages_rpm-x64-7-fips: + extends: .deploy_packages_rpm-7 + needs: [ agent_rpm-x64-a7-fips ] + variables: + PACKAGE_ARCH: x86_64 + +deploy_packages_rpm-arm64-7-fips: + extends: .deploy_packages_rpm-7 + needs: [ agent_rpm-arm64-a7-fips ] + variables: + PACKAGE_ARCH: aarch64 + deploy_packages_iot_rpm-x64-7: extends: .deploy_packages_rpm-7 needs: [ iot_agent_rpm-x64 ] @@ -98,6 +122,18 @@ deploy_packages_suse_rpm-arm64-7: variables: PACKAGE_ARCH: aarch64 +deploy_packages_suse_rpm-x64-7-fips: + extends: .deploy_packages_suse_rpm-7 + needs: [ agent_suse-x64-a7-fips ] + variables: + PACKAGE_ARCH: x86_64 + +deploy_packages_suse_rpm-arm64-7-fips: + extends: .deploy_packages_suse_rpm-7 + needs: [ agent_suse-arm64-a7-fips ] + variables: + PACKAGE_ARCH: aarch64 + deploy_packages_iot_suse_rpm-x64-7: extends: .deploy_packages_suse_rpm-7 needs: [ iot_agent_suse-x64 ] diff --git a/.gitlab/dev_container_deploy/docker_linux.yml b/.gitlab/dev_container_deploy/docker_linux.yml index 57178929aff04..5b61ffc403a2b 100644 --- a/.gitlab/dev_container_deploy/docker_linux.yml +++ b/.gitlab/dev_container_deploy/docker_linux.yml @@ -32,6 +32,24 @@ dev_branch_multiarch-a7: - IMG_SOURCES: ${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-jmx-amd64,${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-jmx-arm64 IMG_DESTINATIONS: agent-dev:${CI_COMMIT_REF_SLUG}-py3-jmx +dev_branch_multiarch-fips: + extends: .docker_publish_job_definition + stage: dev_container_deploy + rules: !reference [.manual] + needs: + - docker_build_fips_agent7 + - docker_build_fips_agent7_arm64 + - docker_build_fips_agent7_jmx + - docker_build_fips_agent7_arm64_jmx + variables: + IMG_REGISTRIES: dev + parallel: + matrix: + - IMG_SOURCES: ${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-fips-amd64,${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-fips-arm64 + IMG_DESTINATIONS: agent-dev:${CI_COMMIT_REF_SLUG}-fips + - IMG_SOURCES: ${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-fips-jmx-amd64,${SRC_AGENT}:v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-fips-jmx-arm64 + IMG_DESTINATIONS: agent-dev:${CI_COMMIT_REF_SLUG}-fips-jmx + dev_branch_multiarch-dogstatsd: extends: .docker_publish_job_definition stage: dev_container_deploy diff --git a/.gitlab/internal_image_deploy/internal_image_deploy.yml b/.gitlab/internal_image_deploy/internal_image_deploy.yml index 82ffc58bc25f8..7c155413ced58 100644 --- a/.gitlab/internal_image_deploy/internal_image_deploy.yml +++ b/.gitlab/internal_image_deploy/internal_image_deploy.yml @@ -47,6 +47,51 @@ docker_trigger_internal: --variable TARGET_ENV --variable DYNAMIC_BUILD_RENDER_TARGET_FORWARD_PARAMETERS" +docker_trigger_internal-fips: + stage: internal_image_deploy + rules: !reference [.on_deploy_internal_or_manual] + needs: + - job: docker_build_fips_agent7 + artifacts: false + - job: docker_build_fips_agent7_arm64 + artifacts: false + image: registry.ddbuild.io/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + variables: + DYNAMIC_BUILD_RENDER_RULES: agent-build-only # fake rule to not trigger the ones in the images repo + IMAGE_VERSION: tmpl-v11 + IMAGE_NAME: datadog-agent + RELEASE_TAG: ${CI_COMMIT_REF_SLUG}-fips + BUILD_TAG: ${CI_COMMIT_REF_SLUG}-fips + TMPL_SRC_IMAGE: v${CI_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-7-fips + TMPL_SRC_REPO: ci/datadog-agent/agent + RELEASE_STAGING: "true" + script: + - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN write_api) || exit $?; export GITLAB_TOKEN + - if [ "$BUCKET_BRANCH" = "beta" ] || [ "$BUCKET_BRANCH" = "stable" ]; then TMPL_SRC_REPO="${TMPL_SRC_REPO}-release"; fi + - | + if [ "$BUCKET_BRANCH" = "nightly" ]; then + RELEASE_TAG="${RELEASE_TAG}-${CI_COMMIT_SHORT_SHA}" + TMPL_SRC_REPO="${TMPL_SRC_REPO}-nightly" + fi + - if [ "$BUCKET_BRANCH" = "dev" ]; then RELEASE_TAG="dev-${RELEASE_TAG}-${CI_COMMIT_SHORT_SHA}"; fi + - "inv pipeline.trigger-child-pipeline --project-name DataDog/images --git-ref master --timeout 3600 + --variable IMAGE_VERSION + --variable IMAGE_NAME + --variable RELEASE_TAG + --variable BUILD_TAG + --variable TMPL_SRC_IMAGE + --variable TMPL_SRC_REPO + --variable RELEASE_STAGING + --variable RELEASE_PROD + --variable DYNAMIC_BUILD_RENDER_RULES + --variable APPS + --variable BAZEL_TARGET + --variable DDR + --variable DDR_WORKFLOW_ID + --variable TARGET_ENV + --variable DYNAMIC_BUILD_RENDER_TARGET_FORWARD_PARAMETERS" + docker_trigger_internal-ot: stage: internal_image_deploy rules: !reference [.on_deploy_internal_or_manual] diff --git a/.gitlab/package_build/linux.yml b/.gitlab/package_build/linux.yml index d129676f3bc3c..f8d785d4ef62d 100644 --- a/.gitlab/package_build/linux.yml +++ b/.gitlab/package_build/linux.yml @@ -67,6 +67,12 @@ before_script: - export RELEASE_VERSION=$RELEASE_VERSION_7 +.agent_fips_build: + variables: + FLAVOR: fips + before_script: + - export RELEASE_VERSION=$RELEASE_VERSION_7 + # build Agent 7 binaries for x86_64 datadog-agent-7-x64: extends: [.agent_build_common, .agent_build_x86, .agent_7_build] @@ -83,6 +89,14 @@ datadog-ot-agent-7-x64: datadog-ot-agent-7-arm64: extends: [.agent_build_common, .agent_build_arm64, .ot_agent_7_build] +# build Agent 7 binaries for x86_64 with FIPS +datadog-agent-7-x64-fips: + extends: [.agent_build_common, .agent_build_x86, .agent_fips_build] + +# build Agent 7 binaries for arm64 with FIPS +datadog-agent-7-arm64-fips: + extends: [.agent_build_common, .agent_build_arm64, .agent_fips_build] + .iot-agent-common: extends: .agent_build_common needs: ["go_mod_tidy_check", "go_deps"] diff --git a/.gitlab/packaging/deb.yml b/.gitlab/packaging/deb.yml index a133b70d749a1..277897c6060f6 100644 --- a/.gitlab/packaging/deb.yml +++ b/.gitlab/packaging/deb.yml @@ -56,6 +56,26 @@ agent_deb-arm64-a7: variables: DD_PROJECT: "agent" +agent_deb-x64-a7-fips: + extends: [.package_deb_common, .package_deb_x86, .package_deb_agent_7] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: ["datadog-agent-7-x64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: "agent" + +agent_deb-arm64-a7-fips: + extends: [.package_deb_common, .package_deb_arm64, .package_deb_agent_7] + rules: + - !reference [.except_mergequeue] + - when: on_success + needs: ["datadog-agent-7-arm64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: "agent" + .package_ot_deb_common: extends: [.package_deb_common] script: diff --git a/.gitlab/packaging/rpm.yml b/.gitlab/packaging/rpm.yml index 9444f7062ad89..883f3910cc086 100644 --- a/.gitlab/packaging/rpm.yml +++ b/.gitlab/packaging/rpm.yml @@ -92,6 +92,35 @@ installer_rpm-amd64: # explicitly disable the check PACKAGE_REQUIRED_FILES_LIST: "" +agent_rpm-x64-a7-fips: + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_x86] + tags: ["arch:amd64"] + needs: ["datadog-agent-7-x64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: agent + +agent_rpm-arm64-a7-fips: + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] + needs: ["datadog-agent-7-arm64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: agent + +agent_suse-x64-a7-fips: + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_x86] + needs: ["datadog-agent-7-x64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: agent + +agent_suse-arm64-a7-fips: + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] + needs: ["datadog-agent-7-arm64-fips"] + variables: + OMNIBUS_EXTRA_ARGS: "--flavor fips" + DD_PROJECT: agent + installer_rpm-arm64: extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] needs: ["installer-arm64"] diff --git a/Dockerfiles/agent/Dockerfile b/Dockerfiles/agent/Dockerfile index bf99bef8e05a1..6c8cb42eedccf 100644 --- a/Dockerfiles/agent/Dockerfile +++ b/Dockerfiles/agent/Dockerfile @@ -180,6 +180,13 @@ RUN tar xzf s6.tgz -C / --exclude="./bin" \ # * https://datadoghq.atlassian.net/wiki/spaces/TS/pages/2615709591/Why+the+containerized+Agent+runs+as+root#Agent-user RUN [ "$(getent passwd dd-agent | cut -d: -f 3)" -eq 100 ] +# Enable FIPS if needed +RUN if [ -x /opt/datadog-agent/embedded/bin/fipsinstall.sh ]; then \ + /opt/datadog-agent/embedded/bin/fipsinstall.sh; \ +fi +# This is used by MSGO to enable FIPS mode so it won't affect the non-FIPS image +ENV GOFIPS=1 + # Override the exit script by ours to fix --pid=host operations RUN mv /etc/s6/init/init-stage3 /etc/s6/init/init-stage3-original COPY init-stage3 /etc/s6/init/init-stage3 diff --git a/omnibus/config/projects/agent.rb b/omnibus/config/projects/agent.rb index 7678dd69eac28..bd24283687dbf 100644 --- a/omnibus/config/projects/agent.rb +++ b/omnibus/config/projects/agent.rb @@ -237,6 +237,9 @@ if linux_target? dependency 'datadog-security-agent-policies' + if fips_mode? + dependency 'openssl-fips-provider' + end end # Include traps db file in snmp.d/traps_db/ @@ -326,10 +329,22 @@ GO_BINARIES << "#{install_dir}\\bin\\agent\\security-agent.exe" end + raise_if_fips_symbol_not_found = Proc.new { |symbols| + count = symbols.scan("github.com/microsoft/go-crypto-winnative").count() + if count == 0 + raise FIPSSymbolsNotFound.new("Expected to find symbol 'github.com/microsoft/go-crypto-winnative' but no symbol was found.") + end + } + GO_BINARIES.each do |bin| # Check the exported symbols from the binary inspect_binary(bin, &raise_if_forbidden_symbol_found) + if fips_mode? + # Check that CNG symbols are present + inspect_binary(bin, &raise_if_fips_symbol_not_found) + end + # strip the binary of debug symbols windows_symbol_stripping_file bin end diff --git a/omnibus/config/software/datadog-agent.rb b/omnibus/config/software/datadog-agent.rb index 5fbbd18138d0a..bfd08c4dd9a20 100644 --- a/omnibus/config/software/datadog-agent.rb +++ b/omnibus/config/software/datadog-agent.rb @@ -33,7 +33,9 @@ # set GOPATH on the omnibus source dir for this software gopath = Pathname.new(project_dir) + '../../../..' + msgoroot = "/usr/local/msgo" flavor_arg = ENV['AGENT_FLAVOR'] + fips_args = fips_mode? ? "--fips-mode" : "" if windows_target? env = { 'GOPATH' => gopath.to_path, @@ -58,6 +60,12 @@ # include embedded path (mostly for `pkg-config` binary) env = with_standard_compiler_flags(with_embedded_path(env)) + + # Use msgo toolchain when fips mode is enabled + if fips_mode? && !windows_target? + env["GOROOT"] = msgoroot + env["PATH"] = "#{msgoroot}/bin:#{env['PATH']}" + end default_install_dir = "/opt/datadog-agent" if Omnibus::Config.host_distribution == "ociru" default_install_dir = "#{install_dir}" @@ -139,7 +147,7 @@ if windows_target? command "invoke -e system-probe.build", env: env elsif linux_target? - command "invoke -e system-probe.build-sysprobe-binary --install-path=#{install_dir}", env: env + command "invoke -e system-probe.build-sysprobe-binary #{fips_args} --install-path=#{install_dir}", env: env end if windows_target? @@ -160,7 +168,7 @@ # Security agent secagent_support = (not heroku_target?) and (not windows_target? or (ENV['WINDOWS_DDPROCMON_DRIVER'] and not ENV['WINDOWS_DDPROCMON_DRIVER'].empty?)) if secagent_support - command "invoke -e security-agent.build --install-path=#{install_dir} --major-version #{major_version_arg}", :env => env + command "invoke -e security-agent.build #{fips_args} --install-path=#{install_dir} --major-version #{major_version_arg}", :env => env if windows_target? copy 'bin/security-agent/security-agent.exe', "#{install_dir}/bin/agent" else @@ -224,6 +232,37 @@ delete "#{install_dir}/uselessfile" end + # TODO: move this to omnibus-ruby::health-check.rb + # check that linux binaries contains OpenSSL symbols when building to support FIPS + if fips_mode? && linux_target? + # Put the ruby code in a block to prevent omnibus from running it directly but rather at build step with the rest of the code above. + # If not in a block, it will search for binaries that have not been built yet. + block do + LINUX_BINARIES = [ + "#{install_dir}/bin/agent/agent", + "#{install_dir}/embedded/bin/trace-agent", + "#{install_dir}/embedded/bin/process-agent", + "#{install_dir}/embedded/bin/security-agent", + "#{install_dir}/embedded/bin/system-probe", + ] + + symbol = "_Cfunc_go_openssl" + check_block = Proc.new { |binary, symbols| + count = symbols.scan(symbol).count + if count > 0 + log.info(log_key) { "Symbol '#{symbol}' found #{count} times in binary '#{binary}'." } + else + raise FIPSSymbolsNotFound.new("Expected to find '#{symbol}' symbol in #{binary} but did not") + end + }.curry + + LINUX_BINARIES.each do |bin| + partially_applied_check = check_block.call(bin) + GoSymbolsInspector.new(bin, &partially_applied_check).inspect() + end + end + end + python_scripts_dir = "#{project_dir}/omnibus/python-scripts" mkdir "#{install_dir}/python-scripts" copy "#{python_scripts_dir}/*", "#{install_dir}/python-scripts" diff --git a/omnibus/config/software/openssl-fips-provider.rb b/omnibus/config/software/openssl-fips-provider.rb new file mode 100644 index 0000000000000..776d7e81531b3 --- /dev/null +++ b/omnibus/config/software/openssl-fips-provider.rb @@ -0,0 +1,47 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com). +# Copyright 2016-present Datadog, Inc. + +# Embedded OpenSSL to meet FIPS requirements. It comes in two parts: +# 1. The FIPS module itself (this software definition). It must use a FIPS-validated version +# and follow the build steps outlined in the OpenSSL FIPS Security Policy. +# 2. The OpenSSL library, which can be any 3.0.x version. This library will use the FIPS provider. + +name "openssl-fips-provider" +default_version "0.0.1" + +OPENSSL_FIPS_MODULE_VERSION="3.0.9" +OPENSSL_FIPS_MODULE_FILENAME="openssl-#{OPENSSL_FIPS_MODULE_VERSION}.tar.gz" +OPENSSL_FIPS_MODULE_SHA256_SUM="eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90" + +source url: "https://www.openssl.org/source/#{OPENSSL_FIPS_MODULE_FILENAME}", + sha256: "#{OPENSSL_FIPS_MODULE_SHA256_SUM}", + extract: :seven_zip + +relative_path "openssl-#{OPENSSL_FIPS_MODULE_VERSION}" + +build do + # Exact build steps from security policy: + # https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4282.pdf + # + # ---------------- DO NOT MODIFY LINES BELOW HERE ---------------- + command "./Configure enable-fips" + + command "make" + command "make install" + # ---------------- DO NOT MODIFY LINES ABOVE HERE ---------------- + + mkdir "#{install_dir}/embedded/ssl" + mkdir "#{install_dir}/embedded/lib/ossl-modules" + copy "/usr/local/lib*/ossl-modules/fips.so", "#{install_dir}/embedded/lib/ossl-modules/fips.so" + + erb source: "openssl.cnf.erb", + dest: "#{install_dir}/embedded/ssl/openssl.cnf.tmp", + mode: 0644, + vars: { install_dir: install_dir } + erb source: "fipsinstall.sh.erb", + dest: "#{install_dir}/embedded/bin/fipsinstall.sh", + mode: 0755, + vars: { install_dir: install_dir } +end diff --git a/omnibus/config/templates/openssl-fips-provider/fipsinstall.sh.erb b/omnibus/config/templates/openssl-fips-provider/fipsinstall.sh.erb new file mode 100644 index 0000000000000..a789db7c20df4 --- /dev/null +++ b/omnibus/config/templates/openssl-fips-provider/fipsinstall.sh.erb @@ -0,0 +1,29 @@ +#!/bin/bash + +# The OpenSSL security policy states: +# "The Module shall have the self-tests run, and the Module config file output generated on each +# platform where it is intended to be used. The Module config file output data shall not be copied from +# one machine to another." +# This script aims to run self-tests and generate `fipsmodule.cnf.` +# Because the provided `openssl.cnf` references to `fipsmodule.cnf` which is not yet created, we first create it +# as `openssl.cnf.tmp` and then move it to its final name `openssl.cnf` when `fipsmodule.cnf` has been created + +set -euo pipefail + +INSTALL_DIR="<%= install_dir %>/embedded" + +FIPS_MODULE_PATH="${INSTALL_DIR}/ssl/fipsmodule.cnf" +OPENSSL_CONF_PATH="${INSTALL_DIR}/ssl/openssl.cnf" + +FIPS_SO_PATH="${INSTALL_DIR}/lib/ossl-modules/fips.so" +OPENSSL_BIN="${INSTALL_DIR}/bin/openssl" + + +if [ ! -f "${FIPS_MODULE_PATH}" ]; then + "${OPENSSL_BIN}" fipsinstall -module "${FIPS_SO_PATH}" -out "${FIPS_MODULE_PATH}" + mv "${OPENSSL_CONF_PATH}.tmp" "${OPENSSL_CONF_PATH}" +fi + +if ! "${OPENSSL_BIN}" fipsinstall -module "${FIPS_SO_PATH}" -in "${FIPS_MODULE_PATH}" -verify; then + echo "openssl fipsinstall: verification of FIPS compliance failed. $INSTALL_DIR/fipsmodule.cnf was corrupted or the installation failed." +fi diff --git a/omnibus/config/templates/openssl-fips-provider/openssl.cnf.erb b/omnibus/config/templates/openssl-fips-provider/openssl.cnf.erb new file mode 100644 index 0000000000000..1304d1021b08d --- /dev/null +++ b/omnibus/config/templates/openssl-fips-provider/openssl.cnf.erb @@ -0,0 +1,14 @@ +config_diagnostics = 1 +openssl_conf = openssl_init + +.include <%= install_dir %>/embedded/ssl/fipsmodule.cnf + +[openssl_init] +providers = provider_sect + +[provider_sect] +fips = fips_sect +base = base_sect + +[base_sect] +activate = 1 diff --git a/omnibus/lib/ostools.rb b/omnibus/lib/ostools.rb index 13d8d26e06e67..6ec8612585c37 100644 --- a/omnibus/lib/ostools.rb +++ b/omnibus/lib/ostools.rb @@ -63,3 +63,6 @@ def os end end +def fips_mode?() + return ENV['AGENT_FLAVOR'] == "fips" && (linux_target? || windows_target?) +end \ No newline at end of file diff --git a/omnibus/lib/symbols_inspectors.rb b/omnibus/lib/symbols_inspectors.rb index 31a56c0171a9d..797f239c0dc12 100644 --- a/omnibus/lib/symbols_inspectors.rb +++ b/omnibus/lib/symbols_inspectors.rb @@ -5,6 +5,10 @@ class ForbiddenSymbolsFoundError < StandardError end +class FIPSSymbolsNotFound < StandardError + +end + # Helper class to locate `dumpbin.exe` on Windows class Dumpbin include Singleton @@ -76,4 +80,4 @@ def inspect() log.info(self.class.name) { "Inspecting binary #{@binary}" } @block.call(`go tool nm #{@binary}`) end -end \ No newline at end of file +end diff --git a/omnibus/package-scripts/agent-deb/postinst b/omnibus/package-scripts/agent-deb/postinst index f0d2f12e78ad1..09a1c4a0d6d41 100755 --- a/omnibus/package-scripts/agent-deb/postinst +++ b/omnibus/package-scripts/agent-deb/postinst @@ -65,7 +65,11 @@ install_method: echo "$install_info_content" > $CONFIG_DIR/install_info fi -set +e +if [ -x ${INSTALL_DIR}/embedded/bin/fipsinstall.sh ]; then + ${INSTALL_DIR}/embedded/bin/fipsinstall.sh +fi + +set +e generate_install_id() { # Try generating a UUID based on /proc/sys/kernel/random/uuid diff --git a/omnibus/package-scripts/agent-deb/prerm b/omnibus/package-scripts/agent-deb/prerm index 19c8714ec2cb9..67c916c335da5 100755 --- a/omnibus/package-scripts/agent-deb/prerm +++ b/omnibus/package-scripts/agent-deb/prerm @@ -167,6 +167,13 @@ remove_persist_integration_files() fi } +remove_fips_module() +{ + # We explicitly remove the ssl directory because files within this folder are generated via a script + # outside of package installation (deb package only removes files initially present in the package). + rm -rf "${INSTALL_DIR}/embedded/ssl/fipsmodule.cnf" || true +} + case "$1" in #this can't be merged with the later case block because running 'remove_custom_integrations' would defeat the persisting integrations feature upgrade) # We're upgrading. @@ -189,6 +196,7 @@ case "$1" in remove_version_history remove_sysprobe_secagent_files remove_remote_config_db + remove_fips_module remove_persist_integration_files ;; upgrade) diff --git a/omnibus/package-scripts/agent-rpm/posttrans b/omnibus/package-scripts/agent-rpm/posttrans index a560a4626b45d..7206123b61636 100755 --- a/omnibus/package-scripts/agent-rpm/posttrans +++ b/omnibus/package-scripts/agent-rpm/posttrans @@ -110,6 +110,11 @@ install_method: echo "$install_info_content" > $CONFIG_DIR/install_info fi +if [ -x ${INSTALL_DIR}/embedded/bin/fipsinstall.sh ]; then + ${INSTALL_DIR}/embedded/bin/fipsinstall.sh +fi + + set +e generate_install_id() { diff --git a/omnibus/package-scripts/agent-rpm/prerm b/omnibus/package-scripts/agent-rpm/prerm index 9568f9b55f5ee..df2a03b266047 100755 --- a/omnibus/package-scripts/agent-rpm/prerm +++ b/omnibus/package-scripts/agent-rpm/prerm @@ -151,6 +151,13 @@ remove_persist_integration_files() fi } +remove_fips_module() +{ + # We explicitly remove the ssl directory because files within this folder are generated via a script + # outside of package installation (rpm package only removes files initially present in the package). + rm -rf "${INSTALL_DIR}/embedded/ssl/fipsmodule.cnf" || true +} + case "$*" in 1) # We're upgrading. @@ -173,6 +180,7 @@ case "$*" in remove_version_history remove_sysprobe_secagent_files remove_remote_config_db + remove_fips_module remove_persist_integration_files ;; 1) diff --git a/tasks/agent.py b/tasks/agent.py index 3399e4d7c4a58..50d88463e274e 100644 --- a/tasks/agent.py +++ b/tasks/agent.py @@ -14,7 +14,7 @@ from invoke import task from invoke.exceptions import Exit -from tasks.build_tags import filter_incompatible_tags, get_build_tags, get_default_build_tags +from tasks.build_tags import add_fips_tags, filter_incompatible_tags, get_build_tags, get_default_build_tags from tasks.devcontainer import run_on_devcontainer from tasks.flavor import AgentFlavor from tasks.libs.common.utils import ( @@ -150,6 +150,7 @@ def build( if flavor.is_ot(): # for agent build purposes the UA agent is just like base flavor = AgentFlavor.base + fips_mode = flavor.is_fips() if not exclude_rtloader and not flavor.is_iot(): # If embedded_path is set, we should give it to rtloader as it should install the headers/libs @@ -193,6 +194,7 @@ def build( exclude_tags = [] if build_exclude is None else build_exclude.split(",") build_tags = get_build_tags(include_tags, exclude_tags) + build_tags = add_fips_tags(build_tags, fips_mode) cmd = "go build -mod={go_mod} {race_opt} {build_type} -tags \"{go_build_tags}\" " diff --git a/tasks/build_tags.py b/tasks/build_tags.py index 593219ef96b56..caa73601e65b6 100644 --- a/tasks/build_tags.py +++ b/tasks/build_tags.py @@ -105,6 +105,8 @@ } ) +FIPS_AGENT_TAGS = AGENT_TAGS.union({"goexperiment.systemcrypto"}) + # CLUSTER_AGENT_TAGS lists the tags needed when building the cluster-agent CLUSTER_AGENT_TAGS = {"clusterchecks", "datadog.no_waf", "kubeapiserver", "orchestrator", "zlib", "zstd", "ec2", "gce"} @@ -235,6 +237,11 @@ "lint": DOGSTATSD_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), "unit-tests": DOGSTATSD_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), }, + AgentFlavor.fips: { + "agent": FIPS_AGENT_TAGS, + "lint": FIPS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), + "unit-tests": FIPS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), + }, } @@ -414,3 +421,9 @@ def compute_config_build_tags(targets="all", build_include=None, build_exclude=N build_exclude = [] if build_exclude is None else build_exclude.split(",") use_tags = get_build_tags(build_include, build_exclude) return use_tags + + +def add_fips_tags(tags: list[str], fips_mode: bool) -> list[str]: + if fips_mode: + tags.append("goexperiment.systemcrypto") + return tags diff --git a/tasks/flavor.py b/tasks/flavor.py index 8b38d93b00181..d935211441053 100644 --- a/tasks/flavor.py +++ b/tasks/flavor.py @@ -8,9 +8,13 @@ class AgentFlavor(enum.Enum): heroku = 3 dogstatsd = 4 ot = 5 + fips = 6 def is_iot(self): return self == type(self).iot def is_ot(self): return self == type(self).ot + + def is_fips(self): + return self == type(self).fips diff --git a/tasks/omnibus.py b/tasks/omnibus.py index 10cbc9db9b899..649992fdcfb44 100644 --- a/tasks/omnibus.py +++ b/tasks/omnibus.py @@ -91,6 +91,7 @@ def get_omnibus_env( flavor=AgentFlavor.base, pip_config_file="pip.conf", custom_config_dir=None, + fips_mode=False, ): env = load_release_versions(ctx, release_version) @@ -133,6 +134,9 @@ def get_omnibus_env( if custom_config_dir: env["OUTPUT_CONFIG_DIR"] = custom_config_dir + if fips_mode: + env['FIPS_MODE'] = 'true' + # We need to override the workers variable in omnibus build when running on Kubernetes runners, # otherwise, ohai detect the number of CPU on the host and run the make jobs with all the CPU. kubernetes_cpu_request = os.environ.get('KUBERNETES_CPU_REQUEST') @@ -187,6 +191,7 @@ def build( """ flavor = AgentFlavor[flavor] + fips_mode = flavor.is_fips() durations = {} if not skip_deps: with timed(quiet=True) as durations['Deps']: @@ -211,6 +216,7 @@ def build( flavor=flavor, pip_config_file=pip_config_file, custom_config_dir=config_directory, + fips_mode=fips_mode, ) if not target_project: diff --git a/tasks/process_agent.py b/tasks/process_agent.py index 6c3481d9c5213..d1020f3e88899 100644 --- a/tasks/process_agent.py +++ b/tasks/process_agent.py @@ -6,7 +6,7 @@ from invoke import task from invoke.exceptions import Exit -from tasks.build_tags import filter_incompatible_tags, get_build_tags, get_default_build_tags +from tasks.build_tags import add_fips_tags, filter_incompatible_tags, get_build_tags, get_default_build_tags from tasks.flavor import AgentFlavor from tasks.libs.common.utils import REPO_PATH, bin_name, get_build_flags from tasks.system_probe import copy_ebpf_and_related_files @@ -35,6 +35,7 @@ def build( flavor = AgentFlavor[flavor] if flavor.is_ot(): flavor = AgentFlavor.base + fips_mode = flavor.is_fips() ldflags, gcflags, env = get_build_flags( ctx, @@ -67,6 +68,7 @@ def build( build_exclude = [] if build_exclude is None else build_exclude.split(",") build_tags = get_build_tags(build_include, build_exclude) + build_tags = add_fips_tags(build_tags, fips_mode) if os.path.exists(BIN_PATH): os.remove(BIN_PATH) diff --git a/tasks/security_agent.py b/tasks/security_agent.py index 3d0dc7e752d95..f591473c2217e 100644 --- a/tasks/security_agent.py +++ b/tasks/security_agent.py @@ -14,7 +14,7 @@ from invoke.tasks import task from tasks.agent import generate_config -from tasks.build_tags import get_default_build_tags +from tasks.build_tags import add_fips_tags, get_default_build_tags from tasks.go import run_golangci_lint from tasks.libs.build.ninja import NinjaWriter from tasks.libs.common.git import get_commit_sha, get_current_branch @@ -58,6 +58,7 @@ def build( go_mod="mod", skip_assets=False, static=False, + fips_mode=False, ): """ Build the security agent @@ -88,6 +89,7 @@ def build( ldflags += ' '.join([f"-X '{main + key}={value}'" for key, value in ld_vars.items()]) build_tags += get_default_build_tags(build="security-agent") + build_tags = add_fips_tags(build_tags, fips_mode) if os.path.exists(BIN_PATH): os.remove(BIN_PATH) diff --git a/tasks/system_probe.py b/tasks/system_probe.py index 2517cc71a2edd..5ddffee1496c8 100644 --- a/tasks/system_probe.py +++ b/tasks/system_probe.py @@ -19,7 +19,7 @@ from invoke.exceptions import Exit from invoke.tasks import task -from tasks.build_tags import UNIT_TEST_TAGS, get_default_build_tags +from tasks.build_tags import UNIT_TEST_TAGS, add_fips_tags, get_default_build_tags from tasks.libs.build.ninja import NinjaWriter from tasks.libs.common.color import color_message from tasks.libs.common.git import get_commit_sha @@ -735,6 +735,7 @@ def build_sysprobe_binary( install_path=None, bundle_ebpf=False, strip_binary=False, + fips_mode=False, static=False, ) -> None: arch_obj = Arch.from_str(arch) @@ -748,6 +749,7 @@ def build_sysprobe_binary( ) build_tags = get_default_build_tags(build="system-probe") + build_tags = add_fips_tags(build_tags, fips_mode) if bundle_ebpf: build_tags.append(BUNDLE_TAG) if strip_binary: diff --git a/tasks/trace_agent.py b/tasks/trace_agent.py index 812116eeb3c32..a9503e77c3453 100644 --- a/tasks/trace_agent.py +++ b/tasks/trace_agent.py @@ -3,7 +3,7 @@ from invoke import Exit, task -from tasks.build_tags import filter_incompatible_tags, get_build_tags, get_default_build_tags +from tasks.build_tags import add_fips_tags, filter_incompatible_tags, get_build_tags, get_default_build_tags from tasks.flavor import AgentFlavor from tasks.libs.common.utils import REPO_PATH, bin_name, get_build_flags from tasks.windows_resources import build_messagetable, build_rc, versioninfo_vars @@ -30,6 +30,7 @@ def build( flavor = AgentFlavor[flavor] if flavor.is_ot(): flavor = AgentFlavor.base + fips_mode = flavor.is_fips() ldflags, gcflags, env = get_build_flags( ctx, @@ -59,6 +60,7 @@ def build( build_exclude = [] if build_exclude is None else build_exclude.split(",") build_tags = get_build_tags(build_include, build_exclude) + build_tags = add_fips_tags(build_tags, fips_mode) race_opt = "-race" if race else "" build_type = "-a" if rebuild else ""