如何自架 Open Connector 提升 AI Agent 安全性
透過自架 Open Connector 驗證閘道,將 SaaS API 權杖保留在個人 VPS,避免 AI Agent 洩漏機密。本文說明如何設定 OAuth 回呼、TLS 來源與 SQLite 資料庫備份,確保您的憑證絕不離開私有環境。
Open Connector 對 AI Agent 的作用
自架 Open Connector 可在 AI Agent 與其呼叫的所有軟體即服務 (SaaS) API 之間建立單一驗證閘道,確保 Agent 不會持有任何供應商的存取權杖 (token)。這是由 OOMOL Lab 開發的開源閘道,採用 Apache 2.0 授權。它以單一容器執行,狀態儲存於單一 SQLite 檔案中,並透過 HTTP 與 MCP (Model Context Protocol) 暴露供應商的動作。
整合第二個服務時,問題便隨之而來。每個供應商都有各自的 OAuth (Open Authorization) 流程、權杖更新週期與權限範圍名稱。若手動將五個供應商整合至 Agent,意味著必須實作五個重新導向處理器、五個憑證儲存庫,以及五個在權杖過期前執行的更新迴圈。幾乎沒人會撰寫這些程式碼。開發者通常會為每個服務產生一個長期有效的個人存取權杖 (PAT),並將其貼入 Agent 設定檔、環境變數檔案或提示詞 (prompt) 中。該權杖隨後會被 Agent 執行的所有工具讀取,並出現在對話紀錄中,這正是 如何避免 AI Agent 洩漏機密 一文所描述的失敗案例。
驗證閘道將憑證一分為二。閘道負責儲存供應商憑證並執行 OAuth 流程。Agent 僅取得一個僅對該閘道有效的執行期權杖。當 Agent 呼叫某個動作時,閘道會載入儲存的憑證,在伺服器端將其注入外發請求,並僅回傳回應主體。Agent 從未接收過供應商的存取權杖,因此即便 Agent 的對話紀錄外洩,您損失的僅是一個可撤銷的執行期權杖,而非您的 GitHub 帳號。
該目錄宣稱擁有超過 1,000 個供應商與 10,000 個預先建構的動作,這是專案方的數據,無法從外部驗證。您可以驗證的是其架構:每個動作對應一個 HTTP 端點、每個供應商對應一個儲存的連線、每個 Agent 對應一個權杖。若您對 Agent 領域尚感陌生,且「工具呼叫」(tool call) 或「MCP 伺服器」等術語尚未定型,建議參考 如何從零開始學習 AI Agent 中的階段性路徑,逐步建立此類閘道所預設的迴圈、工具與安全習慣。
為何選擇自架 Open Connector 而非使用託管連接器服務
託管連接器服務執行相同的工作,並持有您所連接的每個提供者的重新整理權杖(refresh tokens)。Google 或 GitHub 的重新整理權杖是通往您郵件與儲存庫的長效金鑰,且通常在變更密碼後依然有效。一旦該服務遭入侵,您也將隨之受害。自架則將這些紀錄移至您租用並管理的機器上的 SQLite 資料庫,並以絕不離開該機器的金鑰進行封裝。
在開始之前,請先評估其成本。此 VPS 將成為您運作中最具價值的伺服器。它在單一檔案中保存了十幾個服務的有效憑證,因此必須給予如同密碼管理員主機般的對待:防火牆僅開放 443 埠、禁止共用登入帳號、執行過實際還原測試的備份,並在服務停止回應時發出警報。如果您不會將密碼庫放在這台機器上,也請勿將連接器部署於此。
在安裝任何軟體前先鎖定版本
Open Connector 尚處於早期階段。該儲存庫於 2026 年 6 月 29 日首次出現,截至 2026 年 8 月 1 日,最新的標記版本為 v1.3.3,於 2026 年 7 月 30 日發布,並帶有 latest 標籤。登錄檔同時發布了 tip 標籤,該標籤是基於 main 上的最新提交所建置。
在如此新的專案中,變動標籤(moving tags)更新頻繁。一個跳過兩個版本的 docker compose pull 可能會變更您的代理程式所依賴的端點,這將導致您花費整個晚上將其除錯為代理程式問題。請將映像檔鎖定在特定的發布標籤上,並在閱讀發布說明後,由您自行決定升級時間。
在您的 VPS 上部署 TLS 後端的 Open Connector
在容器啟動前,您需要準備:
- 安裝 Docker 與 Compose 外掛程式的 Ubuntu 24.04 或相容系統
- 一個 A 記錄指向此 VPS 的主機名稱,例如
connect.example.com - 一個已針對該主機名稱處理 TLS (transport layer security) 終止的反向代理
- 兩個隨機產生的密鑰(產生方式如下)
適用於多個 Docker Compose 應用的 Traefik 反向代理 一文涵蓋了代理端的設定。關於單一應用的完整憑證配置流程,請參考 在 VPS 上使用 Docker 與 HTTPS 部署 n8n 指南。
請先產生密鑰。加密金鑰用於保護儲存的憑證,管理員權杖則用於保護網頁控制台與整個 /api 介面。兩者皆無預設值,若未設定,執行環境仍會啟動,但這是不安全的。
mkdir -p ~/open-connector && cd ~/open-connector
umask 077
printf 'OOMOL_CONNECT_ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" > .env
printf 'OOMOL_CONNECT_ADMIN_TOKEN=%s\n' "$(openssl rand -base64 32)" >> .env
chmod 600 .env請在首次啟動前,將這兩個數值複製到您的密碼管理員中。加密金鑰無法復原,原因請參閱下方的失敗清單。
現在設定 compose.yaml。它與上游範例有兩處不同,且兩者皆至關重要。
services:
connector:
image: ghcr.io/oomol-lab/open-connector:v1.3.3
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- connector-data:/app/data
environment:
OOMOL_CONNECT_DATA_DIR: /app/data
OOMOL_CONNECT_ORIGIN: "https://connect.example.com"
OOMOL_CONNECT_ENCRYPTION_KEY: "${OOMOL_CONNECT_ENCRYPTION_KEY:?set this in .env}"
OOMOL_CONNECT_ADMIN_TOKEN: "${OOMOL_CONNECT_ADMIN_TOKEN:?set this in .env}"
volumes:
connector-data:第一個變更是使用固定標籤而非 latest。第二個是連接埠設定。上游檔案發布了 3000:3000,這會綁定主機上的所有介面。Docker 會在 ufw 過濾鏈接收封包前,將發布的連接埠寫入 NAT (network address translation) 表,因此 ufw deny 3000 無法關閉該連接埠,此陷阱詳述於 為何 Docker 連接埠會繞過 ufw。設定 127.0.0.1:3000:3000 僅會發布在 loopback 介面上,而您的反向代理將從同一台主機進行連線。
:? 將每個變數標記為必要項目,因此當 .env 遺失時,堆疊會拒絕啟動,而非在憑證未加密的狀態下執行。將數值保留在 .env 而非 compose 檔案中,是遵循 Docker Compose 環境檔案與密鑰 的最佳實踐。
docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000一旦執行環境啟動,/health 會回應 { "ok": true }。ss 必須輸出 127.0.0.1:3000。若出現 0.0.0.0:3000,代表連接埠對應仍為上游預設值,且閘道正直接對網際網路開放。若健康檢查顯示連線被拒,代表容器尚未開始監聽,請在調整代理前先閱讀日誌。
相同服務的 Traefik 標籤
labels:
- "traefik.enable=true"
- "traefik.http.routers.connector.rule=Host(`connect.example.com`)"
- "traefik.http.routers.connector.entrypoints=websecure"
- "traefik.http.routers.connector.tls.certresolver=le"
- "traefik.http.services.connector.loadbalancer.server.port=3000"當 Traefik 與此服務運行於同一台主機的 Docker 中時,請將此服務加入 Traefik 網路並刪除 ports: 區塊,因為 Traefik 可透過內部網路存取容器,無需對主機發布任何連接埠。certresolver=le 必須與您 Traefik 靜態設定中的解析器名稱相符,否則路由器將無法取得憑證。
為何 OAuth 強制要求使用真實主機名稱
OOMOL_CONNECT_ORIGIN 是使用者最常忽略的設定,若未設定,OAuth 將會失效,且錯誤訊息看起來就像是供應商端的臭蟲。執行時期會根據該來源(origin)建構重新導向 URI,格式為 <origin>/oauth/callback。若未設定,來源預設為 http://localhost:3000,因此執行時期會向供應商發送 http://localhost:3000/oauth/callback 作為重新導向 URI,但您的 OAuth 應用程式註冊的卻是 https://connect.example.com/oauth/callback。由於這兩個字串不符,GitHub 會回應:
The redirect_uri MUST match the registered callback URL for this application.OAuth 供應商會將瀏覽器重新導向回該 URI,這代表它必須是一個外部網路可存取的位址;此外,除了 localhost 之外,供應商會拒絕使用純 http://。這就是為何此部署需要主機名稱與憑證的根本原因。請務必在首次啟動前設定來源,因為該數值是在啟動時讀取的:編輯 .env 或 compose.yaml 後,請再次執行 docker compose up -d 以套用變更。
透過 OAuth 連接您的第一個提供者
請先在提供者端建立 OAuth 應用程式。以 GitHub 為例,路徑為 Settings,接著是 Developer settings,然後是 OAuth Apps,最後選擇 New OAuth App。將授權回呼 URL 設定為 https://connect.example.com/oauth/callback。請妥善保存 client ID 與 client secret。
每個 /api 呼叫都會攜帶管理員權杖(admin token),因此請在 shell session 中匯出一次。
export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
-H "authorization: Bearer $ADMIN_TOKEN"該列表顯示執行階段(runtime)為每個提供者所預期的重新導向 URI,這是確認您的來源設定是否生效最快的方式。若顯示的仍是 localhost,代表容器正以舊值執行,OAuth 流程將會在最後一步失敗。
儲存用戶端憑證,然後啟動授權。
curl -s -X PUT https://connect.example.com/api/oauth/configs/github \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"clientId":"...","clientSecret":"..."}'
curl -s -X POST https://connect.example.com/api/oauth/authorizations \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"service":"github"}'第二次呼叫會回傳一個 authorizationUrl。請在瀏覽器中開啟該連結並核准範圍(scopes),提供者會將瀏覽器重新導向回 /oauth/callback,執行階段會在此處交換代碼並儲存憑證。您來源端的網頁控制台會在同一個管理員權杖的保護下,透過表單引導您完成相同的步驟。使用純 API key 的提供者則會跳過這些程序:直接使用 PUT /api/connections/<service> 搭配 {"authType":"api_key","values":{"apiKey":"..."}} 即可儲存金鑰。
為每個代理程式分配執行階段權杖,切勿使用憑證
代理程式使用執行階段權杖(runtime token)向閘道進行驗證,該權杖由管理 API 產生。
curl -s -X POST https://connect.example.com/api/runtime-tokens \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"research-agent"}'回應中包含一個以 oct_ 開頭的權杖。請為每個代理程式核發一個權杖,並以該代理程式的名稱命名,因為若無法識別權杖,撤銷時將被迫撤銷所有權杖。隨後,代理程式即可透過一般 HTTP 呼叫執行動作。
curl -s -X POST https://connect.example.com/v1/actions/github.get_current_user \
-H "authorization: Bearer oct_..." \
-H 'content-type: application/json' \
-d '{"input":{}}'正常的回答是一個封裝物件,其 success 欄位為 true,而供應商的酬載(payload)位於 data 下方。GitHub 權杖不會出現在該回應的任何位置。對於 MCP 客戶端,請將其指向 https://connect.example.com/mcp 並使用相同的 bearer 標頭。閘道會提供如 search_actions 與 execute_action 等探索工具,而非為每個 API 提供單一工具,這能確保代理程式的工具清單保持精簡。在 VPS 上執行 MCP 伺服器 涵蓋了該連接設定的客戶端部分。
在宣告完成前,請再執行一次檢查。刪除 authorization 標頭後重複執行動作呼叫。該專案的快速入門指南會呼叫 /v1 且不帶任何 bearer 標頭,因此若未設定執行階段驗證,任何能存取該連接埠的人皆可執行動作。若未經驗證的呼叫成功,您有兩種解決方案:設定執行階段權杖並確認匿名呼叫現在會失敗,或者在反向代理層將 /api、/v1 與 /mcp 限制為僅允許來自您代理程式所在位址的存取。僅 /oauth/callback 必須對外開放,因為這是供應商瀏覽器重新導向所需的唯一路徑。
將動作清單縮減至代理程式所需的範圍
若閘道器後方串接了上千個供應商,這對語言模型而言是一個巨大的攻擊面。當模型開始讀取非其所撰寫的文字時,風險會進一步擴大,因為由 代理程式搜尋網頁時所回傳的 SearXNG 執行個體頁面 可能包含針對代理程式所擁有的任何動作之指令。如同 編寫程式的代理程式應採取最小可行變更 的原則,其權限也應遵循相同限制:僅授予該工作確實需要的少數動作,其餘一概不予授權。透過以下兩項控制措施可縮小權限範圍。
OOMOL_CONNECT_ALLOWED_ACTIONS 接受以逗號分隔的允許清單,並支援 service.* 與 *。OOMOL_CONNECT_BLOCKED_ACTIONS 為拒絕清單,且拒絕清單的優先權最高。將允許清單設定為 github.get_current_user,github.list_issues 意味著無論代理程式要求什麼,所有其他動作皆會被拒絕,這正是區分「操作失誤」與「資安事件」的關鍵。執行階段權杖(Runtime tokens)會在全域規則之上額外套用自身的動作規則,且其 allowedProxies 清單預設為空,因此在您授予權限前,POST /v1/proxy/:service 會被拒絕。該代理端點會將原始請求轉發給附帶您憑證的供應商,因此除非有特定代理程式需要,否則請保持留空。
OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK 預設為 false,這能防止自架的供應商連線指向私有位址,例如 169.254.169.254 上的雲端中繼資料服務,或是您位於同一網路中的資料庫。請保持關閉。僅在您自行託管的供應商上才開啟此功能。
備份儲存所有權杖的伺服器
有兩項要素至關重要,且缺一不可。位於 /app/data/connect.sqlite、掛載於 connector-data 儲存卷內的資料庫存放著已密封的憑證;而位於 .env 的加密金鑰則用於解密這些憑證。若僅備份儲存卷而無金鑰,將無法還原任何資料;反之,若僅有金鑰而無儲存卷亦同。因此,請將金鑰存入密碼管理器,並將儲存卷納入常規備份排程中。
在複製 SQLite 檔案前,請務必停止容器,因為在寫入過程中進行複製可能導致還原時資料庫損毀。
docker volume ls | grep connector-data
docker compose stop connector
docker run --rm -v open-connector_connector-data:/data -v "$PWD":/backup alpine \
tar czf /backup/connector-data.tgz -C /data .
docker compose start connector儲存卷名稱由您的專案目錄加上 _connector-data 組成,這也是為何需要執行第一個指令的原因:請將實際名稱貼入第三個指令中。請使用 從 VPS 進行 restic 備份 將封存檔傳送至 VPS 外部,該工具會在傳輸前進行加密,因為該封存檔即為憑證儲存庫。
執行環境會將近期的動作執行紀錄保留為稽核紀錄,預設為 5,000 筆,以便控制台能顯示各代理程式(agent)的執行內容與時間。當代理程式行為異常時,該日誌是首要查閱的資訊。同時,請將 Uptime Kuma 狀態頁面 指向 https://connect.example.com/health。當閘道器停止回應時,代理程式會出現令人困惑的錯誤;確認閘道器是否離線,可節省閱讀代理程式輸出內容的時間。
故障情形與錯誤訊息
redirect_uri_mismatch 於供應商端驗證失敗。 來源位址與註冊的回呼 URL 不符。請比對 /api/oauth/configs 的確切字串與供應商應用程式設定,包含 https 對照 http,並檢查結尾斜線是否一致。
所有 /api 呼叫均回傳 401。 管理員權杖標頭遺失或拼字錯誤。該標頭為 Authorization: Bearer <token>,網頁控制台亦要求相同的權杖。
容器執行中,但憑證以明文儲存。 當 OOMOL_CONNECT_ENCRYPTION_KEY 未成功傳遞至容器時會發生此情況,因為執行環境會將憑證記錄以未加密方式儲存,而非拒絕啟動。請在您的安裝環境驗證:連結一個可識別 API 金鑰的供應商,接著在資料庫中搜尋該金鑰。
docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite若搜尋結果大於 0,代表金鑰未生效。請檢查 .env 是否位於與 compose.yaml 相同的目錄,並確認 docker compose config 是否顯示該數值。當金鑰設定正確,相同的搜尋應回傳 0,因為記錄已透過 AES-256-GCM(進階加密標準,256 位元金鑰,Galois/counter 模式)加密。
還原後無法解密。 加密金鑰已變更或遺失。基於設計,金鑰絕不會與資料存放在一起,因此沒有復原路徑,也無法透過支援票證解決。請重新連結所有供應商。系統支援透過獨立的金鑰變數與執行環境中的資料指令進行輪替,請在執行任何輪替前閱讀當前版本的發行說明。
代理程式回報錯誤,指出目錄中可見的動作無法執行。 探索與執行是分開的。某個動作可能出現在 search_actions 中,但仍可能被 OOMOL_CONNECT_ALLOWED_ACTIONS、黑名單或該執行環境權杖本身的規則所拒絕。
升級。 請備份儲存卷,將映像檔標籤修改為新版本,然後執行 docker compose pull && docker compose up -d。監控 docker compose logs -n 50 connector 是否出現遷移訊息,並在重新信任系統前,執行健康檢查與一次實際動作。回滾意指將標籤改回舊版,此操作僅在您已鎖定版本時有效。
FAQ
我需要公開網域名稱才能自架 Open Connector 嗎?
若服務提供者使用 API key,則不需要:僅使用 127.0.0.1 的閘道即可。但若使用 OAuth,實務上則必須。服務提供者會將瀏覽器重新導向至您的回呼 URL,因此該 URL 必須能從公用網際網路解析,且提供者通常會拒絕 localhost 以外的純 http://。請在首次啟動前將 OOMOL_CONNECT_ORIGIN 設定為您的 https:// 主機名稱,並在服務提供者的 OAuth 應用程式中註冊 <origin>/oauth/callback。
如果遺失 Open Connector 加密金鑰會發生什麼事?
儲存的憑證將無法解密,且無法復原。該金鑰刻意不與資料儲存在一起,因此任何持有資料庫的人(包括您自己)都無法讀取內容。唯一的解決方案是設定新金鑰並重新連接所有服務提供者。請將金鑰存放在密碼管理員中,並將資料庫納入備份排程,因為還原時兩者缺一不可。
我的 AI agent 能看到服務提供者的存取權杖(access token)嗎?
透過閘道呼叫時無法看到。Agent 使用以 oct_ 開頭的執行階段權杖進行驗證,閘道會在伺服器端將服務提供者的憑證注入外送請求,並僅回傳回應。有兩種情況會破壞此安全性:使用 /v1/proxy/:service 端點(它會轉發附帶您憑證的原始請求,且其授權預設為空是有原因的),以及您自行將 API key 貼入 agent(這會完全繞過閘道)。
閘道應該要能從公用網際網路存取嗎?
只有 /oauth/callback 需要。請將容器連接埠發佈在 127.0.0.1,確保 Docker 的 NAT 規則不會將其暴露在防火牆之外,並在前方架設反向代理。接著在沒有 authorization 標頭的情況下測試一次動作呼叫。若測試成功,請在代理層將 /api、/v1 與 /mcp 限制為僅允許您的 agent 使用的位址,直到確保只有經過驗證的呼叫才能運作。
Open Connector 適合用於生產環境嗎?
它採用 Apache 2.0 授權且更新頻繁:儲存庫於 2026 年 6 月 29 日出現,v1.3.3 於 2026 年 7 月 30 日發佈,因此請將本指南中的每個版本號視為 2026 年 8 月 1 日的快照。請鎖定特定發行標籤(release tag)執行,切勿使用 latest 或 tip,升級前請詳閱發行說明,並保留一份您已實際測試還原過的儲存卷備份。其架構設計對於您擁有的伺服器而言是穩健的,風險在於版本變動頻繁,而非架構本身。