#内核模块 #Linux 内核 #SPA #Netfilter #身份验证 #公钥

app dbtools

Drawbridge 的用户模式工具。一个使用 Netfilter 挂钩和内核支持的伯克利包过滤器 (BPF) 的第 4 层单包身份验证 Linux 内核模块

6 个稳定版本

1.0.5 2023 年 3 月 25 日
1.0.4 2022 年 11 月 15 日
1.0.3 2022 年 10 月 11 日
1.0.2 2022 年 9 月 19 日
1.0.0 2021 年 3 月 14 日

#739 in 命令行工具

每月 37 次下载

GPL-3.0-or-later

32KB
527

logo

Drawbridge 的用户模式工具包;一个第 4 层单包身份验证 (SPA) 模块,用于隐藏公共 facing 机器上的 TCP/UDP 端口并增加一层安全措施。

请阅读相应的 文章 以深入了解设计。

基本用法

sudo db auth --server [REMOTE_SERVER] --dport 53 -p udp --unlock [PORT_TO_UNLOCK]

为了给 db 二进制文件赋予 CAP_NET_RAW 权限,以便您无需 sudo 就可以运行它

chmod 500 ~/.cargo/bin/db
sudo setcap cap_net_raw=pe ~/.cargo/bin/db

创建一个 bash 别名以在您想要访问它所守护的端口时自动运行 db 也非常方便。

alias "connect"="db auth -s [REMOTE] -d 53 -p udp --unlock [PORT] && ssh -p [PORT] user@[REMOTE]"

构建和安装 Drawbridge 工具

用户模式工具现在是用 Rust 编写的!使用 cargo 构建和安装它们

git clone https://github.com/landhb/Drawbridge
cargo install --path Drawbridge/tools

# or 
cargo install dbtools

构建和安装 Drawbridge 模块

要在客户端机器上自动生成密钥,请运行以下命令

db keygen

keygen 工具的输出将生成三个文件: ~/.drawbridge/db_rsa~/.drawbridge/db_rsa.pubkey.h。请妥善保管 db_rsa,它是您的私钥。 key.h 是格式化为 C 头文件格式的公钥。它将被编译到内核模块中。

要简单地编译内核模块,只需将 key.h 带入内核模块目录,然后运行 make

# on the server compile the module and load it
# pass the ports you want to monitor as an argument
mv key.h module/include/
cd module/
make
sudo modprobe x_tables
sudo insmod drawbridge.ko ports=22,445 

您可能需要安装内核头文件以编译模块,您可以使用以下命令来完成此操作

sudo apt-get install linux-headers-$(uname -r)
sudo apt-get update && sudo apt-get upgrade

此代码已在 4.X 到 5.9 版本的 Linux 内核上进行了测试。我不计划支持低于 4.X 的任何版本,但如果您在较新的内核上遇到一些可移植性问题,请告诉我。

自定义唯一的 'knock' 数据包

如果您希望进一步自定义您的敲击,您可以编辑客户端/bridge.c 中的 TCP 报头选项。例如,也许您希望使您的敲击数据包具有 PSH、RST 和 ACK 标志以及 3104 的窗口大小。将这些选项打开

// Flags
(*pkt)->tcp_h.fin = 0;   // 1
(*pkt)->tcp_h.syn = 0;   // 2
(*pkt)->tcp_h.rst = 1;   // 4
(*pkt)->tcp_h.psh = 1;   // 8
(*pkt)->tcp_h.ack = 1;   // 16
(*pkt)->tcp_h.urg = 0;   // 32


(*pkt)->tcp_h.window = htons(3104);

然后确保您能创建一个BPF过滤器来匹配那个特定的数据包。对于上面的情况,我们会得到RST(4) + PSH(8) + ACK(16) = 28,TCP头部窗口字段的偏移量是14。

"tcp[tcpflags] == 28 and tcp[14:2] = 3104"

这里有一篇关于TCP标志的简要文章,如果您不熟悉的话。由于tcpdump不支持IPv6的TCP偏移量快捷方式,因此您需要使用与IPv6头部相关的偏移量来支持它。

(tcp[tcpflags] == 28 and tcp[14:2] = 3104) or (ip6[40+13] == 28 and ip6[(40+14):2] = 3104)"

在您有一个工作的BPF过滤器之后,您需要编译它并将过滤器包含在服务器端的内核模块中。因此,为了编译这个并将其输出放置在内核/listen.c中的struct sock_filter code[]。

tcpdump "(tcp[tcpflags] == 28 and tcp[14:2] = 3104) or (ip6[40+13] == 28 and ip6[(40+14):2] = 3104)" -dd

这给我们带来了

struct sock_filter code[] = {
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 0, 9, 0x00000800 },
	{ 0x30, 0, 0, 0x00000017 },
	{ 0x15, 0, 13, 0x00000006 },
	{ 0x28, 0, 0, 0x00000014 },
	{ 0x45, 11, 0, 0x00001fff },
	{ 0xb1, 0, 0, 0x0000000e },
	{ 0x50, 0, 0, 0x0000001b },
	{ 0x15, 0, 8, 0x0000001c },
	{ 0x48, 0, 0, 0x0000001c },
	{ 0x15, 5, 6, 0x00000c20 },
	{ 0x15, 0, 5, 0x000086dd },
	{ 0x30, 0, 0, 0x00000043 },
	{ 0x15, 0, 3, 0x0000001c },
	{ 0x28, 0, 0, 0x00000044 },
	{ 0x15, 0, 1, 0x00000c20 },
	{ 0x6, 0, 0, 0x00040000 },
	{ 0x6, 0, 0, 0x00000000 },
};

就是这样!您有一个DrawBridge内核模块将解析的唯一数据包!

手动生成RSA密钥对

首先生成密钥对

openssl genrsa -des3 -out private.pem 2048

将公钥导出到一个单独的文件

openssl rsa -in private.pem -outform DER -pubout -out public.der

如果您看一下格式,您会看到这并不完全符合内核struct公钥表示,因此我们需要从DER格式的BIT_STRING字段中提取相关数据。

vagrant@ubuntu-xenial:~$ openssl asn1parse  -in public.der -inform DER

0:d=0  hl=4 l= 290 cons: SEQUENCE
4:d=1  hl=2 l=  13 cons: SEQUENCE
6:d=2  hl=2 l=   9 prim: OBJECT            :rsaEncryption
17:d=2  hl=2 l=   0 prim: NULL
19:d=1  hl=4 l= 271 prim: BIT STRING        <-------------------- THIS IS WHAT WE NEED

您可以看到BIT_STRING在偏移量19处。从这里我们可以提取私钥格式的相关部分以提供给内核模块。

openssl asn1parse  -in public.der -inform DER -strparse 19 -out output.der

您会注意到这与RFC 3447兼容,其中概述了RSA公钥的ASN.1语法。

0:d=0  hl=4 l= 266 cons: SEQUENCE
4:d=1  hl=4 l= 257 prim: INTEGER           :BB82865B85ED420CF36054....
265:d=1  hl=2 l=   3 prim: INTEGER           :010001

如果您需要将output.der作为C风格字节字符串导出

hexdump -v -e '16/1 "_x%02X" "\n"' output.der | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

依赖关系

~11–18MB
~441K SLoC