1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-10-18 00:50:07 +00:00

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
This commit is contained in:
catborise 2026-09-25 22:06:16 +03:00
parent b1a843e846
commit 7435d66492
5 changed files with 42 additions and 58 deletions

View file

@ -42,15 +42,11 @@ RUN python3 -m venv --system-site-packages venv && \
# Copy application source # Copy application source
COPY . /srv/webvirtcloud 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 && \ RUN . venv/bin/activate && \
if [ ! -f webvirtcloud/settings.py ]; then \
cp webvirtcloud/settings.py.template webvirtcloud/settings.py && \ cp webvirtcloud/settings.py.template webvirtcloud/settings.py && \
python3 -c 'import secrets; print(secrets.token_urlsafe(50))' > /tmp/secret_key && \ SECRET_KEY="build-dummy-key-only-for-collectstatic" python3 manage.py collectstatic --noinput && \
sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"$(cat /tmp/secret_key)\"|" webvirtcloud/settings.py && \ rm -f webvirtcloud/settings.py && \
rm -f /tmp/secret_key; \
fi && \
python3 manage.py collectstatic --noinput && \
chown -R www-data:www-data /srv/webvirtcloud chown -R www-data:www-data /srv/webvirtcloud
# Setup Nginx # Setup Nginx

View file

@ -170,21 +170,17 @@ sudo systemctl restart nginx supervisord
# 1. Install system prerequisites (Python 3.11 stack and C bindings) # 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 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 # 2. Clone repository to /srv/webvirtcloud
sudo git clone https://github.com/retspen/webvirtcloud /srv/webvirtcloud sudo git clone https://github.com/retspen/webvirtcloud /srv/webvirtcloud
cd /srv/webvirtcloud cd /srv/webvirtcloud
# 3. Configure settings # 3. Configure settings
cp webvirtcloud/settings.py.template webvirtcloud/settings.py 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 sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"${SECRET_KEY}\"|" webvirtcloud/settings.py
# 4. Create virtual environment and install dependencies # 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 source venv/bin/activate
pip install -r conf/requirements.txt pip install -r conf/requirements.txt
@ -251,10 +247,9 @@ python manage.py runserver 0.0.0.0:8000
```bash ```bash
# 1. Install system prerequisites # 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 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 # 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 source .venv/bin/activate
# 3. Install Python dependencies # 3. Install Python dependencies
@ -263,7 +258,7 @@ pip install -r dev/requirements.txt
# 4. Initialize configuration and run local dev server # 4. Initialize configuration and run local dev server
cp webvirtcloud/settings.py.template webvirtcloud/settings.py 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 migrate
python manage.py runserver 0.0.0.0:8000 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 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 ## Configuration & Operational Notes

View file

@ -11,22 +11,13 @@ if [ ! -f "$APP_DIR/webvirtcloud/settings.py" ]; then
cp "$APP_DIR/webvirtcloud/settings.py.template" "$APP_DIR/webvirtcloud/settings.py" cp "$APP_DIR/webvirtcloud/settings.py.template" "$APP_DIR/webvirtcloud/settings.py"
fi fi
# Set SECRET_KEY if placeholder exists # Ensure SECRET_KEY is persisted across container restarts if not supplied via environment
if grep -q 'SECRET_KEY = ""' "$APP_DIR/webvirtcloud/settings.py" || grep -q "SECRET_KEY = ''" "$APP_DIR/webvirtcloud/settings.py"; then if [ -z "$SECRET_KEY" ]; then
KEY="${SECRET_KEY:-$("$APP_DIR/venv/bin/python3" -c 'import secrets; print(secrets.token_urlsafe(50))')}" if [ ! -s "$DATA_DIR/secret_key" ]; then
sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"${KEY}\"|" "$APP_DIR/webvirtcloud/settings.py" echo "* Generating fresh random SECRET_KEY..."
fi "$APP_DIR/venv/bin/python3" -c 'import secrets; print(secrets.token_urlsafe(50))' > "$DATA_DIR/secret_key"
chmod 600 "$DATA_DIR/secret_key"
# Configure CSRF_TRUSTED_ORIGINS if environment variable is set fi
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"
fi fi
# Persist SQLite database in DATA_DIR # Persist SQLite database in DATA_DIR

View file

@ -71,7 +71,7 @@ readonly APP_REPO_URL="${APP_REPO_URL:-https://github.com/retspen/webvirtcloud.g
readonly APP_NAME="webvirtcloud" readonly APP_NAME="webvirtcloud"
readonly APP_PATH="/srv/$APP_NAME" readonly APP_PATH="/srv/$APP_NAME"
readonly PYTHON="python3" PYTHON="python3"
progress () { progress () {
spin[0]="-" spin[0]="-"
@ -238,26 +238,26 @@ run_as_app_user () {
} }
check_python () { 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. # check if python3 is installed.
if ! hash "$PYTHON" 2>/dev/null; then if ! hash "$PYTHON" 2>/dev/null; then
echo "Python3 is not installed. Please install Python3 and try again." echo "Python3 is not installed. Please install Python3 and try again."
exit 1 exit 1
fi 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 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 "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 "Please install Python 3.10 or greater (such as python311) and try again."
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."
exit 1 exit 1
fi fi
} }
@ -367,7 +367,9 @@ set_hosts () {
echo "* Setting up hosts file." echo "* Setting up hosts file."
local hname local hname
hname="$(hostname 2>/dev/null || uname -n)" hname="$(hostname 2>/dev/null || uname -n)"
if ! grep -q "$fqdn" /etc/hosts 2>/dev/null; then
echo >> /etc/hosts "127.0.0.1 $hname $fqdn" echo >> /etc/hosts "127.0.0.1 $hname $fqdn"
fi
} }
restart_supervisor () { restart_supervisor () {
@ -750,16 +752,6 @@ case $distro in
fi fi
install_packages 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 set_hosts
# Supervisor on SUSE (available as python3-supervisor, supervisor, or via pip) # Supervisor on SUSE (available as python3-supervisor, supervisor, or via pip)

View file

@ -17,7 +17,14 @@ from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent 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 DEBUG = False