mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 04:36:48 +00:00
fix: stop printing secrets during installation
- The generated admin password is written to data/admin_password (mode 0600) instead of being printed, so it no longer ends up in the install log or `docker logs`. If the file cannot be written it is printed as before, so the admin is never locked out. - webvirtcloud.sh no longer echoes the generated SECRET_KEY. - install.sh creates the install log with mode 0600.
This commit is contained in:
parent
cf1cde376b
commit
7b53493800
5 changed files with 72 additions and 14 deletions
15
README.md
15
README.md
|
|
@ -129,7 +129,7 @@ source venv/bin/activate
|
|||
pip install -r conf/requirements.txt
|
||||
|
||||
# 6. Database migrations and static files
|
||||
# (the first migrate creates the "admin" user and prints its generated password)
|
||||
# (the first migrate creates the "admin" user; its password is in data/admin_password)
|
||||
python3 manage.py migrate
|
||||
python3 manage.py collectstatic --noinput
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ source venv/bin/activate
|
|||
pip install -r conf/requirements.txt
|
||||
|
||||
# 5. Database migrations and static files
|
||||
# (the first migrate creates the "admin" user and prints its generated password)
|
||||
# (the first migrate creates the "admin" user; its password is in data/admin_password)
|
||||
python3 manage.py migrate
|
||||
python3 manage.py collectstatic --noinput
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ source venv/bin/activate
|
|||
pip install -r conf/requirements.txt
|
||||
|
||||
# 5. Database migrations and static files
|
||||
# (the first migrate creates the "admin" user and prints its generated password)
|
||||
# (the first migrate creates the "admin" user; its password is in data/admin_password)
|
||||
python3 manage.py migrate
|
||||
python3 manage.py collectstatic --noinput
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ mkdir -p data && python conf/runit/secret_generator.py > data/secret_key && chmo
|
|||
### 4. Migrate and run the dev server
|
||||
|
||||
```bash
|
||||
# The first migrate creates the "admin" user and prints its generated password.
|
||||
# The first migrate creates the "admin" user; its password is in data/admin_password.
|
||||
# Set ADMIN_PASSWORD beforehand to choose it yourself.
|
||||
python manage.py migrate
|
||||
|
||||
|
|
@ -347,12 +347,7 @@ sudo zypper install -y dmidecode && sudo systemctl restart libvirtd
|
|||
|
||||
### Default Credentials
|
||||
|
||||
The first `python3 manage.py migrate` on an empty database creates a superuser named `admin` with a random password and prints it to the console:
|
||||
|
||||
```text
|
||||
* Generated random admin password: <password>
|
||||
* Creating default admin user 'admin'
|
||||
```
|
||||
The first `python3 manage.py migrate` on an empty database creates a superuser named `admin` with a random password, written to `data/admin_password` (mode 0600). In Docker: `docker compose exec webvirtcloud cat data/admin_password`.
|
||||
|
||||
Set `ADMIN_USERNAME` and/or `ADMIN_PASSWORD` in the environment before that first migrate to choose them yourself. Then sign in at `http://<server-ip>`.
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,29 @@ def apply_change_password(sender, **kwargs):
|
|||
print("\033[1m! Don`t forget to remove the option from settings.py\033[0m")
|
||||
|
||||
|
||||
def _store_generated_password(password):
|
||||
"""
|
||||
Write the generated admin password to data/admin_password (mode 0600)
|
||||
instead of printing it, so it does not end up in install logs or
|
||||
`docker logs` (ROADMAP O-02). Falls back to printing if the file cannot
|
||||
be written, so the admin is never locked out.
|
||||
"""
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
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)
|
||||
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")
|
||||
|
||||
|
||||
def create_admin(sender, **kwargs):
|
||||
"""
|
||||
Create initial admin user
|
||||
|
|
@ -54,9 +77,7 @@ def create_admin(sender, **kwargs):
|
|||
admin_pass = "admin"
|
||||
else:
|
||||
admin_pass = secrets.token_urlsafe(16)
|
||||
print(
|
||||
f"\033[1m* \033[93mGenerated random admin password: {admin_pass}\033[0m"
|
||||
)
|
||||
_store_generated_password(admin_pass)
|
||||
|
||||
print(f"\033[1m* \033[92mCreating default admin user '{admin_user}'\033[0m")
|
||||
admin = User.objects.create_superuser(admin_user, None, admin_pass)
|
||||
|
|
|
|||
40
accounts/test_admin_password.py
Normal file
40
accounts/test_admin_password.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
"""ROADMAP O-02: the generated admin password must not be printed to the log."""
|
||||
import io
|
||||
import os
|
||||
import stat
|
||||
import tempfile
|
||||
from contextlib import redirect_stdout
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.contrib.auth import authenticate, get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from accounts.apps import create_admin
|
||||
|
||||
|
||||
class GeneratedAdminPasswordTestCase(TestCase):
|
||||
def _run_create_admin(self, base_dir):
|
||||
get_user_model().objects.all().delete()
|
||||
migration = MagicMock(app_label="accounts")
|
||||
migration.name = "0001_initial"
|
||||
out = io.StringIO()
|
||||
env = {k: v for k, v in os.environ.items() if k not in ("ADMIN_PASSWORD", "ADMIN_USERNAME")}
|
||||
with override_settings(BASE_DIR=Path(base_dir)), patch("sys.argv", ["manage.py", "migrate"]), patch.dict(
|
||||
os.environ, env, clear=True
|
||||
), redirect_stdout(out):
|
||||
create_admin(sender=None, plan=[(migration, False)])
|
||||
return out.getvalue()
|
||||
|
||||
def test_password_is_written_to_a_private_file_not_printed(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
(Path(tmp) / "data").mkdir()
|
||||
output = self._run_create_admin(tmp)
|
||||
password_file = Path(tmp) / "data" / "admin_password"
|
||||
|
||||
self.assertTrue(password_file.exists())
|
||||
password = password_file.read_text().strip()
|
||||
self.assertEqual(stat.S_IMODE(password_file.stat().st_mode), 0o600)
|
||||
self.assertNotIn(password, output)
|
||||
self.assertIn(str(password_file), output)
|
||||
self.assertIsNotNone(authenticate(username="admin", password=password))
|
||||
|
|
@ -84,7 +84,9 @@ 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"
|
||||
fi
|
||||
chmod 600 "$LOG_FILE" 2>/dev/null || true
|
||||
|
||||
echo "* Executing installer ($INSTALLER)..."
|
||||
echo "* Output is logged to $LOG_FILE"
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ install_webvirtcloud () {
|
|||
cp "$APP_PATH/webvirtcloud/settings.py.template" "$APP_PATH/webvirtcloud/settings.py"
|
||||
|
||||
secret_key=$(generate_secret_key)
|
||||
echo "* Secret for Django generated: $secret_key"
|
||||
echo "* Secret for Django generated."
|
||||
tzone_escape="$(echo -n "$tzone"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
|
||||
secret_key_escape="$(echo -n "$secret_key"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
|
||||
novncd_port_escape="$(echo -n "$novncd_port"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue