diff --git a/root/docker-mods b/root/docker-mods new file mode 100755 index 0000000000000000000000000000000000000000..c90e5a47b9f6431b3583ed406c0983ae2fe7bd85 --- /dev/null +++ b/root/docker-mods @@ -0,0 +1,80 @@ +#!/usr/bin/with-contenv bash + +# Exit if mods is not set +if [ -z ${DOCKER_MODS+x} ]; then + exit 0 +fi + +# Check for curl +if [ ! -f /usr/bin/curl ]; then + echo "[mod-init] Curl was not found on this system for Docker mods installing" + if [ -f /usr/bin/apt ]; then + ## Ubuntu + apt-get update + apt-get install --no-install-recommends -y \ + curl + elif [ -f /sbin/apk ]; then + # Alpine + apk add --no-cache \ + curl + fi +fi + +# Main run logic +echo "[mod-init] Attempting to run Docker Modification Logic" +IFS='|' +DOCKER_MODS=(${DOCKER_MODS}) +for DOCKER_MOD in "${DOCKER_MODS[@]}"; do + FILENAME=$(echo ${DOCKER_MOD} | sed 's/[:\/]/./g') + ENDPOINT=$(echo ${DOCKER_MOD} | awk -F: '{print $1}') + USERNAME=$(echo ${ENDPOINT} | awk -F/ '{print $1}') + TAG=$(echo ${DOCKER_MOD} | awk -F: '{print $2}') + # Kill off modification logic if any of the usernames are banned + BLACKLIST=$(curl -s https://raw.githubusercontent.com/linuxserver/docker-mods/master/blacklist.txt) + IFS=$'\n' + BLACKLIST=(${BLACKLIST}) + for BANNED in "${BLACKLIST[@]}"; do + if [ "${BANNED}" == "${USERNAME,,}" ]; then + if [ -z ${RUN_BANNED_MODS+x} ]; then + echo "[mod-init] ${DOCKER_MOD} is banned from use due to reported abuse aborting mod logic" + exit 0 + else + echo "[mod-init] You have chosen to run banned mods ${DOCKER_MOD} will be applied" + fi + fi + done + echo "[mod-init] Applying ${DOCKER_MOD} files to container" + # Get Dockerhub token for api operations + TOKEN=\ +"$(curl \ + --silent \ + --header 'GET' \ + "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ENDPOINT}:pull" \ + | awk -F'"' '{print $4}' \ + )" + # Determine first and only layer of image + SHALAYER=\ +"$(curl \ + --silent \ + --location \ + --request GET \ + --header "Authorization: Bearer ${TOKEN}" \ + https://registry-1.docker.io/v2/${ENDPOINT}/manifests/${TAG} \ + |grep -m1 "blobSum" \ + | awk -F'"' '{print $4}' \ + )" + # Check if we have allready applied this layer + if [ -f "/${FILENAME}" ] && [ "${SHALAYER}" == "$(cat /${FILENAME})" ]; then + echo "[mod-init] ${DOCKER_MOD} at ${SHALAYER} has been previously applied skipping" + else + # Download and extract layer to / + curl \ + --silent \ + --location \ + --request GET \ + --header "Authorization: Bearer ${TOKEN}" \ + "https://registry-1.docker.io/v2/${ENDPOINT}/blobs/${SHALAYER}" \ + | tar xz -C / + echo ${SHALAYER} > "/${FILENAME}" + fi +done diff --git a/root/etc/cont-init.d/99-custom-scripts b/root/etc/cont-init.d/99-custom-scripts new file mode 100644 index 0000000000000000000000000000000000000000..7e4c16b66f9200cc7a65e8b756f689a1f1cf1255 --- /dev/null +++ b/root/etc/cont-init.d/99-custom-scripts @@ -0,0 +1,15 @@ +#!/usr/bin/with-contenv bash + +# Make sure custom script directory exists and has files in it +SCRIPTS_DIR="/config/custom-init-scripts" +if [ -e "${SCRIPTS_DIR}" ] && \ + [ -n "$(/bin/ls -A ${SCRIPTS_DIR} 2>/dev/null)" ]; then + echo "[custom-init] files found in ${SCRIPTS_DIR} executing" + for SCRIPT in ${SCRIPTS_DIR}/*; do + echo "[custom-init] ${SCRIPT}: executing..." + /bin/bash ${SCRIPT} + echo "[custom-init] ${SCRIPT}: exited $?" + done +else + echo "[custom-init] no custom scripts found exiting..." +fi diff --git a/root/etc/s6/init/init-stage2 b/root/etc/s6/init/init-stage2 new file mode 100755 index 0000000000000000000000000000000000000000..7efa57a428d72c3c01968086579556f45e22bc73 --- /dev/null +++ b/root/etc/s6/init/init-stage2 @@ -0,0 +1,226 @@ +#!/bin/execlineb -S0 + +# This file is executed (not as process 1!) as soon as s6-svscan +# starts, with the original stdin/out/err, but NOT the original +# environment. +# Purpose of this file: to perform all the one-time initialization tasks. + +# Merge environments from our custom stage into current context +s6-envdir -I /var/run/s6/env-stage2 + +# This env decides what to do if stage2 fails +backtick -D 0 -n S6_BEHAVIOUR_IF_STAGE2_FAILS { printcontenv S6_BEHAVIOUR_IF_STAGE2_FAILS } +importas -u S6_BEHAVIOUR_IF_STAGE2_FAILS S6_BEHAVIOUR_IF_STAGE2_FAILS + +# This env determines whether user provided files in /etc should be linked +# or copied into /var/run/s6 +backtick -D 0 -n S6_READ_ONLY_ROOT { printcontenv S6_READ_ONLY_ROOT } +importas -u S6_READ_ONLY_ROOT S6_READ_ONLY_ROOT + +# Docker Mods run logic +foreground +{ + /docker-mods +} + +foreground +{ + if + { + /etc/s6/init/init-stage2-redirfd + foreground + { + ## + ## copy user provided files to /var/run/s6/etc, depending on S6_RUNTIME_PROFILE env, + ## /etc (if not defined) or /etc/cont-profile.d/${S6_RUNTIME_PROFILE} will be used + ## as copying source. + ## + + if + { + if { s6-echo -n -- "[s6-init] making user provided files available at /var/run/s6/etc..." } + foreground + { + backtick -n S6_RUNTIME_PROFILE { printcontenv S6_RUNTIME_PROFILE } + importas -u S6_RUNTIME_PROFILE S6_RUNTIME_PROFILE + backtick -n S6_RUNTIME_PROFILE_SRC { + ifte { s6-echo "/etc/cont-profile.d/${S6_RUNTIME_PROFILE}" } { s6-echo "/etc" } + s6-test -n ${S6_RUNTIME_PROFILE} + } + importas -u S6_RUNTIME_PROFILE_SRC S6_RUNTIME_PROFILE_SRC + if { s6-rmrf /var/run/s6/etc } + if { s6-mkdir -pm 0755 /var/run/s6/etc } + forx i { "fix-attrs.d" "cont-init.d" "cont-finish.d" "services.d" } + importas -u i i + if { s6-test -d ${S6_RUNTIME_PROFILE_SRC}/${i} } + # although s6-hiercopy is prefered, and until it doesn't support 'follow symlinks' + # option, there is no clean way to allow symlinks between user provided runcoms. + ifelse { s6-test ${S6_READ_ONLY_ROOT} -eq 0 } { + s6-ln -s ${S6_RUNTIME_PROFILE_SRC}/${i} /var/run/s6/etc/${i} + } + if { s6-hiercopy ${S6_RUNTIME_PROFILE_SRC}/${i} /var/run/s6/etc/${i} } + } + importas -u ? ? + if { s6-echo -- "exited ${?}." } + ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -eq 0 } { exit 0 } + exit ${?} + } + + + ## + ## fix-attrs: ensure user-provided files have correct ownership & perms + ## + + if + { + if { s6-echo -n -- "[s6-init] ensuring user provided files have correct perms..." } + foreground { redirfd -r 0 /etc/s6/init/init-stage2-fixattrs.txt fix-attrs } + importas -u ? ? + if { s6-echo -- "exited ${?}." } + ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -eq 0 } { exit 0 } + exit ${?} + } + + + ## + ## fix-attrs.d: apply user-provided ownership & permission fixes + ## + + if + { + if -t { s6-test -d /var/run/s6/etc/fix-attrs.d } + if { s6-echo "[fix-attrs.d] applying ownership & permissions fixes..." } + if + { + pipeline { s6-ls -0 -- /var/run/s6/etc/fix-attrs.d } + pipeline { s6-sort -0 -- } + forstdin -0 -- i + importas -u i i + if { s6-echo -- "[fix-attrs.d] ${i}: applying... " } + foreground { redirfd -r 0 /var/run/s6/etc/fix-attrs.d/${i} fix-attrs } + importas -u ? ? + if { s6-echo -- "[fix-attrs.d] ${i}: exited ${?}." } + ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -eq 0 } { exit 0 } + exit ${?} + } + if { s6-echo -- "[fix-attrs.d] done." } + } + + ## + ## cont-init.d: one-time init scripts + ## + + if + { + if -t { s6-test -d /var/run/s6/etc/cont-init.d } + if { s6-echo "[cont-init.d] executing container initialization scripts..." } + if + { + pipeline { s6-ls -0 -- /var/run/s6/etc/cont-init.d } + pipeline { s6-sort -0 -- } + forstdin -o 0 -0 -- i + importas -u i i + if { s6-echo -- "[cont-init.d] ${i}: executing... " } + foreground { /var/run/s6/etc/cont-init.d/${i} } + importas -u ? ? + if { s6-echo -- "[cont-init.d] ${i}: exited ${?}." } + ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -eq 0 } { exit 0 } + exit ${?} + } + if { s6-echo -- "[cont-init.d] done." } + } + + ## + ## services.d: long-lived processes to be supervised + ## + + if + { + if -t { s6-test -d /var/run/s6/etc/services.d } + if { s6-echo "[services.d] starting services" } + if + { + pipeline { s6-ls -0 -- /var/run/s6/etc/services.d } + forstdin -0 -p -- i + importas -u i i + if { s6-test -d /var/run/s6/etc/services.d/${i} } + s6-hiercopy /var/run/s6/etc/services.d/${i} /var/run/s6/services/${i} + } + if { s6-svscanctl -a /var/run/s6/services } + if + { + # This envs decide if CMD should wait until services are up + backtick -D 0 -n S6_CMD_WAIT_FOR_SERVICES { printcontenv S6_CMD_WAIT_FOR_SERVICES } + importas -u S6_CMD_WAIT_FOR_SERVICES S6_CMD_WAIT_FOR_SERVICES + backtick -D 5000 -n S6_CMD_WAIT_FOR_SERVICES_MAXTIME { printcontenv S6_CMD_WAIT_FOR_SERVICES_MAXTIME } + importas -u S6_CMD_WAIT_FOR_SERVICES_MAXTIME S6_CMD_WAIT_FOR_SERVICES_MAXTIME + + if -t { if { s6-test ${S6_CMD_WAIT_FOR_SERVICES} -ne 0 } s6-test $# -ne 0 } + s6-maximumtime -t ${S6_CMD_WAIT_FOR_SERVICES_MAXTIME} + pipeline { s6-ls -0 -- /var/run/s6/etc/services.d } + forstdin -0 -o 0 -- i + importas -u i i + ifelse { s6-test -f /var/run/s6/services/${i}/down } { exit 0 } + ifelse { s6-test -f /var/run/s6/services/${i}/notification-fd } + { + s6-svwait -t ${S6_CMD_WAIT_FOR_SERVICES_MAXTIME} -U /var/run/s6/services/${i} + } + s6-svwait -t ${S6_CMD_WAIT_FOR_SERVICES_MAXTIME} -u /var/run/s6/services/${i} + } + if { s6-echo -- "[services.d] done." } + } + } + importas -u ? ? + ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -eq 0 } { exit 0 } + + # Make stage2 exit code available in stage3 + foreground { redirfd -w 1 /var/run/s6/env-stage3/S6_STAGE2_EXITED s6-echo -n -- "${?}" } + exit ${?} + } + + + ## + ## The init is complete, If the user has a given CMD, run it now, then + ## kill everything when it exits. + ## + + if -t { s6-test $# -ne 0 } + + foreground { + s6-setsid -gq -- with-contenv + backtick -D 0 -n S6_LOGGING { printcontenv S6_LOGGING } + importas S6_LOGGING S6_LOGGING + ifelse { s6-test ${S6_LOGGING} -eq 2 } + { + redirfd -w 1 /var/run/s6/uncaught-logs-fifo + fdmove -c 2 1 + $@ + } + $@ + } + + importas -u ? ? + + foreground { + /etc/s6/init/init-stage2-redirfd + s6-echo -- "[cmd] ${1} exited ${?}" + } + + # Make CMD exit code available in stage3 + foreground { redirfd -w 1 /var/run/s6/env-stage3/S6_STAGE2_EXITED s6-echo -n -- "${?}" } + + # Stop supervision tree + foreground { s6-svscanctl -t /var/run/s6/services } + + # Wait to be nuked + s6-pause -th + +} +importas -u ? ? +if { s6-test ${?} -ne 0 } +if { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -ne 0 } +ifelse { s6-test ${S6_BEHAVIOUR_IF_STAGE2_FAILS} -ne 1 } +{ + s6-svscanctl -t /var/run/s6/services +} +s6-echo -- "\n!!!!!\n init-stage2 failed.\n!!!!!"