Files
Aether/tests/tunnel_installer_config_security_test.sh
T
elky 579f2c7cc1 feat(security): harden gateway boundaries and usage policies
Consolidate subscription usage policy enforcement, privacy-safe persistence, and gateway security hardening into one reviewable change.

Includes bounded HTTP and execution envelopes, header and protocol guards, DNS and relay validation, authentication and secret projection hardening, secure backup/install paths, and regression coverage.
2026-09-04 03:45:52 +08:00

222 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
INSTALLER="${REPO_ROOT}/apps/aether-tunnel/install.sh"
POWERSHELL_INSTALLER="${REPO_ROOT}/apps/aether-tunnel/install.ps1"
TEST_ROOT="${REPO_ROOT}/.tmp-tunnel-installer-test.$$"
cleanup_test_root() {
chmod -R u+rwX "${TEST_ROOT}" 2>/dev/null || true
rm -rf -- "${TEST_ROOT}"
}
trap cleanup_test_root EXIT
fail_test() {
echo "FAIL: $*" >&2
exit 1
}
assert_rejected() {
if ("$@") >/dev/null 2>&1; then
fail_test "unsafe installer fixture was accepted: $*"
fi
}
file_mode() {
stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1"
}
mkdir -m 700 "${TEST_ROOT}"
LIB="${TEST_ROOT}/installer-lib.sh"
sed '/^main()/,$d' "${INSTALLER}" >"${LIB}"
# shellcheck source=/dev/null
source "${LIB}"
trap cleanup_test_root EXIT
validate_release_repo "fawney19/Aether"
validate_tunnel_release_tag "tunnel-v0.3.16-rc.1"
validate_tunnel_release_tag "tunnel-v1.2.3+build.7"
validate_release_asset_name "aether-tunnel-linux-amd64.tar.gz"
validate_node_name "Tokyo edge 01"
validate_node_name "东京节点"
assert_rejected validate_release_repo "owner/repo/extra"
assert_rejected validate_release_repo 'owner/repo;touch injected'
assert_rejected validate_tunnel_release_tag "../tunnel-v0.3.16"
assert_rejected validate_tunnel_release_tag "gateway-v0.3.16"
assert_rejected validate_tunnel_release_tag "tunnel-v1.2"
assert_rejected validate_tunnel_release_tag "tunnel-v01.2.3"
assert_rejected validate_tunnel_release_tag "tunnel-v1.2.3-01"
assert_rejected validate_release_asset_name "../aether-tunnel-linux-amd64.tar.gz"
assert_rejected validate_node_name " trailing "
assert_rejected validate_node_name $'node\tname'
assert_rejected validate_node_name $'node\nname'
assert_rejected validate_node_name "$(printf 'n%.0s' {1..256})"
assert_rejected validate_https_download_url "http://example.test/release.tar.gz"
assert_rejected validate_https_download_url "https://user:pass@example.test/release.tar.gz"
assert_rejected validate_https_download_url "https://example.test/release.tar.gz#fragment"
validate_trusted_github_download_url "https://github.com/fawney19/Aether/releases/download/tunnel-v1.2.3/aether-tunnel-linux-amd64.tar.gz"
validate_trusted_github_download_url "https://release-assets.githubusercontent.com/example/object"
assert_rejected validate_trusted_github_download_url "https://github.com.evil.example/release.tar.gz"
assert_rejected validate_trusted_github_download_url "https://github.com:444/release.tar.gz"
CURL_ARGS="${TEST_ROOT}/curl-args"
curl() {
printf '%s\n' "$@" >"${CURL_ARGS}"
local headers='' output='' url=''
while (($#)); do
case "$1" in
--dump-header) headers="$2"; shift 2 ;;
--output) output="$2"; shift 2 ;;
--write-out) shift 2 ;;
-*) shift ;;
*) url="$1"; shift ;;
esac
done
: >"${headers}"
printf '%s\n' "verified download from ${url}" >"${output}"
printf '200'
}
download "https://github.com/fawney19/Aether/release.tar.gz" "${TEST_ROOT}/downloaded"
grep -Fxq -- '--proto' "${CURL_ARGS}" || fail_test "curl HTTPS protocol restriction is missing"
grep -Fxq -- '=https' "${CURL_ARGS}" || fail_test "curl HTTPS protocol value is missing"
grep -Fxq -- '--max-redirs' "${CURL_ARGS}" || fail_test "curl automatic redirects were not disabled"
unset -f curl
checksum_asset="aether-tunnel-linux-amd64.tar.gz"
checksum_archive="${TEST_ROOT}/${checksum_asset}"
checksum_manifest="${TEST_ROOT}/SHA256SUMS.txt"
printf '%s\n' "verified release payload" >"${checksum_archive}"
if command -v sha256sum >/dev/null 2>&1; then
checksum="$(sha256sum "${checksum_archive}" | awk '{print $1}')"
else
checksum="$(shasum -a 256 "${checksum_archive}" | awk '{print $1}')"
fi
printf '%s %s\n' "${checksum}" "${checksum_asset}" >"${checksum_manifest}"
verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%064d %s\n' 0 "${checksum_asset}" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%063d %s\n' 0 "${checksum_asset}" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%s %s\n' "${checksum}" "other-asset.tar.gz" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%s %s trailing-field\n' "${checksum}" "${checksum_asset}" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%s extra-field %s\n' "${checksum}" "${checksum_asset}" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
printf '%s %s\n%s *%s\n' \
"${checksum}" "${checksum_asset}" "${checksum}" "${checksum_asset}" >"${checksum_manifest}"
assert_rejected verify_checksum "${checksum_archive}" "${checksum_manifest}" "${checksum_asset}"
CONFIG_PATH="${TEST_ROOT}/normal/aether-tunnel.toml"
append_server_config "https://aether.example" "secret-token" "node-one" "off" ""
[[ "$(file_mode "${CONFIG_PATH}")" == "600" ]] || fail_test "config mode is not 0600"
grep -Fq 'management_token = "secret-token"' "${CONFIG_PATH}" || fail_test "config block was not written"
source_binary="${TEST_ROOT}/source-aether-tunnel"
printf '#!/bin/sh\nexit 0\n' >"${source_binary}"
chmod 755 "${source_binary}"
INSTALL_DIR="${TEST_ROOT}/install-bin"
install_tunnel_binary_file "${source_binary}"
[[ -x "${INSTALL_DIR}/aether-tunnel" ]] || fail_test "tunnel binary was not installed"
binary_victim="${TEST_ROOT}/binary-victim"
printf '%s\n' "keep-binary-victim" >"${binary_victim}"
rm -f "${INSTALL_DIR}/aether-tunnel"
ln -s "${binary_victim}" "${INSTALL_DIR}/aether-tunnel"
if (install_tunnel_binary_file "${source_binary}") 2>/dev/null; then
fail_test "symbolic-link binary target was accepted"
fi
[[ "$(cat "${binary_victim}")" == "keep-binary-victim" ]] \
|| fail_test "symbolic-link binary target was modified"
rm -f "${INSTALL_DIR}/aether-tunnel"
binary_hardlink_victim="${TEST_ROOT}/binary-hardlink-victim"
printf '%s\n' "keep-hardlink-victim" >"${binary_hardlink_victim}"
ln "${binary_hardlink_victim}" "${INSTALL_DIR}/aether-tunnel"
if (install_tunnel_binary_file "${source_binary}") 2>/dev/null; then
fail_test "hard-linked binary target was accepted"
fi
[[ "$(cat "${binary_hardlink_victim}")" == "keep-hardlink-victim" ]] \
|| fail_test "hard-linked binary victim was modified"
linked_install_target="${TEST_ROOT}/linked-install-target"
mkdir -m 700 "${linked_install_target}"
INSTALL_DIR="${TEST_ROOT}/linked-install-dir"
ln -s "${linked_install_target}" "${INSTALL_DIR}"
if (install_tunnel_binary_file "${source_binary}") 2>/dev/null; then
fail_test "symbolic-link install directory was accepted"
fi
[[ ! -e "${linked_install_target}/aether-tunnel" ]] \
|| fail_test "symbolic-link install directory target was modified"
ancestor_target="${TEST_ROOT}/ancestor-target"
mkdir -m 700 "${ancestor_target}"
ancestor_link="${TEST_ROOT}/ancestor-link"
ln -s "${ancestor_target}" "${ancestor_link}"
INSTALL_DIR="${ancestor_link}/nested/bin"
if (install_tunnel_binary_file "${source_binary}") 2>/dev/null; then
fail_test "symbolic-link install ancestor was accepted"
fi
[[ ! -e "${ancestor_target}/nested/bin/aether-tunnel" ]] \
|| fail_test "symbolic-link install ancestor target was modified"
chmod 644 "${CONFIG_PATH}"
append_server_config "https://aether.example" "second-token" "node-two" "off" ""
[[ "$(file_mode "${CONFIG_PATH}")" == "600" ]] || fail_test "existing config was not protected before update"
backup="$(find "$(dirname "${CONFIG_PATH}")" -maxdepth 1 -type f -name 'aether-tunnel.toml.bak.*' | head -n1)"
[[ -n "${backup}" && "$(file_mode "${backup}")" == "600" ]] || fail_test "backup mode is not 0600"
victim="${TEST_ROOT}/victim.txt"
printf '%s\n' "keep-me" >"${victim}"
CONFIG_PATH="${TEST_ROOT}/linked.toml"
ln -s "${victim}" "${CONFIG_PATH}"
if (append_server_config "https://aether.example" "stolen-token" "node-link" "off" "") 2>/dev/null; then
fail_test "symbolic-link config was accepted"
fi
[[ "$(cat "${victim}")" == "keep-me" ]] || fail_test "symbolic-link target was modified"
hardlink_victim="${TEST_ROOT}/hardlink-victim.toml"
printf '%s\n' 'victim = true' >"${hardlink_victim}"
CONFIG_PATH="${TEST_ROOT}/hardlinked.toml"
ln "${hardlink_victim}" "${CONFIG_PATH}"
if (append_server_config "https://aether.example" "stolen-token" "node-hardlink" "off" "") 2>/dev/null; then
fail_test "hard-linked config was accepted"
fi
[[ "$(cat "${hardlink_victim}")" == 'victim = true' ]] || fail_test "hard-linked config victim was modified"
grep -Fq 'Get-Content -LiteralPath' "${POWERSHELL_INSTALLER}" || fail_test "PowerShell reads are not literal-path safe"
grep -Fq '[IO.File]::Replace' "${POWERSHELL_INSTALLER}" || fail_test "PowerShell config replacement is not atomic"
grep -Fq 'FileAttributes]::ReparsePoint' "${POWERSHELL_INSTALLER}" || fail_test "PowerShell reparse-point guard is missing"
grep -Fq 'Assert-NoReparsePointAncestors' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell ancestor reparse-point guard is missing"
grep -Fq "LinkType -eq 'HardLink'" "${POWERSHELL_INSTALLER}" || fail_test "PowerShell hard-link guard is missing"
grep -Fq 'AllowAutoRedirect = $false' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell automatic redirects are still enabled"
grep -Fq 'Assert-TrustedGithubUri $CurrentUri' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell trusted redirect validation is missing"
if grep -Eq 'Invoke-(WebRequest|RestMethod)' "${POWERSHELL_INSTALLER}"; then
fail_test "PowerShell installer still uses an automatically redirecting download command"
fi
grep -Fq '[IO.File]::Replace($TempBinary, $TargetBinary' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell binary replacement is not atomic"
grep -Fq '$ExpectedLines.Count -ne 1' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell checksum matching does not reject duplicates"
grep -Fq 'if (-not $ExpectedMatch.Success)' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell checksum matching does not reject malformed target entries"
if grep -Fq 'Select-Object -First 1' "${POWERSHELL_INSTALLER}"; then
fail_test "PowerShell checksum matching still silently accepts duplicate entries"
fi
grep -Fq 'Assert-SafeReleaseRepo $Repo' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell release repository validation is missing"
grep -Fq 'Assert-SafeTunnelReleaseTag $Tag' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell release tag validation is missing"
grep -Fq 'Assert-SafeNodeName $NodeName' "${POWERSHELL_INSTALLER}" \
|| fail_test "PowerShell node-name validation is missing"
if grep -Eq 'Add-Content[[:space:]]+-Path' "${POWERSHELL_INSTALLER}"; then
fail_test "PowerShell config still uses wildcard-aware Add-Content -Path"
fi
echo "PASS: tunnel installer config safety fixtures"