diff --git a/lib/functions.sh b/lib/functions.sh index b4e0f68d..866ca971 100644 --- a/lib/functions.sh +++ b/lib/functions.sh @@ -9,6 +9,15 @@ detectCompose() { fi } +# Encodes all characters not in unrestricted character set of RFC3986 Section 2.3 +urlencode() { + for ((i=0;i<${#1};i++)); do + local c=${1:i:1} + [[ "$c" =~ [a-zA-Z0-9._~-] ]] && printf '%s' "$c" || printf '%%%02X' "'$c" + done + echo +} + setupProxy() { ### Note: As the current data protection concepts do not allow communication via HTTP, ### we are not setting a proxy for HTTP requests. @@ -22,10 +31,12 @@ setupProxy() { 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')" if [[ ! -z "$HTTPS_PROXY_USERNAME" && ! -z "$HTTPS_PROXY_PASSWORD" ]]; then - local ESCAPED_PASSWORD="$(echo $HTTPS_PROXY_PASSWORD | od -An -v -t x1 | sed -e 's/[[:space:]]//g' -e 's/\([0-9a-f][0-9a-f]\)/%\1/g' | tr -d '\n')" + local ESCAPED_PASSWORD="$(echo $HTTPS_PROXY_PASSWORD | od -An -v -t x1 | sed -e 's/[[:space:]]//g' -e 's/\([0-9a-f][0-9a-f]\)/%\1/g' | tr -d '\n')" + local CURL_ESCAPED_PW="$(urlencode $HTTPS_PROXY_PASSWORD)" local proto="$(echo $HTTPS_PROXY_URL | grep :// | sed -e 's,^\(.*://\).*,\1,g')" local fqdn="$(echo ${HTTPS_PROXY_URL/$proto/})" HTTPS_PROXY_FULL_URL="$(echo $proto$HTTPS_PROXY_USERNAME:$ESCAPED_PASSWORD@$fqdn)" + CURL_HTTPS_PROXY_FULL_URL="$(echo $proto$HTTPS_PROXY_USERNAME:$CURL_ESCAPED_PW@$fqdn)" https="authenticated" else HTTPS_PROXY_FULL_URL=$HTTPS_PROXY_URL @@ -34,7 +45,7 @@ setupProxy() { fi log INFO "Configuring proxy servers: $http http proxy (we're not supporting unencrypted comms), $https https proxy" - export HTTPS_PROXY_HOST HTTPS_PROXY_PORT HTTPS_PROXY_FULL_URL + export HTTPS_PROXY_HOST HTTPS_PROXY_PORT HTTPS_PROXY_FULL_URL CURL_HTTPS_PROXY_FULL_URL } exitIfNotRoot() { diff --git a/lib/monitoring.sh b/lib/monitoring.sh index b5466a56..640bf9ac 100755 --- a/lib/monitoring.sh +++ b/lib/monitoring.sh @@ -47,8 +47,8 @@ function hc_send(){ if [ -n "$2" ]; then MSG="$2\n\nDocker stats:\n$UPTIME" - echo -e "$MSG" | https_proxy=$HTTPS_PROXY_FULL_URL curl --max-time 5 -A "$USER_AGENT" -s -o /dev/null -X POST --data-binary @- "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" + echo -e "$MSG" | https_proxy=$CURL_HTTPS_PROXY_FULL_URL curl --max-time 5 -A "$USER_AGENT" -s -o /dev/null -X POST --data-binary @- "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" else - https_proxy=$HTTPS_PROXY_FULL_URL curl --max-time 5 -A "$USER_AGENT" -s -o /dev/null "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" + https_proxy=$CURL_HTTPS_PROXY_FULL_URL curl --max-time 5 -A "$USER_AGENT" -s -o /dev/null "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" fi } diff --git a/lib/prerequisites.sh b/lib/prerequisites.sh index 2c1e186e..945f4d02 100755 --- a/lib/prerequisites.sh +++ b/lib/prerequisites.sh @@ -71,7 +71,7 @@ source ${PROJECT}/vars if [ "${PROJECT}" != "minimal" ]; then set +e - SERVERTIME="$(https_proxy=$HTTPS_PROXY_FULL_URL curl -m 5 -s -I $BROKER_URL_FOR_PREREQ 2>&1 | grep -i -e '^Date: ' | sed -e 's/^Date: //i')" + SERVERTIME="$(https_proxy=$CURL_HTTPS_PROXY_FULL_URL curl -m 5 -s -I $BROKER_URL_FOR_PREREQ 2>&1 | grep -i -e '^Date: ' | sed -e 's/^Date: //i')" RET=$? set -e if [ $RET -ne 0 ]; then diff --git a/lib/tests/test_proxyparsing.sh b/lib/tests/test_proxyparsing.sh new file mode 100755 index 00000000..32e959aa --- /dev/null +++ b/lib/tests/test_proxyparsing.sh @@ -0,0 +1,123 @@ +source ../functions.sh + +test_setupProxy() { + # simple logger for tests + log() { :; } + + local failures=0 + local total=0 + + assert_eq() { + local label="$1" got="$2" expected="$3" + total=$((total + 1)) + if [[ "$got" != "$expected" ]]; then + failures=$((failures + 1)) + printf 'FAIL: %s\n got: %q\n expected: %q\n\n' "$label" "$got" "$expected" + else + printf 'ok: %s\n' "$label" + fi + } + + run_case() { + local name="$1" + local url="$2" + local u="$3" + local p="$4" + local exp_host="$5" + local exp_port="$6" + local exp_full="$7" + + HTTPS_PROXY_URL="$url" + HTTPS_PROXY_USERNAME="$u" + HTTPS_PROXY_PASSWORD="$p" + + setupProxy >/dev/null 2>&1 + + assert_eq "$name host" "$HTTPS_PROXY_HOST" "$exp_host" + assert_eq "$name port" "$HTTPS_PROXY_PORT" "$exp_port" + assert_eq "$name full" "$HTTPS_PROXY_FULL_URL" "$exp_full" + } + + echo "Running setupProxy tests..." + echo + + # 1) Basic https host:port + run_case "basic https" \ + "https://proxy.example.org:8443" "" "" \ + "proxy.example.org" "8443" \ + "https://proxy.example.org:8443" + + # 2) https without port -> default 443 + run_case "https no port" \ + "https://proxy.example.org" "" "" \ + "proxy.example.org" "443" \ + "https://proxy.example.org" + + # 3) no scheme, host:port -> defaults scheme=https + run_case "no scheme hostport" \ + "proxy.example.org:3128" "" "" \ + "proxy.example.org" "3128" \ + "https://proxy.example.org:3128" + + # 4) URL with path/query/fragment + run_case "ignores path" \ + "https://proxy.example.org:8443/some/path?x=1#y" "" "" \ + "proxy.example.org" "8443" \ + "https://proxy.example.org:8443" + + # 5) explicit env creds inserted + run_case "env creds override" \ + "https://proxy.example.org:8443" "alice" "secret" \ + "proxy.example.org" "8443" \ + "https://alice:secret@proxy.example.org:8443" + + # 6) embedded creds used if env creds absent + run_case "embedded creds" \ + "https://bob:pw@proxy.example.org:8443" "" "" \ + "proxy.example.org" "8443" \ + "https://bob:pw@proxy.example.org:8443" + + # 7) env creds override embedded creds + run_case "env overrides embedded" \ + "https://bob:pw@proxy.example.org:8443" "alice" "secret" \ + "proxy.example.org" "8443" \ + "https://alice:secret@proxy.example.org:8443" + + # 8) IPv6 literal with port + run_case "ipv6 with port" \ + "https://[2001:db8::1]:8080" "" "" \ + "2001:db8::1" "8080" \ + "https://[2001:db8::1]:8080" + + # 9) IPv6 literal without port -> default 443 + run_case "ipv6 no port" \ + "https://[2001:db8::1]" "" "" \ + "2001:db8::1" "443" \ + "https://[2001:db8::1]" + + # 10) http scheme rejected -> outputs empty + HTTPS_PROXY_URL="http://proxy.example.org:8080" + HTTPS_PROXY_USERNAME="" + HTTPS_PROXY_PASSWORD="" + setupProxy >/dev/null 2>&1 + assert_eq "http rejected host" "${HTTPS_PROXY_HOST:-}" "" + assert_eq "http rejected port" "${HTTPS_PROXY_PORT:-}" "" + assert_eq "http rejected full" "${HTTPS_PROXY_FULL_URL:-}" "" + + # 11) empty URL -> outputs empty but no failure + HTTPS_PROXY_URL="" + setupProxy >/dev/null 2>&1 + assert_eq "empty url host" "${HTTPS_PROXY_HOST:-}" "" + assert_eq "empty url port" "${HTTPS_PROXY_PORT:-}" "" + assert_eq "empty url full" "${HTTPS_PROXY_FULL_URL:-}" "" + + echo + echo "Tests complete: $((total - failures))/$total passed." + if (( failures > 0 )); then + echo "Some tests failed." + return 1 + fi + return 0 +} + +test_setupProxy