From 601191830ece69f731b7b256c02f9f804da1e6ec Mon Sep 17 00:00:00 2001 From: Will Holley Date: Mon, 2 Sep 2019 09:14:17 +0100 Subject: [PATCH] allow running as arbitrary uid (#151) * Adds guards around entrypoints commands that require root * Broaden permissions within the container filesystem to allow access by non-couchdb users. * Added an example to the documentation which specifies `--user`. Fixes #147 --- 2.3.1/Dockerfile | 156 +++++++++++++++++++------------------ 2.3.1/docker-entrypoint.sh | 63 ++++++++------- README.md | 9 +++ 3 files changed, 128 insertions(+), 100 deletions(-) diff --git a/2.3.1/Dockerfile b/2.3.1/Dockerfile index a266ed8..f8ad0c9 100644 --- a/2.3.1/Dockerfile +++ b/2.3.1/Dockerfile @@ -19,77 +19,77 @@ RUN groupadd -g 5984 -r couchdb && useradd -u 5984 -d /opt/couchdb -g couchdb co # be sure GPG and apt-transport-https are available and functional RUN set -ex; \ - apt-get update; \ - apt-get install -y --no-install-recommends \ - apt-transport-https \ - ca-certificates \ - dirmngr \ - gnupg \ - ; \ - rm -rf /var/lib/apt/lists/* + apt-get update; \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + dirmngr \ + gnupg \ + ; \ + rm -rf /var/lib/apt/lists/* # grab gosu for easy step-down from root and tini for signal handling and zombie reaping # see https://github.com/apache/couchdb-docker/pull/28#discussion_r141112407 ENV GOSU_VERSION 1.11 ENV TINI_VERSION 0.18.0 RUN set -ex; \ - \ - apt-get update; \ - apt-get install -y --no-install-recommends wget; \ - rm -rf /var/lib/apt/lists/*; \ - \ - dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ - \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + \ # install gosu - 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)"; \ - echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ - for server in $(shuf -e pgpkeys.mit.edu \ - ha.pool.sks-keyservers.net \ - hkp://p80.pool.sks-keyservers.net:80 \ - pgp.mit.edu) ; do \ - gpg --batch --keyserver $server --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && break || : ; \ - done; \ - gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ - rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ - chmod +x /usr/local/bin/gosu; \ - gosu nobody true; \ + 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)"; \ + echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ + for server in $(shuf -e pgpkeys.mit.edu \ + ha.pool.sks-keyservers.net \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu) ; do \ + gpg --batch --keyserver $server --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && break || : ; \ + done; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ \ # install tini - wget -O /usr/local/bin/tini "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$dpkgArch"; \ - wget -O /usr/local/bin/tini.asc "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$dpkgArch.asc"; \ - export GNUPGHOME="$(mktemp -d)"; \ - echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ - for server in $(shuf -e pgpkeys.mit.edu \ - ha.pool.sks-keyservers.net \ - hkp://p80.pool.sks-keyservers.net:80 \ - pgp.mit.edu) ; do \ - gpg --batch --keyserver $server --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 && break || : ; \ - done; \ - gpg --batch --verify /usr/local/bin/tini.asc /usr/local/bin/tini; \ - rm -rf "$GNUPGHOME" /usr/local/bin/tini.asc; \ - chmod +x /usr/local/bin/tini; \ - apt-get purge -y --auto-remove wget; \ - tini --version + wget -O /usr/local/bin/tini "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$dpkgArch"; \ + wget -O /usr/local/bin/tini.asc "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ + for server in $(shuf -e pgpkeys.mit.edu \ + ha.pool.sks-keyservers.net \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu) ; do \ + gpg --batch --keyserver $server --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 && break || : ; \ + done; \ + gpg --batch --verify /usr/local/bin/tini.asc /usr/local/bin/tini; \ + rm -rf "$GNUPGHOME" /usr/local/bin/tini.asc; \ + chmod +x /usr/local/bin/tini; \ + apt-get purge -y --auto-remove wget; \ + tini --version # http://docs.couchdb.org/en/latest/install/unix.html#installing-the-apache-couchdb-packages ENV GPG_COUCH_KEY \ # gpg: key D401AB61: public key "Bintray (by JFrog) imported - 8756C4F765C9AC3CB6B85D62379CE192D401AB61 + 8756C4F765C9AC3CB6B85D62379CE192D401AB61 RUN set -xe; \ - export GNUPGHOME="$(mktemp -d)"; \ - echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ - for server in $(shuf -e pgpkeys.mit.edu \ - ha.pool.sks-keyservers.net \ - hkp://p80.pool.sks-keyservers.net:80 \ - pgp.mit.edu) ; do \ - gpg --batch --keyserver $server --recv-keys $GPG_COUCH_KEY && break || : ; \ - done; \ - gpg --batch --export $GPG_COUCH_KEY > /etc/apt/trusted.gpg.d/couchdb.gpg; \ - command -v gpgconf && gpgconf --kill all || :; \ - rm -rf "$GNUPGHOME"; \ - apt-key list + export GNUPGHOME="$(mktemp -d)"; \ + echo "disable-ipv6" >> ${GNUPGHOME}/dirmngr.conf; \ + for server in $(shuf -e pgpkeys.mit.edu \ + ha.pool.sks-keyservers.net \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu) ; do \ + gpg --batch --keyserver $server --recv-keys $GPG_COUCH_KEY && break || : ; \ + done; \ + gpg --batch --export $GPG_COUCH_KEY > /etc/apt/trusted.gpg.d/couchdb.gpg; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -rf "$GNUPGHOME"; \ + apt-key list ENV COUCHDB_VERSION 2.3.1 @@ -97,32 +97,40 @@ RUN echo "deb https://apache.bintray.com/couchdb-deb stretch main" > /etc/apt/so # https://github.com/apache/couchdb-pkg/blob/master/debian/README.Debian RUN set -xe; \ - apt-get update; \ - \ - echo "couchdb couchdb/mode select none" | debconf-set-selections; \ + apt-get update; \ + \ + echo "couchdb couchdb/mode select none" | debconf-set-selections; \ # we DO want recommends this time - DEBIAN_FRONTEND=noninteractive apt-get install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages \ - couchdb="$COUCHDB_VERSION"~stretch \ - ; \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages \ + couchdb="$COUCHDB_VERSION"~stretch \ + ; \ # Undo symlinks to /var/log and /var/lib - rmdir /var/lib/couchdb /var/log/couchdb; \ - rm /opt/couchdb/data /opt/couchdb/var/log; \ - mkdir -p /opt/couchdb/data /opt/couchdb/var/log; \ - chown couchdb:couchdb /opt/couchdb/data /opt/couchdb/var/log; \ - chmod 777 /opt/couchdb/data /opt/couchdb/var/log; \ + rmdir /var/lib/couchdb /var/log/couchdb; \ + rm /opt/couchdb/data /opt/couchdb/var/log; \ + mkdir -p /opt/couchdb/data /opt/couchdb/var/log; \ + chown couchdb:couchdb /opt/couchdb/data /opt/couchdb/var/log; \ + chmod 777 /opt/couchdb/data /opt/couchdb/var/log; \ # Remove file that sets logging to a file - rm /opt/couchdb/etc/default.d/10-filelog.ini; \ - rm -rf /var/lib/apt/lists/* + rm /opt/couchdb/etc/default.d/10-filelog.ini; \ +# Check we own everything in /opt/couchdb. Matches the command in dockerfile_entrypoint.sh + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' +; \ +# Setup directories and permissions for config. Technically these could be 555 and 444 respectively +# but we keep them as 755 and 644 for consistency with CouchDB defaults and the dockerfile_entrypoint.sh. + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' +; \ + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' +; \ +# only local.d needs to be writable for the docker_entrypoint.sh + chmod -f 0777 /opt/couchdb/etc/local.d; \ +# apt clean-up + rm -rf /var/lib/apt/lists/*; # Add configuration -COPY 10-docker-default.ini /opt/couchdb/etc/default.d/ -COPY vm.args /opt/couchdb/etc/ +COPY --chown=couchdb:couchdb 10-docker-default.ini /opt/couchdb/etc/default.d/ +COPY --chown=couchdb:couchdb vm.args /opt/couchdb/etc/ + COPY docker-entrypoint.sh /usr/local/bin RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] -# Setup directories and permissions -RUN find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' + VOLUME /opt/couchdb/data # 5984: Main CouchDB endpoint diff --git a/2.3.1/docker-entrypoint.sh b/2.3.1/docker-entrypoint.sh index 7fdb04b..be9e099 100755 --- a/2.3.1/docker-entrypoint.sh +++ b/2.3.1/docker-entrypoint.sh @@ -25,36 +25,44 @@ if [ "$1" = 'couchdb' ]; then fi if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then - # Check that we own everything in /opt/couchdb and fix if necessary. We also - # add the `-f` flag in all the following invocations because there may be - # cases where some of these ownership and permissions issues are non-fatal - # (e.g. a config file owned by root with o+r is actually fine), and we don't - # to be too aggressive about crashing here ... - find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' + + # this is where runtime configuration changes will be written. + # we need to explicitly touch it here in case /opt/couchdb/etc has + # been mounted as an external volume, in which case it won't exist. + # If running as the couchdb user (i.e. container starts as root), + # write permissions will be granted below. + touch /opt/couchdb/etc/local.d/docker.ini + + # if user is root, assume running under the couchdb user (default) + # and ensure it is able to access files and directories that may be mounted externally + if [ "$(id -u)" = '0' ]; then + # Check that we own everything in /opt/couchdb and fix if necessary. We also + # add the `-f` flag in all the following invocations because there may be + # cases where some of these ownership and permissions issues are non-fatal + # (e.g. a config file owned by root with o+r is actually fine), and we don't + # to be too aggressive about crashing here ... + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' + - # Ensure that data files have the correct permissions. We were previously - # preventing any access to these files outside of couchdb:couchdb, but it - # turns out that CouchDB itself does not set such restrictive permissions - # when it creates the files. The approach taken here ensures that the - # contents of the datadir have the same permissions as they had when they - # were initially created. This should minimize any startup delay. - find /opt/couchdb/data -type d ! -perm 0755 -exec chmod -f 0755 '{}' + - find /opt/couchdb/data -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + # Ensure that data files have the correct permissions. We were previously + # preventing any access to these files outside of couchdb:couchdb, but it + # turns out that CouchDB itself does not set such restrictive permissions + # when it creates the files. The approach taken here ensures that the + # contents of the datadir have the same permissions as they had when they + # were initially created. This should minimize any startup delay. + find /opt/couchdb/data -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/data -type f ! -perm 0644 -exec chmod -f 0644 '{}' + - # Do the same thing for configuration files and directories. Technically - # CouchDB only needs read access to the configuration files as all online - # changes will be applied to the "docker.ini" file below, but we set 644 - # for the sake of consistency. - find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' + - find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + # Do the same thing for configuration files and directories. Technically + # CouchDB only needs read access to the configuration files as all online + # changes will be applied to the "docker.ini" file below, but we set 644 + # for the sake of consistency. + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + fi if [ ! -z "$NODENAME" ] && ! grep "couchdb@" /opt/couchdb/etc/vm.args; then echo "-name couchdb@$NODENAME" >> /opt/couchdb/etc/vm.args fi - # Ensure that CouchDB will write custom settings in this file - touch /opt/couchdb/etc/local.d/docker.ini - if [ "$COUCHDB_USER" ] && [ "$COUCHDB_PASSWORD" ]; then # Create admin only if not already present if ! grep -Pzoqr "\[admins\]\n$COUCHDB_USER =" /opt/couchdb/etc/local.d/*.ini; then @@ -69,7 +77,9 @@ if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then fi fi - chown -f couchdb:couchdb /opt/couchdb/etc/local.d/docker.ini || true + if [ "$(id -u)" = '0' ]; then + chown -f couchdb:couchdb /opt/couchdb/etc/local.d/docker.ini || true + fi # if we don't find an [admins] section followed by a non-comment, display a warning if ! grep -Pzoqr '\[admins\]\n[^;]\w+' /opt/couchdb/etc/default.d/*.ini /opt/couchdb/etc/local.d/*.ini; then @@ -88,8 +98,9 @@ if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then EOWARN fi - - exec gosu couchdb "$@" + if [ "$(id -u)" = '0' ]; then + exec gosu couchdb "$@" + fi fi exec "$@" diff --git a/README.md b/README.md index 42f0a93..7f7d73a 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,15 @@ file = /opt/couchdb/log/couch.log It is recommended to then mount this path to a directory on the host, as CouchDB logging can be quite voluminous. +## Running under a custom UID + +By default, CouchDB will run as the `couchdb` user with UID 5984. Running under a different UID is supported, so long as any volume mounts have appropriate read/write permissions. For example, assuming user `myuser` has write access to `/home/couchdb/data`, the following command will run CouchDB as that user: + +``` +docker run --name my-couchdb --user myuser -v /home/couchdb/data:/opt/couchdb/data %%IMAGE%%:tag +``` + + ----- # Development images