fix: never let root read the SSH configuration from stdin
CI / test (3.10) (push) Successful in 23s
CI / test (3.11) (push) Successful in 22s
CI / test (3.12) (push) Successful in 24s
CI / test (3.10) (pull_request) Successful in 23s
CI / test (3.11) (pull_request) Successful in 25s
CI / test (3.12) (pull_request) Successful in 24s
CI / test (3.10) (push) Successful in 23s
CI / test (3.11) (push) Successful in 22s
CI / test (3.12) (push) Successful in 24s
CI / test (3.10) (pull_request) Successful in 23s
CI / test (3.11) (pull_request) Successful in 25s
CI / test (3.12) (pull_request) Successful in 24s
A driver gains root with sudo -S and puts the password in front of stdin. A sudo that does not prompt -- NOPASSWD, a cached timestamp -- leaves it there, and the cat under sudo wrote it into the drop-in as its first line. Seen on the first real run (NetOrk/netork#1027): sshd -t rejected the file and the rollback removed it, but the password was in a 0644 file for a moment and in the error the caller showed. The content now goes without privilege to a mktemp file only the login user can read; root installs that file (mode 644, root-owned) and removes it. The fake host in the tests prepends the password to every privileged stdin, as a non-prompting sudo does. Bumps to 4.5.1. NetOrk/netork#1027, NetOrk/netork#1029
This commit is contained in:
1 parent
d43e15e8db
commit
7ec427c743
3 files changed
+60
-9
No files matched your search
@@ -285,11 +285,28 @@ class OpenSshServerMixin:
|
||||
return result.stdout if result.exit_code == 0 else None
|
||||
|
||||
def _ssh_write(self, path: str, content: str) -> None:
|
||||
script = (
|
||||
f'tmp={quote(path + ".netork-tmp")} && cat > "$tmp" && chmod 644 "$tmp"'
|
||||
f' && mv -f -- "$tmp" {quote(path)} || {{ s=$?; rm -f -- "$tmp"; exit $s; }}'
|
||||
"""Replace *path* with *content*, as root, without root reading stdin.
|
||||
|
||||
A driver gains root with ``sudo -S`` and puts the password in front of
|
||||
stdin. A sudo that does not prompt -- NOPASSWD, a cached timestamp --
|
||||
leaves it there, and a ``cat`` under sudo writes it into the file. So the
|
||||
content goes, without privilege, to a file only the login user can read;
|
||||
root then installs that file (mode 644, owned by root) and removes it.
|
||||
"""
|
||||
staged = self.run_command(
|
||||
'umask 077 && t=$(mktemp /tmp/netork-ssh.XXXXXX) && cat > "$t" && printf %s "$t"',
|
||||
privileged=False,
|
||||
timeout=30,
|
||||
stdin=content.encode(),
|
||||
)
|
||||
tmp = staged.stdout.strip()
|
||||
if staged.exit_code != 0 or not tmp.startswith("/tmp/netork-ssh."):
|
||||
raise RuntimeError(f"could not stage {path}: {(staged.stderr or staged.stdout).strip()}")
|
||||
part = path + ".netork-tmp"
|
||||
result = self._ssh_run(
|
||||
f"install -m 644 -- {quote(tmp)} {quote(part)} && mv -f -- {quote(part)} {quote(path)};"
|
||||
f" s=$?; rm -f -- {quote(tmp)} {quote(part)}; exit $s"
|
||||
)
|
||||
result = self._ssh_run(script, stdin=content.encode())
|
||||
if result.exit_code != 0:
|
||||
raise RuntimeError(f"could not write {path}: {(result.stderr or result.stdout).strip()}")
|
||||
|
||||
|
||||
Reference in new issue
Block a user