From 32734c3aa0cff658c61b1cfdba38d81210200180 Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Sat, 13 Mar 2021 16:17:21 +0000 Subject: [PATCH] Build Plex on musl, from scratch Plex now provide a first-party musl Plex build that works without any external dependencies whatsoever. It's built with LLVM with many compiler and linker optimisations enabled: https://forums.plex.tv/t/plex-media-server-forum-preview-faster-and-smaller-builds-with-new-toolchain/699575 Changes for this release include: - Drop curl, OpenSSL and zlib; they're no longer required. libcurl and libssl/libcrypto are provided by Plex anyway. - Build `FROM spritsail/alpine` instead of `FROM debian` to ensure musl compatibility with all compiled binaries. Use `FROM scratch` for the resulting image. ld-musl is provided by Plex. - Build busybox, su-exec and tini as they're no longer provided by the base image. - Build binaries/libraries with standard hardening flags, including the popular -flto. Signed-off-by: Joe Groocock --- .drone.yml | 66 ++--------------- Dockerfile | 185 ++++++++++++++++++++++-------------------------- README.md | 6 +- claim-server.sh | 25 +++---- 4 files changed, 102 insertions(+), 180 deletions(-) diff --git a/.drone.yml b/.drone.yml index 4631ccb..7f26f77 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,12 +2,6 @@ kind: pipeline name: build-amd64 -trigger: - event: - - push - - pull_request - - tag - platform: os: linux arch: amd64 @@ -24,7 +18,7 @@ steps: image: spritsail/docker-test settings: run: | - curl --version && + busybox && \ xmlstarlet --version - name: test @@ -44,65 +38,15 @@ steps: settings: repo: spritsail/plex-media-server tags: - - latest - - "%label io.spritsail.version.plex | %remsuf [0-9a-f]+$ | %auto 2" - username: {from_secret: docker_username} - password: {from_secret: docker_password} + - musl + login: {from_secret: docker_login} when: branch: - - master + - musl event: - push - ---- -kind: pipeline -name: update-readme - -steps: -- name: dockerhub-readme - pull: always - image: jlesage/drone-push-readme - settings: - username: {from_secret: docker_username} - password: {from_secret: docker_password} - repo: spritsail/plex-media-server - when: - branch: - - master - - pass - event: - - push - ---- -kind: pipeline -name: update-cron - -trigger: - event: - - cron - -platform: - os: linux - arch: amd64 - -steps: -- name: update - pull: always - image: spritsail/alpine - commands: - - apk add bash curl jq git - - ./update.sh - -- name: push - pull: always - image: appleboy/drone-git-push - settings: - branch: master - remote: git@github.com:spritsail/plex-media-server.git - ssh_key: {from_secret: git_ssh_key} - --- kind: signature -hmac: ffa33c4e8d4d71e4a1dcbc850859ebde679f6c574885d99df3ca9496e1db55b9 +hmac: 63bede647ba8e4f4f0a69983c22535fbda0afa811b60830c31edb0fbbf0b8b86 ... diff --git a/Dockerfile b/Dockerfile index a6b81c9..080c6b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,49 @@ -ARG PLEX_VER=1.21.4.4079-1b7748a7b -ARG PLEX_SHA=081d0ad5dacf455216495e40161be4e45727ceb8 +ARG PLEX_VER=1.22.2.4180-2f337bbd5 +ARG PLEX_SHA=23b53f67e65a4310cb12f3dd6566de5c3224104f ARG XMLSTAR_VER=1.6.1 -ARG CURL_VER=curl-7_74_0 -ARG ZLIB_VER=1.2.11 -ARG OPENSSL_VER=1.1.1i +ARG BUSYBOX_VER=1.33.0 +ARG SU_EXEC_VER=0.4 +ARG TINI_VER=0.19.0 -FROM spritsail/debian-builder:buster-slim as builder +FROM spritsail/alpine:3.13 AS builder ARG PLEX_VER ARG PLEX_SHA ARG LIBXML2_VER=v2.9.10 ARG LIBXSLT_VER=v1.1.34 ARG XMLSTAR_VER -ARG OPENSSL_VER -ARG CURL_VER -ARG ZLIB_VER +ARG BUSYBOX_VER +ARG SU_EXEC_VER +ARG TINI_VER ARG MAKEFLAGS ARG PREFIX=/prefix WORKDIR /plex +ENV CFLAGS="-O2 -pipe -fstack-protector-strong -D_FORTIFY_SOURCE=2 -flto" \ + CXXFLAGS="${CFLAGS}" \ + LDFLAGS="${CFLAGS} -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now" + +RUN apk add --no-cache \ + autoconf \ + automake \ + binutils \ + cmake \ + curl \ + dpkg \ + file \ + gcc \ + git \ + libtool \ + linux-headers \ + make \ + musl-dev \ + pkgconfig \ + xxd + # Fetch Plex and required libraries -RUN curl -fsSL -o plexmediaserver.deb https://downloads.plex.tv/plex-media-server-new/${PLEX_VER}/debian/plexmediaserver_${PLEX_VER}_amd64.deb \ +RUN curl -fsSL -o plexmediaserver.deb https://artifacts.plex.tv/testing/pms/${PLEX_VER}/debian/plexmediaserver_${PLEX_VER}_amd64.deb \ && echo "$PLEX_SHA plexmediaserver.deb" | sha1sum -c - \ && dpkg-deb -x plexmediaserver.deb . \ \ @@ -32,34 +53,51 @@ RUN curl -fsSL -o plexmediaserver.deb https://downloads.plex.tv/plex-media-serve \ && cd usr/lib/plexmediaserver \ && rm \ - lib/libcrypto.so* \ - lib/libcurl.so* \ - lib/libssl.so* \ lib/libxml2.so* \ lib/libxslt.so* \ lib/libexslt.so* \ lib/plexmediaserver.* \ Resources/start.sh \ + \ # Place shared libraries in usr/lib so they can be actually shared && mv lib/*.so* lib/dri ../ \ && rmdir lib \ - && cp /lib/x86_64-linux-gnu/libgcc_s.so.1 ../ + && ln -sv ../ lib -# Download and build zlib -WORKDIR /tmp/zlib -RUN curl -sSf https://www.zlib.net/zlib-$ZLIB_VER.tar.xz \ - | tar xJ --strip-components=1 \ - && ./configure \ - --prefix=/usr \ - --shared \ - && make DESTDIR=$PREFIX install +WORKDIR /tmp/busybox + +# Download and build busybox +RUN curl -fsSL https://busybox.net/downloads/busybox-${BUSYBOX_VER}.tar.bz2 \ + | tar xj --strip-components=1 \ + && make defconfig \ + && make \ + && install -Dm755 busybox "${PREFIX}/bin/busybox" \ + # "Install" busybox, creating symlinks to all binaries it provides + && mkdir -p "${PREFIX}/bin" "${PREFIX}/sbin" "${PREFIX}/usr/bin" "${PREFIX}/usr/sbin" \ + && ./busybox --list-full | xargs -i ln -Tsv /bin/busybox "${PREFIX}/{}" + +WORKDIR /tmp/su-exec + +# Download and build su-exec +RUN curl -fL https://github.com/frebib/su-exec/archive/v${SU_EXEC_VER}.tar.gz \ + | tar xz --strip-components=1 \ + && make \ + && install -Dm755 su-exec "${PREFIX}/sbin/su-exec" + +WORKDIR /tmp/tini + +# Download and build tini +RUN curl -fL https://github.com/krallin/tini/archive/v${TINI_VER}.tar.gz \ + | tar xz --strip-components=1 \ + && cmake . \ + && make tini \ + && install -Dm755 tini "${PREFIX}/sbin/tini" # Download and build libxml2 WORKDIR /tmp/libxml2 RUN git clone https://gitlab.gnome.org/GNOME/libxml2.git --branch $LIBXML2_VER --depth 1 . \ && ./autogen.sh \ --prefix=/usr \ - --with-zlib=$PREFIX/usr \ --without-catalog \ --without-docbook \ --without-ftp \ @@ -95,99 +133,43 @@ RUN git clone git://git.code.sf.net/p/xmlstar/code --branch $XMLSTAR_VER --depth --with-libxslt-prefix=$PREFIX/usr \ && make DESTDIR=$PREFIX install -# Download and build OpenSSL as a cURL dependency -WORKDIR /tmp/openssl -RUN curl -sSL https://openssl.org/source/openssl-${OPENSSL_VER}.tar.gz \ - | tar xz --strip-components=1 \ - # Install to the default system directories so cURL can find it - && ./config \ - --prefix=/usr \ - --libdir=lib \ - --with-zlib-lib=$PREFIX/usr/lib/ \ - --with-zlib-include=$PREFIX/usr/include \ - shared \ - zlib-dynamic \ - no-rc5 \ - no-ssl3-method \ - && make build_libs \ - && make build_programs \ - && make \ - install_sw \ - install_ssldirs \ - && cp libssl*.so* libcrypto*.so* $PREFIX/usr/lib - -# Download and build curl -WORKDIR /tmp/curl -RUN git clone https://github.com/curl/curl.git --branch $CURL_VER --depth 1 . \ - && autoreconf -sif \ - && ./configure \ - --prefix=/usr \ - --enable-ipv6 \ - --enable-optimize \ - --enable-symbol-hiding \ - --enable-versioned-symbols \ - --enable-threaded-resolver \ - --with-ssl \ - --with-zlib=$PREFIX/usr \ - --disable-crypto-auth \ - --disable-curldebug \ - --disable-dependency-tracking \ - --disable-dict \ - --disable-gopher \ - --disable-imap \ - --disable-libcurl-option \ - --disable-ldap \ - --disable-ldaps \ - --disable-manual \ - --disable-ntlm-wb \ - --disable-pop3 \ - --disable-rtsp \ - --disable-smb \ - --disable-smtp \ - --disable-sspi \ - --disable-telnet \ - --disable-tftp \ - --disable-tls-srp \ - --disable-verbose \ - --without-axtls \ - --without-libmetalink \ - --without-libpsl \ - --without-librtmp \ - --without-winidn \ - && make DESTDIR=$PREFIX install - WORKDIR $PREFIX -RUN mkdir -p /output/usr/lib /output/usr/bin /output/etc/ssl/certs \ +RUN mkdir -p \ + /output/usr/lib \ + /output/usr/bin \ + /output/usr/sbin \ + /output/etc/ssl/certs \ + && install -m 1777 -o root -g root -d /output/tmp \ + && ln -s /usr/lib /usr/bin /usr/sbin /output/ \ # Link Plex ca-certificates as system store so curl and others can use them too && ln -sv /usr/lib/plexmediaserver/Resources/cacert.pem /output/etc/ssl/certs/ca-certificates.crt \ + # Move binaries and libraries into their final locations && mv usr/lib/*.so* \ /plex/usr/lib/* \ /output/usr/lib \ - && mv usr/bin/curl /output/usr/bin \ - && mv usr/bin/xml /output/usr/bin/xmlstarlet - + && mv usr/bin/xml /output/usr/bin/xmlstarlet \ + && mv bin/* usr/bin/* /output/usr/bin \ + && mv sbin/* usr/sbin/* /output/usr/sbin \ # Strip all unneeded symbols for optimum size -RUN find /output -exec sh -c 'file "{}" | grep -q ELF && strip --strip-debug "{}"' \; \ - # Disable executable stack in all libraries. This should already be the case - # but it seems libgnsdk is not playing along - && apt-get -y update \ - && apt-get -y install execstack \ - && execstack -c /output/usr/lib/*.so* + && find /output -type f -exec sh -c 'file "{}" | grep -q ELF && strip --strip-debug "{}"' \; ADD --chmod=755 \ entrypoint \ - *.sh \ - /output/usr/local/bin/ + claim-server.sh \ + gen-config.sh \ + plex-util.sh \ + /output/usr/bin/ #========================= -FROM spritsail/busybox:latest +FROM scratch ARG PLEX_VER -ARG CURL_VER -ARG OPENSSL_VER ARG XMLSTAR_VER +ARG BUSYBOX_VER +ARG SU_EXEC_VER +ARG TINI_VER LABEL maintainer="Spritsail " \ org.label-schema.vendor="Spritsail" \ @@ -196,8 +178,7 @@ LABEL maintainer="Spritsail " \ org.label-schema.description="Tiny Docker image for Plex Media Server, built on busybox" \ org.label-schema.version=${PLEX_VER} \ io.spritsail.version.plex=${PLEX_VER} \ - io.spritsail.version.curl=${CURL_VER} \ - io.spritsail.version.openssl=${OPENSSL_VER} \ + io.spritsail.version.busybox=${BUSYBOX_VER} \ io.spritsail.version.xmlstarlet=${XMLSTAR_VER} WORKDIR /usr/lib/plexmediaserver @@ -222,4 +203,4 @@ RUN mkdir -p "$PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR" \ && ln -sfv /config "$PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR/Plex Media Server" ENTRYPOINT ["/sbin/tini", "--"] -CMD ["/usr/local/bin/entrypoint"] +CMD ["/usr/bin/entrypoint"] diff --git a/README.md b/README.md index 426ea28..793dc26 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,10 @@ [![Build Status](https://drone.spritsail.io/api/badges/spritsail/plex-media-server/status.svg)][drone] [![Last Build](https://api.spritsail.io/badge/lastbuild/spritsail/plex-media-server:latest)][drone] -The _smallest*_ Plex Media Server docker image, built on barebones [spritsail/busybox](https://hub.docker.com/r/spritsail/busybox/) with glibc and libraries built from source. The container hosts a fully featured Plex Media Server, with almost all of the useless crap removed, resulting in the smallest container possible whilst maintaining full functionality. - -You can find out more about the [spritsail/busybox](https://hub.docker.com/r/spritsail/busybox) base image [here](https://github.com/spritsail/busybox) +The _smallest*_ Plex Media Server docker image, built `FROM scratch` with musl provided by Plex and supporting libraries and binaries built from source. The container hosts a fully featured Plex Media Server, with almost all of the useless crap removed, resulting in the smallest container possible whilst maintaining full functionality. _*last we checked_ -**NOTICE:** This build has changed the `/config/Plex Media Server` mountpoint inside the container to now be present at `/config`. If you previously used this container, please update your mountpoint to `/config`. - ## Getting Started Navigate to [plex.tv/claim](https://www.plex.tv/claim) and obtain a token in the form `claim-xxxx...` diff --git a/claim-server.sh b/claim-server.sh index d9cf853..de9307b 100755 --- a/claim-server.sh +++ b/claim-server.sh @@ -2,9 +2,9 @@ set -e # Contains getPref/setPref and PREF_FILE vars -source plex-util.sh +. plex-util.sh -opts=`getopt -n "$0" -l save -l token: -l client-id: -l load-client-id -- st:c:l "$@"` || exit 1 +opts=$(getopt -n "$0" -l save -l token: -l client-id: -l load-client-id -- st:c:l "$@") || exit 1 eval set -- "$opts" while true; do case "$1" in @@ -33,16 +33,17 @@ if [ -z "${clientId}" ]; then fi >&2 echo "Attempting to obtain server token from claim token" -loginInfo="$(curl -X POST \ - -H 'X-Plex-Client-Identifier: '${clientId} \ - -H 'X-Plex-Product: Plex Media Server'\ - -H 'X-Plex-Version: 1.1' \ - -H 'X-Plex-Provides: server' \ - -H 'X-Plex-Platform: Linux' \ - -H 'X-Plex-Platform-Version: 1.0' \ - -H 'X-Plex-Device-Name: PlexMediaServer' \ - -H 'X-Plex-Device: Linux' \ - "https://plex.tv/api/claim/exchange?token=${claimToken}")" +loginInfo="$(wget -O- --post-data= \ + --header "X-Plex-Client-Identifier: ${clientId}" \ + --header "X-Plex-Product: Plex Media Server" \ + --header "X-Plex-Version: 1.1" \ + --header "X-Plex-Provides: server" \ + --header "X-Plex-Platform: Linux" \ + --header "X-Plex-Platform-Version: 1.0" \ + --header "X-Plex-Device-Name: PlexMediaServer" \ + --header "X-Plex-Device: Linux" \ + "https://plex.tv/api/claim/exchange?token=${claimToken}" +)" authtoken="$(echo "$loginInfo" | sed -n 's/.*\(.*\)<\/authentication-token>.*/\1/p')"