diff --git a/airflow-core/newsfragments/54079.significant.rst b/airflow-core/newsfragments/54079.significant.rst new file mode 100644 index 0000000000000..3f2c3d56506bc --- /dev/null +++ b/airflow-core/newsfragments/54079.significant.rst @@ -0,0 +1,3 @@ +The SSH and SFTP providers now require ``paramiko>=4.0.0``, which drops support for DSA (DSS) +private keys. Connections that rely on a DSA key must generate a new RSA, ECDSA, or Ed25519 key +and update the connection's ``key_file`` or ``private_key`` accordingly. diff --git a/devel-common/pyproject.toml b/devel-common/pyproject.toml index 4dee7e82e2648..c273088cc02ea 100644 --- a/devel-common/pyproject.toml +++ b/devel-common/pyproject.toml @@ -118,8 +118,7 @@ dependencies = [ "types-certifi>=2021.10.8.3", "types-croniter>=2.0.0.20240423", "types-docutils>=0.21.0.20240704", - # TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 - "types-paramiko>=3.4.0.20240423,<4.0.0", + "types-paramiko>=4.0.0", "types-protobuf>=5.26.0.20240422", "types-python-dateutil>=2.9.0.20240316", "types-python-slugify>=8.0.2.20240310", diff --git a/providers/sftp/pyproject.toml b/providers/sftp/pyproject.toml index e56b35bbb6f6d..85665739ca76c 100644 --- a/providers/sftp/pyproject.toml +++ b/providers/sftp/pyproject.toml @@ -63,8 +63,7 @@ dependencies = [ "apache-airflow>=2.11.0", "apache-airflow-providers-ssh>=4.0.0", "apache-airflow-providers-common-compat>=1.12.0", - # TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 is handled - "paramiko>=3.5.1,<4.0.0", + "paramiko>=4.0.0", "asyncssh>=2.12.0; python_version < '3.14'", "asyncssh>=2.22.0; python_version >= '3.14'", ] diff --git a/providers/ssh/docs/connections/ssh.rst b/providers/ssh/docs/connections/ssh.rst index a248a60477c4f..caa23f77b86ff 100644 --- a/providers/ssh/docs/connections/ssh.rst +++ b/providers/ssh/docs/connections/ssh.rst @@ -58,6 +58,13 @@ Extra (optional) * ``disabled_algorithms`` - A dictionary mapping algorithm type to an iterable of algorithm identifiers, which will be disabled for the lifetime of the transport. * ``ciphers`` - A list of ciphers to use in order of preference. + .. note:: + As of ``paramiko`` 4.0 (a minimum requirement of this provider), DSA (DSS) private keys are no + longer supported due to their removal from the underlying library. Supported key types are RSA, + ECDSA, and Ed25519. If your connection currently uses a DSA key, generate a new key of one of the + supported types (e.g. ``ssh-keygen -t ed25519``) and update the connection's ``key_file`` or + ``private_key`` accordingly. + Example "extras" field: .. code-block:: json diff --git a/providers/ssh/pyproject.toml b/providers/ssh/pyproject.toml index 5a9d50fccf951..b0f3daf25be6d 100644 --- a/providers/ssh/pyproject.toml +++ b/providers/ssh/pyproject.toml @@ -62,8 +62,7 @@ dependencies = [ "apache-airflow>=2.11.0", "apache-airflow-providers-common-compat>=1.12.0", "asyncssh>=2.12.0", - # TODO: Bump to >= 4.0.0 once https://github.com/apache/airflow/issues/54079 is handled - "paramiko>=3.5.1,<4.0.0", + "paramiko>=4.0.0", ] diff --git a/providers/ssh/src/airflow/providers/ssh/hooks/ssh.py b/providers/ssh/src/airflow/providers/ssh/hooks/ssh.py index 54b7edf6008c7..4a9535ba315c7 100644 --- a/providers/ssh/src/airflow/providers/ssh/hooks/ssh.py +++ b/providers/ssh/src/airflow/providers/ssh/hooks/ssh.py @@ -88,16 +88,16 @@ class SSHHook(BaseHook): """ # List of classes to try loading private keys as, ordered (roughly) by most common to least common + # Note: DSSKey (DSA) support was removed in paramiko 4.0+, so it is no longer offered here. + # Users relying on DSA keys must migrate to RSA, ECDSA, or Ed25519 keys. _pkey_loaders: Sequence[type[paramiko.PKey]] = ( paramiko.RSAKey, paramiko.ECDSAKey, paramiko.Ed25519Key, - paramiko.DSSKey, ) _host_key_mappings = { "rsa": paramiko.RSAKey, - "dss": paramiko.DSSKey, "ecdsa": paramiko.ECDSAKey, "ed25519": paramiko.Ed25519Key, } diff --git a/uv.lock b/uv.lock index 11bbace1114d2..d844950ac902c 100644 --- a/uv.lock +++ b/uv.lock @@ -2719,7 +2719,7 @@ requires-dist = [ { name = "types-deprecated", marker = "extra == 'mypy'", specifier = ">=1.2.9.20240311" }, { name = "types-docutils", marker = "extra == 'mypy'", specifier = ">=0.21.0.20240704" }, { name = "types-markdown", marker = "extra == 'mypy'", specifier = ">=3.6.0.20240316" }, - { name = "types-paramiko", marker = "extra == 'mypy'", specifier = ">=3.4.0.20240423,<4.0.0" }, + { name = "types-paramiko", marker = "extra == 'mypy'", specifier = ">=4.0.0" }, { name = "types-protobuf", marker = "extra == 'mypy'", specifier = ">=5.26.0.20240422" }, { name = "types-pymysql", marker = "extra == 'mypy'", specifier = ">=1.1.0.20240425" }, { name = "types-python-dateutil", marker = "extra == 'mypy'", specifier = ">=2.9.0.20240316" }, @@ -7584,7 +7584,7 @@ requires-dist = [ { name = "apache-airflow-providers-ssh", editable = "providers/ssh" }, { name = "asyncssh", marker = "python_full_version < '3.14'", specifier = ">=2.12.0" }, { name = "asyncssh", marker = "python_full_version >= '3.14'", specifier = ">=2.22.0" }, - { name = "paramiko", specifier = ">=3.5.1,<4.0.0" }, + { name = "paramiko", specifier = ">=4.0.0" }, { name = "sshfs", marker = "extra == 'sshfs'", specifier = ">=2023.1.0" }, ] provides-extras = ["openlineage", "sshfs"] @@ -7861,7 +7861,7 @@ requires-dist = [ { name = "apache-airflow", editable = "." }, { name = "apache-airflow-providers-common-compat", editable = "providers/common/compat" }, { name = "asyncssh", specifier = ">=2.12.0" }, - { name = "paramiko", specifier = ">=3.5.1,<4.0.0" }, + { name = "paramiko", specifier = ">=4.0.0" }, ] [package.metadata.requires-dev] @@ -14543,6 +14543,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/97/9c/1646ca469bc2dc299ac393c8d31136c6c22a35ca1e373fa462ac01100d37/inputimeout-1.0.4-py3-none-any.whl", hash = "sha256:f4e23d27753cfc25268eefc8d52a3edc46280ad831d226617c51882423475a43", size = 4639, upload-time = "2018-03-02T14:28:06.903Z" }, ] +[[package]] +name = "invoke" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/227c48c5fe47fa178ccf1fda8f047d16c97ba926567b661e9ce2045c600c/invoke-3.0.3.tar.gz", hash = "sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c", size = 343419, upload-time = "2026-04-07T15:17:48.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/de/bbc12563bbf979618d17625a4e753ff7a078523e28d870d3626daa97261a/invoke-3.0.3-py3-none-any.whl", hash = "sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053", size = 160958, upload-time = "2026-04-07T15:17:46.875Z" }, +] + [[package]] name = "ipdb" version = "0.13.13" @@ -18115,16 +18124,17 @@ all = [ [[package]] name = "paramiko" -version = "3.5.1" +version = "5.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bcrypt" }, { name = "cryptography" }, + { name = "invoke" }, { name = "pynacl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/15/ad6ce226e8138315f2451c2aeea985bf35ee910afb477bae7477dc3a8f3b/paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822", size = 1566110, upload-time = "2025-02-04T02:37:59.783Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/93/dcc25d52f49022ae6175d15e6bd751f1acc99b98bc61fc55e5155a7be2e7/paramiko-5.0.0.tar.gz", hash = "sha256:36763b5b95c2a0dcfdf1abc48e48156ee425b21efe2f0e787c2dd5a95c0e5e79", size = 1548586, upload-time = "2026-05-09T18:28:52.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/f8/c7bd0ef12954a81a1d3cea60a13946bd9a49a0036a5927770c461eade7ae/paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61", size = 227298, upload-time = "2025-02-04T02:37:57.672Z" }, + { url = "https://files.pythonhosted.org/packages/82/5b/eadf6d45de38d30ab603f49393b6cd2cbe7e233af8cf90197e32782b68a9/paramiko-5.0.0-py3-none-any.whl", hash = "sha256:b7044611c30140d9a75261653210e2002977b71a0497ff3ba0d98d7edbf62f7c", size = 208919, upload-time = "2026-05-09T18:28:50.295Z" }, ] [[package]] @@ -23601,14 +23611,14 @@ wheels = [ [[package]] name = "types-paramiko" -version = "3.5.0.20250801" +version = "5.0.0.20260617" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/c4/4df427c835e9c795662241410732ab936c6f58be798ec25ce7015771c448/types_paramiko-3.5.0.20250801.tar.gz", hash = "sha256:e79ff84eaf44f2a5ad811743edef5d9c0cd6632a264a526f00405b87db1ec99b", size = 28838, upload-time = "2025-08-01T03:48:52.777Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/89/902652d7b62bb7cd2b73c7f08c8fd03945c223c3814022f0ad305e1018b9/types_paramiko-5.0.0.20260617.tar.gz", hash = "sha256:50a5b0dc68b39d30097cb7d93b4915dbbc97ed740ea633bd492be25ca1f25df4", size = 28474, upload-time = "2026-06-17T06:56:52.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/5b/0f0bb1e45f7547d081ae9d490a88c6f6031d0f2c97459236e0a2a8b27207/types_paramiko-3.5.0.20250801-py3-none-any.whl", hash = "sha256:3e02a0fcf2b7e7b213e0cd569f7223ff9af417052a4d149d84172ebaa6fd742e", size = 39705, upload-time = "2025-08-01T03:48:51.855Z" }, + { url = "https://files.pythonhosted.org/packages/50/4d/eab89decb4201dfff665a3b92088f384ff1e165bf6caa447509733bc1bc6/types_paramiko-5.0.0.20260617-py3-none-any.whl", hash = "sha256:83d87c396405666524441710ec59e693bca8b19021861456ac0a2401327812f8", size = 37123, upload-time = "2026-06-17T06:56:51.81Z" }, ] [[package]]