From e199cb1467516189703d14b1ce8d540b879f1a1b Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:19:45 +0530 Subject: [PATCH 1/9] remove DSS key references; upgrade paramiko dependency to version 4.0 --- README.rst | 4 ++-- setup.py | 2 +- sshtunnel.py | 9 ++++----- tests/test_forwarder.py | 8 +++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 7400816e..ff277335 100644 --- a/README.rst +++ b/README.rst @@ -255,9 +255,9 @@ CLI usage -k SSH_HOST_KEY, --ssh_host_key SSH_HOST_KEY Gateway's host key -K KEY_FILE, --private_key_file KEY_FILE - RSA/DSS/ECDSA private key file + RSA/ECDSA private key file -S KEY_PASSWORD, --private_key_password KEY_PASSWORD - RSA/DSS/ECDSA private key password + RSA/ECDSA private key password -t, --threaded Allow concurrent connections to each tunnel -v, --verbose Increase output verbosity (default: ERROR) -V, --version Show version number and quit diff --git a/setup.py b/setup.py index ccaaab8c..ae6c7afd 100644 --- a/setup.py +++ b/setup.py @@ -97,7 +97,7 @@ # requirements files see: # https://packaging.python.org/en/latest/requirements.html install_requires=[ - 'paramiko>=2.7.2', + 'paramiko>=4.0', ], # List additional groups of dependencies here (e.g. development diff --git a/sshtunnel.py b/sshtunnel.py index a7db0c44..c512a012 100644 --- a/sshtunnel.py +++ b/sshtunnel.py @@ -1090,7 +1090,6 @@ def get_keys(logger=None, host_pkey_directories=None, allow_agent=False): host_pkey_directories = [DEFAULT_SSH_DIRECTORY] paramiko_key_types = {'rsa': paramiko.RSAKey, - 'dsa': paramiko.DSSKey, 'ecdsa': paramiko.ECDSAKey} if hasattr(paramiko, 'Ed25519Key'): # NOQA: new in paramiko>=2.2: http://docs.paramiko.org/en/stable/api/keys.html#module-paramiko.ed25519key @@ -1286,7 +1285,7 @@ def read_private_key_file(pkey_file, Arguments: pkey_file (str): - File containing a private key (RSA, DSS or ECDSA) + File containing a private key (RSA or ECDSA) Keyword Arguments: pkey_password (Optional[str]): Password to decrypt the private key @@ -1295,7 +1294,7 @@ def read_private_key_file(pkey_file, paramiko.Pkey """ ssh_pkey = None - key_types = (paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey) + key_types = (paramiko.RSAKey, paramiko.ECDSAKey) if hasattr(paramiko, 'Ed25519Key'): # NOQA: new in paramiko>=2.2: http://docs.paramiko.org/en/stable/api/keys.html#module-paramiko.ed25519key key_types += (paramiko.Ed25519Key, ) @@ -1806,7 +1805,7 @@ def _parse_arguments(args=None): dest='ssh_private_key', metavar='KEY_FILE', type=str, - help='RSA/DSS/ECDSA private key file' + help='RSA/ECDSA private key file' ) parser.add_argument( @@ -1814,7 +1813,7 @@ def _parse_arguments(args=None): dest='ssh_private_key_password', metavar='KEY_PASSWORD', type=str, - help='RSA/DSS/ECDSA private key password' + help='RSA/ECDSA private key password' ) parser.add_argument( diff --git a/tests/test_forwarder.py b/tests/test_forwarder.py index 40662d08..02af1758 100644 --- a/tests/test_forwarder.py +++ b/tests/test_forwarder.py @@ -81,11 +81,9 @@ def capture_stdout_stderr(): SSH_USERNAME = get_random_string() SSH_PASSWORD = get_random_string() -SSH_DSS = b'\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c' SSH_RSA = b'\x60\x73\x38\x44\xcb\x51\x86\x65\x7f\xde\xda\xa2\x2b\x5a\x57\xd5' ECDSA = b'\x25\x19\xeb\x55\xe6\xa1\x47\xff\x4f\x38\xd2\x75\x6f\xa5\xd5\x60' FINGERPRINTS = { - 'ssh-dss': SSH_DSS, 'ssh-rsa': SSH_RSA, 'ecdsa-sha2-nistp256': ECDSA, } @@ -1202,7 +1200,7 @@ def test_parse_arguments_short(self): '-P={0}'.format(SSH_PASSWORD), # GW password '-R', '10.0.0.1:8080', '10.0.0.2:8080', # remote bind list '-L', ':8081', ':8082', # local bind list - '-k={0}'.format(SSH_DSS), # hostkey + '-k={0}'.format(SSH_RSA), # hostkey '-K={0}'.format(__file__), # pkey file '-S={0}'.format(SSH_PASSWORD), # pkey password '-t', # concurrent connections (threaded) @@ -1232,7 +1230,7 @@ def test_parse_arguments_long(self): '--password={0}'.format(SSH_PASSWORD), # GW password '--remote_bind_address', '10.0.0.1:8080', '10.0.0.2:8080', '--local_bind_address', ':8081', ':8082', # local bind list - '--ssh_host_key={0}'.format(SSH_DSS), # hostkey + '--ssh_host_key={0}'.format(SSH_RSA), # hostkey '--private_key_file={0}'.format(__file__), # pkey file '--private_key_password={0}'.format(SSH_PASSWORD), '--threaded', # concurrent connections (threaded) @@ -1254,7 +1252,7 @@ def _test_parser(self, parser): [('10.0.0.1', 8080), ('10.0.0.2', 8080)]) self.assertListEqual(parser['local_bind_addresses'], [('', 8081), ('', 8082)]) - self.assertEqual(parser['ssh_host_key'], str(SSH_DSS)) + self.assertEqual(parser['ssh_host_key'], str(SSH_RSA)) self.assertEqual(parser['ssh_private_key'], __file__) self.assertEqual(parser['ssh_private_key_password'], SSH_PASSWORD) self.assertTrue(parser['threaded']) From 339e08a72c699ff46dbed2ec97655f83d76814c6 Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:33:33 +0530 Subject: [PATCH 2/9] update min python version to 3.9 --- .circleci/config.yml | 12 +++++------- .github/workflows/database.yml | 23 +++++++++++++---------- setup.py | 13 ++++++------- sshtunnel.py | 16 +++++----------- tox.ini | 2 +- 5 files changed, 30 insertions(+), 36 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 144d0883..5e502bc4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ params: ¶ms parameters: version: description: Python docker image version - default: 3.9.16 + default: 3.12 type: string job_defaults: &job_defaults @@ -163,12 +163,10 @@ workflows: matrix: parameters: version: - - "2.7" - - "3.4" - - "3.5" - - "3.6" - - "3.7" - - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" - testdeploy: requires: - tests diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml index 2f0e5d9d..8bd75f51 100644 --- a/.github/workflows/database.yml +++ b/.github/workflows/database.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - python: [ python3, python2 ] + python: [ python3.9, python3.10, python3.11, python3.12 ] steps: - uses: actions/checkout@v2 @@ -26,18 +26,21 @@ jobs: chmod 600 ./e2e_tests/ssh-server-config/ssh_host_rsa_key cd e2e_tests && docker-compose up -d + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies run: | id uname -a lsb_release -a - ${{ matrix.python }} -V - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py - ${{ matrix.python }} get-pip.py - ${{ matrix.python }} -m pip install --upgrade pip - ${{ matrix.python }} -m pip install . - ${{ matrix.python }} -m pip install psycopg2-binary>=2.9.6 pymysql>=1.0.3 pymongo>=4.3.3 - ${{ matrix.python }} -m pip install --upgrade pyopenssl + python -V + python -m pip install --upgrade pip + python -m pip install . + python -m pip install psycopg2-binary>=2.9.6 pymysql>=1.0.3 pymongo>=4.3.3 + python -m pip install --upgrade pyopenssl ssh -o "StrictHostKeyChecking=no" linuxserver@127.0.0.1 -p 2223 -i ./e2e_tests/ssh-server-config/ssh_host_rsa_key -vvvvv "uname -a" @@ -46,10 +49,10 @@ jobs: # docker exec openssh-server tail -f /config/logs/openssh/current - name: Run db tests ${{ matrix.python }} - run: ${{ matrix.python }} e2e_tests/run_docker_e2e_db_tests.py + run: python e2e_tests/run_docker_e2e_db_tests.py - name: Run hungs tests ${{ matrix.python }} - run: timeout 10s ${{ matrix.python }} e2e_tests/run_docker_e2e_hangs_tests.py + run: timeout 10s python e2e_tests/run_docker_e2e_hangs_tests.py - name: Collect openssh-server logs from docker container if: failure() diff --git a/setup.py b/setup.py index ae6c7afd..9fefea35 100644 --- a/setup.py +++ b/setup.py @@ -69,16 +69,15 @@ # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], + python_requires='>=3.9', + platforms=['unix', 'macos', 'windows'], # What does your project relate to? diff --git a/sshtunnel.py b/sshtunnel.py index c512a012..d27a00bc 100644 --- a/sshtunnel.py +++ b/sshtunnel.py @@ -25,17 +25,11 @@ from binascii import hexlify import paramiko +import queue +import socketserver -if sys.version_info[0] < 3: # pragma: no cover - import Queue as queue - import SocketServer as socketserver - string_types = basestring, # noqa - input_ = raw_input # noqa -else: # pragma: no cover - import queue - import socketserver - string_types = str - input_ = input +string_types = str +input_ = input __version__ = '0.4.0' @@ -208,7 +202,7 @@ def create_logger(logger=None, if add_paramiko_handler: _check_paramiko_handlers(logger=logger) - if capture_warnings and sys.version_info >= (2, 7): + if capture_warnings: logging.captureWarnings(True) pywarnings = logging.getLogger('py.warnings') pywarnings.handlers.extend(logger.handlers) diff --git a/tox.ini b/tox.ini index 3baf2bb6..f6a8e447 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = syntax, py{27,34,35,36,37,38}, docs +envlist = syntax, py{39,310,311,312}, docs [testenv] deps = From 3654755e28029687bc964d69ba3988f02be568ca Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:38:12 +0530 Subject: [PATCH 3/9] fix python version value in circleCI config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e502bc4..600cd7b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ params: ¶ms parameters: version: description: Python docker image version - default: 3.12 + default: "3.12" type: string job_defaults: &job_defaults From ff6d07764b2a9751b5bbf9254f7468683442b40e Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:43:16 +0530 Subject: [PATCH 4/9] fix docs failures on circleci --- docs/conf.py | 2 +- docs/requirements.txt | 4 +--- setup.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7be950a4..8c2ef4e2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,7 +44,7 @@ def _warn_node(self, msg, node): extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', - 'sphinxcontrib.napoleon', + 'sphinx.ext.napoleon', ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/requirements.txt b/docs/requirements.txt index 76356efc..430705c9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1 @@ -docutils -sphinx -sphinxcontrib-napoleon +sphinx>=4.0 diff --git a/setup.py b/setup.py index 9fefea35..66f2f7d2 100644 --- a/setup.py +++ b/setup.py @@ -112,8 +112,7 @@ 'tox>=1.8.1', ], 'build_sphinx': [ - 'sphinx', - 'sphinxcontrib-napoleon', + 'sphinx>=4.0', ], }, From 0d229ea179c0712dd59ce61e87ac0f778b693cb2 Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:47:26 +0530 Subject: [PATCH 5/9] set universal flag to 0 for Python 3 only compatibility --- setup.cfg | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 94f65bb8..08fc00ec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,6 @@ [bdist_wheel] -# This flag says that the code is written to work on both Python 2 and Python -# 3. If at all possible, it is good practice to do this. If you cannot, you -# will need to generate wheels for each Python version that you support. -universal=1 +# Python 3 only +universal=0 [check-manifest] ignore = From 00c5a27d2aa27237c05e0b8e706ee770785ef30e Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 07:55:15 +0530 Subject: [PATCH 6/9] refactor build commands and update dependencies --- .circleci/config.yml | 4 ++-- pyproject.toml | 4 ++-- tests/requirements-syntax.txt | 1 + tests/requirements.txt | 6 +----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 600cd7b3..25e8b7e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -116,7 +116,7 @@ jobs: - run: name: Build artifact command: | - pipenv run python setup.py bdist_egg bdist_wheel sdist + pipenv run python -m build - run: name: Check artifacts command: pipenv run twine check dist/* @@ -141,7 +141,7 @@ jobs: - run: name: Build artifact command: | - pipenv run python setup.py bdist_egg bdist_wheel sdist + pipenv run python -m build - run: name: Upload to PyPI command: >- diff --git a/pyproject.toml b/pyproject.toml index b0471b7f..f5b211d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" \ No newline at end of file +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/tests/requirements-syntax.txt b/tests/requirements-syntax.txt index 8e66e79c..c6d75176 100644 --- a/tests/requirements-syntax.txt +++ b/tests/requirements-syntax.txt @@ -1,4 +1,5 @@ bashtest +build check-manifest docutils flake8 diff --git a/tests/requirements.txt b/tests/requirements.txt index 6a91ea46..28ca48f2 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,4 @@ +build coveralls mock pytest @@ -6,8 +7,3 @@ pytest-xdist twine # required by twine! bleach<5.0.0 -# readme-renderer (required by twine) 25.0 has removed support for Python 3.4 -readme-renderer<25.0; python_version == '3.4' -# try to solve CI problem -importlib-metadata==1.7.0; python_version == '3.5' -importlib-metadata==1.1.3; python_version == '3.4' From a2ed9ef5bb015c24cb765889654baa0e4204a41b Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 08:03:49 +0530 Subject: [PATCH 7/9] update version to 0.5.0 and update docs with breaking changes --- README.rst | 13 ++++++++++++- changelog.rst | 9 +++++++++ sshtunnel.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ff277335..8d5a64ed 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,18 @@ See also: https://github.com/paramiko/paramiko/blob/master/demos/forward.py Requirements ------------- -* `paramiko`_ +* Python 3.9 or later +* `paramiko`_ 4.0 or later + +**Note**: Version 0.5.0+ requires Python 3.9+ and paramiko 4.0+. If you need Python 2.x or Python 3.4-3.8 support, please use version 0.4.0. + +**Breaking Changes in 0.5.0**: + +- **DSA keys are no longer supported**. DSA was deprecated in OpenSSH 7.0 (2016) and removed from paramiko 4.0 due to security concerns. If you're using DSA keys, you'll need to: + + - Generate new RSA, ECDSA, or Ed25519 keys + - Update your SSH server configuration to use the new keys + - Example: ``ssh-keygen -t rsa -b 4096`` or ``ssh-keygen -t ed25519`` Installation ============ diff --git a/changelog.rst b/changelog.rst index daf3808a..a9a0b5b3 100644 --- a/changelog.rst +++ b/changelog.rst @@ -22,6 +22,15 @@ CONTRIBUTORS CHANGELOG ========= +- v.0.5.0 (BREAKING CHANGES) + + **Drop Python 2.x and Python 3.4-3.8 support** - Now requires Python 3.9+ + + **Drop DSA key support** - Removed support for DSA/DSS keys (deprecated in OpenSSH 7.0, removed in paramiko 4.0) + + **Update to paramiko>=4.0** - Now requires paramiko 4.0 or later + + **Remove Python 2 compatibility code** - Simplified imports and removed legacy code paths + + **Modernize build system** - Use PEP 517 build (`python -m build`) instead of deprecated `setup.py` commands + + **Update documentation build** - Use built-in `sphinx.ext.napoleon` instead of deprecated `sphinxcontrib-napoleon` + + **Update CI/CD** - Test on Python 3.9, 3.10, 3.11, 3.12 + - v.0.X.Y (`V0idk`_, `Bruno Inec`_, `alex3d`_) + Remove the potential deadlock that is associated with threading.Lock (`#231`_) + Remove the hidden modification of the logger in cases where a custom logger is used. (`#250`_) diff --git a/sshtunnel.py b/sshtunnel.py index d27a00bc..503753da 100644 --- a/sshtunnel.py +++ b/sshtunnel.py @@ -32,7 +32,7 @@ input_ = input -__version__ = '0.4.0' +__version__ = '0.5.0' __author__ = 'pahaz' From 65e9af3c64691cf0d1893d85004d937414054e59 Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 08:10:38 +0530 Subject: [PATCH 8/9] fix ci failure --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 8d5a64ed..3d5dfd3c 100644 --- a/README.rst +++ b/README.rst @@ -237,7 +237,7 @@ CLI usage ssh_address Pure python ssh tunnel utils - Version 0.4.0 + Version 0.5.0 positional arguments: ssh_address SSH server IP address (GW for SSH tunnels) @@ -258,7 +258,7 @@ CLI usage Example: -R 10.10.10.10: 10.10.10.10:5900 -L [IP:PORT ...], --local_bind_address [IP:PORT ...] Local bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n - Elements may also be valid UNIX socket domains: + Elements may also be valid UNIX socket domains: /tmp/foo.sock /tmp/bar.sock ... /tmp/baz.sock Equivalent to ssh -LPORT:xxxxxxxxx:xxxx, being the local IP address optional. By default it will listen in all interfaces (0.0.0.0) and choose a random port. From b93a5e91dc164c31dacf7201a9b56c064b0fd1f6 Mon Sep 17 00:00:00 2001 From: kalyanr Date: Wed, 29 Oct 2025 13:05:19 +0530 Subject: [PATCH 9/9] update appveyor config --- appveyor.yml | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ad754699..1fd2a443 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,44 +3,36 @@ platform: x64 environment: matrix: - - PYTHON: "C:\\Python27" - PYTHON_VERSION: "2.7.x" + - PYTHON: "C:\\Python39" + PYTHON_VERSION: "3.9.x" PYTHON_ARCH: "32" - - PYTHON: "C:\\Python27-x64" - PYTHON_VERSION: "2.7.x" + - PYTHON: "C:\\Python39-x64" + PYTHON_VERSION: "3.9.x" PYTHON_ARCH: "64" - - PYTHON: "C:\\Python35" - PYTHON_VERSION: "3.5.x" + - PYTHON: "C:\\Python310" + PYTHON_VERSION: "3.10.x" PYTHON_ARCH: "32" - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5.x" + - PYTHON: "C:\\Python310-x64" + PYTHON_VERSION: "3.10.x" PYTHON_ARCH: "64" - - PYTHON: "C:\\Python36" - PYTHON_VERSION: "3.6.x" + - PYTHON: "C:\\Python311" + PYTHON_VERSION: "3.11.x" PYTHON_ARCH: "32" - - PYTHON: "C:\\Python36-x64" - PYTHON_VERSION: "3.6.x" + - PYTHON: "C:\\Python311-x64" + PYTHON_VERSION: "3.11.x" PYTHON_ARCH: "64" - - PYTHON: "C:\\Python37" - PYTHON_VERSION: "3.7.x" + - PYTHON: "C:\\Python312" + PYTHON_VERSION: "3.12.x" PYTHON_ARCH: "32" - - PYTHON: "C:\\Python37-x64" - PYTHON_VERSION: "3.7.x" - PYTHON_ARCH: "64" - - - PYTHON: "C:\\Python38" - PYTHON_VERSION: "3.8.x" - PYTHON_ARCH: "32" - - - PYTHON: "C:\\Python38-x64" - PYTHON_VERSION: "3.8.x" + - PYTHON: "C:\\Python312-x64" + PYTHON_VERSION: "3.12.x" PYTHON_ARCH: "64" init: - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%" @@ -51,7 +43,7 @@ install: - pip install paramiko - pip install mock pytest pytest-cov pytest-xdist -build: off +build: false test_script: - python setup.py install