From 23d2930f9c26beee2c6b136879d8d1d029ebf9bc Mon Sep 17 00:00:00 2001 From: David Kirov Date: Mon, 23 Dec 2024 18:31:16 +0200 Subject: [PATCH 1/2] 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()) From 70e2d7cc61aff1b3cf2b2c1bd21ea7bf9721fa7c Mon Sep 17 00:00:00 2001 From: David Kirov Date: Mon, 23 Dec 2024 18:50:37 +0200 Subject: [PATCH 2/2] Experiment with changes --- .github/workflows/experimental.yml | 44 +++++++++++++++----------- datadog_checks_base/tests/test_fips.py | 4 +++ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml index b5bd0ed0b81ca..0bd67f1b03c09 100644 --- a/.github/workflows/experimental.yml +++ b/.github/workflows/experimental.yml @@ -155,19 +155,22 @@ jobs: - name: Verify OpenSSL if: runner.os == 'Windows' working-directory: .\python_dir - shell: powershell run: | - .\openssl.exe version -a - .\openssl.exe list -providers + ./openssl version -a + ./openssl 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 + if [[ "$RUNNER_OS" == "Windows" ]]; then + echo "OPENSSL_MODULES=$(pwd)\ossl-modules" >> $GITHUB_ENV + echo "OPENSSL_CONF=$(pwd)\openssl.cnf" >> $GITHUB_ENV + else + echo "OPENSSL_MODULES=$(pwd)/ossl-modules" >> $GITHUB_ENV + echo "OPENSSL_CONF=$(pwd)/openssl.cnf" >> $GITHUB_ENV + fi + ./openssl list -providers - name: Add Python to PATH run: | @@ -207,16 +210,19 @@ jobs: ddev config set repo core - name: Test - if: runner.os == 'Windows' - shell: powershell + working-directory: ./python_dir 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 + if [[ "$RUNNER_OS" == "Windows" ]]; then + echo "PATH_TO_OPENSSL_CONF=$(pwd)\openssl.cnf" >> $GITHUB_ENV + echo "PATH_TO_OPENSSL_MODULES=$(pwd)\ossl-modules" >> $GITHUB_ENV + echo "OPENSSL_CONF=$(pwd)\openssl.cnf" >> $GITHUB_ENV + echo "OPENSSL_MODULES=$(pwd)\ossl-modules" >> $GITHUB_ENV + else + echo "PATH_TO_OPENSSL_CONF=$(pwd)/openssl.cnf" >> $GITHUB_ENV + echo "PATH_TO_OPENSSL_MODULES=$(pwd)/ossl-modules" >> $GITHUB_ENV + echo "OPENSSL_CONF=$(pwd)/openssl.cnf" >> $GITHUB_ENV + echo "OPENSSL_MODULES=$(pwd)/ossl-modules" >> $GITHUB_ENV + fi + ./openssl list -providers + ddev test datadog_checks_base -- -s -m fips_off + ddev test datadog_checks_base -- -s -m fips_on diff --git a/datadog_checks_base/tests/test_fips.py b/datadog_checks_base/tests/test_fips.py index 426e5e0861265..2aaa516178965 100644 --- a/datadog_checks_base/tests/test_fips.py +++ b/datadog_checks_base/tests/test_fips.py @@ -5,6 +5,7 @@ import os import pytest +import sys from datadog_checks.base.utils.fips import enable_fips @@ -49,6 +50,9 @@ def test_ssl_md5_after_fips(clean_environment): """ import ssl + print(f'\nPython Path: {sys.executable}') + print(f'\nEnv Vars: {os.environ}') + 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)