diff --git a/bridgehead b/bridgehead index 09b46f59..499af66f 100755 --- a/bridgehead +++ b/bridgehead @@ -169,13 +169,21 @@ case "$ACTION" in uninstall) exec ./lib/uninstall-bridgehead.sh $PROJECT ;; - adduser) + setuser) loadVars - log "INFO" "Adding encrypted credentials in /etc/bridgehead/$PROJECT.local.conf" - read -p "Please choose the component (LDM_AUTH|NNGM_AUTH|EXPORTER_USER) you want to add a user to : " COMPONENT - read -p "Please enter a username: " USER - read -s -p "Please enter a password (will not be echoed): "$'\n' PASSWORD - add_basic_auth_user $USER $PASSWORD $COMPONENT $PROJECT + log "INFO" "Setting encrypted credentials in /etc/bridgehead/$PROJECT.local.conf" + read -p "Please choose the component ($(echo $BASIC_AUTH_VARIABLES | tr ' ' '|')) you want to set the credentials for : " COMPONENT + case " $BASIC_AUTH_VARIABLES " in + *" $COMPONENT "*) + ;; + *) + log "ERROR" "\"$COMPONENT\" is not a basic auth component. Choose one of: $BASIC_AUTH_VARIABLES" + exit 1 + ;; + esac + read -p "Please enter a username: " USER + read -s -p "Please enter a password (will not be echoed): "$'\n' PASSWORD + set_basic_auth_user "$USER" "$PASSWORD" "$COMPONENT" "$PROJECT" ;; enroll) loadVars diff --git a/lib/check-bridgehead.sh b/lib/check-bridgehead.sh index 57ca537c..8048b8b2 100755 --- a/lib/check-bridgehead.sh +++ b/lib/check-bridgehead.sh @@ -64,7 +64,29 @@ if [ "$GIT_REMOTE_OK" = true ]; then log INFO "Git remote connection successful." fi -if [ "$OWNERSHIP_OK" = true ] && [ "$GIT_OK" = true ] && [ "$GIT_REMOTE_OK" = true ]; then +# Basic auth credentials +log INFO "Checking basic auth credentials..." +AUTH_OK=true +LDM_AUTH_USERS=0 +while IFS= read -r ENTRY; do + if is_valid_basic_auth_entry "$ENTRY"; then + LDM_AUTH_USERS=$((LDM_AUTH_USERS + 1)) + else + log ERROR "LDM_AUTH contains the invalid entry \"$ENTRY\". Traefik rejects the whole list, leaving the local data management unreachable." + AUTH_OK=false + fi +done < <([ -n "${LDM_AUTH:-}" ] && printf '%s\n' "$LDM_AUTH" | tr ',' '\n') +if [ "$LDM_AUTH_USERS" -ne 1 ]; then + log ERROR "LDM_AUTH holds $LDM_AUTH_USERS sets of valid credentials, but exactly one is expected." + AUTH_OK=false +fi +if [ "$AUTH_OK" = true ]; then + log INFO "Basic auth credentials are valid." +else + log INFO "Hint: Run 'bridgehead setuser $PROJECT' to replace LDM_AUTH with a single set of credentials." +fi + +if [ "$OWNERSHIP_OK" = true ] && [ "$GIT_OK" = true ] && [ "$GIT_REMOTE_OK" = true ] && [ "$AUTH_OK" = true ]; then log INFO "All checks passed." exit 0 else diff --git a/lib/functions.sh b/lib/functions.sh index 3a5cfe4a..57296554 100644 --- a/lib/functions.sh +++ b/lib/functions.sh @@ -53,7 +53,7 @@ checkOwner(){ } printUsage() { - echo "Usage: bridgehead start|stop|logs|docker-logs|is-running|update|check|install|uninstall|adduser|enroll PROJECTNAME" + echo "Usage: bridgehead start|stop|logs|docker-logs|is-running|update|check|install|uninstall|setuser|enroll PROJECTNAME" echo "PROJECTNAME should be one of ccp|bbmri|cce|itcc|kr|dhki|nngm" } @@ -247,21 +247,55 @@ function do_enroll { do_enroll_inner $@ } -add_basic_auth_user() { - 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 +BASIC_AUTH_VARIABLES="LDM_AUTH NNGM_AUTH TRANSFAIR_AUTH EXPORTER_USER" + +# One entry of Traefik's basicauth.users list: "user:hash", with the username +# restricted to characters that cannot collide with either separator. +is_valid_basic_auth_entry() { + local entry="$1" + local user="${entry%%:*}" + local hash="${entry#*:}" + [ "$user" != "$entry" ] || return 1 + [ -n "$hash" ] || return 1 + [ "$hash" = "${hash#*:}" ] || return 1 + [[ "$user" =~ ^[A-Za-z0-9._-]+$ ]] +} + +# Stores one set of basic auth credentials in $NAME, replacing any existing ones. +set_basic_auth_user() { + local USER="${1}" + local PASSWORD="${2}" + local NAME="${3}" + local PROJECT="${4}" + local FILE="/etc/bridgehead/${PROJECT}.local.conf" + local ENCRY_CREDENTIALS + if [ -z "$USER" ] || [ -z "$PASSWORD" ]; then + log ERROR "Both a username and a password are required. $FILE is unchanged." + return 1 + fi + if ! ENCRY_CREDENTIALS="$(docker run --rm docker.verbis.dkfz.de/cache/httpd:alpine htpasswd -nb "$USER" "$PASSWORD")"; then + log ERROR "Unable to run htpasswd, so no credentials were generated. $FILE is unchanged." + return 1 + fi + ENCRY_CREDENTIALS="$(printf '%s' "$ENCRY_CREDENTIALS" | tr -d '\n' | tr -d '\r')" + if ! is_valid_basic_auth_entry "$ENCRY_CREDENTIALS"; then + log ERROR "htpasswd returned no usable credentials for \"$USER\". $FILE is unchanged." + return 1 + fi + if [ -f $FILE ] && grep -q "^$NAME=" $FILE # if this basic auth variable already exists: + then + sed -i "/^$NAME=/{:a;N;s/\n# User: [^\n]*//;s/\n# Password: [^\n]*//;ta}" $FILE + sed -i "0,/^$NAME=/!{/^$NAME=/d}" $FILE + 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 + if [ "$(grep -c "^$NAME=" $FILE)" -ne 1 ] || [ "$(sed -n "s|^$NAME='\(.*\)'$|\1|p" $FILE)" != "$ENCRY_CREDENTIALS" ]; then + log ERROR "$NAME in $FILE does not hold exactly one set of credentials. Please correct it manually." + return 1 + fi } OIDC_PUBLIC_REDIRECT_URLS=${OIDC_PUBLIC_REDIRECT_URLS:-""} diff --git a/lib/install-bridgehead.sh b/lib/install-bridgehead.sh index 2e2ec69a..cbe6d5bd 100755 --- a/lib/install-bridgehead.sh +++ b/lib/install-bridgehead.sh @@ -29,29 +29,29 @@ EOF # TODO: Determine whether this should be located in setup-bridgehead (triggered through bridgehead install) or in update bridgehead (triggered every hour) if [ -z "$LDM_AUTH" ]; then - log "INFO" "Now generating basic auth for the local data management (see adduser in bridgehead for more information). " + log "INFO" "Now generating basic auth for the local data management (see setuser in bridgehead for more information). " generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" - add_basic_auth_user $PROJECT $generated_passwd "LDM_AUTH" $PROJECT + set_basic_auth_user "$PROJECT" "$generated_passwd" "LDM_AUTH" "$PROJECT" fi if [ ! -z "$NNGM_CTS_APIKEY" ] && [ -z "$NNGM_AUTH" ]; then - log "INFO" "Now generating basic auth for nNGM upload API (see adduser in bridgehead for more information). " + log "INFO" "Now generating basic auth for nNGM upload API (see setuser in bridgehead for more information). " generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" - add_basic_auth_user "nngm" $generated_passwd "NNGM_AUTH" $PROJECT + set_basic_auth_user "nngm" "$generated_passwd" "NNGM_AUTH" "$PROJECT" fi if [ -z "$TRANSFAIR_AUTH" ]; then if [[ -n "$TTP_URL" || -n "$EXCHANGE_ID_SYSTEM" ]]; then - log "INFO" "Now generating basic auth user for transfair API (see adduser in bridgehead for more information). " + log "INFO" "Now generating basic auth user for transfair API (see setuser in bridgehead for more information). " generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" - add_basic_auth_user "transfair" $generated_passwd "TRANSFAIR_AUTH" $PROJECT + set_basic_auth_user "transfair" "$generated_passwd" "TRANSFAIR_AUTH" "$PROJECT" fi fi if [ "$ENABLE_EXPORTER" == "true" ] && [ -z "$EXPORTER_USER" ]; then - log "INFO" "Now generating basic auth for the exporter and reporter (see adduser in bridgehead for more information)." + log "INFO" "Now generating basic auth for the exporter and reporter (see setuser in bridgehead for more information)." generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" - add_basic_auth_user $PROJECT $generated_passwd "EXPORTER_USER" $PROJECT + set_basic_auth_user "$PROJECT" "$generated_passwd" "EXPORTER_USER" "$PROJECT" fi log "INFO" "Registering system units for bridgehead and bridgehead-update" diff --git a/lib/update-bridgehead.sh b/lib/update-bridgehead.sh index cf846db7..5a493b38 100755 --- a/lib/update-bridgehead.sh +++ b/lib/update-bridgehead.sh @@ -140,9 +140,14 @@ fi if [ ! -z "$LDM_PASSWORD" ]; then FILE="/etc/bridgehead/$PROJECT.local.conf" log "INFO" "Migrating LDM_PASSWORD to encrypted credentials in $FILE" - add_basic_auth_user $PROJECT $LDM_PASSWORD "LDM_AUTH" $PROJECT - add_basic_auth_user $PROJECT $LDM_PASSWORD "NNGM_AUTH" $PROJECT - sed -i "/LDM_PASSWORD/{d;}" $FILE + if set_basic_auth_user "$PROJECT" "$LDM_PASSWORD" "LDM_AUTH" "$PROJECT"; then + if [ ! -z "$NNGM_CTS_APIKEY" ]; then + set_basic_auth_user "$PROJECT" "$LDM_PASSWORD" "NNGM_AUTH" "$PROJECT" + fi + sed -i "/LDM_PASSWORD/{d;}" $FILE + else + log "ERROR" "Migration failed, keeping LDM_PASSWORD in $FILE for the next attempt." + fi fi exit 0