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