diff --git a/3.4/Dockerfile b/3.4/Dockerfile index 86ad4f4441..d0b66e9221 100644 --- a/3.4/Dockerfile +++ b/3.4/Dockerfile @@ -3,12 +3,17 @@ FROM debian:jessie-slim # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mongodb && useradd -r -g mongodb mongodb -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ ca-certificates \ jq \ numactl \ - && rm -rf /var/lib/apt/lists/* + ; \ + if ! command -v ps > /dev/null; then \ + apt-get install -y --no-install-recommends procps; \ + fi; \ + rm -rf /var/lib/apt/lists/* # grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) ENV GOSU_VERSION 1.10 @@ -21,6 +26,9 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ wget \ ; \ + if ! command -v gpg > /dev/null; then \ + apt-get install -y --no-install-recommends gnupg dirmngr; \ + fi; \ rm -rf /var/lib/apt/lists/*; \ \ dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ @@ -41,12 +49,7 @@ RUN set -ex; \ RUN mkdir /docker-entrypoint-initdb.d -ENV GPG_KEYS \ -# pub 4096R/A15703C6 2016-01-11 [expires: 2018-01-10] -# Key fingerprint = 0C49 F373 0359 A145 1858 5931 BC71 1F9B A157 03C6 -# uid MongoDB 3.4 Release Signing Key - 0C49F3730359A14518585931BC711F9BA15703C6 -# https://docs.mongodb.com/manual/tutorial/verify-mongodb-packages/#download-then-import-the-key-file +ENV GPG_KEYS 0C49F3730359A14518585931BC711F9BA15703C6 RUN set -ex; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ @@ -87,7 +90,7 @@ RUN mkdir -p /data/db /data/configdb \ VOLUME /data/db /data/configdb COPY docker-entrypoint.sh /usr/local/bin/ -RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat +RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat (3.4) ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 27017 diff --git a/3.4/docker-entrypoint.sh b/3.4/docker-entrypoint.sh index 66a379c6f2..d340570997 100755 --- a/3.4/docker-entrypoint.sh +++ b/3.4/docker-entrypoint.sh @@ -232,6 +232,7 @@ if [ "$originalArgOne" = 'mongod' ]; then fi _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" + _mongod_hack_ensure_no_arg --bind_ip_all "${mongodHackedArgs[@]}" # remove "--auth" and "--replSet" for our initial startup (see https://docs.mongodb.com/manual/tutorial/enable-authentication/#start-mongodb-without-access-control) # https://github.com/docker-library/mongo/issues/211 @@ -319,6 +320,20 @@ if [ "$originalArgOne" = 'mongod' ]; then echo fi + # MongoDB 3.6+ defaults to localhost-only binding + if mongod --help 2>&1 | grep -q -- --bind_ip_all; then # TODO remove this conditional when 3.4 is no longer supported + haveBindIp= + if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then + haveBindIp=1 + elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then + haveBindIp=1 + fi + if [ -z "$haveBindIp" ]; then + # so if no "--bind_ip" is specified, let's add "--bind_ip_all" + set -- "$@" --bind_ip_all + fi + fi + unset "${!MONGO_INITDB_@}" fi diff --git a/3.6/Dockerfile b/3.6/Dockerfile index 4f7feab338..c7396d89c9 100644 --- a/3.6/Dockerfile +++ b/3.6/Dockerfile @@ -3,14 +3,17 @@ FROM debian:stretch-slim # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mongodb && useradd -r -g mongodb mongodb -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ ca-certificates \ - gnupg dirmngr \ jq \ numactl \ - procps \ - && rm -rf /var/lib/apt/lists/* + ; \ + if ! command -v ps > /dev/null; then \ + apt-get install -y --no-install-recommends procps; \ + fi; \ + rm -rf /var/lib/apt/lists/* # grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) ENV GOSU_VERSION 1.10 @@ -23,6 +26,9 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ wget \ ; \ + if ! command -v gpg > /dev/null; then \ + apt-get install -y --no-install-recommends gnupg dirmngr; \ + fi; \ rm -rf /var/lib/apt/lists/*; \ \ dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ @@ -31,7 +37,7 @@ RUN set -ex; \ export GNUPGHOME="$(mktemp -d)"; \ gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ - gpgconf --kill all; \ + command -v gpgconf && gpgconf --kill all || :; \ rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ chmod +x /usr/local/bin/gosu; \ gosu nobody true; \ @@ -43,19 +49,14 @@ RUN set -ex; \ RUN mkdir /docker-entrypoint-initdb.d -ENV GPG_KEYS \ -# pub 4096R/91FA4AD5 2016-12-14 [expires: 2018-12-14] -# Key fingerprint = 2930 ADAE 8CAF 5059 EE73 BB4B 5871 2A22 91FA 4AD5 -# uid MongoDB 3.6 Release Signing Key - 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5 -# https://docs.mongodb.com/manual/tutorial/verify-mongodb-packages/#download-then-import-the-key-file +ENV GPG_KEYS 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5 RUN set -ex; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ done; \ gpg --batch --export $GPG_KEYS > /etc/apt/trusted.gpg.d/mongodb.gpg; \ - gpgconf --kill all; \ + command -v gpgconf && gpgconf --kill all || :; \ rm -r "$GNUPGHOME"; \ apt-key list diff --git a/3.6/docker-entrypoint.sh b/3.6/docker-entrypoint.sh index 229d3271c8..d340570997 100755 --- a/3.6/docker-entrypoint.sh +++ b/3.6/docker-entrypoint.sh @@ -321,15 +321,17 @@ if [ "$originalArgOne" = 'mongod' ]; then fi # MongoDB 3.6+ defaults to localhost-only binding - haveBindIp= - if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then - haveBindIp=1 - elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then - haveBindIp=1 - fi - if [ -z "$haveBindIp" ]; then - # so if no "--bind_ip" is specified, let's add "--bind_ip_all" - set -- "$@" --bind_ip_all + if mongod --help 2>&1 | grep -q -- --bind_ip_all; then # TODO remove this conditional when 3.4 is no longer supported + haveBindIp= + if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then + haveBindIp=1 + elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then + haveBindIp=1 + fi + if [ -z "$haveBindIp" ]; then + # so if no "--bind_ip" is specified, let's add "--bind_ip_all" + set -- "$@" --bind_ip_all + fi fi unset "${!MONGO_INITDB_@}" diff --git a/4.0/Dockerfile b/4.0/Dockerfile index 2f3d2581ed..08831d8cf4 100644 --- a/4.0/Dockerfile +++ b/4.0/Dockerfile @@ -3,12 +3,17 @@ FROM ubuntu:xenial # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mongodb && useradd -r -g mongodb mongodb -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ ca-certificates \ jq \ numactl \ - && rm -rf /var/lib/apt/lists/* + ; \ + if ! command -v ps > /dev/null; then \ + apt-get install -y --no-install-recommends procps; \ + fi; \ + rm -rf /var/lib/apt/lists/* # grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) ENV GOSU_VERSION 1.10 @@ -21,6 +26,9 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ wget \ ; \ + if ! command -v gpg > /dev/null; then \ + apt-get install -y --no-install-recommends gnupg dirmngr; \ + fi; \ rm -rf /var/lib/apt/lists/*; \ \ dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ @@ -41,12 +49,7 @@ RUN set -ex; \ RUN mkdir /docker-entrypoint-initdb.d -ENV GPG_KEYS \ -# pub rsa4096 2018-04-18 [SC] [expires: 2023-04-17] -# 9DA3 1620 334B D75D 9DCB 49F3 6881 8C72 E525 29D4 -# uid [ unknown] MongoDB 4.0 Release Signing Key - 9DA31620334BD75D9DCB49F368818C72E52529D4 -# https://docs.mongodb.com/manual/tutorial/verify-mongodb-packages/#download-then-import-the-key-file +ENV GPG_KEYS 9DA31620334BD75D9DCB49F368818C72E52529D4 RUN set -ex; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ diff --git a/4.0/docker-entrypoint.sh b/4.0/docker-entrypoint.sh index 229d3271c8..d340570997 100755 --- a/4.0/docker-entrypoint.sh +++ b/4.0/docker-entrypoint.sh @@ -321,15 +321,17 @@ if [ "$originalArgOne" = 'mongod' ]; then fi # MongoDB 3.6+ defaults to localhost-only binding - haveBindIp= - if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then - haveBindIp=1 - elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then - haveBindIp=1 - fi - if [ -z "$haveBindIp" ]; then - # so if no "--bind_ip" is specified, let's add "--bind_ip_all" - set -- "$@" --bind_ip_all + if mongod --help 2>&1 | grep -q -- --bind_ip_all; then # TODO remove this conditional when 3.4 is no longer supported + haveBindIp= + if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then + haveBindIp=1 + elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then + haveBindIp=1 + fi + if [ -z "$haveBindIp" ]; then + # so if no "--bind_ip" is specified, let's add "--bind_ip_all" + set -- "$@" --bind_ip_all + fi fi unset "${!MONGO_INITDB_@}" diff --git a/4.1/Dockerfile b/4.1/Dockerfile index ddded7e422..9d5dcf67f9 100644 --- a/4.1/Dockerfile +++ b/4.1/Dockerfile @@ -3,12 +3,17 @@ FROM ubuntu:xenial # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mongodb && useradd -r -g mongodb mongodb -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ ca-certificates \ jq \ numactl \ - && rm -rf /var/lib/apt/lists/* + ; \ + if ! command -v ps > /dev/null; then \ + apt-get install -y --no-install-recommends procps; \ + fi; \ + rm -rf /var/lib/apt/lists/* # grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) ENV GOSU_VERSION 1.10 @@ -21,6 +26,9 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ wget \ ; \ + if ! command -v gpg > /dev/null; then \ + apt-get install -y --no-install-recommends gnupg dirmngr; \ + fi; \ rm -rf /var/lib/apt/lists/*; \ \ dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ @@ -41,12 +49,7 @@ RUN set -ex; \ RUN mkdir /docker-entrypoint-initdb.d -ENV GPG_KEYS \ -# pub rsa4096 2018-04-18 [SC] [expires: 2023-04-17] -# E162 F504 A20C DF15 827F 718D 4B7C 549A 058F 8B6B -# uid [ unknown] MongoDB 4.2 Release Signing Key - E162F504A20CDF15827F718D4B7C549A058F8B6B -# https://docs.mongodb.com/manual/tutorial/verify-mongodb-packages/#download-then-import-the-key-file +ENV GPG_KEYS E162F504A20CDF15827F718D4B7C549A058F8B6B RUN set -ex; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ diff --git a/4.1/docker-entrypoint.sh b/4.1/docker-entrypoint.sh index 229d3271c8..d340570997 100755 --- a/4.1/docker-entrypoint.sh +++ b/4.1/docker-entrypoint.sh @@ -321,15 +321,17 @@ if [ "$originalArgOne" = 'mongod' ]; then fi # MongoDB 3.6+ defaults to localhost-only binding - haveBindIp= - if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then - haveBindIp=1 - elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then - haveBindIp=1 - fi - if [ -z "$haveBindIp" ]; then - # so if no "--bind_ip" is specified, let's add "--bind_ip_all" - set -- "$@" --bind_ip_all + if mongod --help 2>&1 | grep -q -- --bind_ip_all; then # TODO remove this conditional when 3.4 is no longer supported + haveBindIp= + if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then + haveBindIp=1 + elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then + haveBindIp=1 + fi + if [ -z "$haveBindIp" ]; then + # so if no "--bind_ip" is specified, let's add "--bind_ip_all" + set -- "$@" --bind_ip_all + fi fi unset "${!MONGO_INITDB_@}" diff --git a/Dockerfile-linux.template b/Dockerfile-linux.template new file mode 100644 index 0000000000..c609b41d3c --- /dev/null +++ b/Dockerfile-linux.template @@ -0,0 +1,97 @@ +FROM placeholder + +# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added +RUN groupadd -r mongodb && useradd -r -g mongodb mongodb + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + jq \ + numactl \ + ; \ + if ! command -v ps > /dev/null; then \ + apt-get install -y --no-install-recommends procps; \ + fi; \ + rm -rf /var/lib/apt/lists/* + +# grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) +ENV GOSU_VERSION 1.10 +# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases) +ENV JSYAML_VERSION 3.10.0 + +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + wget \ + ; \ + if ! command -v gpg > /dev/null; then \ + apt-get install -y --no-install-recommends gnupg dirmngr; \ + fi; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ + \ + wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \ +# TODO some sort of download verification here + \ + apt-get purge -y --auto-remove wget + +RUN mkdir /docker-entrypoint-initdb.d + +ENV GPG_KEYS placeholder +RUN set -ex; \ + export GNUPGHOME="$(mktemp -d)"; \ + for key in $GPG_KEYS; do \ + gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ + done; \ + gpg --batch --export $GPG_KEYS > /etc/apt/trusted.gpg.d/mongodb.gpg; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -r "$GNUPGHOME"; \ + apt-key list + +# Allow build-time overrides (eg. to build image with MongoDB Enterprise version) +# Options for MONGO_PACKAGE: mongodb-org OR mongodb-enterprise +# Options for MONGO_REPO: repo.mongodb.org OR repo.mongodb.com +# Example: docker build --build-arg MONGO_PACKAGE=mongodb-enterprise --build-arg MONGO_REPO=repo.mongodb.com . +ARG MONGO_PACKAGE=placeholder +ARG MONGO_REPO=repo.mongodb.org +ENV MONGO_PACKAGE=${MONGO_PACKAGE} MONGO_REPO=${MONGO_REPO} + +ENV MONGO_MAJOR placeholder +ENV MONGO_VERSION placeholder + +RUN echo "deb http://$MONGO_REPO/apt/%%DISTRO%% %%SUITE%%/${MONGO_PACKAGE%-unstable}/$MONGO_MAJOR %%COMPONENT%%" | tee "/etc/apt/sources.list.d/${MONGO_PACKAGE%-unstable}.list" + +RUN set -x \ + && apt-get update \ + && apt-get install -y \ + ${MONGO_PACKAGE}=$MONGO_VERSION \ + ${MONGO_PACKAGE}-server=$MONGO_VERSION \ + ${MONGO_PACKAGE}-shell=$MONGO_VERSION \ + ${MONGO_PACKAGE}-mongos=$MONGO_VERSION \ + ${MONGO_PACKAGE}-tools=$MONGO_VERSION \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /var/lib/mongodb \ + && mv /etc/mongod.conf /etc/mongod.conf.orig + +RUN mkdir -p /data/db /data/configdb \ + && chown -R mongodb:mongodb /data/db /data/configdb +VOLUME /data/db /data/configdb + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat (3.4) +ENTRYPOINT ["docker-entrypoint.sh"] + +EXPOSE 27017 +CMD ["mongod"] diff --git a/Dockerfile-windows.template b/Dockerfile-windows.template new file mode 100644 index 0000000000..22d1d3653e --- /dev/null +++ b/Dockerfile-windows.template @@ -0,0 +1,49 @@ +FROM microsoft/windowsservercore:placeholder + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"] + +ENV MONGO_VERSION placeholder +ENV MONGO_DOWNLOAD_URL placeholder +ENV MONGO_DOWNLOAD_SHA256 placeholder + +RUN Write-Host ('Downloading {0} ...' -f $env:MONGO_DOWNLOAD_URL); \ + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \ + (New-Object System.Net.WebClient).DownloadFile($env:MONGO_DOWNLOAD_URL, 'mongo.msi'); \ + \ + Write-Host ('Verifying sha256 ({0}) ...' -f $env:MONGO_DOWNLOAD_SHA256); \ + if ((Get-FileHash mongo.msi -Algorithm sha256).Hash -ne $env:MONGO_DOWNLOAD_SHA256) { \ + Write-Host 'FAILED!'; \ + exit 1; \ + }; \ + \ + Write-Host 'Installing ...'; \ +# https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/#install-mongodb-community-edition + Start-Process msiexec -Wait \ + -ArgumentList @( \ + '/i', \ + 'mongo.msi', \ + '/quiet', \ + '/qn', \ + 'INSTALLLOCATION=C:\mongodb', \ + 'ADDLOCAL=all' \ + ); \ + $env:PATH = 'C:\mongodb\bin;' + $env:PATH; \ + [Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine); \ + \ + Write-Host 'Verifying install ...'; \ + Write-Host ' mongo --version'; mongo --version; \ + Write-Host ' mongod --version'; mongod --version; \ + \ + Write-Host 'Removing ...'; \ + Remove-Item C:\mongodb\bin\*.pdb -Force; \ + Remove-Item C:\windows\installer\*.msi -Force; \ + Remove-Item mongo.msi -Force; \ + \ + Write-Host 'Complete.'; + +VOLUME C:\\data\\db C:\\data\\configdb + +# TODO docker-entrypoint.ps1 ? (for "docker run --flag --flag --flag") + +EXPOSE 27017 +CMD ["mongod", "--bind_ip_all"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000000..d340570997 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,342 @@ +#!/bin/bash +set -Eeuo pipefail + +if [ "${1:0:1}" = '-' ]; then + set -- mongod "$@" +fi + +originalArgOne="$1" + +# allow the container to be started with `--user` +# all mongo* commands should be dropped to the correct user +if [[ "$originalArgOne" == mongo* ]] && [ "$(id -u)" = '0' ]; then + if [ "$originalArgOne" = 'mongod' ]; then + find /data/configdb /data/db \! -user mongodb -exec chown mongodb '{}' + + fi + + # make sure we can write to stdout and stderr as "mongodb" + # (for our "initdb" code later; see "--logpath" below) + chown --dereference mongodb "/proc/$$/fd/1" "/proc/$$/fd/2" || : + # ignore errors thanks to https://github.com/docker-library/mongo/issues/149 + + exec gosu mongodb "$BASH_SOURCE" "$@" +fi + +# you should use numactl to start your mongod instances, including the config servers, mongos instances, and any clients. +# https://docs.mongodb.com/manual/administration/production-notes/#configuring-numa-on-linux +if [[ "$originalArgOne" == mongo* ]]; then + numa='numactl --interleave=all' + if $numa true &> /dev/null; then + set -- $numa "$@" + fi +fi + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + local val="$def" + if [ "${!var:-}" ]; then + val="${!var}" + elif [ "${!fileVar:-}" ]; then + val="$(< "${!fileVar}")" + fi + export "$var"="$val" + unset "$fileVar" +} + +# see https://github.com/docker-library/mongo/issues/147 (mongod is picky about duplicated arguments) +_mongod_hack_have_arg() { + local checkArg="$1"; shift + local arg + for arg; do + case "$arg" in + "$checkArg"|"$checkArg"=*) + return 0 + ;; + esac + done + return 1 +} +# _mongod_hack_get_arg_val '--some-arg' "$@" +_mongod_hack_get_arg_val() { + local checkArg="$1"; shift + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$checkArg") + echo "$1" + return 0 + ;; + "$checkArg"=*) + echo "${arg#$checkArg=}" + return 0 + ;; + esac + done + return 1 +} +declare -a mongodHackedArgs +# _mongod_hack_ensure_arg '--some-arg' "$@" +# set -- "${mongodHackedArgs[@]}" +_mongod_hack_ensure_arg() { + local ensureArg="$1"; shift + mongodHackedArgs=( "$@" ) + if ! _mongod_hack_have_arg "$ensureArg" "$@"; then + mongodHackedArgs+=( "$ensureArg" ) + fi +} +# _mongod_hack_ensure_no_arg '--some-unwanted-arg' "$@" +# set -- "${mongodHackedArgs[@]}" +_mongod_hack_ensure_no_arg() { + local ensureNoArg="$1"; shift + mongodHackedArgs=() + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + if [ "$arg" = "$ensureNoArg" ]; then + continue + fi + mongodHackedArgs+=( "$arg" ) + done +} +# _mongod_hack_ensure_no_arg '--some-unwanted-arg' "$@" +# set -- "${mongodHackedArgs[@]}" +_mongod_hack_ensure_no_arg_val() { + local ensureNoArg="$1"; shift + mongodHackedArgs=() + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$ensureNoArg") + shift # also skip the value + continue + ;; + "$ensureNoArg"=*) + # value is already included + continue + ;; + esac + mongodHackedArgs+=( "$arg" ) + done +} +# _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@" +# set -- "${mongodHackedArgs[@]}" +_mongod_hack_ensure_arg_val() { + local ensureArg="$1"; shift + local ensureVal="$1"; shift + _mongod_hack_ensure_no_arg_val "$ensureArg" "$@" + mongodHackedArgs+=( "$ensureArg" "$ensureVal" ) +} + +# _js_escape 'some "string" value' +_js_escape() { + jq --null-input --arg 'str' "$1" '$str' +} + +jsonConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-config.json" +tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json" +_parse_config() { + if [ -s "$tempConfigFile" ]; then + return 0 + fi + + local configPath + if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then + # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys) + # see https://docs.mongodb.com/manual/reference/configuration-options/ + mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" > "$jsonConfigFile" + jq 'del(.systemLog, .processManagement, .net, .security)' "$jsonConfigFile" > "$tempConfigFile" + return 0 + fi + + return 1 +} +dbPath= +_dbPath() { + if [ -n "$dbPath" ]; then + echo "$dbPath" + return + fi + + if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then + if _parse_config "$@"; then + dbPath="$(jq -r '.storage.dbPath // empty' "$jsonConfigFile")" + fi + fi + + : "${dbPath:=/data/db}" + + echo "$dbPath" +} + +if [ "$originalArgOne" = 'mongod' ]; then + file_env 'MONGO_INITDB_ROOT_USERNAME' + file_env 'MONGO_INITDB_ROOT_PASSWORD' + # pre-check a few factors to see if it's even worth bothering with initdb + shouldPerformInitdb= + if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then + # if we have a username/password, let's set "--auth" + _mongod_hack_ensure_arg '--auth' "$@" + set -- "${mongodHackedArgs[@]}" + shouldPerformInitdb='true' + elif [ "$MONGO_INITDB_ROOT_USERNAME" ] || [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then + cat >&2 <<-'EOF' + + error: missing 'MONGO_INITDB_ROOT_USERNAME' or 'MONGO_INITDB_ROOT_PASSWORD' + both must be specified for a user to be created + + EOF + exit 1 + fi + + if [ -z "$shouldPerformInitdb" ]; then + # if we've got any /docker-entrypoint-initdb.d/* files to parse later, we should initdb + for f in /docker-entrypoint-initdb.d/*; do + case "$f" in + *.sh|*.js) # this should match the set of files we check for below + shouldPerformInitdb="$f" + break + ;; + esac + done + fi + + # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts) + if [ -n "$shouldPerformInitdb" ]; then + dbPath="$(_dbPath "$@")" + for path in \ + "$dbPath/WiredTiger" \ + "$dbPath/journal" \ + "$dbPath/local.0" \ + "$dbPath/storage.bson" \ + ; do + if [ -e "$path" ]; then + shouldPerformInitdb= + break + fi + done + fi + + if [ -n "$shouldPerformInitdb" ]; then + mongodHackedArgs=( "$@" ) + if _parse_config "$@"; then + _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}" + fi + _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" + _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" + _mongod_hack_ensure_no_arg --bind_ip_all "${mongodHackedArgs[@]}" + + # remove "--auth" and "--replSet" for our initial startup (see https://docs.mongodb.com/manual/tutorial/enable-authentication/#start-mongodb-without-access-control) + # https://github.com/docker-library/mongo/issues/211 + _mongod_hack_ensure_no_arg --auth "${mongodHackedArgs[@]}" + if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then + _mongod_hack_ensure_no_arg_val --replSet "${mongodHackedArgs[@]}" + fi + + sslMode="$(_mongod_hack_have_arg '--sslPEMKeyFile' "$@" && echo 'allowSSL' || echo 'disabled')" # "BadValue: need sslPEMKeyFile when SSL is enabled" vs "BadValue: need to enable SSL via the sslMode flag when using SSL configuration parameters" + _mongod_hack_ensure_arg_val --sslMode "$sslMode" "${mongodHackedArgs[@]}" + + if stat "/proc/$$/fd/1" > /dev/null && [ -w "/proc/$$/fd/1" ]; then + # https://github.com/mongodb/mongo/blob/38c0eb538d0fd390c6cb9ce9ae9894153f6e8ef5/src/mongo/db/initialize_server_global_state.cpp#L237-L251 + # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668 + _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}" + else + initdbLogPath="$(_dbPath "$@")/docker-initdb.log" + echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead" + _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}" + fi + _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}" + + pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid" + rm -f "$pidfile" + _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}" + + "${mongodHackedArgs[@]}" --fork + + mongo=( mongo --host 127.0.0.1 --port 27017 --quiet ) + + # check to see that our "mongod" actually did start up (catches "--help", "--version", MongoDB 3.2 being silly, slow prealloc, etc) + # https://jira.mongodb.org/browse/SERVER-16292 + tries=30 + while true; do + if ! { [ -s "$pidfile" ] && ps "$(< "$pidfile")" &> /dev/null; }; then + # bail ASAP if "mongod" isn't even running + echo >&2 + echo >&2 "error: $originalArgOne does not appear to have stayed running -- perhaps it had an error?" + echo >&2 + exit 1 + fi + if "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then + # success! + break + fi + (( tries-- )) + if [ "$tries" -le 0 ]; then + echo >&2 + echo >&2 "error: $originalArgOne does not appear to have accepted connections quickly enough -- perhaps it had an error?" + echo >&2 + exit 1 + fi + sleep 1 + done + + if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then + rootAuthDatabase='admin' + + "${mongo[@]}" "$rootAuthDatabase" <<-EOJS + db.createUser({ + user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"), + pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"), + roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ] + }) + EOJS + fi + + export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}" + + echo + for f in /docker-entrypoint-initdb.d/*; do + case "$f" in + *.sh) echo "$0: running $f"; . "$f" ;; + *.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;; + *) echo "$0: ignoring $f" ;; + esac + echo + done + + "${mongodHackedArgs[@]}" --shutdown + rm -f "$pidfile" + + echo + echo 'MongoDB init process complete; ready for start up.' + echo + fi + + # MongoDB 3.6+ defaults to localhost-only binding + if mongod --help 2>&1 | grep -q -- --bind_ip_all; then # TODO remove this conditional when 3.4 is no longer supported + haveBindIp= + if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then + haveBindIp=1 + elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then + haveBindIp=1 + fi + if [ -z "$haveBindIp" ]; then + # so if no "--bind_ip" is specified, let's add "--bind_ip_all" + set -- "$@" --bind_ip_all + fi + fi + + unset "${!MONGO_INITDB_@}" +fi + +rm -f "$jsonConfigFile" "$tempConfigFile" + +exec "$@" diff --git a/gpg-keys.sh b/gpg-keys.sh new file mode 100755 index 0000000000..87e61c5c33 --- /dev/null +++ b/gpg-keys.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +versions=( $(grep -vE '^#|^$' gpg-keys.txt | cut -d: -f1) ) + +for version in "${versions[@]}"; do + fingerprints="$( + docker run --rm -e v="$version" buildpack-deps:stretch-curl bash -Eeuo pipefail -xc ' + wget -O key.asc "https://www.mongodb.org/static/pgp/server-$v.asc" >&2 + gpg --batch --import key.asc >&2 + gpg --batch --fingerprint --with-colons | grep "^fpr:" | cut -d: -f10 + ' + )" + awk -F: -v v="$version" -v fpr="$fingerprints" ' + $1 == v { + printf "%s:%s\n", v, fpr + next + } + { print } + ' gpg-keys.txt > gpg-keys.txt.new + mv gpg-keys.txt.new gpg-keys.txt +done diff --git a/gpg-keys.txt b/gpg-keys.txt new file mode 100644 index 0000000000..b28f0a3a0f --- /dev/null +++ b/gpg-keys.txt @@ -0,0 +1,8 @@ +# this file's values are updated via "./gpg-keys.sh" + +4.2:E162F504A20CDF15827F718D4B7C549A058F8B6B +4.0:9DA31620334BD75D9DCB49F368818C72E52529D4 +3.6:2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5 +3.4:0C49F3730359A14518585931BC711F9BA15703C6 + +# these all come directly via https://www.mongodb.org/static/pgp/server-X.Y.asc diff --git a/update.sh b/update.sh index bc9ec34ad6..b22b6f3206 100755 --- a/update.sh +++ b/update.sh @@ -9,8 +9,11 @@ if [ ${#versions[@]} -eq 0 ]; then fi versions=( "${versions[@]%/}" ) -# TODO do something with https://www.mongodb.org/dl/linux/x86_64 instead of scraping the APT repo contents -# (but then have to solve hard "release candidate" problems; ie, if we have 2.6.4 and 2.6.5-rc0 comes out, we don't want 2.6 to switch over to the RC) +defaultFrom='ubuntu:xenial' +declare -A froms=( + [3.4]='debian:jessie-slim' + [3.6]='debian:stretch-slim' +) travisEnv= appveyorEnv= @@ -23,7 +26,7 @@ for version in "${versions[@]}"; do major='testing' fi - from="$(gawk -F '[[:space:]]+' 'toupper($1) == "FROM" { print $2; exit }' "$version/Dockerfile")" # "debian:xxx" + from="${froms[$version]:-$defaultFrom}" distro="${from%%:*}" # "debian", "ubuntu" suite="${from#$distro:}" # "jessie-slim", "xenial" suite="${suite%-slim}" # "jessie", "xenial" @@ -49,14 +52,32 @@ for version in "${versions[@]}"; do packageName="${fullVersion#*=}" fullVersion="${fullVersion%=$packageName}" - ( - set -x - sed -ri \ - -e 's/^(ENV MONGO_MAJOR) .*/\1 '"$major"'/' \ - -e 's/^(ENV MONGO_VERSION) .*/\1 '"$fullVersion"'/' \ - -e 's/^(ARG MONGO_PACKAGE)=.*/\1='"$packageName"'/' \ - "$version/Dockerfile" - ) + gpgKeyVersion="$rcVersion" + minor="${major#*.}" # "4.3" -> "3" + if [ "$(( minor % 2 ))" = 1 ]; then + gpgKeyVersion="${major%.*}.$(( minor + 1 ))" + fi + gpgKeys="$(grep "^$gpgKeyVersion:" gpg-keys.txt | cut -d: -f2)" + + echo "$version: $fullVersion (linux)" + + sed -r \ + -e 's/^(ENV MONGO_MAJOR) .*/\1 '"$major"'/' \ + -e 's/^(ENV MONGO_VERSION) .*/\1 '"$fullVersion"'/' \ + -e 's/^(ARG MONGO_PACKAGE)=.*/\1='"$packageName"'/' \ + -e 's/^(FROM) .*/\1 '"$from"'/' \ + -e 's/%%DISTRO%%/'"$distro"'/' \ + -e 's/%%SUITE%%/'"$suite"'/' \ + -e 's/%%COMPONENT%%/'"$component"'/' \ + -e 's/^(ENV GPG_KEYS) .*/\1 '"$gpgKeys"'/' \ + Dockerfile-linux.template \ + > "$version/Dockerfile" + + if [ "$version" != '3.4' ]; then + sed -ri -e '/backwards compat/d' "$version/Dockerfile" + fi + + cp -a docker-entrypoint.sh "$version/" if [ -d "$version/windows" ]; then windowsVersions="$( @@ -72,24 +93,24 @@ for version in "${versions[@]}"; do windowsSha256="$(curl -fsSL "$windowsLatest.sha256" | cut -d' ' -f1)" windowsVersion="$(echo "$windowsLatest" | sed -r -e "s!^https?://.+(${rcVersion//./\\.}\.[^\"]+)-signed.msi\$!\1!")" - ( - set -x - sed -ri \ - -e 's/^(ENV MONGO_VERSION) .*/\1 '"$windowsVersion"'/' \ - -e 's!^(ENV MONGO_DOWNLOAD_URL) .*!\1 '"$windowsLatest"'!' \ - -e 's/^(ENV MONGO_DOWNLOAD_SHA256) .*/\1 '"$windowsSha256"'/' \ - "$version/windows/"*"/Dockerfile" - ) + echo "$version: $windowsVersion (windows)" for winVariant in \ - nanoserver-{1803,1709,sac2016} \ windowsservercore-{1803,1709,ltsc2016} \ ; do - [ -f "$version/windows/$winVariant/Dockerfile" ] || continue + [ -d "$version/windows/$winVariant" ] || continue - sed -ri \ + sed -r \ + -e 's/^(ENV MONGO_VERSION) .*/\1 '"$windowsVersion"'/' \ + -e 's!^(ENV MONGO_DOWNLOAD_URL) .*!\1 '"$windowsLatest"'!' \ + -e 's/^(ENV MONGO_DOWNLOAD_SHA256) .*/\1 '"$windowsSha256"'/' \ -e 's!^FROM .*!FROM microsoft/'"${winVariant%%-*}"':'"${winVariant#*-}"'!' \ - "$version/windows/$winVariant/Dockerfile" + Dockerfile-windows.template \ + > "$version/windows/$winVariant/Dockerfile" + + if [ "$version" = '3.4' ]; then + sed -ri -e 's/, "--bind_ip_all"//' "$version/windows/$winVariant/Dockerfile" + fi case "$winVariant" in *-1803) travisEnv='\n - os: windows\n dist: 1803-containers\n env: VERSION='"$version VARIANT=windows/$winVariant$travisEnv" ;;