Prepare QSfera production release
This commit is contained in:
@@ -15,4 +15,6 @@ This deployment example is documented in two locations for different audiences:
|
||||
Providing details which are more developer focused. This description can also be used when deviating from the default.\
|
||||
Note that this examples uses self signed certificates and is intended for testing purposes.
|
||||
|
||||
|
||||
For production rollouts from this compose directory, use `env.production.example` as the starting point for `.env`.
|
||||
It keeps TLS validation enabled, disables demo users, and requires explicit admin, Keycloak, and S3 secrets instead of demo defaults.
|
||||
Run `./validate-production-env.sh .env` before `docker compose config` and rollout.
|
||||
|
||||
@@ -9,6 +9,6 @@ services:
|
||||
# decomposeds3 specific settings
|
||||
STORAGE_USERS_DECOMPOSEDS3_ENDPOINT: ${DECOMPOSEDS3_ENDPOINT:-http://minio:9000}
|
||||
STORAGE_USERS_DECOMPOSEDS3_REGION: ${DECOMPOSEDS3_REGION:-default}
|
||||
STORAGE_USERS_DECOMPOSEDS3_ACCESS_KEY: ${DECOMPOSEDS3_ACCESS_KEY:-qsfera}
|
||||
STORAGE_USERS_DECOMPOSEDS3_SECRET_KEY: ${DECOMPOSEDS3_SECRET_KEY:-qsfera-secret-key}
|
||||
STORAGE_USERS_DECOMPOSEDS3_BUCKET: ${DECOMPOSEDS3_BUCKET:-qsfera-bucket}
|
||||
STORAGE_USERS_DECOMPOSEDS3_ACCESS_KEY: ${DECOMPOSEDS3_ACCESS_KEY:?Set DECOMPOSEDS3_ACCESS_KEY before starting decomposeds3 in production}
|
||||
STORAGE_USERS_DECOMPOSEDS3_SECRET_KEY: ${DECOMPOSEDS3_SECRET_KEY:?Set DECOMPOSEDS3_SECRET_KEY before starting decomposeds3 in production}
|
||||
STORAGE_USERS_DECOMPOSEDS3_BUCKET: ${DECOMPOSEDS3_BUCKET:?Set DECOMPOSEDS3_BUCKET before starting decomposeds3 in production}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
## Production-safe qsfera_full environment template.
|
||||
## Copy to .env and fill every placeholder before running docker compose.
|
||||
|
||||
COMPOSE_PATH_SEPARATOR=:
|
||||
QSFERA=:qsfera.yml
|
||||
|
||||
## Public deployment identity.
|
||||
OC_DOMAIN=<cloud.example.com>
|
||||
TRAEFIK_ACME_MAIL=<admin@example.com>
|
||||
|
||||
## Use the production image and pin a release tag for repeatable rollouts.
|
||||
OC_DOCKER_IMAGE=qsfera/qsfera
|
||||
OC_DOCKER_TAG=<release-tag>
|
||||
|
||||
## Production must validate TLS certificates and must not create demo users.
|
||||
INSECURE=false
|
||||
DEMO_USERS=false
|
||||
|
||||
## Required by qsfera.yml.
|
||||
ADMIN_PASSWORD=<replace-with-strong-password>
|
||||
|
||||
## Optional: enable Keycloak-backed production identity.
|
||||
## Uncomment KEYCLOAK and fill all values before enabling it.
|
||||
#KEYCLOAK=:keycloak.yml
|
||||
KEYCLOAK_DOMAIN=<keycloak.example.com>
|
||||
KEYCLOAK_REALM=qsfera
|
||||
KEYCLOAK_POSTGRES_PASSWORD=<replace-with-strong-password>
|
||||
KEYCLOAK_ADMIN_USER=<replace-with-admin-user>
|
||||
KEYCLOAK_ADMIN_PASSWORD=<replace-with-strong-password>
|
||||
|
||||
## Optional: enable S3-backed user storage.
|
||||
## Uncomment DECOMPOSEDS3 and fill all values before enabling it.
|
||||
#DECOMPOSEDS3=:decomposeds3.yml
|
||||
DECOMPOSEDS3_ENDPOINT=<https://s3.example.com>
|
||||
DECOMPOSEDS3_REGION=<region>
|
||||
DECOMPOSEDS3_ACCESS_KEY=<replace-with-access-key>
|
||||
DECOMPOSEDS3_SECRET_KEY=<replace-with-secret-key>
|
||||
DECOMPOSEDS3_BUCKET=<bucket>
|
||||
|
||||
## Optional: local MinIO is intended for test/lab installs, not public production.
|
||||
#DECOMPOSEDS3_MINIO=:minio.yml
|
||||
#MINIO_DOMAIN=<minio.example.com>
|
||||
|
||||
## Optional: use host paths for backup-friendly config and data storage.
|
||||
#OC_CONFIG_DIR=/srv/qsfera/config
|
||||
#OC_DATA_DIR=/srv/qsfera/data
|
||||
|
||||
## Keep this last: it assembles the selected compose files.
|
||||
COMPOSE_FILE=docker-compose.yml${QSFERA:-}${DECOMPOSEDS3:-}${DECOMPOSEDS3_MINIO:-}${KEYCLOAK:-}
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: keycloak
|
||||
POSTGRES_PASSWORD: ${KEYCLOAK_POSTGRES_PASSWORD:?Set KEYCLOAK_POSTGRES_PASSWORD before starting Keycloak in production}
|
||||
logging:
|
||||
driver: ${LOG_DRIVER:-local}
|
||||
restart: always
|
||||
@@ -53,10 +53,10 @@ services:
|
||||
KC_DB: postgres
|
||||
KC_DB_URL: "jdbc:postgresql://postgres:5432/keycloak"
|
||||
KC_DB_USERNAME: keycloak
|
||||
KC_DB_PASSWORD: keycloak
|
||||
KC_DB_PASSWORD: ${KEYCLOAK_POSTGRES_PASSWORD:?Set KEYCLOAK_POSTGRES_PASSWORD before starting Keycloak in production}
|
||||
KC_FEATURES: impersonation
|
||||
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER:-admin}
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
|
||||
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER:?Set KEYCLOAK_ADMIN_USER before starting Keycloak in production}
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:?Set KEYCLOAK_ADMIN_PASSWORD before starting Keycloak in production}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.keycloak.entrypoints=https"
|
||||
|
||||
@@ -10,13 +10,13 @@ services:
|
||||
command:
|
||||
[
|
||||
"-c",
|
||||
"mkdir -p /data/${DECOMPOSEDS3_BUCKET:-qsfera-bucket} && minio server --console-address ':9001' /data",
|
||||
"mkdir -p /data/${DECOMPOSEDS3_BUCKET:?Set DECOMPOSEDS3_BUCKET before starting MinIO in production} && minio server --console-address ':9001' /data",
|
||||
]
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${DECOMPOSEDS3_ACCESS_KEY:-qsfera}
|
||||
MINIO_ROOT_PASSWORD: ${DECOMPOSEDS3_SECRET_KEY:-qsfera-secret-key}
|
||||
MINIO_ROOT_USER: ${DECOMPOSEDS3_ACCESS_KEY:?Set DECOMPOSEDS3_ACCESS_KEY before starting MinIO in production}
|
||||
MINIO_ROOT_PASSWORD: ${DECOMPOSEDS3_SECRET_KEY:?Set DECOMPOSEDS3_SECRET_KEY before starting MinIO in production}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.minio.entrypoints=https"
|
||||
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
# basic auth (not recommended, but needed for eg. WebDav clients that do not support OpenID Connect)
|
||||
PROXY_ENABLE_BASIC_AUTH: "${PROXY_ENABLE_BASIC_AUTH:-false}"
|
||||
# admin user password
|
||||
IDM_ADMIN_PASSWORD: "${ADMIN_PASSWORD:-admin}" # this overrides the admin password from the configuration file
|
||||
IDM_ADMIN_PASSWORD: "${ADMIN_PASSWORD:?Set ADMIN_PASSWORD before starting qsfera_full in production}" # this overrides the admin password from the configuration file
|
||||
# demo users
|
||||
IDM_CREATE_DEMO_USERS: "${DEMO_USERS:-false}"
|
||||
# idp login form settings
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
ENV_FILE="${1:-.env}"
|
||||
errors=0
|
||||
warnings=0
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "ERROR: env file not found: $ENV_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
value() {
|
||||
key="$1"
|
||||
awk -v key="$key" '
|
||||
/^[[:space:]]*#/ || /^[[:space:]]*$/ { next }
|
||||
{
|
||||
line = $0
|
||||
sub(/\r$/, "", line)
|
||||
split(line, parts, "=")
|
||||
current = parts[1]
|
||||
gsub(/^[ \t]+|[ \t]+$/, "", current)
|
||||
if (current == key) {
|
||||
sub(/^[^=]*=/, "", line)
|
||||
gsub(/^"/, "", line)
|
||||
gsub(/"$/, "", line)
|
||||
gsub(/^'\''/, "", line)
|
||||
gsub(/'\''$/, "", line)
|
||||
print line
|
||||
exit
|
||||
}
|
||||
}
|
||||
' "$ENV_FILE"
|
||||
}
|
||||
|
||||
is_placeholder() {
|
||||
case "${1:-}" in
|
||||
""|"<"*">"|*example.com*|"change-me"|"changeme"|"password"|"secret")
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
error() {
|
||||
errors=$((errors + 1))
|
||||
echo "ERROR: $1" >&2
|
||||
}
|
||||
|
||||
warn() {
|
||||
warnings=$((warnings + 1))
|
||||
echo "WARN: $1" >&2
|
||||
}
|
||||
|
||||
require_value() {
|
||||
key="$1"
|
||||
label="${2:-$1}"
|
||||
current="$(value "$key")"
|
||||
if is_placeholder "$current"; then
|
||||
error "$label must be set to a production value."
|
||||
fi
|
||||
}
|
||||
|
||||
require_secret() {
|
||||
key="$1"
|
||||
current="$(value "$key")"
|
||||
if is_placeholder "$current"; then
|
||||
error "$key must be set."
|
||||
return
|
||||
fi
|
||||
case "$current" in
|
||||
admin|demo|keycloak|qsfera|qsfera-secret-key)
|
||||
error "$key uses a known demo/default value."
|
||||
return
|
||||
;;
|
||||
esac
|
||||
if [ "${#current}" -lt 16 ]; then
|
||||
error "$key must be at least 16 characters."
|
||||
fi
|
||||
}
|
||||
|
||||
enabled() {
|
||||
current="$(value "$1")"
|
||||
[ -n "$current" ]
|
||||
}
|
||||
|
||||
require_value OC_DOMAIN "OC_DOMAIN"
|
||||
require_value TRAEFIK_ACME_MAIL "TRAEFIK_ACME_MAIL"
|
||||
require_secret ADMIN_PASSWORD
|
||||
|
||||
case "$(value INSECURE)" in
|
||||
true|TRUE|1|yes|YES)
|
||||
error "INSECURE must be false for production."
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$(value DEMO_USERS)" in
|
||||
true|TRUE|1|yes|YES)
|
||||
error "DEMO_USERS must be false for production."
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$(value OC_DOCKER_IMAGE)" in
|
||||
qsfera/qsfera)
|
||||
;;
|
||||
"")
|
||||
warn "OC_DOCKER_IMAGE is empty; compose default must be verified before rollout."
|
||||
;;
|
||||
*)
|
||||
error "OC_DOCKER_IMAGE must be qsfera/qsfera for production."
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$(value OC_DOCKER_TAG)" in
|
||||
""|latest|"<"*">")
|
||||
error "OC_DOCKER_TAG must pin a concrete production release tag."
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! enabled QSFERA; then
|
||||
error "QSFERA=:qsfera.yml must be enabled."
|
||||
fi
|
||||
|
||||
if enabled DECOMPOSEDS3; then
|
||||
require_value DECOMPOSEDS3_ENDPOINT "DECOMPOSEDS3_ENDPOINT"
|
||||
require_value DECOMPOSEDS3_REGION "DECOMPOSEDS3_REGION"
|
||||
require_secret DECOMPOSEDS3_ACCESS_KEY
|
||||
require_secret DECOMPOSEDS3_SECRET_KEY
|
||||
require_value DECOMPOSEDS3_BUCKET "DECOMPOSEDS3_BUCKET"
|
||||
fi
|
||||
|
||||
if enabled DECOMPOSEDS3_MINIO; then
|
||||
error "DECOMPOSEDS3_MINIO is for test/lab installs and must not be enabled for public production."
|
||||
fi
|
||||
|
||||
if enabled KEYCLOAK; then
|
||||
require_value KEYCLOAK_DOMAIN "KEYCLOAK_DOMAIN"
|
||||
require_value KEYCLOAK_REALM "KEYCLOAK_REALM"
|
||||
require_secret KEYCLOAK_POSTGRES_PASSWORD
|
||||
require_value KEYCLOAK_ADMIN_USER "KEYCLOAK_ADMIN_USER"
|
||||
require_secret KEYCLOAK_ADMIN_PASSWORD
|
||||
fi
|
||||
|
||||
if [ -z "$(value OC_CONFIG_DIR)" ] || [ -z "$(value OC_DATA_DIR)" ]; then
|
||||
warn "OC_CONFIG_DIR and OC_DATA_DIR are not both set; docker volumes are harder to back up and restore."
|
||||
fi
|
||||
|
||||
if [ "$errors" -gt 0 ]; then
|
||||
echo "Production env validation failed: $errors error(s), $warnings warning(s)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Production env validation passed: $warnings warning(s)."
|
||||
Reference in New Issue
Block a user