自行代管 Open Connector,保護 AI 代理程式權杖
在自己的 VPS 執行 Open Connector 驗證閘道,使用固定 image、TLS origin、OAuth callbacks 與備份,讓 AI 代理程式不持有 SaaS token。
Open Connector 為 AI 代理程式提供的功能
自行代管 Open Connector,可在 AI 代理程式與其呼叫的所有軟體即服務 (SaaS) API 之間放置單一驗證閘道,因此代理程式不會持有供應商權杖。這是由 OOMOL Lab 開發、採用 Apache 2.0 授權的開放原始碼閘道。它以單一容器執行,將狀態儲存在單一 SQLite 檔案中,並透過 HTTP 和 MCP (model context protocol) 公開供應商動作。
問題會從第二個整合開始。每個供應商都有自己的 OAuth (open authorization) 流程、自己的 refresh token 有效期限,以及自己的 scope 名稱。手動將 5 個供應商接入代理程式,代表必須撰寫 5 個重新導向處理常式、5 個憑證儲存區,以及 5 個必須在權杖到期前執行的重新整理迴圈。幾乎沒有人會撰寫這些程式碼。他們會為每項服務產生 1 個長效個人存取權杖,並將它貼入代理程式設定、環境檔案,甚至提示中。這個權杖接著會被代理程式執行的每個工具讀取,也會寫入轉錄內容;這正是 讓機密資訊離開 AI 代理程式 所描述的失敗情境。
驗證閘道會將憑證分成兩部分。閘道儲存供應商憑證並執行 OAuth 流程。代理程式取得的執行階段權杖只能用於閘道。代理程式呼叫動作時,閘道會載入已儲存的憑證,在伺服器端將其注入輸出要求,然後只回傳回應本文。代理程式永遠不會收到供應商存取權杖,因此代理程式轉錄內容外洩時,您只需撤銷 1 個執行階段權杖,而不是處理 GitHub 帳戶外洩。
目錄宣稱支援超過 1,000 個供應商和 10,000 個預建動作;這是專案自行提供的數字,無法從外部驗證。可以驗證的是其架構:每個動作使用 1 個 HTTP 端點、每個供應商使用 1 個已儲存連線,以及每個代理程式使用 1 個權杖。
為何自行代管 Open Connector,而不使用代管連接器服務
代管連接器服務會執行相同工作,並保存您連線之所有提供者的 refresh token。Google 或 GitHub 的 refresh token 是存取您郵件與儲存庫的長期金鑰,通常即使變更密碼後仍然有效。若該服務遭到入侵,您的帳戶也會遭到入侵。自行代管會將這些記錄移至您租用並管理的機器上的 SQLite,並使用永不離開您主機的金鑰加以保護。
開始前,請先明確計算成本。這部 VPS 會成為您執行的伺服器中最有價值的一部。它會在單一檔案中保存十幾項服務的有效認證,因此應按照密碼管理器主機的標準管理:防火牆僅開放 443、不使用共用登入、準備一份您確實曾還原過的備份,並在服務停止回應時發出警示。若您不會將密碼保存庫放在這部主機上,也不要將連接器放在上面。
安裝任何項目前先固定版本
Open Connector 尚屬早期專案。該儲存庫首次出現於 29 June 2026;截至 1 August 2026,最新的標記版本為 v1.3.3,發布於 30 July 2026,且同樣帶有 latest 標記。registry 也發布了 tip 標記,該標記是以 main 上的最新提交建置而成。
對於如此新的專案,可變動標記經常更新。跳過兩個版本的 docker compose pull 可能會變更代理程式所依賴的端點,導致您整晚將問題誤判為代理程式問題而進行偵錯。請將映像固定至發布標記,並在閱讀發布說明後,依您的決定進行升級。
在自己的 VPS 上以 TLS 部署 Open Connector
容器啟動前,您需要:
- 在 Ubuntu 24.04 或相近版本上安裝 Docker 與 Compose plugin
- 一個 A 記錄指向此 VPS 的主機名稱,例如
connect.example.com - 已為該主機名稱終止 TLS(傳輸層安全性)的反向代理
- 下方產生的 2 個隨機密鑰
適用於多個 Docker Compose 應用程式的 Traefik 反向代理涵蓋反向代理設定。若要瞭解單一應用程式從頭到尾的相同憑證設定流程,請參閱在 VPS 上使用 Docker 和 HTTPS 執行 n8n指南。
先產生密鑰。加密密鑰會保護儲存的憑證。管理員 token 會保護 Web 主控台及整個 /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。它與上游範例有 2 處不同,而且兩處都很重要。
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(網路位址轉換)表,因此 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,表示連接埠對應仍是上游設定,而閘道正直接回應整個網際網路。健康檢查回報 Connection refused 表示容器尚未監聽,因此請先讀取日誌,再處理反向代理。
相同服務的 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 靜態設定中的 resolver 名稱,否則路由器啟動時不會取得憑證。
為什麼 OAuth 強制要求使用實際的主機名稱
OOMOL_CONNECT_ORIGIN 是使用者最常略過的設定,而略過後會以看似供應商錯誤的方式導致 OAuth 失敗。執行階段會根據此來源建立重新導向 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,因此該 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 呼叫都會攜帶管理員權杖,因此請為 shell 工作階段匯出一次。
export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
-H "authorization: Bearer $ADMIN_TOKEN"該清單會顯示執行階段針對各提供者預期使用的重新導向 URI,因此這是最快確認來源設定已生效的方法。如果仍顯示 localhost,表示容器仍使用舊值執行,OAuth 流程會在最後一個步驟失敗。
儲存 client 憑證,然後開始授權。
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。請在瀏覽器中開啟該網址並核准範圍,提供者會將瀏覽器導回 /oauth/callback,此時執行階段會交換程式碼並儲存憑證。位於您來源的 Web 主控台也會使用表單引導您完成相同步驟,並由相同的管理員權杖保護。使用一般 API key 的提供者不需要這些步驟:使用 {"authType":"api_key","values":{"apiKey":"..."}} 執行 PUT /api/connections/<service> 會直接儲存金鑰。
為每個代理程式提供執行階段權杖,絕不提供認證資料
代理程式使用由管理 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,而提供者承載內容位於 data 下方。該回應中任何位置都不會出現 GitHub 權杖。對於 MCP 用戶端,請將其指向 https://connect.example.com/mcp,並使用相同的 bearer 標頭。閘道會提供 search_actions 和 execute_action 等探索工具,而不是每個 API 各提供一個工具,因此可縮小代理程式的工具清單。在 VPS 上執行 MCP 伺服器涵蓋該連線設定的用戶端部分。
在確認完成前,再執行一次檢查。刪除 authorization 標頭後,重複執行動作呼叫。該專案自己的快速入門指南會在完全不使用 bearer 的情況下呼叫 /v1。因此,如果未設定執行階段驗證,任何能連線至該連接埠的人都能執行動作。若未驗證的呼叫成功,您有兩種處理方式:設定執行階段權杖,並確認匿名呼叫現在會失敗;或在反向代理上限制 /api、/v1 和 /mcp,僅允許代理程式來源的位址。只有 /oauth/callback 必須對全世界開放,因為提供者的瀏覽器重新導向只需要這一條路徑。
將操作清單縮減至代理程式所需範圍
後方連接 1000 個提供者的閘道,對語言模型而言暴露面很大。兩項控制項可以縮小範圍。
OOMOL_CONNECT_ALLOWED_ACTIONS 接受以逗號分隔的允許清單,並支援 service.* 與 *。OOMOL_CONNECT_BLOCKED_ACTIONS 是拒絕清單,且拒絕清單優先。將允許清單設為 github.get_current_user,github.list_issues,表示拒絕所有其他操作,不論代理程式要求什麼。這是錯誤與事件之間的差異。執行階段權杖在全域規則之上,還有各自的操作規則;其 allowedProxies 清單預設為空,因此在授予權限前,POST /v1/proxy/:service 會遭拒。該 proxy endpoint 會將原始要求轉送至提供者,並附加您的認證資訊,因此除非某個特定代理程式需要,否則請保持空白。
OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK 預設為 false。這會阻止自行代管的提供者連線指向私有位址,例如 169.254.169.254 上的雲端中繼資料服務,或同一網路上的資料庫。請保持關閉。只有在自行代管某個提供者時,才將其開啟。
備份存放所有權杖的主機
有兩項內容很重要,缺一不可。connector-data volume 中的 /app/data/connect.sqlite database 儲存封存的認證資料。.env 中的 encryption key 可解封這些資料。只有 volume 的備份沒有 key,還原時無法取得任何資料;只有 key 沒有 volume,也無法取得任何資料。因此,key 應存放在 password manager 中,而 volume 應納入一般備份輪替。
複製 SQLite file 時,請先停止 container。因為在寫入期間建立的複本,還原後可能成為損毀的 database。
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 connectorvolume 名稱是您的 project directory 加上 _connector-data,因此才需要第一個 command:將實際名稱貼到第三個 command 中。使用從 VPS 備份的 restic將 archive 傳離 VPS。該工具會在 archive 離開前先加密,因為該 archive 就是認證資料儲存區。
runtime 預設會將最近 5,000 次 action run 保留為 audit record,讓 console 能指出哪個 agent 在何時執行了哪些操作。agent 行為異常時,應先讀取這份 log。也請將 Uptime Kuma status page 指向 https://connect.example.com/health。gateway 停止回應時,agent 會以難以判讀的方式失敗;確認 gateway 已停止運作,可省下一小時閱讀 agent output 的時間。
發生的問題與您會看到的訊息
供應商端出現 redirect_uri_mismatch。 來源與已註冊的回呼 URL 不一致。將 /api/oauth/configs 的完整字串與供應商的應用程式設定進行比對,包括 https 是否與 http 一致,以及結尾是否有斜線。
每次 /api 呼叫都回傳 401。 管理員 token 標頭遺失或拼寫錯誤。標頭是 Authorization: Bearer <token>,Web 主控台也要求相同的 token。
容器正在執行,而認證資料以純文字儲存。 這表示 OOMOL_CONNECT_ENCRYPTION_KEY 未傳入容器,因為執行環境會以未加密方式儲存認證記錄,而不是拒絕啟動。您可以在自己的安裝環境中驗證:使用可辨識的 API key 連線至供應商,然後在資料庫中搜尋該 key。
docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite計數大於 0 表示該 key 未生效,因此請確認 .env 與 compose.yaml 位於同一個目錄,並確認 docker compose config 顯示該值。設定 key 後,相同的搜尋會回傳 0,因為該記錄已使用 AES-256-GCM 加密(進階加密標準、256 位元金鑰、Galois/Counter 模式)。
還原後沒有任何資料可以解密。 加密金鑰已變更或遺失。依設計,金鑰絕不會寫入資料旁,因此沒有復原途徑,也沒有任何支援請求可以解決此問題。請重新連線每個供應商。執行環境支援透過個別金鑰變數與資料命令進行金鑰輪替,因此在輪替前請先閱讀目前版本的發行說明。
代理程式顯示目錄中存在某個動作,卻回報該動作的錯誤。 探索與執行是分開的。動作可以出現在 search_actions 中,但仍可能被 OOMOL_CONNECT_ALLOWED_ACTIONS、拒絕清單或該執行環境 token 自身的規則拒絕。
升級。 備份磁碟區,將映像標籤編輯為新版本,然後執行 docker compose pull && docker compose up -d。監看 docker compose logs -n 50 connector 是否出現移轉訊息,並重新執行健康檢查與一項實際動作,確認正常後再使用。回復舊版本表示將舊標籤填回去;這只有在您固定使用該標籤時才有效。
FAQ
我需要公開網域才能自行代管 Open Connector 嗎?
對於使用 API key 的 provider,不需要:127.0.0.1 上的 gateway 即已足夠。對於 OAuth,實務上需要。provider 會將瀏覽器重新導向至您的 callback URL,因此該 URL 必須能從公用網際網路解析,且 provider 不接受 localhost 以外的純 http://。首次啟動前,將 OOMOL_CONNECT_ORIGIN 設為您的 https:// hostname,並在 provider 的 OAuth app 中註冊 <origin>/oauth/callback。
如果遺失 Open Connector encryption key,會發生什麼事?
儲存的憑證將無法解密,而且沒有復原方式。該 key 刻意不與資料一同儲存,因此持有資料庫的任何人都無法讀取資料,包括您自己。您唯一的選項是設定新的 key,並重新連線每個 provider。請將 key 儲存在 password manager 中,並將資料庫納入 backup rotation,因為還原時兩者缺一不可。
我的 AI agent 能看到 provider access token 嗎?
透過 gateway 呼叫時不能。agent 會使用以 oct_ 開頭的 runtime token 進行驗證,gateway 會在 server 上將 provider credential 注入 outbound request,並只回傳回應。有兩種情況會破壞這項隔離:/v1/proxy/:service endpoint 會轉送附帶您 credential 的原始 request,其 grants 預設為空是有原因的;此外,若您自行將 API key 貼到 agent 中,也會完全略過 gateway。
gateway 應該能從公用網際網路連線嗎?
只有 /oauth/callback 必須能連線。請在 127.0.0.1 上發布 container port,讓 Docker 的 NAT rules 無法讓它穿過您的 firewall 對外暴露,並在前方配置 reverse proxy。接著,在不帶 authorization header 的情況下測試一次 action call。若成功,請在 proxy 上將 /api、/v1 和 /mcp 限制為 agent 使用的位址,直到只有通過驗證的呼叫能夠運作。
Open Connector 已可用於 production 嗎?
它採用 Apache 2.0 授權,且開發快速:repository 於 29 June 2026 出現,v1.3.3 於 30 July 2026 發布,因此請將本指南中的每個 version number 視為 1 August 2026 的快照。請固定使用 release tag 執行,絕不要使用 latest 或 tip;每次升級前閱讀 release notes,並保留一份曾成功還原過的 volume backup。對於您擁有的主機,這項設計可靠;風險在於版本快速變動,而不是架構。