diff --git a/.drone.yml b/.drone.yml index ad4d60f..be2d5e7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,7 +18,8 @@ steps: image: spritsail/docker-test settings: run: | - curl --version && + busybox && \ + curl --version && \ xmlstarlet --version - name: test @@ -40,10 +41,7 @@ steps: tags: - plexpass - "%label io.spritsail.version.plex | %prefix plexpass | %remsuf [0-9a-f]+$ | %auto 2" - username: - from_secret: docker_username - password: - from_secret: docker_password + login: {from_secret: docker_login} when: branch: - pass @@ -51,6 +49,6 @@ steps: - push --- kind: signature -hmac: d7ad7e2102295415a2f975e1cb216c712d3b7fd78b11bd48020eb8f464a13b88 +hmac: a6e529b62ed2ecf24b2c3125f7267c321d56ce16d1cd8178f96e0399dd3096bd ... diff --git a/Dockerfile b/Dockerfile index c589c86..63a0124 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,52 @@ -ARG PLEX_VER=1.23.0.4438-5eef0fd80 -ARG PLEX_SHA=3fa5b5410fba1720d55f1a3e20920c799cdf559e -ARG XMLSTAR_VER=1.6.1 -ARG CURL_VER=curl-7_74_0 +ARG PLEX_VER=1.23.0.4482-62106842a +ARG PLEX_SHA=022f2ac2a18ec602f0402baf94d69f405b9207a4 +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 " \ org.label-schema.vendor="Spritsail" \ @@ -196,13 +335,16 @@ 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.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"] diff --git a/README.md b/README.md index 1772aa4..503a4dd 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?branch=pass)][drone] [![Last Build](https://api.spritsail.io/badges/lastbuild/spritsail/plex-media-server:plexpass.svg)][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..4b9d97d 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 @@ -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>.*/\1/p')"