├── supervisor └── conf.d │ ├── fluxbox.conf │ ├── xvfb.conf │ ├── x11vnc.conf │ └── novnc.conf ├── supervisord.conf ├── LICENSE ├── startup.sh ├── download_gecko_and_mono.sh ├── .github └── workflows │ └── docker-publish.yml ├── Dockerfile └── README.md /supervisor/conf.d/fluxbox.conf: -------------------------------------------------------------------------------- 1 | [program:fluxbox] 2 | command=fluxbox 3 | autostart=true 4 | autorestart=true 5 | priority=30 6 | stdout_logfile=/var/log/supervisord/fluxbox.log 7 | stderr_logfile=/var/log/supervisord/fluxbox_error.log 8 | -------------------------------------------------------------------------------- /supervisor/conf.d/xvfb.conf: -------------------------------------------------------------------------------- 1 | [program:xvfb] 2 | command=Xvfb :1 -screen 0 1024x768x16 3 | autostart=true 4 | autorestart=true 5 | priority=10 6 | stdout_logfile=/var/log/supervisord/xvfb.log 7 | stderr_logfile=/var/log/supervisord/xvfb_error.log 8 | -------------------------------------------------------------------------------- /supervisor/conf.d/x11vnc.conf: -------------------------------------------------------------------------------- 1 | [program:x11vnc] 2 | command=x11vnc -display :1 -rfbport %(ENV_VNC_PORT)s -rfbauth /root/.vnc/passwd 3 | autostart=true 4 | autorestart=true 5 | priority=20 6 | stdout_logfile=/var/log/supervisord/x11vnc.log 7 | stderr_logfile=/var/log/supervisord/x11vnc_error.log 8 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/var/log/supervisord/supervisord.log 4 | logfile_maxbytes=50MB 5 | logfile_backups=10 6 | loglevel=info 7 | 8 | ; Include all configurations from conf.d directory 9 | [include] 10 | files = /etc/supervisor/conf.d/*.conf 11 | -------------------------------------------------------------------------------- /supervisor/conf.d/novnc.conf: -------------------------------------------------------------------------------- 1 | [program:novnc] 2 | command=/opt/novnc/utils/novnc_proxy --vnc localhost:%(ENV_VNC_PORT)s --listen 0.0.0.0:%(ENV_NOVNC_PORT)s 3 | autostart=true 4 | autorestart=true 5 | priority=40 6 | stdout_logfile=/var/log/supervisord/novnc.log 7 | stderr_logfile=/var/log/supervisord/novnc_error.log 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 p0ise 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VNC_PASSWD_FILE="/root/.vnc/passwd" 4 | 5 | # 如果传入了 VNC_PASSWORD 环境变量,则更新密码 6 | if [[ -n "$VNC_PASSWORD" ]]; then 7 | echo "Custom VNC password provided. Updating password..." 8 | mkdir -p /root/.vnc 9 | x11vnc -storepasswd "$VNC_PASSWORD" "$VNC_PASSWD_FILE" 10 | if [[ $? -ne 0 ]]; then 11 | echo "Error: Failed to set custom VNC password." 12 | exit 1 13 | fi 14 | 15 | # 如果没有传入 VNC_PASSWORD 且没有现有密码文件,则生成随机密码 16 | elif [[ ! -f "$VNC_PASSWD_FILE" ]]; then 17 | echo "No VNC password provided and no existing password file found. Generating a random password..." 18 | VNC_PASSWORD=$(openssl rand -base64 12) # 生成随机密码 19 | echo "Generated VNC password: $VNC_PASSWORD" 20 | mkdir -p /root/.vnc 21 | x11vnc -storepasswd "$VNC_PASSWORD" "$VNC_PASSWD_FILE" 22 | if [[ $? -ne 0 ]]; then 23 | echo "Error: Failed to set generated VNC password." 24 | exit 1 25 | fi 26 | else 27 | echo "Using existing VNC password file." 28 | fi 29 | 30 | # 启动 Supervisor 并输出日志路径 31 | echo "Starting supervisord to manage services..." 32 | exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf 33 | -------------------------------------------------------------------------------- /download_gecko_and_mono.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Scrapes the Wine source code for versions of mono and gecko to download for a given version of Wine 4 | 5 | get_hrefs () { 6 | local url="$1" 7 | local regexp="$2" 8 | 9 | wget -q -O- "${url}" | sed -E "s/>\n.*|\1|p" | uniq 10 | } 11 | 12 | get_app_ver () { 13 | local app="${1^^}" # Convert to uppercase 14 | local url="https://raw.githubusercontent.com/wine-mirror/wine/wine-${WINE_VER}/dlls/appwiz.cpl/addons.c" 15 | 16 | wget -q -O- "${url}" | grep -E "^#define ${app}_VERSION\s" | awk -F\" '{print $2}' 17 | } 18 | 19 | WINE_VER="$1" 20 | 21 | if [ -z "${WINE_VER}" ]; then 22 | echo "Please specify the version of wine that requires gecko and mono installers" 23 | echo "e.g." 24 | echo " $0 5.0.1" 25 | exit 1 26 | fi 27 | 28 | for APP in "gecko" "mono"; do 29 | # Get the app version required from wine source code 30 | APP_VER=$(get_app_ver "${APP}") 31 | 32 | # Get the list of files to download 33 | APP_URL="http://dl.winehq.org/wine/wine-${APP}/${APP_VER}/" 34 | mapfile -t FILES < <(get_hrefs "${APP_URL}" ".*\.msi") 35 | 36 | # Download the files 37 | [ ! -d "/usr/share/wine/${APP}" ] && mkdir -p "/usr/share/wine/${APP}" 38 | for FILE in "${FILES[@]}"; do 39 | echo "Downloading '${FILE}'" 40 | wget -nv -O "/usr/share/wine/${APP}/${FILE}" "${APP_URL}${FILE}" 41 | done 42 | done 43 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build and Publish 2 | 3 | on: 4 | schedule: 5 | - cron: '45 2 * * *' # 定时触发,GMT 时间 2:45 AM 6 | push: 7 | branches: [ "main", "develop" ] 8 | tags: [ 'v*.*.*' ] # 以版本号标签发布 9 | pull_request: 10 | branches: [ "main", "develop" ] 11 | 12 | env: 13 | REGISTRY: docker.io 14 | IMAGE_NAME: invelop/wine-novnc # Docker Hub 镜像名称 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | # 检出仓库代码 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | # 设置 Docker Buildx 以支持多平台构建 29 | - name: Set up Docker Buildx 30 | uses: docker/setup-buildx-action@v3 31 | 32 | # 登录 Docker Hub(跳过 PR 构建) 33 | - name: Log into Docker Hub 34 | if: github.event_name != 'pull_request' 35 | uses: docker/login-action@v2 36 | with: 37 | registry: ${{ env.REGISTRY }} 38 | username: ${{ secrets.DOCKER_USERNAME }} 39 | password: ${{ secrets.DOCKER_PASSWORD }} 40 | 41 | # 提取 Docker 镜像的元数据(标签和标签) 42 | - name: Extract Docker metadata 43 | id: meta 44 | uses: docker/metadata-action@v5 45 | with: 46 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 47 | 48 | # 使用 Buildx 构建并推送 Docker 镜像 49 | - name: Build and push Docker image 50 | uses: docker/build-push-action@v5 51 | with: 52 | context: . 53 | push: ${{ github.event_name != 'pull_request' }} 54 | tags: ${{ steps.meta.outputs.tags }} 55 | labels: ${{ steps.meta.outputs.labels }} 56 | cache-from: type=gha 57 | cache-to: type=gha,mode=max 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用 Ubuntu 22.04 基础镜像 2 | FROM ubuntu:22.04 3 | 4 | # 设置环境变量 5 | ENV DEBIAN_FRONTEND=noninteractive \ 6 | DISPLAY=:1 \ 7 | VNC_PORT=5901 \ 8 | NOVNC_PORT=6080 9 | 10 | # 配置 i386 架构并添加 WineHQ 源、安装所需依赖和工具,清理缓存 11 | RUN dpkg --add-architecture i386 && \ 12 | apt-get update && \ 13 | apt-get install -y --no-install-recommends \ 14 | software-properties-common wget curl supervisor x11vnc xvfb xterm fluxbox python3 ca-certificates && \ 15 | . /etc/os-release && CODENAME=${UBUNTU_CODENAME:-${VERSION_CODENAME}} && \ 16 | mkdir -pm755 /etc/apt/keyrings && \ 17 | wget -q -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key && \ 18 | wget -q -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/${CODENAME}/winehq-${CODENAME}.sources && \ 19 | apt-get update && \ 20 | apt-get install -y --install-recommends winehq-stable && \ 21 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 22 | 23 | # 安装 winetricks 24 | RUN wget -q -O /usr/bin/winetricks https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks && \ 25 | chmod +x /usr/bin/winetricks 26 | 27 | # 复制并执行下载 Gecko 和 Mono 的脚本 28 | COPY download_gecko_and_mono.sh /root/download_gecko_and_mono.sh 29 | RUN chmod +x /root/download_gecko_and_mono.sh && \ 30 | /root/download_gecko_and_mono.sh "$(wine --version | sed -E 's/^wine-//')" && \ 31 | rm -f /root/download_gecko_and_mono.sh 32 | 33 | # 安装 noVNC 和 websockify,设置 noVNC 的默认页面 34 | RUN mkdir -p /opt/novnc/utils/websockify && \ 35 | curl -sL https://github.com/novnc/noVNC/archive/v1.5.0.tar.gz | tar xz -C /opt/novnc --strip-components=1 && \ 36 | curl -sL https://github.com/novnc/websockify/archive/v0.12.0.tar.gz | tar xz -C /opt/novnc/utils/websockify --strip-components=1 && \ 37 | ln -s /opt/novnc/vnc.html /opt/novnc/index.html 38 | 39 | # 创建 supervisor 配置目录和日志目录并复制独立配置文件 40 | RUN mkdir -p /etc/supervisor/conf.d /var/log/supervisord 41 | COPY supervisord.conf /etc/supervisor/supervisord.conf 42 | COPY supervisor/conf.d/* /etc/supervisor/conf.d/ 43 | 44 | # 添加启动脚本 45 | COPY startup.sh /opt/startup.sh 46 | RUN chmod +x /opt/startup.sh 47 | 48 | # 暴露端口 49 | EXPOSE ${VNC_PORT} ${NOVNC_PORT} 50 | 51 | # 创建应用挂载目录并设置权限 52 | RUN mkdir -p /app && chmod -R 755 /app 53 | 54 | # 设置默认工作目录 55 | WORKDIR /app 56 | 57 | # 使用启动脚本启动服务 58 | ENTRYPOINT ["/opt/startup.sh"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wine noVNC Docker 基础镜像 2 | 3 | 该项目提供了一个 Docker 基础镜像,用于在浏览器中通过 noVNC 访问虚拟桌面运行 Windows 应用程序。项目集成了 Wine、VNC 和 noVNC,并使用模块化的 Supervisor 管理多个服务。该镜像非常适合作为基础镜像,用户可在其上添加自定义应用程序。 4 | 5 | ## 项目特性 6 | 7 | - **Wine**: 支持运行 Windows 应用程序。 8 | - **noVNC**: 通过 WebSocket 提供浏览器访问 VNC 虚拟桌面。 9 | - **x11vnc**: 提供 VNC 虚拟桌面服务。 10 | - **Fluxbox**: 轻量级、开箱即用的窗口管理器。 11 | - **xterm**: 简单、轻量的终端工具,方便用户在桌面环境中使用命令行。 12 | - **Supervisor**: 管理和监控多进程服务,采用模块化配置,易于扩展和自定义。 13 | 14 | ## 快速开始 15 | 16 | ### 获取镜像 17 | 18 | 从 Docker Hub 拉取基础镜像: 19 | 20 | ```bash 21 | docker pull invelop/wine-novnc:latest 22 | ``` 23 | 24 | 或者克隆本仓库并构建镜像: 25 | 26 | ```bash 27 | git clone https://github.com/p0ise/wine-novnc-docker.git 28 | cd wine-novnc-docker 29 | docker build -t invelop/wine-novnc . 30 | ``` 31 | 32 | ### 运行容器并设置 VNC 密码 33 | 34 | 此镜像支持通过 `VNC_PASSWORD` 环境变量设置自定义 VNC 密码,同时支持自动生成随机密码的逻辑: 35 | 36 | - **自定义密码**:如果在启动容器时传入 `VNC_PASSWORD` 环境变量,则该密码将作为 VNC 密码,无论密码文件是否已存在。 37 | - **自动生成密码**:如果未提供 `VNC_PASSWORD` 且容器没有现有密码文件,启动时将自动生成一个随机密码并显示在控制台。 38 | - **保留现有密码**:如果容器内已有密码文件且未传入 `VNC_PASSWORD`,则使用现有密码,不会覆盖。 39 | 40 | #### 设置自定义密码的示例 41 | 42 | ```bash 43 | docker run -p 6080:6080 -p 5901:5901 -e VNC_PASSWORD=my_custom_password invelop/wine-novnc 44 | ``` 45 | 46 | #### 自动生成密码的示例 47 | 48 | 如果未设置 `VNC_PASSWORD` 且没有现有密码文件,容器将生成一个随机密码并显示在控制台: 49 | 50 | ```bash 51 | docker run -p 6080:6080 -p 5901:5901 invelop/wine-novnc 52 | ``` 53 | 54 | ### 访问 noVNC 界面 55 | 56 | 打开浏览器,访问 `http://localhost:6080`,在提示框中输入 VNC 密码,即可访问虚拟桌面并运行 Windows 应用。 57 | 58 | ## 环境变量 59 | 60 | 以下环境变量在 Dockerfile 中进行配置,以确保 VNC 和 noVNC 的端口一致性: 61 | 62 | - `VNC_PORT`:VNC 服务端口,默认 `5901`。 63 | - `NOVNC_PORT`:noVNC WebSocket 端口,默认 `6080`。 64 | 65 | ## 文件结构 66 | 67 | 项目的主要文件包括: 68 | 69 | - `Dockerfile`:定义镜像构建步骤,配置 Wine、VNC、noVNC 及其他依赖。 70 | - `supervisord.conf`:主 `supervisor` 配置文件,包含基本设置并引入模块化配置。 71 | - `supervisor/conf.d/`:包含各服务的独立 `supervisor` 配置文件: 72 | - `xvfb.conf`:配置 Xvfb 虚拟显示服务。 73 | - `x11vnc.conf`:配置 x11vnc VNC 服务。 74 | - `fluxbox.conf`:配置 Fluxbox 窗口管理器。 75 | - `novnc.conf`:配置 noVNC 服务。 76 | - `startup.sh`:启动脚本,用于设置 VNC 密码并启动 `supervisord` 管理服务。 77 | - `download_gecko_and_mono.sh`:下载并配置 Wine 的 Gecko 和 Mono 支持文件,确保 Wine 的完整运行环境。 78 | 79 | ## 自定义应用配置 80 | 81 | 该镜像提供 `/app` 目录,便于用户挂载和自定义应用。可以在 `supervisor` 的模块化配置目录 `conf.d/` 中添加应用的配置文件,以便在镜像启动时运行自定义应用。 82 | 83 | ### 示例:基于此基础镜像构建自定义应用 84 | 85 | 以下示例展示如何在基础镜像上添加自定义的 Windows 应用 `my-windows-app`: 86 | 87 | #### 自定义应用的 `Dockerfile` 88 | 89 | ```Dockerfile 90 | # 基于 wine-novnc 基础镜像 91 | FROM invelop/wine-novnc 92 | 93 | # 复制应用到 /app 目录 94 | COPY my-windows-app /app/my-windows-app 95 | 96 | # 添加应用的 Supervisor 配置文件 97 | COPY myapp-supervisor.conf /etc/supervisor/conf.d/myapp.conf 98 | 99 | # 设置工作目录 100 | WORKDIR /app 101 | 102 | # 继续使用基础镜像的 ENTRYPOINT 103 | CMD [] 104 | ``` 105 | 106 | #### `myapp-supervisor.conf` 配置 107 | 108 | 通过 `supervisor` 启动应用的配置示例: 109 | 110 | ```ini 111 | [program:myapp] 112 | command=wine /app/my-windows-app/myapp.exe 113 | autostart=true 114 | autorestart=true 115 | priority=50 116 | stdout_logfile=/var/log/supervisord/myapp.log 117 | stderr_logfile=/var/log/supervisord/myapp_error.log 118 | ``` 119 | 120 | #### 构建和运行自定义应用镜像 121 | 122 | 1. **构建镜像**: 123 | 124 | ```bash 125 | docker build -t my-custom-app . 126 | ``` 127 | 128 | 2. **运行容器**: 129 | 130 | ```bash 131 | docker run -p 6080:6080 -p 5901:5901 my-custom-app 132 | ``` 133 | 134 | 在浏览器中访问 `http://localhost:6080`,即可看到 `Fluxbox` 桌面环境,并确认 `my-windows-app` 已启动。 135 | 136 | ## 日志文件 137 | 138 | 所有服务的日志文件存储在 `/var/log/supervisord` 目录下,便于监控和调试: 139 | 140 | - `supervisord.log`:Supervisor 全局日志文件,记录 supervisord 本身的运行信息。 141 | - `xvfb.log`:记录 Xvfb 服务的日志,虚拟显示的运行状态。 142 | - `x11vnc.log`:记录 x11vnc 服务的日志,提供 VNC 访问信息。 143 | - `novnc.log`:记录 noVNC 服务的日志,提供 WebSocket 连接日志。 144 | - `fluxbox.log`:记录 Fluxbox 窗口管理器的日志。 145 | - `myapp.log`:记录自定义应用的日志(根据 `myapp-supervisor.conf` 配置文件)。 146 | 147 | 可以通过检查这些日志文件来诊断和监控各个服务的状态。 148 | 149 | ## 贡献 150 | 151 | 欢迎提交 Issue 和 Pull Request,帮助改进项目。 152 | 153 | ## 许可证 154 | 155 | 本项目采用 MIT 许可证开源,详情请参阅 `LICENSE` 文件。 156 | --------------------------------------------------------------------------------