Nix:一个纯粹的函数式包管理器


Nix 既是一个Javascript包管理器:一个可以下载和运行的预构建包的来源,也是一种函数性语言,可以帮助我们以可重现的方式编写“构建表达式”。Nix 表达式是一个具有一个副作用的函数:创建构建本身的规范。
Nix 方法的主要思想是将软件组件彼此隔离地存储在中央组件存储中,路径名包含构建组件所涉及的所有输入的加密哈希。
 
为什么?
与包管理器打交道是当今开发人员体验的核心,但仍然充满陷阱。我们倾向于将我们的信任外包给包缓存,例如 NPM 或 Hex,然后将它们返回的任何内容合并到我们的系统中。大多数情况下,这很好用,因为包管理器有诚实行事的动机。
但是,存在单点故障,作为这个中心故障点变得有问题的一个例子是:攻击者可以修改一个包以在所谓的数字供应链攻击中包含恶意软件。
这就是今天现有包管理器所没有的:可重复性。
可重复性是指在您自己的系统上一点一点地重新创建包的构建的能力。无论以您的方式抛出什么包,您都可以在本地重新构建它并检查您是否具有预期的构建输出。这类似于通过计算其 sha256 哈希值来验证从 Web 下载的二进制文件,并将其与二进制文件发布者提供的哈希值进行比较。
它消除了信任提供商的需要,并使开发人员检测故障或攻击的成本大大降低。可重复性意味着您的系统和应用程序的安全性,以及您的开发人员的安心。
达到这种可重现状态的方法是对本地环境不做任何假设。Nix 中的所有内容都必须由用户明确声明。大多数构建工具或环境都依赖于对本地环境的假设,并尽可能地处理它,但如果你想提供一种在任何地方构建任何东西的方法,你就必须放弃对本地环境的任何束缚。Nix 通过强制你声明你想要的包以及如何将它们组合在一起来做到这一点。因为您的环境或构建的每个部分都是明确的,所以您可以实现隔离和可重现性。
 
如何做到? 
首先,使用 Nix 构建的所有内容都放在一个中央存储中,默认情况下是 `/nix/store`,这与您的 Linux FHS(文件系统层次结构标准)不同。
其次,每个构建的工件都用一个哈希标识,它是包的完整依赖图的摘要标识符,涉及的特定于平台的构建步骤,以及定义如何构建它的 Nix 表达式。
假设您的公司维护了一个 25 年前使用 C 编译器构建的工具。它无法安全升级,因为如果升级,整个世界的银行系统都会崩溃。也许您保留一台专用笔记本电脑作为数字时间胶囊来维护此工具。相反,您可以使用所需的任何版本的工具创建一个 Nix shell,固定且永不升级。这个环境是孤立的,并且可以与系统上的所有其他 C 版本配合使用,因为它只是另一个 Nix 包。您可以与任何人共享此环境,他们只需一个“nix-shell”命令即可拥有与您相同的环境。