Linux 發行版們應該禁用 IPv4 映射的 IPv6 地址嗎?
地址映射
IPv6 在很多方面看起來可能很像 IPv4,但它是一個不同地址空間的不同的協議。伺服器程序想要接受使用二者之中任意一個協議的連接,必須給兩個不同的地址族分別打開一個套接字——IPv4 的 AF_INET
和 IPv6 的 AF_INET6
。特別是一個程序希望在主機上的使用兩種地址協議的任意介面都接受連接的話,需要創建一個綁定到全零通配符地址(0.0.0.0
)的 AF_INET
套接字和一個綁定到 IPv6 等效地址(寫作 ::
)的 AF_INET6
套接字。它必須在兩個套接字上都監聽連接——或者有人會這麼認為。
多年前,在 RFC 3493,IETF 指定了一個機制,程序可以使用一個單獨的 IPv6 套接字工作在兩個協議之上。有了一個啟用這個行為的套接字,程序只需要綁定到 ::
地址從而在所有介面上接受使用這兩個協議的連接。當創建了一個 IPv4 連接到該綁定埠,源地址會像 RFC 2373 中描述的那樣映射到 IPv6。所以,舉個例子,一個使用了這個模式的程序會將一個 192.168.1.1
的傳入連接看作來自 ::ffff:192.168.1.1
(這個混合的寫法就是這種地址的通常寫法)。程序也能通過相同的映射方法打開一個到 IPv4 地址的連接。
RFC 要求默認實現這個行為,所以大多數系統這麼做了。不過也有些例外,OpenBSD 就是其中之一;在那裡,希望在兩種協議下工作的程序能做的只能是創建兩個獨立的套接字。但一個在 Linux 中打開兩個套接字的程序會遇到麻煩:IPv4 和 IPv6 套接字都會嘗試綁定到 IPv4 地址,所以不論是哪個,後者都會失敗。換句話說,一個綁定到 ::
指定埠的套接字的程序會同時綁定到那個埠上的 IPv6 的 ::
和 IPv4 的 0.0.0.0
地址。如果程序之後嘗試綁定一個 IPv4 套接字到 0.0.0.0
的相同埠上時,這個操作會失敗,因為這個埠已經被綁定了。
當然有個辦法可以解決這個問題;程序可以調用 setsockopt()
來打開 IPV6_V6ONLY
選項。一個打開兩個套接字並且設置了 IPV6_V6ONLY
的程序應該可以在所有的系統間移植。
讀者們可能對不是每個程序都能正確處理這一問題沒那麼震驚。事實證明,這些程序的其中之一是 網路時間協議 的 OpenNTPD 實現。Brent Cook 最近給上游 OpenNTPD 源碼提交了一個小補丁,添加了必要的 setsockopt()
調用,它也被提交到了 OpenBSD 中了。不過那個補丁看起來不大可能被接受,最可能的原因是因為 OpenBSD 式的理由(LCTT 譯註:如前文提到的,OpenBSD 並不受這個問題的影響)。
安全擔憂
正如上文所提到,OpenBSD 根本不支持 IPv4 映射的 IPv6 套接字。即使一個程序試著通過將 IPV6_V6ONLY
選項設置為 0 來顯式地啟用地址映射,它的作者也會感到沮喪,因為這個設置在 OpenBSD 系統中無效。這個決定背後的原因是這個映射帶來了一些安全隱憂。攻擊打開的介面的攻擊類型有很多種,但它們最後都會回到規定的兩個途徑到達相同的埠,每個埠都有它自己的控制規則。
任何給定的伺服器系統可能都設置了防火牆規則,描述埠的允許訪問許可權。也許還會有適當的機制,比如 TCP wrappers 或一個基於 BPF 的過濾器,或一個網路上的路由器可以做連接狀態協議過濾。結果可能是導致防火牆保護和潛在的所有類型的混亂連接之間的缺口造成同一 IPv4 地址可以通過兩個不同的協議到達。如果地址映射是在網路邊界完成的,情況甚至會變得更加複雜;參看這個 2003 年的 RFC 草案,它描述了如果映射地址在主機之間傳播,一些隨之而來的其它攻擊場景。
改變系統和軟體正確地處理 IPv4 映射的 IPv6 地址當然可以實現。但那增加了系統的整體複雜度,並且可以確定這個改動沒有實際地完整實現到它應該實現的範圍內。如同 Theo de Raadt 說的:
有時候人們將一個糟糕的想法放進了 RFC。之後他們發現這個想法是不可能的就將它丟回垃圾箱了。結果就是概念變得如此複雜,每個人都得在管理和編碼方面是個全職專家。
我們也根本不清楚這些全職專家有多少在實際配置使用 IPv4 映射的 IPv6 地址的系統和網路。
有人可能會說,儘管 IPv4 映射的 IPv6 地址造成了安全危險,更改一下程序讓它在實現了地址映射的系統上關閉地址映射應該沒什麼危害。但 Theo 認為不應該這麼做,有兩個理由。第一個是有許多破舊的程序,它們永遠不會被修復。而實際的原因是給發行版們施加了壓力去默認關閉地址映射。正如他說的:「最終有人會理解這個危害是系統性的,並更改系統默認行為使之『secure by default』。」
Linux 上的地址映射
在 Linux 系統,地址映射由一個叫做 net.ipv6.bindv6only
的 sysctl 開關控制;它默認設置為 0(啟用地址映射)。管理員(或發行版們)可以通過將它設置為 1 來關閉地址映射,但在部署這樣一個系統到生產環境之前最好確認軟體都能正常工作。一個快速調查顯示沒有哪個主要發行版改變這個默認值;Debian 在 2009 年的 「squeeze」 中改變了這個默認值,但這個改動破壞了很多的軟體包(比如任何包含 Java 的程序),在經過了幾次的 Debian 式的討論之後,它恢復到了原來的設置。看上去不少程序依賴於默認啟用地址映射。
OpenBSD 有以「secure by default」的名義打破其核心系統之外的東西的傳統;而 Linux 發行版們則更傾向於難以作出這樣的改變。所以那些一般不願意收到他們用戶的不滿的發行版們,不太可能很快對 bindv6only 的默認設置作出改變。好消息是這個功能作為默認已經很多年了,但很難找到被利用的例子。但是,正如我們都知道的,誰都無法保證這樣的利用不可能發生。
via: https://lwn.net/Articles/688462/
作者:Jonathan Corbet 譯者:alim0x 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive