From 7435d664920209da5a7529d551df93a29fd857e6 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 25 Sep 2026 22:06:16 +0300 Subject: [PATCH] fix(security): resolve reviewer findings on secret key baking, host symlinks, and injection safety - Prevent baking SECRET_KEY into Docker image during collectstatic - Persist runtime SECRET_KEY in data/secret_key volume or read from environment - Remove sed-based injection of CSRF_TRUSTED_ORIGINS in favor of native os.getenv - Avoid overwriting system python3 symlink on SUSE; auto-detect python 3.10+ binaries - Make /etc/hosts entry in installer idempotent - Add security notice regarding compute node VNC/SPICE port firewall isolation --- Dockerfile | 12 +++------ README.md | 16 ++++++------ conf/runit/10_webvirtcloud_init.sh | 23 ++++++----------- webvirtcloud.sh | 40 ++++++++++++------------------ webvirtcloud/settings.py.template | 9 ++++++- 5 files changed, 42 insertions(+), 58 deletions(-) diff --git a/Dockerfile b/Dockerfile index d1ce88d..6d9f1fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,15 +42,11 @@ RUN python3 -m venv --system-site-packages venv && \ # Copy application source COPY . /srv/webvirtcloud -# Setup build-time settings, run collectstatic, and set permissions +# Run collectstatic with temporary dummy key, then remove temporary settings file RUN . venv/bin/activate && \ - if [ ! -f webvirtcloud/settings.py ]; then \ - cp webvirtcloud/settings.py.template webvirtcloud/settings.py && \ - python3 -c 'import secrets; print(secrets.token_urlsafe(50))' > /tmp/secret_key && \ - sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"$(cat /tmp/secret_key)\"|" webvirtcloud/settings.py && \ - rm -f /tmp/secret_key; \ - fi && \ - python3 manage.py collectstatic --noinput && \ + cp webvirtcloud/settings.py.template webvirtcloud/settings.py && \ + SECRET_KEY="build-dummy-key-only-for-collectstatic" python3 manage.py collectstatic --noinput && \ + rm -f webvirtcloud/settings.py && \ chown -R www-data:www-data /srv/webvirtcloud # Setup Nginx diff --git a/README.md b/README.md index 7cfdfc7..22c81c9 100644 --- a/README.md +++ b/README.md @@ -170,21 +170,17 @@ sudo systemctl restart nginx supervisord # 1. Install system prerequisites (Python 3.11 stack and C bindings) sudo zypper --non-interactive install -y git hostname python311 python311-base python311-devel python311-pip python311-libvirt-python python311-lxml python311-ldap libvirt-devel cyrus-sasl-devel libopenssl-devel gcc pkg-config nginx -# Ensure python3 points to Python 3.11 -sudo ln -sf /usr/bin/python3.11 /usr/bin/python3 -sudo ln -sf /usr/bin/pip3.11 /usr/bin/pip3 - # 2. Clone repository to /srv/webvirtcloud sudo git clone https://github.com/retspen/webvirtcloud /srv/webvirtcloud cd /srv/webvirtcloud # 3. Configure settings cp webvirtcloud/settings.py.template webvirtcloud/settings.py -SECRET_KEY=$(python3 conf/runit/secret_generator.py) +SECRET_KEY=$(python3.11 conf/runit/secret_generator.py) sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"${SECRET_KEY}\"|" webvirtcloud/settings.py # 4. Create virtual environment and install dependencies -python3 -m venv --system-site-packages venv +python3.11 -m venv --system-site-packages venv source venv/bin/activate pip install -r conf/requirements.txt @@ -251,10 +247,9 @@ python manage.py runserver 0.0.0.0:8000 ```bash # 1. Install system prerequisites sudo zypper --non-interactive install -y git hostname python311 python311-base python311-devel python311-pip python311-libvirt-python python311-lxml python311-ldap libvirt-devel cyrus-sasl-devel libopenssl-devel gcc pkg-config -sudo ln -sf /usr/bin/python3.11 /usr/bin/python3 # 2. Create virtual environment with system site packages -python3 -m venv --system-site-packages .venv +python3.11 -m venv --system-site-packages .venv source .venv/bin/activate # 3. Install Python dependencies @@ -263,7 +258,7 @@ pip install -r dev/requirements.txt # 4. Initialize configuration and run local dev server cp webvirtcloud/settings.py.template webvirtcloud/settings.py -sed -i -E 's/SECRET_KEY = .*/SECRET_KEY = "'$(python3 conf/runit/secret_generator.py)'"/' webvirtcloud/settings.py +sed -i -E 's/SECRET_KEY = .*/SECRET_KEY = "'$(python3.11 conf/runit/secret_generator.py)'"/' webvirtcloud/settings.py python manage.py migrate python manage.py runserver 0.0.0.0:8000 ``` @@ -326,6 +321,9 @@ sudo dnf install -y dmidecode && sudo systemctl restart libvirtd sudo zypper install -y dmidecode && sudo systemctl restart libvirtd ``` +> **Security Notice (Compute Node Firewall):** +> Libvirt compute nodes listen on VNC/SPICE ports (`5900`–`65535`) to allow WebVirtCloud to proxy graphical consoles. Ensure your firewall (`ufw`, `firewalld`, or `iptables`) restricts these ports to accept connections **only** from the WebVirtCloud panel IP, and never exposes them directly to public networks. + --- ## Configuration & Operational Notes diff --git a/conf/runit/10_webvirtcloud_init.sh b/conf/runit/10_webvirtcloud_init.sh index 9502525..d44e1d5 100755 --- a/conf/runit/10_webvirtcloud_init.sh +++ b/conf/runit/10_webvirtcloud_init.sh @@ -11,22 +11,13 @@ if [ ! -f "$APP_DIR/webvirtcloud/settings.py" ]; then cp "$APP_DIR/webvirtcloud/settings.py.template" "$APP_DIR/webvirtcloud/settings.py" fi -# Set SECRET_KEY if placeholder exists -if grep -q 'SECRET_KEY = ""' "$APP_DIR/webvirtcloud/settings.py" || grep -q "SECRET_KEY = ''" "$APP_DIR/webvirtcloud/settings.py"; then - KEY="${SECRET_KEY:-$("$APP_DIR/venv/bin/python3" -c 'import secrets; print(secrets.token_urlsafe(50))')}" - sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"${KEY}\"|" "$APP_DIR/webvirtcloud/settings.py" -fi - -# Configure CSRF_TRUSTED_ORIGINS if environment variable is set -if [ -n "$CSRF_TRUSTED_ORIGINS" ]; then - echo "* Setting CSRF_TRUSTED_ORIGINS from environment..." - origins="" - IFS=',' read -ra ADDR <<< "$CSRF_TRUSTED_ORIGINS" - for o in "${ADDR[@]}"; do - clean_o=$(echo "$o" | xargs) - [ -n "$clean_o" ] && origins="${origins}'${clean_o}', " - done - sed -i "s|^CSRF_TRUSTED_ORIGINS = .*|CSRF_TRUSTED_ORIGINS = [ ${origins} ]|" "$APP_DIR/webvirtcloud/settings.py" +# Ensure SECRET_KEY is persisted across container restarts if not supplied via environment +if [ -z "$SECRET_KEY" ]; then + if [ ! -s "$DATA_DIR/secret_key" ]; then + echo "* Generating fresh random SECRET_KEY..." + "$APP_DIR/venv/bin/python3" -c 'import secrets; print(secrets.token_urlsafe(50))' > "$DATA_DIR/secret_key" + chmod 600 "$DATA_DIR/secret_key" + fi fi # Persist SQLite database in DATA_DIR diff --git a/webvirtcloud.sh b/webvirtcloud.sh index 2104c30..60c792e 100755 --- a/webvirtcloud.sh +++ b/webvirtcloud.sh @@ -71,7 +71,7 @@ readonly APP_REPO_URL="${APP_REPO_URL:-https://github.com/retspen/webvirtcloud.g readonly APP_NAME="webvirtcloud" readonly APP_PATH="/srv/$APP_NAME" -readonly PYTHON="python3" +PYTHON="python3" progress () { spin[0]="-" @@ -238,26 +238,26 @@ run_as_app_user () { } check_python () { + # dynamically find python >= 3.10 if default python3 is older + for py_bin in python3.11 python3.12 python3.13 python3.10 python3; do + if command -v "$py_bin" >/dev/null 2>&1; then + if "$py_bin" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)' >/dev/null 2>&1; then + PYTHON="$py_bin" + break + fi + fi + done + # check if python3 is installed. if ! hash "$PYTHON" 2>/dev/null; then echo "Python3 is not installed. Please install Python3 and try again." exit 1 fi - # check if python3 version is grater than 3.10 amd set it as default + # check if python3 version is greater than 3.10 if ! "$PYTHON" -c 'import sys; assert sys.version_info >= (3, 10)' >/dev/null 2>&1; then echo "Your Python version is less than 3.10. This script requires Python 3.10 or greater." - echo "Please install Python 3.10 or greater and set it as the default version." - echo "Use update-alternatives command to set default python version to latest." - echo "For example: sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1" - echo "Then run this script again." - echo "Do not forget to install pip3 and python3-devel for python3.10 or later." - exit 1 - fi - - # check if pip3 is installed - if ! hash pip3 2>/dev/null; then - echo "pip3 is not installed. Please install pip3 and try again." + echo "Please install Python 3.10 or greater (such as python311) and try again." exit 1 fi } @@ -367,7 +367,9 @@ set_hosts () { echo "* Setting up hosts file." local hname hname="$(hostname 2>/dev/null || uname -n)" - echo >> /etc/hosts "127.0.0.1 $hname $fqdn" + if ! grep -q "$fqdn" /etc/hosts 2>/dev/null; then + echo >> /etc/hosts "127.0.0.1 $hname $fqdn" + fi } restart_supervisor () { @@ -750,16 +752,6 @@ case $distro in fi install_packages - # Ensure python3 and pip3 point to python 3.11+ if installed - if command -v python3.11 >/dev/null 2>&1; then - update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 2>/dev/null || true - ln -sf /usr/bin/python3.11 /usr/bin/python3 - if command -v pip3.11 >/dev/null 2>&1; then - update-alternatives --install /usr/bin/pip3 pip3 /usr/bin/pip3.11 1 2>/dev/null || true - ln -sf /usr/bin/pip3.11 /usr/bin/pip3 - fi - fi - set_hosts # Supervisor on SUSE (available as python3-supervisor, supervisor, or via pip) diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 5c2257a..71e84f2 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -17,7 +17,14 @@ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent -SECRET_KEY = "" +SECRET_KEY = os.getenv("SECRET_KEY", "") +if not SECRET_KEY: + secret_file = BASE_DIR / "data" / "secret_key" + if secret_file.exists(): + try: + SECRET_KEY = secret_file.read_text().strip() + except Exception: + pass DEBUG = False