gosuda/keyless_tls

GitHub: gosuda/keyless_tls

Go 语言实现的无密钥 TLS 方案,将私钥隔离在远程签名服务中,隧道应用仅持有公钥证书并委托签名操作,从而降低私钥泄露风险。

Stars: 2 | Forks: 0

# 无密钥 TLS ![keyless_tls logo](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/c92ed96595024612.svg) `keyless_tls` 的设计宗旨是让隧道应用处理 TLS 握手和流量加密/解密,而仅将 `CertificateVerify` 签名委托给远程签名者。 - TLS 引擎、会话密钥、流量加密:`tunneling app` - TLS 签名(`CertificateVerify`):远程 `relay signer` - 签名者传输:带有强制 `mTLS` 的 `HTTPS + JSON` 本仓库支持两种使用模式: 1. 作为 SDK 库使用(`keyless` 包) 2. 运行 `cmd/*` 下提供的二进制文件 ## 首先选择您的集成路径 - **我想直接附加到我的应用(`http.Server`)**:SDK 模式 - **我想立即运行并验证行为**:二进制模式 ## 1) 使用 SDK 库 ### 核心概念 隧道应用仅保留公共证书链(`cert PEM`),并且**不**持有私钥。 `keyless` SDK 将远程签名者作为 `crypto.Signer` 附加,因此握手签名是远程执行的。 ### 公共 API - `keyless.AttachToHTTPServer`:最简单的入口点(直接附加到 `http.Server`) - `keyless.NewRemoteSigner`:显式创建远程签名者客户端 - `keyless.NewServerTLSConfig`:手动构建 `tls.Config` ### 最简单的设置(`AttachToHTTPServer`) ``` package main import ( "log" "net/http" "os" "github.com/gosuda/keyless_tls/keyless" ) func main() { certPEM := mustRead("certs/public-chain.crt") mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("ok\n")) }) srv := &http.Server{ Addr: ":8443", Handler: mux, } remoteSigner, err := keyless.AttachToHTTPServer(srv, keyless.HTTPServerAttachConfig{ CertPEM: certPEM, RemoteSigner: keyless.RemoteSignerConfig{ Endpoint: "127.0.0.1:9443", ServerName: "relay.internal", KeyID: "relay-cert", RootCAPEM: mustRead("certs/relay-ca.crt"), ClientCertPEM: mustRead("certs/tunnel-client.crt"), ClientKeyPEM: mustRead("certs/tunnel-client.key"), }, }) if err != nil { log.Fatal(err) } defer remoteSigner.Close() log.Fatal(srv.ListenAndServeTLS("", "")) } func mustRead(path string) []byte { b, err := os.ReadFile(path) if err != nil { panic(err) } return b } ``` ### 高级设置(`NewRemoteSigner` + `NewServerTLSConfig`) 当您已有自己的 `tls.Config` 构建流程,或与 `http.Server` 以外的组件集成时使用此选项。 ``` rSigner, err := keyless.NewRemoteSigner(remoteSignerCfg, certPEM) if err != nil { // handle error } defer rSigner.Close() tlsConf, err := keyless.NewServerTLSConfig(keyless.ServerTLSConfig{ CertPEM: certPEM, Signer: rSigner, NextProtos: []string{"h2", "http/1.1"}, // MinVersion: tls.VersionTLS13, }) if err != nil { // handle error } ``` ### SDK:用于 Relay 路由的 SNI 元数据(调用者控制) 如果您正在使用此库实现自己的 relay/proxy,请使用 `relay/l4` API 来检查 ClientHello 并根据 SNI/ALPN 进行路由,同时将所有策略保留在调用者代码中。 - `l4.InspectClientHello(conn, timeout)`:解析 `ServerName`/`ALPNProtocols` 并返回一个包装后的 `net.Conn` - `l4.Proxy.DialByClientHello(ctx, info, parseErr)`:由调用者决定路由/回退/拒绝策略 这在实践中的工作原理: 1. 传入的 TCP 连接到达 2. 库仅读取 ClientHello 元数据(无 TLS 终止) 3. 您的回调接收 `info.ServerName`、`info.ALPNProtocols` 和 `parseErr` 4. 您的代码选择上游目标(或拒绝) 5. Relay 继续原始 TCP 转发,无负载丢失 典型的 SDK 路由策略: - 多租户主机路由:`app1.example.com -> tenant A`,`app2.example.com -> tenant B` - 协议感知路由:优先选择 `h2` 上游还是 `http/1.1` 上游 - 严格安全模式:当 ClientHello 解析失败时拒绝 - 兼容模式:当解析失败时回退到默认上游 具体策略示例(易于调整): ``` routes := map[string]string{ "app1.demo.local": "127.0.0.1:9001", "app2.demo.local": "127.0.0.1:9002", } proxy := &l4.Proxy{ ListenAddr: ":443", ClientHelloTimeout: 2 * time.Second, DialByClientHello: func(ctx context.Context, info l4.ClientHelloInfo, parseErr error) (net.Conn, error) { d := net.Dialer{Timeout: 3 * time.Second} // 1) Decide what to do with non-TLS / invalid ClientHello if parseErr != nil { // strict mode: return nil, parseErr // compatibility mode: send to default route return d.DialContext(ctx, "tcp", "127.0.0.1:9011") } // 2) SNI host-based route if target, ok := routes[strings.ToLower(strings.TrimSuffix(info.ServerName, "."))]; ok { return d.DialContext(ctx, "tcp", target) } // 3) Optional ALPN-aware split for _, proto := range info.ALPNProtocols { if proto == "h2" { return d.DialContext(ctx, "tcp", "127.0.0.1:9443") } } // 4) Default route return d.DialContext(ctx, "tcp", "127.0.0.1:9011") }, } ``` 有关包含 10 个主机的完整可运行 SDK 样式路由示例,请参阅 `examples/relay-10-targets`。 ### SDK 集成检查清单 - 在隧道应用中仅部署公共证书链(`cert PEM`) - 配置签名者端点/服务器名称/`KeyID`/根 CA - 提供 mTLS 客户端材料(`client cert/key`) - 在关闭时调用 `remoteSigner.Close()` ## 2) 使用二进制文件 `cmd/` 包含面向生产的 `main` 包(可运行的二进制文件)。 示例应用程序分离在 `examples/` 下。 ### 命令布局 - `cmd/relay-signer`:远程签名者 HTTPS 服务器 - `cmd/relay-l4`:带有可选基于 SNI 路由映射的 L4 TCP Relay - `examples/tunnel-http`:与 SDK 集成的示例隧道 HTTP 服务器 - `examples/relay-10-targets`:通过 SNI 路由到 10 个目标主机的一个 Relay 服务器 ### 用于自定义 Relay 的 SNI/ALPN 路由钩子 如果您正在构建自己的 relay/proxy,请使用 `relay/l4.InspectClientHello` 在不终止 TLS 的情况下读取 ClientHello 元数据(`ServerName`、`ALPNProtocols`)。 该辅助函数返回一个包装后的 `net.Conn`,它会重放已读取的字节,因此您的 Relay 可以在做出路由决策后继续正常的 TCP 转发。 `relay/l4.Proxy` 还通过 `DialByClientHello(ctx, info, parseErr)` 支持基于回调的拨号,因此所有策略决策(回退、拒绝、默认路由)保留在调用者代码中。 ### 三进程快速入门 1. 运行签名者服务器 ``` go run ./cmd/relay-signer \ -listen :9443 \ -key-id relay-cert \ -tls-cert certs/relay-server.crt \ -tls-key certs/relay-server.key \ -sign-key certs/relay-signing.key ``` 2. 运行隧道应用 ``` go run ./examples/tunnel-http \ -listen :8443 \ -cert certs/public-chain.crt \ -signer-addr 127.0.0.1:9443 \ -signer-name relay.internal \ -key-id relay-cert \ -client-cert certs/tunnel-client.crt \ -client-key certs/tunnel-client.key \ -root-ca certs/relay-ca.crt ``` 3. 运行 L4 Relay ``` go run ./cmd/relay-l4 \ -listen :443 \ -route app1.example.com=127.0.0.1:8443 \ -default-upstream 127.0.0.1:8443 ``` SNI 路由模式(`-route` 可重复): ``` go run ./cmd/relay-l4 \ -listen :443 \ -route app1.example.com=127.0.0.1:8441 \ -route app2.example.com=127.0.0.1:8442 \ -default-upstream 127.0.0.1:8440 ``` `cmd/relay-l4` 不强制执行路由策略。调用者侧策略由标志控制,包括 ClientHello 解析失败是否可以使用默认上游。 有用的 `cmd/relay-l4` 路由模式标志: - `-route host=upstream`(可重复):显式 SNI 映射 - `-default-upstream`:未知 SNI 的回退目标 - `-allow-parse-error`:允许非 TLS/无效 ClientHello 使用回退 - `-clienthello-timeout`:最大 ClientHello 检查时间 ### 示例应用:一个 Relay 路由 10 个目标主机 `examples/relay-10-targets` 演示了一个实用的入口布局: - 一个公共 Relay 监听器 - 十个目标隧道应用 - 由调用者代码实现的基于 SNI 的目标选择 运行示例 Relay: ``` go run ./examples/relay-10-targets \ -listen :443 \ -upstream-host 127.0.0.1 \ -base-port 9001 \ -domain demo.local \ -default-upstream 127.0.0.1:9011 ``` 生成的静态路由: - `app1.demo.local -> 127.0.0.1:9001` - `app2.demo.local -> 127.0.0.1:9002` - `app3.demo.local -> 127.0.0.1:9003` - `app4.demo.local -> 127.0.0.1:9004` - `app5.demo.local -> 127.0.0.1:9005` - `app6.demo.local -> 127.0.0.1:9006` - `app7.demo.local -> 127.0.0.1:9007` - `app8.demo.local -> 127.0.0.1:9008` - `app9.demo.local -> 127.0.0.1:9009` - `app10.demo.local -> 127.0.0.1:9010` 策略仍归调用者所有: - 已知 SNI:路由到映射的上游 - 未知 SNI:配置时路由到 `-default-upstream` - 非 TLS 或无效 ClientHello:配置时路由到 `-default-upstream`,否则拒绝 `examples/relay-10-targets` 的重要标志: - `-listen`:公共 Relay 地址 - `-upstream-host`:用于生成目标的主机 - `-base-port`:第一个目标端口(`app1`) - `-domain`:用于 SNI 匹配的主机后缀 - `-default-upstream`:可选的回退上游 - `-dial-timeout`:上游拨号超时 - `-clienthello-timeout`:ClientHello 检查超时 ### 签名者传输需要 mTLS 签名者和隧道客户端必须始终配置为双向 TLS。 ``` go run ./cmd/relay-signer \ -listen :9443 \ -key-id relay-cert \ -tls-cert certs/relay-server.crt \ -tls-key certs/relay-server.key \ -client-ca certs/client-ca.crt \ -sign-key certs/relay-signing.key go run ./examples/tunnel-http \ -listen :8443 \ -cert certs/public-chain.crt \ -signer-addr 127.0.0.1:9443 \ -signer-name relay.internal \ -key-id relay-cert \ -client-cert certs/tunnel-client.crt \ -client-key certs/tunnel-client.key \ -root-ca certs/relay-ca.crt ``` ## 安全和操作说明 - 仅在 `relay-signer` 中存储私钥;永远不要将它们分发到隧道应用 - 在隧道应用中仅保留公共证书链 - 强制执行签名者 mTLS 并将其与 `KeyID` 范围的 ACL 配对 ### 签名者 API 协议(`/v1/sign`) 请求: ``` { "key_id": "relay-cert", "algorithm": "RSA_PSS_SHA256", "digest": "", "timestamp_unix": 1735628400, "nonce": "c4d76ad40f5d8f95a1fe4b2f1c922f4a" } ``` 响应: ``` { "key_id": "relay-cert", "algorithm": "RSA_PSS_SHA256", "signature": "" } ``` ## 包结构 - `keyless`:面向应用开发者的 SDK(隧道应用集成点) - `keyless/signerclient`:远程签名者客户端实现 - `relay/signrpc`:签名者 JSON 请求/响应类型 - `relay/signer`:签名服务/密钥存储 - `relay/server`:签名者 HTTPS(强制 mTLS)服务器启动器 - `keyless/lifecycle`:每次租约的 mTLS 身份管理(颁发、续订、验证、磁盘备份的加密存储) - `relay/l4`:TCP 直通 Relay + 可选的 ClientHello(SNI/ALPN)检查钩子 ## 当前状态 此实现处于早期阶段。在生产使用之前,请考虑添加: - 重放缓存 - 速率限制 - 密钥轮换策略 - 可观测性(OTel/指标/日志关联)
标签:Delegated Cryptography, EVTX分析, EVTX分析, Go语言, Homebrew安装, HSM, HTTPs, JSONLines, mTLS, SamuraiWTF, TLS, 加密技术, 安全中间件, 安全通信, 密码学, 底层编程, 手动系统调用, 无密钥TLS, 日志审计, 私钥保护, 程序破解, 网络安全, 证书验证, 远程签名, 防御工具, 隐私保护, 零信任