SSD Nodes Learn 8GB 記憶體 — 每年 $66
指南 Matt Connor作者: Matt Connor

自行代管 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://。這就是此部署需要主機名稱和憑證的完整原因。請在第一次啟動前設定來源,因為系統會在啟動時讀取此值:編輯 .envcompose.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_actionsexecute_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 connector

volume 名稱是您的 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 未生效,因此請確認 .envcompose.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 執行,絕不要使用 latesttip;每次升級前閱讀 release notes,並保留一份曾成功還原過的 volume backup。對於您擁有的主機,這項設計可靠;風險在於版本快速變動,而不是架構。