├── .dockerignore
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .gitmodules
├── ARCHITECTURE.md
├── DEVELOPMENT.md
├── Dockerfile
├── Dockerfile.amd64
├── Dockerfile.arm64
├── Dockerfile.armhf
├── Dockerfile.j2
├── LICENSE
├── Makefile
├── README.md
├── TODO
├── flavors
├── lxde.yml
├── lxqt.yml
└── xfce4.yml
├── hooks
└── pre_build
├── rootfs
├── etc
│ ├── nginx
│ │ └── sites-enabled
│ │ │ └── default
│ └── supervisor
│ │ └── conf.d
│ │ ├── supervisord.conf
│ │ └── supervisord.conf.j2
├── root
│ ├── .asoundrc
│ └── .gtkrc-2.0
├── startup.sh
└── usr
│ ├── local
│ ├── bin
│ │ ├── chromium-browser-sound.sh
│ │ └── xvfb.sh
│ ├── lib
│ │ └── web
│ │ │ ├── backend
│ │ │ ├── config
│ │ │ │ └── __init__.py
│ │ │ ├── log
│ │ │ │ ├── __init__.py
│ │ │ │ └── config.py
│ │ │ ├── requirements.txt
│ │ │ ├── run.py
│ │ │ └── vnc
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app.py
│ │ │ │ ├── log.py
│ │ │ │ ├── response.py
│ │ │ │ ├── state.py
│ │ │ │ └── util.py
│ │ │ └── frontend
│ │ │ └── .gitkeep
│ └── share
│ │ └── doro-lxde-wallpapers
│ │ ├── bg1.jpg
│ │ ├── bg2.jpg
│ │ ├── bg3.jpg
│ │ ├── bg4.jpg
│ │ └── desktop-items-0.conf
│ └── share
│ └── applications
│ └── chromium-browser-sound.desktop
├── screenshots
└── lxde.png
└── web
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── index.html
├── novnc-armhf-1.patch
├── package.json
├── src
├── App.vue
├── assets
│ └── .gitkeep
├── components
│ └── Vnc.vue
├── main.js
└── router
│ └── index.js
├── static
├── .gitkeep
├── scripts
│ ├── flv.min.js
│ └── jquery-3.3.1.min.js
├── video.html
└── vnc.html
├── test
├── e2e
│ ├── custom-assertions
│ │ └── elementCount.js
│ ├── nightwatch.conf.js
│ ├── runner.js
│ └── specs
│ │ └── test.js
└── unit
│ ├── .eslintrc
│ └── specs
│ └── HelloWorld.spec.js
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | web/node_modules
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Versions (please complete the following information):**
27 | - OS: [e.g. Ubuntu 18.04]
28 | - image tag [e.g. develop]
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | tags
3 | cscope*
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "web/static/novnc"]
2 | path = web/static/novnc
3 | url = https://github.com/novnc/noVNC
4 | [submodule "web/static/websockify"]
5 | path = web/static/websockify
6 | url = https://github.com/novnc/websockify
7 |
--------------------------------------------------------------------------------
/ARCHITECTURE.md:
--------------------------------------------------------------------------------
1 | # Architecture of the container #
2 |
3 | Components
4 | ============
5 |
6 | The container contains the following components :
7 | - An Ubuntu base system
8 | - The tini + supervisord startup and daemon control system
9 | - Nginx Web server
10 | - A backend ("novnc2") Python Web app providing an API (written with
11 | Flask) on port 6079
12 | - A frontend VueJS Web app displayed to the user, which will wrap noVNC
13 | - noVNC + WebSockify providing the Web VNC client in an HTML5 canvas
14 | - Xvfb running the X11 server in memory
15 | - x11vnc exporting the X11 display through VNC
16 | - and all regular X applications, like the LXDE desktop and apps
17 |
18 | Wiring them all
19 | ------------------
20 |
21 | Internally, Xvfb will be started in DISPLAY :1, then x11vnc will
22 | provide access to it on the default VNC port (5900).
23 |
24 | noVNC will be started listening to HTTP requests on port 6081.
25 | It is possible to connect directly to port 6081 of the container, to
26 | only use the regular noVNC Web interface (provided it is exported by
27 | the container).
28 |
29 | Above noVNC stands the VueJS frontend Web app provided by nginx, which
30 | will proxy the noVNC canvas, and will add some useful features over
31 | noVNC.
32 |
33 | User-oriented features
34 | ==========================
35 |
36 | The Web frontend adds the following features :
37 | - upon display of the Web page, the app will detect the size of the
38 | Web browser's window, and will invoke the backend API so as to make
39 | sure the noVNC rendering ajusts to that size
40 | - provide a flash video rendering transporting the sound (???)
41 |
42 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Get code
2 |
3 | ```
4 | git clone --recursive https://github.com/fcwu/docker-ubuntu-vnc-desktop
5 | ```
6 |
7 | or, if you have already cloned it, get submodules contents :
8 | ```
9 | git submodule init; git submodule update
10 | ```
11 |
12 | # Test local code
13 |
14 | ## Test-run in container rebuilt from local repo
15 |
16 | You may edit the code in your local copy of the repo, rebuild the
17 | container, and test the changes:
18 |
19 | ```
20 | make clean
21 | FLAVOR=lxqt ARCH=amd64 IMAGE=ubuntu:18.04 make build
22 | make run
23 | ```
24 |
25 | ## develop backend
26 |
27 | You may wish to work on the backend app. As the "make run" makes sure
28 | to mount the current dir contents under /src in the container, you can
29 | proceed as such (no compilation of the Python code):
30 | ```
31 | make shell
32 | supervisorctl -c /etc/supervisor/supervisord.conf stop web
33 | cd /src/image/usr/local/lib/web/backend
34 | ./run.py --debug
35 | ```
36 |
37 | ## develop frontend
38 |
39 | ```
40 | cd web
41 | yarn add
42 | BACKEND=http://127.0.0.1:6080 npm run dev
43 | ```
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | Dockerfile.amd64
--------------------------------------------------------------------------------
/Dockerfile.amd64:
--------------------------------------------------------------------------------
1 | # Built with arch: amd64 flavor: lxde image: ubuntu:20.04
2 | #
3 | ################################################################################
4 | # base system
5 | ################################################################################
6 |
7 | FROM ubuntu:20.04 as system
8 |
9 |
10 |
11 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
12 |
13 |
14 | # built-in packages
15 | ENV DEBIAN_FRONTEND noninteractive
16 | RUN apt update \
17 | && apt install -y --no-install-recommends software-properties-common curl apache2-utils \
18 | && apt update \
19 | && apt install -y --no-install-recommends --allow-unauthenticated \
20 | supervisor nginx sudo net-tools zenity xz-utils \
21 | dbus-x11 x11-utils alsa-utils \
22 | mesa-utils libgl1-mesa-dri \
23 | && apt autoclean -y \
24 | && apt autoremove -y \
25 | && rm -rf /var/lib/apt/lists/*
26 | # install debs error if combine together
27 | RUN apt update \
28 | && apt install -y --no-install-recommends --allow-unauthenticated \
29 | xvfb x11vnc \
30 | vim-tiny firefox ttf-ubuntu-font-family ttf-wqy-zenhei \
31 | && apt autoclean -y \
32 | && apt autoremove -y \
33 | && rm -rf /var/lib/apt/lists/*
34 |
35 | RUN apt update \
36 | && apt install -y gpg-agent \
37 | && curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
38 | && (dpkg -i ./google-chrome-stable_current_amd64.deb || apt-get install -fy) \
39 | && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add \
40 | && rm google-chrome-stable_current_amd64.deb \
41 | && rm -rf /var/lib/apt/lists/*
42 |
43 | RUN apt update \
44 | && apt install -y --no-install-recommends --allow-unauthenticated \
45 | lxde gtk2-engines-murrine gnome-themes-standard gtk2-engines-pixbuf gtk2-engines-murrine arc-theme \
46 | && apt autoclean -y \
47 | && apt autoremove -y \
48 | && rm -rf /var/lib/apt/lists/*
49 |
50 |
51 | # Additional packages require ~600MB
52 | # libreoffice pinta language-pack-zh-hant language-pack-gnome-zh-hant firefox-locale-zh-hant libreoffice-l10n-zh-tw
53 |
54 | # tini to fix subreap
55 | ARG TINI_VERSION=v0.18.0
56 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /bin/tini
57 | RUN chmod +x /bin/tini
58 |
59 | # ffmpeg
60 | RUN apt update \
61 | && apt install -y --no-install-recommends --allow-unauthenticated \
62 | ffmpeg \
63 | && rm -rf /var/lib/apt/lists/* \
64 | && mkdir /usr/local/ffmpeg \
65 | && ln -s /usr/bin/ffmpeg /usr/local/ffmpeg/ffmpeg
66 |
67 | # python library
68 | COPY rootfs/usr/local/lib/web/backend/requirements.txt /tmp/
69 | RUN apt-get update \
70 | && dpkg-query -W -f='${Package}\n' > /tmp/a.txt \
71 | && apt-get install -y python3-pip python3-dev build-essential \
72 | && pip3 install setuptools wheel && pip3 install -r /tmp/requirements.txt \
73 | && ln -s /usr/bin/python3 /usr/local/bin/python \
74 | && dpkg-query -W -f='${Package}\n' > /tmp/b.txt \
75 | && apt-get remove -y `diff --changed-group-format='%>' --unchanged-group-format='' /tmp/a.txt /tmp/b.txt | xargs` \
76 | && apt-get autoclean -y \
77 | && apt-get autoremove -y \
78 | && rm -rf /var/lib/apt/lists/* \
79 | && rm -rf /var/cache/apt/* /tmp/a.txt /tmp/b.txt
80 |
81 |
82 | ################################################################################
83 | # builder
84 | ################################################################################
85 | FROM ubuntu:20.04 as builder
86 |
87 |
88 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
89 |
90 |
91 | RUN apt-get update \
92 | && apt-get install -y --no-install-recommends curl ca-certificates gnupg patch
93 |
94 | # nodejs
95 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
96 | && apt-get install -y nodejs
97 |
98 | # yarn
99 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
100 | && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
101 | && apt-get update \
102 | && apt-get install -y yarn
103 |
104 | # build frontend
105 | COPY web /src/web
106 | RUN cd /src/web \
107 | && yarn \
108 | && yarn build
109 | RUN sed -i 's#app/locale/#novnc/app/locale/#' /src/web/dist/static/novnc/app/ui.js
110 |
111 |
112 |
113 | ################################################################################
114 | # merge
115 | ################################################################################
116 | FROM system
117 | LABEL maintainer="fcwu.tw@gmail.com"
118 |
119 | COPY --from=builder /src/web/dist/ /usr/local/lib/web/frontend/
120 | COPY rootfs /
121 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify /usr/local/lib/web/frontend/static/novnc/utils/websockify && \
122 | chmod +x /usr/local/lib/web/frontend/static/websockify/run
123 |
124 | EXPOSE 80
125 | WORKDIR /root
126 | ENV HOME=/home/ubuntu \
127 | SHELL=/bin/bash
128 | HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://127.0.0.1:6079/api/health
129 | ENTRYPOINT ["/startup.sh"]
130 |
--------------------------------------------------------------------------------
/Dockerfile.arm64:
--------------------------------------------------------------------------------
1 | # Built with arch: arm64 flavor: lxde image: ubuntu:18.04
2 | #
3 | ################################################################################
4 | # base system
5 | ################################################################################
6 |
7 | # qemu helper for arm build
8 | FROM ubuntu:20.04 as amd64
9 | RUN apt update && apt install -y qemu-user-static
10 | FROM arm64v8/ubuntu:20.04 as system
11 | COPY --from=amd64 /usr/bin/qemu-aarch64-static /usr/bin/
12 |
13 |
14 |
15 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
16 |
17 |
18 | # built-in packages
19 | ENV DEBIAN_FRONTEND noninteractive
20 | RUN apt update \
21 | && apt install -y --no-install-recommends software-properties-common curl apache2-utils \
22 | && apt update \
23 | && apt install -y --no-install-recommends --allow-unauthenticated \
24 | supervisor nginx sudo net-tools zenity xz-utils \
25 | dbus-x11 x11-utils alsa-utils \
26 | mesa-utils libgl1-mesa-dri \
27 | && apt autoclean -y \
28 | && apt autoremove -y \
29 | && rm -rf /var/lib/apt/lists/*
30 | # install debs error if combine together
31 | RUN apt update \
32 | && apt install -y --no-install-recommends --allow-unauthenticated \
33 | xvfb x11vnc \
34 | vim-tiny firefox chromium-browser ttf-ubuntu-font-family ttf-wqy-zenhei \
35 | && add-apt-repository -r ppa:fcwu-tw/apps \
36 | && apt autoclean -y \
37 | && apt autoremove -y \
38 | && rm -rf /var/lib/apt/lists/*
39 |
40 | RUN apt update \
41 | && apt install -y --no-install-recommends --allow-unauthenticated \
42 | lxde gtk2-engines-murrine gnome-themes-standard gtk2-engines-pixbuf gtk2-engines-murrine arc-theme \
43 | && apt autoclean -y \
44 | && apt autoremove -y \
45 | && rm -rf /var/lib/apt/lists/*
46 |
47 |
48 | # Additional packages require ~600MB
49 | # libreoffice pinta language-pack-zh-hant language-pack-gnome-zh-hant firefox-locale-zh-hant libreoffice-l10n-zh-tw
50 |
51 | # tini for subreap
52 | ARG TINI_VERSION=v0.18.0
53 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-arm64 /bin/tini
54 | RUN chmod +x /bin/tini
55 |
56 | # ffmpeg
57 | RUN apt update \
58 | && apt install -y --no-install-recommends --allow-unauthenticated \
59 | ffmpeg \
60 | && rm -rf /var/lib/apt/lists/* \
61 | && mkdir /usr/local/ffmpeg \
62 | && ln -s /usr/bin/ffmpeg /usr/local/ffmpeg/ffmpeg
63 |
64 | # python library
65 | COPY rootfs/usr/local/lib/web/backend/requirements.txt /tmp/
66 | RUN apt-get update \
67 | && dpkg-query -W -f='${Package}\n' > /tmp/a.txt \
68 | && apt-get install -y python3-pip python3-dev build-essential \
69 | && pip3 install setuptools wheel && pip3 install -r /tmp/requirements.txt \
70 | && ln -s /usr/bin/python3 /usr/local/bin/python \
71 | && dpkg-query -W -f='${Package}\n' > /tmp/b.txt \
72 | && apt-get remove -y `diff --changed-group-format='%>' --unchanged-group-format='' /tmp/a.txt /tmp/b.txt | xargs` \
73 | && apt-get autoclean -y \
74 | && apt-get autoremove -y \
75 | && rm -rf /var/lib/apt/lists/* \
76 | && rm -rf /var/cache/apt/* /tmp/a.txt /tmp/b.txt
77 |
78 |
79 | ################################################################################
80 | # builder
81 | ################################################################################
82 | FROM ubuntu:20.04 as builder
83 |
84 |
85 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
86 |
87 |
88 | RUN apt-get update \
89 | && apt-get install -y --no-install-recommends curl ca-certificates gnupg patch
90 |
91 | # nodejs
92 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
93 | && apt-get install -y nodejs
94 |
95 | # yarn
96 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
97 | && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
98 | && apt-get update \
99 | && apt-get install -y yarn
100 |
101 | # build frontend
102 | COPY web /src/web
103 | RUN cd /src/web \
104 | && yarn \
105 | && yarn build
106 | RUN sed -i 's#app/locale/#novnc/app/locale/#' /src/web/dist/static/novnc/app/ui.js
107 |
108 |
109 | RUN cd /src/web/dist/static/novnc && patch -p0 < /src/web/novnc-armhf-1.patch
110 |
111 |
112 | ################################################################################
113 | # merge
114 | ################################################################################
115 | FROM system
116 | LABEL maintainer="fcwu.tw@gmail.com"
117 |
118 | COPY --from=builder /src/web/dist/ /usr/local/lib/web/frontend/
119 | COPY rootfs /
120 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify" "/usr/local/lib/web/frontend/static/novnc/utils/websockify && chmod +x /usr/local/lib/web/frontend/static/websockify/run
121 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify /usr/local/lib/web/frontend/static/novnc/utils/websockify && \
122 | chmod +x /usr/local/lib/web/frontend/static/websockify/run
123 |
124 | EXPOSE 80
125 | WORKDIR /root
126 | ENV HOME=/home/ubuntu \
127 | SHELL=/bin/bash
128 | HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://127.0.0.1:6079/api/health
129 | ENTRYPOINT ["/startup.sh"]
130 |
--------------------------------------------------------------------------------
/Dockerfile.armhf:
--------------------------------------------------------------------------------
1 | # Built with arch: armhf flavor: lxde image: ubuntu:18.04
2 | #
3 | ################################################################################
4 | # base system
5 | ################################################################################
6 |
7 | # qemu helper for arm build
8 | FROM ubuntu:18.04 as amd64
9 | RUN apt update && apt install -y qemu-user-static
10 | FROM arm32v7/ubuntu:18.04 as system
11 | COPY --from=amd64 /usr/bin/qemu-arm-static /usr/bin/
12 |
13 |
14 |
15 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
16 |
17 |
18 | # built-in packages
19 | ENV DEBIAN_FRONTEND noninteractive
20 | RUN apt update \
21 | && apt install -y --no-install-recommends software-properties-common curl apache2-utils \
22 | && apt update \
23 | && apt install -y --no-install-recommends --allow-unauthenticated \
24 | supervisor nginx sudo net-tools zenity xz-utils \
25 | dbus-x11 x11-utils alsa-utils \
26 | mesa-utils libgl1-mesa-dri \
27 | && apt autoclean -y \
28 | && apt autoremove -y \
29 | && rm -rf /var/lib/apt/lists/*
30 | # install debs error if combine together
31 | RUN add-apt-repository -y ppa:fcwu-tw/apps \
32 | && apt update \
33 | && apt install -y --no-install-recommends --allow-unauthenticated \
34 | xvfb x11vnc=0.9.16-1 \
35 | vim-tiny firefox chromium-browser ttf-ubuntu-font-family ttf-wqy-zenhei \
36 | && add-apt-repository -r ppa:fcwu-tw/apps \
37 | && apt autoclean -y \
38 | && apt autoremove -y \
39 | && rm -rf /var/lib/apt/lists/*
40 |
41 | RUN apt update \
42 | && apt install -y --no-install-recommends --allow-unauthenticated \
43 | lxde gtk2-engines-murrine gnome-themes-standard gtk2-engines-pixbuf gtk2-engines-murrine arc-theme \
44 | && apt autoclean -y \
45 | && apt autoremove -y \
46 | && rm -rf /var/lib/apt/lists/*
47 |
48 |
49 | # Additional packages require ~600MB
50 | # libreoffice pinta language-pack-zh-hant language-pack-gnome-zh-hant firefox-locale-zh-hant libreoffice-l10n-zh-tw
51 |
52 | # tini for subreap
53 | ARG TINI_VERSION=v0.18.0
54 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-armhf /bin/tini
55 | RUN chmod +x /bin/tini
56 |
57 | # ffmpeg
58 | RUN mkdir -p /usr/local/ffmpeg \
59 | && curl -sSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | tar xJvf - -C /usr/local/ffmpeg/ --strip 1
60 |
61 | # python library
62 | COPY rootfs/usr/local/lib/web/backend/requirements.txt /tmp/
63 | RUN apt-get update \
64 | && dpkg-query -W -f='${Package}\n' > /tmp/a.txt \
65 | && apt-get install -y python-pip python-dev build-essential \
66 | && pip install setuptools wheel && pip install -r /tmp/requirements.txt \
67 | && dpkg-query -W -f='${Package}\n' > /tmp/b.txt \
68 | && apt-get remove -y `diff --changed-group-format='%>' --unchanged-group-format='' /tmp/a.txt /tmp/b.txt | xargs` \
69 | && apt-get autoclean -y \
70 | && apt-get autoremove -y \
71 | && rm -rf /var/lib/apt/lists/* \
72 | && rm -rf /var/cache/apt/* /tmp/a.txt /tmp/b.txt
73 |
74 |
75 | ################################################################################
76 | # builder
77 | ################################################################################
78 | FROM ubuntu:18.04 as builder
79 |
80 |
81 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
82 |
83 |
84 | RUN apt-get update \
85 | && apt-get install -y --no-install-recommends curl ca-certificates gnupg patch
86 |
87 | # nodejs
88 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
89 | && apt-get install -y nodejs
90 |
91 | # yarn
92 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
93 | && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
94 | && apt-get update \
95 | && apt-get install -y yarn
96 |
97 | # build frontend
98 | COPY web /src/web
99 | RUN cd /src/web \
100 | && yarn \
101 | && yarn run build
102 | RUN sed -i 's#app/locale/#novnc/app/locale/#' /src/web/dist/static/novnc/app/ui.js
103 |
104 | RUN cd /src/web/dist/static/novnc && patch -p0 < /src/web/novnc-armhf-1.patch
105 |
106 |
107 | ################################################################################
108 | # merge
109 | ################################################################################
110 | FROM system
111 | LABEL maintainer="fcwu.tw@gmail.com"
112 |
113 | COPY --from=builder /src/web/dist/ /usr/local/lib/web/frontend/
114 | COPY rootfs /
115 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify" "/usr/local/lib/web/frontend/static/novnc/utils/websockify && chmod +x /usr/local/lib/web/frontend/static/websockify/run
116 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify /usr/local/lib/web/frontend/static/novnc/utils/websockify && \
117 | chmod +x /usr/local/lib/web/frontend/static/websockify/run
118 |
119 | EXPOSE 80
120 | WORKDIR /root
121 | ENV HOME=/home/ubuntu \
122 | SHELL=/bin/bash
123 | HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://127.0.0.1:6079/api/health
124 | ENTRYPOINT ["/startup.sh"]
125 |
--------------------------------------------------------------------------------
/Dockerfile.j2:
--------------------------------------------------------------------------------
1 | # Built with arch: {{ arch }} flavor: {{ flavor }} image: {{ image }}
2 | #
3 | ################################################################################
4 | # base system
5 | ################################################################################
6 | {%if arch == "amd64"%}
7 | FROM {{image}} as system
8 | {%elif arch == "armhf"%}
9 | # qemu helper for arm build
10 | FROM {{image}} as amd64
11 | RUN apt update && apt install -y qemu-user-static
12 | FROM arm32v7/{{image}} as system
13 | COPY --from=amd64 /usr/bin/qemu-arm-static /usr/bin/
14 | {%endif%}
15 |
16 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
17 |
18 | # built-in packages
19 | ENV DEBIAN_FRONTEND noninteractive
20 | RUN apt update \
21 | && apt install -y --no-install-recommends software-properties-common curl apache2-utils \
22 | && apt update \
23 | && apt install -y --no-install-recommends --allow-unauthenticated \
24 | supervisor nginx sudo net-tools zenity xz-utils \
25 | dbus-x11 x11-utils alsa-utils \
26 | mesa-utils libgl1-mesa-dri \
27 | && apt autoclean -y \
28 | && apt autoremove -y \
29 | && rm -rf /var/lib/apt/lists/*
30 | # install debs error if combine together
31 | RUN apt update \
32 | && apt install -y --no-install-recommends --allow-unauthenticated \
33 | xvfb x11vnc \
34 | vim-tiny firefox ttf-ubuntu-font-family ttf-wqy-zenhei \
35 | && apt autoclean -y \
36 | && apt autoremove -y \
37 | && rm -rf /var/lib/apt/lists/*
38 | RUN apt update \
39 | && apt install -y gpg-agent \
40 | && curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
41 | && (dpkg -i ./google-chrome-stable_current_amd64.deb || apt-get install -fy) \
42 | && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add \
43 | && rm google-chrome-stable_current_amd64.deb \
44 | && rm -rf /var/lib/apt/lists/*
45 | {%if desktop == "lxde" %}
46 | {%endif%}
47 | {%if desktop == "lxqt" %}
48 | {%endif%}
49 | {%if desktop == "xfce4" %}
50 | {%endif%}
51 | RUN apt update \
52 | && apt install -y --no-install-recommends --allow-unauthenticated \
53 | lxde gtk2-engines-murrine gnome-themes-standard gtk2-engines-pixbuf gtk2-engines-murrine arc-theme \
54 | && apt autoclean -y \
55 | && apt autoremove -y \
56 | && rm -rf /var/lib/apt/lists/*
57 | # Additional packages require ~600MB
58 | # libreoffice pinta language-pack-zh-hant language-pack-gnome-zh-hant firefox-locale-zh-hant libreoffice-l10n-zh-tw
59 |
60 | # tini to fix subreap
61 | ARG TINI_VERSION=v0.18.0
62 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /bin/tini
63 | RUN chmod +x /bin/tini
64 |
65 | # ffmpeg
66 | RUN apt update \
67 | && apt install -y --no-install-recommends --allow-unauthenticated \
68 | ffmpeg \
69 | && rm -rf /var/lib/apt/lists/* \
70 | && mkdir /usr/local/ffmpeg \
71 | && ln -s /usr/bin/ffmpeg /usr/local/ffmpeg/ffmpeg
72 |
73 | # python library
74 | COPY rootfs/usr/local/lib/web/backend/requirements.txt /tmp/
75 | RUN apt-get update \
76 | && dpkg-query -W -f='${Package}\n' > /tmp/a.txt \
77 | && apt-get install -y python3-pip python3-dev build-essential \
78 | && pip3 install setuptools wheel && pip3 install -r /tmp/requirements.txt \
79 | && ln -s /usr/bin/python3 /usr/local/bin/python \
80 | && dpkg-query -W -f='${Package}\n' > /tmp/b.txt \
81 | && apt-get remove -y `diff --changed-group-format='%>' --unchanged-group-format='' /tmp/a.txt /tmp/b.txt | xargs` \
82 | && apt-get autoclean -y \
83 | && apt-get autoremove -y \
84 | && rm -rf /var/lib/apt/lists/* \
85 | && rm -rf /var/cache/apt/* /tmp/a.txt /tmp/b.txt
86 |
87 |
88 | ################################################################################
89 | # builder
90 | ################################################################################
91 | FROM {{image}} as builder
92 |
93 | {% if localbuild == 1 %}
94 | RUN sed -i 's#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list;
95 | {% endif %}
96 |
97 | RUN apt-get update \
98 | && apt-get install -y --no-install-recommends curl ca-certificates gnupg patch
99 |
100 | # nodejs
101 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
102 | && apt-get install -y nodejs
103 |
104 | # yarn
105 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
106 | && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
107 | && apt-get update \
108 | && apt-get install -y yarn
109 |
110 | # build frontend
111 | COPY web /src/web
112 | RUN cd /src/web \
113 | && yarn \
114 | && yarn build
115 | RUN sed -i 's#app/locale/#novnc/app/locale/#' /src/web/dist/static/novnc/app/ui.js
116 |
117 | {%if arch == "armhf"%}
118 | RUN cd /src/web/dist/static/novnc && patch -p0 < /src/web/novnc-armhf-1.patch
119 | {%endif%}
120 |
121 | ################################################################################
122 | # merge
123 | ################################################################################
124 | FROM system
125 | LABEL maintainer="fcwu.tw@gmail.com"
126 |
127 | COPY --from=builder /src/web/dist/ /usr/local/lib/web/frontend/
128 | COPY rootfs /
129 | RUN ln -sf /usr/local/lib/web/frontend/static/websockify /usr/local/lib/web/frontend/static/novnc/utils/websockify && \
130 | chmod +x /usr/local/lib/web/frontend/static/websockify/run
131 |
132 | EXPOSE 80
133 | WORKDIR /root
134 | ENV HOME=/home/ubuntu \
135 | SHELL=/bin/bash
136 | HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://127.0.0.1:6079/api/health
137 | ENTRYPOINT ["/startup.sh"]
138 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build run
2 |
3 | # Default values for variables
4 | REPO ?= dorowu/ubuntu-desktop-lxde-vnc
5 | TAG ?= latest
6 | # you can choose other base image versions
7 | IMAGE ?= ubuntu:20.04
8 | # IMAGE ?= nvidia/cuda:10.1-cudnn7-devel-ubuntu18.04
9 | # choose from supported flavors (see available ones in ./flavors/*.yml)
10 | FLAVOR ?= lxde
11 | # armhf or amd64
12 | ARCH ?= amd64
13 |
14 | # These files will be generated from teh Jinja templates (.j2 sources)
15 | templates = Dockerfile rootfs/etc/supervisor/conf.d/supervisord.conf
16 |
17 | # Rebuild the container image
18 | build: $(templates)
19 | docker build -t $(REPO):$(TAG) .
20 |
21 | # Test run the container
22 | # the local dir will be mounted under /src read-only
23 | run:
24 | docker run --privileged --rm \
25 | -p 6080:80 -p 6081:443 \
26 | -v ${PWD}:/src:ro \
27 | -e USER=doro -e PASSWORD=mypassword \
28 | -e ALSADEV=hw:2,0 \
29 | -e SSL_PORT=443 \
30 | -e RELATIVE_URL_ROOT=approot \
31 | -e OPENBOX_ARGS="--startup /usr/bin/galculator" \
32 | -v ${PWD}/ssl:/etc/nginx/ssl \
33 | --device /dev/snd \
34 | --name ubuntu-desktop-lxde-test \
35 | $(REPO):$(TAG)
36 |
37 | # Connect inside the running container for debugging
38 | shell:
39 | docker exec -it ubuntu-desktop-lxde-test bash
40 |
41 | # Generate the SSL/TLS config for HTTPS
42 | gen-ssl:
43 | mkdir -p ssl
44 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
45 | -keyout ssl/nginx.key -out ssl/nginx.crt
46 |
47 | clean:
48 | rm -f $(templates)
49 |
50 | extra-clean:
51 | docker rmi $(REPO):$(TAG)
52 | docker image prune -f
53 |
54 | # Run jinja2cli to parse Jinja template applying rules defined in the flavors definitions
55 | %: %.j2 flavors/$(FLAVOR).yml
56 | docker run -v $(shell pwd):/data vikingco/jinja2cli \
57 | -D flavor=$(FLAVOR) \
58 | -D image=$(IMAGE) \
59 | -D localbuild=$(LOCALBUILD) \
60 | -D arch=$(ARCH) \
61 | $< flavors/$(FLAVOR).yml > $@ || rm $@
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # docker-ubuntu-vnc-desktop
2 |
3 | [](https://hub.docker.com/r/dorowu/ubuntu-desktop-lxde-vnc/)
4 | [](https://hub.docker.com/r/dorowu/ubuntu-desktop-lxde-vnc/)
5 |
6 | docker-ubuntu-vnc-desktop is a Docker image to provide web VNC interface to access Ubuntu LXDE/LxQT desktop environment.
7 |
8 |
9 |
10 |
11 |
12 | - [Quick Start](#quick-start)
13 | - [VNC Viewer](#vnc-viewer)
14 | - [HTTP Base Authentication](#http-base-authentication)
15 | - [SSL](#ssl)
16 | - [Screen Resolution](#screen-resolution)
17 | - [Default Desktop User](#default-desktop-user)
18 | - [Deploy to a subdirectory (relative url root)](#deploy-to-a-subdirectory-relative-url-root)
19 | - [Sound (Preview version and Linux only)](#sound-preview-version-and-linux-only)
20 | - [Generate Dockerfile from jinja template](#generate-dockerfile-from-jinja-template)
21 | - [Troubleshooting and FAQ](#troubleshooting-and-faq)
22 | - [License](#license)
23 |
24 |
25 |
26 | ## Quick Start
27 |
28 | Run the docker container and access with port `6080`
29 |
30 | ```shell
31 | docker run -p 6080:80 -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
32 | ```
33 |
34 | Browse http://127.0.0.1:6080/
35 |
36 |
37 |
38 | ### Ubuntu Flavors
39 |
40 | Choose your favorite Ubuntu version with [tags](https://hub.docker.com/r/dorowu/ubuntu-desktop-lxde-vnc/tags/)
41 |
42 | - focal: Ubuntu 20.04 (latest)
43 | - focal-lxqt: Ubuntu 20.04 LXQt
44 | - bionic: Ubuntu 18.04
45 | - bionic-lxqt: Ubuntu 18.04 LXQt
46 | - xenial: Ubuntu 16.04 (deprecated)
47 | - trusty: Ubuntu 14.04 (deprecated)
48 |
49 | ## VNC Viewer
50 |
51 | Forward VNC service port 5900 to host by
52 |
53 | ```shell
54 | docker run -p 6080:80 -p 5900:5900 -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
55 | ```
56 |
57 | Now, open the vnc viewer and connect to port 5900. If you would like to protect vnc service by password, set environment variable `VNC_PASSWORD`, for example
58 |
59 | ```shell
60 | docker run -p 6080:80 -p 5900:5900 -e VNC_PASSWORD=mypassword -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
61 | ```
62 |
63 | A prompt will ask password either in the browser or vnc viewer.
64 |
65 | ## HTTP Base Authentication
66 |
67 | This image provides base access authentication of HTTP via `HTTP_PASSWORD`
68 |
69 | ```shell
70 | docker run -p 6080:80 -e HTTP_PASSWORD=mypassword -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
71 | ```
72 |
73 | ## SSL
74 |
75 | To connect with SSL, generate self signed SSL certificate first if you don't have it
76 |
77 | ```shell
78 | mkdir -p ssl
79 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/nginx.key -out ssl/nginx.crt
80 | ```
81 |
82 | Specify SSL port by `SSL_PORT`, certificate path to `/etc/nginx/ssl`, and forward it to 6081
83 |
84 | ```shell
85 | docker run -p 6081:443 -e SSL_PORT=443 -v ${PWD}/ssl:/etc/nginx/ssl -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
86 | ```
87 |
88 | ## Screen Resolution
89 |
90 | The Resolution of virtual desktop adapts browser window size when first connecting the server. You may choose a fixed resolution by passing `RESOLUTION` environment variable, for example
91 |
92 | ```shell
93 | docker run -p 6080:80 -e RESOLUTION=1920x1080 -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
94 | ```
95 |
96 | ## Default Desktop User
97 |
98 | The default user is `root`. You may change the user and password respectively by `USER` and `PASSWORD` environment variable, for example,
99 |
100 | ```shell
101 | docker run -p 6080:80 -e USER=doro -e PASSWORD=password -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc
102 | ```
103 |
104 | ## Deploy to a subdirectory (relative url root)
105 |
106 | You may deploy this application to a subdirectory, for example `/some-prefix/`. You then can access application by `http://127.0.0.1:6080/some-prefix/`. This can be specified using the `RELATIVE_URL_ROOT` configuration option like this
107 |
108 | ```shell
109 | docker run -p 6080:80 -e RELATIVE_URL_ROOT=some-prefix dorowu/ubuntu-desktop-lxde-vnc
110 | ```
111 |
112 | NOTE: this variable should not have any leading and trailing splash (/)
113 |
114 | ## Sound (Preview version and Linux only)
115 |
116 | It only works in Linux.
117 |
118 | First of all, insert kernel module `snd-aloop` and specify `2` as the index of sound loop device
119 |
120 | ```shell
121 | sudo modprobe snd-aloop index=2
122 | ```
123 |
124 | Start the container
125 |
126 | ```shell
127 | docker run -it --rm -p 6080:80 --device /dev/snd -e ALSADEV=hw:2,0 dorowu/ubuntu-desktop-lxde-vnc
128 | ```
129 |
130 | where `--device /dev/snd -e ALSADEV=hw:2,0` means to grant sound device to container and set basic ASLA config to use card 2.
131 |
132 | Launch a browser with URL http://127.0.0.1:6080/#/?video, where `video` means to start with video mode. Now you can start Chromium in start menu (Internet -> Chromium Web Browser Sound) and try to play some video.
133 |
134 | Following is the screen capture of these operations. Turn on your sound at the end of video!
135 |
136 | [](http://www.youtube.com/watch?v=Kv9FGClP1-k)
137 |
138 |
139 | ## Generate Dockerfile from jinja template
140 |
141 | WARNING: Deprecated
142 |
143 | Dockerfile and configuration can be generated by template.
144 |
145 | - arch: one of `amd64` or `armhf`
146 | - flavor: refer to file in flavor/`flavor`.yml
147 | - image: base image
148 | - desktop: desktop environment which is set in flavor
149 | - addon_package: Debian package to be installed which is set in flavor
150 |
151 | Dockerfile and configuration are re-generate if they do not exist. Or you may force to re-generate by removing them with the command `make clean`.
152 |
153 | ## Troubleshooting and FAQ
154 |
155 | 1. boot2docker connection issue, https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/2
156 | 2. Multi-language supports, https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/80
157 | 3. Autostart, https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/85#issuecomment-466778407
158 | 4. x11vnc arguments(multiptr), https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/101
159 | 5. firefox/chrome crash (/dev/shm), https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/112
160 | 6. resize display size without destroying container, https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/115#issuecomment-522426037
161 |
162 | ## License
163 |
164 | See the LICENSE file for details.
165 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | - upgrade frontend packages
2 | - rewrite backend by golang
3 |
--------------------------------------------------------------------------------
/flavors/lxde.yml:
--------------------------------------------------------------------------------
1 | ---
2 | addon_packages:
3 | - vim-tiny
4 | - firefox
5 | - chromium-browser
6 | - ttf-ubuntu-font-family
7 | - ttf-wqy-zenhei
8 | desktop: lxde
9 |
--------------------------------------------------------------------------------
/flavors/lxqt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | addon_packages:
3 | - vim-tiny
4 | - firefox
5 | - chromium-browser
6 | - ttf-ubuntu-font-family
7 | - ttf-wqy-zenhei
8 | desktop: lxqt
9 |
--------------------------------------------------------------------------------
/flavors/xfce4.yml:
--------------------------------------------------------------------------------
1 | ---
2 | addon_packages:
3 | - vim-tiny
4 | - firefox
5 | - xfce4-terminal
6 | desktop: xfce4
7 |
--------------------------------------------------------------------------------
/hooks/pre_build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Register qemu-*-static for all supported processors except the
3 | # current one, but also remove all registered binfmt_misc before
4 | docker run --rm --privileged multiarch/qemu-user-static:register --reset
5 |
--------------------------------------------------------------------------------
/rootfs/etc/nginx/sites-enabled/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | # listen [::]:80 default_server ipv6only=on;
4 |
5 | #_SSL_PORT_#listen 443 ssl default_server;
6 | #_SSL_PORT_#listen [::]:443 ssl default_server ipv6only=on;
7 | #_SSL_PORT_#ssl_certificate /etc/nginx/ssl/nginx.crt;
8 | #_SSL_PORT_#ssl_certificate_key /etc/nginx/ssl/nginx.key;
9 |
10 | #_HTTP_PASSWORD_#auth_basic "Private Property";
11 | #_HTTP_PASSWORD_#auth_basic_user_file /etc/nginx/.htpasswd;
12 |
13 | root /usr/local/lib/web/frontend/;
14 | index index.html index.htm;
15 |
16 | #_RELATIVE_URL_ROOT_location /_RELATIVE_URL_ROOT_/ {
17 | #_RELATIVE_URL_ROOT_ rewrite /_RELATIVE_URL_ROOT_/(.*) /$1 break;
18 | #_RELATIVE_URL_ROOT_ root /usr/local/lib/web/frontend/;
19 | #_RELATIVE_URL_ROOT_}
20 |
21 | location ~ .*/(api/.*|websockify) {
22 | try_files $uri @api$http_upgrade;
23 | }
24 |
25 | location / {
26 | rewrite /approot/(.*) /$1 break;
27 | root /usr/local/lib/web/frontend/;
28 | }
29 |
30 | location @apiwebsocket {
31 | #_RELATIVE_URL_ROOT_rewrite /_RELATIVE_URL_ROOT_/(.*) $1 break;
32 | proxy_connect_timeout 7d;
33 | proxy_send_timeout 7d;
34 | proxy_read_timeout 7d;
35 | proxy_buffering off;
36 |
37 | proxy_http_version 1.1;
38 | proxy_set_header Upgrade $http_upgrade;
39 | proxy_set_header Connection "upgrade";
40 | proxy_pass http://127.0.0.1:6081;
41 | }
42 |
43 | location @api {
44 | #_RELATIVE_URL_ROOT_rewrite /_RELATIVE_URL_ROOT_/(.*) $1 break;
45 | proxy_set_header X-Real-IP $remote_addr;
46 | proxy_set_header X-Forwarded-For $remote_addr;
47 | proxy_set_header Host $host;
48 | max_ranges 0;
49 | proxy_pass http://127.0.0.1:6079;
50 | }
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/rootfs/etc/supervisor/conf.d/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | redirect_stderr=true
3 | stopsignal=QUIT
4 | autorestart=true
5 | directory=/root
6 |
7 | [program:nginx]
8 | priority=10
9 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
10 |
11 | [program:web]
12 | priority=10
13 | directory=/usr/local/lib/web/backend
14 | command=/usr/local/lib/web/backend/run.py
15 | stdout_logfile=/dev/fd/1
16 | stdout_logfile_maxbytes=0
17 | stderr_logfile=/dev/fd/1
18 | stderr_logfile_maxbytes=0
19 |
20 |
21 | [group:x]
22 | programs=xvfb,wm,lxpanel,pcmanfm,x11vnc,novnc
23 |
24 | [program:wm]
25 | priority=15
26 | command=/usr/bin/openbox
27 | environment=DISPLAY=":1",HOME="/root",USER="root"
28 |
29 | [program:lxpanel]
30 | priority=15
31 | directory=%HOME%
32 | command=/usr/bin/lxpanel --profile LXDE
33 | user=%USER%
34 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
35 |
36 | [program:pcmanfm]
37 | priority=15
38 | directory=%HOME%
39 | command=/usr/bin/pcmanfm --desktop --profile LXDE
40 | user=%USER%
41 | stopwaitsecs=3
42 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
43 |
44 |
45 |
46 |
47 |
48 |
49 | [program:xvfb]
50 | priority=10
51 | command=/usr/local/bin/xvfb.sh
52 | stopsignal=KILL
53 |
54 | [program:x11vnc]
55 | priority=20
56 | command=x11vnc -display :1 -xkb -forever -shared -repeat -capslock
57 |
58 | [program:novnc]
59 | priority=25
60 | directory=/usr/local/lib/web/frontend/static/novnc
61 | command=bash /usr/local/lib/web/frontend/static/novnc/utils/launch.sh --listen 6081
62 | stopasgroup=true
63 |
--------------------------------------------------------------------------------
/rootfs/etc/supervisor/conf.d/supervisord.conf.j2:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | redirect_stderr=true
3 | stopsignal=QUIT
4 | autorestart=true
5 | directory=/root
6 |
7 | [program:nginx]
8 | priority=10
9 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
10 |
11 | [program:web]
12 | priority=10
13 | directory=/usr/local/lib/web/backend
14 | command=/usr/local/lib/web/backend/run.py
15 | stdout_logfile=/dev/fd/1
16 | stdout_logfile_maxbytes=0
17 | stderr_logfile=/dev/fd/1
18 | stderr_logfile_maxbytes=0
19 |
20 | {% if desktop == "lxde" %}
21 | [group:x]
22 | programs=xvfb,wm,lxpanel,pcmanfm,x11vnc,novnc
23 |
24 | [program:wm]
25 | priority=15
26 | command=/usr/bin/openbox
27 | environment=DISPLAY=":1",HOME="/root",USER="root"
28 |
29 | [program:lxpanel]
30 | priority=15
31 | directory=%HOME%
32 | command=/usr/bin/lxpanel --profile LXDE
33 | user=%USER%
34 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
35 |
36 | [program:pcmanfm]
37 | priority=15
38 | directory=%HOME%
39 | command=/usr/bin/pcmanfm --desktop --profile LXDE
40 | user=%USER%
41 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
42 | {% endif %}
43 |
44 | {% if desktop == "lxqt" %}
45 | [group:x]
46 | programs=xvfb,wm,lxpanel,x11vnc,novnc
47 |
48 | [program:wm]
49 | priority=15
50 | command=/usr/bin/openbox
51 | environment=DISPLAY=":1",HOME="/root",USER="root"
52 |
53 | [program:lxpanel]
54 | priority=15
55 | directory=%HOME%
56 | command=/usr/bin/startlxqt
57 | user=%USER%
58 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
59 | {% endif %}
60 |
61 | {% if desktop == "xfce4" %}
62 | [group:x]
63 | programs=xvfb,lxpanel,x11vnc,novnc
64 |
65 | [program:lxpanel]
66 | priority=15
67 | directory=%HOME%
68 | command=/usr/bin/startxfce4
69 | user=%USER%
70 | environment=DISPLAY=":1",HOME="%HOME%",USER="%USER%"
71 | {% endif %}
72 |
73 | [program:xvfb]
74 | priority=10
75 | command=/usr/local/bin/xvfb.sh
76 | stopsignal=KILL
77 |
78 | [program:x11vnc]
79 | priority=20
80 | command=x11vnc -display :1 -xkb -forever -shared -repeat -capslock
81 |
82 | [program:novnc]
83 | priority=25
84 | directory=/usr/local/lib/web/frontend/static/novnc
85 | command=bash /usr/local/lib/web/frontend/static/novnc/utils/launch.sh --listen 6081
86 | stopasgroup=true
87 |
--------------------------------------------------------------------------------
/rootfs/root/.asoundrc:
--------------------------------------------------------------------------------
1 | pcm.loop {
2 | type plug
3 | slave.pcm "hw:Loopback,2,0"
4 | }
5 |
--------------------------------------------------------------------------------
/rootfs/root/.gtkrc-2.0:
--------------------------------------------------------------------------------
1 | # DO NOT EDIT! This file will be overwritten by LXAppearance.
2 | # Any customization should be done in ~/.gtkrc-2.0.mine instead.
3 | include "/usr/share/themes/Arc/gtk-2.0/gtkrc"
4 |
5 | gtk-theme-name="Arc"
6 | gtk-icon-theme-name="nuoveXT2"
7 | gtk-font-name="Sans 10"
8 | gtk-cursor-theme-name="DMZ-White"
9 | gtk-cursor-theme-size=18
10 | gtk-toolbar-style=GTK_TOOLBAR_BOTH_HORIZ
11 | gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
12 | gtk-button-images=1
13 | gtk-menu-images=1
14 | gtk-enable-event-sounds=1
15 | gtk-enable-input-feedback-sounds=1
16 | gtk-xft-antialias=1
17 | gtk-xft-hinting=1
18 | gtk-xft-hintstyle="hintslight"
19 | gtk-xft-rgba="rgb"
20 | include "/root/.gtkrc-2.0.mine"
21 |
--------------------------------------------------------------------------------
/rootfs/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -n "$VNC_PASSWORD" ]; then
4 | echo -n "$VNC_PASSWORD" > /.password1
5 | x11vnc -storepasswd $(cat /.password1) /.password2
6 | chmod 400 /.password*
7 | sed -i 's/^command=x11vnc.*/& -rfbauth \/.password2/' /etc/supervisor/conf.d/supervisord.conf
8 | export VNC_PASSWORD=
9 | fi
10 |
11 | if [ -n "$X11VNC_ARGS" ]; then
12 | sed -i "s/^command=x11vnc.*/& ${X11VNC_ARGS}/" /etc/supervisor/conf.d/supervisord.conf
13 | fi
14 |
15 | if [ -n "$OPENBOX_ARGS" ]; then
16 | sed -i "s#^command=/usr/bin/openbox\$#& ${OPENBOX_ARGS}#" /etc/supervisor/conf.d/supervisord.conf
17 | fi
18 |
19 | if [ -n "$RESOLUTION" ]; then
20 | sed -i "s/1024x768/$RESOLUTION/" /usr/local/bin/xvfb.sh
21 | fi
22 |
23 | USER=${USER:-root}
24 | HOME=/root
25 | if [ "$USER" != "root" ]; then
26 | echo "* enable custom user: $USER"
27 | useradd --create-home --shell /bin/bash --user-group --groups adm,sudo $USER
28 | if [ -z "$PASSWORD" ]; then
29 | echo " set default password to \"ubuntu\""
30 | PASSWORD=ubuntu
31 | fi
32 | HOME=/home/$USER
33 | echo "$USER:$PASSWORD" | chpasswd
34 | cp -r /root/{.config,.gtkrc-2.0,.asoundrc} ${HOME}
35 | chown -R $USER:$USER ${HOME}
36 | [ -d "/dev/snd" ] && chgrp -R adm /dev/snd
37 | fi
38 | sed -i -e "s|%USER%|$USER|" -e "s|%HOME%|$HOME|" /etc/supervisor/conf.d/supervisord.conf
39 |
40 | # home folder
41 | if [ ! -x "$HOME/.config/pcmanfm/LXDE/" ]; then
42 | mkdir -p $HOME/.config/pcmanfm/LXDE/
43 | ln -sf /usr/local/share/doro-lxde-wallpapers/desktop-items-0.conf $HOME/.config/pcmanfm/LXDE/
44 | chown -R $USER:$USER $HOME
45 | fi
46 |
47 | # nginx workers
48 | sed -i 's|worker_processes .*|worker_processes 1;|' /etc/nginx/nginx.conf
49 |
50 | # nginx ssl
51 | if [ -n "$SSL_PORT" ] && [ -e "/etc/nginx/ssl/nginx.key" ]; then
52 | echo "* enable SSL"
53 | sed -i 's|#_SSL_PORT_#\(.*\)443\(.*\)|\1'$SSL_PORT'\2|' /etc/nginx/sites-enabled/default
54 | sed -i 's|#_SSL_PORT_#||' /etc/nginx/sites-enabled/default
55 | fi
56 |
57 | # nginx http base authentication
58 | if [ -n "$HTTP_PASSWORD" ]; then
59 | echo "* enable HTTP base authentication"
60 | htpasswd -bc /etc/nginx/.htpasswd $USER $HTTP_PASSWORD
61 | sed -i 's|#_HTTP_PASSWORD_#||' /etc/nginx/sites-enabled/default
62 | fi
63 |
64 | # dynamic prefix path renaming
65 | if [ -n "$RELATIVE_URL_ROOT" ]; then
66 | echo "* enable RELATIVE_URL_ROOT: $RELATIVE_URL_ROOT"
67 | sed -i 's|#_RELATIVE_URL_ROOT_||' /etc/nginx/sites-enabled/default
68 | sed -i 's|_RELATIVE_URL_ROOT_|'$RELATIVE_URL_ROOT'|' /etc/nginx/sites-enabled/default
69 | fi
70 |
71 | # clearup
72 | PASSWORD=
73 | HTTP_PASSWORD=
74 |
75 | exec /bin/tini -- supervisord -n -c /etc/supervisor/supervisord.conf
76 |
--------------------------------------------------------------------------------
/rootfs/usr/local/bin/chromium-browser-sound.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z "$ALSADEV" ]; then
4 | zenity --error --text "To support audio, please read README.md and run container with --device /dev/snd -e ALSADEV=..."
5 | exit 1
6 | fi
7 |
8 | exec /usr/bin/google-chrome --no-sandbox --alsa-output-device="$ALSADEV" "$@"
9 |
--------------------------------------------------------------------------------
/rootfs/usr/local/bin/xvfb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | exec /usr/bin/Xvfb :1 -screen 0 1024x768x24
4 |
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/config/__init__.py:
--------------------------------------------------------------------------------
1 | class Default(object):
2 | DEBUG = True
3 |
4 |
5 | class Development(Default):
6 | PHASE = 'development'
7 |
8 |
9 | class Production(Default):
10 | PHASE = 'production'
11 | DEBUG = False
12 |
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/log/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fcwu/docker-ubuntu-vnc-desktop/e4922ce92f945fc482994b7a0fd95ca5de7295b3/rootfs/usr/local/lib/web/backend/log/__init__.py
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/log/config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import logging
4 | import logging.handlers
5 |
6 |
7 | # The terminal has 8 colors with codes from 0 to 7
8 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
9 |
10 | # These are the sequences need to get colored ouput
11 | RESET_SEQ = "\033[0m"
12 | COLOR_SEQ = "\033[1;%dm"
13 | BOLD_SEQ = "\033[1m"
14 |
15 | # The background is set with 40 plus the number of the color,
16 | # and the foreground with 30
17 | COLORS = {
18 | 'WARNING': COLOR_SEQ % (30 + YELLOW) + 'WARN ' + RESET_SEQ,
19 | 'INFO': COLOR_SEQ % (30 + WHITE) + 'INFO ' + RESET_SEQ,
20 | 'DEBUG': COLOR_SEQ % (30 + BLUE) + 'DEBUG' + RESET_SEQ,
21 | 'CRITICAL': COLOR_SEQ % (30 + YELLOW) + 'CRITI' + RESET_SEQ,
22 | 'ERROR': COLOR_SEQ % (30 + RED) + 'ERROR' + RESET_SEQ,
23 | }
24 |
25 |
26 | class ColoredFormatter(logging.Formatter):
27 | def __init__(self, msg, use_color=True):
28 | logging.Formatter.__init__(self, msg)
29 | self.use_color = use_color
30 |
31 | def format(self, record):
32 | if self.use_color:
33 | record.levelname = COLORS.get(record.levelname, record.levelname)
34 | return logging.Formatter.format(self, record)
35 |
36 |
37 | class LoggingConfiguration(object):
38 | COLOR_FORMAT = "%(asctime)s" + \
39 | " %(levelname)s %(message)s " + \
40 | "(" + BOLD_SEQ + "%(filename)s" + RESET_SEQ + ":%(lineno)d)"
41 | NO_COLOR_FORMAT = "%(asctime)s %(levelname)s " + \
42 | "%(message)s " + \
43 | "(%(filename)s:%(lineno)d)"
44 | FILE_FORMAT = "%(asctime)s %(levelname)s " + \
45 | "%(message)s "
46 |
47 | @classmethod
48 | def set(cls, log_level, log_filename, append=None, **kwargs):
49 | """ Configure a rotating file logging
50 | """
51 | logger = logging.getLogger()
52 | logger.setLevel(log_level)
53 |
54 | COLOR_FORMAT = cls.COLOR_FORMAT
55 | NO_COLOR_FORMAT = cls.NO_COLOR_FORMAT
56 | FILE_FORMAT = cls.FILE_FORMAT
57 | if 'name' in kwargs:
58 | COLOR_FORMAT = COLOR_FORMAT.replace('%(threadName)-22s',
59 | '%-22s' % (kwargs['name']))
60 | NO_COLOR_FORMAT = NO_COLOR_FORMAT.replace(
61 | '%(threadName)-22s', '%-22s' % (kwargs['name']))
62 | FILE_FORMAT = FILE_FORMAT.replace(
63 | '%(threadName)-22s', '%s' % (kwargs['name']))
64 |
65 | # Log to rotating file
66 | try:
67 | fh = logging.handlers.RotatingFileHandler(
68 | log_filename,
69 | mode='a+',
70 | backupCount=3
71 | )
72 | fh.setFormatter(ColoredFormatter(FILE_FORMAT, False))
73 | fh.setLevel(log_level)
74 | logger.addHandler(fh)
75 | if not append:
76 | # Create a new log file on every new
77 | fh.doRollover()
78 | except IOError as e:
79 | print('ignore to log to {}: {}'.format(log_filename, e))
80 |
81 | # Log to sys.stderr using log level passed through command line
82 | if log_level != logging.NOTSET:
83 | log_handler = logging.StreamHandler(sys.stdout)
84 | if sys.platform.find('linux') >= 0:
85 | formatter = ColoredFormatter(COLOR_FORMAT)
86 | else:
87 | formatter = ColoredFormatter(NO_COLOR_FORMAT, False)
88 | log_handler.setFormatter(formatter)
89 | log_handler.setLevel(log_level)
90 | logger.addHandler(log_handler)
91 |
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | backports.ssl-match-hostname==3.7.0.1
2 | certifi==2019.9.11
3 | chardet==3.0.4
4 | Click==7.0
5 | Flask==1.1.1
6 | Flask-Login==0.4.1
7 | gevent==1.4.0
8 | gevent-websocket==0.10.1
9 | greenlet==0.4.15
10 | idna==2.8
11 | itsdangerous==1.1.0
12 | Jinja2==2.11.3
13 | MarkupSafe==1.1.1
14 | meld3==2.0.0
15 | requests==2.22.0
16 | six==1.12.0
17 | urllib3==1.25.6
18 | websocket-client==0.47.0
19 | Werkzeug==0.16.0
20 |
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/run.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from __future__ import (
3 | absolute_import, division, print_function, with_statement
4 | )
5 | import os
6 | import time
7 | import sys
8 | import subprocess
9 | from vnc.util import ignored
10 |
11 |
12 | def main():
13 | def run_with_reloader(main_func, extra_files=None, interval=3):
14 | """Run the given function in an independent python interpreter."""
15 | def find_files(directory="./"):
16 | for root, dirs, files in os.walk(directory):
17 | for basename in files:
18 | if basename.endswith('.py'):
19 | filename = os.path.join(root, basename)
20 | yield filename
21 |
22 | if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
23 | try:
24 | main_func()
25 | except KeyboardInterrupt:
26 | pass
27 | return
28 |
29 | proc = None
30 | try:
31 | while True:
32 | log.info('Restarting with reloader {} {}'.format(
33 | sys.executable,
34 | ' '.join(sys.argv))
35 | )
36 | args = [sys.executable] + sys.argv
37 | new_environ = os.environ.copy()
38 | new_environ['WERKZEUG_RUN_MAIN'] = 'true'
39 |
40 | proc = subprocess.Popen(
41 | args,
42 | env=new_environ,
43 | close_fds=True,
44 | preexec_fn=os.setsid
45 | )
46 | mtimes = {}
47 | restart = False
48 | while not restart:
49 | for filename in find_files():
50 | try:
51 | mtime = os.stat(filename).st_mtime
52 | except OSError:
53 | continue
54 |
55 | old_time = mtimes.get(filename)
56 | if old_time is None:
57 | mtimes[filename] = mtime
58 | continue
59 | elif mtime > old_time:
60 | log.info(
61 | 'Detected change in {}, reloading'.format(
62 | filename
63 | )
64 | )
65 | restart = True
66 | proc.terminate()
67 | break
68 | time.sleep(interval)
69 | except KeyboardInterrupt:
70 | pass
71 | finally:
72 | with ignored(Exception):
73 | proc.terminate()
74 |
75 | def run_server():
76 | import socket
77 | from gevent.pywsgi import WSGIServer
78 | from vnc.app import app
79 |
80 | # websocket conflict: WebSocketHandler
81 | if DEBUG:
82 | # from werkzeug.debug import DebuggedApplication
83 | app.debug = True
84 | # app = DebuggedApplication(app, evalex=True)
85 |
86 | try:
87 | log.info('Listening on http://localhost:{}'.format(PORT))
88 | http_server = WSGIServer(('localhost', PORT), app)
89 | http_server.serve_forever()
90 | # app.run(host='localhost', port=PORT)
91 | except socket.error as e:
92 | log.exception(e)
93 | except KeyboardInterrupt:
94 | pass
95 | finally:
96 | http_server.stop(timeout=10)
97 | log.info('shutdown gracefully')
98 |
99 | PORT = 6079
100 | DEBUG = False
101 | os.environ['CONFIG'] = 'config.Production'
102 | entrypoint = run_server
103 | if '--debug' in sys.argv:
104 | DEBUG = True
105 | os.environ['CONFIG'] = 'config.Development'
106 | entrypoint = lambda: run_with_reloader(run_server)
107 |
108 | # logging
109 | import logging
110 | from log.config import LoggingConfiguration
111 | LoggingConfiguration.set(
112 | logging.DEBUG if DEBUG else logging.INFO,
113 | '/var/log/web.log'
114 | )
115 | logging.getLogger("werkzeug").setLevel(logging.WARNING)
116 | log = logging.getLogger('novnc2')
117 |
118 | entrypoint()
119 |
120 |
121 | if __name__ == "__main__":
122 | main()
123 |
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/vnc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fcwu/docker-ubuntu-vnc-desktop/e4922ce92f945fc482994b7a0fd95ca5de7295b3/rootfs/usr/local/lib/web/backend/vnc/__init__.py
--------------------------------------------------------------------------------
/rootfs/usr/local/lib/web/backend/vnc/app.py:
--------------------------------------------------------------------------------
1 | from __future__ import (
2 | absolute_import, division, print_function, with_statement
3 | )
4 | import re
5 | import os
6 | from flask import (
7 | Flask,
8 | request,
9 | Response,
10 | jsonify,
11 | abort,
12 | )
13 | from gevent import subprocess as gsp, spawn, sleep
14 | from geventwebsocket.exceptions import WebSocketError
15 | from .response import httperror
16 | from .util import ignored
17 | from .state import state
18 | from .log import log
19 |
20 |
21 | # Flask app
22 | app = Flask('novnc2')
23 | app.config.from_object('config.Default')
24 | app.config.from_object(os.environ.get('CONFIG') or 'config.Development')
25 |
26 |
27 | @app.route('/api/state')
28 | @httperror
29 | def apistate():
30 | state.wait(int(request.args.get('id', -1)), 30)
31 | state.switch_video(request.args.get('video', 'false') == 'true')
32 | mystate = state.to_dict()
33 | return jsonify({
34 | 'code': 200,
35 | 'data': mystate,
36 | })
37 |
38 |
39 | @app.route('/api/health')
40 | def apihealth():
41 | if state.health:
42 | return 'success'
43 | abort(503, 'unhealthy')
44 |
45 |
46 | @app.route('/api/reset')
47 | def reset():
48 | if 'w' in request.args and 'h' in request.args:
49 | args = {
50 | 'w': int(request.args.get('w')),
51 | 'h': int(request.args.get('h')),
52 | }
53 | state.set_size(args['w'], args['h'])
54 |
55 | state.apply_and_restart()
56 |
57 | # check all running
58 | for i in range(40):
59 | if state.health:
60 | break
61 | sleep(1)
62 | log.info('wait services is ready...')
63 | else:
64 | return jsonify({
65 | 'code': 500,
66 | 'errorMessage': 'service is not ready, please restart container'
67 | })
68 | return jsonify({'code': 200})
69 |
70 |
71 | @app.route('/resize')
72 | @httperror
73 | def apiresize():
74 | state.reset_size()
75 | return '