From e43bfe6a38a3f35410c498d290ce2356c837a41e Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 13 Sep 2021 18:02:21 +0100 Subject: [PATCH] Don't generate DSA ssh key if system FIPS is enabled --- changelog/80.improvement.rst | 1 + requirements/tests.txt | 1 + src/saltfactories/daemons/sshd.py | 4 +- src/saltfactories/utils/platform.py | 37 +++++++++++++++ .../{test_platforms.py => test_platform.py} | 45 +++++++++++++++++-- 5 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 changelog/80.improvement.rst rename tests/unit/utils/{test_platforms.py => test_platform.py} (73%) diff --git a/changelog/80.improvement.rst b/changelog/80.improvement.rst new file mode 100644 index 00000000..4f1a6fed --- /dev/null +++ b/changelog/80.improvement.rst @@ -0,0 +1 @@ +The ``sshd`` server no longer generates ``dsa`` keys if the system has FIPS enabled diff --git a/requirements/tests.txt b/requirements/tests.txt index ee5e02c2..5ff20ec8 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,3 +1,4 @@ -r base.txt docker pytest-subtests +pyfakefs; python_version >= '3.5' diff --git a/src/saltfactories/daemons/sshd.py b/src/saltfactories/daemons/sshd.py index ff437c05..00ecf6cb 100644 --- a/src/saltfactories/daemons/sshd.py +++ b/src/saltfactories/daemons/sshd.py @@ -11,6 +11,7 @@ from saltfactories.bases import Daemon from saltfactories.exceptions import FactoryFailure +from saltfactories.utils import platform from saltfactories.utils import ports from saltfactories.utils import running_username from saltfactories.utils import socket @@ -97,7 +98,8 @@ def _write_config(self): config_lines.append("{} {}\n".format(key, value)) # Let's generate the host keys - self._generate_server_dsa_key() + if platform.is_fips_enabled() is False: + self._generate_server_dsa_key() self._generate_server_ecdsa_key() self._generate_server_ed25519_key() for host_key in pathlib.Path(self.config_dir).glob("ssh_host_*_key"): diff --git a/src/saltfactories/utils/platform.py b/src/saltfactories/utils/platform.py index 5039c2e0..a535cdc0 100644 --- a/src/saltfactories/utils/platform.py +++ b/src/saltfactories/utils/platform.py @@ -8,6 +8,9 @@ Platform related utilities """ +import pathlib +import shutil +import subprocess import sys import salt.utils.platform @@ -154,3 +157,37 @@ def on_platforms( return True return False + + +def is_fips_enabled(): + """ + Check is FIPS is enabled + + :return bool: Return true when enabled + """ + if pathlib.Path("/etc/system-fips").exists(): + return True + kernel_fips_enabled_path = pathlib.Path("/proc/sys/crypto/fips_enabled") + if kernel_fips_enabled_path.exists() and kernel_fips_enabled_path.read_text().strip() == "1": + return True + sysctl_path = shutil.which("sysctl") + if not sysctl_path: + return False + ret = subprocess.run( + [sysctl_path, "crypto.fips_enabled"], + check=False, + shell=False, + stdout=subprocess.PIPE, + universal_newlines=True, + ) + if ret.returncode == 0: + stripped_output = ret.stdout.strip() + if not stripped_output: + # No output? + return False + if "=" not in stripped_output: + # Don't know how to parse this + return False + if stripped_output.split("=")[-1].strip() == "1": + return True + return False diff --git a/tests/unit/utils/test_platforms.py b/tests/unit/utils/test_platform.py similarity index 73% rename from tests/unit/utils/test_platforms.py rename to tests/unit/utils/test_platform.py index 8933e545..08bd6b88 100644 --- a/tests/unit/utils/test_platforms.py +++ b/tests/unit/utils/test_platform.py @@ -1,11 +1,15 @@ """ - tests.unit.utils.test_platforms - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + tests.unit.utils.test_platform + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Tests for saltfactories.utils.platforms + Tests for saltfactories.utils.platform """ +import subprocess +import sys from unittest import mock +import pytest + import saltfactories.utils.platform @@ -127,3 +131,38 @@ def test_is_not_aarch64(): return_value = False with mock.patch("sys.platform", "not_aarch64"): assert saltfactories.utils.platform.is_aarch64() is return_value + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="pyfakes is not available on Py3.5") +def test_is_fips_enabled_etc_system_fips(fs): + fs.create_file("/etc/system-fips") + assert saltfactories.utils.platform.is_fips_enabled() is True + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="pyfakes is not available on Py3.5") +@pytest.mark.parametrize("value, expected", [("0", False), ("1", True)]) +def test_is_fips_enabled_procfs(fs, value, expected): + fs.create_file("/proc/sys/crypto/fips_enabled", contents=value) + assert saltfactories.utils.platform.is_fips_enabled() is expected + + +@pytest.mark.parametrize( + "output, expected", + ( + ("", False), + ("crypto.fips_enabled", False), + ("crypto.fips_enabled =", False), + ("crypto.fips_enabled = 0", False), + ("crypto.fips_enabled=1", True), + ("crypto.fips_enabled = 1", True), + ("crypto.fips_enabled = 1", True), + ), +) +def test_is_fips_enabled_sysctl(output, expected): + subprocess_run_return_value = subprocess.CompletedProcess( + args=(), returncode=0, stdout=output, stderr=None + ) + with mock.patch("shutil.which", return_value="sysctl"), mock.patch( + "subprocess.run", return_value=subprocess_run_return_value + ): + assert saltfactories.utils.platform.is_fips_enabled() is expected