如何反向代理原始 TCP 数据包

如何反向代理原始 TCP 数据包

我正在设计一个系统,其中远程设备安全地将状态更新发送到中央日志服务器进行聚合。在服务器上,我使用 Redis + Logstash + Elasticsearch 解决方案。发送到服务器的数据很敏感,必须加密。我正在努力寻找一种高效且安全的方法将日志“LPUSH”到 Redis 列表中。

这些设备目前能够将以下 Redis 命令直接发送到端口 6379。

"*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"

在服务器上的 Redis 中创建正确的键和列表条目。

下一步是将 redis 置于防火墙后面并加密数据包。我目前的尝试是使用 Apache 作为反向代理。设备将与 Apache 建立双向 SSL 连接,然后使用环回接口将解密的信息反向代理到端口 6379。双向 SSL 连接毫无问题地建立,并且消息被转发到 Redis。不幸的是,这不是设备发送的消息。tcpdump 告诉我以下内容...

tcpdump -nnXvv -i lo host localhost and port 6379

127.0.0.1.48916 > 127.0.0.1.6379: Flags [P.], cksum 0xfeab (incorrect -> 0x9415),
seq 1:132, ack 1, win 1025, options [nop,nop,TS val 299310518 ecr 299310518], 
length 131
0x0000:  4500 00b7 12b7 4000 4006 2988 7f00 0001  E.....@.@.).....
0x0010:  7f00 0001 bf14 18eb ce9c 0f04 e920 abec  ................
0x0020:  8018 0401 feab 0000 xxxx xxxx xxxx xxxx  ................
0x0030:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  ....*3./.HTTP/1.
0x0040:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  1..Host:.localho
0x0050:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  st:6379..X-Forwa
0x0060:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  rded-For:.xx.xxx
0x0070:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  .xxx.xxx..X-Forw
0x0080:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  arded-Server:.xx
0x0090:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  .xxx.xxx.xx..Con
0x00a0:  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx  nection:.Keep-Al
0x00b0:  xxxx xxxx xxxx xx                        ive....

从 ASCII 翻译中可以看出,Apache 正在 *3 后的第一个 CRLF 处截断消息,并附加 HTTP 标头信息以进行转发,这是理所当然的。当然,Redis 会回复错误,因为该消息不再使用 Redis 序列化协议 (RESP) 进行格式化。

1)有没有办法配置 Apache 来盲目转发原始 TCP 数据包?

2)如果没有,那么有没有一个标准的开源解决方案可以解决这个问题?

感谢您的时间!

答案1

如果除了 Apache 对标头的附加内容之外所有功能均正常运行,则可以通过 mod_headers 关闭这些功能:http://httpd.apache.org/docs/2.2/mod/mod_headers.html

答案2

虽然我的直觉告诉我 Apache+PHP 不是最佳解决方案,但它是一个快速且迄今为止令人满意的解决方案。Apache 将 HTTP 消息发送到 PHP 脚本,然后使用环回接口提取帖子并将其转发到 redis。为了“后人”,我在此处粘贴了脚本的骨架版本。

<?php
    $file = fopen("/some/logging.txt","a");
    $entityBody = file_get_contents('php://input');
    $redisSocket = fsockopen("localhost", 6379, $errno, $errstr, 30);
    if (!$redisSocket) {
        fwrite($file, "Error: $errstr ($errno)\n");
    } else {
        fwrite($redisSocket, "$entityBody");
        $redisResponse = fgets($redisSocket);
        if (!$redisResponse) {
            fwrite($file, "No response from Redis");
        } else {
            fwrite($file, "Redis Response: $redisResponse");
        }
    }
?>

我尽量避免使用脚本将事物“粘合”在一起,但到目前为止,它提供了额外的好处,允许我检查输入数据的完整性并创建自定义日志和响应。

相关内容