diff --git a/Makefile b/Makefile index 8e442e446c..ec3d131199 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,14 @@ dev-ui-mem: assetcheck dev-ui dev-dynamic-mem: BUILD_TAGS+=memprofiler dev-dynamic-mem: dev-dynamic +# Creates a Docker image by adding the compiled linux/amd64 binary found in ./bin. +# The resulting image is tagged "vault:dev". +docker-dev: prep + docker build -f scripts/docker/Dockerfile -t vault:dev . + +docker-dev-ui: prep + docker build -f scripts/docker/Dockerfile.ui -t vault:dev-ui . + # test runs the unit tests and vets the code test: prep @CGO_ENABLED=$(CGO_ENABLED) \ diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile new file mode 100644 index 0000000000..f511b9a093 --- /dev/null +++ b/scripts/docker/Dockerfile @@ -0,0 +1,64 @@ +# Multi-stage builder to avoid polluting users environment with wrong +# architecture binaries. Since this binary is used in an alpine container, +# we're explicitly compiling for 'linux/amd64' +ARG VERSION=1.13.10 + +FROM golang:${VERSION} AS builder + +ARG CGO_ENABLED=0 +ARG BUILD_TAGS + +WORKDIR /go/src/github.com/hashicorp/vault +COPY . . + +RUN make bootstrap \ + && CGO_ENABLED=$CGO_ENABLED BUILD_TAGS='$BUILD_TAGS' VAULT_DEV_BUILD=1 XC_OSARCH='linux/amd64' sh -c "'./scripts/build.sh'" + +# Docker Image + +FROM alpine:3.10 + +# Create a vault user and group first so the IDs get set the same way, +# even as the rest of this may change over time. +RUN addgroup vault && \ + adduser -S -G vault vault + +# Set up certificates, our base tools, and Vault. +RUN set -eux; \ + apk add --no-cache ca-certificates libcap su-exec dumb-init tzdata + +COPY --from=builder /go/bin/vault /bin/vault + +# /vault/logs is made available to use as a location to store audit logs, if +# desired; /vault/file is made available to use as a location with the file +# storage backend, if desired; the server will be started with /vault/config as +# the configuration directory so you can add additional config files in that +# location. +RUN mkdir -p /vault/logs && \ + mkdir -p /vault/file && \ + mkdir -p /vault/config && \ + chown -R vault:vault /vault + +# Expose the logs directory as a volume since there's potentially long-running +# state in there +VOLUME /vault/logs + +# Expose the file directory as a volume since there's potentially long-running +# state in there +VOLUME /vault/file + +# 8200/tcp is the primary interface that applications use to interact with +# Vault. +EXPOSE 8200 + +# The entry point script uses dumb-init as the top-level process to reap any +# zombie processes created by Vault sub-processes. +# +# For production derivatives of this container, you should add the IPC_LOCK +# capability so that Vault can mlock memory. +COPY ./scripts/docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["docker-entrypoint.sh"] + +# By default you'll get a single-node development server that stores everything +# in RAM and bootstraps itself. Don't use this configuration for production. +CMD ["server", "-dev"] diff --git a/scripts/docker/Dockerfile.ui b/scripts/docker/Dockerfile.ui new file mode 100644 index 0000000000..299d581a73 --- /dev/null +++ b/scripts/docker/Dockerfile.ui @@ -0,0 +1,90 @@ +# Multi-stage builder to avoid polluting users environment with wrong +# architecture binaries. Since this binary is used in an alpine container, +# we're explicitly compiling for 'linux/amd64' +FROM debian:buster AS builder + +ARG VERSION=1.13.10 +ARG CGO_ENABLED=0 +ARG BUILD_TAGS +ENV JOBS=2 + +RUN apt-get update -y && apt-get install --no-install-recommends -y -q \ + curl \ + zip \ + build-essential \ + gcc-multilib \ + g++-multilib \ + ca-certificates \ + git mercurial bzr \ + gnupg \ + libltdl-dev \ + libltdl7 + +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - +RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + +RUN apt-get update -y && apt-get install -y -q nodejs yarn + +RUN rm -rf /var/lib/apt/lists/* + +RUN mkdir /goroot && mkdir /go +RUN curl https://storage.googleapis.com/golang/go${VERSION}.linux-amd64.tar.gz \ + | tar xvzf - -C /goroot --strip-components=1 +ENV GOPATH /go +ENV GOROOT /goroot +ENV PATH $GOROOT/bin:$GOPATH/bin:$PATH + +WORKDIR /go/src/github.com/hashicorp/vault +COPY . . +RUN make bootstrap static-dist \ + && CGO_ENABLED=$CGO_ENABLED BUILD_TAGS='$BUILD_TAGS ui' VAULT_DEV_BUILD=1 XC_OSARCH='linux/amd64' sh -c "'./scripts/build.sh'" + +# Docker Image + +FROM alpine:3.10 + +# Create a vault user and group first so the IDs get set the same way, +# even as the rest of this may change over time. +RUN addgroup vault && \ + adduser -S -G vault vault + +# Set up certificates, our base tools, and Vault. +RUN set -eux; \ + apk add --no-cache ca-certificates libcap su-exec dumb-init tzdata + +COPY --from=builder /go/bin/vault /bin/vault + +# /vault/logs is made available to use as a location to store audit logs, if +# desired; /vault/file is made available to use as a location with the file +# storage backend, if desired; the server will be started with /vault/config as +# the configuration directory so you can add additional config files in that +# location. +RUN mkdir -p /vault/logs && \ + mkdir -p /vault/file && \ + mkdir -p /vault/config && \ + chown -R vault:vault /vault + +# Expose the logs directory as a volume since there's potentially long-running +# state in there +VOLUME /vault/logs + +# Expose the file directory as a volume since there's potentially long-running +# state in there +VOLUME /vault/file + +# 8200/tcp is the primary interface that applications use to interact with +# Vault. +EXPOSE 8200 + +# The entry point script uses dumb-init as the top-level process to reap any +# zombie processes created by Vault sub-processes. +# +# For production derivatives of this container, you should add the IPC_LOCK +# capability so that Vault can mlock memory. +COPY ./scripts/docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["docker-entrypoint.sh"] + +# By default you'll get a single-node development server that stores everything +# in RAM and bootstraps itself. Don't use this configuration for production. +CMD ["server", "-dev"] diff --git a/scripts/docker/docker-entrypoint.sh b/scripts/docker/docker-entrypoint.sh new file mode 100755 index 0000000000..81214cb9b6 --- /dev/null +++ b/scripts/docker/docker-entrypoint.sh @@ -0,0 +1,104 @@ +#!/usr/bin/dumb-init /bin/sh +set -e + +# Note above that we run dumb-init as PID 1 in order to reap zombie processes +# as well as forward signals to all processes in its session. Normally, sh +# wouldn't do either of these functions so we'd leak zombies as well as do +# unclean termination of all our sub-processes. + +# Prevent core dumps +ulimit -c 0 + +# Allow setting VAULT_REDIRECT_ADDR and VAULT_CLUSTER_ADDR using an interface +# name instead of an IP address. The interface name is specified using +# VAULT_REDIRECT_INTERFACE and VAULT_CLUSTER_INTERFACE environment variables. If +# VAULT_*_ADDR is also set, the resulting URI will combine the protocol and port +# number with the IP of the named interface. +get_addr () { + local if_name=$1 + local uri_template=$2 + ip addr show dev $if_name | awk -v uri=$uri_template '/\s*inet\s/ { \ + ip=gensub(/(.+)\/.+/, "\\1", "g", $2); \ + print gensub(/^(.+:\/\/).+(:.+)$/, "\\1" ip "\\2", "g", uri); \ + exit}' +} + +if [ -n "$VAULT_REDIRECT_INTERFACE" ]; then + export VAULT_REDIRECT_ADDR=$(get_addr $VAULT_REDIRECT_INTERFACE ${VAULT_REDIRECT_ADDR:-"http://0.0.0.0:8200"}) + echo "Using $VAULT_REDIRECT_INTERFACE for VAULT_REDIRECT_ADDR: $VAULT_REDIRECT_ADDR" +fi +if [ -n "$VAULT_CLUSTER_INTERFACE" ]; then + export VAULT_CLUSTER_ADDR=$(get_addr $VAULT_CLUSTER_INTERFACE ${VAULT_CLUSTER_ADDR:-"https://0.0.0.0:8201"}) + echo "Using $VAULT_CLUSTER_INTERFACE for VAULT_CLUSTER_ADDR: $VAULT_CLUSTER_ADDR" +fi + +# VAULT_CONFIG_DIR isn't exposed as a volume but you can compose additional +# config files in there if you use this image as a base, or use +# VAULT_LOCAL_CONFIG below. +VAULT_CONFIG_DIR=/vault/config + +# You can also set the VAULT_LOCAL_CONFIG environment variable to pass some +# Vault configuration JSON without having to bind any volumes. +if [ -n "$VAULT_LOCAL_CONFIG" ]; then + echo "$VAULT_LOCAL_CONFIG" > "$VAULT_CONFIG_DIR/local.json" +fi + +# If the user is trying to run Vault directly with some arguments, then +# pass them to Vault. +if [ "${1:0:1}" = '-' ]; then + set -- vault "$@" +fi + +# Look for Vault subcommands. +if [ "$1" = 'server' ]; then + shift + set -- vault server \ + -config="$VAULT_CONFIG_DIR" \ + -dev-root-token-id="$VAULT_DEV_ROOT_TOKEN_ID" \ + -dev-listen-address="${VAULT_DEV_LISTEN_ADDRESS:-"0.0.0.0:8200"}" \ + "$@" +elif [ "$1" = 'version' ]; then + # This needs a special case because there's no help output. + set -- vault "$@" +elif vault --help "$1" 2>&1 | grep -q "vault $1"; then + # We can't use the return code to check for the existence of a subcommand, so + # we have to use grep to look for a pattern in the help output. + set -- vault "$@" +fi + +# If we are running Vault, make sure it executes as the proper user. +if [ "$1" = 'vault' ]; then + if [ -z "$SKIP_CHOWN" ]; then + # If the config dir is bind mounted then chown it + if [ "$(stat -c %u /vault/config)" != "$(id -u vault)" ]; then + chown -R vault:vault /vault/config || echo "Could not chown /vault/config (may not have appropriate permissions)" + fi + + # If the logs dir is bind mounted then chown it + if [ "$(stat -c %u /vault/logs)" != "$(id -u vault)" ]; then + chown -R vault:vault /vault/logs + fi + + # If the file dir is bind mounted then chown it + if [ "$(stat -c %u /vault/file)" != "$(id -u vault)" ]; then + chown -R vault:vault /vault/file + fi + fi + + if [ -z "$SKIP_SETCAP" ]; then + # Allow mlock to avoid swapping Vault memory to disk + setcap cap_ipc_lock=+ep $(readlink -f $(which vault)) + + # In the case vault has been started in a container without IPC_LOCK privileges + if ! vault -version 1>/dev/null 2>/dev/null; then + >&2 echo "Couldn't start vault with IPC_LOCK. Disabling IPC_LOCK, please use --privileged or --cap-add IPC_LOCK" + setcap cap_ipc_lock=-ep $(readlink -f $(which vault)) + fi + fi + + if [ "$(id -u)" = '0' ]; then + set -- su-exec vault "$@" + fi +fi + +exec "$@"