Strikte Validierung von Server, Freigabe, Pfaden und Mount-Optionen (Allowlist, gesperrte Zugangsdaten-Schlüssel), Fernet-Schlüssel mit 0600, Ed25519-Schlüsselerzeugung, Schwärzung von Geheimnissen und Dateizugriffe mit Zeitlimit in Daemon-Threads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
"""Fallstrick 7: Schlüsseldatei, Verschlüsselung, Schwärzung, SSH-Schlüssel."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
import paramiko
|
|
import pytest
|
|
from cryptography.fernet import Fernet
|
|
|
|
from netmover.crypto import (
|
|
REDACTED,
|
|
SecretBox,
|
|
SecretError,
|
|
generate_ssh_keypair,
|
|
load_or_create_key,
|
|
redact,
|
|
)
|
|
|
|
|
|
def test_key_created_with_0600_and_reused(tmp_path: Path) -> None:
|
|
path = tmp_path / "lib" / "secret.key"
|
|
key1 = load_or_create_key(path)
|
|
assert stat.S_IMODE(path.stat().st_mode) == 0o600
|
|
assert load_or_create_key(path) == key1
|
|
|
|
|
|
def test_key_mode_is_tightened(tmp_path: Path) -> None:
|
|
path = tmp_path / "secret.key"
|
|
path.write_bytes(Fernet.generate_key())
|
|
path.chmod(0o644)
|
|
load_or_create_key(path)
|
|
assert stat.S_IMODE(path.stat().st_mode) == 0o600
|
|
|
|
|
|
def test_invalid_key_file(tmp_path: Path) -> None:
|
|
path = tmp_path / "secret.key"
|
|
path.write_text("kaputt")
|
|
path.chmod(0o600)
|
|
with pytest.raises(SecretError):
|
|
load_or_create_key(path)
|
|
|
|
|
|
def test_roundtrip_and_wrong_key() -> None:
|
|
box = SecretBox(Fernet.generate_key())
|
|
token = box.encrypt("geheim")
|
|
assert "geheim" not in token
|
|
assert box.decrypt(token) == "geheim"
|
|
with pytest.raises(SecretError, match=r"secret\.key"):
|
|
SecretBox(Fernet.generate_key()).decrypt(token)
|
|
|
|
|
|
def test_redact() -> None:
|
|
text = "mount error: user scanner pw Sommer2026! failed (Sommer2026)"
|
|
out = redact(text, ["Sommer2026!", "Sommer2026", None, ""])
|
|
assert "Sommer2026" not in out
|
|
assert out.count(REDACTED) == 2
|
|
|
|
|
|
def test_ssh_keypair_usable_by_paramiko() -> None:
|
|
private, public = generate_ssh_keypair("netmover@test")
|
|
key = paramiko.Ed25519Key.from_private_key(io.StringIO(private))
|
|
assert public.startswith("ssh-ed25519 ")
|
|
assert public.split()[1] == key.get_base64()
|
|
assert public.endswith(" netmover@test")
|