From 97f710716fca43f73c9db6f1962869d9f071b1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Molinero=20Fern=C3=A1ndez?= Date: Sun, 16 Jun 2019 11:17:51 +0200 Subject: [PATCH] First commit --- .dockerignore | 1 + .gitignore | 1 + .gitlab-ci.yml | 137 ++++++++++ Dockerfile.m4 | 425 ++++++++++++++++++++++++++++++ LICENSE.md | 22 ++ Makefile | 258 ++++++++++++++++++ README.md | 27 ++ config/ssh/sshd_config | 29 ++ config/xrdp/sesman.ini | 40 +++ config/xrdp/xrdp.ini | 77 ++++++ run.sh | 44 ++++ scripts/bin/docker-foreground-cmd | 37 +++ scripts/bin/export-env | 12 + scripts/service/dbus-daemon/run | 3 + scripts/service/sshd/run | 3 + scripts/service/xdummy/run | 7 + scripts/service/xrdp-sesman/run | 3 + scripts/service/xrdp/run | 3 + 18 files changed, 1129 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile.m4 create mode 100644 LICENSE.md create mode 100755 Makefile create mode 100644 README.md create mode 100644 config/ssh/sshd_config create mode 100644 config/xrdp/sesman.ini create mode 100644 config/xrdp/xrdp.ini create mode 100755 run.sh create mode 100755 scripts/bin/docker-foreground-cmd create mode 100755 scripts/bin/export-env create mode 100755 scripts/service/dbus-daemon/run create mode 100755 scripts/service/sshd/run create mode 100755 scripts/service/xdummy/run create mode 100755 scripts/service/xrdp-sesman/run create mode 100755 scripts/service/xrdp/run diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..178135c --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +/dist/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..178135c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dist/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..72185c1 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,137 @@ +image: docker:stable + +services: + - docker:dind + +stages: + - build:images + - push:images + - push:manifests + +variables: + DOCKER_HOST: tcp://docker:2375 + DOCKER_DRIVER: overlay2 + +build:native-image: + stage: build:images + before_script: + - docker info + - apk add --no-cache coreutils git m4 make xz + script: + - make build-native-image save-native-image + except: + - tags + artifacts: + expire_in: 1 day + paths: + - dist/ + +build:amd64-image: + stage: build:images + before_script: + - docker info + - apk add --no-cache coreutils git m4 make xz + - make binfmt-register + script: + - make build-amd64-image save-amd64-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + artifacts: + expire_in: 1 week + paths: + - dist/ + +build:arm32v7-image: + stage: build:images + before_script: + - docker info + - apk add --no-cache coreutils git m4 make xz + - make binfmt-register + script: + - make build-arm32v7-image save-arm32v7-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + artifacts: + expire_in: 1 week + paths: + - dist/ + +build:arm64v8-image: + stage: build:images + before_script: + - docker info + - apk add --no-cache coreutils git m4 make xz + - make binfmt-register + script: + - make build-arm64v8-image save-arm64v8-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + artifacts: + expire_in: 1 week + paths: + - dist/ + +push:amd64-image: + stage: push:images + before_script: + - apk add --no-cache coreutils git make xz + - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}" >/dev/null 2>&1 + script: + - make load-amd64-image push-amd64-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + dependencies: + - build:amd64-image + +push:arm32v7-image: + stage: push:images + before_script: + - apk add --no-cache coreutils git make xz + - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}" >/dev/null 2>&1 + script: + - make load-arm32v7-image push-arm32v7-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + dependencies: + - build:arm32v7-image + +push:arm64v8-image: + stage: push:images + before_script: + - apk add --no-cache coreutils git make xz + - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}" >/dev/null 2>&1 + script: + - make load-arm64v8-image push-arm64v8-image + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + dependencies: + - build:arm64v8-image + +push:cross-manifest: + stage: push:manifests + before_script: + - apk add --no-cache coreutils git make xz + - "mkdir -p ~/.docker/ && printf '%s\n' '{\"experimental\": \"enabled\"}' > ~/.docker/config.json" + - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}" >/dev/null 2>&1 + script: + - make push-cross-manifest + only: + - /^v([0-9.]+)(-.+)?$/ + except: + - branches + dependencies: + - push:amd64-image + - push:arm32v7-image + - push:arm64v8-image diff --git a/Dockerfile.m4 b/Dockerfile.m4 new file mode 100644 index 0000000..a002428 --- /dev/null +++ b/Dockerfile.m4 @@ -0,0 +1,425 @@ +m4_changequote([[, ]]) + +################################################## +## "build" stage +################################################## + +m4_ifdef([[CROSS_ARCH]], [[FROM docker.io/CROSS_ARCH/ubuntu:18.04]], [[FROM docker.io/ubuntu:18.04]]) AS build +m4_ifdef([[CROSS_QEMU]], [[COPY --from=docker.io/hectormolinero/qemu-user-static:latest CROSS_QEMU CROSS_QEMU]]) + +# Install system packages +RUN export DEBIAN_FRONTEND=noninteractive \ + # Remove Canonical's "partner" repository + && sed -i '/archive\.canonical\.com/d;' /etc/apt/sources.list \ + # Uncomment source packages repositories + && sed -i 's/^#\s*\(deb-src\s\)/\1/g' /etc/apt/sources.list \ +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl + # Enable multiarch support + && dpkg --add-architecture i386 \ +]])m4_dnl + && apt-get update \ + && apt-get install -y --no-install-recommends \ + autoconf \ + automake \ + bison \ + build-essential \ + ca-certificates \ + checkinstall \ + cmake \ + flex \ + git \ + intltool \ + libfdk-aac-dev \ + libfuse-dev \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + libmp3lame-dev \ + libopus-dev \ + libpam0g-dev \ + libpixman-1-dev \ + libpulse-dev \ + libssl-dev \ + libsystemd-dev \ + libtool \ + libx11-dev \ + libxext-dev \ + libxfixes-dev \ + libxml2-dev \ + libxrandr-dev \ + libxtst-dev \ + libxv-dev \ + nasm \ + pkg-config \ + python \ + python-libxml2 \ + texinfo \ + xserver-xorg-dev \ + xsltproc \ + xutils-dev \ +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl + g++-multilib \ + libgl1-mesa-dev:i386 \ + libglu1-mesa-dev:i386 \ + libxtst-dev:i386 \ + libxv-dev:i386 \ +]])m4_dnl + && rm -rf /var/lib/apt/lists/* + +# Build libjpeg-turbo +ARG LIBJPEG_TURBO_TREEISH=2.0.2 +ARG LIBJPEG_TURBO_REMOTE=https://github.com/libjpeg-turbo/libjpeg-turbo.git +WORKDIR /tmp/libjpeg-turbo/ +RUN git clone "${LIBJPEG_TURBO_REMOTE}" ./ +RUN git checkout "${LIBJPEG_TURBO_TREEISH}" +RUN git submodule update --init --recursive +WORKDIR ./build/ +RUN cmake ./ \ + -G 'Unix Makefiles' \ + -D PKGNAME=libjpeg-turbo \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=/opt/libjpeg-turbo \ + -D CMAKE_POSITION_INDEPENDENT_CODE=1 \ + ../ +RUN make -j"$(nproc)" && make deb +RUN dpkg -i --force-architecture ./libjpeg-turbo_*.deb +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl +WORKDIR ../build32/ +RUN CFLAGS='-m32' CXXFLAGS='-m32' LDFLAGS='-m32' \ + cmake ./ \ + -G 'Unix Makefiles' \ + -D PKGNAME=libjpeg-turbo \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=/opt/libjpeg-turbo \ + -D CMAKE_POSITION_INDEPENDENT_CODE=1 \ + ../ +RUN make -j"$(nproc)" && make deb +RUN dpkg -i --force-architecture ./libjpeg-turbo32_*.deb +]])m4_dnl + +# Build VirtualGL +ARG VIRTUALGL_TREEISH=2.6.2 +ARG VIRTUALGL_REMOTE=https://github.com/VirtualGL/virtualgl.git +WORKDIR /tmp/virtualgl/ +RUN git clone "${VIRTUALGL_REMOTE}" ./ +RUN git checkout "${VIRTUALGL_TREEISH}" +RUN git submodule update --init --recursive +WORKDIR ./build/ +RUN cmake ./ \ + -G 'Unix Makefiles' \ + -D PKGNAME=virtualgl \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=/opt/VirtualGL \ + -D CMAKE_POSITION_INDEPENDENT_CODE=1 \ + ../ +RUN make -j"$(nproc)" && make deb +RUN dpkg -i --force-architecture ./virtualgl_*.deb +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl +WORKDIR ../build32/ +RUN CFLAGS='-m32' CXXFLAGS='-m32' LDFLAGS='-m32' \ + cmake ./ \ + -G 'Unix Makefiles' \ + -D PKGNAME=virtualgl \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=/opt/VirtualGL \ + -D CMAKE_POSITION_INDEPENDENT_CODE=1 \ + ../ +RUN make -j"$(nproc)" && make deb +RUN dpkg -i --force-architecture ./virtualgl32_*.deb +]])m4_dnl + +# Build XRDP +ARG XRDP_TREEISH=v0.9.10 +ARG XRDP_REMOTE=https://github.com/neutrinolabs/xrdp.git +WORKDIR /tmp/xrdp/ +RUN git clone "${XRDP_REMOTE}" ./ +RUN git checkout "${XRDP_TREEISH}" +RUN git submodule update --init --recursive +RUN ./bootstrap +RUN ./configure \ + --prefix=/usr \ + --enable-vsock \ + --enable-tjpeg \ + --enable-fuse \ + --enable-fdkaac \ + --enable-opus \ + --enable-mp3lame \ + --enable-pixman +RUN make -j"$(nproc)" +RUN checkinstall --default --pkgname=xrdp --pkgversion=0 --pkgrelease=0 + +# Build xorgxrdp +ARG XORGXRDP_TREEISH=v0.2.10 +ARG XORGXRDP_REMOTE=https://github.com/neutrinolabs/xorgxrdp.git +WORKDIR /tmp/xorgxrdp/ +RUN git clone "${XORGXRDP_REMOTE}" ./ +RUN git checkout "${XORGXRDP_TREEISH}" +RUN git submodule update --init --recursive +RUN ./bootstrap +RUN ./configure +RUN make -j"$(nproc)" +RUN checkinstall --default --pkgname=xorgxrdp --pkgversion=0 --pkgrelease=0 + +# Build XRDP PulseAudio module +ARG XRDP_PULSEAUDIO_TREEISH=v0.3 +ARG XRDP_PULSEAUDIO_REMOTE=https://github.com/neutrinolabs/pulseaudio-module-xrdp.git +WORKDIR /tmp/xrdp-pulseaudio/ +RUN git clone "${XRDP_PULSEAUDIO_REMOTE}" ./ +RUN git checkout "${XRDP_PULSEAUDIO_TREEISH}" +RUN git submodule update --init --recursive +RUN apt-get update +RUN apt-get build-dep -y pulseaudio +RUN apt-get source pulseaudio +WORKDIR ./pulseaudio-11.1/ +RUN ./configure +WORKDIR ../ +RUN ./bootstrap +RUN ./configure PULSE_DIR="$(pwd)"/pulseaudio-11.1/ +RUN make -j"$(nproc)" +RUN checkinstall --default --pkgname=xrdp-pulseaudio --pkgversion=0 --pkgrelease=0 + +################################################## +## "xubuntu" stage +################################################## + +m4_ifdef([[CROSS_ARCH]], [[FROM docker.io/CROSS_ARCH/ubuntu:18.04]], [[FROM docker.io/ubuntu:18.04]]) AS xubuntu +m4_ifdef([[CROSS_QEMU]], [[COPY --from=docker.io/hectormolinero/qemu-user-static:latest CROSS_QEMU CROSS_QEMU]]) + +# Install system packages +RUN export DEBIAN_FRONTEND=noninteractive \ +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl + && dpkg --add-architecture i386 \ +]])m4_dnl + && apt-get update \ + && apt-get install -y --no-install-recommends \ + adwaita-qt \ + apt-utils \ + atril \ + bash \ + ca-certificates \ + curl \ + dbus \ + dbus-x11 \ + desktop-file-utils \ + dialog \ + engrampa \ + file \ + firefox \ + fonts-dejavu \ + fonts-liberation \ + fonts-noto \ + fonts-noto-color-emoji \ + fuse \ + gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-ugly \ + gtk2-engines-pixbuf \ + gtk2-engines-xfce \ + gtk3-engines-xfce \ + htop \ + iproute2 \ + less \ + libavcodec-extra \ + libcanberra-gtk-module \ + libcanberra-gtk3-module \ + libfdk-aac1 \ + libgl1-mesa-dri \ + libgl1-mesa-glx \ + libglu1-mesa \ + libgtk-3-bin \ + libmp3lame0 \ + libopus0 \ + libpam0g \ + libpixman-1-0 \ + libpulse0 \ + libssl1.1 \ + libsystemd0 \ + libx11-6 \ + libxext6 \ + libxfixes3 \ + libxml2 \ + libxrandr2 \ + libxtst6 \ + libxv1 \ + locales \ + menu \ + menu-xdg \ + menulibre \ + mesa-utils \ + mesa-utils-extra \ + mime-support \ + mousepad \ + mugshot \ + nano \ + openssh-server \ + openssl \ + pavucontrol \ + procps \ + psmisc \ + pulseaudio \ + pulseaudio-utils \ + ristretto \ + runit \ + sudo \ + thunar-archive-plugin \ + thunar-volman \ + tzdata \ + unzip \ + vlc \ + xauth \ + xdg-user-dirs \ + xdg-utils \ + xfce4 \ + xfce4-indicator-plugin \ + xfce4-notifyd \ + xfce4-pulseaudio-plugin \ + xfce4-statusnotifier-plugin \ + xfce4-taskmanager \ + xfce4-terminal \ + xfce4-whiskermenu-plugin \ + xfce4-xkb-plugin \ + xfonts-base \ + xfpanel-switch \ + xserver-xorg-core \ + xserver-xorg-video-all \ + xterm \ + xubuntu-default-settings \ + xutils \ + xz-utils \ + zenity \ + zip \ +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl + libgl1-mesa-dri:i386 \ + libgl1-mesa-glx:i386 \ + libglu1-mesa:i386 \ + libxtst6:i386 \ + libxv1:i386 \ +]])m4_dnl + && rm -rf /var/lib/apt/lists/* + +# Copy Tini build +m4_define([[TINI_IMAGE_TAG]], m4_ifdef([[CROSS_ARCH]], [[latest-CROSS_ARCH]], [[latest]]))m4_dnl +COPY --from=docker.io/hectormolinero/tini:TINI_IMAGE_TAG --chown=root:root /usr/bin/tini /usr/bin/tini + +# Install libjpeg-turbo from package +COPY --from=build --chown=root:root /tmp/libjpeg-turbo/build/libjpeg-turbo_*.deb /tmp/libjpeg-turbo.deb +RUN dpkg -i --force-architecture /tmp/libjpeg-turbo.deb && rm -f /tmp/libjpeg-turbo.deb +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl +COPY --from=build --chown=root:root /tmp/libjpeg-turbo/build32/libjpeg-turbo32_*.deb /tmp/libjpeg-turbo32.deb +RUN dpkg -i --force-architecture /tmp/libjpeg-turbo32.deb && rm -f /tmp/libjpeg-turbo32.deb +]])m4_dnl + +# Install VirtualGL from package +COPY --from=build --chown=root:root /tmp/virtualgl/build/virtualgl_*.deb /tmp/virtualgl.deb +RUN dpkg -i --force-architecture /tmp/virtualgl.deb && rm -f /tmp/virtualgl.deb +m4_ifelse(ENABLE_32BIT, 1, [[m4_dnl +COPY --from=build --chown=root:root /tmp/virtualgl/build32/virtualgl32_*.deb /tmp/virtualgl32.deb +RUN dpkg -i --force-architecture /tmp/virtualgl32.deb && rm -f /tmp/virtualgl32.deb +]])m4_dnl + +# Install XRDP from package +COPY --from=build --chown=root:root /tmp/xrdp/xrdp_*.deb /tmp/xrdp.deb +RUN dpkg -i /tmp/xrdp.deb && rm -f /tmp/xrdp.deb + +# Install xorgxrdp from package +COPY --from=build --chown=root:root /tmp/xorgxrdp/xorgxrdp_*.deb /tmp/xorgxrdp.deb +RUN dpkg -i /tmp/xorgxrdp.deb && rm -f /tmp/xorgxrdp.deb + +# Install XRDP PulseAudio module from package +COPY --from=build --chown=root:root /tmp/xrdp-pulseaudio/xrdp-pulseaudio_*.deb /tmp/xrdp-pulseaudio.deb +RUN dpkg -i /tmp/xrdp-pulseaudio.deb && rm -f /tmp/xrdp-pulseaudio.deb + +# Environment +ENV RDP_TLS_KEY_PATH=/etc/xrdp/key.pem +ENV RDP_TLS_CERT_PATH=/etc/xrdp/cert.pem +ENV PATH=/opt/VirtualGL/bin:"${PATH}" +ENV VGL_DISPLAY=:0 +ENV QT_STYLE_OVERRIDE=Adwaita + +# Setup locale +RUN sed -i 's|^# \(en_US\.UTF-8 UTF-8\)$|\1|' /etc/locale.gen && locale-gen +ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 + +# Setup timezone +ENV TZ=Etc/UTC +RUN ln -sf /usr/share/zoneinfo/"${TZ}" /etc/localtime + +# Setup D-Bus +RUN mkdir /run/dbus/ && chown messagebus:messagebus /run/dbus/ +RUN dbus-uuidgen > /etc/machine-id && ln -sf /var/lib/dbus/machine-id /etc/machine-id + +# Forward logs to Docker log collector +RUN ln -sf /dev/stdout /var/log/xdummy.log +RUN ln -sf /dev/stdout /var/log/xrdp.log +RUN ln -sf /dev/stdout /var/log/xrdp-sesman.log + +# Create /etc/skel/.xsession file +RUN printf '%s\n' 'xfce4-session' \ + >> /etc/skel/.xsession + +# Create /etc/skel/.xsessionrc file +RUN printf '%s\n' \ + 'export XDG_CACHE_HOME=${HOME}/.cache' \ + 'export XDG_CONFIG_DIRS=/etc/xdg/xdg-xubuntu:/etc/xdg' \ + 'export XDG_CONFIG_HOME=${HOME}/.config' \ + 'export XDG_CURRENT_DESKTOP=XFCE' \ + 'export XDG_DATA_DIRS=/usr/share/xubuntu:/usr/share/xfce4:/usr/local/share:/usr/share' \ + 'export XDG_DATA_HOME=${HOME}/.local/share' \ + 'export XDG_MENU_PREFIX=xfce-' \ + 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' \ + 'export XDG_SESSION_DESKTOP=xubuntu' \ + >> /etc/skel/.xsessionrc + +# Create /etc/skel/.Xauthority file +RUN touch /etc/skel/.Xauthority + +# Create /run/sshd directory +RUN mkdir /run/sshd/ + +# Create socket directory for X server +RUN mkdir /tmp/.X11-unix/ \ + && chmod 1777 /tmp/.X11-unix/ \ + && chown root:root /tmp/.X11-unix/ + +# Configure server for use with VirtualGL +RUN vglserver_config -config +s +f -t + +# Create guest user and group +ARG GUEST_USER_UID=1000 +ARG GUEST_USER_GID=1000 +RUN groupadd --gid "${GUEST_USER_GID}" guest +RUN useradd \ + --uid "${GUEST_USER_UID}" \ + --gid "${GUEST_USER_GID}" \ + --shell "$(command -v bash)" \ + --groups audio,video \ + --home-dir /home/guest/ \ + --create-home \ + guest + +# Set guest user password +ARG GUEST_USER_PASSWORD=guest +RUN printf '%s' guest:"${GUEST_USER_PASSWORD}" | chpasswd + +# Create /run/user/${GUEST_USER_UID}/dbus-1/ directory +RUN mkdir -p /run/user/"${GUEST_USER_UID}"/dbus-1/ \ + && chmod -R 700 /run/user/"${GUEST_USER_UID}"/ \ + && chown -R guest:guest /run/user/"${GUEST_USER_UID}"/ + +# Copy config +COPY --chown=root:root config/ssh/sshd_config /etc/ssh/sshd_config +COPY --chown=root:root config/xrdp/xrdp.ini /etc/xrdp/xrdp.ini +COPY --chown=root:root config/xrdp/sesman.ini /etc/xrdp/sesman.ini + +# Copy services +COPY --chown=root:root scripts/service/ /etc/sv/ +RUN find /etc/sv/ -type d -mindepth 1 -maxdepth 1 -exec ln -sv '{}' /etc/service/ ';' + +# Copy scripts +COPY --chown=root:root scripts/bin/ /usr/local/bin/ + +# Expose RDP port +EXPOSE 3389/tcp + +WORKDIR / +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["/usr/local/bin/docker-foreground-cmd"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7597f5c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License (MIT) +===================== + +Copyright © 2019 Héctor Molinero Fernández + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..0a29ef2 --- /dev/null +++ b/Makefile @@ -0,0 +1,258 @@ +#!/usr/bin/make -f + +SHELL := /bin/sh +.SHELLFLAGS := -eu -c + +DOCKER := $(shell command -v docker 2>/dev/null) +GIT := $(shell command -v git 2>/dev/null) +M4 := $(shell command -v m4 2>/dev/null) + +DISTDIR := ./dist +VERSION_FILE = ./VERSION +DOCKERFILE_TEMPLATE := ./Dockerfile.m4 + +IMAGE_REGISTRY := docker.io +IMAGE_NAMESPACE := hectormolinero +IMAGE_PROJECT := xubuntu +IMAGE_NAME := $(IMAGE_REGISTRY)/$(IMAGE_NAMESPACE)/$(IMAGE_PROJECT) + +IMAGE_VERSION := v0 +ifneq ($(wildcard $(VERSION_FILE)),) + IMAGE_VERSION := $(shell cat '$(VERSION_FILE)') +endif + +IMAGE_NATIVE_DOCKERFILE := $(DISTDIR)/Dockerfile +IMAGE_NATIVE_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).txz + +IMAGE_AMD64_DOCKERFILE := $(DISTDIR)/Dockerfile.amd64 +IMAGE_AMD64_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).amd64.txz + +IMAGE_ARM32V7_DOCKERFILE := $(DISTDIR)/Dockerfile.arm32v7 +IMAGE_ARM32V7_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).arm32v7.txz + +IMAGE_ARM64V8_DOCKERFILE := $(DISTDIR)/Dockerfile.arm64v8 +IMAGE_ARM64V8_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).arm64v8.txz + +################################################## +## "all" target +################################################## + +.PHONY: all +all: save-native-image + +################################################## +## "build-*" targets +################################################## + +.PHONY: build-native-image +build-native-image: $(IMAGE_NATIVE_DOCKERFILE) + +$(IMAGE_NATIVE_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D ENABLE_32BIT=1 \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)' \ + --tag '$(IMAGE_NAME):latest' \ + --file '$@' ./ + +.PHONY: build-cross-images +build-cross-images: build-amd64-image build-arm32v7-image build-arm64v8-image + +.PHONY: build-amd64-image +build-amd64-image: $(IMAGE_AMD64_DOCKERFILE) + +$(IMAGE_AMD64_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=amd64 \ + -D CROSS_QEMU=/usr/bin/qemu-x86_64-static \ + -D ENABLE_32BIT=1 \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-amd64' \ + --tag '$(IMAGE_NAME):latest-amd64' \ + --file '$@' ./ + +.PHONY: build-arm32v7-image +build-arm32v7-image: $(IMAGE_ARM32V7_DOCKERFILE) + +$(IMAGE_ARM32V7_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=arm32v7 \ + -D CROSS_QEMU=/usr/bin/qemu-arm-static \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7' \ + --tag '$(IMAGE_NAME):latest-arm32v7' \ + --file '$@' ./ + +.PHONY: build-arm64v8-image +build-arm64v8-image: $(IMAGE_ARM64V8_DOCKERFILE) + +$(IMAGE_ARM64V8_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=arm64v8 \ + -D CROSS_QEMU=/usr/bin/qemu-aarch64-static \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8' \ + --tag '$(IMAGE_NAME):latest-arm64v8' \ + --file '$@' ./ + +################################################## +## "save-*" targets +################################################## + +define save_image + '$(DOCKER)' save '$(1)' | xz -T0 > '$(2)' +endef + +.PHONY: save-native-image +save-native-image: $(IMAGE_NATIVE_TARBALL) + +$(IMAGE_NATIVE_TARBALL): $(IMAGE_NATIVE_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION),$@) + +.PHONY: save-cross-images +save-cross-images: save-amd64-image save-arm32v7-image save-arm64v8-image + +.PHONY: save-amd64-image +save-amd64-image: $(IMAGE_AMD64_TARBALL) + +$(IMAGE_AMD64_TARBALL): $(IMAGE_AMD64_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64,$@) + +.PHONY: save-arm32v7-image +save-arm32v7-image: $(IMAGE_ARM32V7_TARBALL) + +$(IMAGE_ARM32V7_TARBALL): $(IMAGE_ARM32V7_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7,$@) + +.PHONY: save-arm64v8-image +save-arm64v8-image: $(IMAGE_ARM64V8_TARBALL) + +$(IMAGE_ARM64V8_TARBALL): $(IMAGE_ARM64V8_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8,$@) + +################################################## +## "load-*" targets +################################################## + +define load_image + '$(DOCKER)' load -i '$(1)' +endef + +define tag_image + '$(DOCKER)' tag '$(1)' '$(2)' +endef + +.PHONY: load-native-image +load-native-image: + $(call load_image,$(IMAGE_NATIVE_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION),$(IMAGE_NAME):latest) + +.PHONY: load-cross-images +load-cross-images: load-amd64-image load-arm32v7-image load-arm64v8-image + +.PHONY: load-amd64-image +load-amd64-image: + $(call load_image,$(IMAGE_AMD64_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64,$(IMAGE_NAME):latest-amd64) + +.PHONY: load-arm32v7-image +load-arm32v7-image: + $(call load_image,$(IMAGE_ARM32V7_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7,$(IMAGE_NAME):latest-arm32v7) + +.PHONY: load-arm64v8-image +load-arm64v8-image: + $(call load_image,$(IMAGE_ARM64V8_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8,$(IMAGE_NAME):latest-arm64v8) + +################################################## +## "push-*" targets +################################################## + +define push_image + '$(DOCKER)' push '$(1)' +endef + +define push_cross_manifest + '$(DOCKER)' manifest create --amend '$(1)' '$(2)-amd64' '$(2)-arm32v7' '$(2)-arm64v8' + '$(DOCKER)' manifest annotate '$(1)' '$(2)-amd64' --os linux --arch amd64 + '$(DOCKER)' manifest annotate '$(1)' '$(2)-arm32v7' --os linux --arch arm --variant v7 + '$(DOCKER)' manifest annotate '$(1)' '$(2)-arm64v8' --os linux --arch arm64 --variant v8 + '$(DOCKER)' manifest push --purge '$(1)' +endef + +.PHONY: push-native-image +push-native-image: + @printf '%s\n' 'Unimplemented' + +.PHONY: push-cross-images +push-cross-images: push-amd64-image push-arm32v7-image push-arm64v8-image + +.PHONY: push-amd64-image +push-amd64-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64) + $(call push_image,$(IMAGE_NAME):latest-amd64) + +.PHONY: push-arm32v7-image +push-arm32v7-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7) + $(call push_image,$(IMAGE_NAME):latest-arm32v7) + +.PHONY: push-arm64v8-image +push-arm64v8-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8) + $(call push_image,$(IMAGE_NAME):latest-arm64v8) + +push-cross-manifest: + $(call push_cross_manifest,$(IMAGE_NAME):$(IMAGE_VERSION),$(IMAGE_NAME):$(IMAGE_VERSION)) + $(call push_cross_manifest,$(IMAGE_NAME):latest,$(IMAGE_NAME):latest) + +################################################## +## "binfmt-*" targets +################################################## + +.PHONY: binfmt-register +binfmt-register: + '$(DOCKER)' run --rm --privileged docker.io/multiarch/qemu-user-static:register + +.PHONY: binfmt-reset +binfmt-reset: + '$(DOCKER)' run --rm --privileged docker.io/multiarch/qemu-user-static:register --reset + +################################################## +## "version" target +################################################## + +.PHONY: version +version: + @if printf -- '%s' '$(IMAGE_VERSION)' | grep -q '^v[0-9]\{1,\}$$'; then \ + NEW_IMAGE_VERSION=$$(awk -v 'v=$(IMAGE_VERSION)' 'BEGIN {printf "v%.0f", substr(v,2)+1}'); \ + printf -- '%s\n' "$${NEW_IMAGE_VERSION}" > '$(VERSION_FILE)'; \ + '$(GIT)' add '$(VERSION_FILE)'; '$(GIT)' commit -m "$${NEW_IMAGE_VERSION}"; \ + '$(GIT)' tag -a "$${NEW_IMAGE_VERSION}" -m "$${NEW_IMAGE_VERSION}"; \ + else \ + >&2 printf -- 'Malformed version string: %s\n' '$(IMAGE_VERSION)'; \ + exit 1; \ + fi + +################################################## +## "clean" target +################################################## + +.PHONY: clean +clean: + rm -f '$(IMAGE_NATIVE_DOCKERFILE)' '$(IMAGE_AMD64_DOCKERFILE)' '$(IMAGE_ARM32V7_DOCKERFILE)' '$(IMAGE_ARM64V8_DOCKERFILE)' + rm -f '$(IMAGE_NATIVE_TARBALL)' '$(IMAGE_AMD64_TARBALL)' '$(IMAGE_ARM32V7_TARBALL)' '$(IMAGE_ARM64V8_TARBALL)' + if [ -d '$(DISTDIR)' ] && [ -z "$$(ls -A '$(DISTDIR)')" ]; then rmdir '$(DISTDIR)'; fi diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e910b4 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Xubuntu on Docker + +A Docker image based on Ubuntu 18.04 with Xfce desktop environment, +[VirtualGL](https://github.com/VirtualGL/virtualgl), +[XRDP](https://github.com/neutrinolabs/xrdp) and +[XRDP PulseAudio module](https://github.com/neutrinolabs/pulseaudio-module-xrdp). + +## Start an instance + +```sh +docker run --detach \ + --name xubuntu \ + --publish 3389:3389/tcp \ + hectormolinero/xubuntu:latest +``` + +> You will be able to connect to the container via RDP through 3389/tcp port. + +> **Important:** if you use the `--privileged` option the container will be able to use the GPU with +VirtualGL, but this will conflict with the host X server. + +> **Important:** some software (like Firefox) need the shared memory to be increased, if you +encounter any problem related to this you may use the `--shm-size` option. + +## Environment variables + +* `GUEST_USER_PASSWORD`: guest user password (`guest` by default). diff --git a/config/ssh/sshd_config b/config/ssh/sshd_config new file mode 100644 index 0000000..c23c9bd --- /dev/null +++ b/config/ssh/sshd_config @@ -0,0 +1,29 @@ +Protocol 2 +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key + +Port 3322 +ListenAddress 0.0.0.0 + +StrictModes yes + +UsePAM yes +PermitRootLogin no +PubkeyAuthentication yes +PasswordAuthentication yes +PermitEmptyPasswords no +ChallengeResponseAuthentication no +TCPKeepAlive yes +LoginGraceTime 30 +ClientAliveInterval 300 +ClientAliveCountMax 1 + +X11Forwarding yes +X11DisplayOffset 10 +X11UseLocalhost no + +PrintMotd no +PrintLastLog yes + +SyslogFacility AUTH +LogLevel INFO diff --git a/config/xrdp/sesman.ini b/config/xrdp/sesman.ini new file mode 100644 index 0000000..b3af3f5 --- /dev/null +++ b/config/xrdp/sesman.ini @@ -0,0 +1,40 @@ +[Globals] +ListenAddress=127.0.0.1 +ListenPort=3350 +EnableUserWindowManager=true +UserWindowManager=startwm.sh +DefaultWindowManager=startwm.sh +ReconnectScript=reconnectwm.sh + +[Security] +AllowRootLogin=false +MaxLoginRetry=0 +AlwaysGroupCheck=false + +[Sessions] +X11DisplayOffset=10 +MaxSessions=10 +KillDisconnected=true +DisconnectedTimeLimit=120 +IdleTimeLimit=0 +Policy=Default + +[Logging] +LogFile=/var/log/xrdp-sesman.log +LogLevel=INFO +EnableSyslog=0 +SyslogLevel=INFO + +[Xorg] +param=/usr/lib/xorg/Xorg +param=-config +param=xrdp/xorg.conf +param=-noreset +param=-nolisten +param=tcp + +[Chansrv] +FuseMountName=.thinclient_drives + +[SessionVariables] +PULSE_SCRIPT=/etc/xrdp/pulse/default.pa diff --git a/config/xrdp/xrdp.ini b/config/xrdp/xrdp.ini new file mode 100644 index 0000000..5538b73 --- /dev/null +++ b/config/xrdp/xrdp.ini @@ -0,0 +1,77 @@ +[Globals] +ini_version=1 +fork=true +address=0.0.0.0 +port=3389 +use_vsock=false +tcp_nodelay=true +tcp_keepalive=true +security_layer=tls +key_file=/etc/xrdp/key.pem +certificate=/etc/xrdp/cert.pem +ssl_protocols=TLSv1.2, TLSv1.3 +tls_ciphers=HIGH +autorun=XorgOther +allow_channels=true +allow_multimon=true +bitmap_cache=true +bitmap_compression=true +bulk_compression=true +max_bpp=32 +new_cursors=true +use_fastpath=both +blue=009cb5 +grey=dedede +ls_top_window_bg_color=009cb5 +ls_width=350 +ls_height=430 +ls_bg_color=dedede +ls_logo_filename= +ls_logo_x_pos=55 +ls_logo_y_pos=50 +ls_label_x_pos=30 +ls_label_width=65 +ls_input_x_pos=110 +ls_input_width=210 +ls_input_y_pos=220 +ls_btn_ok_x_pos=142 +ls_btn_ok_y_pos=370 +ls_btn_ok_width=85 +ls_btn_ok_height=30 +ls_btn_cancel_x_pos=237 +ls_btn_cancel_y_pos=370 +ls_btn_cancel_width=85 +ls_btn_cancel_height=30 + +[Logging] +LogFile=/var/log/xrdp.log +LogLevel=INFO +EnableSyslog=false +SyslogLevel=INFO + +[Channels] +rdpdr=true +rdpsnd=true +drdynvc=true +cliprdr=true +rail=true +xrdpvr=true +tcutils=true + +[XorgGuest] +name=Guest +lib=libxup.so +username=guest +password=ask +ip=127.0.0.1 +port=-1 +code=20 + +[XorgOther] +name=Other +lib=libxup.so +username=ask +password=ask +ip=127.0.0.1 +port=-1 +code=20 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..3dc1675 --- /dev/null +++ b/run.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -eu +export LC_ALL=C + +IMAGE_NAMESPACE=hectormolinero +IMAGE_PROJECT=xubuntu +IMAGE_TAG=latest +IMAGE_NAME=${IMAGE_NAMESPACE}/${IMAGE_PROJECT}:${IMAGE_TAG} +CONTAINER_NAME=${IMAGE_PROJECT} + +imageExists() { [ -n "$(docker images -q "$1")" ]; } +containerExists() { docker ps -aqf name="$1" --format '{{.Names}}' | grep -Fxq "$1"; } +containerIsRunning() { docker ps -qf name="$1" --format '{{.Names}}' | grep -Fxq "$1"; } + +if ! imageExists "${IMAGE_NAME}"; then + >&2 printf -- '%s\n' "\"${IMAGE_NAME}\" image doesn't exist!" + exit 1 +fi + +if containerIsRunning "${CONTAINER_NAME}"; then + printf -- '%s\n' "Stopping \"${CONTAINER_NAME}\" container..." + docker stop "${CONTAINER_NAME}" >/dev/null +fi + +if containerExists "${CONTAINER_NAME}"; then + printf -- '%s\n' "Removing \"${CONTAINER_NAME}\" container..." + docker rm "${CONTAINER_NAME}" >/dev/null +fi + +printf -- '%s\n' "Creating \"${CONTAINER_NAME}\" container..." +docker run --detach \ + --name "${CONTAINER_NAME}" \ + --hostname "${CONTAINER_NAME}" \ + --restart on-failure:3 \ + --log-opt max-size=32m \ + --publish 0.0.0.0:3322:3322/tcp \ + --publish 0.0.0.0:3389:3389/tcp \ + --privileged \ + --shm-size 2g \ + "${IMAGE_NAME}" "$@" >/dev/null + +printf -- '%s\n\n' 'Done!' +exec docker logs -f "${CONTAINER_NAME}" diff --git a/scripts/bin/docker-foreground-cmd b/scripts/bin/docker-foreground-cmd new file mode 100755 index 0000000..bcdeb20 --- /dev/null +++ b/scripts/bin/docker-foreground-cmd @@ -0,0 +1,37 @@ +#!/bin/sh + +set -eu + +# Disable xdummy if there is no graphics card +if [ ! -d /dev/dri/ ]; then + unlink /etc/service/xdummy +fi + +# Update guest user password +if [ -n "${GUEST_USER_PASSWORD-}" ]; then + printf '%s' "guest:${GUEST_USER_PASSWORD}" | chpasswd + unset GUEST_USER_PASSWORD +fi + +# Dump environment variables +export-env >> /etc/profile.d/env.sh + +# Generate self-signed certificate +if [ ! -f "${RDP_TLS_KEY_PATH}" ] || [ ! -f "${RDP_TLS_CERT_PATH}" ]; then + KEY_FILE=${RDP_TLS_KEY_PATH} + CRT_FILE=${RDP_TLS_CERT_PATH} + CSR_FILE=$(mktemp -u) + + (umask 077 \ + && openssl genrsa -out "${KEY_FILE}" 2048 \ + ) >/dev/null + + (umask 022 \ + && openssl req -new -subj "/CN=$(uname -n)" -key "${KEY_FILE}" -out "${CSR_FILE}" \ + && openssl x509 -req -days 3650 -signkey "${KEY_FILE}" -in "${CSR_FILE}" -out "${CRT_FILE}" \ + && rm -f "${CSR_FILE}" \ + ) >/dev/null +fi + +# Start all services +exec runsvdir -P /etc/service/ diff --git a/scripts/bin/export-env b/scripts/bin/export-env new file mode 100755 index 0000000..c140c6f --- /dev/null +++ b/scripts/bin/export-env @@ -0,0 +1,12 @@ +#!/usr/bin/awk -f + +BEGIN { + for (v in ENVIRON) { + if (v !~ /^(HOME|PWD|SHELL|USER|GROUP|UID|GID)$/) { + gsub(/[^0-9A-Za-z]/, "_", v) + gsub(/\n/, " ", ENVIRON[v]) + gsub(/'/, "'\\''", ENVIRON[v]) + print "export '"v"="ENVIRON[v]"'" + } + } +} diff --git a/scripts/service/dbus-daemon/run b/scripts/service/dbus-daemon/run new file mode 100755 index 0000000..30d10d7 --- /dev/null +++ b/scripts/service/dbus-daemon/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/bin/sudo -u messagebus /usr/bin/dbus-daemon --system --nofork diff --git a/scripts/service/sshd/run b/scripts/service/sshd/run new file mode 100755 index 0000000..bb20d03 --- /dev/null +++ b/scripts/service/sshd/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/sshd -D diff --git a/scripts/service/xdummy/run b/scripts/service/xdummy/run new file mode 100755 index 0000000..f8560e4 --- /dev/null +++ b/scripts/service/xdummy/run @@ -0,0 +1,7 @@ +#!/bin/sh + +exec /usr/bin/Xorg \ + -noreset -nolisten tcp \ + +extension GLX +extension RANDR +extension RENDER \ + -logfile /var/log/xdummy.log \ + :0 diff --git a/scripts/service/xrdp-sesman/run b/scripts/service/xrdp-sesman/run new file mode 100755 index 0000000..47707e1 --- /dev/null +++ b/scripts/service/xrdp-sesman/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/xrdp-sesman --nodaemon diff --git a/scripts/service/xrdp/run b/scripts/service/xrdp/run new file mode 100755 index 0000000..3a5c9a2 --- /dev/null +++ b/scripts/service/xrdp/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/xrdp --nodaemon