1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix: write the admin password file without following symlinks

In Docker, migrate runs as root while data/ belongs to www-data, so a
planted data/admin_password symlink would make root overwrite its target.
Remove any existing path first and create the file with
O_CREAT|O_EXCL|O_NOFOLLOW (mode set via fchmod). install.sh's /tmp
fallback log is now created with mktemp instead of a fixed name.
This commit is contained in:
catborise 2026-10-02 23:17:32 +03:00
parent 02dd838a56
commit 40822e7184
3 changed files with 21 additions and 4 deletions

View file

@ -40,10 +40,14 @@ def _store_generated_password(password):
path = os.path.join(str(settings.BASE_DIR), "data", "admin_password")
try:
os.makedirs(os.path.dirname(path), exist_ok=True)
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
# Never write through a pre-existing file or symlink (this runs as
# root in Docker, while data/ belongs to www-data).
if os.path.lexists(path):
os.unlink(path)
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
os.fchmod(fd, 0o600)
with os.fdopen(fd, "w") as f:
f.write(password + "\n")
os.chmod(path, 0o600)
print(f"\033[1m* \033[93mGenerated admin password written to {path}\033[0m")
except OSError:
print(f"\033[1m* \033[93mGenerated random admin password: {password}\033[0m")

View file

@ -38,3 +38,17 @@ class GeneratedAdminPasswordTestCase(TestCase):
self.assertNotIn(password, output)
self.assertIn(str(password_file), output)
self.assertIsNotNone(authenticate(username="admin", password=password))
def test_does_not_follow_a_planted_symlink(self):
with tempfile.TemporaryDirectory() as tmp:
(Path(tmp) / "data").mkdir()
victim = Path(tmp) / "victim"
victim.write_text("original\n")
(Path(tmp) / "data" / "admin_password").symlink_to(victim)
self._run_create_admin(tmp)
password_file = Path(tmp) / "data" / "admin_password"
self.assertEqual(victim.read_text(), "original\n")
self.assertFalse(password_file.is_symlink())
self.assertEqual(stat.S_IMODE(password_file.stat().st_mode), 0o600)

View file

@ -83,8 +83,7 @@ chmod 744 "$INSTALLER"
LOG_FILE="/var/log/webvirtcloud-install.log"
mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
if ! touch "$LOG_FILE" 2>/dev/null; then
LOG_FILE="/tmp/webvirtcloud-install.log"
touch "$LOG_FILE"
LOG_FILE="$(mktemp /tmp/webvirtcloud-install.XXXXXX)"
fi
chmod 600 "$LOG_FILE" 2>/dev/null || true