#shell #ssh #escaping #aaargh

app arghsh

为 SSH RPC 提供一个安全地传递你的参数向量的登录外壳

2 个版本

0.1.1 2022年1月4日
0.1.0 2021年4月26日

#65 in #escaping

Unlicense 协议

7KB

ARGHSH

这是什么

为 SSH RPC 提供一个安全地传递你的参数向量的登录外壳。适用于具有 execv 的 Posix 系统型。

为什么是它

当我们像这样(简化情况,Python)使用 SSH 作为 RPC 传输机制时

from subprocess import run
run(['ssh', 'somewhere', 'ls' '-la', 'a filename with spaces'])

那么迟早我们会发现远程端的 OpenSSH 服务器不会直接执行 ls —— 而是启动登录外壳(如 /etc/passwd 或其他来源所引用的),并带有两个参数

  1. -c
  2. ls-一个包含空格的文件名.

即使你使用 ForceCommand 也会这样做。换句话说,我们如此整洁地传递给 ssh 的参数向量框架丢失了;它已经被字符串连接并作为一个单一参数传递给登录外壳 >:(

被调用的登录外壳(比如 bash)随后将按与你在交互式使用外壳时相同的方式对其进行标记化。不幸的是,它不会简单地重建原始的三元参数向量 ['ls', '-la', 'a filename with spaces']。结果是,我们需要对 a filename with spaces 中的空格进行转义。或者我们可以对命令的文件名部分进行引号处理。这确实适用于这个简单案例,但为了处理任意输入,我们还需要对那些输入中已经存在的任何引号和转义字符进行转义。并注意可能出现的所有外壳元字符。沿着这个兔子洞走下去不一定很漂亮,也不一定愉快或容易。甚至可能暴露于注入,如果参数向量的一部分是危险输入的话。随着标记化轮次的增加(比如你的 RPCd 命令轮流进行 RPC —— 如 ssh somewhere ssh somewhere_else ls -la a filename with spaces),这种痛苦会加剧。 ARGH!

所以。如果原始参数向量元素边界得到了保留,使用广为人知且简单的框架和转义,那就好了。JSON 字符串数组是一种可从许多编程语言接近的方法,因此我们将使用它,并且我们将简单地避免任何外壳在循环中。在这种设置中,arghsh 扮演登录外壳的角色。

用法

构建 arghsh。为了创建一个静态链接的可执行文件,请继续阅读。然后使用 chsharghsh 设置为登录shell。你可能想为通过 arghsh 的SSH RPC创建一个专用用户,因为这个shell作为日常登录shell有点儿 *cough* 不便。如果你出于某种原因不想有完全不同的用户,那么一个巧妙的方法是通过复制一个 passwd条目,只修改用户名和登录shell,为现有用户创建另一个用户名。然后你可以通过将适当的用户名传递给SSH来调用“正常”的登录shell或 arghsh

一旦安装到远程系统上,你应该能够像这样整洁且无忧地进行SSH RPC(简单的Python示例)

from json import dumps
from subprocess import run

cmd = dumps(["/bin/ls", "-la", r"a file with spaces and !horrib;le$ shell meta\ charac'ters"])
run(['ssh', 'somewhere_with_arghsh', cmd])

太棒了。

创建静态链接的可执行文件(Linux)

对于Linux,Rust支持静态链接,但使用musl而不是glibc。所以,安装该平台

rustup target add x86_64-unknown-linux-musl

然后编译

cargo build --release --target=x86_64-unknown-linux-musl

就这样

$  ldd target/x86_64-unknown-linux-musl/release/arghsh     
	statically linked

依赖项

约2.5MB
约54K SLoC