这个包提供了一个完整且灵活的 TURN/STUN 服务器实现,支持 TCP、UDP 和 TLS 传输,适用于 WebRTC 应用程序。
log/sloggo get github.com/darkit/turn
我们提供了多种优雅的API调用方式,满足不同的使用场景和偏好。
package main
import (
"log/slog"
"os"
"github.com/darkit/turn"
)
func main() {
// 使用默认配置并提供外部IP
config, err := turn.DefaultConfigWithIP("203.0.113.5")
if err != nil {
slog.Error("无法创建默认配置", "error", err)
os.Exit(1)
}
// 启动服务器
server, err := turn.Start(config)
if err != nil {
slog.Error("无法启动TURN服务器", "error", err)
os.Exit(1)
}
// 服务器已启动,等待中断信号...
_ = server
}
提示:当需要停止服务时,调用
server.Stop()即可完成默认优雅停机,也可以传入context.WithTimeout等上下文控制最长等待时间。
package main
import (
"log/slog"
"os"
"github.com/darkit/turn"
)
func main() {
// 获取默认配置
config := turn.DefaultConfig()
// 自定义特定选项
config.TurnAddress = ":3479" // 更改监听端口
config.LogLevel = "debug" // 更改日志级别
// 创建IP提供者
ipProvider, err := ipdns.NewStatic([]string{"203.0.113.5"})
if err != nil {
slog.Error("无法创建IP地址提供者", "error", err)
os.Exit(1)
}
config.TurnIPProvider = ipProvider
// 预处理配置(包含CIDR解析和验证)
if err := config.PrepareForStart(); err != nil {
slog.Error("配置预处理失败", "error", err)
os.Exit(1)
}
// 启动服务器
server, err := turn.Start(config)
if err != nil {
slog.Error("无法启动TURN服务器", "error", err)
os.Exit(1)
}
// 服务器已启动,等待中断信号...
}
package main
import (
"log"
"github.com/darkit/turn"
)
func main() {
// 使用构建器模式,更灵活更清晰
server, err := turn.NewServerBuilder().
WithExternalIP("203.0.113.5").
WithPortRange("49152:49200").
WithLogging("json", "info").
Build()
if err != nil {
log.Fatal("无法启动TURN服务器:", err)
}
// 服务器已启动...
}
package main
import (
"log"
"github.com/darkit/turn"
)
func main() {
// 链式配置,支持直接启动
result := turn.NewConfigChain().
Address(":3478").
ExternalIP("203.0.113.5").
PortRange("49152:49200").
Logging("json", "info").
Start()
server := result.
LogError("服务器启动失败").
Unwrap()
// 服务器已启动...
}
package main
import (
"log"
"github.com/darkit/turn"
)
func main() {
// 一行代码启动服务器
server, err := turn.QuickStart("203.0.113.5")
if err != nil {
log.Fatal(err)
}
// 或者带选项的快速启动
server, err = turn.QuickStart("203.0.113.5",
turn.WithPortRangeOption("49160:49200"),
turn.WithDebugOption(),
)
}
package main
import (
"log/slog"
"os"
"github.com/darkit/turn"
)
func main() {
// 使用外部认证配置
config, err := turn.DefaultConfigWithAuth("203.0.113.5", "your-secret-key-at-least-16-chars")
if err != nil {
slog.Error("无法创建认证配置", "error", err)
os.Exit(1)
}
// 启动服务器
server, err := turn.Start(config)
if err != nil {
slog.Error("无法启动TURN服务器", "error", err)
os.Exit(1)
}
// 生成用户凭证
username, password := server.Credentials("user123", nil)
slog.Info("生成用户凭证", "username", username, "password", password)
// 服务器已启动,等待中断信号...
}
提示:客户端使用外部认证时,
realm与secret必须与服务端一致,否则 Allocate 会失败。
package main
import (
"log"
"time"
"github.com/darkit/turn"
)
func main() {
// 启动服务器
server, err := turn.QuickStartWithAuth("203.0.113.5", "your-secret-key")
if err != nil {
log.Fatal(err)
}
// 创建管理器
manager := turn.WithManager(server, turn.Config{})
// 启动自动清理(每小时清理不活跃24小时的用户)
manager.StartCleanupRoutine(time.Hour, 24*time.Hour)
// 创建用户
username, password, err := manager.CreateUser("user123", net.ParseIP("192.168.1.100"))
if err != nil {
log.Printf("创建用户失败: %v", err)
}
// 获取统计信息
stats := manager.GetStats()
log.Printf("活跃用户: %d, 总用户: %d", stats.ActiveUsers, stats.TotalUsers)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Stop(ctx); err != nil {
slog.Error("停止服务器失败", "error", err)
}
// 如果使用 ServerManager,可以直接透传上下文
manager.Stop(ctx)
如无特殊需要,可直接调用 server.Stop() 或 manager.Stop() 使用默认上下文完成优雅停机。
package main
import (
"log/slog"
"os"
"github.com/darkit/turn"
"github.com/darkit/turn/ipdns"
)
func main() {
// 配置日志
turn.ConfigureLogger("text", "info")
// 创建一个静态IP提供者
ipProvider, err := ipdns.NewStatic([]string{"203.0.113.5"})
if err != nil {
slog.Error("无法创建IP地址提供者", "error", err)
os.Exit(1)
}
// 创建配置
config := turn.Config{
Realm: "example.com", // 自定义认证领域
CredentialTTL: 12 * time.Hour, // 外部认证凭证有效期
BlacklistTTL: 6 * time.Hour, // 黑名单自动过期
TurnAddress: ":3478",
TurnExternalPort: "3478", // 对外广告端口,可与监听端口不同
TurnIPProvider: ipProvider,
TurnDenyPeers: []string{"0.0.0.0/8", "127.0.0.1/8"},
}
// 预处理配置
if err := config.PrepareForStart(); err != nil {
slog.Error("配置预处理失败", "error", err)
os.Exit(1)
}
// 启动服务器
server, err := turn.Start(config)
if err != nil {
slog.Error("无法启动TURN服务器", "error", err)
os.Exit(1)
}
// 服务器已启动,等待中断信号...
}
package main
import (
"log/slog"
"os"
"github.com/darkit/turn"
)
func main() {
// 获取默认配置并启用TLS
config := turn.DefaultConfig()
config.TLSEnabled = true
config.TLSCertFile = "/path/to/cert.pem"
config.TLSKeyFile = "/path/to/key.pem"
// 设置外部IP
ipProvider, err := ipdns.NewStatic([]string{"203.0.113.5"})
if err != nil {
slog.Error("无法创建IP地址提供者", "error", err)
os.Exit(1)
}
config.TurnIPProvider = ipProvider
// 预处理配置
if err := config.PrepareForStart(); err != nil {
slog.Error("配置预处理失败", "error", err)
os.Exit(1)
}
// 启动TLS服务器
server, err := turn.StartTLS(config)
if err != nil {
slog.Error("无法启动TURN TLS服务器", "error", err)
os.Exit(1)
}
// 服务器已启动,等待中断信号...
}
// 获取服务器实例
server, err := turn.Start(config)
if err != nil {
slog.Error("无法启动TURN服务器", "error", err)
os.Exit(1)
}
// 为用户生成凭证
username, password := server.Credentials("user123", net.ParseIP("192.168.1.100"))
slog.Info("生成用户凭证", "username", username, "password", password)
// 稍后,如果需要禁用此用户
server.Disallow(username)
slog.Info("用户已被禁用", "username", username)
包中提供了一个完整的命令行TURN服务器应用程序:
# 启动基本TLS TURN服务器
go run ./pkg/turn/turn-tls-server/main.go \
-tls-cert /path/to/cert.pem \
-tls-key /path/to/key.pem \
-turn-address :3478 \
-turn-port-range 49160:49200 \
-turn-external-ip 203.0.113.5 \
-log-level debug
包提供了两个便捷的默认配置函数:
// 返回具有合理默认值的基本配置
config := turn.DefaultConfig()
// 快速创建包含外部IP的配置
config, err := turn.DefaultConfigWithIP("203.0.113.5")
DefaultConfig 提供的默认值包括:
:347849152:65535(IANA推荐的临时端口范围)DefaultConfigWithIP 在默认配置基础上添加:
Config 结构体包含以下重要配置项:
type Config struct {
// TLS证书配置
TLSEnabled bool // 是否启用TLS
TLSCertFile string // TLS证书文件路径
TLSKeyFile string // TLS私钥文件路径
// TURN服务器配置
TurnAddress string // TURN服务器监听地址,默认":3478"
TurnPortRange string // TURN端口范围,格式为"min:max"
// TURN外部IP配置
TurnExternalIP []string // TURN服务器的外部IP地址列表
TurnExternalPort string // TURN服务器的外部端口,默认"3478"
TurnExternalSecret string // 用于外部认证的密钥
TurnIPProvider ipdns.Provider // IP地址提供者
// 安全配置
TurnDenyPeers []string // 拒绝的对等方IP范围
TurnDenyPeersParsed []*net.IPNet // 解析后的拒绝对等方IP网络
// 日志配置
LogFormat string // 日志格式:text, json, console
LogLevel string // 日志级别:debug, info, warn, error
// 内部使用字段
TurnExternal bool // 是否使用外部TURN服务器
}
IP地址提供者用于确定TURN服务器的外部IP地址,有以下几种实现:
静态IP提供者:明确指定外部IPv4和IPv6地址
provider, err := ipdns.NewStatic([]string{"203.0.113.5", "2001:db8::1"})
DNS提供者:通过DNS记录获取IP地址
resolver := &net.Resolver{/* 配置 */}
provider := &ipdns.DNS{
DNS: "1.1.1.1:53",
Resolver: resolver,
Domain: "turn.example.com",
}
STUN探测器:通过 STUN 服务器实时探测外部 IP,内建 30 秒缓存与失败回退策略
provider, err := ipdns.NewProber("stun.l.google.com:19302", true)
stun:/turn: URL 并自动填充默认端口nat1to1 为 true 时,如果 STUN 返回私网/环回地址默认只告警并回退;若需严格拒绝,可将 prober.AllowPrivateFallback=falseTURN服务器支持两种认证模式:
内部认证:在服务器内部管理凭证
// 不提供TurnExternalSecret,将使用内部认证
config := turn.Config{
// ...其他配置
TurnExternal: false,
}
外部认证:使用外部密钥生成凭证
config := turn.Config{
// ...其他配置
TurnExternalSecret: "your-shared-secret",
TurnExternal: true,
}
在内部认证模式下,服务器直接存储用户凭证:
// 生成凭证并存储在服务器中
username, password := server.Credentials("user123", net.ParseIP("192.168.1.100"))
// 禁用用户(立即生效)
server.Disallow(username)
在外部认证模式下,服务器使用共享密钥和时间戳生成临时凭证:
// 生成基于时间的临时凭证
username, password := server.Credentials("user123", net.ParseIP("192.168.1.100"))
// 格式为:timestamp:userID
// 禁用用户(通过黑名单机制实现)
server.Disallow(username)
// 也可以直接使用用户 ID,便于在认证层统一屏蔽
server.Disallow("user123")
外部认证模式下的黑名单机制会提取用户名中的ID部分(而不是时间戳),并将其加入黑名单。这样即使用户尝试使用有效的时间戳重新获取凭证,只要用户ID在黑名单中,认证仍会失败。
黑名单和凭证的有效期可通过
CredentialTTL与BlacklistTTL配置,黑名单到期会自动清理并允许重新认证。
可以限制TURN服务器使用的端口范围:
config := turn.Config{
// ...其他配置
TurnPortRange: "49160:49200", // 端口范围从49160到49200
}
// 如果监听端口因 NAT 映射为其他公网端口,可单独设置对外广告端口
config.TurnExternalPort = "3479"
可以通过设置黑名单阻止特定IP范围:
config := turn.Config{
// ...其他配置
TurnDenyPeers: []string{
"0.0.0.0/8", // 保留地址
"127.0.0.1/8", // 本地环回地址
"10.0.0.0/8", // 私有网络
"172.16.0.0/12", // 私有网络
"192.168.0.0/16" // 私有网络
},
}
提供灵活的日志配置选项:
// 使用JSON格式,DEBUG级别日志
turn.ConfigureLogger("json", "debug")
// 使用文本格式,INFO级别日志
turn.ConfigureLogger("text", "info")
// 使用控制台格式,ERROR级别日志
turn.ConfigureLogger("console", "error")
turn.DefaultConfig() Config - 返回具有合理默认值的配置turn.DefaultConfigWithIP(externalIP string) (Config, error) - 返回包含指定外部IP的默认配置turn.DefaultConfigWithAuth(externalIP, secret string) (Config, error) - 返回包含外部认证的默认配置turn.Start(conf Config) (Server, error) - 启动标准TURN服务器turn.StartTLS(conf Config) (Server, error) - 启动支持TLS的TURN服务器turn.ConfigureLogger(format, level string) - 配置日志系统turn.NewServerBuilder() - 创建服务器构建器builder.WithExternalIP(ip) - 设置外部IPbuilder.WithExternalAuth(secret) - 启用外部认证builder.WithTLS(cert, key) - 启用TLSbuilder.WithPortRange(range) - 设置端口范围builder.Build() - 构建服务器turn.NewConfigChain() - 创建配置链chain.ExternalIP(ip) - 设置外部IPchain.ExternalSecret(secret) - 设置外部认证chain.TLS(cert, key) - 启用TLSchain.Start() - 直接启动服务器turn.QuickStart(externalIP, ...options) - 快速启动服务器turn.QuickStartWithAuth(externalIP, secret, ...options) - 快速启动认证服务器turn.WithTLSOption(cert, key) - TLS选项turn.WithPortRangeOption(range) - 端口范围选项turn.WithDebugOption() - 调试选项turn.WithManager(server, config) - 为服务器添加管理功能(传入已使用或默认配置)manager.CreateUser(id, ip) - 创建用户manager.DisableUser(id) - 禁用用户manager.GetStats() - 获取统计信息manager.StartCleanupRoutine(interval, inactive) - 启动清理例程type Server interface {
// 生成用户凭证
Credentials(id string, addr net.IP) (string, string)
// 禁用用户
// - 内部认证:从用户存储中删除凭证
// - 外部认证:将用户ID添加到黑名单
Disallow(username string)
// 用户认证
Authenticate(username, realm string, addr net.Addr) ([]byte, bool)
}
type Provider interface {
Get() (net.IP, net.IP, error)
}
+----------------+ +----------------+ | 应用程序 | | 命令行TURN工具 | +----------------+ +----------------+ | | v v +-------------------------------+ | turn 包 | +-------------------------------+ | Server | Config | +--------------+----------------+ | Generator | RelayAddressGen | +--------------+----------------+ | ipdns | +--------------+----------------+ | Static | DNS | Prober | +-------------+----------------+-----------+ | +--------v---------+ | pion/turn 包 | +------------------+
TurnPortRange)避免端口耗尽TurnDenyPeers)防止滥用TurnExternalSecret)以便集成认证系统无法启动服务器
客户端无法连接
日志问题
-log-level debug获取更详细的日志LogFormat配置是否正确认证问题
欢迎对本项目做出贡献。在提交代码前,请确保:
本项目基于 MIT 许可证,详见LICENSE文件。