First commit

This commit is contained in:
Héctor Molinero Fernández
2019-06-16 11:17:51 +02:00
commit 97f710716f
18 changed files with 1129 additions and 0 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
/dist/

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/dist/

137
.gitlab-ci.yml Normal file
View File

@@ -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

425
Dockerfile.m4 Normal file
View File

@@ -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"]

22
LICENSE.md Normal file
View File

@@ -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.

258
Makefile Executable file
View File

@@ -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

27
README.md Normal file
View File

@@ -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).

29
config/ssh/sshd_config Normal file
View File

@@ -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

40
config/xrdp/sesman.ini Normal file
View File

@@ -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

77
config/xrdp/xrdp.ini Normal file
View File

@@ -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

44
run.sh Executable file
View File

@@ -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}"

View File

@@ -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/

12
scripts/bin/export-env Executable file
View File

@@ -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]"'"
}
}
}

View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/bin/sudo -u messagebus /usr/bin/dbus-daemon --system --nofork

3
scripts/service/sshd/run Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/sbin/sshd -D

7
scripts/service/xdummy/run Executable file
View File

@@ -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

View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/sbin/xrdp-sesman --nodaemon

3
scripts/service/xrdp/run Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/sbin/xrdp --nodaemon