Add NO_PROXY defaults to Docker proxy setup

This commit is contained in:
jxxghp
2026-06-23 20:22:40 +08:00
parent c1d8676b25
commit 109be58abe
4 changed files with 162 additions and 5 deletions

View File

@@ -35,12 +35,43 @@ function apply_package_cache_env() {
mkdir -p "${PIP_CACHE_DIR}" "${UV_CACHE_DIR}"
}
function apply_no_proxy_env() {
local default_no_proxy="localhost,127.0.0.1,::1,0.0.0.0,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,fc00::/7,fe80::/10,host.docker.internal,host.containers.internal,gateway.docker.internal,.local,.lan,.internal,.home.arpa,.localdomain"
local merged="${NO_PROXY:-}"
local source_value item old_ifs
local -a no_proxy_items
# Docker 内常见本机、局域网和内网服务必须直连,避免 PROXY_HOST 被映射到
# HTTP_PROXY 后拦截 Telegram 回调、下载器、媒体服务器等本地请求。
for source_value in "${no_proxy:-}" "${default_no_proxy}"; do
old_ifs="${IFS}"
IFS=','
read -ra no_proxy_items <<< "${source_value}"
IFS="${old_ifs}"
for item in "${no_proxy_items[@]}"; do
item="${item#"${item%%[![:space:]]*}"}"
item="${item%"${item##*[![:space:]]}"}"
if [ -z "${item}" ]; then
continue
fi
case ",${merged}," in
*",${item},"*) ;;
*) merged="${merged:+${merged},}${item}" ;;
esac
done
done
export NO_PROXY="${merged}"
export no_proxy="${merged}"
}
function apply_package_proxy_env() {
if [ -n "${PROXY_HOST}" ]; then
if [ -n "${PROXY_HOST:-}" ]; then
export HTTP_PROXY="${PROXY_HOST}"
export HTTPS_PROXY="${PROXY_HOST}"
export http_proxy="${PROXY_HOST}"
export https_proxy="${PROXY_HOST}"
apply_no_proxy_env
fi
}
@@ -167,6 +198,9 @@ function load_config_from_app_env() {
done
shopt -u extglob
if [ -n "${PROXY_HOST:-}${HTTP_PROXY:-}${HTTPS_PROXY:-}${http_proxy:-}${https_proxy:-}" ]; then
apply_no_proxy_env
fi
INFO "配置加载流程执行完毕。"
}

View File

@@ -36,12 +36,42 @@ function apply_package_cache_env() {
apply_package_cache_env
function apply_no_proxy_env() {
local default_no_proxy="localhost,127.0.0.1,::1,0.0.0.0,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,fc00::/7,fe80::/10,host.docker.internal,host.containers.internal,gateway.docker.internal,.local,.lan,.internal,.home.arpa,.localdomain"
local merged="${NO_PROXY:-}"
local source_value item old_ifs
local -a no_proxy_items
# 代理仅用于外网依赖下载,容器访问本机、局域网和内网服务时应保持直连。
for source_value in "${no_proxy:-}" "${default_no_proxy}"; do
old_ifs="${IFS}"
IFS=','
read -ra no_proxy_items <<< "${source_value}"
IFS="${old_ifs}"
for item in "${no_proxy_items[@]}"; do
item="${item#"${item%%[![:space:]]*}"}"
item="${item%"${item##*[![:space:]]}"}"
if [[ -z "${item}" ]]; then
continue
fi
case ",${merged}," in
*",${item},"*) ;;
*) merged="${merged:+${merged},}${item}" ;;
esac
done
done
export NO_PROXY="${merged}"
export no_proxy="${merged}"
}
function apply_package_proxy_env() {
if [[ -n "${PROXY_HOST}" ]]; then
if [[ -n "${PROXY_HOST:-}" ]]; then
export HTTP_PROXY="${PROXY_HOST}"
export HTTPS_PROXY="${PROXY_HOST}"
export http_proxy="${PROXY_HOST}"
export https_proxy="${PROXY_HOST}"
apply_no_proxy_env
fi
}

View File

@@ -12,6 +12,8 @@ cat > "${TMP_DIR}/venv/bin/pip" <<'SH'
printf 'argv=%s\n' "$*" >> "${MP_FAKE_PIP_LOG}"
printf 'HTTP_PROXY=%s\n' "${HTTP_PROXY:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'HTTPS_PROXY=%s\n' "${HTTPS_PROXY:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'NO_PROXY=%s\n' "${NO_PROXY:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'no_proxy=%s\n' "${no_proxy:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'PACKAGE_CACHE_ROOT=%s\n' "${PACKAGE_CACHE_ROOT:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'PIP_CACHE_DIR=%s\n' "${PIP_CACHE_DIR:-}" >> "${MP_FAKE_PIP_LOG}"
printf 'UV_CACHE_DIR=%s\n' "${UV_CACHE_DIR:-}" >> "${MP_FAKE_PIP_LOG}"
@@ -56,6 +58,8 @@ export CONFIG_DIR="${TMP_DIR}/config"
export MOVIEPILOT_AUTO_UPDATE=false
export PIP_PROXY="https://mirror.example/simple"
export PROXY_HOST="http://proxy.example:7890"
export NO_PROXY="custom.internal,127.0.0.1"
unset no_proxy
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR
source "${UPDATE_FUNCS}" >/dev/null
@@ -63,6 +67,9 @@ source "${UPDATE_FUNCS}" >/dev/null
test_connectivity_pip 0
assert_contains "argv=install -i https://mirror.example/simple pip-hello-world" "${MP_FAKE_PIP_LOG}"
assert_contains "HTTPS_PROXY=http://proxy.example:7890" "${MP_FAKE_PIP_LOG}"
assert_contains "NO_PROXY=custom.internal,127.0.0.1,localhost,::1,0.0.0.0,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16" "${MP_FAKE_PIP_LOG}"
assert_contains "host.docker.internal,host.containers.internal,gateway.docker.internal" "${MP_FAKE_PIP_LOG}"
assert_contains "no_proxy=custom.internal,127.0.0.1,localhost,::1,0.0.0.0,10.0.0.0/8,100.64.0.0/10" "${MP_FAKE_PIP_LOG}"
assert_contains "PACKAGE_CACHE_ROOT=${TMP_DIR}/config/.cache" "${MP_FAKE_PIP_LOG}"
assert_contains "PIP_CACHE_DIR=${TMP_DIR}/config/.cache/pip" "${MP_FAKE_PIP_LOG}"
assert_contains "UV_CACHE_DIR=${TMP_DIR}/config/.cache/uv" "${MP_FAKE_PIP_LOG}"
@@ -97,6 +104,7 @@ export MP_FAKE_PIP_LOG
export UV_CACHE_DIR="${TMP_DIR}/explicit-uv-cache"
export PIP_PROXY="https://mirror.example/simple"
export PROXY_HOST="http://proxy.example:7890"
unset NO_PROXY no_proxy
source "${UPDATE_FUNCS}" >/dev/null
test_connectivity_pip 0
)
@@ -113,7 +121,7 @@ export MP_FAKE_PIP_LOG
export MOVIEPILOT_AUTO_UPDATE=false
export PIP_PROXY="https://mirror.example/simple"
export PROXY_HOST="http://proxy.example:7890"
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR HTTP_PROXY HTTPS_PROXY http_proxy https_proxy
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR HTTP_PROXY HTTPS_PROXY http_proxy https_proxy NO_PROXY no_proxy
source "${UPDATE_FUNCS}" >/dev/null
MP_FAKE_PIP_FAIL=1 test_connectivity_pip 0 && exit 1
if [[ -n "${HTTPS_PROXY:-}" || -n "${https_proxy:-}" ]]; then
@@ -157,9 +165,11 @@ export MP_FAKE_PIP_LOG MP_FAKE_PYTHON_COUNT
(
export VENV_PATH="${TMP_DIR}/venv"
export CONFIG_DIR="${TMP_DIR}/config"
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR NO_PROXY no_proxy
export PIP_PROXY=""
export PROXY_HOST="http://proxy.example:7890"
export NO_PROXY="service.local"
export no_proxy="extra.lan"
source "${ENTRYPOINT_FUNCS}"
apply_package_cache_env
ensure_backend_runtime_dependencies
@@ -167,6 +177,8 @@ export MP_FAKE_PIP_LOG MP_FAKE_PYTHON_COUNT
assert_contains "argv=install -r /app/requirements.txt" "${MP_FAKE_PIP_LOG}"
assert_contains "HTTPS_PROXY=http://proxy.example:7890" "${MP_FAKE_PIP_LOG}"
assert_contains "NO_PROXY=service.local,extra.lan,localhost,127.0.0.1,::1,0.0.0.0,10.0.0.0/8" "${MP_FAKE_PIP_LOG}"
assert_contains "no_proxy=service.local,extra.lan,localhost,127.0.0.1,::1,0.0.0.0,10.0.0.0/8" "${MP_FAKE_PIP_LOG}"
assert_contains "PACKAGE_CACHE_ROOT=${TMP_DIR}/config/.cache" "${MP_FAKE_PIP_LOG}"
assert_contains "PIP_CACHE_DIR=${TMP_DIR}/config/.cache/pip" "${MP_FAKE_PIP_LOG}"
assert_contains "UV_CACHE_DIR=${TMP_DIR}/config/.cache/uv" "${MP_FAKE_PIP_LOG}"
@@ -182,7 +194,7 @@ export MP_FAKE_PIP_LOG MP_FAKE_PYTHON_COUNT
(
export VENV_PATH="${TMP_DIR}/venv"
export CONFIG_DIR="${TMP_DIR}/config"
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR PIP_PROXY PROXY_HOST
unset PACKAGE_CACHE_ROOT PIP_CACHE_DIR UV_CACHE_DIR PIP_PROXY PROXY_HOST NO_PROXY no_proxy
source "${ENTRYPOINT_FUNCS}"
load_config_from_app_env
apply_package_cache_env

View File

@@ -0,0 +1,81 @@
from __future__ import annotations
import os
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
def _read_shell_prefix(path: Path, stop_marker: str) -> str:
"""
读取 Docker 脚本中可独立执行的函数定义前缀。
"""
lines = []
for line in path.read_text(encoding="utf-8").splitlines():
if line.startswith(stop_marker):
break
lines.append(line)
return "\n".join(lines)
@pytest.mark.parametrize(
("script_path", "stop_marker"),
[
(ROOT / "docker" / "entrypoint.sh", "# 环境变量补全"),
(ROOT / "docker" / "update.sh", "# 下载及解压"),
],
)
def test_docker_proxy_env_adds_default_no_proxy_ranges(
tmp_path: Path, script_path: Path, stop_marker: str
) -> None:
"""
Docker 代理环境应默认绕过本机、局域网和常见容器内部地址。
"""
shell_prefix = _read_shell_prefix(script_path, stop_marker)
config_dir = tmp_path / "config"
script = f"""
set -euo pipefail
CONFIG_DIR="{config_dir}"
PROXY_HOST="http://proxy.example:7890"
NO_PROXY="custom.internal,127.0.0.1"
no_proxy="extra.lan,127.0.0.1"
{shell_prefix}
apply_package_proxy_env
printf 'HTTP_PROXY=%s\\n' "${{HTTP_PROXY:-}}"
printf 'HTTPS_PROXY=%s\\n' "${{HTTPS_PROXY:-}}"
printf 'NO_PROXY=%s\\n' "${{NO_PROXY:-}}"
printf 'no_proxy=%s\\n' "${{no_proxy:-}}"
"""
result = subprocess.run(
["bash"],
input=script,
text=True,
capture_output=True,
check=True,
env={"PATH": os.environ.get("PATH", "")},
)
output = dict(line.split("=", 1) for line in result.stdout.splitlines())
assert output["HTTP_PROXY"] == "http://proxy.example:7890"
assert output["HTTPS_PROXY"] == "http://proxy.example:7890"
assert output["NO_PROXY"] == output["no_proxy"]
assert output["NO_PROXY"].count("127.0.0.1") == 1
for item in (
"localhost",
"::1",
"10.0.0.0/8",
"100.64.0.0/10",
"172.16.0.0/12",
"192.168.0.0/16",
"host.docker.internal",
"host.containers.internal",
"gateway.docker.internal",
"custom.internal",
"extra.lan",
):
assert item in output["NO_PROXY"]