blob: 4377974ac5aac3bef581af69d3870c724469d040 [file]
#!/bin/sh
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Renders both pieces of runtime configuration — the nginx server block and the application's
# config.json — from the environment, then hands off to nginx.
#
# ## config.json belongs to this script. Deployments must not edit it.
#
# This file is written whole on every container start, from the environment and nothing else.
# Any key it does not emit is therefore *erased* at startup, whatever the image contained.
#
# That used to be a trap. An earlier revision of this comment claimed "every key now has a
# default here, so the file is complete by construction and no key can go missing" — but the
# heredoc below emits five keys, and `allowedApiOrigins`, `institutionFeatures` and `nav` were
# added to `AppConfig` afterwards. A deployment that baked any of those into the image lost them
# silently on the next restart, `allowedApiOrigins` included, which backs a security control.
#
# The fix is not to keep this table in step with `AppConfig` forever — that is a race this script
# will keep losing. It is that a deployment has its own file now:
#
# branding/config.json
#
# read by `ConfigService` after this one and merged over it, never written by this script or by
# upstream, and absent by default. Everything a deployment wants to set goes there. See
# DOCS/CUSTOMIZATION.md.
#
# So the rule for this heredoc is narrow: it carries only what the *container environment* is the
# right source for. Adding an `AppConfig` key here is almost always the wrong move.
set -eu
HTML_ROOT=/usr/share/nginx/html
TEMPLATE=/etc/nginx/templates/default.conf.template
RENDERED=/etc/nginx/conf.d/default.conf
# ---------------------------------------------------------------------------------------------
# nginx: where the /api/ proxy forwards to
# ---------------------------------------------------------------------------------------------
#
# FINERACT_API_URL names the *upstream* Fineract, reachable from this container — not from the
# browser. The browser always talks to /api on this origin, which is what lets the Content-
# Security-Policy keep `connect-src 'self'` instead of being widened per deployment.
FINERACT_UPSTREAM="${FINERACT_API_URL:-https://fineract:8443/fineract-provider/api}"
FINERACT_PROXY_SSL_VERIFY="${FINERACT_PROXY_SSL_VERIFY:-off}"
# See the note in nginx.conf.template: Fineract answers 302 to every API call when this says
# anything but https, because it decides its channel requirement from this header and the hop it
# is answering really is TLS.
FINERACT_FORWARDED_PROTO="${FINERACT_FORWARDED_PROTO:-https}"
export FINERACT_UPSTREAM FINERACT_PROXY_SSL_VERIFY FINERACT_FORWARDED_PROTO
# A trailing slash here would double up against the one in `proxy_pass ${FINERACT_UPSTREAM}/`
# and send Fineract `//v1/clients`, which it answers with a 404 that looks like a routing bug.
FINERACT_UPSTREAM="${FINERACT_UPSTREAM%/}"
export FINERACT_UPSTREAM
if [ ! -f "$TEMPLATE" ]; then
echo "entrypoint: $TEMPLATE is missing; the image was not built from deploy/Dockerfile." >&2
exit 1
fi
# Only these three names are substituted. Passing the list is what keeps envsubst from eating
# nginx's own runtime variables — $uri, $host, $remote_addr — which must reach the finished file
# intact. Without the list the server block silently loses its routing and its forwarded headers.
envsubst '${FINERACT_UPSTREAM} ${FINERACT_PROXY_SSL_VERIFY} ${FINERACT_FORWARDED_PROTO}' < "$TEMPLATE" > "$RENDERED"
echo "entrypoint: proxying /api/ -> ${FINERACT_UPSTREAM} (proxy_ssl_verify ${FINERACT_PROXY_SSL_VERIFY}, X-Forwarded-Proto ${FINERACT_FORWARDED_PROTO})"
# ---------------------------------------------------------------------------------------------
# The application's runtime configuration
# ---------------------------------------------------------------------------------------------
#
# fineractApiUrl is same-origin on purpose and is not taken from the environment: pointing it
# elsewhere would need both a CSP change and an allowedApiOrigins entry, which is a deployment
# decision to make by editing this file's output deliberately, not one to fall into by setting a
# variable.
FINERACT_UI_TENANT="${DEFAULT_TENANT:-default}"
FINERACT_UI_RBAC="${RBAC_ENABLED:-true}"
FINERACT_UI_INSTITUTION="${INSTITUTION_TYPE:-universal}"
FINERACT_UI_DEVTOOLS="${DEVELOPER_TOOLS_ENABLED:-false}"
# Rejected rather than coerced. `RBAC_ENABLED=0` or `=no` would otherwise land in the JSON as a
# string, which is truthy, and a deployment that believed it had turned RBAC off would have it on.
# Failing to start is the only outcome that cannot be misread.
for pair in "RBAC_ENABLED:$FINERACT_UI_RBAC" "DEVELOPER_TOOLS_ENABLED:$FINERACT_UI_DEVTOOLS"; do
name="${pair%%:*}"
value="${pair#*:}"
if [ "$value" != "true" ] && [ "$value" != "false" ]; then
echo "entrypoint: $name must be exactly 'true' or 'false', got '$value'." >&2
exit 1
fi
done
cat > "$HTML_ROOT/config.json" <<EOF
{
"fineractApiUrl": "/api/v1",
"defaultTenant": "$FINERACT_UI_TENANT",
"rbacEnabled": $FINERACT_UI_RBAC,
"institutionType": "$FINERACT_UI_INSTITUTION",
"developerToolsEnabled": $FINERACT_UI_DEVTOOLS
}
EOF
echo "entrypoint: tenant ${FINERACT_UI_TENANT}, rbacEnabled ${FINERACT_UI_RBAC}, developerTools ${FINERACT_UI_DEVTOOLS}"
exec "$@"