diff --git a/.drone.yml b/.drone.yml index c40a29a..74b7188 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,6 +4,13 @@ pipeline: volumes: [ '/var/run/docker.sock:/var/run/docker.sock' ] repo: plex-dev + test-bin: + image: docker + volumes: [ '/var/run/docker.sock:/var/run/docker.sock' ] + commands: + - docker run --rm plex-dev curl --version + - docker run --rm plex-dev xmlstarlet --version + test: image: spritsail/docker-test volumes: [ '/var/run/docker.sock:/var/run/docker.sock' ] @@ -19,7 +26,7 @@ pipeline: image: spritsail/docker-publish volumes: [ '/var/run/docker.sock:/var/run/docker.sock' ] secrets: [ docker_username, docker_password, microbadger_token ] - when: { branch: [ master, pass ], event: [ push, tag, deployment ] } + when: { branch: [ master ], event: [ push, tag, deployment ] } from: plex-dev repo: spritsail/plex-media-server tags: diff --git a/Dockerfile b/Dockerfile index cf7d9a9..7145dd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,8 @@ ARG PLEX_VER=1.13.5.5291-6fa5e50a8 ARG PLEX_SHA=0cb4cb011583edb0b26898d2e62253c55fc88bc7 ARG LIBSTDCPP_VER=6.3.0-18+deb9u1 ARG LIBGCC1_VER=6.3.0-18+deb9u1 +ARG XMLSTAR_VER=1.6.1 +ARG CURL_VER=curl-7_61_0 FROM spritsail/debian-builder:stretch-slim as builder @@ -9,28 +11,145 @@ ARG PLEX_VER ARG PLEX_SHA ARG LIBSTDCPP_VER ARG LIBGCC1_VER +ARG LIBXML2_VER=v2.9.8 +ARG LIBXSLT_VER=v1.1.32 +ARG XMLSTAR_VER +ARG LIBRE_VER=2.7.4 +ARG CURL_VER -WORKDIR /tmp +ARG MAKEFLAGS=-j4 -RUN mkdir -p /output/usr/lib +RUN apt-get -y update \ + && apt-get -y install zlib1g-dev -RUN curl -fsSL -o libstdcpp.deb http://ftp.de.debian.org/debian/pool/main/g/gcc-${LIBSTDCPP_VER:0:1}/libstdc++6_${LIBSTDCPP_VER}_amd64.deb \ - && curl -fsSL -o libgcc1.deb http://ftp.de.debian.org/debian/pool/main/g/gcc-${LIBGCC1_VER:0:1}/libgcc1_${LIBGCC1_VER}_amd64.deb \ - && dpkg-deb -x libstdcpp.deb . \ - && dpkg-deb -x libgcc1.deb . \ - # We only need the lib files, everything else is debian junk. - && mv $PWD/usr/lib/x86_64-linux-gnu/* /output/usr/lib \ - # Maybe /lib - && mv $PWD/lib/x86_64-linux-gnu/* /output/usr/lib +# 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 \ + --without-catalog \ + --without-docbook \ + --without-ftp \ + --without-http \ + --without-iconv \ + --without-iso8859x \ + --without-legacy \ + --without-modules \ + --without-python \ + && make \ + && make DESTDIR=/prefix install -RUN curl -fsSL -o plexmediaserver.deb https://downloads.plex.tv/plex-media-server/${PLEX_VER}/plexmediaserver_${PLEX_VER}_amd64.deb \ +# 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" \ + --without-crypto \ + --without-plugins \ + --without-python \ + && make \ + && make DESTDIR=/prefix install + +# 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 . \ + && git apply /tmp/xmlstarlet*.patch \ + && autoreconf -sif \ + && ./configure \ + --prefix=/usr \ + --disable-build-docs \ + --with-libxml-prefix=/prefix/usr \ + --with-libxslt-prefix=/prefix/usr \ + && make \ + && make DESTDIR=/prefix install + +# Download and build LibreSSL as a cURL dependency +WORKDIR /tmp/libressl +RUN curl -sSL https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRE_VER}.tar.gz \ + | tar xz --strip-components=1 \ + # Install to the default system directories so cURL can find it + && ./configure --prefix=/usr \ + && make install + +# 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 \ + --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 \ + && make DESTDIR=/prefix install + +WORKDIR /prefix + +# Fetch Plex and required libraries +RUN curl -fsSL http://ftp.de.debian.org/debian/pool/main/g/gcc-${LIBSTDCPP_VER:0:1}/libstdc++6_${LIBSTDCPP_VER}_amd64.deb | dpkg-deb -x - . \ + && curl -fsSL http://ftp.de.debian.org/debian/pool/main/g/gcc-${LIBGCC1_VER:0:1}/libgcc1_${LIBGCC1_VER}_amd64.deb | dpkg-deb -x - . \ + && curl -fsSL -o plexmediaserver.deb https://downloads.plex.tv/plex-media-server/${PLEX_VER}/plexmediaserver_${PLEX_VER}_amd64.deb \ + \ && echo "$PLEX_SHA plexmediaserver.deb" | sha1sum -c - \ && dpkg-deb -x plexmediaserver.deb . \ - && mv usr/lib/plexmediaserver /output/usr/lib + \ + && cd usr/lib/plexmediaserver \ + && rm -f \ + "Plex Media Server Tests" \ + libcrypto.so.1.0.0 \ + libcurl.so.4 \ + libssl.so.1.0.0 \ + libxml2.so.2 \ + libxslt.so.1 \ + # Place shared libraries in usr/lib so they can be actually shared + && mv *.so* ../ + # Strip all unneeded symbols for optimum size +RUN find -exec sh -c 'file "{}" | grep -q ELF && strip --strip-debug "{}"' \; \ + \ + && mkdir -p /output/usr/lib /output/usr/bin \ + && mv usr/lib/x86_64-linux-gnu/*.so* \ + lib/x86_64-linux-gnu/*.so* \ + usr/lib/plexmediaserver \ + usr/lib/*.so* \ + /output/usr/lib \ + && mv usr/bin/curl /output/usr/bin \ + && mv usr/bin/xml /output/usr/bin/xmlstarlet -ADD start_pms /output/usr/sbin/start_pms -RUN chmod +x /output/usr/sbin/start_pms +ADD entrypoint /output/usr/local/bin/ +ADD *.sh /output/usr/local/bin/ +RUN chmod +x /output/usr/local/bin/* #========================= @@ -39,6 +158,8 @@ FROM spritsail/libressl ARG PLEX_VER ARG LIBSTDCPP_VER ARG LIBGCC1_VER +ARG CURL_VER +ARG XMLSTAR_VER LABEL maintainer="Spritsail " \ org.label-schema.vendor="Spritsail" \ @@ -47,19 +168,31 @@ 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.libgcc1=${LIBGCC1_VER} \ io.spritsail.version.libstdcpp=${LIBSTDCPP_VER} \ - io.spritsail.version.libgcc1=${LIBGCC1_VER} + io.spritsail.version.xmlstarlet=${XMLSTAR_VER} -ENV SUID=900 SGID=900 +WORKDIR /usr/lib/plexmediaserver COPY --from=builder /output/ / +ENV SUID=900 SGID=900 \ + PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6" \ + PLEX_MEDIA_SERVER_MAX_STACK_SIZE="3000" \ + PLEX_MEDIA_SERVER_TMPDIR="/tmp" \ + PLEX_MEDIA_SERVER_HOME="/usr/lib/plexmediaserver" \ + PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR="/var/lib/plexmediaserver" + HEALTHCHECK --interval=10s --timeout=5s \ CMD [ "wget", "-O", "/dev/null", "-T", "10", "-q", "localhost:32400/identity" ] -WORKDIR /usr/lib/plexmediaserver - EXPOSE 32400 +VOLUME ["/config", "/transcode"] + +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/sbin/start_pms"] +CMD ["/usr/local/bin/entrypoint"] diff --git a/README.md b/README.md index b029e81..d8c6d95 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,70 @@ [![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/spritsail/busybox/) with glibc and libraries from source. The container contains a bare minimum glibc dependency tree, with most of the useless crap removed, resulting in the smallest container possible whilst maintaining full functionality. +The _smallest*_ Plex Media Server docker image, built on barebones [spritsail/busybox](https://hub.docker.com/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) _*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 -_Coming soon.. hopefully_ +Navigate to [plex.tv/claim](https://www.plex.tv/claim) and obtain a token in the form `claim-xxxx...` + +Start the container, as demonstrated below, passing the claim token via the `PLEX_CLAIM` environment variable. This only has to be present on the first run (when the configuration is generated) and can be removed for subsequent runs. _The Plex claim token is optional however it will make the server available to your account immediately._ + +Setting the container hostname on first boot will set the Plex server name. + +```shell +docker run -dt \ + --name=plex \ + --restart=unless-stopped \ + --hostname=my-plex-server \ + -p 32400:32400 \ + -e PLEX_CLAIM=claim-xxxx... \ + -v /config/plex:/config \ + -v /transcode:/transcode \ + -v /media:/media \ + spritsail/plex-media-server +``` + +Finally, navigate to [app.plex.tv/desktop](https://app.plex.tv/desktop) or [your-ip:32400/web](http://localhost:32400/web) and you're done! + + +### Volumes + +- `/config` - Configuration, logs, caches and other Plex crap. You should keep this +- `/transcode` - Transcoder temporary directory. This should be backed by fast storage, ideally tmpfs/RAM. +- Don't forget to mount your media (tv-shows/movies) inside the container too! + +### Environment + +- `$SUID` - User ID to run as _default: 900_ +- `$SGID` - Group ID to run as _default: 900_ +- `$ALLOWED_NETWORKS` - IP/netmask entries which allow access to the server without requiring authorization. We recommend you set this only if you do not sign in your server. For example `192.168.1.0/24,172.16.0.0/16` will allow access to the entire `192.168.1.x` range and the `172.16.x.x` range. +- `$ADVERTISE_IP` - This variable defines the additional IPs on which the server may be be found. For example: `http://10.1.1.23:32400`. This adds to the list where the server advertises that it can be found. +- `$DISABLE_REMOTE_SEC` - +- `$PLEX_CLAIM` - The claim token for the server to obtain a real server token. If not provided, server will not be automatically logged in. If server is already logged in, this parameter is ignored. + +### Network + +The following ports are all used by Plex for various applications + +- `32400/tcp` Plex Web/Client Access +- `5353/udp` Bonjour/Avahi +- `3005/tcp` Plex Home Theatre via Plex Companion +- `8324/tcp` Plex for Roku via Plex Companion +- `1900/udp` Plex DLNA Server +- `32469/udp` Plex DLNA Server +- `32410/udp` GDM network discovery +- `32412/udp` GDM network discovery +- `32413/udp` GDM network discovery +- `32414/udp` GDM network discovery + +See also: https://support.plex.tv/articles/201543147-what-network-ports-do-i-need-to-allow-through-my-firewall/ + +At the very least, you should expose `32400/tcp` to your network, and _port forward_ it through your router if you would like Plex access outside your home network. + +If you wish, you can map the Plex port to any other port outside your network, just be sure to update the port in _Settings > Server > Remote Access_ (Show Advanced) under _Manually specify public port_. diff --git a/entrypoint b/entrypoint new file mode 100755 index 0000000..781810d --- /dev/null +++ b/entrypoint @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +# ANSI colour escape sequences +RED='\033[0;31m' +RESET='\033[0m' + +if su-exec -e [ ! -w "/config" ]; then + 2>&1 echo -e "${RED}####################### WARNING #######################${RESET}" + 2>&1 echo + 2>&1 echo -e "${RED} No permission to write in '/config' directory.${RESET}" + 2>&1 echo -e "${RED} Correcting permissions to prevent a crash.${RESET}" + 2>&1 echo + 2>&1 echo -e "${RED}#######################################################${RESET}" + + chown $SUID:$SGID /config + chmod o+rw /config +fi + +exec su-exec -e sh </dev/null & + +# Use a random pidfile +export PLEX_MEDIA_SERVER_PIDFILE="\$(mktemp -ut pms-pid.XXXXXX)" + +exec "\$PLEX_MEDIA_SERVER_HOME/Plex Media Server" + +EOF diff --git a/gen-config.sh b/gen-config.sh new file mode 100755 index 0000000..48046ae --- /dev/null +++ b/gen-config.sh @@ -0,0 +1,64 @@ +#!/bin/sh +set -e + +PREF_FILE="${1:-/config/Preferences.xml}" + +getPref() { + xmlstarlet sel -T -t -m "/Preferences" -v "@$1" -n "${PREF_FILE}" +} +setPref() { + count="$(xmlstarlet sel -t -v "count(/Preferences/@$1)" "${PREF_FILE}")" + if [ $(($count + 0)) -gt 0 ]; then + xmlstarlet ed --inplace --update "/Preferences/@$1" -v "$2" "${PREF_FILE}" 2>/dev/null + else + xmlstarlet ed --inplace --insert "/Preferences" --type attr -n "$1" -v "$2" "${PREF_FILE}" 2>/dev/null + fi +} + +# Create a default config file allowing external access +echo -e $'\n' > "/config/Preferences.xml" + +# Enforced defaults. These can be changed manually afterwards. +setPref "AcceptedEULA" "1" +setPref "TranscoderTempDirectory" "/transcode" + +# The following below is ripped directly from the official (inferior) Plex container: +# https://github.com/plexinc/pms-docker/blob/155f00c71b50f94c73ffea0aae16cc61ef957df7/root/etc/cont-init.d/40-plex-first-run + +# Setup Server's client identifier +serial="$(getPref "MachineIdentifier")" +if [ -z "${serial}" ]; then + serial="$(cat /proc/sys/kernel/random/uuid)" + setPref "MachineIdentifier" "${serial}" +fi +clientId="$(getPref "ProcessedMachineIdentifier")" +if [ -z "${clientId}" ]; then + clientId="$(echo -n "${serial}- Plex Media Server" | sha1sum | cut -b 1-40)" + setPref "ProcessedMachineIdentifier" "${clientId}" +fi + +# Get server token and only turn claim token into server token if we have former but not latter. +token="$(getPref "PlexOnlineToken")" +if [ ! -z "${PLEX_CLAIM}" ] && [ -z "${token}" ]; then + 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=${PLEX_CLAIM}")" + token="$(echo "$loginInfo" | sed -n 's/.*\(.*\)<\/authentication-token>.*/\1/p')" + + if [ "$token" ]; then + echo "Token obtained successfully" + setPref "PlexOnlineToken" "${token}" + fi +fi + +test -n "${ADVERTISE_IP}" && setPref "customConnections" "${ADVERTISE_IP}" +test -n "${ALLOWED_NETWORKS}" && setPref "allowedNetworks" "${ALLOWED_NETWORKS}" +test -n "${DISABLE_REMOTE_SEC}" && setPref "disableRemoteSecurity" "1" diff --git a/start_pms b/start_pms deleted file mode 100644 index a91a5ff..0000000 --- a/start_pms +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh - -# Default values -export PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="${PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS:-6}" -export PLEX_MEDIA_SERVER_HOME="${PLEX_MEDIA_SERVER_HOME:-/usr/lib/plexmediaserver}" -export PLEX_MEDIA_SERVER_MAX_STACK_SIZE="${PLEX_MEDIA_SERVER_MAX_STACK_SIZE:-3000}" -export PLEX_MEDIA_SERVER_TMPDIR="${PLEX_MEDIA_SERVER_TMPDIR:-/tmp}" -export PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR="${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR:-/config}" -export PLEX_MEDIA_SERVER_CONFIG_DIR="${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR}/Plex Media Server" - -if [ ! -d "$PLEX_MEDIA_SERVER_CONFIG_DIR" ] -then - mkdir -p "$PLEX_MEDIA_SERVER_CONFIG_DIR" && \ - chown $SUID:$SGID "$PLEX_MEDIA_SERVER_CONFIG_DIR" - if [ ! $? -eq 0 ] - then - echo "WARNING COULDN'T CREATE $PLEX_MEDIA_SERVER_CONFIG_DIR, MAKE SURE I HAVE PERMISSON TO DO THAT!" - exit 1 - fi -fi -if su-exec -e [ ! -w "$PLEX_MEDIA_SERVER_CONFIG_DIR" ] -then - echo "ERROR: CANNOT WRITE IN $PLEX_MEDIA_SERVER_CONFIG_DIR, MAKE SURE I HAVE PERMISSION TO DO THAT!" - exit 2 -fi -if [ ! -e "$PLEX_MEDIA_SERVER_CONFIG_DIR/Preferences.xml" ]; -then - # Create a default config file allowing external access - echo -e '\n' \ - >> "$PLEX_MEDIA_SERVER_CONFIG_DIR/Preferences.xml" -fi - -export LD_LIBRARY_PATH="${PLEX_MEDIA_SERVER_HOME}:$LD_LIBRARY_PATH" -export TMPDIR="${PLEX_MEDIA_SERVER_TMPDIR}" - -# Allow Plex group to write to tmpdir -chgrp $SGID "$TMPDIR" -chmod g+rwx "$TMPDIR" - -PIDFILE="${PLEX_MEDIA_SERVER_CONFIG_DIR}/plexmediaserver.pid" -if [ -f "$PIDFILE" ]; then - rm -f "$PIDFILE" -fi - -tail -F "$PLEX_MEDIA_SERVER_CONFIG_DIR/Logs/Plex Media Server.log" >/proc/1/fd/1 2>/dev/null & - -exec su-exec -e "$PLEX_MEDIA_SERVER_HOME/Plex Media Server" diff --git a/xmlstarlet-0001-Fix-disable-build-docs.patch b/xmlstarlet-0001-Fix-disable-build-docs.patch new file mode 100644 index 0000000..ebe36ac --- /dev/null +++ b/xmlstarlet-0001-Fix-disable-build-docs.patch @@ -0,0 +1,35 @@ +diff --git a/Makefile.am b/Makefile.am +index 1f3ba33..56a36b4 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -75,11 +75,6 @@ $(generated_usage_sources) : usage2c.awk + # doc + include doc/doc.mk + +-man_MANS = $(manpage) +-dist_doc_DATA = $(userguide_gen) doc/html.css $(txtguide) +-EXTRA_DIST += $(manpage_src) $(userguide_src) $(txtguide_src) +-EXTRA_DIST += $(buildfiles_docs) $(manpage) +- + + CLEANFILES = version.h $(generated_usage_sources) + MAINTAINERCLEANFILES = $(manpage) $(userguide_gen) $(txtguide) +diff --git a/doc/doc.mk b/doc/doc.mk +index bde1e0e..d4633eb 100644 +--- a/doc/doc.mk ++++ b/doc/doc.mk +@@ -28,6 +28,11 @@ EDIT_XML = $(XSLTPROC) \ + + if BUILD_DOCS + ++man_MANS = $(manpage) ++dist_doc_DATA = $(userguide_gen) doc/html.css $(txtguide) ++EXTRA_DIST += $(manpage_src) $(userguide_src) $(txtguide_src) ++EXTRA_DIST += $(buildfiles_docs) $(manpage) ++ + .xml.html: + $(V_DOCBOOK)$(EDIT_XML) $< | $(XSLTPROC) $(DOCBOOK_PARAMS) \ + --stringparam html.stylesheet html.css \ +-- +2.18.0 +