diff --git a/.github/actions/publish_docker_image/action.yml b/.github/actions/publish_docker_image/action.yml index 1a8bad7..c21be74 100644 --- a/.github/actions/publish_docker_image/action.yml +++ b/.github/actions/publish_docker_image/action.yml @@ -16,8 +16,6 @@ runs: with: images: | axiom/docker-erddap - flavor: | - suffix=-jdk21-openjdk,onlatest=true tags: | type=raw,value=latest,enable={{is_default_branch}} type=ref,event=tag diff --git a/Dockerfile b/Dockerfile index 8fb6f4b..15faee0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,100 +1,58 @@ -ARG BASE_IMAGE=tomcat:10.1.26-jdk21-temurin-jammy -#referencing a specific image digest pins our unidata tomcat-docker image to platform amd64 (good) -ARG UNIDATA_TOMCAT_IMAGE=unidata/tomcat-docker:10-jdk17@sha256:af7d3fecec753cbd438f25881deeaf48b40ac1f105971d6f300252e104e39fb2 -FROM ${UNIDATA_TOMCAT_IMAGE} AS unidata-tomcat-image -FROM ${BASE_IMAGE} +ARG ERDDAP_VERSION=v2.27.0 +ARG BASE_IMAGE=erddap/erddap:$ERDDAP_VERSION +FROM $BASE_IMAGE -#use approaches and hardened files from https://github.com/Unidata/tomcat-docker -#note: we don't inherit directly from Unidata/tomcat-docker to allow more -#flexibility in building images using different tomcat base images, architectures, etc -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gosu \ - zip \ - unzip \ - && \ - # Cleanup - apt-get clean && \ - rm -rf /var/lib/apt/lists/* && \ - # Eliminate default web applications - rm -rf ${CATALINA_HOME}/webapps/* && \ - rm -rf ${CATALINA_HOME}/webapps.dist && \ - # Obscuring server info - cd ${CATALINA_HOME}/lib && \ - mkdir -p org/apache/catalina/util/ && \ - unzip -j catalina.jar org/apache/catalina/util/ServerInfo.properties \ - -d org/apache/catalina/util/ && \ - sed -i 's/server.info=.*/server.info=Apache Tomcat/g' \ - org/apache/catalina/util/ServerInfo.properties && \ - zip -ur catalina.jar \ - org/apache/catalina/util/ServerInfo.properties && \ - rm -rf org && cd ${CATALINA_HOME} && \ - # Setting restrictive umask container-wide - echo "session optional pam_umask.so" >> /etc/pam.d/common-session && \ - sed -i 's/UMASK.*022/UMASK 007/g' /etc/login.defs - -# Security enhanced web.xml -COPY --from=unidata-tomcat-image ${CATALINA_HOME}/conf/web.xml ${CATALINA_HOME}/conf/ - -# Security enhanced server.xml -COPY --from=unidata-tomcat-image ${CATALINA_HOME}/conf/server.xml ${CATALINA_HOME}/conf/ - -ARG ERDDAP_VERSION=2.25.1 -ARG ERDDAP_CONTENT_VERSION=1.0.0 -ARG ERDDAP_WAR_URL="https://github.com/ERDDAP/erddap/releases/download/v${ERDDAP_VERSION}/erddap.war" -ARG ERDDAP_CONTENT_URL="https://github.com/ERDDAP/erddapContent/archive/refs/tags/content${ERDDAP_CONTENT_VERSION}.zip" -ENV ERDDAP_bigParentDirectory=/erddapData - -RUN apt-get update && apt-get install -y unzip xmlstarlet \ - && if ! command -v gosu &> /dev/null; then apt-get install -y gosu; fi \ +RUN apt-get update && apt-get install -y gettext-base xmlstarlet \ && rm -rf /var/lib/apt/lists/* -ARG BUST_CACHE=1 -RUN \ - mkdir -p /tmp/dl && \ - curl -fSL "${ERDDAP_WAR_URL}" -o /tmp/dl/erddap.war && \ - unzip /tmp/dl/erddap.war -d ${CATALINA_HOME}/webapps/erddap/ && \ - curl -fSL "${ERDDAP_CONTENT_URL}" -o /tmp/dl/erddapContent.zip && \ - unzip /tmp/dl/erddapContent.zip -d /tmp/dl/erddapContent && \ - find /tmp/dl/erddapContent -type d -name content -exec cp -r "{}" ${CATALINA_HOME} \; && \ - rm -rf /tmp/dl && \ - sed -i 's##\n&#' ${CATALINA_HOME}/conf/context.xml && \ - rm -rf /tmp/* /var/tmp/* && \ - mkdir -p ${ERDDAP_bigParentDirectory} - -# Java options -COPY files/setenv.sh ${CATALINA_HOME}/bin/setenv.sh - -# server.xml fixup -COPY update-server-xml.sh /opt/update-server-xml.sh -RUN /opt/update-server-xml.sh - -# Default configuration -# Note: Make sure ERDDAP_flagKeyKey is set either in a runtime environment variable or in setup.xml -# If a value is not set, a random value for ERDDAP_flagKeyKey will be generated at runtime. -ENV ERDDAP_baseHttpsUrl="https://localhost:8443" \ - ERDDAP_emailEverythingTo="nobody@example.com" \ - ERDDAP_emailDailyReportsTo="nobody@example.com" \ - ERDDAP_emailFromAddress="nothing@example.com" \ - ERDDAP_emailUserName="" \ - ERDDAP_emailPassword="" \ - ERDDAP_emailProperties="" \ - ERDDAP_emailSmtpHost="" \ - ERDDAP_emailSmtpPort="" \ - ERDDAP_adminInstitution="Axiom Docker Install" \ - ERDDAP_adminInstitutionUrl="https://github.com/axiom-data-science/docker-erddap" \ - ERDDAP_adminIndividualName="Axiom Docker Install" \ - ERDDAP_adminPosition="Software Engineer" \ - ERDDAP_adminPhone="555-555-5555" \ - ERDDAP_adminAddress="123 Irrelevant St." \ - ERDDAP_adminCity="Nowhere" \ - ERDDAP_adminStateOrProvince="AK" \ - ERDDAP_adminPostalCode="99504" \ - ERDDAP_adminCountry="USA" \ - ERDDAP_adminEmail="nobody@example.com" - -COPY entrypoint.sh datasets.d.sh / -ENTRYPOINT ["/entrypoint.sh"] - -EXPOSE 8080 -CMD ["catalina.sh", "run"] +COPY datasets.d.sh / + +# advise users to use upstream offical ERDDAP docker image +# if they aren't using experimental features in this image +COPY --chmod=755 </dev/null | grep -q "$DATASETS_XML_MD5SUM"; then + #we don't have a backup of this version of datasets.xml yet, make one + DATASETS_XML_BACKUP="${ERDDAP_CONTENT_DIR}"/datasets.xml.$(date -u +"%Y%m%dT%H%M%SZ").bak + echo "Backing up "${DATASETS_XML}" to ${DATASETS_XML_BACKUP}" + cp "$DATASETS_XML" "${DATASETS_XML_BACKUP}" + fi + fi + /datasets.d.sh -o "$DATASETS_XML" -w +fi +EOF + +ENV ERDDAP_useHeadersForUrl=true \ + ERDDAP_useSaxParser=true diff --git a/README.md b/README.md index 4b810b7..1d84c05 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,37 @@ # ERDDAP on Docker -A feature full Tomcat (SSL over APR, etc.) running [ERDDAP](http://coastwatch.pfeg.noaa.gov/erddap/index.html) +[ERDDAP](http://coastwatch.pfeg.noaa.gov/erddap/index.html) docker image with experimental features (datasets.d, see below). + +## Upstream Official ERDDAP Docker Image + +As of version v2.27.0 this image (axiom/docker-erddap) +is derived from the official ERDDAP Docker image (erddap/erddap). + +If you are not using any experimental functionality offered +by the axiom image (notably datasets.d), you are recommended +to use the official ERDDAP Docker image instead. + +See https://hub.docker.com/r/erddap/erddap for more details. + +## Versions Most recent versions: -* `axiom/docker-erddap:latest-jdk21-openjdk` (2.25.1) +* `axiom/docker-erddap:latest` +* `axiom/docker-erddap:v2.27.0` * `axiom/docker-erddap:2.25.1-jdk21-openjdk` -* `axiom/docker-erddap:2.24-jdk21-openjdk` -* `axiom/docker-erddap:2.23-jdk17-openjdk` -See all versions available [here](https://hub.docker.com/r/axiom/docker-erddap/tags). As always, consult the [ERDDAP Changes](https://coastwatch.pfeg.noaa.gov/erddap/download/changes.html) documentation before upgrading your server. +See all versions available [here](https://hub.docker.com/r/axiom/docker-erddap/tags). +As always, consult the [ERDDAP Changes](https://coastwatch.pfeg.noaa.gov/erddap/download/changes.html) +documentation before upgrading your server. + +Use any of the `latest-*` images with caution as they follow the upstream image, +and is not as thoroughly tested as tagged images. -Use any of the `latest-*` images with caution as they follow the upstream image, and is not as thoroughly tested as tagged images. +## Breaking changes -[Dependabot](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/keeping-your-dependencies-updated-automatically) is used to automatically make PRs to update the upstream image ([`.github/dependabot.yml`](.github/dependabot.yml)). +* v2.27.0 +** CORS is now off by default. To enable it, set `ERDDAP_enableCors=true`. ## Quickstart @@ -37,7 +55,7 @@ or, generate a basic dataset configuration without input for later customization ```bash -./make-dataset.xml /path/to/your.csv EDDTableFromAsciiFiles > /path/to/your-dataset.xml +./make-dataset-xml.sh /path/to/your.csv EDDTableFromAsciiFiles > /path/to/your-dataset.xml ``` ## Configuration @@ -48,10 +66,7 @@ See [these instructions for configuring Tomcat](https://github.com/unidata/tomca ### CORS -The [Tomcat configuration](https://github.com/unidata/tomcat-docker) used by this image enables the -[Apache Tomcat CORS filter](https://tomcat.apache.org/tomcat-8.5-doc/config/filter.html#CORS_Filter) by -default. To disable it (maybe you want to handle CORS uniformly in a proxying webserver?), set environment -variable `DISABLE_CORS` to `1`. +CORS is off by default. To enable it, set `ERDDAP_enableCors=true`. ### ERDDAP @@ -241,6 +256,8 @@ Example: ERDDAP_DATASETS_cacheMinutes=20 ./datasets.d.sh -d examples/datasets.d ``` +As of version v2.27.0 environment variables in the generated datasets.xml file will also be replaced using envsubst. + ##### Elegantly removing datasets in datasets.d mode ERDDAP has a [specific process](https://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html#active) diff --git a/datasets.d.sh b/datasets.d.sh index e215f61..52bf70e 100755 --- a/datasets.d.sh +++ b/datasets.d.sh @@ -12,6 +12,8 @@ DATASETSD_OUTPUT_PATH="${DATASETSD_OUTPUT_PATH:-/usr/local/tomcat/content/erddap DATASETSD_MARK_REMOVED_DATASETS_INACTIVE="${DATASETSD_MARK_REMOVED_DATASETS_INACTIVE:-0}" DATASETSD_WRITE_TO_OUTPUT_PATH="${DATASETSD_WRITE_TO_OUTPUT_PATH:-0}" DATASETSD_REFRESH_MISSING_DATASETS="${DATASETSD_REFRESH_MISSING_DATASETS:-0}" +ERDDAP_bigParentDirectory="${ERDDAP_bigParentDirectory:-/erddapData}" + while getopts ":d:io:rw" opt; do case ${opt} in d ) @@ -71,18 +73,25 @@ if [ "$DATASETSD_MARK_REMOVED_DATASETS_INACTIVE" == "1" ] && [ -n "$DATASETSD_OU true fi -#empty edit for formatting -DXML=$(echo "$DXML" | xmlstarlet edit --inplace) +#empty edit for formatting, substituting env vars with envsubst +DXML=$(echo "$DXML" | xmlstarlet edit --inplace | envsubst) #write output to target file if one was provided and write to stdout flag was not provided if [ -n "$DATASETSD_OUTPUT_PATH" ] && [ "$DATASETSD_WRITE_TO_OUTPUT_PATH" == "1" ]; then echo "$DXML" > "$DATASETSD_OUTPUT_PATH" #set refresh flags for any datasetIDs in datasets.xml that are not in the running ERDDAP config if the refresh option was set - if [ -n "$DATASETSD_REFRESH_MISSING_DATASETS" ] && [ "$DATASETSD_REFRESH_MISSING_DATASETS" == "1" ]; then - comm -23 \ - <(xmlstarlet select -t -v "/erddapDatasets/dataset/@datasetID" "$DATASETSD_OUTPUT_PATH" | sort) \ - <(curl -sS "http://localhost:8080/erddap/tabledap/allDatasets.csv0?datasetID" | grep -v "^allDatasets$" | sort) \ - | xargs -I{} touch /erddapData/flag/{} + #check if erddap is running and /erddapData/flag exists first + if [ -n "$DATASETSD_REFRESH_MISSING_DATASETS" ] && [ "$DATASETSD_REFRESH_MISSING_DATASETS" == "1" ] ; then + if ! curl -fsS "http://localhost:8080/erddap/index.html" &> /dev/null; then + echo "Skipping refresh of missing datasets because ERDDAP isn't running" + elif ! [ -d "${ERDDAP_bigParentDirectory}/flag" ]; then + echo "Skipping refresh of missing datasets because ${ERDDAP_bigParentDirectory}/flag directory doesn't exist" + else + comm -23 \ + <(xmlstarlet select -t -v "/erddapDatasets/dataset/@datasetID" "$DATASETSD_OUTPUT_PATH" | sort) \ + <(curl -sS "http://localhost:8080/erddap/tabledap/allDatasets.csv0?datasetID" | grep -v "^allDatasets$" | sort) \ + | xargs -I{} touch "${ERDDAP_bigParentDirectory}"/flag/{} + fi fi else echo "$DXML" diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 9f6f42f..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash -set -e - -# preferable to fire up Tomcat via start-tomcat.sh which will start Tomcat with -# security manager, but inheriting containers can also start Tomcat via -# catalina.sh - -if [ "$1" = 'start-tomcat.sh' ] || [ "$1" = 'catalina.sh' ]; then - # generate random flagKeyKey if not set - if [ -z "$ERDDAP_flagKeyKey" ] && grep "CHANGE THIS TO YOUR FAVORITE QUOTE" \ - "${CATALINA_HOME}/content/erddap/setup.xml" &> /dev/null; then - echo "flagKeyKey isn't properly set. Generating a random value." >&2 - export ERDDAP_flagKeyKey=$(cat /proc/sys/kernel/random/uuid) - fi - - USER_ID=${TOMCAT_USER_ID:-1000} - GROUP_ID=${TOMCAT_GROUP_ID:-1000} - - ### - # Tomcat user - ### - # create group for GROUP_ID if one doesn't already exist - if ! getent group $GROUP_ID &> /dev/null; then - groupadd -r tomcat -g $GROUP_ID - fi - # create user for USER_ID if one doesn't already exist - if ! getent passwd $USER_ID &> /dev/null; then - useradd -u $USER_ID -g $GROUP_ID tomcat - fi - # alter USER_ID with nologin shell and CATALINA_HOME home directory - usermod -d "${CATALINA_HOME}" -s /sbin/nologin $(id -u -n $USER_ID) - - ### - # Change CATALINA_HOME ownership to tomcat user and tomcat group - # Restrict permissions on conf - ### - - chown -R $USER_ID:$GROUP_ID ${CATALINA_HOME} && find ${CATALINA_HOME}/conf \ - -type d -exec chmod 755 {} \; -o -type f -exec chmod 400 {} \; - chown -R $USER_ID:$GROUP_ID /erddapData - sync - - ### - # Deactivate CORS filter in web.xml if DISABLE_CORS=1 - # Useful if CORS is handled outside of Tomcat (e.g. in a proxying webserver like nginx) - ### - if [ "$DISABLE_CORS" == "1" ]; then - echo "Deactivating Tomcat CORS filter" - xmlstarlet edit --inplace --delete '//_:filter[./_:filter-name = "CorsFilter"]' \ - --delete '//_:filter-mapping[./_:filter-name = "CorsFilter"]' "${CATALINA_HOME}/conf/web.xml" - fi - - ### - # Add datasets in /datasets.d to datasets.xml - ### - if [ -d "/datasets.d" ]; then - echo "Creating datasets.xml from /datasets.d" - ERDDAP_CONTENT_DIR="/usr/local/tomcat/content/erddap" - DATASETS_XML="${ERDDAP_CONTENT_DIR}/datasets.xml" - if [ -f "$DATASETS_XML" ]; then - #datasets.xml exists, make sure we have a backup of it - DATASETS_XML_MD5SUM=$(md5sum "$DATASETS_XML" | awk '{print $1}') - if ! md5sum "${ERDDAP_CONTENT_DIR}/datasets.xml.*.bak" 2>/dev/null | grep -q "$DATASETS_XML_MD5SUM"; then - #we don't have a backup of this version of datasets.xml yet, make one - DATASETS_XML_BACKUP="${ERDDAP_CONTENT_DIR}"/datasets.xml.$(date -u +"%Y%m%dT%H%M%SZ").bak - echo "Backing up "${DATASETS_XML}" to ${DATASETS_XML_BACKUP}" - cp "$DATASETS_XML" "${DATASETS_XML_BACKUP}" - fi - fi - /datasets.d.sh -o "$DATASETS_XML" -w - fi - - ### - # Run executables/shell scripts in /init.d on each container startup - # Inspired by postgres' /docker-entrypoint-initdb.d - # https://github.com/docker-library/docs/blob/master/postgres/README.md#initialization-scripts - # https://github.com/docker-library/postgres/blob/master/docker-entrypoint.sh#L156 - ### - if [ -d "/init.d" ]; then - for f in /init.d/*; do - if [ -x "$f" ]; then - echo "Executing $f" - "$f" - elif [[ $f == *.sh ]]; then - echo "Sourcing $f (not executable)" - . "$f" - fi - done - fi - - exec gosu $USER_ID "$@" -fi - -exec "$@" diff --git a/examples/datasets.d/trees.csv.xml b/examples/datasets.d/trees.csv.xml index 9dad79c..12f4ae8 100644 --- a/examples/datasets.d/trees.csv.xml +++ b/examples/datasets.d/trees.csv.xml @@ -1,4 +1,4 @@ - + 10080 10000 /data/ diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml index 75c7920..19927eb 100644 --- a/examples/docker-compose.yml +++ b/examples/docker-compose.yml @@ -8,7 +8,7 @@ services: ERDDAP_DATASETS_standardPrivacyPolicy: "

Any and all usage of this data is permitted.

" DATASETSD_MARK_REMOVED_DATASETS_INACTIVE: "1" DATASETSD_REFRESH_MISSING_DATASETS: "1" - DISABLE_CORS: "1" + TREES_DATASET_NAME: "asdf-trees" volumes: - ./data:/data:ro - ./datasets.d:/datasets.d:ro diff --git a/files/setenv.sh b/files/setenv.sh deleted file mode 100755 index 5956d9b..0000000 --- a/files/setenv.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh - -if [ -f "${CATALINA_HOME}/bin/config.sh" ]; -then - set -o allexport - source "${CATALINA_HOME}/bin/config.sh" - set +o allexport -fi - -ERDDAP_CONFIG=$(env | grep --regexp "^ERDDAP_.*$" | sort) -if [ -n "$ERDDAP_CONFIG" ]; then - echo -e "ERDDAP configured with:\n$ERDDAP_CONFIG" -fi - -JAVA_MAJOR_VERSION=$(java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1) - -# JAVA_OPTS -NORMAL="-server" - -# Memory -if [ -n "$ERDDAP_MAX_RAM_PERCENTAGE" ]; then - JVM_MEMORY_ARGS="-XX:MaxRAMPercentage=${ERDDAP_MAX_RAM_PERCENTAGE}" -else - ERDDAP_MEMORY="${ERDDAP_MEMORY:-4G}" - JVM_MEMORY_ARGS="-Xms${ERDDAP_MIN_MEMORY:-${ERDDAP_MEMORY}} -Xmx${ERDDAP_MAX_MEMORY:-${ERDDAP_MEMORY}}" -fi - -HEAP_DUMP="-XX:+HeapDumpOnOutOfMemoryError" -HEADLESS="-Djava.awt.headless=true" - -EXTRAS=${JAVA_EXTRAS:-} -if [ $JAVA_MAJOR_VERSION -lt 9 ]; then - #these options are deprecated in java 9 and illegal in java 14+ - EXTRAS="$EXTRAS -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled" -fi - -CONTENT_ROOT="-DerddapContentDirectory=$CATALINA_HOME/content/erddap" -JNA_DIR="-Djna.tmpdir=/tmp/" -FASTBOOT="-Djava.security.egd=file:/dev/./urandom" - -JAVA_OPTS="$JAVA_OPTS $NORMAL $JVM_MEMORY_ARGS $HEAP_DUMP $HEADLESS $EXTRAS $CONTENT_ROOT/ $JNA_DIR $FASTBOOT" -echo "ERDDAP Running with: $JAVA_OPTS" diff --git a/make-dataset-xml.sh b/make-dataset-xml.sh index 745d07b..ff935a8 100755 --- a/make-dataset-xml.sh +++ b/make-dataset-xml.sh @@ -20,7 +20,9 @@ DATASET_TYPE=${2:-"EDDTableFromAsciiFiles"} TARGET_FILENAME="$(basename ""$TARGET_FILE"")" docker run --rm -v $(realpath "$TARGET_FILE"):/data_directory/data_file \ - --workdir /usr/local/tomcat/webapps/erddap/WEB-INF axiom/docker-erddap \ + --workdir /usr/local/tomcat/webapps/erddap/WEB-INF \ + -e ERDDAP_flagKeyKey=$(date +%s) \ + axiom/docker-erddap:latest-jdk21-openjdk \ bash ./GenerateDatasetsXml.sh ${DATASET_TYPE} /data_directory data_file $(yes '""' | head -n 18) \ | sed -n '/^&2 - exit 1 -fi - -RELAXED_PATH_CHARS="[]|" -RELAXED_QUERY_CHARS="[]:|{}^\`"<>" - -function set_attribute { - ELEM="$1" - ATTR="$2" - VAL="$3" - if [ -z "$(xmlstarlet sel -t -c "${ELEM}[@${ATTR}='${VAL}']" $SERVER_XML)" ]; then - #xmlstarlet escapes special characters like & when writing values, and we - #want the attributes to be exactly as we define them. insert replacement - #target tokens instead, and then replace with sed. - #ampersands are also special characters in sed, so replace with ~ first - #and then replace again back to & - TOKEN="__${ATTR}__" - xmlstarlet edit --inplace -P -u "${ELEM}/@${ATTR}" -v "${TOKEN}" \ - -i "${ELEM}[not(@${ATTR})]" -t attr -n "${ATTR}" -v "${TOKEN}" \ - $SERVER_XML - sed -i -e "s/${TOKEN}/$( echo $VAL | tr '&' '~')/" -e "s/~/\&/g" $SERVER_XML - fi -} - -#set Connector relaxedPathChars and relaxedQueryChars to allow DAP queries -set_attribute /Server/Service/Connector relaxedPathChars "$RELAXED_PATH_CHARS" -set_attribute /Server/Service/Connector relaxedQueryChars "$RELAXED_QUERY_CHARS" - -# Enable request attributes so that, when using a reverse proxy, the original -# client ip is recorded in logs rather than the internal proxy ip -set_attribute /Server/Service/Engine/Host/Valve requestAttributesEnable "true" - -#create RemoteIpValve if missing. this is needed so ERDDAP knows when its responding to https requests -#end result should look like: -# -#https://stackoverflow.com/a/9172796/193435 -if [ -z "$(xmlstarlet sel -t -c "/Server/Service/Engine/Host/Valve[@className='org.apache.catalina.valves.RemoteIpValve']" $SERVER_XML)" ]; then - xmlstarlet edit --inplace -P -s /Server/Service/Engine/Host -t elem -n RemoteIpValve -v "" \ - -i //RemoteIpValve -t attr -n "className" -v "org.apache.catalina.valves.RemoteIpValve" \ - -i //RemoteIpValve -t attr -n "remoteIpHeader" -v "X-Forwarded-For" \ - -i //RemoteIpValve -t attr -n "protocolHeader" -v "X-Forwarded-Proto" \ - -i //RemoteIpValve -t attr -n "protocolHeaderHttpsValue" -v "https" \ - -r //RemoteIpValve -v Valve \ - $SERVER_XML -fi