2
0
mirror of https://github.com/spritsail/plex-media-server.git synced 2024-06-14 11:57:23 +00:00

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

Restructure build into multiple distinct Docker build stages to better
leverage caching and significantly improve build time on multicore
systems with BuildKit, particularly with LTO enabled.

Changes for this release include:
- 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 <me@frebib.net>
This commit is contained in:
Joe Groocock 2021-05-28 11:12:19 +01:00
parent 48abfda1b6
commit f01f4950ab
Signed by: frebib
GPG Key ID: E0B16BEACFBB6A86
4 changed files with 224 additions and 85 deletions

View File

@ -18,7 +18,8 @@ steps:
image: spritsail/docker-test
settings:
run: |
curl --version &&
busybox && \
curl --version && \
xmlstarlet --version
- name: test
@ -40,8 +41,7 @@ steps:
tags:
- latest
- "%label io.spritsail.version.plex | %remsuf [0-9a-f]+$ | %auto 2"
username: {from_secret: docker_username}
password: {from_secret: docker_password}
login: {from_secret: docker_login}
when:
branch:
- master
@ -69,6 +69,6 @@ steps:
---
kind: signature
hmac: 6d4fdd6274fdab370550ea310af156a7e6fdb74794c80bf234a825d7136f6783
hmac: ee4c475518f6fddf4c2227fdee938a3e8d5c0a63e52fbab79f1133c8f5a2a91d
...

View File

@ -1,25 +1,52 @@
ARG PLEX_VER=1.22.3.4523-d0ce30438
ARG PLEX_SHA=da302c2607b246113500d5974a30b67d29a738ea
ARG XMLSTAR_VER=1.6.1
ARG CURL_VER=curl-7_74_0
ARG PLEX_VER=1.23.1.4571-6119e8eed
ARG PLEX_SHA=91f7fe52a66aa8099e2f8307f316afe728c3fc04
ARG BUSYBOX_VER=1.33.0
ARG SU_EXEC_VER=0.4
ARG TINI_VER=0.19.0
ARG ZLIB_VER=1.2.11
ARG LIBXML2_VER=v2.9.10
ARG LIBXSLT_VER=v1.1.34
ARG XMLSTAR_VER=1.6.1
ARG OPENSSL_VER=1.1.1i
ARG CURL_VER=curl-7_76_1
FROM spritsail/debian-builder:buster-slim as builder
ARG OUTPUT=/output
ARG DESTDIR=/prefix
ARG CFLAGS="-O2 -pipe -fstack-protector-strong -D_FORTIFY_SOURCE=2 -flto"
ARG LDFLAGS="$CFLAGS -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM spritsail/alpine:3.13 AS builder
RUN apk add --no-cache \
autoconf \
automake \
binutils \
cmake \
curl \
dpkg \
file \
gcc \
git \
libtool \
linux-headers \
make \
musl-dev \
nghttp2-dev \
pkgconfig \
xxd
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM builder AS plex
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 MAKEFLAGS
ARG OUTPUT
ARG PREFIX=/prefix
WORKDIR /plex
WORKDIR $OUTPUT
# 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 \
@ -35,31 +62,101 @@ RUN curl -fsSL -o plexmediaserver.deb https://downloads.plex.tv/plex-media-serve
lib/libcrypto.so* \
lib/libcurl.so* \
lib/libssl.so* \
lib/libnghttp2.so* \
lib/libxml2.so* \
lib/libxslt.so* \
lib/libexslt.so* \
lib/plexmediaserver.* \
etc/ld-musl-x86_64.path \
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 ../
&& rmdir lib etc \
&& ln -sv ../ lib \
# Replace hardlink with a symlink; these files are the same
&& cd .. && ln -sfvn ld-musl-x86_64.so.1 libc.so
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM builder AS busybox
ARG BUSYBOX_VER
ARG SU_EXEC_VER
ARG TINI_VER
ARG CFLAGS
ARG LDFLAGS
ARG MAKEFLAGS
ARG OUTPUT
WORKDIR /tmp/busybox
RUN curl -fsSL https://busybox.net/downloads/busybox-${BUSYBOX_VER}.tar.bz2 \
| tar xj --strip-components=1 \
&& make defconfig \
&& make \
&& install -Dm755 busybox "$OUTPUT/usr/bin/busybox" \
# "Install" busybox, creating symlinks to all binaries it provides
&& mkdir -p "$OUTPUT/usr/bin" "$OUTPUT/usr/sbin" \
&& ./busybox --list-full | sed -E 's@^(s?bin)@usr/\1@' | xargs -i ln -Tsv /usr/bin/busybox "$OUTPUT/{}"
WORKDIR /tmp/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 "$OUTPUT/usr/sbin/su-exec"
WORKDIR /tmp/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 "$OUTPUT/usr/sbin/tini"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM builder AS zlib
ARG ZLIB_VER
ARG CFLAGS
ARG LDFLAGS
ARG MAKEFLAGS
ARG OUTPUT
ARG DESTDIR
# 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
&& make DESTDIR="$DESTDIR" install \
&& mkdir -p "$OUTPUT/usr/lib" \
&& cp -a "$DESTDIR"/usr/lib/*.so* "$OUTPUT/usr/lib"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM builder AS xml
ARG LIBXML2_VER
ARG LIBXSLT_VER
ARG XMLSTAR_VER
ARG CFLAGS
ARG LDFLAGS
ARG MAKEFLAGS
ARG OUTPUT
ARG DESTDIR
COPY --from=zlib "$DESTDIR" "$DESTDIR"
# 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 \
--with-zlib="$DESTDIR/usr" \
--without-catalog \
--without-docbook \
--without-ftp \
@ -69,20 +166,22 @@ RUN git clone https://gitlab.gnome.org/GNOME/libxml2.git --branch $LIBXML2_VER -
--without-legacy \
--without-modules \
--without-python \
&& make DESTDIR=$PREFIX install
&& make DESTDIR="$DESTDIR" install \
&& mkdir -p "$OUTPUT/usr/lib" \
&& cp -a "$DESTDIR"/usr/lib/*.so* "$OUTPUT/usr/lib"
# Download and build libxslt
WORKDIR /tmp/libxslt
RUN git clone https://gitlab.gnome.org/GNOME/libxslt.git --branch $LIBXSLT_VER --depth 1 . \
&& ./autogen.sh \
--prefix=/usr \
--with-libxml-src="../libxml2" \
--with-libxml-src=../libxml2 \
--without-crypto \
--without-plugins \
--without-python \
&& make DESTDIR=$PREFIX install
&& make DESTDIR="$DESTDIR" install \
&& mkdir -p "$OUTPUT/usr/lib" \
&& cp -a "$DESTDIR"/usr/lib/*.so* "$OUTPUT/usr/lib"
# Download and build xmlstarlet
ADD xmlstarlet-*.patch /tmp
WORKDIR /tmp/xmlstarlet
RUN git clone git://git.code.sf.net/p/xmlstar/code --branch $XMLSTAR_VER --depth 1 . \
@ -91,54 +190,82 @@ RUN git clone git://git.code.sf.net/p/xmlstar/code --branch $XMLSTAR_VER --depth
&& ./configure \
--prefix=/usr \
--disable-build-docs \
--with-libxml-prefix=$PREFIX/usr \
--with-libxslt-prefix=$PREFIX/usr \
&& make DESTDIR=$PREFIX install
--with-libxml-prefix="$DESTDIR/usr" \
--with-libxslt-prefix="$DESTDIR/usr" \
&& make DESTDIR="$DESTDIR" install \
&& install -Dm755 "$DESTDIR/usr/bin/xml" "$OUTPUT/usr/bin/xmlstarlet"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FROM builder AS curl
ARG OPENSSL_VER
ARG CURL_VER
ARG CFLAGS
ARG LDFLAGS
ARG MAKEFLAGS
ARG OUTPUT
ARG DESTDIR
COPY --from=zlib "$DESTDIR" "$DESTDIR"
# 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 \
--with-zlib-lib="$DESTDIR/usr/lib" \
--with-zlib-include="$DESTDIR/usr/include" \
shared \
zlib-dynamic \
no-engine \
no-rc5 \
no-ssl3-method \
&& make build_libs \
&& make build_programs \
&& make \
&& make DESTDIR="$DESTDIR" \
install_sw \
install_ssldirs \
&& cp libssl*.so* libcrypto*.so* $PREFIX/usr/lib
&& mkdir -p "$OUTPUT/usr/lib" \
&& cp -a "$DESTDIR"/usr/lib/*.so* "$OUTPUT/usr/lib" \
&& sed -i "s@prefix=/usr@prefix=$DESTDIR/usr@g" "$DESTDIR"/usr/lib/pkgconfig/*.pc
# /usr/lib # curl --version
# curl 7.74.0-DEV (x86_64-pc-linux-musl) libcurl/7.73.0-DEV OpenSSL/1.1.1i zlib/1.2.11 nghttp2/1.41.0
# Protocols: http https
# Features: AsynchDNS HTTP2 HTTPS-proxy IPv6 Largefile libz SSL UnixSockets
# 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-http \
--enable-ipv6 \
--enable-largefile \
--enable-proxy \
--enable-unix-sockets \
--with-ssl="$DESTDIR/usr" \
--with-zlib="$DESTDIR/usr" \
--enable-optimize \
--enable-symbol-hiding \
--enable-versioned-symbols \
--enable-threaded-resolver \
--with-ssl \
--with-zlib=$PREFIX/usr \
--disable-cookies \
--disable-crypto-auth \
--disable-curldebug \
--disable-dependency-tracking \
--disable-dict \
--disable-file \
--disable-ftp \
--disable-gopher \
--disable-imap \
--disable-libcurl-option \
--disable-ldap \
--disable-ldaps \
--disable-libcurl-option \
--disable-manual \
--disable-mqtt \
--disable-ntlm-wb \
--disable-pop3 \
--disable-rtsp \
@ -154,40 +281,52 @@ RUN git clone https://github.com/curl/curl.git --branch $CURL_VER --depth 1 . \
--without-libpsl \
--without-librtmp \
--without-winidn \
&& make DESTDIR=$PREFIX install
&& make DESTDIR="$DESTDIR" install \
&& install -Dm755 "$DESTDIR/usr/bin/curl" "$OUTPUT/usr/bin/curl" \
# Cheat and "borrow" libnghttp2 from Alpine
&& mkdir -p "$OUTPUT/usr/lib" \
&& cp -a "$DESTDIR"/usr/lib/*.so* /usr/lib/libnghttp2.so* "$OUTPUT/usr/lib"
WORKDIR $PREFIX
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RUN mkdir -p /output/usr/lib /output/usr/bin /output/etc/ssl/certs \
FROM builder AS combine
ARG OUTPUT
WORKDIR $OUTPUT
COPY --from=plex "$OUTPUT" .
COPY --from=busybox "$OUTPUT" .
COPY --from=zlib "$OUTPUT" .
COPY --from=xml "$OUTPUT" .
COPY --from=curl "$OUTPUT" .
RUN install -m 1777 -o root -g root -d tmp \
&& ln -sv /usr/lib /usr/bin /usr/sbin . \
# 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 \
&& 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
&& mkdir -p etc/ssl/certs \
&& ln -sv /usr/lib/plexmediaserver/Resources/cacert.pem etc/ssl/certs/ca-certificates.crt \
# 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 . -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 \
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
ARG OPENSSL_VER
ARG CURL_VER
ARG OUTPUT
LABEL maintainer="Spritsail <plex@spritsail.io>" \
org.label-schema.vendor="Spritsail" \
@ -196,13 +335,16 @@ LABEL maintainer="Spritsail <plex@spritsail.io>" \
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.xmlstarlet=${XMLSTAR_VER} \
io.spritsail.version.busybox=${BUSYBOX_VER} \
io.spritsail.version.su-exec=${SU_EXEC_VER} \
io.spritsail.version.tini=${TINI_VER} \
io.spritsail.version.openssl=${OPENSSL_VER} \
io.spritsail.version.xmlstarlet=${XMLSTAR_VER}
io.spritsail.version.curl=${CURL_VER}
WORKDIR /usr/lib/plexmediaserver
COPY --from=builder /output/ /
COPY --from=combine "$OUTPUT" /
ENV SUID=900 SGID=900 \
PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6" \
@ -222,4 +364,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"]

View File

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

View File

@ -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
@ -34,15 +34,16 @@ 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}")"
-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}"
)"
authtoken="$(echo "$loginInfo" | sed -n 's/.*<authentication-token>\(.*\)<\/authentication-token>.*/\1/p')"