diff --git a/must-gather/Makefile b/must-gather/Makefile index c0ea5255e53..b4a3043657a 100644 --- a/must-gather/Makefile +++ b/must-gather/Makefile @@ -7,6 +7,9 @@ build: podman-build podman-push check: shellcheck collection-scripts/* +test: + bash tests/gather_cni_config_test.sh + ensure-must-gather-image-is-set: ifndef MUST_GATHER_IMAGE $(error MUST_GATHER_IMAGE is not set.) @@ -18,4 +21,4 @@ podman-build: ensure-must-gather-image-is-set podman-push: ensure-must-gather-image-is-set podman push ${IMAGE_REGISTRY}/${MUST_GATHER_IMAGE}:${IMAGE_TAG} -.PHONY: build podman-build podman-push +.PHONY: build check test podman-build podman-push diff --git a/must-gather/collection-scripts/gather_cni_config b/must-gather/collection-scripts/gather_cni_config new file mode 100755 index 00000000000..25d67fe4b88 --- /dev/null +++ b/must-gather/collection-scripts/gather_cni_config @@ -0,0 +1,13 @@ +#!/bin/bash + +# Filter on the node before transferring the archive: kubeconfigs can contain +# credentials, including the Whereabouts service account token. +set -o pipefail + +pod=$1 +conf_dir=$2 +destination=$3 + +oc exec "$pod" -n node-gather -- \ + tar -C "/host/$conf_dir" --ignore-case --exclude='*kubeconfig*' -cf - . | \ + tar -xf - -C "$destination" diff --git a/must-gather/collection-scripts/gather_nodes b/must-gather/collection-scripts/gather_nodes index 344948482ab..8a938fbb4e5 100755 --- a/must-gather/collection-scripts/gather_nodes +++ b/must-gather/collection-scripts/gather_nodes @@ -155,12 +155,16 @@ gather_single_pod() { IFS=$' ' for conf_dir in "${config_dirs[@]}"; do if exec_with_retry oc exec "$pod" -n node-gather -- [ -d "/host/$conf_dir" ]; then - CNI_COFIG_PATH=${NODE_PATH}/$conf_dir - mkdir -p "${CNI_COFIG_PATH}" - if ! output=$(exec_with_retry oc cp "$pod:/host/$conf_dir" "${CNI_COFIG_PATH}" -n node-gather 2>&1); then + CNI_CONFIG_PATH=${NODE_PATH}/$conf_dir + mkdir -p "${CNI_CONFIG_PATH}" + # Filter on the node to avoid gathering kubeconfig credentials. + if output=$(exec_with_retry bash /usr/bin/gather_cni_config "$pod" "$conf_dir" "${CNI_CONFIG_PATH}" 2>&1); then + : + else + exit_code=$? log_error "Copying CNI config from $conf_dir for node $node" \ - "oc cp $pod:/host/$conf_dir ${CNI_COFIG_PATH} -n node-gather" \ - "$?" \ + "gather_cni_config $pod $conf_dir ${CNI_CONFIG_PATH}" \ + "$exit_code" \ "$output" fi fi diff --git a/must-gather/tests/gather_cni_config_test.sh b/must-gather/tests/gather_cni_config_test.sh new file mode 100755 index 00000000000..2cd615f9ddf --- /dev/null +++ b/must-gather/tests/gather_cni_config_test.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -euo pipefail + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +temp_dir=$(mktemp -d) +trap 'rm -rf "$temp_dir"' EXIT + +export FAKE_HOST="$temp_dir/host" +export OC_ARCHIVE="$temp_dir/sent.tar" +mkdir -p "$temp_dir/bin" "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d" \ + "$FAKE_HOST/etc/cni/net.d" "$temp_dir/output with spaces" + +# Run the node-side tar locally and capture its output as the data sent by oc. +cat > "$temp_dir/bin/oc" <<'EOF' +#!/bin/bash +set -o pipefail +if [[ ${OC_FAIL:-} == 1 ]]; then + exit 42 +fi +[[ $1 == exec && $3 == -n && $4 == node-gather && $5 == -- && $6 == tar && $7 == -C ]] || exit 1 +shift 7 +host_dir=$1 +shift +[[ $host_dir == /host/* ]] || exit 1 +tar -C "$FAKE_HOST/${host_dir#/host/}" "$@" | tee "$OC_ARCHIVE" +EOF +chmod +x "$temp_dir/bin/oc" +export PATH="$temp_dir/bin:$PATH" + +printf 'multus configuration\n' > "$FAKE_HOST/etc/kubernetes/cni/net.d/00-multus.conf" +printf 'kubeconfig: /etc/kubernetes/cni/net.d/whereabouts.d/whereabouts.kubeconfig\n' > "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d/whereabouts.conf" +printf 'node name\n' > "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d/nodename" +printf 'sensitive-test-token\n' > "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d/whereabouts.kubeconfig" +printf 'sensitive-test-token\n' > "$FAKE_HOST/etc/kubernetes/cni/net.d/other.kubeconfig" +printf 'alternate CNI configuration\n' > "$FAKE_HOST/etc/cni/net.d/10-cni.conf" +printf 'sensitive-test-token\n' > "$FAKE_HOST/etc/cni/net.d/kubeconfig" +printf 'sensitive-test-token\n' > "$FAKE_HOST/etc/cni/net.d/other.KubeConfig" + +for conf_dir in etc/kubernetes/cni/net.d etc/cni/net.d; do + destination="$temp_dir/output with spaces/$conf_dir" + mkdir -p "$destination" + bash "$script_dir/collection-scripts/gather_cni_config" test-pod "$conf_dir" "$destination" + + if tar -tf "$OC_ARCHIVE" | grep -qi kubeconfig || grep -aq 'sensitive-test-token' "$OC_ARCHIVE"; then + echo "kubeconfig was sent from $conf_dir" >&2 + exit 1 + fi + if find "$destination" -iname '*kubeconfig*' -print -quit | grep -q .; then + echo "kubeconfig was gathered from $conf_dir" >&2 + exit 1 + fi +done + +cmp "$FAKE_HOST/etc/kubernetes/cni/net.d/00-multus.conf" "$temp_dir/output with spaces/etc/kubernetes/cni/net.d/00-multus.conf" +cmp "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d/whereabouts.conf" "$temp_dir/output with spaces/etc/kubernetes/cni/net.d/whereabouts.d/whereabouts.conf" +cmp "$FAKE_HOST/etc/kubernetes/cni/net.d/whereabouts.d/nodename" "$temp_dir/output with spaces/etc/kubernetes/cni/net.d/whereabouts.d/nodename" +cmp "$FAKE_HOST/etc/cni/net.d/10-cni.conf" "$temp_dir/output with spaces/etc/cni/net.d/10-cni.conf" + +if OC_FAIL=1 bash "$script_dir/collection-scripts/gather_cni_config" test-pod etc/cni/net.d "$temp_dir/output with spaces" 2>/dev/null; then + echo "node-side failure was not reported" >&2 + exit 1 +fi +if bash "$script_dir/collection-scripts/gather_cni_config" test-pod etc/cni/net.d "$temp_dir/missing" 2>/dev/null; then + echo "local extraction failure was not reported" >&2 + exit 1 +fi + +echo "CNI collection tests passed"