From 23d2930f9c26beee2c6b136879d8d1d029ebf9bc Mon Sep 17 00:00:00 2001 From: David Kirov Date: Mon, 23 Dec 2024 18:31:16 +0200 Subject: [PATCH] Add integration tests and experimental workflow --- .github/workflows/experimental.yml | 222 +++++++++++++++++++++++++ datadog_checks_base/tests/test_fips.py | 68 ++++++++ 2 files changed, 290 insertions(+) create mode 100644 .github/workflows/experimental.yml create mode 100644 datadog_checks_base/tests/test_fips.py diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml new file mode 100644 index 0000000000000..b5bd0ed0b81ca --- /dev/null +++ b/.github/workflows/experimental.yml @@ -0,0 +1,222 @@ +name: Test FIPS experimental + +on: + workflow_dispatch: + inputs: + zip_url: + required: true + type: string + default: 'https://agent-ints-python-build-sandbox.s3.eu-north-1.amazonaws.com/python-windows-combined-v3.12.6-openssl-3.0.15-openssl-3.0.9-amd64.zip' + pull_request: + path: + - datadog_checks_base/datadog_checks/** + schedule: + - cron: '0 0,8,16 * * *' + +defaults: + run: + shell: bash + +jobs: + test: + strategy: + matrix: + include: + - platform: "Windows" + runner: "windows-2022" + zip_url: "https://agent-ints-python-build-sandbox.s3.eu-north-1.amazonaws.com/python-windows-combined-v3.12.6-openssl-3.0.15-openssl-3.0.9-amd64.zip" + - platform: "Linux" + runner: "ubuntu-22.04" + zip_url: "" + name: FIPS test on ${{ matrix.platform }} + runs-on: ${{ matrix.runner }} + + env: + FORCE_COLOR: "1" + DEBIAN_FRONTEND: "noninteractive" + OPENSSL_FIPS: "1" + PYTHON_VERSION: "3.12" + OPENSSL_VERSION: "3.0.15" + FIPS_MODULE_VERSION: "3.0.9" + + steps: + + - uses: actions/checkout@v4 + + - name: Install System Dependencies + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install -y --no-install-recommends \ + wget \ + build-essential \ + gcc \ + make \ + perl \ + libc6-dev + + - name: Build FIPS Module + if: runner.os == 'Linux' + run: | + wget https://www.openssl.org/source/openssl-${{ env.FIPS_MODULE_VERSION }}.tar.gz \ + && tar -xvzf openssl-${{ env.FIPS_MODULE_VERSION }}.tar.gz \ + && cd openssl-${{ env.FIPS_MODULE_VERSION }} \ + && ./Configure enable-fips \ + && make \ + && sudo make install + + - name: Build OpenSSL + if: runner.os == 'Linux' + run: | + wget https://www.openssl.org/source/openssl-${{ env.OPENSSL_VERSION }}.tar.gz \ + && tar -xvzf openssl-${{ env.OPENSSL_VERSION }}.tar.gz \ + && cd openssl-${{ env.OPENSSL_VERSION }} \ + && ./Configure enable-fips \ + && make \ + && sudo make install + + - name: Build Python from Source with Custom OpenSSL + if: runner.os == 'Linux' + run: | + + # Install dependencies for building Python + sudo apt-get update && sudo apt-get install -y \ + build-essential \ + zlib1g-dev \ + libffi-dev \ + libssl-dev \ + libncurses5-dev \ + libsqlite3-dev \ + libreadline-dev \ + libbz2-dev \ + liblzma-dev \ + tk-dev \ + uuid-dev \ + libgdbm-dev \ + wget + + # Download and extract Python source + wget https://www.python.org/ftp/python/${{ env.PYTHON_VERSION }}/Python-${{ env.PYTHON_VERSION }}.tgz + tar -xvzf Python-${{ env.PYTHON_VERSION }}.tgz -C python_dir + cd python_dir + + # Configure and build Python with custom OpenSSL + ./configure --enable-optimizations --with-openssl=$(pwd)/../openssl-${{ env.OPENSSL_VERSION }} + make -j$(nproc) + sudo make altinstall + + - name: Download python-windows-combined + if: runner.os == 'Windows' + shell: powershell + run: | + Invoke-WebRequest -Uri ${{ inputs.zip_url || matrix.zip_url }} -OutFile 'python_combined.zip' + + - name: Unzip python_combined.zip + if: runner.os == 'Windows' + shell: powershell + run: | + Expand-Archive -Path python_combined.zip -DestinationPath .\python_dir + + - name: Run fipsintall.exe + if: runner.os == 'Windows' + working-directory: .\python_dir + shell: powershell + run: | + .\openssl.exe fipsinstall -module .\ossl-modules\fips.dll -out fipsmodule.cnf + + - name: Configure OpenSSL for FIPS + if: runner.os == 'Windows' + working-directory: .\python_dir + shell: powershell + run: | + # Create openssl.cnf to enable FIPS mode + $OpenSSLConf = @" + config_diagnostics = 1 + openssl_conf = openssl_init + + .include fipsmodule.cnf + + [openssl_init] + providers = provider_sect + alg_section = algorithm_sect + + [provider_sect] + fips = fips_sect + base = base_sect + + [base_sect] + activate = 1 + + [algorithm_sect] + default_properties = fips=yes + "@ + $OpenSSLConf | Set-Content -Path ".\openssl.cnf" + + - name: Verify OpenSSL + if: runner.os == 'Windows' + working-directory: .\python_dir + shell: powershell + run: | + .\openssl.exe version -a + .\openssl.exe list -providers + + - name: Verify OpenSSL with FIPS ENV vars + if: runner.os == 'Windows' + working-directory: .\python_dir + shell: powershell + run: | + $env:OPENSSL_MODULES = ".\ossl-modules" + $env:OPENSSL_CONF = ".\openssl.cnf" + .\openssl.exe list -providers + + - name: Add Python to PATH + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + echo "PATH=$(pwd)\python_dir;$(pwd)\python_dir\Scripts;$PATH" >> $GITHUB_ENV + else + echo "PATH=$(pwd)/python_dir:$PATH" >> $GITHUB_ENV + fi + + - name: Install pip + run: | + python -m ensurepip + + - name: Restore cache + uses: actions/cache/restore@v4 + with: + path: ${{ runner.os == 'Windows' && '~\AppData\Local\pip\Cache' || '~/.cache/pip' }} + key: >- + ${{ format( + 'v01-python-{0}-{1}-{2}-{3}', + env.pythonLocation, + hashFiles('datadog_checks_base/pyproject.toml'), + hashFiles('datadog_checks_dev/pyproject.toml'), + hashFiles('ddev/pyproject.toml') + )}} + restore-keys: |- + v01-python-${{ env.pythonLocation }} + + - name: Install ddev from local folder + run: | + python -m pip install -e ./datadog_checks_dev[cli] + python -m pip install -e ./ddev + + - name: Configure ddev + run: | + ddev config set repos.core . + ddev config set repo core + + - name: Test + if: runner.os == 'Windows' + shell: powershell + run: | + $env:PATH_TO_OPENSSL_CONF = "$(pwd)\openssl.cnf" + $env:PATH_TO_OPENSSL_MODULES = "$(pwd)\ossl-modules" + $env:OPENSSL_CONF = "$(pwd)\openssl.cnf" + $env:OPENSSL_MODULES = "$(pwd)\ossl-modules" + .\python_dir\openssl.exe list -providers + .\python_dir\openssl.exe md5 + ddev datadog_checks_base -m fips_off + ddev datadog_checks_base -m fips_on + python -c "import ssl; ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT).set_ciphers('MD5')" + which python diff --git a/datadog_checks_base/tests/test_fips.py b/datadog_checks_base/tests/test_fips.py new file mode 100644 index 0000000000000..426e5e0861265 --- /dev/null +++ b/datadog_checks_base/tests/test_fips.py @@ -0,0 +1,68 @@ +# (C) Datadog, Inc. 2024-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import Any # noqa: F401 + +import os +import pytest + +from datadog_checks.base.utils.fips import enable_fips + +PATH_TO_OPENSSL_CONF = os.getenv("PATH_TO_OPENSSL_CONF") +PATH_TO_OPENSSL_MODULES = os.getenv("PATH_TO_OPENSSL_MODULES") + + +@pytest.fixture(scope="function") +def clean_environment(monkeypatch): + monkeypatch.setenv("GOFIPS", "0") + monkeypatch.setenv("OPENSSL_CONF", "") + monkeypatch.setenv("OPENSSL_MODULES", "") + yield + + +@pytest.mark.fips_off +def test_ssl_md5_before_fips(clean_environment): + """ + MD5 cipher should be available through ssl before enabling FIPS mode. + """ + import ssl + + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.set_ciphers("MD5") + assert True + + +@pytest.mark.fips_off +def test_cryptography_md5_before_fips(clean_environment): + """ + MD5 cipher should be available through cryptography before enabling FIPS mode. + """ + from cryptography.hazmat.primitives import hashes + + assert hashes.Hash(hashes.MD5()) + + +@pytest.mark.fips_on +def test_ssl_md5_after_fips(clean_environment): + """ + MD5 cipher should not be available through ssl after enabling FIPS mode. + """ + import ssl + + enable_fips(path_to_openssl_conf=PATH_TO_OPENSSL_CONF, path_to_openssl_modules=PATH_TO_OPENSSL_MODULES) + with pytest.raises(ssl.SSLError, match='No cipher can be selected.'): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.set_ciphers("MD5") + + +@pytest.mark.fips_on +def test_cryptography_md5_after_fips(clean_environment): + """ + MD5 cipher should not be available through cryptography after enabling FIPS mode. + """ + from cryptography.exceptions import InternalError + from cryptography.hazmat.primitives import hashes + + enable_fips(path_to_openssl_conf=PATH_TO_OPENSSL_CONF, path_to_openssl_modules=PATH_TO_OPENSSL_MODULES) + with pytest.raises(InternalError, match='Unknown OpenSSL error.'): + hashes.Hash(hashes.MD5())