:從最小化裁剪到防火墻策略)
1. 安全加固的整體設計思路先弄清楚你在防誰嵌入式 Linux 和服務器 Linux 有個本質(zhì)區(qū)別服務器的安全威脅大多來自外部網(wǎng)絡你防的是幾百公里外不知名的攻擊者而嵌入式設備往往就暴露在物理可接觸的場景里——機房里的一臺邊緣網(wǎng)關、路側的一臺智能終端、工廠產(chǎn)線上一塊工控板誰都能靠近它、插個 U 盤、按一下復位鍵、甚至直接拆開外殼把 Flash 焊下來。所以嵌入式安全加固的第一課不是“裝個殺毒軟件”而是先建立一個完整的威脅模型。我在做這塊的時候習慣把嵌入式設備的威脅來源分成四類第一類是遠程網(wǎng)絡攻擊比如弱口令爆破、未授權訪問、漏洞利用這類威脅和服務器類似第二類是本地提權攻擊攻擊者通過 Web 漏洞或者應用層漏洞先拿到一個普通用戶權限再通過內(nèi)核漏洞、配置缺陷、SUID 文件等方式提權到 root第三類是物理攻擊包括串口登錄、JTAG 調(diào)試接口、Live 啟動、拆片讀 Flash第四類是供應鏈攻擊固件在編譯、打包、分發(fā)過程中被植入后門。這個第 17 講的核心思路就是針對這四類威脅分別部署防御層最小化裁剪用來縮小攻擊面權限硬化用來提高提權門檻日志審計用來解決“出了事不知道”的問題輕量防火墻用來做網(wǎng)絡邊界管控。四者不是孤立的功能疊加而是層層遞進的關系——裁剪減少了可以被利用的組件數(shù)量權限硬化讓即使被利用也難以擴大戰(zhàn)果日志審計保證你能發(fā)現(xiàn)入侵痕跡并追溯攻擊路徑防火墻則在最外層把大部分自動化掃描直接擋掉。這套方案適合誰呢適合那些已經(jīng)開始做嵌入式 Linux 產(chǎn)品、但是安全加固還停留在“改個默認密碼”階段的人。不管你是做路由器、工業(yè)網(wǎng)關、邊緣計算盒子還是智能終端這套思路都可以直接套用。當然如果你的產(chǎn)品拿到了嚴格的安全合規(guī)需求比如等保、PCI、車規(guī)級的 EVITA 等等那這講的內(nèi)容可以作為地基在上面再疊加對應的合規(guī)要求。2. 系統(tǒng)最小化裁剪從源頭縮小攻擊面2.1 內(nèi)核裁剪關掉那些你根本用不到的功能內(nèi)核裁剪是最容易出效果、也最容易翻車的一步。嵌入式設備功能相對固定內(nèi)核里大部分模塊其實根本不會被用到。我見過不少團隊圖省事直接拿廠商 BSP 的內(nèi)核配置來用一個 4MB 的 Flash 里塞了一個功能齊全的發(fā)行版內(nèi)核里面支持十幾種文件系統(tǒng)、幾十種網(wǎng)絡協(xié)議、各種多媒體驅動一半以上永遠跑不起來。這些用不到的模塊不只是浪費存儲空間更是實打實的安全風險。內(nèi)核里每一個協(xié)議棧、每一個驅動、每一個文件系統(tǒng)實現(xiàn)都可能是潛在的漏洞入口。舉個現(xiàn)實的例子如果你裁剪掉了CONFIG_BLK_DEV_LOOP那攻擊者就沒法輕易掛載一個惡意鏡像文件如果你關掉了CONFIG_BINFMT_MISC很多基于 magic 值的提權利用就沒法落地。攻擊面就是通過這樣一個個看似不起眼的開關縮小下去的。我這邊裁剪內(nèi)核的基本步驟如下先用make menuconfig基于產(chǎn)品實際用到的硬件接口和功能列表把用不到的驅動全部關掉從設備的 BSP 默認配置開始逐步“做減法”。網(wǎng)絡協(xié)議這一塊要特別留意除了 TCP/IP 和必要的協(xié)議之外像 DCCP、SCTP、RDS、TIP 這類協(xié)議該關就關。文件系統(tǒng)保留實際用的那一種或兩種就夠了CONFIG_FUSE、CONFIG_VFAT這些如果不涉及外部存儲就別開。還有一個容易被忽略的CONFIG_KEXEC、CONFIG_HIBERNATION、CONFIG_DEVMEM、CONFIG_DEVKMEM這些屬于典型的危險開關能關則關。內(nèi)核模塊加載功能如果產(chǎn)品確實不需要動態(tài)加載驅動建議直接關掉CONFIG_MODULES徹底斷了注入內(nèi)核模塊這條路。如果因為硬件初始化必須要保留模塊加載能力那就用內(nèi)核模塊簽名機制只信任帶合法簽名的模塊。裁剪完成后用size命令確認一下 vmlinux 的體積再用產(chǎn)品實際要跑的應用做一輪完整的功能回歸不要只測啟動。裁剪內(nèi)核最怕的就是“啟動看起來正常某路 SPI 設備初始化到一半掛了”這類問題。2.2 RootFS 裁剪BusyBox、musl 與依賴清理用戶空間裁剪的核心工具基本繞不開 BusyBox。BusyBox 一個二進制就集成了兩百多個常用命令對嵌入式設備來說非常實用。但要注意的是BusyBox 里的每個 applet 也不是越多越好。編譯 BusyBox 的時候make menuconfig里同樣可以逐個勾選需要保留的命令。一個最小化的系統(tǒng)保留sh、mount、ifconfig、ping、cat、ls、cp、mv、rm、ps、kill、syslogd、logrotate如果集成的話這些就基本夠了。像telnetd、ftpd、httpd這類網(wǎng)絡服務如果產(chǎn)品沒用上就千萬別勾勾上等于白送攻擊者一個入口。C 庫的選擇對體積影響也很明顯。glibc 功能全但體積大依賴多musl 在體積上優(yōu)勢明顯靜態(tài)鏈接時更突出。如果你的應用不依賴 glibc 特有的 GNU 擴展遷移到 musl 是一個很值得考慮的選項。我實測過一個基于 ARM Cortex-A7 的項目換到 musl 之后整個 rootfs 的壓縮鏡像從 8MB 降到了 5MB 左右啟動時內(nèi)存占用也少了約 15%。當然遷移的成本在于一些動態(tài)庫的 ABI 差異和個別 API 行為差異但總體來說性價比很高。還有一個必須做的動作所有不需要的 setuid/setgid 文件統(tǒng)統(tǒng)處理掉。BusyBox 默認編譯如果不帶單獨的 applet那su、mount這些需要特權的操作要怎么實現(xiàn)常見做法是啟用 BusyBox 的 setuid 機制給busybox二進制本身加上 setuid root由它內(nèi)部根據(jù)調(diào)用的 applet 決定是否降權。但這個機制也是雙刃劍——一旦 BusyBox 的某個 applet 有漏洞setuid root 的 BusyBox 就成了提權放大器。所以在產(chǎn)品最終形態(tài)里如果不需要普通用戶執(zhí)行 mount 之類的操作建議把 BusyBox 的CONFIG_FEATURE_SUID關掉讓所有命令都以普通權限運行。實在需要特權操作的場景用單獨的、去掉 setuid 位的小工具來實現(xiàn)別讓整個 BusyBox 都帶 setuid 位。2.3 裁剪時的“功能與安全”平衡決策講到這里必須提醒一句最小化裁剪不是砍得越狠越好而是要在“夠用”的基礎上再砍一刀??车锰莸暮蠊恰氵B排查問題的工具都沒了設備出問題只能拆機接串口而產(chǎn)品經(jīng)理還在一旁催著“遠程解決一下”。我說一個比較實用的平衡思路把系統(tǒng)分區(qū)和運行分區(qū)分開。只讀的系統(tǒng)分區(qū)放內(nèi)核和最精簡的 BusyBox 工具鏈保證基礎啟動和網(wǎng)絡連通可寫的 overlay 分區(qū)放需要更新的應用和臨時數(shù)據(jù)。這樣即使應用區(qū)被寫壞系統(tǒng)區(qū)還能啟動恢復機制可以正常工作。不要把整個 rootfs 都做成可寫的那是給攻擊者改你系統(tǒng)文件的便利條件。3. 權限硬化把攻擊者擋在 root 之外3.1 用戶與權限體系設計刪除、隔離、最小化嵌入式 Linux 默認跑起來是 root 用戶這是很多設備出廠時最明顯的問題。產(chǎn)品在研發(fā)階段用 root 圖省事可以理解但正式發(fā)布的固件里必須建立一套最小化的用戶權限體系。具體做法是刪除不需要的系統(tǒng)賬戶比如games、news、uucp這些發(fā)行版默認創(chuàng)建的賬戶在嵌入式環(huán)境里完全沒有存在的意義。正常需要保留的大概就是root、daemon、bin、sys、nobody以及運行特定服務時創(chuàng)建的專用賬戶。如果你的產(chǎn)品里跑了一個 Web 服務、一個 MQTT 客戶端、一個日志收集進程那就分別創(chuàng)建www、mqtt、logd這樣的獨立賬戶每個賬戶只賦予自己業(yè)務目錄的讀寫權限絕不互相交叉。進程以最小權限運行即使被攻破也只是拿到這個賬戶的權限而不是整個系統(tǒng)。用戶密碼策略也不能忽視。嵌入式設備的 root 密碼如果再使用出廠默認值就是給攻擊者送分。更合理的方式是首次開機強制修改密碼或者干脆不設密碼只允許通過證書密鑰登錄適用于 SSH 管理場景同時把串口登錄限制在 uboot 階段內(nèi)核啟動之后串口終端盡快關閉或者也要求認證。如果產(chǎn)品沒有本地運維需求串口直接關閉也可以。3.2 文件系統(tǒng)掛載選項noexec、nosuid、nodev 與只讀掛載文件系統(tǒng)掛載選項的安全價值我怎么說都不為過。這是性價比最高的權限硬化手段之一幾乎沒有成本卻能讓一大堆攻擊手法直接失效。關鍵掛載選項有這么幾個noexec禁止在該文件系統(tǒng)上執(zhí)行任何可執(zhí)行文件。比如/tmp、/var、/home這些目錄業(yè)務上不應該有可執(zhí)行文件存在的需求統(tǒng)統(tǒng)加上noexec。這樣攻擊者即使通過漏洞把惡意二進制寫進了 /tmp也無法直接執(zhí)行。nosuid忽略該文件系統(tǒng)上的 setuid/setgid 位。再配合上面說的 BusyBox setuid 機制能很大程度上杜絕提權。nodev忽略該文件系統(tǒng)上的設備文件。攻擊者在可寫目錄里創(chuàng)建一個/dev/sda1之類的設備節(jié)點再直接訪問底層存儲這個小技巧在一些場景下能用來繞過訪問控制加上nodev就能堵住。ro只讀掛載。系統(tǒng)分區(qū)、內(nèi)核分區(qū)、只讀 rootfs 都建議直接只讀掛載需要改配置的少量文件單獨用 bind mount 到可寫分區(qū)上。在一個實際項目里我拿到一個新的 BSP 之后第一件事就是改/etc/fstab把能加安全選項的分區(qū)全加上。我一個做物聯(lián)網(wǎng)網(wǎng)關的朋友有一次設備被入侵了攻擊者留下的后門腳本就是放在/tmp下執(zhí)行的。當時/tmp沒有加noexec導致攻擊者輕松拿到了穩(wěn)定的執(zhí)行權限。后來他在所有線上設備上加了這個掛載選項之后同類攻擊就沒有再得逞過。3.3 內(nèi)核安全參數(shù)sysctl 與內(nèi)核防護機制內(nèi)核自身也有一些安全開關通過/etc/sysctl.conf配置。說幾個嵌入式場景下比較值得開的kernel.kptr_restrict1可以限制非特權用戶讀取內(nèi)核符號地址。攻擊者利用內(nèi)核漏洞時通常需要知道一些符號的地址這個參數(shù)會顯著提高利用難度。kernel.dmesg_restrict1限制非特權用戶查看內(nèi)核日志防止攻擊者從 dmesg 里收集內(nèi)核版本、內(nèi)存布局等敏感信息。net.ipv4.tcp_syncookies1開啟 SYN cookies能在一定程度上緩解 SYN Flood 攻擊。對暴露在公網(wǎng)的設備來說這算是基礎防護。net.ipv4.conf.all.rp_filter1開啟反向路徑過濾能防一部分 IP 欺騙攻擊。不過要注意如果網(wǎng)絡環(huán)境里有不對稱路由比如多 WAN 口負載均衡這個選項可能導致正常流量被丟棄開之前要測試線上業(yè)務。內(nèi)核安全模塊方面SELinux 和 AppArmor 是兩個選項。SELinux 功能最強但配置復雜度高對嵌入式設備來說學習成本和維護成本都很高AppArmor 相對輕量配置基于路徑更直觀一些。如果你做的是安全等級要求比較高的產(chǎn)品比如金融終端或者軍工相關設備那 SELinux 值得投入如果只是普通的 IoT 網(wǎng)關、工業(yè)盒子AppArmor 或者一些輕量級的策略約束就夠了。但這不是說不用安全模塊就完全不行——前面做的用戶隔離和文件系統(tǒng)選項已經(jīng)把大部分攻擊路徑都堵掉了安全模塊是在這之上再加一道保險。4. 日志審計的完整落地出了事你得知道4.1 日志采集鏈路從應用到落盤的完整路徑安全加固做到前面那幾步之后剩下一個關鍵問題如果設備真的被入侵了你怎么知道怎么追蹤攻擊者做了什么這就是日志審計存在的意義。嵌入式設備的日志采集鏈路通常是這樣的應用程序產(chǎn)生日志 - syslog 接口或者直接寫文件- syslogd 進程接收 - 寫入本地緩沖區(qū)或落盤 - 日志輪轉工具按策略清理或歸檔 - 遠程 Syslog 轉發(fā)到日志服務器。BusyBox 內(nèi)置的syslogd默認行為是把日志寫到/var/log/messages日志量大的時候很快就把 Flash 寫滿了所以必須配合日志輪轉。BusyBox 里自帶了一個精簡的logread和對應的循環(huán)緩沖機制如果你的系統(tǒng)可用內(nèi)存允許建議把 syslogd 的輸出配置成使用內(nèi)存環(huán)形緩沖區(qū)然后定期將有用的日志刷到外部存儲。這樣既保證了日志采集能力又減少了對 Flash 的頻繁寫入。在 syslogd 的配置里有一個參數(shù)值得特別注意-b參數(shù)可以設置環(huán)形緩沖區(qū)的大小-s參數(shù)可以設置單條日志最大長度。做審計日志時日志內(nèi)容寧可多一些冗余也不能丟關鍵信息建議把單條日志長度設置足夠大免得日志被截斷后關鍵信息丟失。4.2 日志內(nèi)容抓什么who、what、when、where、how日志審計的價值取決于你采集了什么。安全日志至少要覆蓋這幾個維度誰who、做了什么what、什么時候when、在哪臺設備上where、怎么做的how。具體來說需要重點采集的日志類型包括登錄事件本地和控制臺的每次成功與失敗登錄失敗登錄尤其要記錄這是暴力破解的直接證據(jù)。提權操作任何 su、sudo 操作都要記錄。如果系統(tǒng)里沒有正常的 sudo 需求那就更簡單——如果日志里出現(xiàn)了 sudo 記錄直接判定異常。用戶和組變更創(chuàng)建新用戶、修改 UID、添加用戶到 sudo 組這些都是攻擊者常做的事一步步記錄下來。服務啟停網(wǎng)絡服務、定時任務的變化特別是新增了監(jiān)聽端口或者新增了 crontab 條目。內(nèi)核關鍵事件內(nèi)核 panic、模塊加載、錯誤消息。系統(tǒng)重啟記錄攻擊者可能通過重啟來激活某些惡意配置比如修改了啟動腳本。4.3 日志輪轉與遠程轉發(fā)防篡改的最后一公里日志最大的問題是攻擊者拿到 root 權限之后第一件事往往就是清理日志。所以日志審計不能只落在本地遠程日志服務器是必須的。BusyBox 的 syslogd 支持通過網(wǎng)絡把日志轉發(fā)到遠程 Syslog 服務器配置方法是在啟動參數(shù)里加上-R 日志服務器IP:端口。但這里有個坑默認情況下的 Syslog 協(xié)議是明文 UDP 傳輸攻擊者如果控制了局域網(wǎng)內(nèi)的任何一臺設備可以抓包看到所有日志內(nèi)容甚至偽造日志包干擾審計。如果安全等級要求高建議走帶加密的日志通道。標準的 syslog 協(xié)議族里有 TLS 加密方案比如 syslog-ng 或 rsyslog 都支持tls嵌入式場景如果不想引入太大的依賴也可以用輕量級的方案比如用一個簡單的腳本定期把日志通過 SSH 通道傳輸?shù)饺罩痉掌?。日志輪轉配置方面logrotate是標準工具。嵌入式系統(tǒng)里一般用 BusyBox 自帶的logrotateapplet。我通常的配置策略是日志按天輪轉保留最近 7 天的本地日志超過 7 天的自動清理。如果產(chǎn)品對審計要求更高可以保留更長時間或者直接全部遠程存儲。日志輪轉本身也要注意輪轉腳本如果寫得不對可能會產(chǎn)生新的安全風險——比如變量沒加引號導致路徑注入、輪轉后的日志文件權限過寬導致其他用戶可讀等。寫完輪轉配置之后建議實際跑一輪確認沒有問題再發(fā)布。5. 輕量防火墻實戰(zhàn)iptables 規(guī)則與性能取舍5.1 先搞清楚你的設備需不需要防火墻不是所有嵌入式設備都需要防火墻。如果設備部署在可信內(nèi)網(wǎng)并且沒有任何對外監(jiān)聽的端口那防火墻的實際防御價值其實不大反而會增加 CPU 開銷和規(guī)則維護成本。但如果是下面這幾種場景防火墻就是必需品設備有外部網(wǎng)絡訪問能力比如 4G/5G 上網(wǎng)并且監(jiān)聽了某些服務端口設備是網(wǎng)關類產(chǎn)品需要做端口轉發(fā)和訪問控制設備會被放置在不同客戶的網(wǎng)絡環(huán)境里你無法保證客戶的內(nèi)網(wǎng)足夠安全產(chǎn)品需要滿足某些安全合規(guī)要求比如等保 2.0 的邊界防護要求判斷標準其實很簡單設備上有沒有對外監(jiān)聽的 TCP/UDP 端口如果有那就要用防火墻明確“誰能訪問這些端口”。如果設備完全是被動連接出去沒有監(jiān)聽任何端口那防火墻的作用相對有限但依然可以通過 egress 限制來防止設備被當作跳板去攻擊其他主機。5.2 最小規(guī)則集默認 DROP 策略與白名單思維我見過很多嵌入式開發(fā)人員對防火墻的態(tài)度是“配一下 iptables 規(guī)則綁一下端口就完事了”結果規(guī)則鏈用的是默認 ACCEPT 策略只加了少量 DROP 規(guī)則。這個思路本質(zhì)上還是“黑名單”思維——你只能防御你已知的攻擊未知攻擊直接繞過去了。正確的做法是默認 DROP。INPUT 鏈默認策略設置為 DROP然后逐條放開必須的入站流量OUTPUT 鏈默認策略設置為 DROP然后放開必須的出站流量。這個配置邏輯上很簡單但實踐中很多人的反應是“這樣我的設備是不是什么都干不了了”——沒錯而且這恰恰是目標。你需要在本地把設備的網(wǎng)絡通信行為完整梳理一遍列出一份“必須允許”的清單允許 DHCP 請求出站、允許 DNS 查詢出站、允許 NTP 時間同步出站、允許 MQTT 到指定服務器出站、允許 SSH 從管理網(wǎng)段入站然后把這清單翻譯成防火墻規(guī)則。這里有一個典型場景設備需要從遠程服務器下載升級包那就要允許到升級服務器的 443 端口出站設備必須響應某個網(wǎng)段的 ping 請求那就單獨允許 ICMP echo-request 入站。注意別為了省事把整段 ICMP 都放開ICMP 重定向、時間戳請求這些類型都有被利用的可能。一個完整的 iptables 最小規(guī)則集大概長這樣# 清除現(xiàn)有規(guī)則 iptables -F iptables -X iptables -Z # 默認策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT DROP # 允許回環(huán)接口 iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT # 允許已建立的連接及其相關流量 iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 允許 DHCP 出站 iptables -A OUTPUT -p udp --dport 67:68 -j ACCEPT # 允許 DNS 出站 iptables -A OUTPUT -p udp --dport 53 -j ACCEPT iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT # 允許 NTP 出站 iptables -A OUTPUT -p udp --dport 123 -j ACCEPT # 允許 MQTT 到指定云平臺替換為實際地址 iptables -A OUTPUT -d 203.0.113.10 -p tcp --dport 8883 -j ACCEPT # 允許 SSH 從管理網(wǎng)段入站 iptables -A INPUT -s 192.168.10.0/24 -p tcp --dport 22 -j ACCEPT # 允許 ICMP echo-request 入站可選 iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT這個規(guī)則集放在設備上能擋住絕大部分的端口掃描和未授權訪問嘗試。5.3 iptables 還是 nftables嵌入式場景怎么選現(xiàn)在新版本的內(nèi)核里 nftables 已經(jīng)在逐漸替代 iptables。但對嵌入式場景來說選哪個其實首先要看內(nèi)核版本和既有代碼的維護成本。如果 BSP 里已經(jīng)集成了 iptables且你的規(guī)則都是 iptables 語法寫的那就繼續(xù)用 iptables沒必要為了“新”而折騰。nftables 的優(yōu)勢主要是規(guī)則集更緊湊、性能更好、語法上更統(tǒng)一但在低性能的 ARM 設備上這套優(yōu)勢的感知并不明顯。還有一個更輕量的思路如果設備性能極其有限比如單核 Cortex-M 級別跑 Linux跑完整的 iptables/netfilter 棧都可能成為負擔。這種情況下可以考慮在應用層做訪問控制比如在 TCP 協(xié)議棧的 socket 層做 bind/connect 限制或者用更輕量的工具像busybox里自帶的防火墻配置界面其實還是走 iptables。我個人建議是Cortex-A 級別以上的設備直接用 iptables/nftables性能完全夠用再往下的 MCU 級別設備先考慮業(yè)務上的安全設計不要硬上完整防火墻。5.4 防火墻規(guī)則別忘了持久化很多嵌入式工程師在調(diào)試時手動敲了一堆 iptables 規(guī)則一切正常然后重啟設備規(guī)則全沒了。這并不是腳本錯誤而是 iptables 規(guī)則本質(zhì)上是運行時的配置不會自動保存。你需要把最終的規(guī)則集保存下來并在系統(tǒng)啟動時自動加載。常見的做法有兩種一是使用iptables-save導出規(guī)則文件然后在/etc/init.d/下的啟動腳本里用iptables-restore導入二是把規(guī)則直接寫進一個 shell 腳本比如/etc/firewall.sh在網(wǎng)絡服務啟動之后調(diào)用。第二種做法的好處是規(guī)則腳本里可以寫注釋和條件判斷方便適配不同產(chǎn)品形態(tài)。不管用哪種方式都要記住一個關鍵點防火墻規(guī)則的加載時機很重要。如果規(guī)則加載太早網(wǎng)絡接口還沒就緒規(guī)則可能因為找不到匹配的接口而報錯加載太晚則會留下一個沒有防火墻保護的窗口期。我一般建議在網(wǎng)絡服務全部啟動之前、接口配置完成之后加載。6. 全流程實踐從零加固一臺嵌入式 Linux 設備6.1 階段一梳理業(yè)務與確定資產(chǎn)清單在前面幾節(jié)講完了各項技術細節(jié)之后我把一次完整的加固流程串起來。這個流程我稱之為“先理后治”不管你是用 Buildroot、Yocto 還是傳統(tǒng)的手工交叉編譯環(huán)境大致路徑都差不多。第一步是梳理業(yè)務設備上跑哪些服務監(jiān)聽哪些端口需要網(wǎng)絡訪問哪些外部服務SSH 遠程管理是否需要串口調(diào)試在生產(chǎn)階段還需不需要把這些問題列成一個表這是后續(xù)所有裁剪和規(guī)則配置的依據(jù)。這一步不能省略因為如果業(yè)務梳理不清楚后面的裁剪和防火墻配置就是空中樓閣。6.2 階段二構建最小化系統(tǒng)并加固配置接下來按照第 2 節(jié)的方法做內(nèi)核裁剪和 rootfs 裁剪。這里我建議用構建腳本把整個過程固化下來不要每次手工操作。Buildroot 是一個比較順手的選擇配置好后可以一鍵生成內(nèi)核、rootfs、工具鏈并且可以重復構建。如果你用的是 Yocto那裁剪的思路也是類似的只是配置方式變成了 recipe 和 image feature。系統(tǒng)構建完成之后進入配置加固階段修改/etc/inittab把不需要的 tty 終端關掉特別是ttyS0如果產(chǎn)品不需要本地登錄直接注釋掉修改/etc/fstab按第 3.2 節(jié)的方法給各個分區(qū)加上安全掛載選項修改/etc/sysctl.conf啟用內(nèi)核安全參數(shù)創(chuàng)建最小化的用戶賬戶體系刪除默認賬戶配置 BusyBox 的 syslogd 和 logrotate最后把防火墻規(guī)則寫進啟動腳本。6.3 階段三安全功能驗證與回歸測試加固完成之后不要急著發(fā)布先做一輪驗證功能驗證所有業(yè)務功能是否正常特別是網(wǎng)絡相關功能防火墻規(guī)則不能影響正常業(yè)務。權限驗證普通用戶能不能 sudo能不能直接讀寫系統(tǒng)分區(qū)能不能修改/etc/passwd安全驗證從另一臺機器掃描設備的所有端口確認只有預期的端口在監(jiān)聽嘗試通過弱口令、默認口令、關閉的服務入口去連接確認全部失敗。穩(wěn)定性驗證長時間運行至少 24 小時后檢查內(nèi)存泄漏、日志文件大小、Flash 剩余空間是否在預期范圍內(nèi)?;謴万炞C模擬一次固件異常升級、一次配置寫壞確認設備的恢復機制可以正常把系統(tǒng)拉回來。其中端口掃描這一步我建議在每次加固之后都跑一遍用nmap從攻擊者的視角看一下設備暴露了哪些端口。很多時候你以為關閉了某個服務實際還在監(jiān)聽這種問題只有掃描才能發(fā)現(xiàn)。6.4 階段四文檔化與持續(xù)維護安全加固不是一個一次性的動作而是一個持續(xù)的過程。固件每次升級之后攻擊面都可能發(fā)生變化所以你需要把加固的每一個決策記錄下來——為什么裁剪這個驅動為什么放開這個端口為什么給這個分區(qū)加只讀生成一份加固基線文檔后續(xù)每次版本迭代都拿基線文檔來對比。我自己的習慣是把加固過程做成一個 checklist每個版本發(fā)版前跑一遍 checklist。這個 checklist 大概包括幾十項內(nèi)容是否還有默認密碼是否還有沒用的監(jiān)聽端口/tmp 是否加上了 noexec內(nèi)核模塊加載是否關閉日志是否正常輪轉和轉發(fā)這比每次發(fā)版前臨時想“我這次改了哪些東西會不會引入安全問題”要可靠得多。7. 常見問題與排查技巧實錄7.1 加固后業(yè)務異常防火墻靜默丟包這是最常遇到的問題。現(xiàn)象是設備加固之后某項業(yè)務不通了但完全找不到原因。比如設備上的應用原本可以正常連接云端加完防火墻規(guī)則之后就一直連不上。問題往往出在 OUTPUT 鏈的默認 DROP 策略上——設備的業(yè)務進程要訪問某個端口而你忘了在規(guī)則里放開。排查方法其實不復雜先看日志dmesg里開啟 iptables 的日志記錄加一條-j LOG規(guī)則這樣被丟棄的包會打印在內(nèi)核日志里一看就知道是誰訪問什么端口被擋了。然后再對照業(yè)務梳理清單把遺漏的端口補上。需要注意的是LOG規(guī)則在生產(chǎn)環(huán)境不要長時間開啟會產(chǎn)生大量內(nèi)核日志影響性能定位完成后就刪掉。7.2 日志文件把 Flash 寫爆了嵌入式設備的 Flash 寫入次數(shù)和容量都非常有限。有些產(chǎn)品跑了一陣之后內(nèi)存卡或者 Flash 被日志寫滿了導致系統(tǒng)無法正常工作。這個問題通常是兩個原因造成的一是日志采集過于激進應用把調(diào)試日志輸出到了正式環(huán)境二是日志輪轉策略沒有生效。排查時先檢查/var/log下各個文件的大小再用du -sh /var/log/*看一下分布確認哪類日志最多。優(yōu)化手段包括把日志級別從 DEBUG 改為 INFO 或者 WARNING縮短日志輪轉周期限制單條日志的最大長度最有效的還是加上前面說的-b環(huán)形緩沖區(qū)限制把日志放在內(nèi)存里。日志本來就只是用于審計和排障不需要永久保留特別是調(diào)試日志。7.3 文件系統(tǒng)變成只讀配置無法持久化有些產(chǎn)品把 rootfs 做成了只讀結果應用運行時發(fā)現(xiàn)配置文件寫不進去進程直接啟動失敗。這個問題的根源在于只讀和可寫分區(qū)沒有做好規(guī)劃。解決思路是把需要持久化的數(shù)據(jù)目錄單獨放在一個可寫的 overlay 分區(qū)比如/data、/var/lib在應用啟動時通過 bind mount 或者符號鏈接把可寫目錄映射到系統(tǒng)的預期路徑下。比如/etc下的業(yè)務配置文件可以先復制一份放到/data/etc/再用 bind mount 把/etc下的某個具體文件掛載成/data/etc/下的文件這樣既能保持系統(tǒng)分區(qū)只讀又能正常持久化配置。7.4 加固后的性能下降netfilter 與日志開銷有些工程師擔心防火墻規(guī)則會影響設備性能。實測下來在 Cortex-A7 級別的設備上幾十條 iptables 規(guī)則對網(wǎng)絡吞吐的影響其實很小可以忽略不計。真正影響性能的往往不是規(guī)則匹配而是日志記錄。如果在規(guī)則里加了很多-j LOG且系統(tǒng)開啟了內(nèi)核打印那大量日志輸出會拖慢系統(tǒng)。另一個容易被忽略的性能問題是連接跟蹤conntrack表溢出。如果設備處于高并發(fā)網(wǎng)絡環(huán)境中conntrack 表項滿了之后新連接會被丟棄表現(xiàn)為網(wǎng)絡時斷時續(xù)或者特定業(yè)務不可達。解決方法是調(diào)大 conntrack 表的最大值或者對某些場景禁用連接跟蹤。我曾經(jīng)在一臺設備上遇到過這種事情端口掃描工具一跑設備直接卡死。后來發(fā)現(xiàn)是攻擊者發(fā)了大量偽造的 TCP SYN 包把 conntrack 表灌滿了。這個時候net.ipv4.tcp_max_syn_backlog和net.netfilter.nf_conntrack_max就是核心參數(shù)調(diào)優(yōu)之后這個現(xiàn)象基本消失。8. 第 16 講課后思考題解析繞過“信息差”的實操問答8.1 思考題一為什么最小化裁剪能提高安全性裁剪到什么程度合適最小化裁剪提高安全性的核心原理是“攻擊面縮小”。一個系統(tǒng)里存在的代碼越多、功能越復雜潛在漏洞數(shù)量就越多攻擊者可以利用的路徑也就越多。裁剪掉一個用不到的協(xié)議棧等于直接關閉了一整條攻擊路徑比單純靠打補丁更可靠——因為你不再需要為一個永遠不會使用的功能維護安全性。裁剪到什么程度合適這個沒有統(tǒng)一答案但有個基本判斷標準刪掉一個組件后產(chǎn)品的所有功能依然能正常通過測試如果刪掉之后某個功能掛了那么這個組件就是需要的。實際操作中“保留最小集加白名單”比“先完整集再逐個刪”更省事、更安全因為你不需要每次對刪掉的東西做風險評估你只需要對保留的少量模塊做重點維護。但我們前面也說了別把整個 rootfs 砍到連排查問題的工具都沒有了那屬于“為了安全而犧牲了可維護性”在產(chǎn)品實踐里得不償失。8.2 思考題二SELinux 和 AppArmor 在嵌入式場景下應該選哪個這個問題在社區(qū)里爭議很大。我的看法是如果你能接受 CAP 策略的編寫成本SELinux 的安全強度確實更高因為它可以對進程的每一個文件訪問、網(wǎng)絡訪問、系統(tǒng)調(diào)用進行細粒度控制但問題在于它的學習曲線非常陡峭策略編寫和調(diào)試成本很高在嵌入式設備上維護一套完整的 SELinux 策略工作量不亞于維護一個 BSP。AppArmor 的優(yōu)勢是配置基于路徑寫起來直觀普通工程師半天就能上手。對于大多數(shù)嵌入式產(chǎn)品來說AppArmor 提供的隔離能力已經(jīng)夠用了。如果產(chǎn)品說明確要求了 SELinux那就直接選 SELinux如果沒有先上 AppArmor等產(chǎn)品安全團隊成長起來后再考慮更重的方案。另外有個補充思路很多嵌入式產(chǎn)品既沒上 SELinux 也沒上 AppArmor光靠用戶隔離和文件系統(tǒng)權限就已經(jīng)過了等保檢查。這不是說安全模塊沒用而是說安全加固要從投入產(chǎn)出比的角度考慮先把便宜好用的手段用足再考慮重型武器。8.3 思考題三日志審計最重要的是什么是不是日志越多越好日志審計的目標不是收集盡量多的日志而是讓安全團隊在事件發(fā)生后可以回答“發(fā)生了什么、為什么發(fā)生、影響范圍多大”。所以日志審計的第一原則是“關鍵事件不漏”而不是“所有事件都收”。如果日志量大到無法審查和存儲真正關鍵的事件反而會被淹沒。嵌入式設備的日志審計要把握好這幾點采集登錄、提權、用戶變更、服務變更、內(nèi)核異常這幾類關鍵安全事件日志要盡量遠程存儲防止本地銷毀日志要定期人工或自動巡檢不能只存不看。我還見過一些項目日志服務器收到了告警郵件但沒人去看入侵發(fā)生了半年才發(fā)現(xiàn)。再完善的日志審計體系沒有對應的響應機制價值都會大打折扣。8.4 思考題四硬件加密、安全啟動與軟件安全加固的關系有讀者問產(chǎn)品已經(jīng)做了安全啟動Secure Boot和硬件加密是不是軟件安全加固就不需要了這兩者不是替代關系而是互補關系。安全啟動解決的是設備固件的完整性和真實性——防止攻擊者把山寨固件或者被篡改的固件刷進設備里。硬件加密解決的是存儲數(shù)據(jù)被物理拆解后無法被直接讀取——防止攻擊者把 Flash 焊下來讀到密鑰和敏感數(shù)據(jù)。而軟件安全加固解決的是系統(tǒng)運行期的安全問題——一個攻擊者已經(jīng)通過某個漏洞拿到了一個普通用戶權限他能不能提權到 root能不能在系統(tǒng)里留下持久化后門能不能通過網(wǎng)絡橫向移動這些問題安全啟動和硬件加密都管不了。一個完整的設備安全體系應該是安全啟動做可信根、硬件加密做數(shù)據(jù)保護、最小化和權限硬化做攻擊面收斂、日志審計做事件追溯、防火墻做網(wǎng)絡邊界。每一層解決一段問題組合起來才是真正可靠的縱深防御。9. 關于加固工具的選型建議在寫這一部分之前先說一個很現(xiàn)實的問題嵌入式 Linux 安全加固的工具鏈遠沒有服務器生態(tài)那么豐富。很多好用的工具比如 OSSEC、Wazuh、Falco在嵌入式設備上根本跑不起來因為對內(nèi)存、CPU、存儲的要求太高了。所以嵌入式安全加固的邏輯不是“堆工具”而是“用對機制”。Buildroot 和 Yocto 是構建系統(tǒng)層面的兩個主流選擇。Buildroot 的優(yōu)勢是簡單直接、配置項清晰適合產(chǎn)品形態(tài)相對固定的團隊Yocto 的優(yōu)勢是靈活性強、組件版本可控性高、有完整的 layer 機制適合需要長期演進、多產(chǎn)品線復用的團隊。如果你剛入門建議先用 Buildroot 把加固流程跑通理解了整條鏈路之后再遷移到 Yocto。內(nèi)核層面的安全檢查可以借助checksec腳本掃描內(nèi)核和二進制的安全屬性比如 NX、PIE、Stack Canary、RELRO 是否開啟。用戶空間的二進制在編譯時建議加上這些安全編譯選項大部分交叉編譯工具鏈默認可能沒有開全需要你主動添加編譯參數(shù)。這個檢查要放到 CI 流程里每次構建之后自動掃描一遍防止某個新加入的組件把關掉了。文件系統(tǒng)層面的輔助工具有busybox本身和一些靜態(tài)分析腳本。檢查 rootfs 里有沒有帶 setuid 位的文件可以用這條命令find /path/to/rootfs -perm -4000 -type f檢查有沒有對外開放的監(jiān)聽端口可以用ss -lntup這些基礎命令在加固驗證階段非常實用建議整理成一個檢查腳本放到發(fā)布流程里。10. 最后再分享一點個人經(jīng)驗嵌入式 Linux 安全加固這件事做完一輪之后最深的感受是真正的難點不在技術而在產(chǎn)品節(jié)奏的對抗。安全加固每一項動作都牽動著功能可用性、性能開銷、開發(fā)排期、現(xiàn)場運維效率你加固得越狠運維起來就越不自由——調(diào)試工具被裁了、遠程操作被限制了、日志要定期清理了這些都會帶來額外的維護成本。拿我們自己做的一個邊緣網(wǎng)關產(chǎn)品來說。第一版固件出廠時rootfs 可寫、root 空密碼、telnet 還開著那時候的功能迭代確實很快現(xiàn)場出了任何問題都能第一時間遠程登進去查。后來有一批設備因為弱口令被入侵被當成肉雞去掃描外網(wǎng)的其他主機客戶的運維團隊發(fā)現(xiàn)之后直接下了禁止接入公網(wǎng)的通知整個項目停了一周。從那以后我們徹底改變了思路每一版固件發(fā)布前都跑安全基線檢查telnet 關掉、root 密碼隨機化、分區(qū)只讀、防火墻規(guī)則固定成模板。功能迭代的效率確實受了點影響但再也沒有出現(xiàn)過因為安全問題導致的項目停滯。所以我建議每一個做嵌入式 Linux 產(chǎn)品的團隊安全加固不是等產(chǎn)品穩(wěn)定了再考慮而是從第一個可啟動的固件版本開始就要把基本的加固動作加上。哪怕是先關掉 telnet、改掉默認密碼、把 rootfs 掛成只讀這三件事成本極低卻能在產(chǎn)品上線后攔住絕大多數(shù)腳本小子的試探。后面再逐步按著最小化裁剪、權限硬化、日志審計、防火墻的順序一層一層加厚防御。安全是一個持續(xù)演進的過程沒有一招制敵的銀彈但只要把每層防護都做到位你的設備就會比絕大多數(shù)同類產(chǎn)品硬得多。