From 7f4246b6d53f1bead40c7031add21742815c3961 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 14 Aug 2022 23:35:39 -0700 Subject: [PATCH] GUACAMOLE-1540: Manual build all core protocol libraries for Docker image using Alpine Linux base. --- Dockerfile | 219 +++++++++++-------- src/guacd-docker/bin/build-all.sh | 115 ++++++++++ src/guacd-docker/bin/build-guacd.sh | 49 ----- src/guacd-docker/bin/link-freerdp-plugins.sh | 76 ------- src/guacd-docker/bin/list-dependencies.sh | 18 +- 5 files changed, 246 insertions(+), 231 deletions(-) create mode 100755 src/guacd-docker/bin/build-all.sh delete mode 100755 src/guacd-docker/bin/build-guacd.sh delete mode 100755 src/guacd-docker/bin/link-freerdp-plugins.sh diff --git a/Dockerfile b/Dockerfile index 81af0cb71..a556b4334 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,25 +21,32 @@ # Dockerfile for guacamole-server # -# The Ubuntu image that should be used as the basis for the guacd image -ARG UBUNTU_BASE_IMAGE=21.10 +# The Alpine Linux image that should be used as the basis for the guacd image +ARG ALPINE_BASE_IMAGE=latest +FROM alpine:${ALPINE_BASE_IMAGE} AS builder -# Use Debian as base for the build -FROM ubuntu:${UBUNTU_BASE_IMAGE} AS builder - -# -# The Debian repository that should be preferred for dependencies (this will be -# added to /etc/apt/sources.list if not already present) -# -# NOTE: Due to limitations of the Docker image build process, this value is -# duplicated in an ARG in the second stage of the build. -# -ARG UBUNTU_RELEASE=impish-backports +# Install build dependencies +RUN apk add --no-cache \ + autoconf \ + automake \ + build-base \ + cairo-dev \ + cmake \ + git \ + grep \ + libjpeg-turbo-dev \ + libpng-dev \ + libtool \ + libwebp-dev \ + make \ + openssl-dev \ + pango-dev \ + pulseaudio-dev \ + util-linux-dev -# Add repository for specified Ubuntu release if not already present in -# sources.list -RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \ - "deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_RELEASE} main contrib non-free" +# Copy source to container for sake of build +ARG BUILD_DIR=/tmp/guacamole-server +COPY . ${BUILD_DIR} # # Base directory for installed build artifacts. @@ -47,70 +54,99 @@ RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources # NOTE: Due to limitations of the Docker image build process, this value is # duplicated in an ARG in the second stage of the build. # -ARG PREFIX_DIR=/usr/local/guacamole - -# Build arguments -ARG BUILD_DIR=/tmp/guacd-docker-BUILD -ARG BUILD_DEPENDENCIES=" \ - autoconf \ - automake \ - freerdp2-dev \ - gcc \ - libcairo2-dev \ - libgcrypt-dev \ - libjpeg-turbo8-dev \ - libossp-uuid-dev \ - libpango1.0-dev \ - libpulse-dev \ - libssh2-1-dev \ - libssl-dev \ - libtelnet-dev \ - libtool \ - libvncserver-dev \ - libwebsockets-dev \ - libwebp-dev \ - make" - -# Do not require interaction during build -ARG DEBIAN_FRONTEND=noninteractive - -# Bring build environment up to date and install build dependencies -RUN apt-get update && \ - apt-get install -t ${UBUNTU_RELEASE} -y $BUILD_DEPENDENCIES && \ - rm -rf /var/lib/apt/lists/* - -# Add configuration scripts -COPY src/guacd-docker/bin "${PREFIX_DIR}/bin/" - -# Copy source to container for sake of build -COPY . "$BUILD_DIR" - -# Build guacamole-server from local source -RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR" +ARG PREFIX_DIR=/opt/guacamole + +# +# Automatically select the latest versions of each core protocol support +# library (these can be overridden at build time if a specific version is +# needed) +# +ARG WITH_FREERDP='2(\.\d+)+' +ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+' +ARG WITH_LIBTELNET='\d+(\.\d+)+' +ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+' +ARG WITH_LIBWEBSOCKETS='v\d+(\.\d+)+' + +# +# Default build options for each core protocol support library, as well as +# guacamole-server itself (these can be overridden at build time if different +# options are needed) +# + +ARG FREERDP_OPTS="\ + -DBUILTIN_CHANNELS=OFF \ + -DCHANNEL_URBDRC=OFF \ + -DWITH_ALSA=OFF \ + -DWITH_CAIRO=ON \ + -DWITH_CHANNELS=ON \ + -DWITH_CLIENT=ON \ + -DWITH_CUPS=OFF \ + -DWITH_DIRECTFB=OFF \ + -DWITH_FFMPEG=OFF \ + -DWITH_GSM=OFF \ + -DWITH_GSSAPI=OFF \ + -DWITH_IPP=OFF \ + -DWITH_JPEG=ON \ + -DWITH_LIBSYSTEMD=OFF \ + -DWITH_MANPAGES=OFF \ + -DWITH_OPENH264=OFF \ + -DWITH_OPENSSL=ON \ + -DWITH_OSS=OFF \ + -DWITH_PCSC=OFF \ + -DWITH_PULSE=OFF \ + -DWITH_SERVER=OFF \ + -DWITH_SERVER_INTERFACE=OFF \ + -DWITH_SHADOW_MAC=OFF \ + -DWITH_SHADOW_X11=OFF \ + -DWITH_SSE2=ON \ + -DWITH_WAYLAND=OFF \ + -DWITH_X11=OFF \ + -DWITH_X264=OFF \ + -DWITH_XCURSOR=ON \ + -DWITH_XEXT=ON \ + -DWITH_XI=OFF \ + -DWITH_XINERAMA=OFF \ + -DWITH_XKBFILE=ON \ + -DWITH_XRENDER=OFF \ + -DWITH_XTEST=OFF \ + -DWITH_XV=OFF \ + -DWITH_ZLIB=ON" + +ARG GUACAMOLE_SERVER_OPTS="\ + --disable-guaclog" + +ARG LIBSSH2_OPTS="\ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_SHARED_LIBS=ON" + +ARG LIBTELNET_OPTS="\ + --disable-static \ + --disable-util" + +ARG LIBVNCCLIENT_OPTS="" + +ARG LIBWEBSOCKETS_OPTS="\ + -DDISABLE_WERROR=ON \ + -DLWS_WITHOUT_SERVER=ON \ + -DLWS_WITHOUT_TESTAPPS=ON \ + -DLWS_WITHOUT_TEST_CLIENT=ON \ + -DLWS_WITHOUT_TEST_PING=ON \ + -DLWS_WITHOUT_TEST_SERVER=ON \ + -DLWS_WITHOUT_TEST_SERVER_EXTPOLL=ON \ + -DLWS_WITH_STATIC=OFF" + +# Build guacamole-server and its core protocol library dependencies +RUN ${BUILD_DIR}/src/guacd-docker/bin/build-all.sh # Record the packages of all runtime library dependencies -RUN ${PREFIX_DIR}/bin/list-dependencies.sh \ +RUN ${BUILD_DIR}/src/guacd-docker/bin/list-dependencies.sh \ ${PREFIX_DIR}/sbin/guacd \ ${PREFIX_DIR}/lib/libguac-client-*.so \ ${PREFIX_DIR}/lib/freerdp2/*guac*.so \ > ${PREFIX_DIR}/DEPENDENCIES -# Use same Debian as the base for the runtime image -FROM ubuntu:${UBUNTU_BASE_IMAGE} - -# -# The Debian repository that should be preferred for dependencies (this will be -# added to /etc/apt/sources.list if not already present) -# -# NOTE: Due to limitations of the Docker image build process, this value is -# duplicated in an ARG in the first stage of the build. -# -ARG UBUNTU_RELEASE=impish-backports - -# Add repository for specified Ubuntu release if not already present in -# sources.list -RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \ - "deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_RELEASE} main contrib non-free" +# Use same Alpine version as the base for the runtime image +FROM alpine:${ALPINE_BASE_IMAGE} # # Base directory for installed build artifacts. See also the @@ -119,36 +155,27 @@ RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources # NOTE: Due to limitations of the Docker image build process, this value is # duplicated in an ARG in the first stage of the build. # -ARG PREFIX_DIR=/usr/local/guacamole +ARG PREFIX_DIR=/opt/guacamole # Runtime environment ENV LC_ALL=C.UTF-8 ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib ENV GUACD_LOG_LEVEL=info -ARG RUNTIME_DEPENDENCIES=" \ - netcat-openbsd \ - ca-certificates \ - ghostscript \ - fonts-liberation \ - fonts-dejavu \ - xfonts-terminus" - -# Do not require interaction during build -ARG DEBIAN_FRONTEND=noninteractive - # Copy build artifacts into this stage COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR} # Bring runtime environment up to date and install runtime dependencies -RUN apt-get update && \ - apt-get install -t ${UBUNTU_RELEASE} -y --no-install-recommends $RUNTIME_DEPENDENCIES && \ - apt-get install -t ${UBUNTU_RELEASE} -y --no-install-recommends $(cat "${PREFIX_DIR}"/DEPENDENCIES) && \ - rm -rf /var/lib/apt/lists/* - -# Link FreeRDP plugins into proper path -RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \ - ${PREFIX_DIR}/lib/freerdp2/libguac*.so +RUN apk add --no-cache \ + ca-certificates \ + ghostscript \ + netcat-openbsd \ + shadow \ + terminus-font \ + ttf-dejavu \ + ttf-liberation \ + util-linux-login && \ + xargs apk add --no-cache < ${PREFIX_DIR}/DEPENDENCIES # Checks the operating status every 5 minutes with a timeout of 5 seconds HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1 @@ -157,7 +184,7 @@ HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1 ARG UID=1000 ARG GID=1000 RUN groupadd --gid $GID guacd -RUN useradd --system --create-home --shell /usr/sbin/nologin --uid $UID --gid $GID guacd +RUN useradd --system --create-home --shell /sbin/nologin --uid $UID --gid $GID guacd # Run with user guacd USER guacd @@ -170,5 +197,5 @@ EXPOSE 4822 # Note the path here MUST correspond to the value specified in the # PREFIX_DIR build argument. # -CMD /usr/local/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f +CMD /opt/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f diff --git a/src/guacd-docker/bin/build-all.sh b/src/guacd-docker/bin/build-all.sh new file mode 100755 index 000000000..937466e95 --- /dev/null +++ b/src/guacd-docker/bin/build-all.sh @@ -0,0 +1,115 @@ +#!/bin/sh -e +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +## +## @fn build-all.sh +## +## Builds the source of guacamole-server and its various core protocol library +## dependencies. +## + +# Pre-populate build control variables such that the custom build prefix is +# used for C headers, locating libraries, etc. +export CFLAGS="-I${PREFIX_DIR}/include" +export LDFLAGS="-L${PREFIX_DIR}/lib" +export PKG_CONFIG_PATH="${PREFIX_DIR}/lib/pkgconfig" + +# Ensure thread stack size will be 8 MB (glibc's default on Linux) rather than +# 128 KB (musl's default) +export LDFLAGS="$LDFLAGS -Wl,-z,stack-size=8388608" + +## +## Builds and installs the source at the given git repository, automatically +## switching to the version of the source at the tag/commit that matches the +## given pattern. +## +## @param URL +## The URL of the git repository that the source should be downloaded from. +## +## @param PATTERN +## The Perl-compatible regular expression that the tag must match. If no +## tag matches the regular expression, the pattern is assumed to be an +## exact reference to a commit, branch, etc. acceptable by git checkout. +## +## @param ... +## Any additional command-line options that should be provided to CMake or +## the configure script. +## +install_from_git() { + + URL="$1" + PATTERN="$2" + shift 2 + + # Calculate top-level directory name of resulting repository from the + # provided URL + REPO_DIR="$(basename "$URL" .git)" + + # Allow dependencies to be manually omitted with the tag/commit pattern "NO" + if [ "$PATTERN" = "NO" ]; then + echo "NOT building $REPO_DIR (explicitly skipped)" + return + fi + + # Clone repository and change to top-level directory of source + cd /tmp + git clone "$URL" + cd $REPO_DIR/ + + # Locate tag/commit based on provided pattern + VERSION="$(git tag -l --sort=-v:refname | grep -Px -m1 "$PATTERN" \ + || echo "$PATTERN")" + + # Switch to desired version of source + echo "Building $REPO_DIR @ $VERSION ..." + git -c advice.detachedHead=false checkout "$VERSION" + + # Configure build using CMake or GNU Autotools, whichever happens to be + # used by the library being built + if [ -e CMakeLists.txt ]; then + cmake -DCMAKE_INSTALL_PREFIX:PATH="$PREFIX_DIR" "$@" . + else + [ -e configure ] || autoreconf -fi + ./configure --prefix="$PREFIX_DIR" "$@" + fi + + # Build and install + make && make install + +} + +# +# Build and install core protocol library dependencies +# + +install_from_git "https://github.com/FreeRDP/FreeRDP" "$WITH_FREERDP" $FREERDP_OPTS +install_from_git "https://github.com/libssh2/libssh2" "$WITH_LIBSSH2" $LIBSSH2_OPTS +install_from_git "https://github.com/seanmiddleditch/libtelnet" "$WITH_LIBTELNET" $LIBTELNET_OPTS +install_from_git "https://github.com/LibVNC/libvncserver" "$WITH_LIBVNCCLIENT" $LIBVNCCLIENT_OPTS +install_from_git "https://libwebsockets.org/repo/libwebsockets" "$WITH_LIBWEBSOCKETS" $LIBWEBSOCKETS_OPTS + +# +# Build guacamole-server +# + +cd "$BUILD_DIR" +autoreconf -fi && ./configure --prefix="$PREFIX_DIR" $GUACAMOLE_SERVER_OPTS +make && make install + diff --git a/src/guacd-docker/bin/build-guacd.sh b/src/guacd-docker/bin/build-guacd.sh deleted file mode 100755 index 9e263aea5..000000000 --- a/src/guacd-docker/bin/build-guacd.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -e -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -## -## @fn build-guacd.sh -## -## Builds the source of guacamole-server, automatically creating any required -## symbolic links for the proper loading of FreeRDP plugins. -## -## @param BUILD_DIR -## The directory which currently contains the guacamole-server source and -## in which the build should be performed. -## -## @param PREFIX_DIR -## The directory prefix into which the build artifacts should be installed -## in which the build should be performed. This is passed to the --prefix -## option of `configure`. -## - -BUILD_DIR="$1" -PREFIX_DIR="$2" - -# -# Build guacamole-server -# - -cd "$BUILD_DIR" -autoreconf -fi -./configure --prefix="$PREFIX_DIR" --disable-guaclog --with-freerdp-plugin-dir="$PREFIX_DIR/lib/freerdp2" -make -make install -ldconfig diff --git a/src/guacd-docker/bin/link-freerdp-plugins.sh b/src/guacd-docker/bin/link-freerdp-plugins.sh deleted file mode 100755 index e4e85326f..000000000 --- a/src/guacd-docker/bin/link-freerdp-plugins.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -e -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -## -## @fn link-freerdp-plugins.sh -## -## Automatically creates any required symbolic links for the proper loading of -## the given FreeRDP plugins. If a given plugin is already in the correct -## directory, no link is created for that plugin. -## -## @param ... -## The FreeRDP plugins to add links for. -## - -## -## Locates the base directory of the FreeRDP installation (where the FreeRDP library -## .so files are located), printing the result to STDOUT. If the directory cannot be -## determined, an error is printed. -## -where_is_freerdp() { - - # Determine the location of any freerdp2 .so files - PATHS="$(find / -iname '*libfreerdp2.so.*' \ - | xargs -r dirname \ - | xargs -r realpath \ - | sort -u)" - - # Verify that exactly one location was found - if [ -z "$PATHS" -o "$(echo "$PATHS" | wc -l)" != 1 ]; then - echo "$1: Unable to locate FreeRDP install location." >&2 - return 1 - fi - - echo "$PATHS" - -} - -# -# Create symbolic links as necessary to include all given plugins within the -# search path of FreeRDP -# - -# Determine correct install location for FreeRDP plugins -FREERDP_DIR="$(where_is_freerdp)" -FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2" - -while [ -n "$1" ]; do - - # Add symbolic link if necessary - if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then - mkdir -p "$FREERDP_PLUGIN_DIR" - ln -s "$1" "$FREERDP_PLUGIN_DIR" - else - echo "$1: Already in correct directory." >&2 - fi - - shift - -done diff --git a/src/guacd-docker/bin/list-dependencies.sh b/src/guacd-docker/bin/list-dependencies.sh index dd02181b3..3055be463 100755 --- a/src/guacd-docker/bin/list-dependencies.sh +++ b/src/guacd-docker/bin/list-dependencies.sh @@ -21,7 +21,7 @@ ## ## @fn list-dependencies.sh ## -## Lists the Debian/Ubuntu package names for all library dependencies of the +## Lists the Alpine Linux package names for all library dependencies of the ## given binaries. Each package is only listed once, even if multiple binaries ## provided by the same package are given. ## @@ -35,19 +35,17 @@ while [ -n "$1" ]; do ldd "$1" | grep -v 'libguac' | awk '/=>/{print $(NF-1)}' \ | while read LIBRARY; do - # In some cases, the library that's linked against is a hard link - # to the file that's managed by the package, which dpkg doesn't understand. - # Searching by */basename ensures the package will be found in these cases. - LIBRARY_BASENAME=$(basename "$LIBRARY") - - # Determine the Debian package which is associated with that - # library, if any - dpkg-query -S "*/$LIBRARY_BASENAME" || true + # List the package providing that library, if any + apk info -W "$LIBRARY" 2> /dev/null \ + | grep 'is owned by' | grep -o '[^ ]*$' done # Next binary shift -done | cut -f1 -d: | sort -u +# Strip the "-VERSION" suffix from each package name, listing each resulting +# package uniquely ("apk add" cannot handle package names that include the +# version number) +done | sed 's/\(.*\)-[0-9]\..*$/\1/' | sort -u