cjdns 完整上手指南:加密 IPv6 网络的安装、组网配置与运行管理

原创2026-10-05 16:22:301,627 阅读
文章标签:网络网络安全

cjdns 完整上手指南:加密 IPv6 网络的安装、组网配置与运行管理

本文以仓库 doc/README_ES.md(cjdns 官方西班牙语版使用指南)为骨架,结合 client/cjdroute2.c 的配置生成逻辑、doc/configure_ES.md 的配置项详解与 do 构建脚本等源码证据,系统讲解 cjdns 的安装、构建、节点互联、安全加固与管理接口使用,帮助读者从零搭建并运维一个 cjdns 加密 IPv6 节点。

项目概览:用公钥密码学重塑网络

cjdns 实现的是一个加密的 IPv6 网络,其两大基石是:

  • 公钥密码学用于地址分配:每个节点的 IPv6 地址由其公钥哈希确定性推导而来,地址即身份,无需任何中心化登记机构;
  • 分布式哈希表(DHT)用于路由:网络内节点通过 DHT 发现彼此、维护路由,无需依赖互联网骨干的路由基础设施。

这种设计带来了两个核心收益:接近零配置即可组网,以及规避现有网络普遍存在的安全与可扩展性问题。路由前缀 fc00::/8 下的地址均来自节点自身的密钥材料——在 crypto/AddressCalc.c 中可以看到,AddressCalc_addressForPublicKey() 对公钥连续做两次 SHA-512 哈希,取前 16 字节作为地址,并校验其落在 fc00::/8 前缀内(AddressCalc_validAddress 通过掩码比对完成校验)。因此,cjdns 网络中地址无法伪造:你声称的地址必须与你的公钥严格对应。

本文后续所有操作均围绕「下载源码 → 安装依赖 → 构建 → 生成配置 → 连接朋友 → 启动节点 → 运维管理」这一主线展开,对应的全部材料都可在当前仓库中找到。

一、安装依赖

官方安装说明面向 Debian 系发行版与 macOS,但同样适用于其他发行版(只是不能保证命令原样可用)。两个平台都建议先安装 Node.js,但它并非严格必需——如果系统没有 Node.js 或版本不被接受,构建过程会自动在源码树内下载并安装一份。

各发行版安装命令

Debian 系:

sudo apt-get install nodejs git build-essential python2.7

Fedora 22+:

sudo dnf install nodejs git
sudo dnf install @development-tools

RHEL 系(需先添加 EPEL 仓库):

sudo yum localinstall https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum install nodejs git
sudo yum install @development-tools

RHEL 也可直接安装发行打包好的版本:

sudo yum localinstall https://kojipkgs.fedoraproject.org//packages/cjdns/17.4/4.el6/src/cjdns-17.4-4.el6.src.rpm

Gentoo:

emerge --ask nodejs sys-devel/gcc dev-lang/python:3.4 dev-vcs/git

macOS:

brew install cjdns        # Homebrew
sudo port install cjdns   # MacPorts

OpenBSD:(当前支持尚属实验性)

pkg_add git node gcc gmake bash

请选择 gcc-4.8.1p2 或更新的版本。

FreeBSD: 所需组件已预置在 FreeBSD ports 中:

pkg install gmake node

Arch:

pacman -S cjdns

需要从源码构建时:

pacman -S nodejs git base-devel

也可通过 AUR 安装 cjdns-git 包。Arch 安装完成后配置位于 /etc/cjdroute.conf,服务管理使用:

systemctl start cjdns
systemctl stop cjdns

Solus:

sudo eopkg install nodejs git build-essential system.devel python gcc binutils kernel-headers xorg-server-devel

笔记本用户的提醒:启用 resume 服务

如果你在笔记本上使用 cjdns,系统从挂起(suspend)或休眠(hibernate)恢复后,cjdroute 需要几分钟才能重新适应网络状态。可以通过以下命令大幅加速这一过程:

systemctl enable cjdns-resume

cjdns-resume 服务会在系统从睡眠状态恢复时自动重启 cjdns。对应的 systemd 单元定义位于 contrib/systemd/cjdns-resume.service(cjdns.service 中通过 Also=cjdns-resume.service 声明了连带关系)。

二、从源码获取并构建

克隆仓库并进入源码目录:

git clone https://gitcode.com/gh_mirrors/cj/cjdns.git cjdns
cd cjdns

执行构建:

./do

看到如下输出即表示构建成功:

Build completed successfully, type ./cjdroute to begin setup.

查看 do 构建脚本可以看到完整的构建链路:脚本首先要求环境中存在 cargo(Rust 工具链,若缺失会直接报错退出),然后执行 cargo build --release(支持 --debug 参数切换调试构建),构建完成后自动运行 ./target/<profile>/testcjdroute all 执行全部测试套件(可通过环境变量 NO_TEST 跳过),最后将产物 cjdroute 与 cjdnstool 移动/链接到仓库根目录,供用户直接调用。

注意:与早期版本不同,当前构建体系基于 Rust/Cargo,python2.7 等依赖已不再是构建所必需。

三、生成并理解配置文件

不带任何参数直接运行 ./cjdroute 会打印帮助信息,列出全部可用命令行选项。从 client/cjdroute2.c 的 usage() 函数可以看到完整选项列表:

选项 说明
cjdroute --help 显示帮助信息
cjdroute --genconf [--eth] 生成一份配置文件并输出到 stdout;指定 --eth 时启用以太网信标(注意可能与 UDP 信标相互干扰)
cjdroute --genconf-seed [--eth] 从 stdin 读取 64 字节种子,由该种子确定性生成配置
cjdroute --version 打印本节点支持的协议版本
cjdroute --cleanconf < conf 输出一份干净(合法 JSON)的配置版本
cjdroute --nobg 无论配置如何,都不 fork 到后台

0. 检查 TUN 设备

cjdns 通过 TUN 设备把虚拟网络接入系统,因此首先要确认其可用性:

LANG=C cat /dev/net/tun
  • 输出 cat: /dev/net/tun: File descriptor in bad state —— 完美,设备存在且可用;
  • 输出 cat: /dev/net/tun: No such file or directory —— 需要手动创建设备节点:
sudo mkdir -p /dev/net &&
sudo mknod /dev/net/tun c 10 200 &&
sudo chmod 0666 /dev/net/tun

创建后重新执行 cat /dev/net/tun 验证。

  • 输出 cat: /dev/net/tun: Permission denied —— 你很可能运行在基于 OpenVZ 虚拟化平台的 VPS 上,需要联系供应商开启 TUN/TAP 设备(这是一项标准协议,供应商应当知道如何开启)。macOS 用户无需关心此步骤。

1. 生成配置文件

./cjdroute --genconf >> cjdroute.conf

务必保护你的配置文件! 配置文件遗失意味着丢失你的密码与全部连接凭据,曾经连到你节点的朋友将无法再连接;配置文件被窃取则可能让攻击者冒充你的身份。

推荐以仅本人可读写的方式生成:

(umask 077 && ./cjdroute --genconf > cjdroute.conf)

从 client/cjdroute2.c 的 genconf() 实现可以看到生成模板的完整细节,这有助于理解每个字段的来源:

  • 随机生成 4 个独立密码(Random_base32 生成 32 字节随机串并做 base32 编码);
  • 随机挑选一个 大于 1024 的端口(循环拒绝 <= 1024 的端口),用于 UDP 监听;
  • 调用 Key_gen() 生成 ed25519 密钥对,输出 base32 编码的 publicKey(带 .k 后缀)与 hex 编码的 privateKey,并由 AddrTools_printIp 打印出对应的 IPv6 地址;
  • 模板同时包含 admin、interfaces(UDPInterface 的 IPv4 与 IPv6 两个 bind 块)、router(含 dnsSeeds、publicPeer、supernodes、TUN 接口、SocketInterface 备选、ipTunnel)、security、logging、noBackground、pipe、version 等完整配置骨架。

2. 找到一位「朋友」

要接入既有网络(例如 Hyperboria),你需要与一位已经在该网络内的人建立连接。这并非门槛,而是设计使然:

  1. 防止滥用:恶意者不太可能在接受他人善意接入之后还去破坏该系统;
  2. 替代而非叠加:cjdns 不是架设在旧互联网之上的一层,它的目标正是取代旧互联网。每一条连接最终都应被线缆、光纤或无线链路取代;
  3. 争议解决:一旦发生分歧,连接双方之间存在一条可追溯的「朋友链」,为解决问题提供基础。

寻找朋友的方式:加入官方社区(例如 IRC 频道 #cjdns,见下文的运维章节),或参考 Hyperboria 的节点地图找到离你较近的节点。仓库 README_ES.md 中列举了 Hyperboria、Project Meshnet、/r/darknetplan 等社区资源与 doc/projectGoals.md、doc/Whitepaper.md 等背景文档。

3. 连接你的节点与朋友的节点

出站连接(OUTbound,你 → 朋友)

在配置文件中找到 connectTo 段:

// Nodes to connect to.
"connectTo":
{
    // Add connection credentials here to join the network
    // Ask somebody who is already connected.
}

添加多个朋友节点后,配置形如(注意 JSON 语法、逗号分隔):

// Nodes to connect to.
"connectTo":
{
    //friend_1 (IPv4: 0.1.2.3; IPv6 fcaa:5bac:66e4:713:cb00:e446:c317:fc39)
    "0.1.2.3:45678":
    {
        "login": "k.alexander"
        "password": "thisIsNotARealConnection_1",
        "publicKey": "thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k"
    }

    //friend_2 (IPv4: 5.1.2.3; IPv6 fcbb:5bac:66e4:713:cb00:e446:c317:fc39)
    "5.1.2.3:5678":
    {
        "login": "k.alexander"
        "password": "thisIsNotARealConnection_2",
        "publicKey": "thisIsJustForAnExampleDoNotUseThisInYourConfFile_2.k"
    }
}

你可以在 connectTo 中按 JSON 语法添加任意数量的连接。

入站连接(INbound,朋友 → 你)

在配置文件中找到 authorizedPasswords 段:

"authorizedPasswords":
[
    // A unique string which is known to the client and server.
    {"password": "password001", "login": "default-login"}

    // More passwords should look like this.
    // {"password": "password002", "login": "my-second-peer"}
    // {"password": "password003", "login": "my-third-peer}
    // {"password": "password004", "login": "my-fourth-peer"}
    ...

    // "your.external.ip.goes.here:45678":{"login": "default-login", "password": "password001","publicKey":thisisauniqueKEY_001.k}

],

为多个朋友配置入站权限后,配置形如:

"authorizedPasswords":
[
    // A unique string which is known to the client and server.
    {"password": "thisisauniquestring_001", "user": "k.alexander"}

    // More passwords should look like this.
    //William Jevons (IPv4: 0.1.2.3; IPv6 fcaa:5bac:66e4:713:cb00:e446:c317:fc39)
    {"password": "thisisauniquestring_002", "user": "William Jevons"}
    //Marilyn Patel (IPv4: 5.1.2.3; IPv6 fcbb:5bac:66e4:713:cb00:e446:c317:fc39)
    {"password": "thisisauniquestring_003", "user": "Marilyn Patel"}
    // {"password": "thisisauniquestring_004"}
    ...

    // "your.external.ip.goes.here:45678":{"password": "thisisauniquestring_001","publicKey":thisisauniqueKEY_001.k}
],

当朋友(如 William Jevons)要从你这边做入站连接时,你需要提供给他 5 项信息:

  1. 你的公网 IPv4 地址;

  2. 你配置文件 bind 字段中监听的端口,即:

    // Bind to this port.
    "bind": "0.0.0.0:yourportnumberishere",
    
  3. 你为对方创建/取消注释的唯一密码:"password": "thisisauniquestring_002";

  4. 你的公钥:"publicKey": "thisisauniqueKEY_001.k";

  5. 用户名(如 William Jevons)。

对方拿到的连接凭证最终形如(含你的 IPv4 与端口):

"1.2.3.4:56789": {
    "login": "William Jevons",
    "password": "thisisauniquestring_002",
    "publicKey": "thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k"
}

请确认:连接可以由任一方发起(你→朋友 或 朋友→你),但一旦连接建立,双向流量都会流通。

从源码层面看,这些入站密码的运行时管理由 admin/AuthorizedPasswords.c 提供:它注册了 AuthorizedPasswords_add(参数 password 必填,user/ipv6 可选)、AuthorizedPasswords_remove 与 AuthorizedPasswords_list 三个管理函数,分别对应 Ca_addUser_ipv6、Ca_removeUsers 与 Ca_getUsers,这意味着你可以通过管理接口在不重启节点的前提下动态增删入站密码。

更完整的配置项语义(包括顶部密钥区、admin、UDPInterface/ETHInterface、router、ipTunnel、security 等逐字段讲解),参见 doc/configure_ES.md。其中特别强调:authorizedPasswords 中的密码当前不做任何密钥派生(KDF),请务必使用真正随机、不可猜测的长字符串,不要使用可记忆的短密码。

4. 加固你的系统:检查监听中的服务

节点启动后,你就拥有一个全新的 IPv6 地址。操作系统可能会自动让某些网络服务监听并暴露到这个新地址上。如果这不是你的本意,请检查是否意外开放了多余的服务。详细的安全建议参见 doc/network-services.md。

5. 启动节点

sudo ./cjdroute < cjdroute.conf

想将日志写入文件:

sudo ./cjdroute < cjdroute.conf > cjdroute.log

停止节点:

sudo killall cjdroute

遇到问题时的第一反应同样是 killall cjdroute 回到干净状态,然后用 pgrep cjdroute 或 top 确认进程是否仍在运行。

注意: 上述命令以 root 身份运行 cjdns,使其能无权限障碍地完成系统配置。若要以非 root 用户运行,参见 doc/non-root-user.md。

从 client/cjdroute2.c 的启动流程可以看到 cjdroute < cjdroute.conf 的完整内部链路:程序从 stdin 读取 benc 序列化的配置(Rffi_Benc_readConfFromStdin),读取 admin 段的 bind 与 password,检查是否已有实例在运行(checkRunningInstance 向管理端口发 ping,超时 100ms 判定无实例),随后通过 UNIX 管道(pipe 字段,默认 cjdroute.sock)spawn 出 core 进程,把 privateKey 与 admin 配置预先下发给 core,再通过管理接口由 Configurator_config() 完成接口、对端、密码等全部配置,最后按 noBackground 配置决定是否退出前台、让 core 留在后台运行。

6. 接入 IRC

欢迎来到网络!你现在是一名网络管理员了。管理员身份伴随着责任,其中一项就是:当你的设备出问题时你要能被找到。请保持在 IRC(#cjdns 频道)在线,以便他人能联系到你。仓库中也有对应的问题报告流程(见下文)。

四、管理接口(Admin API)

cjdroute 运行起来后,管理接口默认监听在 udp://localhost:11234(可在 cjdroute.conf 中修改,--genconf 生成的默认值是 "bind": "127.0.0.1:11234",密码默认为 "NONE",以便 ./tools/cexec 等本地工具无需额外配置即可调用)。完整协议说明参见 doc/admin-api.md,仓库 contrib/ 目录下还有大量可与该接口交互的工具。

管理接口的工作方式非常直接:向 core 的 UDP 端口发送一个 bencoded 请求包,core 返回 bencoded 响应。例如用 ping 函数探测:

echo -n 'd1:q4:pinge' | nc6 -u -t 1 -n -w3 127.0.0.1 11234

每条消息可以携带 txid(事务 ID),响应会原样反射回来,从而支持并发多请求:

echo '{ "q": "ping", "txid": "my request" }' ...
# 响应示例:{ "txid" : "my request", "q" : "pong" }

认证:任何会改变 core 状态的函数都需要认证,认证按请求逐次进行(ping 等只读函数可免认证调用):

  1. 向服务端请求一个 cookie(cookie 仅 10 秒有效,必须在同一脚本内完成请求与使用);
  2. 计算「admin 密码 + cookie」的 SHA-256,将哈希与 cookie 放入请求;
  3. 再对「整个请求 + 哈希 + cookie」计算一次 SHA-256,替换掉请求中的哈希。

三步分别把密码绑定到 cookie(防止哈希被重放)、把密码与 cookie 绑定到请求(防止中间人篡改请求内容)。改造后的请求结构为 {"q":"auth", "aq":"ping", "hash":<计算值>}——原 q 字段移到 aq,q 变为 auth。

仓库内置的调用工具是 ./tools/cexec,例如:

./tools/cexec 'memory()'

会调用 core 的 memory() 函数获取当前内存消耗;./tools/cjdnslog 则通过该接口拉取 cjdns 日志。可用函数的完整列表可通过 Admin_availableFunctions(带 page 参数分页,因为单包 UDP 装不下全部函数)查询。

此外还有多种语言的客户端库可供选择:

五、深入配置:一份配置文件的完整解读

结合 doc/configure_ES.md 与 --genconf 的生成模板,我们逐段理解生成出来的配置文件。

顶部:密钥与地址

{
    // Private key:
    // Your confidentiality and data integrity depend on this key, keep it secret!
    "privateKey": "823e4EXAMPLE...c70",

    // This key corresponds to the public key and ipv6 address:
    "publicKey": "u2jf87mgqlxfzdnywp60z3tx6tkulvgh2nyc2jk1zc69zzt2s8u0.k",
    "ipv6": "fcff:a215:1e7b:a4e9:c00d:0813:93b3:7c87",
  • privateKey:负责你所有出入站数据的加密,必须保密、不得共享;
  • publicKey:你交给其他节点的公钥,对方用它加密发往你的数据,只有你的私钥能解密,从而保证传输内容无人可窥;
  • ipv6:你在 cjdns 网络中的地址,由公钥哈希安全推导,全局唯一。

值得注意的是:publicKey 与 ipv6 字段只是便利项,并非必需。删除它们后,cjdns 启动时会从 privateKey 重新推导公钥,再由公钥推导 IPv6 地址——三者关系是确定性的,因此只保留最底层的 privateKey 反而能避免配置不一致。

入站密码:authorizedPasswords

"authorizedPasswords":
[
    {"password": "zxl6zgxpl4stnuybdt0xlg4tn2cdl5h", "user": "default-login"}

    // More passwords should look like this.
    // {"password": "10ru8br0mhk25ccpvubv0sqnl7kuc6s", "user": "my-second-peer"},
    // {"password": "y68jm490dztxn3d2gvuv09bz55wqmjj", "user": "my-third-peer"},
    // {"password": "bnpphnq205v8nf2ksrs1fknfr572xzc", "user": "my-fourth-peer"},

    // "your.external.ip.goes.here:33808":{"login": "default-login", "password":"zxl6zgxpl4stnuybdt0xlg4tn2cdl5h","publicKey":"u2jf87mgqlxfzdnywp60z3tx6tkulvgh2nyc2jk1zc69zzt2s8u0.k"}
],
  • password:其他节点凭此密码接入你的节点,对方应把它填进自己 connectTo 段的 password 字段;
  • 每个密码块可附带 user 属性,运行 peerStats 时,用特定密码连接的节点会与其 user 字符串关联显示,方便识别;
  • "your.external.ip.goes.here:33808" 这条注释是你给朋友出站连接用的完整凭证模板,建议同时添加 "name":"so-and-so" 便于知道谁在连你;不再想让某人连接时,只需找到对应行删除或注释掉即可。

管理接口:admin

"admin":
{
    // Port to bind the admin RPC server to.
    "bind": "127.0.0.1:11234",

    // Password for admin RPC server.
    "password": "j6mukf2khplcgpbzz0kulb8hu0xq2v9"
},
  • bind:管理接口绑定的 IP 与端口。你不希望陌生人连上来,所以默认绑定回环地址即可;
  • password:调用需认证的管理函数所需的密码。仅监听本地地址时可用 "NONE";一旦要把管理接口暴露到网络,务必改成强密码。若修改了密码,./tools/cexec 等工具需要你提供 ~/.cjdnsadmin 配置或相应参数。

连接接口:interfaces

当前最常用的是经传统互联网连接其他节点的 UDPInterface;也可用 ETHInterface 在二层直接与另一台机器对接(注意那不是你熟悉的 TCP/IP 连接)。

"interfaces":
{
    // The interface which connects over UDP/IP based VPN tunnel.
    "UDPInterface":
    [
        {
            // Bind to this port.
            "bind": "0.0.0.0:33808",

            // Nodes to connect to.
            "connectTo":
            {
                // Add connection credentials here to join the network
                // Ask somebody who is already connected.
            }
        }
    ]
    ...
},
  • UDPInterface.bind:监听连接的 IP 与端口;
  • UDPInterface.connectTo:你要连接的节点配置,格式为 "12.34.56.78:12345": { "password": "...", "publicKey": "...k" }。注意虽然他人可能添加 node 等字段,但 cjdns 当前只解析 password 与 publicKey;
  • 新版生成的配置在 UDPInterface 中还包含一个面向 IPv6 的第二块,connectTo 键形如 "[2001:db8::2:1]:12345";
  • 可选的 "dscp" 字段用于设置 QoS 的 DSCP 值,默认 0;
  • beacon 字段控制局域网自动发现(取值 0/1/2,见下)。

ETHInterface 的 connectTo 用法与 UDP 类似,只是键从「IP:端口」换成 MAC 地址,如 "01:02:03:04:05:06":{"password":"a","publicKey":"b"};bind 指定要绑定的设备名(可用伪名 "all" 尝试连接所有设备);beacon 控制二层自动发现:0 关闭、1 只接受并尝试连接信标发送方、2 既发送又接受信标。

路由:router

"router":
{
    "interface":
    {
        // The type of interface (only TUNInterface is supported for now)
        "type": "TUNInterface"

        // The name of a persistent TUN device to use.
        // This for starting cjdroute as its own user.
        // *MOST USERS DON'T NEED THIS*
        //"tunDevice": "tun0"
    },
  • type:当前仅支持 TUNInterface;
  • tunDevice:指定使用的持久 TUN 设备名(以自有用户身份启动 cjdroute 时用),大多数用户不需要。

作为 TUN 的替代,模板中还提供了 _disabled_interface 的 SocketInterface 方案:它会创建一个 UNIX socket 来发出本应走 TUN 设备的数据包。启用方法是把 TUN 接口改名为 _disabled_interface、把 SocketInterface 段改名为 interface。

router 段还包含 dnsSeeds(DNS 种子节点,用于自动添加对端,列表第一项被信任用于提供 snode)、publicPeer(设置后节点会把公开对接凭据发布给 supernode,id 以 PUB_ 开头表示公开共享,不设置则凭据完全私有;也可用 ipv4/ipv6 硬编码外部无法探测的地址与端口)、supernodes 等字段。

IP 隧道:ipTunnel

ipTunnel 允许经由 cjdns 网络把 IPv4 与 ICANN IPv6 隧道化(把 cjdns 交换层当作 VPN 载体)。该功能仍在完善中,虽然可用,但要让其真正有用需要两端都做不少手工配置:

"ipTunnel":
{
    // Nodes allowed to connect to us.
    // When a node with the given public key connects, give them the
    // ip4 and/or ip6 addresses and prefix lengths listed.
    "allowedConnections":
    [
        // {
        //     "publicKey": "f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k",
        //     "ip4Address": "192.168.1.24",
        //     "ip4Prefix": 24,
        //     "ip6Address": "2001:123:ab::10",
        //     "ip6Prefix": 0
        // },
        // 也可以只指定一种地址:
        // {
        //     "publicKey": "ydq8csdk8p8ThisIsJustAnExampleAddresstxuyqdf27hvn2z0.k",
        //     "ip4Address": "192.168.1.25",
        //     "ip4Prefix": 24
        // }
    ],

    "outgoingConnections":
    [
        // 连接一台或多台机器并向其索取 IP 地址:
        // "6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k",
    ]
}
  • allowedConnections:允许连到你的节点列表,按公钥识别,并为对方分配 ip4Address/ip6Address 及前缀长度。ip4Prefix 表示从 TUN 可路由的地址集合前缀长度(如向 VPN 通告 10.123.45.0/24 的公司网络则设为 24,默认 IPv4 为 32、IPv6 为 128,即默认不安装路由);ip4Alloc 表示分配给客户端的地址块大小(如想发给对方 192.168.123.0~192.168.123.3 四个地址则设为 30,默认 IPv4 为 32、IPv6 为 128,即 1 个地址);
  • outgoingConnections:主动连接一台或多台机器并向它们索取 IP 地址。

安全:security

// Dropping permissions.
"security":
[
    // Change the user id to this user after starting up and getting resources.
    {"setuser": "nobody"},
    ...
],

--genconf 生成的默认安全配置(见 client/cjdroute2.c)包含以下策略,设计目标是在 cjdns 自身被严重利用时,防止攻击蔓延到宿主系统:

  • setuser:启动并获取资源后切换用户 ID 以沙箱化 cjdns 进程。keepNetAdmin 置 1 时保留网络管理能力(若置 0,IPTunnel 将无法设置 IP 地址、ETHInterface 无法热添加新接口);用 { "setuser": 0 } 可禁用;
  • chroot:改变 cjdns 可见的文件系统根目录(默认 /var/run/),阻断其访问沙箱外的文件;用户无 chroot 权限时会安静失败,可用 { "chroot": 0 } 禁用;
  • nofiles:已废弃的安全特性,禁止 cjdns 打开任何文件(会连带阻止设置 IP 地址与热添加 ETHInterface 设备),默认禁用;
  • noforks:阻止 cjdns 派生新进程或线程,可遏制多类漏洞攻击宿主系统,默认启用;
  • setupComplete:客户端通过一串 RPC 调用完成 core 设置后,以此通知 core 安全规则已全部生效、可以开始运行;没有该调用时 core 会在几秒内以返回码 232 退出,防止客户端崩溃后 core 停留在不安全状态。

一个反直觉但重要的结论(模板注释中明确说明):以 root 启动 cjdns 反而更安全,因为非 root 用户无权使用 chroot 或切换用户名,会限制上述缓解措施的实际效果。

其他杂项

  • logging:默认日志通过管理 socket 提供;取消注释 "logTo": "stdout" 可让 cjdns 直接向 stdout 输出日志;
  • noBackground:设为非 0 时 cjdns 不 fork 到后台,推荐与 "logTo":"stdout" 配合使用(也可用命令行 --nobg 强制);
  • pipe:管理控制管道路径,只给文件名时 cjdns 会猜测完整路径(Unix 默认在 /tmp/,Windows 为 \\.\pipe\),默认值 cjdroute.sock;
  • version:置 2 表示以严格模式解析配置,允许用 cjdnsconf 外部编辑。

以 systemd 方式运行的示例

打包发行版(如 Arch 的 pacman -S cjdns)通常已内置服务单元。仓库自带的 contrib/systemd/cjdns.service 展示了一个完整的托管示例,其关键设计值得借鉴:

  • ExecStartPre:若 /etc/cjdroute.conf 不存在,则以 umask 077 自动 cjdroute --genconf 生成一份(并打印警示信息);
  • ExecStartPre:追加 ip -6 route add blackhole fc00::/8 metric 500,把整个 cjdns 地址空间的黑洞路由提前占位;
  • ExecStart:以 --nobg 前台方式运行 cjdroute < /etc/cjdroute.conf;
  • Restart=always:进程退出后自动重启;
  • ProtectHome / ProtectSystem:systemd 级沙箱加固。

六、问题报告与安全披露

仓库 doc/README_ES.md 给出的问题报告流程如下:

  1. 不要在本仓库发 issue,请前往 hyperboria/bugs 的 issues 区提交;
  2. 同时到 IRC 频道与社区成员沟通;
  3. 两种途径的典型结果:
    • 有人心情好,顺手修复;
    • 你心情好,自己修复;
    • 无人关注,问题被搁置,直到未来有人撞上并修复,或随一次大规模代码重构而被超越;
    • 暂时无人能修,但问题因其对代码演进的重要参考价值而被记录,最终可能由深入了解代码的人解释清楚,并向 docs/bugs 目录提交修复。

安全相关问题同样通过 IRC 公开报告,与普通 bug 无异——项目没有封闭的专家小组,默认的安全披露方式就是完整描述问题。提交前可对照 doc/security_specification.md 判断一个疑似安全问题是否真的构成安全威胁。相关思路还可参考 doc/bugs/index.md 中的已知问题分类。

结语

从「安装依赖 → 源码构建 → 生成配置 → 找到朋友 → 出站/入站互联 → 启动运维 → 管理接口调用」,本文完整覆盖了 cjdns 节点的全生命周期。理解配置文件各字段的语义(尤其是密钥、密码与安全策略),掌握 --genconf 生成的模板结构,并善用管理接口与仓库提供的工具(./tools/cexec、./tools/cjdnslog、各语言客户端库),你就拥有了独立搭建、运维并诊断一个加密 IPv6 节点所需的全部能力。更多细节可继续深入阅读 doc/configure_ES.md、doc/admin-api.md 与 doc/network-services.md 等文档。

登录后查看全文
cjdns