2022-02-16 09:59:53 +01:00
#!/bin/bash -e
2022-10-28 10:12:21 +02:00
detectCompose( ) {
if [ [ " $( docker compose version 2>/dev/null) " = = *"Docker Compose version" * ] ] ; then
COMPOSE = "docker compose"
2023-10-10 10:43:22 +02:00
else
2022-10-28 10:12:21 +02:00
COMPOSE = "docker-compose"
# This is intended to fail on startup in the next prereq check.
fi
}
2023-10-05 09:43:57 +02:00
setupProxy( ) {
2023-10-24 09:01:22 +02:00
### Note: As the current data protection concepts do not allow communication via HTTP,
### we are not setting a proxy for HTTP requests.
2023-10-20 15:59:24 +02:00
2023-10-24 09:01:22 +02:00
local http = "no"
local https = "no"
2023-10-20 15:59:24 +02:00
if [ $HTTPS_PROXY_URL ] ; then
2023-10-24 09:23:24 +02:00
local proto = " $( echo $HTTPS_PROXY_URL | grep :// | sed -e 's,^\(.*://\).*,\1,g' ) "
local fqdn = " $( echo ${ HTTPS_PROXY_URL / $proto / } ) "
2023-10-24 09:12:18 +02:00
local hostport = $( echo $HTTPS_PROXY_URL | sed -e " s, $proto ,,g " | cut -d/ -f1)
2023-10-24 09:07:06 +02:00
HTTPS_PROXY_HOST = " $( echo $hostport | sed -e 's,:.*,,g' ) "
HTTPS_PROXY_PORT = " $( echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g' ) "
2023-10-20 15:59:24 +02:00
if [ [ ! -z " $HTTPS_PROXY_USERNAME " && ! -z " $HTTPS_PROXY_PASSWORD " ] ] ; then
2023-10-24 09:12:18 +02:00
local proto = " $( echo $HTTPS_PROXY_URL | grep :// | sed -e 's,^\(.*://\).*,\1,g' ) "
2023-10-24 09:01:22 +02:00
local fqdn = " $( echo ${ HTTPS_PROXY_URL / $proto / } ) "
2023-10-20 15:59:24 +02:00
HTTPS_PROXY_FULL_URL = " $( echo $proto $HTTPS_PROXY_USERNAME :$HTTPS_PROXY_PASSWORD @$fqdn ) "
https = "authenticated"
else
HTTPS_PROXY_FULL_URL = $HTTPS_PROXY_URL
https = "unauthenticated"
fi
2023-10-05 09:43:57 +02:00
fi
2023-10-20 15:59:24 +02:00
2023-10-24 09:01:22 +02:00
log INFO " Configuring proxy servers: $http http proxy (we're not supporting unencrypted comms), $https https proxy "
2023-10-24 09:23:24 +02:00
export HTTPS_PROXY_HOST HTTPS_PROXY_PORT HTTPS_PROXY_FULL_URL
2023-10-05 09:43:57 +02:00
}
2022-02-16 09:59:53 +01:00
exitIfNotRoot( ) {
if [ " $EUID " -ne 0 ] ; then
2022-05-17 15:55:25 +02:00
log "ERROR" "Please run as root"
2022-10-06 10:45:50 +02:00
fail_and_report 1 "Please run as root"
2022-02-16 09:59:53 +01:00
fi
}
2022-05-31 13:55:40 +02:00
checkOwner( ) {
COUNT = $( find $1 ! -user $2 | wc -l)
if [ $COUNT -gt 0 ] ; then
log ERROR " $COUNT files in $1 are not owned by user $2 . Run find $1 ! -user $2 to see them, chown -R $2 $1 to correct this issue. "
return 1
fi
return 0
}
2022-05-09 15:13:38 +02:00
printUsage( ) {
2024-06-05 14:35:44 +02:00
echo "Usage: bridgehead start|stop|logs|docker-logs|is-running|update|install|uninstall|adduser|enroll PROJECTNAME"
2024-09-12 10:04:53 +02:00
echo "PROJECTNAME should be one of ccp|bbmri|cce|itcc|kr|dhki"
2022-05-09 15:13:38 +02:00
}
checkRequirements( ) {
2022-05-17 18:04:15 +02:00
if ! lib/prerequisites.sh $@ ; then
2022-05-17 15:55:25 +02:00
log "ERROR" "Validating Prerequisites failed, please fix the error(s) above this line."
2022-10-06 10:45:50 +02:00
fail_and_report 1 "Validating prerequisites failed."
2022-05-09 15:13:38 +02:00
else
return 0
fi
}
2022-05-12 18:23:52 +02:00
fetchVarsFromVault( ) {
2022-05-13 14:11:14 +02:00
[ -e /etc/bridgehead/vault.conf ] && source /etc/bridgehead/vault.conf
if [ -z " $BW_MASTERPASS " ] || [ -z " $BW_CLIENTID " ] || [ -z " $BW_CLIENTSECRET " ] ; then
2022-05-17 15:55:25 +02:00
log "ERROR" "Please supply correct credentials in /etc/bridgehead/vault.conf."
2022-05-13 14:11:14 +02:00
return 1
fi
set +e
2023-09-27 09:22:11 +02:00
PASS = $( BW_MASTERPASS = " $BW_MASTERPASS " BW_CLIENTID = " $BW_CLIENTID " BW_CLIENTSECRET = " $BW_CLIENTSECRET " docker run --rm -e BW_MASTERPASS -e BW_CLIENTID -e BW_CLIENTSECRET -e http_proxy docker.verbis.dkfz.de/cache/samply/bridgehead-vaultfetcher:latest $@ )
2022-05-13 14:11:14 +02:00
RET = $?
if [ $RET -ne 0 ] ; then
echo " Code: $RET "
echo $PASS
return $RET
fi
eval $( echo -e " $PASS " | sed 's/\r//g' )
set -e
2022-05-12 18:23:52 +02:00
return 0
}
2022-05-16 09:21:42 +02:00
2022-05-31 09:22:38 +02:00
fetchVarsFromVaultByFile( ) {
VARS_TO_FETCH = ""
for line in $( cat $@ ) ; do
2022-05-31 13:40:25 +02:00
if [ [ $line = ~ .*= [ \" ] *\< VAULT\> [ \" ] *.* ] ] ; then
2022-05-31 09:22:38 +02:00
VARS_TO_FETCH += " $( echo -n $line | sed 's/=.*//' ) "
fi
done
if [ -z " $VARS_TO_FETCH " ] ; then
return 0
fi
log INFO " Fetching $( echo $VARS_TO_FETCH | wc -w) secrets from Vault ... "
fetchVarsFromVault $VARS_TO_FETCH
return 0
}
2022-05-31 13:56:12 +02:00
assertVarsNotEmpty( ) {
MISSING_VARS = ""
for VAR in $@ ; do
if [ -z " ${ !VAR } " ] ; then
MISSING_VARS += " $VAR "
fi
done
if [ -n " $MISSING_VARS " ] ; then
log "ERROR" " Mandatory variables not defined: $MISSING_VARS "
return 1
fi
return 0
}
2022-10-06 10:45:50 +02:00
fixPermissions( ) {
CHOWN = $( which chown)
sudo $CHOWN -R bridgehead /etc/bridgehead /srv/docker/bridgehead
}
source lib/monitoring.sh
2022-11-04 15:26:27 +01:00
report_error( ) {
2022-05-17 18:04:15 +02:00
CODE = $1
shift
log ERROR " $@ "
hc_send $CODE " $@ "
2022-11-04 15:26:27 +01:00
}
fail_and_report( ) {
report_error $@
2022-10-06 10:45:50 +02:00
exit $1
}
2022-11-04 13:09:11 +01:00
setHostname( ) {
if [ -z " $HOST " ] ; then
2022-12-07 15:46:19 +01:00
export HOST = $( hostname -f | tr "[:upper:]" "[:lower:]" )
2022-11-04 13:09:11 +01:00
log DEBUG " Using auto-detected hostname $HOST . "
fi
}
2024-03-11 09:58:30 +01:00
# This function optimizes the usage of memory through blaze, according to the official performance tuning guide:
# https://github.com/samply/blaze/blob/master/docs/tuning-guide.md
# Short summary of the adjustments made:
# - set blaze memory cap to a quarter of the system memory
# - set db block cache size to a quarter of the system memory
# - limit resource count allowed in blaze to 1,25M per 4GB available system memory
optimizeBlazeMemoryUsage( ) {
2024-02-20 15:15:49 +01:00
if [ -z " $BLAZE_MEMORY_CAP " ] ; then
2024-03-15 11:48:25 +01:00
system_memory_in_mb = $( LC_ALL = C free -m | grep 'Mem:' | awk '{print $2}' ) ;
2024-04-02 14:36:23 +02:00
export BLAZE_MEMORY_CAP = $(( $system_memory_in_mb / 4 )) ;
2024-02-20 15:15:49 +01:00
fi
2024-03-11 09:58:30 +01:00
if [ -z " $BLAZE_RESOURCE_CACHE_CAP " ] ; then
2024-04-15 09:08:56 +02:00
available_system_memory_chunks = $(( BLAZE_MEMORY_CAP / 1000 ))
if [ $available_system_memory_chunks -eq 0 ] ; then
2024-03-11 09:58:30 +01:00
log WARN " Only ${ BLAZE_MEMORY_CAP } system memory available for Blaze. If your Blaze stores more than 128000 fhir ressources it will run significally slower. "
export BLAZE_RESOURCE_CACHE_CAP = 128000;
2024-10-10 14:34:28 +02:00
export BLAZE_CQL_CACHE_CAP = 32;
2024-03-11 09:58:30 +01:00
else
2024-04-15 09:08:56 +02:00
export BLAZE_RESOURCE_CACHE_CAP = $(( available_system_memory_chunks * 312500 ))
2024-10-10 14:34:28 +02:00
export BLAZE_CQL_CACHE_CAP = $(( ( $system_memory_in_mb / 4 ) / 16 )) ;
2024-03-11 09:58:30 +01:00
fi
fi
2024-02-20 15:15:49 +01:00
}
2022-12-13 16:51:32 +01:00
# Takes 1) The Backup Directory Path 2) The name of the Service to be backuped
# Creates 3 Backups: 1) For the past seven days 2) For the current month and 3) for each calendar week
createEncryptedPostgresBackup( ) {
docker exec " $2 " bash -c 'pg_dump -U $POSTGRES_USER $POSTGRES_DB --format=p --no-owner --no-privileges' | \
# TODO: Encrypt using /etc/bridgehead/pki/${SITE_ID}.priv.pem | \
tee " $1 / $2 / $( date +Last-%A) .sql " | \
tee " $1 / $2 / $( date +%Y-%m) .sql " > \
" $1 / $2 / $( date +%Y-KW%V) .sql "
}
2022-11-29 08:36:05 +01:00
# from: https://gist.github.com/sj26/88e1c6584397bb7c13bd11108a579746
# ex. use: retry 5 /bin/false
function retry {
local retries = $1
shift
local count = 0
until " $@ " ; do
exit = $?
wait = $(( 2 * * $count ))
count = $(( $count + 1 ))
if [ $count -lt $retries ] ; then
echo " Retry $count / $retries exited with code $exit , retrying in $wait seconds... "
sleep $wait
else
echo " Retry $count / $retries exited with code $exit , giving up. "
return $exit
fi
done
return 0
}
2023-03-08 10:00:38 +01:00
function bk_is_running {
2023-03-08 10:37:37 +01:00
detectCompose
2023-05-10 12:54:05 +02:00
RUNNING = " $( $COMPOSE -p $PROJECT -f minimal/docker-compose.yml -f ./$PROJECT /docker-compose.yml $OVERRIDE ps -q) "
2023-03-08 10:00:38 +01:00
NUMBEROFRUNNING = $( echo " $RUNNING " | wc -l)
2023-03-08 10:37:37 +01:00
if [ $NUMBEROFRUNNING -ge 2 ] ; then
2023-03-08 10:00:38 +01:00
return 0
else
return 1
fi
}
2023-08-07 13:00:37 +02:00
function do_enroll_inner {
PARAMS = ""
MANUAL_PROXY_ID = " ${ 1 :- $PROXY_ID } "
if [ -z " $MANUAL_PROXY_ID " ] ; then
log ERROR "No Proxy ID set"
exit 1
else
log INFO " Enrolling Beam Proxy Id $MANUAL_PROXY_ID "
fi
SUPPORT_EMAIL = " ${ 2 :- $SUPPORT_EMAIL } "
if [ -n " $SUPPORT_EMAIL " ] ; then
PARAMS += " --admin-email $SUPPORT_EMAIL "
fi
2023-07-27 15:38:29 +02:00
2023-09-27 09:22:11 +02:00
docker run --rm -v /etc/bridgehead/pki:/etc/bridgehead/pki docker.verbis.dkfz.de/cache/samply/beam-enroll:latest --output-file $PRIVATEKEYFILENAME --proxy-id $MANUAL_PROXY_ID $PARAMS
2023-08-07 13:00:37 +02:00
chmod 600 $PRIVATEKEYFILENAME
}
function do_enroll {
do_enroll_inner $@
2023-08-15 14:24:19 +02:00
}
2023-07-27 15:38:29 +02:00
add_basic_auth_user( ) {
2023-08-15 14:24:19 +02:00
USER = " ${ 1 } "
PASSWORD = " ${ 2 } "
NAME = " ${ 3 } "
PROJECT = " ${ 4 } "
FILE = " /etc/bridgehead/ ${ PROJECT } .local.conf "
ENCRY_CREDENTIALS = " $( docker run --rm docker.verbis.dkfz.de/cache/httpd:alpine htpasswd -nb $USER $PASSWORD | tr -d '\n' | tr -d '\r' ) "
if [ -f $FILE ] && grep -R -q " $NAME = " $FILE # if a specific basic auth user already exists:
then
sed -i " / $NAME / s|='|=' $ENCRY_CREDENTIALS ,| " $FILE
else
echo -e " \n## Basic Authentication Credentials for:\n $NAME =' $ENCRY_CREDENTIALS ' " >> $FILE ;
fi
log DEBUG " Saving clear text credentials in $FILE . If wanted, delete them manually. "
sed -i " /^ $NAME / s| $|\n# User: $USER \n# Password: $PASSWORD | " $FILE
2023-08-17 13:21:20 +02:00
}
2023-11-07 15:55:26 +01:00
2023-11-30 14:46:08 +01:00
OIDC_PUBLIC_REDIRECT_URLS = ${ OIDC_PUBLIC_REDIRECT_URLS :- "" }
OIDC_PRIVATE_REDIRECT_URLS = ${ OIDC_PRIVATE_REDIRECT_URLS :- "" }
# Add a redirect url to the public oidc client of the bridgehead
function add_public_oidc_redirect_url( ) {
if [ [ $OIDC_PUBLIC_REDIRECT_URLS = = "" ] ] ; then
OIDC_PUBLIC_REDIRECT_URLS += " $( generate_redirect_urls $1 ) "
2023-11-07 15:55:26 +01:00
else
2023-11-30 14:46:08 +01:00
OIDC_PUBLIC_REDIRECT_URLS += " , $( generate_redirect_urls $1 ) "
2023-11-13 17:22:23 +01:00
fi
}
2023-11-30 14:46:08 +01:00
# Add a redirect url to the private oidc client of the bridgehead
function add_private_oidc_redirect_url( ) {
if [ [ $OIDC_PRIVATE_REDIRECT_URLS = = "" ] ] ; then
OIDC_PRIVATE_REDIRECT_URLS += " $( generate_redirect_urls $1 ) "
2023-11-13 17:22:23 +01:00
else
2023-11-30 14:46:08 +01:00
OIDC_PRIVATE_REDIRECT_URLS += " , $( generate_redirect_urls $1 ) "
2023-11-07 15:55:26 +01:00
fi
}
function sync_secrets( ) {
2023-11-30 14:46:08 +01:00
local delimiter = $'\x1E'
local secret_sync_args = ""
if [ [ $OIDC_PRIVATE_REDIRECT_URLS != "" ] ] ; then
secret_sync_args = " OIDC:OIDC_CLIENT_SECRET:private; $OIDC_PRIVATE_REDIRECT_URLS "
fi
2024-04-15 15:53:27 +02:00
if [ [ $OIDC_PUBLIC_REDIRECT_URLS != "" ] ] ; then
2023-11-30 14:46:08 +01:00
if [ [ $secret_sync_args = = "" ] ] ; then
secret_sync_args = " OIDC:OIDC_PUBLIC:public; $OIDC_PUBLIC_REDIRECT_URLS "
2023-11-17 10:27:12 +01:00
else
2023-11-30 15:05:07 +01:00
secret_sync_args += " ${ delimiter } OIDC:OIDC_PUBLIC:public; $OIDC_PUBLIC_REDIRECT_URLS "
2023-11-30 14:46:08 +01:00
fi
fi
if [ [ $secret_sync_args = = "" ] ] ; then
2023-11-07 15:55:26 +01:00
return
fi
2024-03-14 15:09:21 +01:00
mkdir -p /var/cache/bridgehead/secrets/ || fail_and_report 1 " Failed to create '/var/cache/bridgehead/secrets/'. Please run sudo './bridgehead install $PROJECT ' again. "
2023-11-16 14:24:41 +01:00
touch /var/cache/bridgehead/secrets/oidc
2023-11-07 15:55:26 +01:00
docker run --rm \
2023-11-16 14:24:41 +01:00
-v /var/cache/bridgehead/secrets/oidc:/usr/local/cache \
2023-11-07 15:55:26 +01:00
-v $PRIVATEKEYFILENAME :/run/secrets/privkey.pem:ro \
2024-02-13 18:54:26 +01:00
-v /srv/docker/bridgehead/$PROJECT /root.crt.pem:/run/secrets/root.crt.pem:ro \
2023-11-07 15:55:26 +01:00
-v /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro \
-e TLS_CA_CERTIFICATES_DIR = /conf/trusted-ca-certs \
2024-02-06 17:18:23 +01:00
-e NO_PROXY = localhost,127.0.0.1 \
2024-02-08 14:38:37 +01:00
-e ALL_PROXY = $HTTPS_PROXY_FULL_URL \
2023-11-07 15:55:26 +01:00
-e PROXY_ID = $PROXY_ID \
-e BROKER_URL = $BROKER_URL \
2023-11-21 11:39:17 +01:00
-e OIDC_PROVIDER = secret-sync-central.oidc-client-enrollment.$BROKER_ID \
2023-11-30 14:46:08 +01:00
-e SECRET_DEFINITIONS = $secret_sync_args \
2023-11-07 15:55:26 +01:00
docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest
2024-02-08 14:38:37 +01:00
2023-11-23 14:33:28 +01:00
set -a # Export variables as environment variables
2023-11-07 15:55:26 +01:00
source /var/cache/bridgehead/secrets/*
2023-11-23 14:33:28 +01:00
set +a # Export variables in the regular way
2023-11-07 15:55:26 +01:00
}
2023-11-17 10:27:12 +01:00
capitalize_first_letter( ) {
input = " $1 "
capitalized = " $( tr '[:lower:]' '[:upper:]' <<< ${ input : 0 : 1 } ) ${ input : 1 } "
echo " $capitalized "
}
2023-11-23 10:38:50 +01:00
2023-11-23 11:28:43 +01:00
# Generate a string of ',' separated string of redirect urls relative to $HOST.
# $1 will be appended to the url
# If the host looks like dev-jan.inet.dkfz-heidelberg.de it will generate urls with dev-jan and the original $HOST as url Authorities
2023-11-30 14:46:08 +01:00
function generate_redirect_urls( ) {
2023-11-23 10:38:50 +01:00
local redirect_urls = " https:// ${ HOST } $1 "
local host_without_proxy = " $( echo " $HOST " | cut -d '.' -f1) "
2023-11-23 11:28:43 +01:00
# Only append second url if its different and the host is not an ip address
if [ [ " $HOST " != " $host_without_proxy " && ! " $HOST " = ~ ^[ 0-9] +\. [ 0-9] +\. [ 0-9] +\. [ 0-9] +$ ] ] ; then
redirect_urls += " ,https:// $host_without_proxy $1 "
2023-11-23 10:38:50 +01:00
fi
echo " $redirect_urls "
}
2023-11-23 15:54:44 +01:00
2023-12-22 11:41:07 +01:00
# This password contains at least one special char, a random number and a random upper and lower case letter
2023-11-23 15:54:44 +01:00
generate_password( ) {
local seed_text = " $1 "
2023-12-22 11:41:07 +01:00
local seed_num = $( awk 'BEGIN{FS=""} NR==1{print $10}' /etc/bridgehead/pki/${ SITE_ID } .priv.pem | od -An -tuC)
local nums = "1234567890"
local n = $( echo " $seed_num " | awk '{print $1 % 10}' )
local random_digit = ${ nums : $n : 1 }
local n = $( echo " $seed_num " | awk '{print $1 % 26}' )
local upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local lower = "abcdefghijklmnopqrstuvwxyz"
local random_upper = ${ upper : $n : 1 }
local random_lower = ${ lower : $n : 1 }
local n = $( echo " $seed_num " | awk '{print $1 % 8}' )
local special = '@#$%^&+='
local random_special = ${ special : $n : 1 }
2023-11-23 15:54:44 +01:00
local combined_text = " This is a salt string to generate one consistent password for ${ seed_text } . It is not required to be secret. "
2024-03-18 12:36:09 +01:00
local main_password = $( echo " ${ combined_text } " | sha1sum | openssl pkeyutl -sign -inkey " /etc/bridgehead/pki/ ${ SITE_ID } .priv.pem " 2> /dev/null | base64 | head -c 26 | sed 's/\//A/g' )
2023-11-23 15:54:44 +01:00
echo " ${ main_password } ${ random_digit } ${ random_upper } ${ random_lower } ${ random_special } "
}
2023-12-22 11:41:07 +01:00
# This password only contains alphanumeric characters
generate_simple_password( ) {
local seed_text = " $1 "
local combined_text = " This is a salt string to generate one consistent password for ${ seed_text } . It is not required to be secret. "
2024-03-18 12:44:34 +01:00
echo " ${ combined_text } " | sha1sum | openssl pkeyutl -sign -inkey " /etc/bridgehead/pki/ ${ SITE_ID } .priv.pem " 2> /dev/null | base64 | head -c 26 | sed 's/[+\/]/A/g'
2023-12-22 11:41:07 +01:00
}
2024-01-31 10:21:19 +01:00
docker_jq( ) {
2024-02-06 15:08:11 +01:00
docker run --rm -i docker.verbis.dkfz.de/cache/jqlang/jq:latest " $@ "
2024-01-31 10:21:19 +01:00
}