Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper script for generating no_proxy option for ELM #15

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
288 changes: 288 additions & 0 deletions utils/get_no_proxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2024 SAP edge team
# SPDX-FileContributor: Manjun Jiao (@mjiao)
#
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail
IFS=$'\n\t'

usage="$(basename "${BASH_SOURCE[0]}") [Options] [domain.ltd,...] [!excluded.domain]

Generates NO_PROXY settings for SAP Edge Lifecycle Management installation requiring HTTP proxy to access
external resources.

Additional domains can be given as arguments. If a domain is prefixed with '!', it will be
excluded from the list. IOW, it will be proxied.

If a (longer wildcard) domain is matched by a(nother) wildcard domain, it is excluded from the
output.

Requirements:
- OpenShift client binaries (oc) must be installed and located in PATH.
- The user must authenticated to the OpenShift cluster and be at least the cluster-reader.
- awk in PATH

Options:
-h | --help Show this help message and exit.
-e | --elm Compute NO_PROXY settings for ELM."
readonly usage

readonly mustHave=(
127.0.0.1
169.254.169.254

localhost
metadata.google.internal

*.local
*.cluster.local
*.internal
*.google.internal
*.svc
)

readonly mustHaveELM=(
)

readonly longOptions=(
help elm
)

# normalize reads the arguments and produces a newline-separated list of NO_PROXY entries.
# If an argument contains a comma, it will be split.
# If the first argument is `-e` only the entries prefixed with ! will be produced, otherwise
# only non-prefixed entries will be produced.
function normalize() {
local entry
local selectExcluded=0
if [[ "${1:-}" == -e ]]; then
selectExcluded=1
shift
fi

for entry in "$@"; do
# shellcheck disable=SC2001
if [[ "${entry}" =~ , ]]; then
local entries=()
readarray -t entries < <(tr ',' '\n' <<<"${entry}")
local args=()
if [[ "$selectExcluded" == 1 ]]; then
args+=( -e )
fi
# shellcheck disable=SC2068
normalize ${args[@]} "${entries[@]}"
continue
fi

entry="$(sed \
-e 's/\([[:space:]]\|,\)\+//g' \
-e 's/\.\+/./g' \
-e 's/\!\+/!/g' \
-e 's/\*\+/*/g' <<<"${entry:-}")"
if [[ -z "${entry:-}" ]]; then
continue
fi

case "$selectExcluded$entry" in
"0!"*)
continue
;;
"1!"*)
entry="${entry##\!}"
;;
"1"*)
continue
;;
esac

case "${mode:-elm}" in
elm)
entry="${entry##\*}"
;;
*)
if [[ "$entry" =~ ^\. ]]; then
printf '*%s\n' "$entry"
continue
fi
;;
esac
printf '%s\n' "$entry"
done
}

# filterOutRedundancies reads newline-separated list of NO_PROXY entries from stdin, removes the
# duplicates, removes subdomains of wildcard domains and moves wildcard entries to the while
# trying to preserve the order of the items.
# Limitations: does not care about CIDRs.
function filterOutRedundancies() {
awk 3< <(printf '%s\n' "${excludes[@]}") '
{
if (isExcluded($0)) {
next
}
if ($0 ~ /^[*.]/) {
addWildcard($0)
} else {
addDomain($0, nonWildcards, nonWildcardList)
}
}

BEGIN {
split("", nonWildcardList)
split("", wildcardList)

while ((getline line <"/dev/fd/3") > 0) {
excludes[line]++
}
}

END {
for (i in nonWildcardList) {
domain = nonWildcardList[i]
if (isMatchedByAnyWildcard(domain)) {
continue
}
print domain
}
for (i in wildcardList) {
print wildcardList[i]
}
}

function isSubdomain(domain, wildcard, w, i) {
w = wildcard
sub(/^\*/, "", w)
i = index(domain, w)
return i > 0 && i + length(w) >= length(domain)
}

function isMatchedByAnyWildcard(domain) {
for (w in wildcards) {
if (isSubdomain(domain, w)) {
return 1
}
}
return 0
}

function isExcluded(d) {
for (e in excludes) {
if (d == e) {
return 1
}
}
return 0
}

function removeWildcard(w) {
delete wildcards[w]
for (i in wildcardList) {
if (wildcardList[i] == w) {
delete wildcardList[i]
break
}
}
}

function addWildcard(w) {
for (w in wildcards) {
if (isSubdomain($0, w)) {
next
}
if (isSubdomain(w, $0)) {
removeWildcard(w)
}
}
addDomain($0, wildcards, wildcardList)
}

function addDomain(d, map, list) {
if (map[$0]++ == 0) {
list[length(list)] = $0
}
}'
}

function join() { local IFS="$1"; shift; echo "$*"; }

function getOpenShiftNoProxyOrDie() {
local osNoProxy
osNoProxy="$(oc get proxy/cluster -o jsonpath='{.status.noProxy}')"
if [[ -z "${osNoProxy:-}" ]]; then
printf 'Failed to determine noProxy from OpenShift cluster!\n' >&2
printf 'Please configure noProxy first on the OpenShift cluster\n' >&2
printf 'and make sure the current user can read it.\n' >&2
exit 1
fi
printf '%s' "$osNoProxy"
}

function setExcludes() {
readarray -t excludes < <(normalize -e "$@")
if [[ "${#excludes[@]}" == 1 && -z "${excludes[0]:-}" ]]; then
excludes=()
fi
}

function assertDependencies() {
if [[ -z "$(command -v oc)" ]]; then
printf 'Please ensure oc binary is in PATH and its minor release matches the server!\n' >&2
exit 1
fi
if [[ "$(gawk 'END {split("a,b", a, /,/); print length(a)}' </dev/null)" != 2 ]]; then
printf 'Please ensure that GNU AWK is installed and present in the PATH!\n' >&2
exit 1
fi
if ! oc auth can-i list proxies.config.openshift.io --all-namespaces >/dev/null; then
printf 'Please ensure the current OpenShift user is logged-in and has at least the' >&2
printf ' cluster-reader role.\n' >&2
exit 1
fi
}

function computeNoProxy() {
local osNoProxy=() entries=()
readarray -t osNoProxy < <(getOpenShiftNoProxyOrDie | tr ',' '\n')
readarray -t entries < <({ \
normalize "${osNoProxy[@]}" "$@" "${mustHave[@]}"; \
case "${mode:-elm}" in
elm)
printf '%s\n' "${mustHaveELM[@]}";
;;
esac; \
} | filterOutRedundancies)
join , "${entries[@]}"
}

TMPARGS="$(getopt -o he -l "$(join , "${longOptions[@]}")" -n "${BASH_SOURCE[0]}" -- "$@")"
eval set -- "$TMPARGS"

mode=elm
excludes=()

while true; do
case "$1" in
-h | --help)
printf '%s\n' "$usage"
exit 0
;;
-e | --elm)
mode=elm
shift
;;
--)
shift
break
;;
*)
printf 'Unknown argument "%s"!\n' "$1" >&2
exit 1
;;
esac
done

assertDependencies
setExcludes "$@"
computeNoProxy "$@"