shell 模块
shell 模块让节点按运营人员列出的调用方的请求,运行运营人员已批准的命令。典型用途是开发板或远程机器:需要从网络另一端重启服务、读取日志或烧写固件,并取回输出,且权限可随时收回。
该模块是唯一把网络请求变成宿主机命令的模块,因此它是加法模块:不属于默认构建,只包含在带有标签 shell 的构建中(install.sh --shell 或 ./build.sh --with shell)。参见 模块。
三项相互独立的决定
节点是否运行远程命令,取决于三项决定,每一项都须有意作出。
构建。不带标签
shell的二进制不含这部分代码。带该标签的构建在作出第二项决定之前,不注册任何能力,也不运行任何命令。配置。只有
config.json中有modules.shell块,且该块至少定义一条命令或设置了allow_arbitrary,模块才会工作。含有该模块但没有该块的二进制处于未启用状态。调用方名单。有命令而调用方名单为空的块会拒绝每一次调用。名单为空表示没有任何人,而不是所有人。
调用方还必须通过节点的入站策略。默认策略为 closed,因此调用方须先列入 peers.allow(anet peers allow <aid>),调用才会到达模块。两份名单各司其职:peers.allow 决定谁可以把任务交给这台机器,模块的名单决定其中谁可以运行命令。不要把 shell.* 能力列入 inbound.public_capabilities,否则会放陌生人进门,只剩模块的名单这一道屏障。
不带调用方身份的调用会被拒绝,除非 allow_local 为 true。身份为空不能被当作“本机且可信”,否则某个遗漏身份的调用点会不留痕迹地绕过整份名单。
配置
{
"modules": {
"shell": {
"commands": {
"restart-app": {"run": "systemctl restart myapp", "description": "Restart the application"},
"tail-log": {"run": "journalctl -u myapp -n 200 --no-pager"},
"flash": {"run": "/opt/tools/flash.sh", "args": true, "timeout_s": 600}
},
"allow_file": "/etc/anet/shell-allow",
"timeout_s": 30,
"max_output_bytes": 65536
}
}
}
键 |
含义 |
|---|---|
|
命令行。它经解释器运行,因此可以使用管道、重定向和 |
|
能力列表中的说明。 |
|
|
|
工作目录。 |
|
该命令的时间限制,覆盖全局值。 |
|
允许调用的 AID,以内联列表给出,在启动时读取。 |
|
包含允许调用的 AID 的文件,每行一个; |
|
放行不带调用方身份的调用。默认为 |
|
提供 |
|
全局时间限制,单位为秒。默认为 30,最大值为 86,400(24 小时)。 |
|
返回输出的最大大小。默认为 65,536,最大值为 4 MiB。 |
|
解释器。默认为 |
启动时,若没有任何调用方被允许,或 allow_arbitrary 已开启,节点会向标准错误写出警告。
能力
能力 |
行为 |
|---|---|
|
报告节点定义了哪些命令,以及名单上的调用方数量。远程调用方只得到数量而不是 AID,使被接受的调用方无从得知节点还信任哪些其他机器。本机调用得到完整名单。 |
|
运行一条已定义的命令。 |
|
运行调用方发来的命令。仅当 |
命令的名称会成为能力标识,如 shell.run@restart-app,而节点会通告它实际应答的能力。因此,任何能浏览 hub 目录的人都能看到这台机器提供哪些命令,名单仍然保护着这些命令。不希望其存在被人知道的命令,名称应不携带任何信息。
收回权限
allow_file 所指的文件在每次调用前都会重新读取,不做缓存。删除一行或整个文件,在下一次调用时即生效,无需重启。文件不存在等同于名单为空,即拒绝所有人;它既不是错误,也不是对所有人放行。内联列表 allow 只在重新加载配置时改变,因此生产节点应使用 allow_file。
行为
不提权。命令以守护进程所属用户的身份运行。需要 root 的命令要求守护进程以 root 运行,这是安装时的决定,体现在 unit 文件中。模块既不调用
sudo,也不调用setuid。参数不会变成命令。在
args: true时,调用方的每个参数都会加引号后追加。命令行属于运营人员,参数属于网络,两者不会混在一起。时间限制会结束整个进程组。只结束 shell,会让它启动的进程留在机器上。
输出有上限。过长的输出会被截断,并在输出中注明截断。
效果状态准确。退出状态 0 为
OK。其他状态为FAILED,并附带退出状态和标准错误。超过时间限制而被终止的命令为FAILED,消息中注明其被终止。每次运行都进入证据链。事件
anet.shell.command记录调用方、命令、退出状态和耗时。被拒绝的调用记为anet.shell.refused,并带有调用方的 AID。输出本身不在链上。
run 中的命令行不受限制。写下它的运营人员是被信任的,信任边界在运营人员所写的内容与网络发来的内容之间。