#!/bin/bash # Wine CSP Service Entrypoint # # Initializes Wine environment and starts the WineCspService under Wine. # For TEST VECTOR GENERATION ONLY - not for production signing. set -euo pipefail # ------------------------------------------------------------------------------ # Configuration # ------------------------------------------------------------------------------ WINE_CSP_PORT="${WINE_CSP_PORT:-5099}" WINE_CSP_MODE="${WINE_CSP_MODE:-limited}" WINE_CSP_INSTALLER_PATH="${WINE_CSP_INSTALLER_PATH:-/opt/cryptopro/csp-installer.msi}" WINE_CSP_LOG_LEVEL="${WINE_CSP_LOG_LEVEL:-Information}" WINE_PREFIX="${WINEPREFIX:-$HOME/.wine}" DISPLAY="${DISPLAY:-:99}" CSP_DOWNLOAD_MARKER="${WINE_CSP_INSTALLER_PATH}.downloaded" CRYPTOPRO_DOWNLOAD_DIR="${CRYPTOPRO_DOWNLOAD_DIR:-/opt/cryptopro/downloads}" CRYPTOPRO_DOWNLOAD_MARKER="${CRYPTOPRO_DOWNLOAD_MARKER:-${CRYPTOPRO_DOWNLOAD_DIR}/.downloaded}" CRYPTOPRO_FETCH_ON_START="${CRYPTOPRO_FETCH_ON_START:-1}" # Marker files CSP_INSTALLED_MARKER="${WINE_PREFIX}/.csp_installed" WINE_INITIALIZED_MARKER="${WINE_PREFIX}/.wine_initialized" # Log prefix for structured logging log() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] [entrypoint] $*" } log_error() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] [entrypoint] [ERROR] $*" >&2 } # ------------------------------------------------------------------------------ # Virtual Framebuffer Management # ------------------------------------------------------------------------------ start_xvfb() { if ! pgrep -x Xvfb > /dev/null; then log "Starting Xvfb virtual framebuffer on display ${DISPLAY}" Xvfb "${DISPLAY}" -screen 0 1024x768x24 & sleep 2 fi } stop_xvfb() { if pgrep -x Xvfb > /dev/null; then log "Stopping Xvfb" pkill -x Xvfb || true fi } # ------------------------------------------------------------------------------ # Wine Initialization # ------------------------------------------------------------------------------ initialize_wine() { if [[ -f "${WINE_INITIALIZED_MARKER}" ]]; then log "Wine prefix already initialized" return 0 fi log "Initializing Wine prefix at ${WINE_PREFIX}" start_xvfb # Initialize Wine prefix wine64 wineboot --init 2>/dev/null || true wineserver --wait # Set Windows version for CryptoPro compatibility wine64 reg add "HKCU\\Software\\Wine\\Version" /v Windows /d "win10" /f 2>/dev/null || true wineserver --wait # Create marker touch "${WINE_INITIALIZED_MARKER}" log "Wine prefix initialized successfully" } # ------------------------------------------------------------------------------ # CryptoPro Linux Downloads (Playwright-driven) # ------------------------------------------------------------------------------ download_linux_packages() { if [[ "${CRYPTOPRO_FETCH_ON_START}" == "0" ]]; then log "Skipping CryptoPro Linux fetch (CRYPTOPRO_FETCH_ON_START=0)" return 0 fi if [[ -f "${CRYPTOPRO_DOWNLOAD_MARKER}" && "${CRYPTOPRO_FORCE_DOWNLOAD:-0}" != "1" ]]; then log "CryptoPro download marker present at ${CRYPTOPRO_DOWNLOAD_MARKER}; skipping fetch" return 0 fi log "Ensuring CryptoPro Linux packages via Playwright (dry-run unless CRYPTOPRO_DRY_RUN=0)" export CRYPTOPRO_DOWNLOAD_MARKER export CRYPTOPRO_OUTPUT_DIR="${CRYPTOPRO_DOWNLOAD_DIR}" export CRYPTOPRO_UNPACK="${CRYPTOPRO_UNPACK:-1}" if /usr/local/bin/download-cryptopro.sh; then if [[ "${CRYPTOPRO_DRY_RUN:-1}" != "0" ]]; then log "CryptoPro downloader ran in dry-run mode; set CRYPTOPRO_DRY_RUN=0 to fetch binaries" else [[ -f "${CRYPTOPRO_DOWNLOAD_MARKER}" ]] || touch "${CRYPTOPRO_DOWNLOAD_MARKER}" log "CryptoPro Linux artifacts staged in ${CRYPTOPRO_DOWNLOAD_DIR}" fi else log_error "CryptoPro Playwright download failed" fi } # ------------------------------------------------------------------------------ # CryptoPro CSP Installation # ------------------------------------------------------------------------------ install_cryptopro() { # Check if already installed if [[ -f "${CSP_INSTALLED_MARKER}" ]]; then log "CryptoPro CSP already installed" return 0 fi # Attempt to download installer if missing (dry-run by default) if [[ ! -f "${WINE_CSP_INSTALLER_PATH}" ]]; then log "CryptoPro CSP installer not found at ${WINE_CSP_INSTALLER_PATH}; attempting crawl/download (dry-run unless CRYPTOPRO_DRY_RUN=0)." if ! CRYPTOPRO_OUTPUT="${WINE_CSP_INSTALLER_PATH}" /usr/local/bin/fetch-cryptopro.py; then log_error "CryptoPro CSP download failed; continuing without CSP (limited mode)" return 0 fi fi # Check if installer is available if [[ ! -f "${WINE_CSP_INSTALLER_PATH}" ]]; then log "CryptoPro CSP installer not found at ${WINE_CSP_INSTALLER_PATH}" log "Service will run in limited mode without CSP" return 0 fi log "Installing CryptoPro CSP from ${WINE_CSP_INSTALLER_PATH}" start_xvfb # Run the CSP installation script if /usr/local/bin/install-csp.sh; then touch "${CSP_INSTALLED_MARKER}" log "CryptoPro CSP installed successfully" else log_error "CryptoPro CSP installation failed" return 1 fi } # ------------------------------------------------------------------------------ # Service Configuration # ------------------------------------------------------------------------------ configure_service() { log "Configuring Wine CSP service" log " Mode: ${WINE_CSP_MODE}" log " Port: ${WINE_CSP_PORT}" log " Log Level: ${WINE_CSP_LOG_LEVEL}" # Configure Wine debug output based on log level case "${WINE_CSP_LOG_LEVEL}" in Trace|Debug) export WINEDEBUG="warn+all" ;; Information) export WINEDEBUG="-all" ;; Warning|Error|Critical) export WINEDEBUG="-all" ;; *) export WINEDEBUG="-all" ;; esac # Set ASP.NET Core environment export ASPNETCORE_URLS="http://+:${WINE_CSP_PORT}" export ASPNETCORE_ENVIRONMENT="${ASPNETCORE_ENVIRONMENT:-Production}" export Logging__LogLevel__Default="${WINE_CSP_LOG_LEVEL}" # Check if CSP is available if [[ -f "${CSP_INSTALLED_MARKER}" ]]; then export WINE_CSP_CSP_AVAILABLE="true" log "CryptoPro CSP is available" else export WINE_CSP_CSP_AVAILABLE="false" log "Running without CryptoPro CSP (limited mode)" fi } # ------------------------------------------------------------------------------ # Startup Validation # ------------------------------------------------------------------------------ validate_environment() { log "Validating environment" # Check Wine is available if ! command -v wine64 &> /dev/null; then log_error "wine64 not found in PATH" exit 1 fi # Check application exists if [[ ! -f "/app/WineCspService.exe" ]]; then log_error "WineCspService.exe not found at /app/" exit 1 fi # Verify Wine prefix is writable if [[ ! -w "${WINE_PREFIX}" ]]; then log_error "Wine prefix ${WINE_PREFIX} is not writable" exit 1 fi log "Environment validation passed" } # ------------------------------------------------------------------------------ # Signal Handlers # ------------------------------------------------------------------------------ cleanup() { log "Received shutdown signal, cleaning up..." # Stop Wine server gracefully wineserver -k 15 2>/dev/null || true sleep 2 wineserver -k 9 2>/dev/null || true stop_xvfb log "Cleanup complete" exit 0 } trap cleanup SIGTERM SIGINT SIGQUIT # ------------------------------------------------------------------------------ # Main Entry Point # ------------------------------------------------------------------------------ main() { log "==========================================" log "Wine CSP Service Entrypoint" log "==========================================" log "WARNING: For TEST VECTOR GENERATION ONLY" log "==========================================" validate_environment download_linux_packages initialize_wine # Only attempt CSP installation in full mode if [[ "${WINE_CSP_MODE}" == "full" ]]; then install_cryptopro fi configure_service # Start Xvfb for the main process start_xvfb log "Starting WineCspService..." log "Listening on port ${WINE_CSP_PORT}" # Execute the command passed to the container (or default) if [[ $# -gt 0 ]]; then exec "$@" else exec wine64 /app/WineCspService.exe fi } main "$@"