VPS Linux systemd IMDS 雲端安全 SSRF

systemd-imdsd 把 169.254.169.254 收進本地 Varlink broker:systemd 261 讓雲端 IMDS 的 SSRF 攻擊面終於能用一條 kernel 參數堵掉

systemd 261 在 2026 年 6 月釋出,新增的 systemd-imdsd 把每家雲端供應商的 IMDS 169.254.169.254 收進一個本地 Varlink broker,應用程式不再需要直接跟 metadata endpoint 說話。更關鍵的是 systemd.imds.network=locked 這條 kernel 參數會在 routing table 加一條 prohibit route,SSRF 攻擊偷 cloud credential 這條走了十年的老路終於有辦法在 kernel 層堵掉。

systemd 261 在 2026 年 6 月 19 日釋出,一次塞進三個新 subsystem:systemd-sysinstall 這套文字模式 OS 安裝器、storagectl 這個 Varlink 版的儲存管理介面,還有這篇要拆解的 systemd-imdsd。前兩個偏向 image-based 發行版的 tooling 整併,影響範圍比較集中;systemd-imdsd 影響的是任何跑在公有雲或自建私有雲上的 Linux VPS,而且它要處理的是一條存在了十年、每個 cloud 用戶都知道但一直沒人動的攻擊面——169.254.169.254 這個 link-local IMDS endpoint。

這個 IP 位址現在幾乎所有公有雲都有:AWS、Azure、GCP、Oracle、Hetzner、Scaleway、Vultr、阿里雲、Tencent Cloud 全部都用它當 Instance Metadata Service 的入口。問題有兩層:第一,每家雲的 protocol 細節不一樣,AWS IMDSv2 要先 PUT 拿 token、Azure 要帶 Metadata: true header、GCP 要帶 Metadata-Flavor: Google,應用程式端的 metadata library 每家都得寫一套;第二,這個 endpoint 從主機內部看沒有 authentication,任何能發 HTTP 的 process 都讀得到——包含被 SSRF 漏洞誘導去打 metadata 的 web 應用。Capital One 2019 年那次 1 億筆用戶資料外洩就是這條路,之後社群換 token 版 protocol、換 hop limit=1,但攻擊面本質沒消。systemd 261 把這件事收進 kernel/userspace 的分工重新想了一次。

169.254.169.254 這個設計一開始就是半成品

拉開來看,IMDS 這個 convention 是 2009 年 AWS EC2 第一次推出時的設計:挑一個 link-local 位址讓 instance 內部可以查自己的 instance id、region、attached IAM role、以及 user data(開機時執行的 script 內容)。走 HTTP 的理由是任何語言都寫得出 HTTP client,不用額外 SDK。走 unauthenticated 的理由是——「這個 IP 從外部打不進來,所以不需要 auth」。

這個假設在單純的運算場景裡成立。問題是現代應用程式會轉發外部來的 URL:圖片縮圖服務會抓外部 URL、webhook 功能會打外部 URL、PDF 轉檔會嵌入外部資源。只要攻擊者能讓應用程式去 fetch 一個指向 169.254.169.254 的 URL,metadata 就會回到攻擊者手上——包含 IAM role 的 temporary credential,可以用來存取整個 cloud account。

AWS 推 IMDSv2 要求 PUT 拿 session token 之後才能查,看起來堵住了這條路,實務上繞法還是有:PUT 本身在很多 SSRF 場景下也能發(HTTP library 允許),只要攻擊者能控制 method;或者透過 DNS rebinding 繞 hop limit 保護。GCP 跟 Azure 的 header-based 保護理論上更穩,但同樣仰賴應用程式層面不會意外幫你帶那個 header。

這個設計的根本問題是 metadata endpoint 的信任模型綁在「網路可達性」上,而網路可達性是應用程式控制的。要徹底修,得把「誰能查 metadata」這個決定從網路層拉到 OS 層。

systemd-imdsd 要做的其實是一個 proxy

systemd 261 新增的 [email protected] 本質上是一個跑在 instance 內部的 metadata proxy。它開機時啟動,透過 systemd 自家的 hwdb 資料庫(hwdb.d/40-imds.hwdb)讀 SMBIOS 資訊判斷目前跑在哪家 cloud 上,然後把對應的 endpoint URL、token 流程、header 需求全部灌進 daemon。

從應用程式的角度看,查 metadata 不再是打 HTTP 到 169.254.169.254,而是透過 Varlink IPC 跟 /run/systemd/io.systemd.InstanceMetadata 這個 socket 要資料。這條路有幾個直接好處:Varlink socket 可以用 Unix socket permission 控制誰能連、daemon 內部有 cache 不用每次 round-trip 到 endpoint、每家 cloud 的 protocol 差異被抽象在 daemon 裡應用程式只看到統一的 field name。

把這幾件事串起來想,systemd-imdsd 其實是在複製 cloud-init 做了十年的事——cloud-init 開機時也會去打 IMDS 把 SSH key 跟 user data 拉下來。差別在 cloud-init 是 one-shot:開機拉完就結束,runtime 時應用程式還是得自己打 IMDS。systemd-imdsd 是常駐的 daemon,整個 instance 生命週期都在,runtime 查詢也走它。

locked 模式:真正的安全邊界在 kernel routing table

這套架構裡最實用的部分是 systemd.imds.network= 這條 kernel command line 參數。它接受三個值:off、unlocked、locked。預設是 unlocked——daemon 跑,但原本直接打 169.254.169.254 的應用程式也還能打,為了相容性保留這條路。

值設成 locked 的時候,systemd 會在 early boot 階段生成一個 .network 檔,裡面對 IMDS 的 IPv4/IPv6 address 加一條 prohibit 類型的 route。routing table 看起來會長這樣:

1
prohibit 169.254.169.254 dev lo scope link

任何從使用者空間打 169.254.169.254 的 TCP connect 都會收到 EACCES,kernel 直接在 routing 決策階段拒絕。systemd-imdsd 本身因為跑在特定的 systemd slice 裡、擁有 CAP_NET_ADMIN 跟特殊 routing table,繞過這條 prohibit route 去打真正的 endpoint。其他 process——包含被 SSRF 攻擊的 web 應用——打過去就是 connect 失敗。

這條設計把安全邊界從 application layer 移到 kernel layer。SSRF 攻擊能不能偷到 cloud credential 不再取決於 metadata library 寫得好不好、header 加對沒有、hop limit 檢查對不對,而是取決於作業系統願不願意讓這個 process 走到 endpoint。而 systemd-imdsd 的 Varlink socket 本身可以透過 systemd 的 unit IPAddressAllow=、SocketUser= 這類機制進一步限制——誰的 UID、哪個 service 可以透過 broker 查哪些 field 都能設。

有一個細節值得特別標記:locked 模式會跟 cloud-init 衝突。cloud-init 預設行為是開機時直接打 IMDS 拉 user data 跟 SSH key,locked 模式下這條路斷了。解法是用 systemd 261 一起附上的 systemd-imds-import.service——它會透過 broker 讀 metadata,把 ssh-key 寫進 ssh.authorized_keys.root credential,把 hostname 寫進 firstboot.hostname credential,把 user data 裡 JSON 格式的 systemd.credentials array 匯進 /run/credstore/。對只用 SSH key injection 跟 hostname 設定這類基本功能的部署,這個 import service 可以直接替代 cloud-init 的 metadata 處理階段。

自建雲跟客製 endpoint 怎麼接

公有雲的部分 hwdb 自動搞定,臺灣這邊實際遇到比較多的情境反而是自建私有雲、或者是透過 OpenStack、CloudStack 架的區域機房。這些場景 SMBIOS 裡沒有被 hwdb 認得的 vendor 字串,systemd-imdsd 不會自動啟動。

這時候就得走 kernel command line 手動設定。相關參數都在 systemd.imds.* namespace 下:

  • systemd.imds.vendor= 標記 vendor 名稱,純字串給 log 用
  • systemd.imds.data_url= base URL,通常不帶結尾 /
  • systemd.imds.token_url= 拿 bearer token 的 URL,像 AWS IMDSv2 這種需要先換 token 的流程會用到
  • systemd.imds.token_header_name= 傳 token 時用的 header 名稱
  • systemd.imds.extra_header= 全格式 Name:Value header,可以重複指定
  • systemd.imds.address_ipv4= / systemd.imds.address_ipv6= IMDS endpoint 位址,給 routing setup 用
  • systemd.imds.key.<name>= 把 well-known field name 映射到實際的 path

舉例 OpenStack 的 metadata service 跑在 http://169.254.169.254/openstack/latest/,自建雲裡 systemd command line 大致會長這樣:

1
2
3
4
5
systemd.imds.vendor=openstack-local
systemd.imds.data_url=http://169.254.169.254/openstack/latest
systemd.imds.address_ipv4=169.254.169.254
systemd.imds.key.hostname=/meta_data.json
systemd.imds.key.ssh_key=/meta_data.json

這些參數也支援透過 systemd service credentials 設定(imds.vendor、imds.data_url 等,底線取代點),對於用 Ignition 或 systemd-firstboot 做 provisioning 的 image-based 發行版比較順手——不需要改 bootloader 參數。

寫到這裡要坦白講:臺灣中小機房自己架 metadata service 的場景目前不多,大部分 VPS 直接走 cloud-init + 系統 image injection 這條路,沒有真的 IMDS endpoint 可打。systemd-imdsd 對這部分用戶的直接吸引力不大。但從一個方向看是有價值的:如果某天打算做 self-service portal + API driven 的 VPS 開通流程,metadata service 幾乎是必要配備,提前知道 systemd 已經把客戶端介面標準化了,自己的 metadata backend 直接 follow 這個 Varlink schema 可以省掉之後各種發行版的客製適配。

要不要現在就打開 locked 模式

討論實務切換時機。locked 模式明顯是個好東西,但現階段打開要先確認三件事:發行版有沒有把 systemd 升到 261、現有的 metadata 使用者有沒有都走 broker 的路、以及 recovery 腳本有沒有仰賴直接打 IMDS。

發行版面上,Fedora 45(2026 年 10 月預計釋出)會是第一個預設帶 systemd 261 的主流發行版,Ubuntu 要等到 26.10(Oct 2026 interim release)或 27.04(Apr 2027)才會跟上。Debian trixie 已經凍在 systemd 257 這個 LTS 分支,要等 bookworm 下一代。RHEL 系的話需要看 10.2 或 10.3 的 modularity policy,過去幾代 systemd 的 cherry-pick 都偏保守。

使用者端的問題更實際。cloud-init 目前(24.4 版)還沒加 Varlink broker support,直接打開 locked 模式會讓 cloud-init 在開機時 timeout。社群正在推 cloud-init 的 systemd-imds integration PR,但正式 release 之前,locked 模式只建議搭配純 systemd-imds-import 的部署——換句話說,要先確定開機設定能完全透過 import service 處理完,才有本錢把 cloud-init 拆掉。

對 production VPS 的具體建議:先把 systemd 261 升起來、保持 unlocked(預設值)、觀察 /run/systemd/io.systemd.InstanceMetadata 這個 socket 有沒有被應用程式連過、systemd-imds 這個 CLI 工具查得到東西沒。等 metadata 的直接消費者(aws-cli、boto3、各種 SDK)開始支援 Varlink client 之後再 flip 到 locked。這個時程保守估計 2027 年中以前都還在過渡期。

這波改動對 VPS 經營的意義

從機房營運角度看,systemd-imdsd 把 metadata consumption 從「application 層打 HTTP」變成「OS 層的 Varlink service」是個方向正確的收斂。它讓 metadata service 的介面變成系統預設能力,而不是每個 workload 要各自處理的 cloud-specific protocol。

對自架 VPS 的使用者,短期內要做的事只有一件:確認手上 Linux 發行版升級到 systemd 261 之後,自家應用程式沒有踩到相容性雷。cloud-init 繼續用、aws-cli 繼續打 IMDSv2 都還可以。中期可以觀察的是這個介面會不會被更多 metadata 消費者採用——如果會,locked 模式就是未來標配,臺灣 VPS 使用者的 instance 安全邊界可以明顯往前推一截。

NCSE Network 在臺灣是方電訊機房提供的 VPS 服務走的是傳統 virtual machine 模式,每台 instance 配有獨立的 Linux kernel 空間,可以完整掌控 systemd 版本、kernel 參數跟 routing table。想在新架設 VPS 上先行驗證 systemd-imdsd 這類核心層安全特性、或是規劃自家應用程式從公有雲遷到專屬機房的使用者,可以到 ncse.tw 了解可選的 VPS 規格跟上架流程。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →