You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by sb...@apache.org on 2026/02/17 15:18:18 UTC

(tooling-trusted-releases) branch ssh-audit-677 created (now a281800d)

This is an automated email from the ASF dual-hosted git repository.

sbp pushed a change to branch ssh-audit-677
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


      at a281800d Use the intersection of algorithms from asyncssh and ssh-audit

This branch includes the following new commits:

     new a281800d Use the intersection of algorithms from asyncssh and ssh-audit

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org


(tooling-trusted-releases) 01/01: Use the intersection of algorithms from asyncssh and ssh-audit

Posted by sb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch ssh-audit-677
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git

commit a281800dd7dddd3af03f93034561b5543b5610ad
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Tue Feb 17 15:17:56 2026 +0000

    Use the intersection of algorithms from asyncssh and ssh-audit
---
 atr/ssh.py                     | 18 ++++++++++++++++++
 atr/util.py                    | 39 +++++++++++++++++++++++----------------
 pyproject.toml                 |  1 +
 requirements-for-pip-audit.txt |  2 ++
 uv.lock                        | 13 ++++++++++++-
 5 files changed, 56 insertions(+), 17 deletions(-)

diff --git a/atr/ssh.py b/atr/ssh.py
index b04031a7..673d2203 100644
--- a/atr/ssh.py
+++ b/atr/ssh.py
@@ -30,6 +30,10 @@ from typing import Any, Final
 import aiofiles
 import aiofiles.os
 import asyncssh
+import asyncssh.encryption as encryption
+import asyncssh.kex as kex
+import asyncssh.mac as mac
+import ssh_audit.builtin_policies as builtin_policies
 
 import atr.attestable as attestable
 import atr.config as config
@@ -43,6 +47,17 @@ import atr.util as util
 
 _CONFIG: Final = config.get()
 
+_SSH_AUDIT_POLICY: Final = builtin_policies.BUILTIN_POLICIES["Hardened OpenSSH Server v9.9 (version 1)"]
+
+_ASYNCSSH_SUPPORTED_ENC: Final = {bytes(a) for a in encryption.get_encryption_algs()}
+_ASYNCSSH_SUPPORTED_KEX: Final = {bytes(a) for a in kex.get_kex_algs()}
+_ASYNCSSH_SUPPORTED_MAC: Final = {bytes(a) for a in mac.get_mac_algs()}
+
+
+_APPROVED_CIPHERS: Final = util.intersect_algs(_SSH_AUDIT_POLICY, "ciphers", _ASYNCSSH_SUPPORTED_ENC)
+_APPROVED_KEX: Final = util.intersect_algs(_SSH_AUDIT_POLICY, "kex", _ASYNCSSH_SUPPORTED_KEX)
+_APPROVED_MACS: Final = util.intersect_algs(_SSH_AUDIT_POLICY, "macs", _ASYNCSSH_SUPPORTED_MAC)
+
 
 class RsyncArgsError(Exception):
     """Exception raised when the rsync arguments are invalid."""
@@ -178,6 +193,9 @@ async def server_start() -> asyncssh.SSHAcceptor:
         host=_CONFIG.SSH_HOST,
         port=_CONFIG.SSH_PORT,
         encoding=None,
+        encryption_algs=_APPROVED_CIPHERS,
+        kex_algs=_APPROVED_KEX,
+        mac_algs=_APPROVED_MACS,
     )
 
     log.info(f"SSH server started on {_CONFIG.SSH_HOST}:{_CONFIG.SSH_PORT}")
diff --git a/atr/util.py b/atr/util.py
index e03ef41e..3104fefa 100644
--- a/atr/util.py
+++ b/atr/util.py
@@ -190,22 +190,6 @@ async def async_temporary_directory(
             log.exception(f"Failed to remove temporary directory {temp_dir_path}")
 
 
-async def atomic_write_file(file_path: pathlib.Path, content: str, encoding: str = "utf-8") -> None:
-    """Atomically write content to a file using a temporary file."""
-    await aiofiles.os.makedirs(file_path.parent, exist_ok=True)
-    temp_path = file_path.parent / f".{file_path.name}.{uuid.uuid4()}.tmp"
-    try:
-        async with aiofiles.open(temp_path, "w", encoding=encoding) as f:
-            await f.write(content)
-            await f.flush()
-            await asyncio.to_thread(os.fsync, f.fileno())
-        await aiofiles.os.rename(temp_path, file_path)
-    except Exception:
-        with contextlib.suppress(FileNotFoundError):
-            await aiofiles.os.remove(temp_path)
-        raise
-
-
 async def atomic_modify_file(
     file_path: pathlib.Path,
     modify: Callable[[str], str],
@@ -227,6 +211,22 @@ async def atomic_modify_file(
         await asyncio.to_thread(os.close, lock_fd)
 
 
+async def atomic_write_file(file_path: pathlib.Path, content: str, encoding: str = "utf-8") -> None:
+    """Atomically write content to a file using a temporary file."""
+    await aiofiles.os.makedirs(file_path.parent, exist_ok=True)
+    temp_path = file_path.parent / f".{file_path.name}.{uuid.uuid4()}.tmp"
+    try:
+        async with aiofiles.open(temp_path, "w", encoding=encoding) as f:
+            await f.write(content)
+            await f.flush()
+            await asyncio.to_thread(os.fsync, f.fileno())
+        await aiofiles.os.rename(temp_path, file_path)
+    except Exception:
+        with contextlib.suppress(FileNotFoundError):
+            await aiofiles.os.remove(temp_path)
+        raise
+
+
 def chmod_directories(path: pathlib.Path, permissions: int = DIRECTORY_PERMISSIONS) -> None:
     # codeql[py/overly-permissive-file]
     os.chmod(path, permissions)
@@ -621,6 +621,13 @@ async def has_files(release: sql.Release) -> bool:
     return False
 
 
+def intersect_algs(policy: dict[str, Any], policy_key: str, supported: set[bytes]) -> list[str]:
+    algs = policy[policy_key]
+    if not isinstance(algs, list):
+        raise TypeError(f"ssh-audit policy '{policy_key}' is not a list")
+    return [a for a in algs if isinstance(a, str) and (a.encode("ascii") in supported)]
+
+
 def is_dev_environment() -> bool:
     conf = config.get()
     for development_host in ("127.0.0.1", "atr", "atr-dev", "localhost.apache.org"):
diff --git a/pyproject.toml b/pyproject.toml
index e9baf395..776ffe55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -50,6 +50,7 @@ dependencies = [
   "rich~=14.0.0",
   "semver>=3.0.4",
   "sqlmodel~=0.0.24",
+  "ssh-audit>=3.3.0",
   "standard-imghdr>=3.13.0",
   "strictyaml>=1.7.3",
   "structlog>=25.5.0",
diff --git a/requirements-for-pip-audit.txt b/requirements-for-pip-audit.txt
index b48a0135..13bb9880 100644
--- a/requirements-for-pip-audit.txt
+++ b/requirements-for-pip-audit.txt
@@ -326,6 +326,8 @@ sqlalchemy==2.0.46
     #   sqlmodel
 sqlmodel==0.0.34
     # via tooling-trusted-releases
+ssh-audit==3.3.0
+    # via tooling-trusted-releases
 standard-imghdr==3.13.0
     # via tooling-trusted-releases
 strictyaml==1.7.3
diff --git a/uv.lock b/uv.lock
index efa461cb..45991c4e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -3,7 +3,7 @@ revision = 3
 requires-python = "==3.13.*"
 
 [options]
-exclude-newer = "2026-02-17T14:20:34Z"
+exclude-newer = "2026-02-17T14:22:10Z"
 
 [[package]]
 name = "aiofiles"
@@ -1837,6 +1837,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/eb/ee/1910f4eee41af4268b0d8cd688a05fb8ea23e9e6c64b8710592df24a8c66/sqlmodel-0.0.34-py3-none-any.whl", hash = "sha256:aeabc8f0de32076a0ed9216e88568459d737fca1e7133bfc6d1c657920789a2d", size = 27445, upload-time = "2026-02-16T19:06:35.709Z" },
 ]
 
+[[package]]
+name = "ssh-audit"
+version = "3.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3b/ec/e89fdfaaa6f08813e1a5cf926bc0dc155761144ebcac57191b4c8001aae3/ssh_audit-3.3.0.tar.gz", hash = "sha256:b76e36ac9844f45d64986c9f293a4b46766a10412dc29fb43bd52d0f6661a5b0", size = 116958, upload-time = "2024-10-15T21:08:28.632Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4f/21/2b3cd275bc3c09fc765691f6c005a6683edf47f47c18d2f9eb78c39ca7e6/ssh_audit-3.3.0-py3-none-any.whl", hash = "sha256:8d2b22b7bb7c20c9d84452c9f13408015ec76eb025379cc70335e5315e4378d6", size = 121789, upload-time = "2024-10-15T21:08:27.182Z" },
+]
+
 [[package]]
 name = "standard-imghdr"
 version = "3.13.0"
@@ -1920,6 +1929,7 @@ dependencies = [
     { name = "rich" },
     { name = "semver" },
     { name = "sqlmodel" },
+    { name = "ssh-audit" },
     { name = "standard-imghdr" },
     { name = "strictyaml" },
     { name = "structlog" },
@@ -1984,6 +1994,7 @@ requires-dist = [
     { name = "rich", specifier = "~=14.0.0" },
     { name = "semver", specifier = ">=3.0.4" },
     { name = "sqlmodel", specifier = "~=0.0.24" },
+    { name = "ssh-audit", specifier = ">=3.3.0" },
     { name = "standard-imghdr", specifier = ">=3.13.0" },
     { name = "strictyaml", specifier = ">=1.7.3" },
     { name = "structlog", specifier = ">=25.5.0" },


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org