Files
bpf-developer-tutorial/18-further-reading/ebpf-security.zh.html

464 lines
47 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE HTML>
<html lang="en" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>eBPF 运行时的安全性与面临的挑战 - bpf-developer-tutorial</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../https://github.com/eunomia-bpf/bpf-developer-tutorial.html">https://github.com/eunomia-bpf/bpf-developer-tutorial</a></li><li class="chapter-item expanded affix "><li class="part-title">入门文档</li><li class="chapter-item expanded "><a href="../0-introduce/index.html"><strong aria-hidden="true">1.</strong> lesson 0-introduce</a></li><li class="chapter-item expanded "><a href="../1-helloworld/index.html"><strong aria-hidden="true">2.</strong> lesson 1-helloworld</a></li><li class="chapter-item expanded "><a href="../2-kprobe-unlink/index.html"><strong aria-hidden="true">3.</strong> lesson 2-kprobe-unlink</a></li><li class="chapter-item expanded "><a href="../3-fentry-unlink/index.html"><strong aria-hidden="true">4.</strong> lesson 3-fentry-unlink</a></li><li class="chapter-item expanded "><a href="../4-opensnoop/index.html"><strong aria-hidden="true">5.</strong> lesson 4-opensnoop</a></li><li class="chapter-item expanded "><a href="../5-uprobe-bashreadline/index.html"><strong aria-hidden="true">6.</strong> lesson 5-uprobe-bashreadline</a></li><li class="chapter-item expanded "><a href="../6-sigsnoop/index.html"><strong aria-hidden="true">7.</strong> lesson 6-sigsnoop</a></li><li class="chapter-item expanded "><a href="../7-execsnoop/index.html"><strong aria-hidden="true">8.</strong> lesson 7-execsnoop</a></li><li class="chapter-item expanded "><a href="../8-exitsnoop/index.html"><strong aria-hidden="true">9.</strong> lesson 8-execsnoop</a></li><li class="chapter-item expanded "><a href="../9-runqlat/index.html"><strong aria-hidden="true">10.</strong> lesson 9-runqlat</a></li><li class="chapter-item expanded "><a href="../10-hardirqs/index.html"><strong aria-hidden="true">11.</strong> lesson 10-hardirqs</a></li><li class="chapter-item expanded affix "><li class="part-title">进阶文档和示例</li><li class="chapter-item expanded "><a href="../11-bootstrap/index.html"><strong aria-hidden="true">12.</strong> lesson 11-bootstrap</a></li><li class="chapter-item expanded "><a href="../12-profile/index.html"><strong aria-hidden="true">13.</strong> lesson 12-profile</a></li><li class="chapter-item expanded "><a href="../13-tcpconnlat/index.html"><strong aria-hidden="true">14.</strong> lesson 13-tcpconnlat</a></li><li class="chapter-item expanded "><a href="../14-tcpstates/index.html"><strong aria-hidden="true">15.</strong> lesson 14-tcpstates</a></li><li class="chapter-item expanded "><a href="../15-javagc/index.html"><strong aria-hidden="true">16.</strong> lesson 15-javagc</a></li><li class="chapter-item expanded "><a href="../16-memleak/index.html"><strong aria-hidden="true">17.</strong> lesson 16-memleak</a></li><li class="chapter-item expanded "><a href="../17-biopattern/index.html"><strong aria-hidden="true">18.</strong> lesson 17-biopattern</a></li><li class="chapter-item expanded "><a href="../18-further-reading/index.html"><strong aria-hidden="true">19.</strong> lesson 18-further-reading</a></li><li class="chapter-item expanded "><a href="../19-lsm-connect/index.html"><strong aria-hidden="true">20.</strong> lesson 19-lsm-connect</a></li><li class="chapter-item expanded "><a href="../20-tc/index.html"><strong aria-hidden="true">21.</strong> lesson 20-tc</a></li><li class="chapter-item expanded "><a href="../21-xdp/index.html"><strong aria-hidden="true">22.</strong> lesson 21-xdp</a></li><li class="chapter-item expanded affix "><li class="part-title">高级主题</li><li class="chapter-item expanded "><a href="../22-android/index.html"><strong aria-hidden="true">23.</strong> 在 Android 上使用 eBPF 程序</a></li><li class="chapter-item expanded "><a href="../30-sslsniff/index.html"><strong aria-hidden="true">24.</strong> 使用 uprobe 捕获多种库的 SSL/TLS 明文数据</a></li><li class="chapter-item expanded "><a href="../23-http/index.html"><strong aria-hidden="true">25.</strong> 使用 eBPF socket filter 或 syscall trace 追踪 HTTP 请求和其他七层协议</a></li><li class="chapter-item expanded "><a href="../29-sockops/index.html"><strong aria-hidden="true">26.</strong> 使用 sockops 加速网络请求转发</a></li><li class="chapter-item expanded "><a href="../24-hide/index.html"><strong aria-hidden="true">27.</strong> 使用 eBPF 隐藏进程或文件信息</a></li><li class="chapter-item expanded "><a href="../25-signal/index.html"><strong aria-hidden="true">28.</strong> 使用 bpf_send_signal 发送信号终止进程</a></li><li class="chapter-item expanded "><a href="../26-sudo/index.html"><strong aria-hidden="true">29.</strong> 使用 eBPF 添加 sudo 用户</a></li><li class="chapter-item expanded "><a href="../27-replace/index.html"><strong aria-hidden="true">30.</strong> 使用 eBPF 替换任意程序读取或写入的文本</a></li><li class="chapter-item expanded "><a href="../28-detach/index.html"><strong aria-hidden="true">31.</strong> BPF 的生命周期:使用 Detached 模式在用户态应用退出后持续运行 eBPF 程序</a></li><li class="chapter-item expanded "><a href="../18-further-reading/ebpf-security.zh.html" class="active"><strong aria-hidden="true">32.</strong> eBPF 运行时的安全性与面临的挑战</a></li><li class="chapter-item expanded "><a href="../34-syscall/index.html"><strong aria-hidden="true">33.</strong> 使用 eBPF 修改系统调用参数</a></li><li class="chapter-item expanded "><a href="../35-user-ringbuf/index.html"><strong aria-hidden="true">34.</strong> eBPF开发实践使用 user ring buffer 向内核异步发送信息</a></li><li class="chapter-item expanded "><a href="../36-userspace-ebpf/index.html"><strong aria-hidden="true">35.</strong> 用户空间 eBPF 运行时:深度解析与应用实践</a></li><li class="chapter-item expanded "><a href="../37-uprobe-rust/index.html"><strong aria-hidden="true">36.</strong> 使用 uprobe 追踪 Rust 应用程序</a></li><li class="chapter-item expanded "><a href="../38-btf-uprobe/index.html"><strong aria-hidden="true">37.</strong> 借助 eBPF 和 BTF让用户态也能一次编译、到处运行</a></li><li class="chapter-item expanded affix "><li class="part-title">bcc 和 bpftrace 教程与文档</li><li class="chapter-item expanded "><a href="../bcc-documents/kernel-versions.html"><strong aria-hidden="true">38.</strong> BPF Features by Linux Kernel Version</a></li><li class="chapter-item expanded "><a href="../bcc-documents/kernel_config.html"><strong aria-hidden="true">39.</strong> Kernel Configuration for BPF Features</a></li><li class="chapter-item expanded "><a href="../bcc-documents/reference_guide.html"><strong aria-hidden="true">40.</strong> bcc Reference Guide</a></li><li class="chapter-item expanded "><a href="../bcc-documents/special_filtering.html"><strong aria-hidden="true">41.</strong> Special Filtering</a></li><li class="chapter-item expanded "><a href="../bcc-documents/tutorial.html"><strong aria-hidden="true">42.</strong> bcc Tutorial</a></li><li class="chapter-item expanded "><a href="../bcc-documents/tutorial_bcc_python_developer.html"><strong aria-hidden="true">43.</strong> bcc Python Developer Tutorial</a></li><li class="chapter-item expanded "><a href="../bpftrace-tutorial/index.html"><strong aria-hidden="true">44.</strong> bpftrace Tutorial</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">bpf-developer-tutorial</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="ebpf-运行时安全性面临的挑战与前沿创新"><a class="header" href="#ebpf-运行时安全性面临的挑战与前沿创新">eBPF 运行时安全性:面临的挑战与前沿创新</a></h1>
<p>郑昱笙</p>
<p>扩展伯克利数据包过滤器eBPF代表了我们与现代操作系统交互和扩展其能力方式的重大演变。作为一种强大的技术它使得Linux内核能够响应事件运行沙盒程序eBPF已成为系统可观察性、网络和安全特性的基石。</p>
<p>然而像任何与内核紧密接口的系统一样eBPF 运行时本身的安全性至关重要。在这篇博客中,我们将深入探讨常被忽视的 eBPF 安全性问题,探索旨在保护 eBPF 的机制本身如何被加固。我们将解析 eBPF 验证器的作用,审视当前的访问控制模型,并调查研究中的潜在改进机会。</p>
<h2 id="目录"><a class="header" href="#目录">目录</a></h2>
<!-- TOC -->
<ul>
<li><a href="#ebpf-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%AE%89%E5%85%A8%E6%80%A7%E9%9D%A2%E4%B8%B4%E7%9A%84%E6%8C%91%E6%88%98%E4%B8%8E%E5%89%8D%E6%B2%BF%E5%88%9B%E6%96%B0">eBPF 运行时安全性:面临的挑战与前沿创新</a>
<ul>
<li><a href="#%E7%9B%AE%E5%BD%95">目录</a></li>
<li><a href="#ebpf%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%E9%AA%8C%E8%AF%81%E5%99%A8%E7%A1%AE%E4%BF%9D%E5%AE%89%E5%85%A8">eBPF如何通过验证器确保安全</a>
<ul>
<li><a href="#ebpf%E9%AA%8C%E8%AF%81%E5%99%A8%E6%98%AF%E4%BB%80%E4%B9%88%E4%BB%A5%E5%8F%8A%E5%AE%83%E7%9A%84%E4%BD%9C%E7%94%A8">eBPF验证器是什么以及它的作用</a></li>
</ul>
</li>
<li><a href="#ebpf-%E9%AA%8C%E8%AF%81%E5%99%A8%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86">eBPF 验证器的工作原理</a>
<ul>
<li><a href="#verifier-%E7%9A%84%E6%8C%91%E6%88%98">verifier 的挑战</a></li>
<li><a href="#%E5%BC%BA%E5%8C%96-ebpf-%E9%AA%8C%E8%AF%81%E5%99%A8%E7%9A%84%E5%85%B6%E4%BB%96%E7%A0%94%E7%A9%B6%E5%B7%A5%E4%BD%9C">强化 eBPF 验证器的其他研究工作</a></li>
</ul>
</li>
<li><a href="#ebpf%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6%E7%9A%84%E9%99%90%E5%88%B6">eBPF访问控制的限制</a>
<ul>
<li><a href="#cap_bpf">CAP_BPF</a></li>
<li><a href="#bpf%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4">bpf命名空间</a></li>
<li><a href="#%E6%97%A0%E7%89%B9%E6%9D%83ebpf">无特权eBPF</a></li>
<li><a href="#%E5%8F%AF%E4%BF%A1%E7%9A%84%E9%9D%9E%E7%89%B9%E6%9D%83bpf">可信的非特权BPF</a></li>
</ul>
</li>
<li><a href="#%E4%B8%80%E4%BA%9B%E5%85%B6%E4%BB%96%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88">一些其他的解决方案</a>
<ul>
<li><a href="#moat%E5%AE%9E%E7%8E%B0%E5%AE%89%E5%85%A8%E7%9A%84bpf%E5%86%85%E6%A0%B8%E6%89%A9%E5%B1%95%E9%9A%94%E7%A6%BB">MOAT实现安全的BPF内核扩展隔离</a></li>
<li><a href="#%E5%88%A9%E7%94%A8%E5%8A%A8%E6%80%81%E6%B2%99%E7%AE%B1%E9%87%8A%E6%94%BE%E6%97%A0%E7%89%B9%E6%9D%83ebpf%E7%9A%84%E6%BD%9C%E5%8A%9B">利用动态沙箱释放无特权eBPF的潜力</a></li>
<li><a href="#%E5%86%85%E6%A0%B8%E6%89%A9%E5%B1%95%E9%AA%8C%E8%AF%81%E6%98%AF%E4%B8%8D%E5%88%87%E5%AE%9E%E9%99%85%E7%9A%84">内核扩展验证是不切实际的</a></li>
<li><a href="#wasm-bpfwebassembly-ebpf%E5%BA%93%E5%B7%A5%E5%85%B7%E9%93%BE%E5%8F%8A%E8%BF%90%E8%A1%8C%E6%97%B6">Wasm-bpfWebAssembly eBPF库、工具链及运行时</a></li>
<li><a href="#bpftime%E7%94%A8%E6%88%B7%E7%A9%BA%E9%97%B4ebpf%E8%BF%90%E8%A1%8C%E6%97%B6%E7%94%A8%E4%BA%8Euprobe%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E9%92%A9%E5%AD%90%E5%8F%8A%E6%8F%92%E4%BB%B6"><code>bpftime</code>用户空间eBPF运行时用于uprobe、系统调用钩子及插件</a></li>
<li><a href="#%E7%BB%93%E8%AE%BA">结论</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- /TOC -->
<h2 id="ebpf如何通过验证器确保安全"><a class="header" href="#ebpf如何通过验证器确保安全">eBPF如何通过验证器确保安全</a></h2>
<p>eBPF的安全框架在很大程度上取决于其验证器的强大性能。这个组件充当守门人确保只有安全且符合规范的程序被允许在内核空间运行。</p>
<h3 id="ebpf验证器是什么以及它的作用"><a class="header" href="#ebpf验证器是什么以及它的作用">eBPF验证器是什么以及它的作用</a></h3>
<p>eBPF验证器的核心是静态代码分析器。它的主要功能是在BPF程序指令执行前进行审查。它在内核中审查程序副本操作目标包括</p>
<ul>
<li>
<p><code>确保程序终止</code></p>
<p>验证器使用深度优先搜索DFS算法遍历程序的控制流图确保它是一个有向无环图DAG。这对于保证程序不会进入无限循环从而确保其终止至关重要。它仔细检查任何无界循环和格式错误或越界跳转这些都可能破坏内核的正常操作或导致系统挂起。</p>
</li>
<li>
<p><code>确保内存安全</code></p>
<p>内存安全在内核操作中至关重要。验证器检查可能的越界内存访问这些访问可能导致数据损坏或安全漏洞。它还防范使用后释放的错误和对象泄漏这些是常见的可被利用的漏洞。除此之外它还考虑到硬件漏洞如幽灵Spectre执行缓解措施以防止此类旁路攻击。</p>
</li>
<li>
<p><code>确保类型安全</code></p>
<p>类型安全是验证器确保的另一个关键方面。通过防止类型混淆错误它有助于维护内核数据的完整性。eBPF验证器利用BPF类型格式BTF它允许准确理解和检查内核的复杂数据结构确保程序对这些结构的操作是有效和安全的。</p>
</li>
<li>
<p><code>防止硬件异常</code></p>
<p>硬件异常如除以零可能导致程序突然终止和内核恐慌。为了防止这种情况验证器包括检查未知标量的除法确保指令按照与aarch64规范一致的方式重写或处理这些规范规定了这类异常的安全处理。</p>
</li>
</ul>
<p>通过这些机制eBPF验证器在维护内核的安全性和稳定性中发挥了关键作用成为eBPF基础设施中不可或缺的组成部分。它不仅加强了系统的防御还维护了eBPF程序打算执行的操作的完整性使其成为eBPF生态系统中的重要部分。</p>
<h2 id="ebpf-验证器的工作原理"><a class="header" href="#ebpf-验证器的工作原理">eBPF 验证器的工作原理</a></h2>
<ul>
<li>
<p><strong>遵循控制流程图</strong>
验证器首先通过构建并遵循eBPF程序的控制流程图CFG来进行其分析。它细致地计算出每条指令的所有可能状态同时考虑BPF寄存器集和堆栈。然后根据当前的指令上下文进行安全检查。</p>
<p>其中一个关键步骤是跟踪程序私有BPF堆栈的寄存器溢出/填充情况。这确保了堆栈相关操作不会引起溢出或下溢,避免了数据破坏或成为攻击路径。</p>
</li>
<li>
<p><strong>控制流程图的回边处理</strong>
验证器通过识别CFG中的回边来有效处理eBPF程序内的循环。通过模拟所有迭代直到达到预定的上限从而确保循环不会导致无限制执行。</p>
</li>
<li>
<p><strong>处理大量潜在状态</strong>
验证器需要处理程序执行路径中大量潜在状态带来的复杂性。它运用路径修剪逻辑,将当前状态与之前的状态进行比较,判断当前路径是否与之前的路径“等效”,并且有一个安全的出口。这样减少了需要考虑的状态总数。</p>
</li>
<li>
<p><strong>逐函数验证以减少状态数量</strong>
为了简化验证过程,验证器进行逐函数分析。这种模块化的方法使得在任何给定时间内需要分析的状态数量得以减少,从而提高了验证过程的效率。</p>
</li>
<li>
<p><strong>按需标量精度追踪以进一步减少状态</strong>
验证器运用按需标量精度追踪来进一步减少状态空间。通过在必要时对标量值进行回溯,验证器可以更准确地预测程序的行为,优化其分析过程。</p>
</li>
<li>
<p><strong>超过“复杂性”阈值时终止并拒绝</strong>
为了保持实用性能,验证器设定了一个“复杂性”阈值。如果程序分析超过此阈值,验证器将终止过程并拒绝该程序。这样确保只有在可管理的复杂性范围内的程序被允许执行,实现了安全性与系统性能的平衡。</p>
</li>
</ul>
<h3 id="verifier-的挑战"><a class="header" href="#verifier-的挑战">verifier 的挑战</a></h3>
<p>尽管eBPF验证器执行得非常彻底但它面临着一系列重大挑战</p>
<ul>
<li>
<p><strong>对非root用户暴露时成为攻击目标</strong>
随着验证器日益复杂化它逐渐成为攻击者的青睐目标。由于eBPF具备强大的可编程性一旦攻击者绕过验证器并在操作系统内核中执行代码可能带来严重的后果。</p>
</li>
<li>
<p><strong>验证器正确性的复杂推断</strong>
确保验证器正确运行特别是在Spectre缓解方面并非易事。虽然部分形式的验证已经到位但仍有许多挑战特别是在即时编译器JIT和抽象解释模型等方面。</p>
</li>
<li>
<p><strong>有时错误拒绝有效程序</strong>
由于LLVM用于编译eBPF程序的基础架构的优化与验证器的理解能力之间有时存在不匹配导致一些有效的程序被错误地拒绝。</p>
</li>
<li>
<p><strong>为BPF程序类型维护“稳定的ABI”</strong>
为了确保操作系统内核更新时不影响生产环境中运行的BPF程序“稳定的ABI”至关重要。但在保持此稳定性的同时不断发展验证器和BPF生态系统也是一大挑战。</p>
</li>
<li>
<p><strong>性能与安全的平衡</strong>
在验证复杂的eBPF程序时性能与安全之间的平衡格外突出。虽然验证器必须保持高效以确保实用性但同时也不能在安全性上做出妥协因为它所验证的程序性能对现代计算系统至关重要。</p>
</li>
</ul>
<p>eBPF验证器代表了现代计算安全领域的创新它巧妙地在最大化程序可编程性和在内核级别保持坚固防御之间找到了平衡。</p>
<h3 id="强化-ebpf-验证器的其他研究工作"><a class="header" href="#强化-ebpf-验证器的其他研究工作">强化 eBPF 验证器的其他研究工作</a></h3>
<ul>
<li>领域内的规范与验证将形式化方法应用于Linux内核BPF即时编译器<a href="https://www.usenix.org/conference/osdi20/presentation/nelson">https://www.usenix.org/conference/osdi20/presentation/nelson</a></li>
<li>“使用三态数进行准确、精确和快速的抽象解释”Vishwanathan等。<a href="https://arxiv.org/abs/2105.05398">https://arxiv.org/abs/2105.05398</a></li>
<li>“通过自动化形式验证消除BPF JIT的漏洞”Nelson等。<a href="https://arxiv.org/abs/2105.05398">https://arxiv.org/abs/2105.05398</a></li>
<li>“使用证明携带方法构建正确且灵活的BPF验证器”Nelson等。<a href="https://linuxplumbersconf.org/event/7/contributions/685/">https://linuxplumbersconf.org/event/7/contributions/685/</a></li>
<li>“利用程序合成自动优化BPF程序”徐等。<a href="https://linuxplumbersconf.org/event/11/contributions/944/">https://linuxplumbersconf.org/event/11/contributions/944/</a></li>
<li>“简单且精确地静态分析不受信任的Linux内核扩展”Gershuni等。<a href="https://linuxplumbersconf.org/event/11/contributions/951/">https://linuxplumbersconf.org/event/11/contributions/951/</a></li>
<li>“对野外存在的投机型类型混淆漏洞进行分析”Kirzner等。<a href="https://www.usenix.org/conference/usenixsecurity21/presentation/kirzner">https://www.usenix.org/conference/usenixsecurity21/presentation/kirzner</a></li>
</ul>
<p>这些研究共同构成了一个强大而多维的研究倡议旨在加强eBPF验证的基础确保其作为扩展Linux内核能力的工具保持安全和高效。</p>
<p>更多eBPF验证器学习资料</p>
<ul>
<li>BPF和Spectre缓解瞬时执行攻击<a href="https://popl22.sigplan.org/details/prisc-2022-papers/11/BPF-and-Spectre-Mitigating-transient-execution-attacks">https://popl22.sigplan.org/details/prisc-2022-papers/11/BPF-and-Spectre-Mitigating-transient-execution-attacks</a></li>
</ul>
<h2 id="ebpf访问控制的限制"><a class="header" href="#ebpf访问控制的限制">eBPF访问控制的限制</a></h2>
<p>在像Ubuntu和SUSE这样的主要Linux发行版禁止非特权用户使用 eBPF 套接字过滤器和 CGroup 程序之后目前的eBPF访问控制模型只支持一个单一的权限级别。这一级别要求具备CAP_SYS_ADMIN能力用于所有功能。然而CAP_SYS_ADMIN因其广泛的特权特性特别是对于容器环境带来了显著的风险。</p>
<p>为应对这一问题Linux 5.6引入了更为细致的权限系统通过细分eBPF的能力。它引入了一个新的能力CAP_BPF用于调用bpf系统调用。此外安装某些类型的eBPF程序还需要其他能力如CAP_PERFMON用于性能监控或CAP_NET_ADMIN用于网络管理任务。这种设计旨在减少某些攻击类型如更改进程内存或eBPF映射这些攻击仍然需要CAP_SYS_ADMIN权限。</p>
<p>然而这些分割的能力并不能完全防止所有基于eBPF的攻击如拒绝服务DoS和信息窃取。攻击者可能利用这些漏洞制造针对容器的eBPF恶意软件。eBPF在云原生应用中的广泛应用加剧了这种威胁因为用户可能不小心部署了含有不可信eBPF程序的容器。</p>
<p>此外eBPF在容器化环境中的风险还没有被完全理解。一些容器服务可能无意中授予了eBPF权限例如为了实现文件系统挂载功能。现行的权限模型不足以防止容器中这些可能有害的eBPF功能被滥用。</p>
<h3 id="cap_bpf"><a class="header" href="#cap_bpf">CAP_BPF</a></h3>
<p>在传统上几乎所有的BPF行为都需要CAP_SYS_ADMIN权限这同时也授予了广泛的系统访问权限。随着时间的推移已经有努力将BPF权限与根权限分开。因此像CAP_PERFMON和CAP_BPF这样的能力被引入以便在不需要完整的系统管理员权限的情况下对BPF操作进行更精细的控制如读取内核内存和加载跟踪或网络程序。</p>
<p>然而CAP_BPF的范围存在模糊性导致了认知上的问题。不同于明确定义且用于加载内核模块的CAP_SYS_MODULECAP_BPF缺少命名空间约束这意味着它可以访问所有的内核内存而不仅仅是与容器相关的部分。这种广泛的访问权限是有问题的因为BPF程序中的验证器错误可能导致内核崩溃被视为安全漏洞导致过多的CVE常见漏洞和曝光被记录即使是那些已经修复的错误。这种对验证器错误的反应引发了不必要的警报和紧迫感迫使人们修补可能尚未更新的旧内核版本。</p>
<p>此外一些安全初创公司因利用人们对BPF能力的恐惧来推销产品而受到批评他们矛盾地使用BPF本身来防御他们强调的问题。这导致了一个矛盾的叙述其中BPF既被视为问题又被推崇为解决方案。</p>
<h3 id="bpf命名空间"><a class="header" href="#bpf命名空间">bpf命名空间</a></h3>
<p>目前的安全模型要求具备 CAP_SYS_ADMIN 权限,以便迭代 BPF 对象 ID并将其转换为文件描述符FD。这样做是为了防止非特权用户访问其他用户的BPF程序但同时也限制了他们检查自己的BPF对象这在容器环境中尤为挑战。</p>
<p>尽管用户可以使用CAP_BPF等特定权限运行BPF程序但他们缺少一种通用的方法来检查这些程序因为如bpftool这类工具需要CAP_SYS_ADMIN权限。目前在没有CAP_SYS_ADMIN的情况下的解决方法包括使用SCM_RIGHTS和Unix域套接字在</p>
<p>进程间共享BPF对象的FD但这被认为不够方便。</p>
<p>为解决这些限制Yafang Shao提议引入BPF命名空间。这将允许用户在特定的命名空间内创建BPF映射、程序和链接实现这些对象与其他命名空间用户的隔离。然而在一个BPF命名空间内的对象对其父命名空间仍然可见从而使系统管理员能够进行监督。</p>
<p>BPF命名空间在概念上与PID命名空间相似设计上直观易用。最初的实现重点是BPF映射、程序和链接未来计划将其扩展到其他BPF对象如BTF和bpffs。这可能使容器用户能够只追踪自己容器内的进程而不接触到其他容器的数据从而在容器化环境中提高安全性和易用性。</p>
<p>参考资料:</p>
<ul>
<li>BPF和安全<a href="https://lwn.net/Articles/946389/">https://lwn.net/Articles/946389/</a></li>
<li>云上eBPF的跨容器攻击<a href="https://www.usenix.org/system/files/usenixsecurity23-he.pdf">https://www.usenix.org/system/files/usenixsecurity23-he.pdf</a></li>
<li>bpf引入BPF命名空间<a href="https://lwn.net/Articles/927354/">https://lwn.net/Articles/927354/</a></li>
<li>ebpf在Linux命名空间运行的情况<a href="https://stackoverflow.com/questions/48815633/ebpf-running-in-linux-namespaces">https://stackoverflow.com/questions/48815633/ebpf-running-in-linux-namespaces</a></li>
</ul>
<h3 id="无特权ebpf"><a class="header" href="#无特权ebpf">无特权eBPF</a></h3>
<p>无特权eBPF是指非root用户将eBPF程序加载到内核的能力。由于安全问题这个特性在所有主要Linux发行版中默认是关闭的。安全担忧主要来自硬件漏洞如Spectre和内核漏洞恶意eBPF程序可能利用这些漏洞泄露敏感数据或攻击系统。</p>
<p>为应对这一挑战针对这些漏洞的各种版本如v1、v2和v4已经实施了缓解措施。然而这些缓解措施常常以牺牲eBPF程序的灵活性和性能为代价。这种权衡使得该功能对许多用户和应用场景来说变得不具吸引力和实用性。</p>
<h3 id="可信的非特权bpf"><a class="header" href="#可信的非特权bpf">可信的非特权BPF</a></h3>
<p>鉴于这些挑战目前正在探索一种名为“可信的非特权BPF”的中间方案。这种方法涉及一个白名单系统其中已经经过彻底审查并被认为是可信的特定eBPF程序可以由非特权用户加载。审查过程确保只有安全、适合生产环境的程序可以绕过特权要求保持安全性与功能性之间的平衡。这是朝着在不妥协系统完整性的前提下更广泛地使用eBPF的一步。</p>
<ul>
<li>
<p>宽松的LSM钩子由于LSM增加了进一步的限制因此被上游拒绝</p>
<p>Linux安全模块LSM的新钩子专门为BPF子系统设计旨在提供对BPF映射和BTF数据对象更细粒度的控制。这些是现代BPF应用程序的运作基础。</p>
<p>主要添加了两个LSM钩子bpf_map_create_security和bpf_btf_load_security它们提供了覆盖依赖于CAP_BPF和CAP_NET_ADMIN等能力的默认权限检查的能力。这种新机制允许更精细的控制使策略能够强制实施限制或为可信应用程序绕过检查转移决策至自定义LSM策略实现。</p>
<p>这种方法通过不要求应用程序具备与内核BPF子系统交互所需的BPF相关能力实现了更安全的默认设置。相反应用程序可以在没有这些权限的情况下运行只有被审查并信任的情况才被授予操作权限就像它们拥有提升的能力一样。</p>
</li>
<li>
<p>BPF令牌概念特权守护进程通过令牌fd委托BPF的子集</p>
<p>BPF令牌是一种新机制允许特权守护进程将BPF功能的子集委托给可信的非特权应用程序。这一概念使得容器化的BPF应用程序能够在用户命名空间内安全运行这在之前由于CAP_BPF能力的安全限制而无法实现。BPF令牌通过内核API创建和管理并可以在BPF文件系统中固定以实现控制访问。最新版本的补丁确保BPF令牌被限制在其在BPF文件系统中的创建实例中以防止误用。这种添加到BPF子系统的功能促进了更安全、更灵活的无特权BPF操作。</p>
</li>
<li>
<p>BPF签名作为守门员应用程序与BPF程序没有一刀切的解决方案</p>
<p>Song Liu提出了一个补丁通过一个新设备<code>/dev/bpf</code>允许无特权访问BPF功能。这个设备通过两个新的ioctl命令控制访问允许对该设备具有写权限的用户调用<code>sys_bpf()</code>。这些命令切换当前任务调用<code>sys_bpf()</code>的能力,权限状态存储在<code>task_struct</code>中。这种权限也可以由任务创建的新线程继承。引入了一个新的辅助函数<code>bpf_capable()</code>来检查任务是否通过<code>/dev/bpf</code>获得了权限。该补丁包括对文档和头文件的更新。</p>
</li>
<li>
<p>RPC到特权BPF守护进程根据用例/环境的限制</p>
<p>RPC方法例如bpfd与BPF令牌概念类似但它使用特权守护进程来管理BPF程序。这个守护进程负责加载和卸载BPF程序以及管理BPF映射。守护进程还负责在加载前验证BPF程序。这种方法比BPF令牌概念更灵活因为它允许更细致的控制BPF程序。然而它也更复杂带来了更多的维护挑战和单点故障的可能性。</p>
</li>
</ul>
<p>参考资料:</p>
<ul>
<li>宽松的LSM钩子<a href="https://lore.kernel.org/bpf/20230412043300.360803-1-andrii@kernel.org/">https://lore.kernel.org/bpf/20230412043300.360803-1-andrii@kernel.org/</a></li>
<li>BPF令牌概念<a href="https://lore.kernel.org/bpf/20230629051832.897119-1-andrii@kernel.org/">https://lore.kernel.org/bpf/20230629051832.897119-1-andrii@kernel.org/</a></li>
<li>使用fsverity和LSM守门员进行BPF签名<a href="https://www.youtube.com/watch?v=9p4qviq60z8">https://www.youtube.com/watch?v=9p4qviq60z8</a></li>
<li>签名BPF字节码<a href="https://lpc.events/event/16/contributions/1357/attachments/1045/1999/BPF%20Signatures.pdf">https://lpc.events/event/16/contributions/1357/attachments/1045/1999/BPF%20Signatures.pdf</a></li>
<li>bpfd<a href="https://bpfd.dev/">https://bpfd.dev/</a></li>
</ul>
<h2 id="一些其他的解决方案"><a class="header" href="#一些其他的解决方案">一些其他的解决方案</a></h2>
<p>这里还有一些关于如何提高eBPF安全性的研究或讨论。现有工作大致可分为三类虚拟化、软件故障隔离SFI和形式方法。使用类似WebAssembly的沙箱部署eBPF程序或在用户空间运行eBPF程序也是一种可能的解决方案。</p>
<h3 id="moat实现安全的bpf内核扩展隔离"><a class="header" href="#moat实现安全的bpf内核扩展隔离">MOAT实现安全的BPF内核扩展隔离</a></h3>
<p>Linux内核广泛使用伯克利数据包过滤器BPF允许用户编写的BPF应用在内核空间中执行。BPF使用验证器来静态检查用户提供的BPF代码的安全性。最近的攻击表明BPF程序可以绕过安全检查获得对内核内存的未授权访问这表明验证过程并非无懈可击。在本文中我们介绍了MOAT一个使用英特尔内存保护键MPK隔离潜在恶意BPF程序的系统。使用MPK强制执行BPF程序隔离并非易事MOAT被精心设计以解决技术障碍如硬件键数量有限和支持各种内核BPF辅助函数。我们已在原型内核模块中实现MOAT评估结果表明MOAT在多种真实场景下实现了BPF程序的低成本隔离例如对memcached数据库的数据包转发BPF程序隔离平均吞吐量损失为6%。</p>
<p><a href="https://arxiv.org/abs/2301.13421">https://arxiv.org/abs/2301.13421</a></p>
<blockquote>
<p>如果我们必须依赖硬件保护机制,那么语言安全性或验证是否仍然有必要来保护内核及其扩展?</p>
</blockquote>
<h3 id="利用动态沙箱释放无特权ebpf的潜力"><a class="header" href="#利用动态沙箱释放无特权ebpf的潜力">利用动态沙箱释放无特权eBPF的潜力</a></h3>
<p>出于安全原因如今非特权用户只能有限地通过扩展伯克利数据包过滤器eBPF来自定义内核。这非常遗憾尤其是考虑到近年来eBPF框架本身的范围不断扩大。我们提出SandBPF一种基于软件的内核隔离技术它通过动态地对eBPF程序进行沙箱化允许非特权用户安全地扩展内核释放eBPF的全部潜能。我们的早期概念验证表明SandBPF可以有效地防止eBPF本机安全机制即静态验证遗漏的漏洞同时在Web服务器基准测试中带来0%-10%的开销。</p>
<p><a href="https://arxiv.org/abs/2308.01983">https://arxiv.org/abs/2308.01983</a></p>
<blockquote>
<p>这可能与eBPF的原始设计相悖因为它并非设计为依赖沙箱来确保安全。如果你想使用软件故障隔离为什么不在内核中使用 webassembly</p>
</blockquote>
<h3 id="内核扩展验证是不切实际的"><a class="header" href="#内核扩展验证是不切实际的">内核扩展验证是不切实际的</a></h3>
<p>经过验证的eBPF字节码的出现预示着安全内核扩展的新时代。在本文中我们认为eBPF的验证器——其安全保证的来源——已成为一个负担。除了众所周知的错误和漏洞源于内核验证器的复杂性和临时性质我们还突出了一个令人担忧的趋势即向不安全的内核函数引入逃逸通道以辅助函数的形式旨在绕过验证器对表达性的限制不幸的是也绕过了其安全保证。我们提出了使用静态和轻量级运行时技术平衡的安全内核扩展框架。我们描述了一个以安全Rust为中心的内核扩展设计将消除内核验证器的需要提高表达性减少逃逸通道并最终提高内核扩展的安全性。</p>
<p><a href="https://sigops.org/s/conferences/hotos/2023/papers/jia.pdf">https://sigops.org/s/conferences/hotos/2023/papers/jia.pdf</a></p>
<blockquote>
<p>这可能限制内核只加载受信任第三方签名的 eBPF 程序因为内核本身无法独立验证它们。Rust工具链也存在漏洞。</p>
</blockquote>
<h3 id="wasm-bpfwebassembly-ebpf库工具链及运行时"><a class="header" href="#wasm-bpfwebassembly-ebpf库工具链及运行时">Wasm-bpfWebAssembly eBPF库、工具链及运行时</a></h3>
<p>Wasm-bpf是一种WebAssembly eBPF库、工具链和运行时能够使eBPF程序几乎无需更改代码就能构建成Wasm并在Wasm沙箱中实现跨平台运行。</p>
<p>它提供了一个可配置的环境具有限制性的eBPF WASI行为增强了安全性和控制力。这使得实现细粒度权限成为可能限制了对内核资源的访问提供了更安全的环境。例如可以限制eBPF程序仅用于特定类型的用途比如网络监控并且还可以配置哪些类型的eBPF程序能在内核中加载以及它们可以访问哪些类型的附加事件而无需修改内核eBPF的权限模型。</p>
<ul>
<li>Kubecon talk: <a href="https://sched.co/1R2uf">https://sched.co/1R2uf</a></li>
<li>Repo: <a href="https://github.com/eunomia-bpf/wasm-bpf">https://github.com/eunomia-bpf/wasm-bpf</a></li>
</ul>
<blockquote>
<p>将应用程序移植到WebAssembly需要额外的工作。此外内核eBPF的Wasm接口也需要进行维护就像BPF守护进程一样。</p>
</blockquote>
<h3 id="bpftime用户空间ebpf运行时用于uprobe系统调用钩子及插件"><a class="header" href="#bpftime用户空间ebpf运行时用于uprobe系统调用钩子及插件"><code>bpftime</code>用户空间eBPF运行时用于uprobe、系统调用钩子及插件</a></h3>
<p><code>bpftime</code> 是一个用户空间eBPF运行时它使现有的eBPF应用能够在非特权用户空间中运行使用相同的库和工具链。它为eBPF提供了Uprobe和系统调用跟踪点与内核uprobe相比有显著的性能提升且不需要手动的代码插桩或进程重启。运行时促进了用户空间共享内存中的进程间eBPF映射并与内核eBPF映射兼容实现了与内核eBPF基础架构的无缝操作。它包括了针对各种架构的高性能LLVM JIT以及专为x86设计的轻量级JIT和解释器。</p>
<ul>
<li><a href="https://arxiv.org/abs/2311.07923">https://arxiv.org/abs/2311.07923</a></li>
<li>Linux Plumbers: <a href="https://lpc.events/event/17/contributions/1639/">https://lpc.events/event/17/contributions/1639/</a></li>
<li>Repo: <a href="https://github.com/eunomia-bpf/bpftime">https://github.com/eunomia-bpf/bpftime</a></li>
</ul>
<blockquote>
<p>它的应用仅限于特定类型的eBPF程序和用例不是一种普遍适用的方法。</p>
</blockquote>
<h3 id="结论"><a class="header" href="#结论">结论</a></h3>
<p>在我们深入探讨eBPF安全性的多维领域时很明显虽然eBPF的验证器提供了坚实的首层防御但当前访问控制模型中存在的内在限制需要引起关注。我们已经考虑了从虚拟化、软件故障隔离和形式化方法到WebAssembly 或用户空间 eBPF 运行时的各种潜在解决方案每种方法都为加固eBPF抵抗漏洞提供了独特的途径。</p>
<p>然而像所有复杂系统一样新的问题和挑战持续出现。理论安全模型与其实际执行之间的差距呼吁着持续的研究和实验。eBPF安全的未来不仅前景光明而且还需要集体努力以确保该技术能够在保障系统安全的能力上被信赖地采纳。</p>
<blockquote>
<p>我们是 <a href="https://github.com/eunomia-bpf">github.com/eunomia-bpf</a> 开源社区希望能使eBPF更易使用并探索与 eBPF 相关的工具链和运行时等技术。</p>
<p>对eBPF技术感兴趣的朋友欢迎查看我们的教程代码仓库 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 和我们的网站 <a href="https://eunomia.dev/tutorials/">https://eunomia.dev/tutorials/</a>,以获取更多关于 eBPF 的相关资料和实践经验。原文地址:<a href="https://eunomia.dev/zh/blogs/ebpf-security">https://eunomia.dev/zh/blogs/ebpf-security</a></p>
</blockquote>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../28-detach/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../34-syscall/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../28-detach/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../34-syscall/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>