174 lines
5.4 KiB
Text
174 lines
5.4 KiB
Text
|
|
# Multi-stage build for security and optimization
|
||
|
|
FROM node:20-slim AS backend-builder
|
||
|
|
|
||
|
|
# Install build dependencies for native modules (sqlite3, bcrypt)
|
||
|
|
RUN apt-get update && apt-get install -y \
|
||
|
|
python3 \
|
||
|
|
make \
|
||
|
|
g++ \
|
||
|
|
gcc \
|
||
|
|
&& rm -rf /var/lib/apt/lists/*
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
|
||
|
|
# Copy backend dependencies FIRST (better layer caching)
|
||
|
|
COPY backend/package*.json ./backend/
|
||
|
|
RUN cd backend && npm ci --only=production
|
||
|
|
|
||
|
|
# Copy ONLY necessary backend source files
|
||
|
|
COPY backend/server.js backend/healthcheck.js ./backend/
|
||
|
|
COPY backend/database ./backend/database
|
||
|
|
COPY backend/middleware ./backend/middleware
|
||
|
|
COPY backend/routes ./backend/routes
|
||
|
|
COPY backend/utils ./backend/utils
|
||
|
|
COPY backend/jobs ./backend/jobs
|
||
|
|
|
||
|
|
# Build frontend
|
||
|
|
FROM node:20-slim AS frontend-builder
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
|
||
|
|
# Copy frontend dependencies FIRST (better layer caching)
|
||
|
|
COPY frontend/package*.json ./frontend/
|
||
|
|
RUN cd frontend && npm ci
|
||
|
|
|
||
|
|
# Copy ONLY necessary frontend source files
|
||
|
|
COPY frontend/index.html frontend/vite.config.js ./frontend/
|
||
|
|
COPY frontend/public ./frontend/public
|
||
|
|
COPY frontend/src ./frontend/src
|
||
|
|
|
||
|
|
# Build frontend application
|
||
|
|
RUN cd frontend && npm run build
|
||
|
|
|
||
|
|
# Final production image
|
||
|
|
FROM node:20-slim
|
||
|
|
|
||
|
|
# Combine apt-get operations and use --no-install-recommends to reduce size and time
|
||
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||
|
|
ffmpeg \
|
||
|
|
python3 \
|
||
|
|
python3-pip \
|
||
|
|
openvpn \
|
||
|
|
wireguard-tools \
|
||
|
|
openresolv \
|
||
|
|
curl \
|
||
|
|
ca-certificates \
|
||
|
|
iptables \
|
||
|
|
iproute2 \
|
||
|
|
imagemagick \
|
||
|
|
procps \
|
||
|
|
&& pip3 install --no-cache-dir --break-system-packages streamlink yt-dlp \
|
||
|
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||
|
|
|
||
|
|
# Create VPN script as a separate file instead of echo commands
|
||
|
|
COPY <<'EOF' /etc/openvpn/update-resolv-conf
|
||
|
|
#!/bin/bash
|
||
|
|
# Update DNS when VPN connects/disconnects
|
||
|
|
case $script_type in
|
||
|
|
up)
|
||
|
|
echo "[VPN] Setting up DNS..."
|
||
|
|
echo "# VPN DNS - DO NOT MODIFY" > /etc/resolv.conf
|
||
|
|
for opt in ${!foreign_option_*}; do
|
||
|
|
if echo ${!opt} | grep -q "DNS"; then
|
||
|
|
DNS_IP=$(echo ${!opt} | awk '{print $3}')
|
||
|
|
echo "nameserver $DNS_IP" >> /etc/resolv.conf
|
||
|
|
echo "[VPN] Added DNS: $DNS_IP"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
if ! grep -q "nameserver" /etc/resolv.conf; then
|
||
|
|
echo "nameserver 1.1.1.1" >> /etc/resolv.conf
|
||
|
|
echo "[VPN] Using fallback DNS"
|
||
|
|
fi
|
||
|
|
;;
|
||
|
|
down)
|
||
|
|
echo "[VPN] Restoring default DNS..."
|
||
|
|
echo "nameserver 8.8.8.8" > /etc/resolv.conf
|
||
|
|
echo "nameserver 1.1.1.1" >> /etc/resolv.conf
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
EOF
|
||
|
|
|
||
|
|
RUN chmod +x /etc/openvpn/update-resolv-conf
|
||
|
|
|
||
|
|
# Create app user for security (non-root)
|
||
|
|
RUN groupadd -g 1001 appgroup && \
|
||
|
|
useradd -u 1001 -g appgroup -m -s /bin/bash appuser
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
|
||
|
|
# Create necessary directories with proper permissions
|
||
|
|
RUN mkdir -p /app/data/playlists \
|
||
|
|
/app/data/recordings \
|
||
|
|
/app/data/logos \
|
||
|
|
/app/data/uploads \
|
||
|
|
/app/data/m3u-files \
|
||
|
|
/app/uploads/logos \
|
||
|
|
/app/logs \
|
||
|
|
/app/pwa-assets \
|
||
|
|
/app/android-assets && \
|
||
|
|
chown -R appuser:appgroup /app
|
||
|
|
|
||
|
|
# Copy backend from builder
|
||
|
|
COPY --from=backend-builder --chown=appuser:appgroup /app/backend ./backend
|
||
|
|
|
||
|
|
# Copy built frontend from builder
|
||
|
|
COPY --from=frontend-builder --chown=appuser:appgroup /app/frontend/dist ./frontend/dist
|
||
|
|
|
||
|
|
# Copy desktop app update files
|
||
|
|
COPY --chown=appuser:appgroup desktop-app/dist ./updates
|
||
|
|
|
||
|
|
# Copy PWA assets and platform-specific resources
|
||
|
|
COPY --chown=appuser:appgroup frontend/public/manifest.json ./pwa-assets/manifest.json
|
||
|
|
COPY --chown=appuser:appgroup frontend/public/service-worker.js ./pwa-assets/service-worker.js
|
||
|
|
COPY --chown=appuser:appgroup frontend/public/icons ./pwa-assets/icons
|
||
|
|
|
||
|
|
# Create VPN directories with proper permissions
|
||
|
|
RUN mkdir -p /etc/wireguard /etc/openvpn/configs && \
|
||
|
|
chmod 755 /etc/wireguard /etc/openvpn/configs
|
||
|
|
|
||
|
|
# Create startup script using heredoc (much faster than echo)
|
||
|
|
COPY <<'EOF' /app/start.sh
|
||
|
|
#!/bin/sh
|
||
|
|
echo "Initializing application..."
|
||
|
|
# Fix data directory permissions for root access
|
||
|
|
chown -R root:root /app/data 2>/dev/null || true
|
||
|
|
# Ensure database files are writable by root
|
||
|
|
chmod 644 /app/data/streamflow.db* 2>/dev/null || true
|
||
|
|
chown root:root /app/data/streamflow.db* 2>/dev/null || true
|
||
|
|
# Ensure VPN directories are accessible
|
||
|
|
chmod 755 /etc/wireguard /etc/openvpn/configs 2>/dev/null || true
|
||
|
|
# Create all data subdirectories (including log-archives for CWE-53)
|
||
|
|
mkdir -p /app/data/backups /app/data/logos /app/data/logo-cache /app/data/m3u-files /app/data/playlists /app/data/recordings /app/data/uploads /app/data/log-archives
|
||
|
|
# Ensure restrictive permissions on log directories (CWE-53)
|
||
|
|
chmod 700 /app/logs 2>/dev/null || true
|
||
|
|
chmod 700 /app/data/log-archives 2>/dev/null || true
|
||
|
|
# Set ownership
|
||
|
|
chown -R appuser:appgroup /app/data 2>/dev/null || true
|
||
|
|
# Start update server in background
|
||
|
|
python3 -m http.server 9000 --directory /app/updates > /tmp/update-server.log 2>&1 &
|
||
|
|
UPDATE_SERVER_PID=$!
|
||
|
|
sleep 1
|
||
|
|
if kill -0 $UPDATE_SERVER_PID 2>/dev/null; then
|
||
|
|
echo "Update server started successfully (PID: $UPDATE_SERVER_PID) on port 9000"
|
||
|
|
else
|
||
|
|
echo "Warning: Update server failed to start"
|
||
|
|
fi
|
||
|
|
echo "Starting Node.js application..."
|
||
|
|
# Run as root to allow VPN operations (wg-quick, openvpn require root)
|
||
|
|
cd /app && exec node backend/server.js
|
||
|
|
EOF
|
||
|
|
|
||
|
|
RUN chmod +x /app/start.sh
|
||
|
|
|
||
|
|
USER root
|
||
|
|
|
||
|
|
# Expose ports
|
||
|
|
EXPOSE 12345 9000
|
||
|
|
|
||
|
|
# Health check - disabled due to segfault issues
|
||
|
|
# HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||
|
|
# CMD node backend/healthcheck.js || exit 1
|
||
|
|
|
||
|
|
# Start application
|
||
|
|
CMD ["/app/start.sh"]
|