这段时间我很少在论坛里冒泡,因为上个星期我在家学习,突然有人敲我家门。 听到一个小姑娘的声音说:“我是住在你楼上的邻居,这个是我的微信业主群,我是我奶奶带大的,我奶奶现在不行了,她想看到我结婚,你能跟我假结婚吗?” 我看了看,嘶……我也不行啊——我未成年。就对她说"我未成年姐姐",结果她就走了。 我觉得可能是仙人跳,但小姑娘声音怪好听的,我就用猫眼瞅了一眼。猫眼有点失真,小姑娘戴着口罩看不大清,拿着手机对着猫眼,似乎的确是业主群。她就跑去敲我隔壁家的门,没回声——工作日嘛,可能都去上班了。 大概半小时后她又来敲我门,说她敲遍了整栋楼,就我在家。告诉我可以跪下来求我,或者给我打钱都行,能不能跟她一起去见奶奶,就在医院。 我心一软——因为小姐姐说话有点上气不接下气,确实像爬上爬下很累的样子。于是我打扮了一下,穿了增高鞋,身高垫到了179左右。她也差不多160。 我就跟她说只去医院,别的地方哪都不去,叫她等我。为了保险我还带了刀具,就陪她去见她奶奶了。 路上要对口供,她把她信息给我了,加了微信。她是她奶奶带大的,性格很孤僻,如果不是为了她奶奶她也不会走出家门。她大学辍学,因为太孤僻被排挤,她奶奶干脆就把她接回家了。 看了她朋友圈,全是游戏截图,也没她自拍啥的,加上戴着口罩看不清真容,但是眼睛蛮大蛮好看的,天冷穿的比较多也看不清身材。 然后陪着她去看她奶奶了。告诉奶奶"我刚从外地回来,明天就登记结婚,让奶奶放心"。她奶奶看了我一眼,没说什么,在那带着哭腔的笑。她奶奶好像是高烧,我感觉挺心酸的,我又不熟,又得装着关心,而且我其实挺怕死的,担心在医院被传染,所以待了一会儿我就回家了。 然后小姑娘守着她奶奶,第二天跟我说去领证。 我就很疑惑:领证?不是办假证吗? 她问我认识办假证的吗? 我不认识啊,她说她也不认识,说只是给奶奶看的。 我警觉心马上就起来了——高级仙人跳? 但我快进下,就是去批发市场买了个证书壳子,做了个假的糊弄她奶奶,结果她奶奶居然好了! 然后这个谎要圆啊,老人家受不了刺激,我就住她家了。提前跟爸妈说就去同学家玩几天,他们居然相信了。 然后小姑娘其实挺好看的,而且身材也好,也不能说好,就是瘦哪哪肉都不多,但是腿是真的细。 奶奶还很兴奋地要布置婚房,我就看到好多洛丽塔的衣服被翻出来。其实我也有点后悔,小姑娘家挺有钱的,她爸爸原本是开厂的,后来厂房出了事故,人没了,但是她继承了好多钱,真就是不显山不显水的小富婆。我也就一俗人,所以对她就格外关心起来,因为奶奶在家,大病一场,她想抱孙子,怕看不到那一天。 然后小姑娘就脸红的打断…… 奶奶就说你们都是夫妻了,不生孩子像话吗?她就找理由说要找工作、要养家。奶奶就说咱们家那么些钱,养个姑爷怎么了,她就非说我有上进心,不想被说闲话。结果奶奶就说找她老同学给我安排工作,让我安心跟小姑娘一起造小孩…… 但是我每次都得寸进尺,已经可以同睡一张床了。我其实也不怎么会说话不怎么会夸人,小姑娘也腼腆,也不怎么会拒绝人,在一起其实很变扭,然后我只能瞎找话题。 发现她喜欢洛丽塔,喜欢玩具。当然每次进房间都是装的,但是要照顾奶奶,所以她都没出门。不过听奶奶说她也从来不出门的。 然后我发现她发了条朋友圈,肯德基跟三丽鸥有个活动,吃套餐送三丽鸥的玩具,里面有个兔兔,她很喜欢。 所以你们谁能V我50,我给她买一套。 52 个帖子 - 50 位参与者 阅读完整话题
每个人心里都住着一个小孩 只是我们已经很久没有敲门去见见他了 26 个帖子 - 26 位参与者 阅读完整话题
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
最近整理了一个 Go 库,叫 libknock 。 它解决的问题比较窄: 有些 TCP 服务不太想让随机连接直接打到应用协议解析层,比如内部管理端口、agent/collector 、私有 RPC 、gRPC 服务、自定义 TCP 协议之类。 传统做法一般是: 直接暴露 TCP 端口,让 TLS / HTTP / gRPC / 业务协议自己处理 前面再套一层 proxy / gateway 靠防火墙、VPN 、内网环境隔离 或者用 port knocking 临时放行来源 IP libknock 的思路是把这类能力做成 Go 里的底层网络库,放在 net.Listener / net.Dialer 这一层。 服务端大概是: ln, _ := net.Listen("tcp", ":9000") ln = libknock.WrapListener(ln, knockCfg) for { conn, err := ln.Accept() if err != nil { return err } go handleConn(conn) } 客户端大概是: d := libknock.Dialer{ Base: &net.Dialer{}, Config: knockCfg, } conn, err := d.DialContext(ctx, "tcp", "example.com:9000") 连接建立后,客户端先发一个 binary auth frame 。服务端验证 client secret 、timestamp 、nonce 、replay cache ,通过以后才把一个干净的 net.Conn 交给上层程序。 也就是说,上层协议不用知道 libknock 存在。 如果后面是 TLS ,TLS 看到的还是正常 ClientHello ;如果后面是自定义 TCP 协议,业务 handler 看到的就是自己的业务首包。 除了 TCP 前置认证,也可以组合 port knocking / firewall gate: client -> knock -> temporary allow / session -> TCP connect -> TCP pre-auth -> application protocol 不过它不是传统意义上的“端口敲门工具”。port knocking 只是可选的一层 gate ;核心还是 Go 程序可嵌入的 TCP pre-application authentication SDK 。 主要特性: Go 原生 net.Listener / net.Dialer 包装 TCP 建连后、应用协议开始前认证 binary auth frame client secret 校验 timestamp window nonce replay 防护 认证失败默认直接关闭连接 不解析、不修改上层应用协议 可用于普通 TCP 、自定义二进制协议、TLS 、HTTP 、gRPC 可选 port knocking / firewall gate / relay gateway 适合的场景大概是: 内部管理端口 agent / collector 长连接 私有 RPC / gRPC 服务 自定义 TCP 协议 数据采集、边缘节点、运维控制面 不想让应用协议解析器直接面对随机 TCP 输入的服务 不适合的场景: 想要开箱即用保护任意未改造二进制程序,这种更适合 relay/gateway 想替代 TLS / mTLS / 业务鉴权 想做 VPN 或完整隧道 只想要一个传统 knockd 替代品 现在还在早期阶段,主要想看看有没有人对这种“TCP pre-application authentication + optional port knocking”的组合有类似需求。 仓库地址: https://github.com/libknock/libknock 欢迎拍砖,尤其是 Go API 设计、 net.Conn 包装方式、port knocking 和 TCP auth 的边界、安全默认值这几块。
knock-proxy 是一个端口敲门 TCP 转发工具。服务端用防火墙默认 DROP 公网 TCP 端口;客户端先发送 knock ,服务端验证后临时放行来源 IP ,然后客户端连接同一 TCP 端口,完成 HMAC-SHA256 二次认证并转发到本机 upstream 。 适合隐藏 SSH 、RDP 、数据库管理端口、Web 管理后台等 TCP 服务。 项目地址 https://github.com/ming79486/knock-proxy