From b4af8cc29775b67d6341dfcb925c0866f4589da6 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Thu, 8 Dec 2022 12:38:48 +0100 Subject: [PATCH 1/5] python3Packages.certifi: use system ca-bundle We update that one more reliably and it allows ties in with module based configuration applied through `security.pki`. Also allow overwriting the CA bundle used through `NIX_SSL_CERT_FILE` as is common throughout nixpkgs. Fixes: CVE-2022-23491 (cherry picked from commit 8d7cc9cac9ecdf95f554c5ea7ca15118baa06c39) --- .../python-modules/certifi/default.nix | 18 +++++ .../python-modules/certifi/env.patch | 76 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 pkgs/development/python-modules/certifi/env.patch diff --git a/pkgs/development/python-modules/certifi/default.nix b/pkgs/development/python-modules/certifi/default.nix index ecf7ed49aa5c..065b32ddc629 100644 --- a/pkgs/development/python-modules/certifi/default.nix +++ b/pkgs/development/python-modules/certifi/default.nix @@ -1,5 +1,6 @@ { lib , buildPythonPackage +, cacert , pythonOlder , fetchFromGitHub , pytestCheckHook @@ -18,10 +19,27 @@ buildPythonPackage rec { hash = "sha256-B6LO6AfG9cfpyNI7hj3VjmGTFsrrIkDYO4gPMkZY74w="; }; + patches = [ + # Add support for NIX_SSL_CERT_FILE + ./env.patch + ]; + + postPatch = '' + # Use our system-wide ca-bundle instead of the bundled one + rm -v "certifi/cacert.pem" + ln -snvf "${cacert}/etc/ssl/certs/ca-bundle.crt" "certifi/cacert.pem" + ''; + checkInputs = [ pytestCheckHook ]; + preCheck = '' + # NIX_SSL_CERT_FILE is set to /no-cert-file.crt during build, which + # breaks the tests + unset NIX_SSL_CERT_FILE + ''; + pythonImportsCheck = [ "certifi" ]; diff --git a/pkgs/development/python-modules/certifi/env.patch b/pkgs/development/python-modules/certifi/env.patch new file mode 100644 index 000000000000..8d32394521a6 --- /dev/null +++ b/pkgs/development/python-modules/certifi/env.patch @@ -0,0 +1,76 @@ +diff --git a/certifi/core.py b/certifi/core.py +index de02898..3ec9147 100644 +--- a/certifi/core.py ++++ b/certifi/core.py +@@ -4,6 +4,7 @@ certifi.py + + This module returns the installation location of cacert.pem or its contents. + """ ++import os + import sys + + +@@ -12,7 +13,7 @@ if sys.version_info >= (3, 11): + from importlib.resources import as_file, files + + _CACERT_CTX = None +- _CACERT_PATH = None ++ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the file +@@ -39,14 +40,16 @@ if sys.version_info >= (3, 11): + return _CACERT_PATH + + def contents() -> str: +- return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") ++ if _CACERT_PATH is not None: ++ return open(_CACERT_PATH, encoding="utf-8").read() ++ return files("certifi").joinpath("cacert.pem").read_text(encoding="utf-8") + + elif sys.version_info >= (3, 7): + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None +- _CACERT_PATH = None ++ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the +@@ -74,7 +77,9 @@ elif sys.version_info >= (3, 7): + return _CACERT_PATH + + def contents() -> str: +- return read_text("certifi", "cacert.pem", encoding="ascii") ++ if _CACERT_PATH is not None: ++ return open(_CACERT_PATH, encoding="utf-8").read() ++ return read_text("certifi", "cacert.pem", encoding="utf-8") + + else: + import os +@@ -84,6 +89,8 @@ else: + Package = Union[types.ModuleType, str] + Resource = Union[str, "os.PathLike"] + ++ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) ++ + # This fallback will work for Python versions prior to 3.7 that lack the + # importlib.resources module but relies on the existing `where` function + # so won't address issues with environments like PyOxidizer that don't set +@@ -102,7 +109,14 @@ else: + def where() -> str: + f = os.path.dirname(__file__) + ++ if _CACERT_PATH is not None: ++ return _CACERT_PATH ++ + return os.path.join(f, "cacert.pem") + + def contents() -> str: +- return read_text("certifi", "cacert.pem", encoding="ascii") ++ if _CACERT_PATH is not None: ++ with open(_CACERT_PATH, encoding="utf-8") as data: ++ return data.read() ++ ++ return read_text("certifi", "cacert.pem", encoding="utf-8") From 61a5d7bcef94431ce2d1a471cb95cd00a4e31ddf Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Thu, 8 Dec 2022 12:40:07 +0100 Subject: [PATCH 2/5] python3Packages.certifi: 2022.09.24 -> 2022.12.07 Diff: https://github.com/certifi/python-certifi/compare/2022.09.24...2022.12.07 (cherry picked from commit b40cf0d095cc95c6f4c9ff927f1a869c3548946d) --- pkgs/development/python-modules/certifi/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/development/python-modules/certifi/default.nix b/pkgs/development/python-modules/certifi/default.nix index 065b32ddc629..0aa2a321b832 100644 --- a/pkgs/development/python-modules/certifi/default.nix +++ b/pkgs/development/python-modules/certifi/default.nix @@ -8,7 +8,7 @@ buildPythonPackage rec { pname = "certifi"; - version = "2022.09.24"; + version = "2022.12.07"; disabled = pythonOlder "3.6"; @@ -16,7 +16,7 @@ buildPythonPackage rec { owner = pname; repo = "python-certifi"; rev = version; - hash = "sha256-B6LO6AfG9cfpyNI7hj3VjmGTFsrrIkDYO4gPMkZY74w="; + hash = "sha256-r6TJ6YGL0cygz+F6g6wiqBfBa/QKhynZ92C6lHTZ2rI="; }; patches = [ From 8b96eecde99f01525865bc1c7b6426f3e625369f Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Thu, 8 Dec 2022 12:42:27 +0100 Subject: [PATCH 3/5] python3Packages.requests: rely on patched certifi The where() function in certifi has been patched to allow more consumers of the certifi package to use the system ca-bundle. (cherry picked from commit 8456141e25c6b2cc293ed6b147c2a0a2d01c2d0a) --- ...-Nix-default-CA-bundles-over-certifi.patch | 60 ------------------- .../python-modules/requests/default.nix | 5 -- 2 files changed, 65 deletions(-) delete mode 100644 pkgs/development/python-modules/requests/0001-Prefer-NixOS-Nix-default-CA-bundles-over-certifi.patch diff --git a/pkgs/development/python-modules/requests/0001-Prefer-NixOS-Nix-default-CA-bundles-over-certifi.patch b/pkgs/development/python-modules/requests/0001-Prefer-NixOS-Nix-default-CA-bundles-over-certifi.patch deleted file mode 100644 index de6a4b5c1b57..000000000000 --- a/pkgs/development/python-modules/requests/0001-Prefer-NixOS-Nix-default-CA-bundles-over-certifi.patch +++ /dev/null @@ -1,60 +0,0 @@ -From b36083efafec5a3c1c5864cd0b62367ddf3856ae Mon Sep 17 00:00:00 2001 -From: Keshav Kini -Date: Sun, 16 May 2021 20:35:24 -0700 -Subject: [PATCH] Prefer NixOS/Nix default CA bundles over certifi - -Normally, requests gets its default CA bundle from the certifi -package. On NixOS and when using Nix on non-NixOS platforms, we would -rather default to using our own certificate bundles controlled by the -Nix/NixOS user. - -This commit overrides requests.certs.where(), which previously was -just aliased to certifi.where(), so that now it does the following: - -- When run by Nix on non-NixOS, the environment variable - $NIX_SSL_CERT_FILE will point to the CA bundle we're using, so we - use that. - -- When running on NixOS, the CA bundle we're using has the static path - /etc/ssl/certs/ca-certificates.crt , so we use that. - -- Otherwise, we fall back to the original behavior of using certifi's - CA bundle. Higher in the call stack, users of requests can also - explicitly specify a CA bundle to use, which overrides all this - logic. ---- - requests/certs.py | 18 +++++++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - -diff --git a/requests/certs.py b/requests/certs.py -index d1a378d7..faf462b7 100644 ---- a/requests/certs.py -+++ b/requests/certs.py -@@ -12,7 +12,23 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed - environment, you can change the definition of where() to return a separately - packaged CA bundle. - """ --from certifi import where -+ -+import os -+ -+import certifi -+ -+ -+def where(): -+ nix_ssl_cert_file = os.getenv("NIX_SSL_CERT_FILE") -+ if nix_ssl_cert_file and os.path.exists(nix_ssl_cert_file): -+ return nix_ssl_cert_file -+ -+ nixos_ca_bundle = "/etc/ssl/certs/ca-certificates.crt" -+ if os.path.exists(nixos_ca_bundle): -+ return nixos_ca_bundle -+ -+ return certifi.where() -+ - - if __name__ == '__main__': - print(where()) --- -2.31.1 - diff --git a/pkgs/development/python-modules/requests/default.nix b/pkgs/development/python-modules/requests/default.nix index 5eab25fa3e15..d7230c532eb2 100644 --- a/pkgs/development/python-modules/requests/default.nix +++ b/pkgs/development/python-modules/requests/default.nix @@ -27,11 +27,6 @@ buildPythonPackage rec { hash = "sha256-fFWZsQL+3apmHIJsVqtP7ii/0X9avKHrvj5/GdfJeYM="; }; - patches = [ - # Use the default NixOS CA bundle from the certifi package - ./0001-Prefer-NixOS-Nix-default-CA-bundles-over-certifi.patch - ]; - propagatedBuildInputs = [ brotlicffi certifi From c2f855bbd7fde4af3d7d96798e328742654f5cf9 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Fri, 9 Dec 2022 13:30:00 +0100 Subject: [PATCH 4/5] python3Packages.certifi: propagate cacert for its setup-hook For tests that require a working ca-bundle via certifi we need to setup `NIX_SSL_CERT_FILE`, which is kindly provided by cacerts setup-hook. (cherry picked from commit 35750bad505058f11cffbbba28c6e12491e5da81) --- pkgs/development/python-modules/certifi/default.nix | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkgs/development/python-modules/certifi/default.nix b/pkgs/development/python-modules/certifi/default.nix index 0aa2a321b832..c80ba08503d4 100644 --- a/pkgs/development/python-modules/certifi/default.nix +++ b/pkgs/development/python-modules/certifi/default.nix @@ -30,16 +30,15 @@ buildPythonPackage rec { ln -snvf "${cacert}/etc/ssl/certs/ca-bundle.crt" "certifi/cacert.pem" ''; + propagatedNativeBuildInputs = [ + # propagate cacerts setup-hook to set up `NIX_SSL_CERT_FILE` + cacert + ]; + checkInputs = [ pytestCheckHook ]; - preCheck = '' - # NIX_SSL_CERT_FILE is set to /no-cert-file.crt during build, which - # breaks the tests - unset NIX_SSL_CERT_FILE - ''; - pythonImportsCheck = [ "certifi" ]; From 05e059adc54073a89c649fd4ca04195b40e09ee1 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sat, 10 Dec 2022 19:10:15 +0100 Subject: [PATCH 5/5] python3Packages.certifi: Ignore /no-cert-file.crt in NIX_SSL_CERT_FILE (cherry picked from commit 92f67f68b380ca7e7a94a9fccf641441e7ab35de) --- .../python-modules/certifi/env.patch | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/pkgs/development/python-modules/certifi/env.patch b/pkgs/development/python-modules/certifi/env.patch index 8d32394521a6..292f977ef2c0 100644 --- a/pkgs/development/python-modules/certifi/env.patch +++ b/pkgs/development/python-modules/certifi/env.patch @@ -1,8 +1,8 @@ diff --git a/certifi/core.py b/certifi/core.py -index de02898..3ec9147 100644 +index de02898..c033d20 100644 --- a/certifi/core.py +++ b/certifi/core.py -@@ -4,6 +4,7 @@ certifi.py +@@ -4,15 +4,25 @@ certifi.py This module returns the installation location of cacert.pem or its contents. """ @@ -10,16 +10,26 @@ index de02898..3ec9147 100644 import sys -@@ -12,7 +13,7 @@ if sys.version_info >= (3, 11): ++def get_cacert_path_from_environ(): ++ path = os.environ.get("NIX_SSL_CERT_FILE", None) ++ ++ if path == "/no-cert-file.crt": ++ return None ++ ++ return path ++ ++ + if sys.version_info >= (3, 11): + from importlib.resources import as_file, files _CACERT_CTX = None - _CACERT_PATH = None -+ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) ++ _CACERT_PATH = get_cacert_path_from_environ() def where() -> str: # This is slightly terrible, but we want to delay extracting the file -@@ -39,14 +40,16 @@ if sys.version_info >= (3, 11): +@@ -39,14 +49,16 @@ if sys.version_info >= (3, 11): return _CACERT_PATH def contents() -> str: @@ -34,11 +44,11 @@ index de02898..3ec9147 100644 _CACERT_CTX = None - _CACERT_PATH = None -+ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) ++ _CACERT_PATH = get_cacert_path_from_environ() def where() -> str: # This is slightly terrible, but we want to delay extracting the -@@ -74,7 +77,9 @@ elif sys.version_info >= (3, 7): +@@ -74,7 +86,9 @@ elif sys.version_info >= (3, 7): return _CACERT_PATH def contents() -> str: @@ -49,16 +59,16 @@ index de02898..3ec9147 100644 else: import os -@@ -84,6 +89,8 @@ else: +@@ -84,6 +98,8 @@ else: Package = Union[types.ModuleType, str] Resource = Union[str, "os.PathLike"] -+ _CACERT_PATH = os.environ.get("NIX_SSL_CERT_FILE", None) ++ _CACERT_PATH = get_cacert_path_from_environ() + # This fallback will work for Python versions prior to 3.7 that lack the # importlib.resources module but relies on the existing `where` function # so won't address issues with environments like PyOxidizer that don't set -@@ -102,7 +109,14 @@ else: +@@ -102,7 +118,14 @@ else: def where() -> str: f = os.path.dirname(__file__)