Day 18 已把 Foundry、Search 與 Key Vault 的權限分開,Day 19 也讓舊 secret version 失效。現在先做一個尚未在 V2 雲端執行的假設:如果大魔術熊貓工程司替 Foundry resource 建立 Private Endpoint,而且 Portal 顯示 Approved,整條 Agent 系統是否就已經私有?這條雲端路徑目前是 PENDING-CLOUD;本文沒有 portal capture,也沒有把假設寫成已取得的證據。
答案要看你問的是哪一條路。Foundry inbound、Foundry 到 PaaS dependency、Agent 到工具的 egress,各有自己的 DNS、public network access(PNA)、身分與路由政策。Private Endpoint 的綠燈只證明那一個 connection 被核准,不是封包拿到「良民證」,可以一路免檢查。
1. Client/BFF → Foundry endpoint (inbound)
2. Foundry/Agent platform → Search/KV/Storage(PaaS dependencies)
3. Agent runtime → private tool/approved API (runtime egress)
需要一起驗:
DNS 回 private IP 只證明名稱解析;TCP 443、TLS、authentication 與 DataActions 仍要各自測。Microsoft 的 troubleshooting 也提醒,403 通常是 authentication/RBAC,而不是 Private Link 自己壞掉。Configure network isolation(查閱:2026-08-03)
Foundry resource 自己有 Private Endpoint,不會替 Search、Storage、Cosmos DB、Key Vault 與 monitoring 自動建好相同路徑。Standard Agent setup 的客戶自管資源要逐項建立 private path、DNS 與 data role:
| Dependency | 要保存的網路證據 | 身分/資料控制 |
|---|---|---|
| Azure AI Search | PE、private DNS、PNA deny | data role + Day 15 tenant filter |
| Key Vault | PE、privatelink.vaultcore.azure.net 解析、PNA deny |
Secrets User + Day 19 rotation |
| Storage | 對應 blob/file PE 與 DNS | container/object data role |
| Cosmos DB | 實際採用 API 的 private path | Agent state 與應用 tenant 分區 |
| Application Insights | 實際 topology 支援的 private path | export 前 redaction、reader RBAC |
這裡要把 inbound 與 Agent outbound 分開讀。Inbound Private Endpoint 會依實際 setup
有不同支援路徑;涉及 customer-owned Search/Storage/Cosmos DB 與 Agent outbound
isolation 的文件,目前指向 Standard setup + private networking。Basic 使用
Microsoft-managed backing resources,不能拿它當成「我已私接自己的 Search」的
證據。另一方面,另一份 isolation 文件仍列出 Basic private-network template;因此
文章不把一句 setup 標籤推廣成整套保證,而是保存實際 template、runtime、dependency
ownership,並逐條測 packet path。若 Search indexer 也要穿越 Private Endpoint,還要
檢查它的 execution environment;否則可能出現 indexer 沒資料、Agent 查詢卻沒有
明顯錯誤的 silent failure。
即使 Azure dependencies 都走 Private Link,poisoned tool description 仍可能誘導 Agent 呼叫未核准 MCP server 或 Internet endpoint。網路可達不代表工具可用,工具可用也不代表這次 action 有權執行。
正式系統需要同時保存網路與授權證據,但 V2 Lab 沒有假裝用一個物件包辦全部。NetworkBoundaryPolicy.validate() 只檢查:
allowlisted host
+ bare HTTPS origin(僅 host,可省略 /,port 只能是 443)
+ trusted egress-allow outcome
+ identity-auth posture
+ PNA/private-DNS posture
Day 18 的 RBAC 會在前一層檢查 principal + plane + action + scope。網路 policy 刻意只接受 bare origin,任何 path、query 或 fragment 都回 EGRESS_ENDPOINT_DENIED;真正的 API path 與 object authorization 必須由可信 adapter/PEP 處理。這個設計比較保守,卻不能被寫成 path-level egress authorization,因為程式碼裡沒有那個能力。
畫面判讀目標: 看見相同 principal/RBAC 下 public route 如何因缺少 network checks 取得 Receipt。

本機 route policy 綠燈不能冒充封包已走私網。 可觀察狀態:before Receipt=1,after Receipt=0;沒有 socket 或 DNS lookup。 Claim boundary:沒有真實 network IO、Private Endpoint 或 Azure service receipt。
從完整應用的上層看,這一天仍只是很普通的需求:「幫我查一下國內出差住宿費的上限。」使用者不會說 dependency=search、egress_allowed=true,更不會替 DNS 與 PNA 宣布合格。本機 stage 為了單獨量網路邊界,不呼叫模型,而是直接把同一個 dependency intent 與不同 route posture 交給 policy。
V2 Lab 只使用 .invalid endpoint,而且不開 socket。DependencyRoute 是合成的 route posture;其中 egress_allowed 與 identity_auth 是由測試框架注入的狀態,不是封包、Firewall 或 Entra 自己出具的證明。正式 adapter 必須由 trusted configuration 與真實檢查填入,不能接受 prompt 或 request body 自己填 true。
三個主要 fixture:
compliant:所有欄位正確,作為正向分母。public:在 component matrix 中同時使用未核准 host、PNA 開啟、private DNS 未解析、identity auth 關閉。blocked_egress:host、bare origin、identity 與 private posture 都正確,只把 egress_allowed 改成 false。接著逐欄 mutation:
before/after cumulative replay 的 principal 已通過 Day 16 workload identity,也具備
Day 18 Search reader fixture;這一組 public_route 特別把 identity_auth=True 固定住,
只比較 network delta。before 回 LEGACY_NETWORK_ROUTE_UNCHECKED 並留下一張 local
executor Receipt;after 的第一個 fail-closed reason 是DEPENDENCY_ORIGIN_MISMATCH。這不是 component matrix 的EGRESS_HOST_DENIED:前者表示 trusted registry 已先判定「這個 dependency 不該去
這個 origin」,後者才是較底層 allowlisted-host preflight。PNA、private DNS、identity
與 host 的各欄 failure 仍在 component matrix 個別保存,不要把兩組資料混成同一列。
D20-A03 再做一個比較刁鑽的交換:Search 與 Vault 的兩個 host 都在網路 allowlist,
卻把 Search action 指向 Vault origin。純 host allowlist 會放過,TrustedDependencyRegistry 仍依 dependency、origin、plane、action、scope 與 auth mode
的完整 tuple 回 DEPENDENCY_ORIGIN_MISMATCH。兩個都合法,不代表可以互換座位。
這個對照很重要。若把 RBAC 也同時拿掉,receipt 歸零可能只是 403,並不能證明網路政策有工作。
畫面判讀目標: 確認 dependency、plane、action、scope、identity 與 origin 綁在同一 transaction。

網路可達、工具可用與本次 action 有權是三個問題。 可觀察狀態:wrong fields 各有固定 deny;compliant private route 保留 allow positive control。 Claim boundary:registry 不觀察 DNS、socket、TLS 或 service-side authorization。
TrustedDependencyRegistry 是 server-owned configuration。它從已註冊的功能路徑派生 dependency,不接受 prompt 自報dependency=search。它先把 dependency 綁到核准的 origin、data plane、action、
scope 與 authentication mode;之後才交給 Day 18 RBAC 與NetworkBoundaryPolicy。順序是刻意的:先擋 confused deputy,再處理「這條路本身
是否符合 private/egress posture」。
repository 內的 API 是 NetworkBoundaryPolicy.validate():成功回空 list,失敗則回一個或多個固定 reason code。
from magic_panda_agent.network import (
DependencyRoute,
NetworkBoundaryPolicy,
)
policy = NetworkBoundaryPolicy(
allowed_hosts=frozenset({"search.private.example.invalid"}),
require_private_path=True,
)
route = DependencyRoute(
name="search",
endpoint="https://search.private.example.invalid",
public_network_access=False,
private_dns_resolved=True,
egress_allowed=True,
identity_auth=True,
)
failures = policy.validate(route)
assert failures == []
目前實作只會回下列 failure code:
EGRESS_HOST_DENIED
EGRESS_ENDPOINT_DENIED
EGRESS_POLICY_DENIED
IDENTITY_AUTH_REQUIRED
PUBLIC_NETWORK_ACCESS_ENABLED
PRIVATE_DNS_NOT_RESOLVED
bare-origin contract 接受 https://host、尾端 /,以及顯式 :443 的兩種形式;scheme 大小寫與 host 大小寫會正規化。HTTP、FTP、userinfo、非 443 port、path、query、fragment、前後空白與 malformed URL 全部得到 EGRESS_ENDPOINT_DENIED。這份 contract 沒有真的查 DNS,也沒有打開 socket。
截至 2026-08-03,Foundry managed virtual network 提供 allow Internet outbound 與 allow only approved outbound 等模式,也能從 managed network 建立到 Search、Key Vault、Storage 等支援服務的 managed Private Endpoint。官方文件目前說明這些 managed PE 需使用 CLI 建立,Portal UI 尚未提供;Foundry resource managed identity 還要具備建立/核准 connection 所需權限。Managed virtual network(查閱:2026-08-03)
這裡有幾個會影響成本與復原的限制:
networkInjections 等屬性要在 resource 建立時設定,不能事後補上。所以 network mode 不是部署後再按一個「更安全」按鈕。它會影響 resource 拓撲、費用、觀測能力與重建策略,應在建立 Foundry resource 前留下 decision record。
Microsoft 的 GA overview 將 Foundry core platform、RBAC 與 virtual network integration 列入 GA enterprise capabilities,但同一份 readiness 表仍將 Tracing VNet 標為 Preview,並提醒不是所有 GA 功能都完整支援 network isolation。Foundry GA overview(查閱:2026-08-03)
此外,network isolation 文件仍以 hosted (preview) agents 描述部分 outbound injection 路徑;hosting 總覽把 managed Hosted Agents 標為 GA,Agent Framework 的 Hosted Agents 子頁卻仍標 Preview,部分 session/API 也要求 preview surface。第一方文件的粒度與更新節奏沒有完全對齊,文章因此不寫「Foundry networking 全面 GA」,而是逐項記錄:
產品母項的 GA 標籤不能替子路徑升級;同樣地,某個 hosted integration 是 Preview,也不代表所有 Foundry RBAC 或 Private Link 都是 Preview。
畫面判讀目標: 辨認只允許 bare HTTPS origin 的 endpoint contract。

只接受 registry 派生的 bare origin,redirect 每一跳仍需重驗。 可觀察狀態:前四筆皆同時回 EGRESS_HOST_DENIED 與 EGRESS_ENDPOINT_DENIED,bare_https_origin 的 failure_codes 為空且 allowed=true。 Claim boundary:URL validation 不防 DNS rebinding、redirect 或實際 egress routing。
進入 day20/ 後執行:
uv run pytest tests/stages/day20/test_acceptance.py -q
V2 acceptance 必須確認:
validate() 回傳空 list。plain_http、credential_in_origin、non_default_port、path_suffix 與 bare_https_origin 五筆;前四筆同時回 EGRESS_HOST_DENIED/EGRESS_ENDPOINT_DENIED,最後一筆 failure list 為空且 allow。egress_allowed=False 單獨得到 EGRESS_POLICY_DENIED。DEPENDENCY_ORIGIN_MISMATCH。.invalid,network_io_performed=false。本次 Day 20 acceptance 為 5 passed。cumulative_stage_replay() 的 outcomes 合計
兩筆 Receipt:before public route 一筆、after compliant route 一筆;全部side_effect_count=0。external_calls 與 cloud_calls 都是 null,因為這個
preflight 沒有 network observer,不能用「程式裡沒寫 socket」冒充量測到零;blocker
是 REAL_NETWORK_PATH_NOT_EXERCISED。
public-route 與 registry 兩張 UI 各自 fresh-run 一個 deterministic helper,不共用或拼接 capture:
PYTHONPATH=src:. uv run python -c \
'import json; from magic_panda_agent.stages.day20 import cumulative_stage_replay; print(json.dumps(cumulative_stage_replay(), sort_keys=True))'
PYTHONPATH=src:. uv run python -c \
'import json; from magic_panda_agent.stages.day20 import dependency_registry_evidence; print(json.dumps(dependency_registry_evidence(), sort_keys=True))'
修補/test UI 是 endpoint-shape matrix:HTTP、userinfo、非 443 port 與 path suffix 都同時回EGRESS_HOST_DENIED/EGRESS_ENDPOINT_DENIED,bare HTTPS origin 才能通過。這份 structured capture 本來就沒有 tool
call,所以 Receipt 與 side effect 都是 0;external/cloud call 仍是未量測的 null:
PYTHONPATH=src:. uv run python -c \
'import json; from magic_panda_agent.stages.day20 import endpoint_shape_evidence; print(json.dumps(endpoint_shape_evidence(), sort_keys=True))'
這三個 helper command 各自只重現 disclosure-safe JSON;正式發布時,本篇 required app UI 圖
必須由目前相關原始碼經 repository UI capture pipeline 產生,schema 3 manifest 必須以 source-tree SHA-256
與檔案數綁定輸入並交由 strict verifier 重算。正式圖仍只支持 deterministic preflight;沒有 socket/DNS
observer、Private Endpoint receipt 或其他 Foundry cloud evidence。
本篇尚未在 V2 重建 VNet、Private Endpoint、Private DNS、managed VNet、Firewall 或 Standard Agent setup。以下案例都仍是 PENDING-CLOUD:
inside VNet:服務 hostname → private IP;443 可達;正確 identity 成功
outside VNet:public endpoint 被拒絕
remove PE/DNS link:dependency fail closed,不走 public fallback
unapproved FQDN/non-bare endpoint:留下 egress deny evidence
wrong identity:網路可達,但服務端回 401/403
Search/KV/Storage:每個 dependency 分別完成同一組測試
本機 NetworkBoundaryPolicy.validate() 只證明 deterministic preflight logic,不能替代 DNS query、socket、Private Endpoint connection、Firewall log 或 packet/flow evidence。
Private Endpoint 不會阻止合法 identity 被濫用,也不會修正錯誤的 Search tenant filter。已核准 MCP server 仍可能被供應鏈攻擊;redirect 也可能把第一次通過 allowlist 的 request 帶往另一個 origin。正式 adapter 應對 redirect fail closed 或重新授權每一跳,並由 trusted tool registry 派生 dependency/operation。
allow-only-approved outbound 會增加 Firewall、Private Link、DNS 與離線供應鏈的成本。完全關閉 Internet 也可能讓套件下載、映像更新或 incident-response 工具失效;要事前準備 private registry、更新流程與 break-glass,而不是事故時臨時把 PNA 打開。
NIST SP 800-207 不把網路位置當成信任。private IP 只能證明一部分路徑;正確/錯誤 identity、目標 resource、operation 與 401/403 仍要一起測。
明天會在這些確定性邊界前面加入 Prompt Shields。它可以提供 attack signal,卻不會替 identity、RBAC、tenant filter 或 egress policy 做授權決定。