mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-22 20:00:22 +00:00
70d879275a
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
Signed-off-by: dusan <borovcanindusan1@gmail.com>
272 lines
7.7 KiB
Bash
Executable File
272 lines
7.7 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# Copyright (c) Abstract Machines
|
||
# SPDX-License-Identifier: Apache-2.0
|
||
|
||
set -eu
|
||
|
||
ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||
ENV_FILE="$ROOT_DIR/docker/.env"
|
||
COMPOSE_FILE="$ROOT_DIR/docker/docker-compose.yaml"
|
||
|
||
HOST=${MG_PUBLIC_HOST:-}
|
||
EMAIL=${MG_LETSENCRYPT_EMAIL:-}
|
||
LETSENCRYPT_ENABLED=${MG_LETSENCRYPT_ENABLED:-true}
|
||
STAGING=${MG_LETSENCRYPT_STAGING:-false}
|
||
FORCE_RENEWAL=${MG_LETSENCRYPT_FORCE_RENEWAL:-false}
|
||
PROJECT=${DOCKER_PROJECT:-magistrala}
|
||
TIMEOUT_SECONDS=${MG_LETSENCRYPT_TIMEOUT_SECONDS:-180}
|
||
|
||
usage() {
|
||
cat <<EOF
|
||
Usage:
|
||
MG_PUBLIC_HOST=example.com MG_LETSENCRYPT_EMAIL=admin@example.com $0
|
||
MG_PUBLIC_HOST=example.com MG_LETSENCRYPT_EMAIL=admin@example.com MG_LETSENCRYPT_STAGING=true $0
|
||
MG_PUBLIC_HOST=example.com MG_LETSENCRYPT_ENABLED=false $0
|
||
|
||
Required:
|
||
MG_PUBLIC_HOST Public DNS name that points to this Docker host.
|
||
MG_LETSENCRYPT_EMAIL Email address for Let's Encrypt notices when
|
||
MG_LETSENCRYPT_ENABLED=true.
|
||
|
||
Optional:
|
||
MG_LETSENCRYPT_ENABLED true by default. Set false to use the fallback
|
||
Nginx certificate and comment out Let's Encrypt
|
||
cert/key paths in docker/.env.
|
||
MG_LETSENCRYPT_STAGING false by default (production certs). Set true for
|
||
Let's Encrypt staging (testing only).
|
||
MG_LETSENCRYPT_FORCE_RENEWAL
|
||
false by default. Set true to replace an existing cert.
|
||
DOCKER_PROJECT Compose project name. Defaults to magistrala.
|
||
MG_LETSENCRYPT_TIMEOUT_SECONDS
|
||
Wait time for certificate files. Defaults to 180.
|
||
EOF
|
||
}
|
||
|
||
if [ -z "$HOST" ]; then
|
||
usage >&2
|
||
exit 2
|
||
fi
|
||
|
||
case "$LETSENCRYPT_ENABLED" in
|
||
true|false)
|
||
;;
|
||
*)
|
||
echo "MG_LETSENCRYPT_ENABLED must be true or false." >&2
|
||
exit 2
|
||
;;
|
||
esac
|
||
|
||
if [ "$LETSENCRYPT_ENABLED" = "true" ] && [ -z "$EMAIL" ]; then
|
||
usage >&2
|
||
exit 2
|
||
fi
|
||
|
||
if [ "$LETSENCRYPT_ENABLED" = "true" ] && [ "$HOST" = "localhost" ]; then
|
||
echo "MG_PUBLIC_HOST must be a public DNS name, not localhost." >&2
|
||
exit 2
|
||
fi
|
||
|
||
if [ ! -f "$ENV_FILE" ]; then
|
||
echo "Missing $ENV_FILE" >&2
|
||
exit 1
|
||
fi
|
||
|
||
set_env() {
|
||
key=$1
|
||
value=$2
|
||
tmp=$(mktemp)
|
||
awk -v key="$key" -v value="$value" '
|
||
BEGIN { done = 0 }
|
||
!done && (index($0, key "=") == 1 || index($0, "#" key "=") == 1) {
|
||
print key "=" value
|
||
done = 1
|
||
next
|
||
}
|
||
{ print }
|
||
END {
|
||
if (!done) {
|
||
print key "=" value
|
||
}
|
||
}
|
||
' "$ENV_FILE" > "$tmp"
|
||
mv "$tmp" "$ENV_FILE"
|
||
}
|
||
|
||
comment_env() {
|
||
key=$1
|
||
value=$2
|
||
tmp=$(mktemp)
|
||
awk -v key="$key" -v value="$value" '
|
||
BEGIN { done = 0 }
|
||
!done && (index($0, key "=") == 1 || index($0, "# " key "=") == 1 || index($0, "#" key "=") == 1) {
|
||
print "# " key "=" value
|
||
done = 1
|
||
next
|
||
}
|
||
{ print }
|
||
END {
|
||
if (!done) {
|
||
print "# " key "=" value
|
||
}
|
||
}
|
||
' "$ENV_FILE" > "$tmp"
|
||
mv "$tmp" "$ENV_FILE"
|
||
}
|
||
|
||
comment_env_any() {
|
||
key=$1
|
||
tmp=$(mktemp)
|
||
awk -v key="$key" '
|
||
index($0, key "=") == 1 { print "# " $0; next }
|
||
{ print }
|
||
' "$ENV_FILE" > "$tmp"
|
||
mv "$tmp" "$ENV_FILE"
|
||
}
|
||
|
||
compose() {
|
||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT" "$@"
|
||
}
|
||
|
||
write_ui_proxy() {
|
||
ui_host=${MG_UI_HOST:-ui}
|
||
cat > "$ROOT_DIR/docker/nginx/snippets/ui-proxy.conf" <<NGINX
|
||
# Copyright (c) Abstract Machines
|
||
# SPDX-License-Identifier: Apache-2.0
|
||
# Written by docker/setup-tls.sh – do not edit manually.
|
||
|
||
location / {
|
||
if (\$scheme = http) {
|
||
return 301 https://\$host\$request_uri;
|
||
}
|
||
include snippets/proxy-headers.conf;
|
||
include snippets/ws-upgrade.conf;
|
||
proxy_pass http://${ui_host}:3000;
|
||
}
|
||
NGINX
|
||
}
|
||
|
||
wait_for_nginx_http() {
|
||
if ! command -v curl >/dev/null 2>&1; then
|
||
return 0
|
||
fi
|
||
|
||
elapsed=0
|
||
while [ "$elapsed" -lt "$TIMEOUT_SECONDS" ]; do
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 2 \
|
||
http://127.0.0.1/.well-known/acme-challenge/magistrala-tls-probe 2>/dev/null || true)
|
||
case "$status" in
|
||
200|301|302|307|308|404)
|
||
return 0
|
||
;;
|
||
esac
|
||
sleep 2
|
||
elapsed=$((elapsed + 2))
|
||
done
|
||
|
||
echo "Timed out waiting for Nginx to accept HTTP traffic." >&2
|
||
compose logs --tail 80 nginx >&2 || true
|
||
exit 1
|
||
}
|
||
|
||
cert_path="./ssl/letsencrypt/live/$HOST/fullchain.pem"
|
||
key_path="./ssl/letsencrypt/live/$HOST/privkey.pem"
|
||
cert_file="$ROOT_DIR/docker/ssl/letsencrypt/live/$HOST/fullchain.pem"
|
||
key_file="$ROOT_DIR/docker/ssl/letsencrypt/live/$HOST/privkey.pem"
|
||
|
||
if [ "$LETSENCRYPT_ENABLED" = "false" ]; then
|
||
FORCE_RENEWAL=false
|
||
fi
|
||
|
||
if [ "$LETSENCRYPT_ENABLED" = "true" ] && [ "$STAGING" = "false" ]; then
|
||
live_dir="$ROOT_DIR/docker/ssl/letsencrypt/live/$HOST"
|
||
if [ -d "$live_dir" ]; then
|
||
needs_cleanup=false
|
||
if openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | grep -q "STAGING"; then
|
||
echo "Existing staging certificate detected; replacing with a production certificate."
|
||
needs_cleanup=true
|
||
elif [ ! -L "$cert_file" ] || [ ! -L "$key_file" ]; then
|
||
echo "Broken certificate symlinks detected; removing stale data for a fresh issuance."
|
||
needs_cleanup=true
|
||
fi
|
||
if [ "$needs_cleanup" = "true" ]; then
|
||
FORCE_RENEWAL=true
|
||
rm -rf "$live_dir" \
|
||
"$ROOT_DIR/docker/ssl/letsencrypt/archive/$HOST" \
|
||
"$ROOT_DIR/docker/ssl/letsencrypt/renewal/$HOST.conf"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo "Configuring docker/.env for $HOST"
|
||
set_env MG_RELEASE_TAG latest
|
||
set_env MG_PUBLIC_HOST "$HOST"
|
||
set_env MG_UI_HOST "${MG_UI_HOST:-ui}"
|
||
set_env MG_LETSENCRYPT_ENABLED "$LETSENCRYPT_ENABLED"
|
||
set_env MG_LETSENCRYPT_EMAIL "$EMAIL"
|
||
set_env MG_LETSENCRYPT_STAGING "$STAGING"
|
||
set_env MG_LETSENCRYPT_FORCE_RENEWAL "$FORCE_RENEWAL"
|
||
set_env MG_NGINX_SERVER_NAME "$HOST"
|
||
comment_env_any MG_NGINX_SERVER_CERT
|
||
comment_env_any MG_NGINX_SERVER_KEY
|
||
set_env MG_UI_DOCKER_ACCEPT_EULA yes
|
||
|
||
set_env MG_OAUTH_UI_REDIRECT_URL "https://$HOST/api/auth/token"
|
||
set_env MG_OAUTH_UI_ERROR_URL "https://$HOST/login"
|
||
set_env MG_PASSWORD_RESET_URL_PREFIX "https://$HOST/password-reset"
|
||
set_env MG_VERIFICATION_URL_PREFIX "https://$HOST/verify-email"
|
||
set_env MG_GOOGLE_REDIRECT_URL "https://$HOST/oauth/callback/google"
|
||
set_env NEXTAUTH_URL "https://$HOST"
|
||
set_env MG_HOST_URL "https://$HOST"
|
||
set_env MG_UI_BASEURL "https://$HOST"
|
||
set_env MG_UI_CLI_MQTT_HOST "$HOST"
|
||
set_env MG_UI_CLI_WS_URL "wss://$HOST/mqtt"
|
||
set_env MG_UI_CLI_COAP_HOST "$HOST"
|
||
set_env MG_UI_CLI_HTTP_URL "https://$HOST/http"
|
||
|
||
mkdir -p "$ROOT_DIR/docker/ssl/letsencrypt" "$ROOT_DIR/docker/ssl/certbot-www"
|
||
|
||
write_ui_proxy
|
||
|
||
if [ "$LETSENCRYPT_ENABLED" = "false" ]; then
|
||
echo "Starting Magistrala with the fallback Nginx certificate"
|
||
MG_UI_DOCKER_ACCEPT_EULA=yes compose up -d
|
||
MG_UI_DOCKER_ACCEPT_EULA=yes COMPOSE_PROFILES=letsencrypt compose stop certbot >/dev/null 2>&1 || true
|
||
echo "Let's Encrypt disabled. Nginx cert/key paths are commented in docker/.env."
|
||
echo "Fallback TLS setup complete: https://$HOST/"
|
||
exit 0
|
||
fi
|
||
|
||
echo "Starting Magistrala with the fallback Nginx certificate"
|
||
MG_UI_DOCKER_ACCEPT_EULA=yes compose up -d
|
||
wait_for_nginx_http
|
||
|
||
echo "Requesting Let's Encrypt certificate for $HOST"
|
||
MG_UI_DOCKER_ACCEPT_EULA=yes COMPOSE_PROFILES=letsencrypt compose up -d --force-recreate certbot
|
||
|
||
cert_ready() {
|
||
compose logs certbot 2>&1 | \
|
||
grep -qE "Successfully received certificate|Certificate not yet due for renewal"
|
||
}
|
||
|
||
elapsed=0
|
||
until cert_ready || [ "$elapsed" -ge "$TIMEOUT_SECONDS" ]; do
|
||
compose logs --tail 3 certbot 2>&1 | sed 's/^/ [certbot] /'
|
||
sleep 5
|
||
elapsed=$((elapsed + 5))
|
||
done
|
||
|
||
if ! cert_ready; then
|
||
echo "Timed out waiting for Let's Encrypt certificate." >&2
|
||
compose logs --tail 80 certbot >&2 || true
|
||
exit 1
|
||
fi
|
||
|
||
echo "Certificate obtained. Switching Nginx to the issued certificate."
|
||
set_env MG_NGINX_SERVER_CERT "$cert_path"
|
||
set_env MG_NGINX_SERVER_KEY "$key_path"
|
||
set_env MG_LETSENCRYPT_FORCE_RENEWAL false
|
||
|
||
MG_UI_DOCKER_ACCEPT_EULA=yes compose up -d --force-recreate nginx
|
||
|
||
echo "TLS setup complete: https://$HOST/"
|