diff --git a/src/handler/sshkeys.py b/src/handler/sshkeys.py index 8a6cf8d..5528892 100644 --- a/src/handler/sshkeys.py +++ b/src/handler/sshkeys.py @@ -53,6 +53,10 @@ def materialize_private_key(hostname: str, private_key: str) -> str: private_key += "\n" fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, "w") as fh: + # O_CREAT's mode only applies to brand-new files; an existing file keeps + # whatever mode something else gave it (e.g. a k8s fsGroup remount adding + # group rw), and ssh refuses keys readable beyond the owner. + os.fchmod(fd, 0o600) fh.write(private_key) return path diff --git a/tests/test_git_servers.py b/tests/test_git_servers.py index c5f5c76..a42e1b4 100644 --- a/tests/test_git_servers.py +++ b/tests/test_git_servers.py @@ -80,6 +80,18 @@ def test_materialize_private_key_is_0600_under_projects_root(env): assert fh.read() == "KEYDATA\n" +def test_materialize_resets_mode_on_existing_file(env): + # A k8s fsGroup remount (fsGroupChangePolicy: Always) chmods volume files to + # group-rw between pod starts; rematerializing must restore 0600 or ssh + # refuses the key. + from handler import sshkeys + + path = sshkeys.materialize_private_key("github.com", "KEYDATA") + os.chmod(path, 0o660) + path = sshkeys.materialize_private_key("github.com", "KEYDATA") + assert stat.S_IMODE(os.stat(path).st_mode) == 0o600 + + def test_materialize_sanitizes_hostname(env): from handler import sshkeys