目录

前言

Nginx+Lua或Openresty已经是网关代理的流行架构,比如Openresty,KONG,API6等,现有大多数网关均在http协议上工作,虽然Nginx已经支持了TCP、IP stream,但由于对除http协议外,其他协议解析和分析模块的缺乏,使得其应用条件非常有限,除基本的代理功能外,其他一些在http下可以实现的功能,均无法在其他协议,比如在SSH2、TDS(SQL server)、TNS(ORACLE)、LDAP等协议上实现、劫持、修改、响应、负载及会话管理等能力。而对上述能力的支持,均需要建立在对各种协议的详尽解析之上。SuProxy正是在这个条件下出现,目标是建立一个基于nginx+lua的四层的协议分析与代理库,把nginx的稳定性能引入到除http外更广泛的领域,并作为进一步审计,安全攻防,身份管理,协议分析的基础组件。同时Suproxy也设计为一个能灵活的可扩展架构,第三方可以自行扩展协议解析,进一步丰富开源协议解析领域的公共知识。

SuProxy当前还在试验阶段,使用示例和文档将会陆续在博客园,CSDN博客,Git上发布,欢迎大家测试,并提出意见与建议,

项目Git地址:

https://github.com/yizhu2000/suproxy

文档地址(英文):

https://github.com/yizhu2000/suproxy/blob/main/readme.md

有问题可以在此留言,或直接在git上提交

https://github.com/yizhu2000/suproxy/issues

也可以通过邮件yizhu2000@hotmail.com进行沟通

介绍

SuProxy是事件驱动的Lua TCP/IP代理库,用于分析,拦截,负载平衡和会话管理。它提供以下API:

  • 身份验证拦截:在身份验证期间读取或更改凭据,或引入自定义身份验证器。
  • 命令输入拦截:监视,过滤或更改命令输入。
  • 命令输出拦截:监视,过滤或更改命令相应。
  • 上下文收集:获取网络、用户、客户端和服务器信息,例如IP、端口、版本等。
  • 会话管理:将会话存储在Redis中,提供会话列表,终止和搜索会话的API。
  • 协议解析器:解析和编码协议数据包。
  • 负载平衡:具有容错能力的负载均衡。

当前,支持的协议包括SSH2,ORACLE TNS,SQLSERVER TDS,LDAP。

SSH协议 SQL服务器 甲骨文 LDAP
获取用户名 Y [^ 1] Y [^ 2] Y Y [^ 6]
获取密码 Y [^ 1] Y [^ 2] N Y [^ 6]
变更使用者名称 Y Y Y [^ 4] Y
更改密码 Y Y N Y
第三方认证 Y Y Y [^ 5] Y
获取命令 Y Y Y Y [^ 7]
得到回复 Y Y N Y [^ 7]
变更指令 Y Y [^ 3] Y [^ 3] N
获取网络上下文 (IP,端口等)。 Y Y Y Y
获取客户端上下文 (客户端/服务器程序名称 和版本等) Y Y Y N
  • [^ 1]:仅密码认证
  • [^ 2]:获取SQL Server的用户名和密码会禁用SSL加密
  • [^ 3]:更改SQL命令未经过全面测试,某些更改(例如,更改选择命令到删除命令)可能无法成功
  • [^ 4]:不支持为oracle10更改用户名
  • [^ 5]:仅支持基于用户名的身份验证
  • [^ 6]:不支持SSL
  • [^ 7]:仅支持搜索请求和回复

SuProxy由纯粹的Lua编写,基于事件驱动模式,SuProxy库的使用和扩展很简单:启动侦听器通道并处理其事件。下面的示例显示如何启动SSH2侦听器并处理SSH连接的身份验证成功事件。

  1. server {
  2. listen 22;
  3. content_by_lua_block {
  4. local ssh=require("suproxy.ssh2"):new()
  5. local channel=require("suproxy.channel"):new({{ip="192.168.1.135",port=22}},tds)
  6. channel:run()
  7. ssh.AuthSuccessEvent:addHandler(ssh,logAuth)
  8. }
  9. }

SuProxy提供基本的负载平衡功能。以下示例显示了如何将多个upstream传递给通道。

  1. package.loaded.my_SSHB=package.loaded.my_SSHB or
  2. require ("suproxy.balancer.balancer"):new{
  3. {ip="127.0.0.1",port=2222,id="local",gid="linuxServer"},
  4. {ip="192.168.46.128",port=22,id="remote",gid="linuxServer"},
  5. {ip="192.168.1.121",port=22,id="UBUNTU14",gid="testServer"}
  6. }
  7. local channel=require("suproxy.channel"):new(package.loaded.my_SSHB,ssh)

SuProxy可以在内存或Redis中收集和维护会话上下文,以下是SuProxy在ssh连接中收集的信息

  1. {
  2. "sid": "xxxxxxxxxxxx",
  3. "uid": "xxxx",
  4. "stype": "ssh2",
  5. "uptime": 1600831353.066,
  6. "ctime": 1600831353.066,
  7. "ctx": {
  8. "srvIP": "127.0.0.1",
  9. "client": "SSH-2.0-PuTTY_Release_0.74",
  10. "clientIP": "127.0.0.1",
  11. "clientPort": "56127",
  12. "username": "xxxx",
  13. "srvPort": 2222,
  14. "server": "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1"
  15. }
  16. }

安装

下载并拷贝

从这里https://github.com/yizhu2000/suproxy/releases 下载tar或zip包,解压并复制到Openresty的lualib目录。

依赖的库包括

lua-resty-openssl> 0.6 https://github.com/fffonion/lua-resty-openssl

lua-resty-logger-socket(仅用于示例项目)https://github.com/cloudflare/lua-resty-logger-socket

使用LuaRocks安装

在安装了luarocks环境的机器上可以使用下列命令安装本库及其依赖项

luarocks install suproxy

运行测试

luajit.exe ./suproxy/test.lua

使用简介

初始化Suproxy有4个步骤

  1. 创建处理器(processor),传入初始参数
  2. 创建通道(channel),传入upstream服务器列表及上一步创建的processor
  3. 处理处理器(processor)或通道(channel)的事件
  4. 启动通道(channel)

以下代码创建一个TNS通道并处理它的事件

  1. --Create a TNS processor and passing server version to it
  2. local tns=require("suproxy.tns"):new({oracleVersion=11})
  3. --Create a channel with upstreams and TNS processor
  4. local channel=require("suproxy.channel"):new({{ip="192.168.1.96",port=1521}},tns)
  5. --Processing events
  6. tns.AuthSuccessEvent:addHandler(tns,logAuth)
  7. tns.CommandEnteredEvent:addHandler(tns,forbidden)
  8. tns.CommandFinishedEvent:addHandler(tns,logCmd)
  9. tns.BeforeAuthEvent:addHandler(tns,simpleUserPassOracle)
  10. channel.OnConnectEvent:addHandler(channel,logConnect)
  11. --start the channel
  12. channel:run()

执行channel:run()之后,通道将在套接字上侦听。然后,事件将在不同的场合触发。用户程序应处理这些事件以完成其工作。通道和处理器都可以触发事件

处理器(processor)创建

处理器使用协议特定的解析器解析网络流。

  1. --xxx can be ssh2,ldap,tns,tds for now
  2. require("suproxy.xxx"):new(options)

使用上面的代码可以创建处理器(将xxx更改为处理器名称)。目前,SSH,TDS,TNS和LDAP处理器已准备就绪。处理器是否可以具有自定义的初始选项。例如,TNS处理器可以接受两个参数oracleVersion及swapPass,oracleVersion指定服务器主版本,而swapPass则告诉处理器是否需要在登录时更改用户密码。有关详细信息,请参阅每个处理器的文档。

通道(channel)创建

通道维护客户端和服务器之间的连接,从套接字读取数据并将手数据发送到不同的协议处理器以进行进一步处理,通道还负责将数据发送到上游服务器。

  1. require("suproxy.channel"):new({{ip="192.168.1.97",port=1521}},tns)

上面的代码创建了一个具有一个后端服务器(upstream)和TNS协议处理器的通道。如果将多个上游传递到通道,则[默认balancer]将从这些上游中随机选择。请注意,只有在调用channel.run()之后,通道才会启动。

Channel.c2pSend将数据放入客户端代理套接字

Channel.p2sSend将数据发送到代理服务器套接字

Channel.c2pRead从客户端代理套接字读取数据

Channel.p2cRead从代理服务器套接字读取数据

如何使用它们,请参阅[阅读和响应]

channel.new方法可以接受额外的选项来设置套接字超时

  1. options.c2pConnTimeout -- client-proxy connect timeout default 10000
  2. options.c2pSendTimeout -- client-proxy send timeout default 10000
  3. options.c2pReadTimeout -- client-proxy read timeout default 3600000
  4. options.p2sConnTimeout --proxy-server connect timeout default 10000
  5. options.p2sSendTimeout --proxy-server send timeout default 10000
  6. options.p2sReadTimeout --proxy-server read timeout 3600000
  7. require("suproxy.channel"):new(upstream,processor,options)

负载均衡

SuProxy提供基本的负载均衡功能。多个上游可以发送到通道。默认负载平衡器将从给定的上游服务器中随机选择一个。如果一个上游发生故障,负载均衡将暂时将该上游暂停一会儿。创建负载均衡:调用suproxy.balancer.balancer.new 方法,传入upstream list和suspendSpan(可选,默认为30秒,然后将负载均衡传递给通道的构造函数,如下所示:

  1. --here use "package.loaded" to ensure balancer only init once across multiple request, cause balancer will maintain the state of those upstreams.
  2. package.loaded.my_SSHB=package.loaded.my_SSHB or
  3. require ("suproxy.balancer.balancer"):new({
  4. {ip="127.0.0.1",port=2222,id="local",gid="linuxServer"},
  5. {ip="192.168.46.128",port=22,id="remote",gid="linuxServer"},
  6. {ip="192.168.1.121",port=22,id="UBUNTU14",gid="testServer"}
  7. },10)
  8. local channel=require("suproxy.channel"):new(package.loaded.my_SSHB,ssh)

每个上游必须包括IP,端口。ID和GID是可选字段,ID代表此上游服务器的标识符,GID代表该服务器所属的组。事件处理器可以获取这两个参数。

通过实现getBestblame方法,可以轻松编写自己的平衡器。有关更多信息,请参考balancer.balancer.lua。

会话信息和会话管理

SuProxy维护会话上下文包括:服务器IP,服务器端口,客户端IP,客户端端口,连接时间,用户名以及某些处理器特定的属性,例如客户端版本或连接字符串。以下是SSH2处理器的会话上下文:

  1. {
  2. "srvIP": "127.0.0.1",
  3. "client": "SSH-2.0-PuTTY_Release_0.74",
  4. "clientIP": "127.0.0.1",
  5. "clientPort": "56127",
  6. "username": "xxxx",
  7. "srvPort": 2222,
  8. "connTime":1600831353.066
  9. "server": "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1"
  10. }

这些信息将被传递到处理器的事件处理程序。

默认情况下,会话上下文存储在本地,因此不会在请求之间共享。提供了Redis会话管理器以支持Redis存储。Redis会话管理器还提供了简单的会话管理操作,例如获取活动会话列表和终止会话。更改默认会话管理器的方法如下:

  1. local sessionManager= require ("suproxy.session.sessionManager"):new{ip="127.0.0.1",port=6379,expire=-1,extend=false,timeout=2000}
  2. local channel=require("suproxy.channel"):new(package.loaded.my_OracleB,tns,{sessionMan=sessionManager})

其中IP端口是Redis服务器的地址。Expire设置会话默认值3600的默认过期时间跨度(以秒为单位),-1表示永不过期。extend指示从客户端发送新数据包后是否延长会话租约,timeout指示Redis超时(以毫秒为单位),默认为5000。

LUA代码example.session.lua显示了如何使用浏览器管理会话。将以下行添加到nginx config进行测试。

  1. server {
  2. listen 80;
  3. server_name localhost;
  4. ...
  5. location /suproxy/manage{
  6. content_by_lua_file lualib/suproxy/example/session.lua;
  7. }
  8. }

http://localhost/suproxy/manage/session/all 列出所有会话

http://localhost/suproxy/manage/session/clear 杀死所有会话

http://localhost/suproxy/manage/session/kill?sid=xxxx 通过sessionID终止会话

http://localhost/suproxy/manage/session/kill?uid=xxxx 通过uid终止会话

http://localhost/suproxy/manage/session/get?sid=xxxx 通过sessionID获取会话

http://localhost/suproxy/manage/session/get?uid=xxxx 通过uid杀死会话

Event Handling

通道和处理器都会触发事件,为事件添加处理程序的方法如下

  1. event:addHandler(context,handler)

context表示程序将在哪个对象上执行,处理程序可以访问上下文对象中定义的参数(self对象),handler是处理事件的函数。

典型的处理程序如下所示

  1. function handler(context,eventSource,[other event params])
  2. -- handler logic here
  3. end

处理程序至少会接收到两个参数:context和eventSource。context是由addHandler方法定义的执行上下文,eventSource是触发此事件的对象,大多数情况下是处理器本身。

NoReturnEvent 和 ReturnedEvent

事件有两种:NoReturnEventReturnedEventReturnedEvents的处理程序可以返回值,而NoReturn Event的处理程序则不能。NoReturnEvent可以有多个处理程序,但Returned Event可以只有一个。将处理程序添加到已经具有处理程序的Returned Event中将覆盖旧的处理程序。在相同情况下向NoReturnEvent添加更多处理程序将形成一个处理程序链,该链中的每个处理程序将一个接一个地执行。

addHandler 和 setHandler

对于NoReturnEvent,event:addHandler将新处理程序追加到处理程序链,setHandler方法清除该链并将处理程序追加到头部。调用setHandler方法可确保该事件有且仅有一个处理程序。

Channel Event

OnConnectEvent

此事件在刚建立连接时触发。handler参数为连接相关信息:

  1. {
  2. clientIP, --client ip address
  3. clientPort, --client port if tcp is used
  4. srvIP, --upstream server ip
  5. srvPort=serverPort --upstream server port if tcp is used
  6. }

这是处理通道的OnConnectEvent的示例,该事件将连接信息写入日志文件:

  1. local function logConnect(context,source,connInfo)
  2. local rs={
  3. os.date("%Y.%m.%d %H:%M:%S", ngx.time()).."\t" ,
  4. connInfo.clientIP..":"..connInfo.clientPort.."\t",
  5. "connect to ",
  6. connInfo.srvIP..":"..connInfo.srvPort.."\r\n"
  7. }
  8. print(table.concat(rs))
  9. end

结果如下

  1. 2020.09.24 18:28:03 127.0.0.1:60486 connect to 127.0.0.1:2222

Processor Events

不同的处理器可能具备不同的事件,但是所有处理器都实现以下的事件

  • BeforeAuthEvent
  • AuthSuccessEvent
  • AuthFailEvent
  • CommandEnteredEvent(对于ssh2Processor,这由commandCollector而非processor触发)
  • CommandFinishedEvent(对于ssh2Processor,这由commandCollector而非processor触发)
  • ContextUpdateEvent

每个处理器事件将不同的参数和一个会话上下文(session context)对象传递给其处理程序。会话上下文对象包含该会话中处理器收集的所有信息。以下是SSH2.0处理器中的典型会话上下文:

  1. {
  2. "srvIP": "127.0.0.1",
  3. "client": "SSH-2.0-PuTTY_Release_0.74",
  4. "clientIP": "127.0.0.1",
  5. "clientPort": "56127",
  6. "username": "root"
  7. "srvPort": 2222,
  8. "server": "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1"
  9. }

会话上下文信息随处理器类型和连接阶段的不同而有所不同。某些处理器没有“client”字段或“server”字段(如LDAP),有些处理器有附加信息,例如连接字符串(比如TNS)。另外,在连接的某个阶段,用户名尚未传输,因此在此时,username不会出现在上下文信息中。但是,上下文至少包含srvIP,srvPort,clientIP,clientPort,connTime,并且username将在用户身份验证后立即添加到上下文中。

BeforeAuthEvent

此事件在用户身份验证之前触发,这是交换用户凭据的绝佳时机。传递给其处理程序的参数是认证凭证会话上下文对象,凭证对象定义如下(对于TNS处理器,除非满足某些版本条件,否则不提供密码字段)。

  1. {
  2. username,--string, username entered by user
  3. password --string, password entered by user
  4. }

此事件的事件处理程序可以返回新的凭据,如果返回了新的凭据,则新的凭据将转发到上游,而不是旧的凭据。

下面是将原始凭证传递到远程服务器并通过OAUTH2.0从中获取新凭证的示例(此实例中用到的OAUTH验证模块ssoProcessors尚在实验阶段,用户完全可以用成熟的OAUTH2.0客户端替代)

  1. local function oauth(context,source,cred,session)
  2. --show how to get password with oauth protocal,using username as code, an app should be add and a password attributes should be add
  3. local param={
  4. ssoProtocal="OAUTH",
  5. validate_code_url="http://xxxxxxxxxxxx/oauth2/token",
  6. profile_url="http://xxxxxxxxxxxxx/oauth2/userinfo",
  7. client_secret="xxxxxxxxxxx",
  8. }
  9. local authenticator=authFactory.getAuthenticator(param)
  10. local result=authenticator:valiate({username=cred.username,password=cred.password})
  11. local newCred={}
  12. if result.status==ssoProcessors.CHECK_STATUS.SUCCESS then
  13. --confirm oauth password attributes correctly configged
  14. newCred.username=result.accountData.user
  15. newCred.password=result.accountData.attributes.password
  16. end
  17. if not newCred then return nil,"can not get cred from remote server" end
  18. return newCred
  19. end

OnAuthEvent

该事件在认证通过时触发。这是交换用户凭证或引入自定义身份验证的绝佳时机。传递给其处理程序的参数是凭据(用户名和密码)和会话上下文对象,凭据对象定义如下(对于TNS处理器,除特定版本外无密码字段)。

BeforeAuthEvent和OnAuthEvent之间的区别在于:BeforeAuthEvent在凭据的任何部分(如用户名)传输到服务器之前触发,而OnAuthEvent在身份验证真正发生并将密码传输到服务器时触发。对于某些协议(例如LDAP和TDS),这两个时机是相同的;对于处理器(例如SSH2或TNS),用户名是在密码之前传输的。如果需要为这些通道更改用户名,则必须在BeforeAuthEvent处理程序中准备好新的用户名。

此事件的事件处理程序可以返回验证结果,错误消息和新凭据。使用新凭据字段在LDAP和TDS处理器中可以替代BeforeAuthEvent

本示例说明如何引入第三方认证。

  1. local function authenticator(context,source,credential,session)
  2. --OAUTH or other auth protocol should be used to swap real credential in real world
  3. local result=credential.username=="test" and credential.password=="test"
  4. if result then
  5. return result
  6. else
  7. local message="login with "..credential.username.." failed"
  8. return result,message
  9. end
  10. end
  11. ssh.OnAuthEvent:addHandler(tns,authenticator)

AuthSuccessEvent

用户身份验证成功完成后触发此事件,这是写入日志文件的正确时机。传递给其处理程序的参数是用户名会话上下文对象

下面是将登录操作写入日志文件的示例。

  1. local function logAuth(context,source,username,session)
  2. local rs={
  3. os.date("%Y.%m.%d %H:%M:%S", ngx.time()).."\t" ,
  4. session.clientIP..":"..session.clientPort.."\t",
  5. username.."\t",
  6. "login with ",
  7. (session and session.client) and session.client or "unknown client",
  8. (session and session.clientVersion) and session.clientVersion or ""
  9. }
  10. print(table.concat(rs))
  11. end

结果如下

  1. 2020.09.24 19:03:40 127.0.0.1:60844 root login with SSH-2.0-PuTTY_Release_0.74

AuthFailEvent

用户身份验证失败时触发此事件,这是写入日志文件的正确时机。传递给其处理程序的参数是failInfo会话上下文对象

failInfo定义如下

  1. {
  2. username, --string, failed username
  3. message --string, fail message passed
  4. }

下面是将登录失败操作写入日志文件的示例。

  1. local function logAuthFail(context,source,failInfo,session)
  2. local rs={
  3. os.date("%Y.%m.%d %H:%M:%S", ngx.time()).."\t" ,
  4. session.clientIP..":"..session.clientPort.."\t",
  5. failInfo.username.."\t",
  6. "login fail, fail message: ",
  7. failInfo.message
  8. }
  9. print(table.concat(rs))
  10. end

结果如下

  1. 2020.09.24 19:27:48 127.0.0.1:61020 zhuyi login fail, fail message: wrong password

CommandEnteredEvent

该事件将在命令发送到服务器之前触发。如果是数据库或LDAP协议,则命令是SQL命令或LDAP请求;如果是SSH连接,则命令是shell命令。这是执行命令检查或更改某些命令的最佳时机。

传递给其处理程序的参数是命令字符串会话上下文对象。处理程序可能会返回新命令错误消息。如果返回新命令,则新命令将取代原始命令被发送到服务器。如果返回错误消息,则该命令将不会执行,处理器将通知客户端错误消息。(通知客户端的方式随处理器而异,有些可能会提示消息,有些可能没有)以下是检查命令并禁止命令中使用某些关键字的示例。

  1. local function forbidden(context,source,command,session)
  2. if command:match("forbidden")then
  3. print("forbidden command triggered")
  4. return nil,{message=command.." is a forbidden command"}
  5. end
  6. return command
  7. end

以下是sql执行对sqlserver的影响

以下是putty的响应

CommandFinishedEvent

从服务器回复命令时触发此事件,如果是数据库/ LDAP协议,则命令可能是SQL/ LDAP请求,如果是SSH连接,则命令可能是shell命令。这是编写命令及其在文件中回复的最佳时机。

传递给其处理程序的参数是命令字符串回复字符串上下文对象。某些处理器中可能没有答复字符串(在当前版本中未收集TNS(ORACLE)答复),以下是记录命令和答复的示例

  1. local function logCmd(context,source,command,reply,session)
  2. local rs={
  3. os.date("%Y.%m.%d %H:%M:%S", ngx.time()).."\t" ,
  4. session.clientIP..":"..session.clientPort.."\t",
  5. username.."\t",
  6. command.."\r\n"
  7. }
  8. if reply then
  9. rs[#rs+1]="------------------reply--------------------\r\n"
  10. rs[#rs+1]=reply
  11. rs[#rs+1]="\r\n----------------reply end------------------\r\n")
  12. end
  13. print(table.concat(rs))
  14. end

日志文件如下

  1. 2020.09.24 19:28:43 127.0.0.1:61020 root ls
  2. ------------------reply--------------------
  3. libc6_2.31-0ubuntu8+lp1871129~1_amd64.deb
  4. ----------------reply end------------------

ContextUpdateEvent

处理器在会话中收集新信息时触发此事件。听此事件,程序可以获取上下文信息并构造自己的会话状态。以下示例显示如何使用resty.redis库将上下文记录到Redis中

  1. function contextHandler(self,source,ctx)
  2. local red = require ("resty.redis"):new()
  3. red:set_timeouts(10000, 10000, 10000)
  4. local ok, err = red:connect("127.0.0.1", 6379)
  5. if not ok then
  6. ngx.log(ngx.ERR,"failed to connect: ", err)
  7. return
  8. end
  9. red:set(ctx.clientIP..ctx.clientPort,cjson.encode(ctx))
  10. end

Parser 及 Parser Events

每个处理器都有2个解析器c2pParser和s2pParser。c2pParser负责解析客户端与代理间的通讯,s2pParser负责解析服务端和代理间的通讯。当这些解析器成功解析一个数据包时,将触发一个解析器事件。用户程序可以侦听这些事件以进行拦截,更改发送到服务器的数据包或停止转发。

传递给数据包事件处理程序的参数是解析器构造的数据包,其中包含协议特定的信息。处理程序可以获取或设置其字段以获取或更改发送到服务器的内容。例如,可以像这样更改TNS连接中的版本。

  1. function _M:ConnectHandler(src,packet)
  2. packet:setTnsVersion(314)
  3. packet:pack()
  4. end

拦截,更改和停止转发

设置packet的allBytes属性可以修改包的内容,这意味着计算字节长度,重新生成数据包头和将发送到服务器的字节流。每次更改字段时,都必须调用packet.pack()。字节流可以通过packet.allBytes访问。

如果我们不希望我们的数据包再转发。(例如,处理程序已作出手动响应),只需将packet.allBytes字段设置为“”即可。

  1. p.allBytes=""
  2. p:pack()

读取数据与响应

处理程序可以使用Channel提供的4种方法来读取并响应客户端或服务器:

Channel.c2pSend将数据发送到c2p套接字

Channel.p2sSend把数据P2S插座

Channel.c2pRead从c2p套接字读取数据

Channel.p2cRead读取P2S套接字数据

这四个方法包装了lua stream socket。参数和返回值与socket.receive和send相同,xxxSendMehod采用一个参数:要发送的字节。接收方法的输入参数是读取的长度或模式。

以下是拦截LDAP SearchRequest和构造响应的示例。

  1. local function ldap_SearchRequestHandler(context,src,p)
  2. if context.command:match("pleasechangeme") then
  3. local packets=require("suproxy.ldap.ldapPackets")
  4. local response=packets.SearchResultEntry:new()
  5. local done=packets.SearchResultDone:new()
  6. response.objectName="cn=admin,dc=www,dc=test,dc=com"
  7. response.messageId=p.messageId
  8. response.attributes={
  9. {attrType="objectClass",values={"posixGroup","top"}},
  10. {attrType="cn",values={"group"}},
  11. {attrType="memberUid",values={"haha","zhuyi","joeyzhu"}},
  12. {attrType="gidNumber",values={"44789"}},
  13. {attrType="description",values={"group"}}
  14. }
  15. done.resultCode=packets.ResultCode.success
  16. done.messageId=p.messageId
  17. response:pack() done:pack()
  18. context.channel:c2pSend(response.allBytes..done.allBytes)
  19. --stop forwarding
  20. p.allBytes=""
  21. end
  22. end
  23. local ldap=require("suproxy.ldap"):new()
  24. ldap.c2pParser.events.SearchRequest:addHandler(ldap,ldap_SearchRequestHandler)

Examples

example.gateway演示为TNS,TDS,SSH2,LDAP协议实现了一个简单的网关。要测试此演示,请修改nginx配置,将以下部分添加到您的配置文件中。确保commands.log文件路径有效。

  1. stream {
  2. init_by_lua_file lualib/suproxy/init.lua;
  3. lua_code_cache off;
  4. #mock logserver if you do not have one
  5. server {
  6. listen 12080;
  7. content_by_lua_block {
  8. ngx.log(ngx.DEBUG,"logserver Triggerred")
  9. local reqsock, err = ngx.req.socket(true)
  10. reqsock:settimeout(100)
  11. while(not err) do
  12. local command,err=reqsock:receive()
  13. if(err) then ngx.exit(0) end
  14. local f = assert(io.open("/data/logs/commands.log", "a"))
  15. if(command) then
  16. f:write(command .. "\n")
  17. f:close()
  18. end
  19. end
  20. }
  21. }
  22. #listen on ports
  23. server {
  24. listen 389;
  25. listen 1521;
  26. listen 22;
  27. listen 1433;
  28. content_by_lua_file lualib/suproxy/example/gateway.lua;
  29. }
  30. }
  31. #Session manager interfaces. if you want to view and manage your session
  32. #over http, this should be set.
  33. http {
  34. include mime.types;
  35. lua_code_cache off;
  36. server {
  37. listen 80;
  38. server_name localhost;
  39. default_type text/html;
  40. location /suproxy/manage{
  41. content_by_lua_file lualib/suproxy/example/session.lua;
  42. }
  43. }

关于会话管理的接口,请参阅会话管理。

[SuProxy]Ngnix+Lua 实现SSH2,LDAP,ORACLE,SQLSERVER等TCP/IP协议分析,劫持,代理,会话及负载的更多相关文章

  1. JDBC:SqlServer连接TCP/IP连接失败,到主机 的 TCP/IP 连接失败。报错信息:com.microsoft.sqlserver.jdbc.SQLServerException: 到主机 的 TCP/IP 连接失败。

    作者QQ:1161493927,欢迎互相交流学习. 报错信息:com.microsoft.sqlserver.jdbc.SQLServerException: 到主机 的 TCP/IP 连接失败. j ...

  2. SQLServer配置开启TCP/IP连接

    一 先启用SQLServer的TCP/IP协议 1.1 打开SQLServer配置管理器 1.2 启用TCP/IP 二 设置SQLServer端口 2.1 双击TCP/IP,弹出属性设置框 2.2 将 ...

  3. SqlServer 之 用 IP 地址连接数据库报错" 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误 "

    问题描述:       在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (p ...

  4. BI测试工具之跨数据库数据对比,支持oracle,sqlserver

    应用场景: 本周在进行SIT,我帮助仅有的一个测试妹妹对部分表进行数据质量验证,第一步需要做的就是比对source与stage表的table definition 与 数据内容的一致性. 本项目使用的 ...

  5. 【Oracle&SQLServer】并集、交际、补集

    1.并集(UNION/UNION ALL) Oracle&SQLServer中用法一致 UNION 去重 UNION ALL 不去重 -- 去重 select * from tablea un ...

  6. Oracle,sqlserver,mySQl的区别和联系:

    1.日期处理方式 2.对保留字和关键字的处理方式: Oracle,sqlserver,mySQl的保留字不可以用作列字段,关键字可以,但他们对关键字的处理方式又不同: Oracle:关键字作为列时:用 ...

  7. 菜鸡之NetCore 使用EF操作数据库 Oracle & Sqlserver (一)

    摘要: 该篇文章主要记录netCore EFCore 如何操作Oracle和SqlServer 数据库,采用Codefirst方式创建数据库以及表. 一, 项目建立 项目采用DDD领域驱动设计模式[学 ...

  8. Oracle Sales Cloud:报告和分析(BIEE)小细节2——利用变量和过滤器传参(例如,根据提示展示不同部门的数据)

    在上一篇随笔中,我们建立了部门和子部门的双提示,并将部门和子部门做了关联.那么,本篇随笔我们重点介绍利用建好的双提示进行传参. 在操作之前,我们来看一个报告和分析的具体需求: [1] 两个有关联的提示 ...

  9. Oracle Sales Cloud:报告和分析(BIEE)小细节1——创建双提示并建立关联(例如,部门和子部门提示)

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的客户商机管理系统,通过提供丰富的功能来帮助提高销售效率,更好地去了解客户,发现和追踪商机,为最终的销售成交 (d ...

随机推荐

  1. 刷题[CISCN2019 华北赛区 Day2 Web1]Hack World

    解题思路 打开发现是很简单的页面,告诉了表名和列名,只需知道字段即可 尝试一下,输入1,2都有内容,后面无内容.输入1'让他报错,发现返回bool(false) 大概思路就是布尔型注入了,通过不断返回 ...

  2. Kafka日志段读写分析

    引子 之所以写这篇文章是因为之前面试时候被面试官问到(倒)了,面试官说:"你说你对Kafka比较熟?看过源码? 那说说kafka日志段如何读写的吧?" 我心里默默的说了句 &quo ...

  3. Python_快速安装第三方库-pip

    如何快速安装第三方库? 通过python 豆瓣园源https://pypi.douban.com/simple/进行安装,利用国内网速 如何安装? pip -i install https://pyp ...

  4. 使用Maven那么久了,你对企业级Maven的核心配置了解多少?

    写在前面 相信从事Java工作的小伙伴们多多少少都会接触到Maven.使用Maven来搭建项目,能够极大的方便我们构建项目的依赖关系,对于项目中需要依赖的Jar包,也只是简单的在pom.xml中进行配 ...

  5. SQLMAP注入Access数据库

    今天偶遇一Access数据库 1.首先尝试是否存在注入点,and1=1,and 1=2,发现返回信息不一样 2.使用sqlmap脱裤,发现时Access数据库,不能提权, 3.那就直接暴库吧,sqlm ...

  6. weblogic 安装+部署(一)

    昨天刚接触weblogic,在windows下搭建了一个weblogic,没什么技术,留个笔记. 1.首先要有jdk,添加环境变量这个没什么好说的. 2.下载weblogic:可以去官网下:http: ...

  7. ASP。NET MVC警告横幅使用Bootstrap和AngularUI Bootstrap

    Watch this script in action - demo 下载Source Code from GitHub 下载Source Code from CodeProject (1.1 MB) ...

  8. [学习笔记] 数位DP的dfs写法

    跟着洛谷日报走,算法习题全都有! 嗯,没错,这次我也是看了洛谷日报的第84期才学会这种算法的,也感谢Mathison大佬,素不相识,却写了一长篇文章来帮助我学习这个算法. 算法思路: 感觉dfs版的数 ...

  9. 踩坑系列:MySql only_full_group_by配置,竟导致所有应用报错?

    1. 踩坑经历 一个很平常的下午,大家都在埋头认真写bug呢,突然企业微信群里炸锅了,好多应用都出现大量的Error日志,而且都报同一个错误,就是下面这个: Caused by: com.mysql. ...

  10. 本地vue项目跨域服务器接口

    1,打开index.js文件,找到  proxyTable 参照下面链接的方法,你们可以去点赞 https://www.douban.com/note/704314260/?type=like#sep