libknock/libknock

GitHub: libknock/libknock

一款专为 Go 应用设计的可嵌入式 TCP 应用前认证 SDK,在连接建立与应用协议启动之间执行身份验证和准入控制。

Stars: 2 | Forks: 0

# libknock [![License: BSL-1.1](https://img.shields.io/badge/license-BSL--1.1-blue.svg)](LICENSE) [![Future License: Apache-2.0](https://img.shields.io/badge/future%20license-Apache--2.0-lightgrey.svg)](LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/libknock/libknock.svg)](https://pkg.go.dev/github.com/libknock/libknock) 源码基于 BSL-1.1 可用。允许生产环境使用,但在其于 2030-05-15 转换为 Apache-2.0 之前,限制将 libknock 或其衍生作品作为托管或管理服务提供。请参阅 [LICENSE](LICENSE) 和 [许可说明](docs/license.md)。 专为 Go 应用设计的可嵌入式 TCP 应用前认证 SDK。 它负责在 TCP 连接建立之后、应用协议启动之前,对一个紧凑的二进制帧进行身份验证。认证成功后,调用方将获得一个标准的 `net.Conn`,并继续执行其自定义协议栈:纯 TCP、TLS、HTTP、gRPC、自定义 RPC、agent 连接、游戏协议、类数据库协议,或任何基于 `net.Conn` 构建的协议。 `libknock` 不会解析、修改或接管应用载荷。它仅在 TCP 连接边界执行准入步骤,随后将纯净的连接交还给嵌入式应用。 ## 依赖模型 libknock 使用 Go modules 作为主要依赖路径。标准源码归档适用于常规的 Go module 用户。配套的 `with-vendor` 归档包含 `vendor/`、`vendor/modules.txt`、`go.work` 和 `go.work.sum`,适用于离线审查、可重现的本地审计、LLM 辅助集成以及受限的 CI 环境。在验证 vendor 归档时,请从工作区根目录使用 `go test -mod=vendor ./...` 和 `go vet -mod=vendor ./...`。请保持启用 `go.work`;在工作区 vendor 模式下不要使用 `GOWORK=off`。 对于编码 agent 和 LLM 辅助工作,请从 [`llms.txt`](llms.txt)、[`docs/llms.md`](docs/llms.md) 和 [`docs/agents/AGENTS.md`](docs/agents/AGENTS.md) 开始。这些文件描述了受支持的集成入口点、禁止使用的捷径、vendored 工作区要求以及特定任务的方案指南。 ## 提供的功能 - 用于服务端 TCP 服务的已认证 `net.Listener` 包装器。 - 用于客户端的已认证 `net.Dialer` 包装器。 - 用于自定义连接流水线的独立 `ServerAuth` 和 `ClientAuth` 函数。 - 用于 TCP 认证的核心根包 SDK 入口点。 - 用于高级准入路径的可选 knock、firewall、gate 和 relay 包。 - 防重放保护、时间戳验证、节点元数据、事件钩子和策略钩子。 - 位于独立模块中的可选 Prometheus 适配器。 ## 安装 ``` go get github.com/libknock/libknock ``` ## 最简服务端 ``` package main import ( "log" "net" "time" libknock "github.com/libknock/libknock" ) func main() { secret := []byte("0123456789abcdef0123456789abcdef") ln, err := net.Listen("tcp", ":9000") if err != nil { log.Fatal(err) } ln, err = libknock.NewListener(ln, libknock.ServerConfig{ ServerPort: 9000, Secrets: libknock.NewStaticSecretResolver(map[string][]byte{ "client-001": secret, }), ReplayCache: libknock.NewMemoryReplayCache(5 * time.Minute), }) if err != nil { log.Fatal(err) } for { conn, err := ln.Accept() if err != nil { log.Fatal(err) } go handleConn(conn) } } ``` `NewListener` 会直接返回启动验证错误,并在未提供时创建一个由监听器拥有的重放缓存。`WrapListener` 依然可用,作为便捷的 `net.Listener` 包装器;配置错误会在 `Accept` 时暴露。如果您直接调用底层 `ServerAuth` 函数,请自行提供共享的 `ReplayCache`。 ## 最简客户端 ``` package main import ( "context" "net" "time" libknock "github.com/libknock/libknock" ) func dial(ctx context.Context) (net.Conn, error) { secret := []byte("0123456789abcdef0123456789abcdef") d := libknock.Dialer{ Base: &net.Dialer{Timeout: 5 * time.Second}, Config: libknock.ClientConfig{ ClientID: "client-001", Secret: secret, ServerPort: 9000, AuthTimeout: 3 * time.Second, }, } return d.DialContext(ctx, "tcp", "127.0.0.1:9000") } ``` ## 根包与高级包 根包刻意保持精简。它提供了常规的 SDK 路径:`NewListener`、`WrapListener`、`WrapListenerE`、`ServerAuth`、`ClientAuth`、`Dialer`、`ServerConfig`、`ClientConfig`、`PeerInfo`、`NewServer`、`NewMemoryReplayCache` 和 `NewStaticSecretResolver`。 高级准入功能位于子包中。使用 `auth` 处理协议选择器和高级认证钩子,使用 `gate` 处理监听器组合模式,使用 `relay` 处理代理式网关,使用 `firewall` 处理平台后端,使用 `knock` 处理 knock 发送器/监听器,使用 `observability` 处理网关事件。 ``` import ( libknock "github.com/libknock/libknock" "github.com/libknock/libknock/gate" ) ln, err := gate.Listen(ctx, gate.Config{ Mode: gate.AuthOnly, Auth: libknock.ServerConfig{ ServerPort: 9000, Secrets: libknock.NewStaticSecretResolver(map[string][]byte{"client-001": secret}), }, }) ``` ## TCP 认证协议 `libknock` 支持两种 TCP 应用前认证协议: | 协议 | 名称 | 简介 | | --- | --- | --- | | v1 | `tcp-auth-frame-v1` | 带有 AEAD 密封认证元数据的固定二进制帧。 | | v2 | `tcp-auth-envelope-v2` | 支持路由提示和固定大小桶填充的密封信封。 | 两种协议都提供客户端密钥验证、时间戳窗口验证、防重放保护、可选的 knock 会话绑定、节点元数据、事件钩子以及可选的服务器证明。 客户端通过 `ClientConfig.Protocol` 选择协议。服务端通过 `ServerConfig.Protocol` 选择首选协议,并通过 `ServerConfig.AcceptProtocols` 选择接受的协议。默认的 TCP 认证协议是 `tcp-auth-envelope-v2`。 ## UDP knock 帧 UDP knock 使用带有 AEAD 密封载荷的二进制数据报帧。相同的 UDP 帧系列由以下组件使用: - `udp` - `udp-seq` - `udp-passive` - `udp-passive-seq` 该帧携带经过身份验证的元数据,例如客户端身份哈希、方法、时间戳、受保护端口、可选的会话 ID、序列字段和扩展。 ## Knock 方法支持 | 方法 | 简介 | 绑定 UDP socket | 额外权限 | 扫描行为 | | --- | --- | --- | --- | --- | | `tcp-syn` | 单次 TCP SYN 形态的 knock。 | 否。 | 在参与的平台上需要原始数据包或数据包捕获能力。 | 不开放 UDP 端口;TCP SYN 行为取决于具体平台和防火墙。 | | `tcp-syn-seq` | 多部分 TCP SYN 形态的序列 knock。 | 否。 | 需要原始数据包或数据包捕获能力;在需要多次短窗口尝试时非常有用。 | 同 `tcp-syn`,并带有序列聚合。 | | `udp` | 通过常规 UDP socket 的单次 UDP knock。 | 是。 | 否。 | 扫描时可能会发现 UDP knock 端口处于可达状态。 | | `udp-seq` | 多部分 UDP 序列 knock。 | 是。 | 否。 | socket 可见性同 `udp`;具有更强的短窗口准入信号。 | | `udp-passive` | 在服务端通过数据包捕获读取 UDP knock。 | 否。 | 需要 root、`CAP_NET_RAW` 或 pcap/BPF 权限。 | 启用 `DropUDPKnockPort` 时,扫描应表现为 DROP,同时捕获器仍能观察到流量。 | | `udp-passive-seq` | 在服务端通过数据包捕获读取多部分 UDP 序列。 | 否。 | 需要 root、`CAP_NET_RAW` 或 pcap/BPF 权限。 | 被动扫描行为,并带有序列聚合。 | 对于大多数部署场景,在考虑被动或原始数据包方法之前,请优先从 UDP knock 开始。 ## 功能状态 | 功能 | 状态 | | --- | --- | | 已认证的监听器 | stable | | Dialer | stable | | TCP auth frame v1 | stable | | TCP auth envelope v2 | release candidate | | UDP knock / UDP 序列 | release candidate | | 仅 Knock 认证门控 | release candidate | | 防火墙支持的门控 | 特定平台 / 未经完全验证 | | UDP 被动 knock | experimental / 未经完全验证 | | TCP SYN knock | experimental / 未经完全验证 | | Windows 数据包捕获集成 | experimental / 未经完全验证 | | macOS 被动捕获集成 | experimental / 未经完全验证 | ## 门控模式 | 模式 | 描述 | | --- | --- | | `auth-only` | TCP 连接必须在应用程序接收它们之前通过 libknock 的 TCP 身份验证。 | | `knock-auth-only` | 传输层的 TCP 保持打开,但客户端必须先 knock,然后再通过 TCP 身份验证,应用程序才会接收它们。不更改任何防火墙规则。 | | `knock-firewall-auth` | 成功的 knock 会打开短暂的防火墙窗口,随后必须通过 TCP 身份验证。 | | `knock-firewall-only` | 成功的 knock 会打开短暂的防火墙窗口,应用程序随后接收匹配的 TCP 连接。 | `knock-auth-only` 不是一种端口隐藏模式:SYN 扫描仍会将 TCP 端口报告为打开状态,但未经验证的客户端无法访问应用程序协议。它不需要 root 或 `CAP_NET_ADMIN` 权限,适用于容器、受限的 VPS 实例、Windows/macOS 部署,并在 `auth-only` 的基础上增加了短暂的 knock 会话要求。当需要基于防火墙的端口门控时,它不能替代 `knock-firewall-auth`。 `knock-firewall-auth` 和 `knock-firewall-only` 需要真实的防火墙后端。`auth-only` 和 `knock-auth-only` 可以使用 `firewall.Noop{}`。 重复的有效 knock 会从最近接受的 knock 开始续期防火墙允许窗口;来自早期 knock 的过期定时器不会撤销已续期的租约。 ## 中继网关 `relay.Gateway` 是一个可选的 TCP 转发组件。它监听一个地址,执行 libknock 身份验证和可选的 knock/防火墙处理,随后连接到上游 TCP 服务。 ``` gw := relay.Gateway{ Listen: ":9000", Upstream: "127.0.0.1:19000", Auth: serverAuthConfig, Firewall: firewall.Noop{}, } err := gw.Run(ctx) ``` 当受保护的上游是独立的 TCP 服务,而不是直接内嵌 `libknock` 的应用时,请使用 relay。 ## 文档 - 编码 agent:[docs/agents/AGENTS.md](docs/agents/AGENTS.md),[集成指南](docs/agents/integration-guide.md),[反模式](docs/agents/anti-patterns.md),[方案指南](docs/agents/recipes/) - 示例:[examples/README.md](examples/README.md) - [文档索引](docs/README.md) - [快速开始](docs/getting-started.md) - [用例](docs/use-cases.md) - [API 参考](docs/api.md) - [API 范围与兼容性](docs/api-surface.md) - [兼容性策略](COMPATIBILITY.md) - [协议](docs/protocols.md) - [门控与中继](docs/gate-and-relay.md) - [Knock 方法](docs/knock-methods.md) - [防火墙后端](docs/firewall.md) - [可观测性](docs/observability.md) - [生产环境部署](docs/production.md) - [故障排除](docs/troubleshooting.md) - [已知限制](docs/known-limitations.md) - [验证矩阵](docs/validation-matrix.md) - [性能说明](docs/performance.md) - [路线图](docs/roadmap.md) - [发行说明](docs/release-notes.md) - [发布检查清单](docs/release-checklist.md) - [开发指南](docs/development.md) ## 防火墙说明 `nftables` 和 `ipset-iptables` 支持基于超时的规则。普通的 `iptables` 后端依赖 libknock 的 gate/relay 定时器来撤销 ACCEPT 规则,并在启动/关闭时执行托管链清理,因此如果进程非正常退出,在再次执行清理之前可能会残留临时规则。对于需要内核强制执行过期的生产环境部署,请优先使用 `nftables` 或 `ipset-iptables`。 ## 仓库结构 ``` protocol/ binary protocol codecs and cryptographic helpers auth/ server/client authentication, replay cache, secret resolvers netx/ listener, dialer, buffered connection behavior knock/ knock senders and listeners firewall/ firewall backend interfaces and implementations gate/ SDK listener composition modes relay/ optional TCP relay gateway policy/ limiter and ban policy adapters observability/ event interfaces and metrics adapters cmd/knock-proxy command entrypoint examples/ integration examples ``` ## 测试 ``` scripts/check.sh ``` 若需更短的编辑循环: ``` go test ./... go vet ./... go test -race ./auth ./firewall ./knock ./netx ./policy ./protocol ./relay ``` Prometheus 和 gRPC 集成检查位于独立的模块中: ``` go -C observability/prometheus test ./... go -C test/integration/grpc test ./... ``` ## 兼容性命令 `cmd/knock-proxy` 是用于简单客户端/服务端代理部署的兼容性调用器。它并非完整的旧版 knock-proxy 产品,也不会托管每一个可选的集成。当嵌入式应用需要自定义生命周期、指标、策略或应用协议处理时,应优先直接使用 SDK 包。
标签:EVTX分析, Go, Ruby工具, TCP连接, 内核驱动, 开发组件, 日志审计, 网络协议, 自定义请求头