- 网络安全
【免费下载链接】sliver
Adversary Emulation Framework
Sliver 仓库的vendor/tailscale.com/logtail目录内置了 Tailscale Logs Service 的完整客户端库与接口文档(api.md)。本文以该 API 文档为主体,逐节讲解 Collection/Instance 双级数据模型、日志存储、检索与配置四类 REST 接口的请求格式与语义,并结合仓库内logtail.go、config.go、buffer.go等源码实现,说明 Sliver 所依赖的tsnet组件是如何在实际运行中调用这套接口的。
一、日志服务总览与认证方式
Tailscale Logs Service 定义了一套 REST 接口,用于日志条目的配置、存储、检索与处理。HTTP 请求发送到服务的 base URLhttps://log.tailscale.com,响应为 JSON 编码并使用标准 HTTP 状态码。
配置类与检索类 API 的授权方式是将 secret API key 作为 HTTP Basic Auth 的 username传入。secret key 通过 base URL 下的 Web UI 生成。原文档给出的 curl 示例:
curl -u <log_api_key>: https://log.tailscale.com/collections注意 password 部分留空,key 位于 username 位置。此外文档预告了未来的演进方向:后续会支持通过 HTTP 头指定 MessagePack 替代 JSON 作为响应/请求格式。
二、核心数据模型:Collection 与 Instance
Collection:以域名划分的日志分组
日志被组织进collection。每个 collection 内可以包含任意数量的instance。
- Collection 的名字就是一个域名,是对相关日志的分组。文档建议:每个产品创建一个 collection,使用公司域名的子域名。
- Collection 必须在存储任何日志之前先向 logs service 注册。
Instance:每台机器一个实例,private/public 双 ID
每个 collection 由若干 instance 构成,写日志的每台机器对应一个 instance。instance 有名字和编号,并持有两个 ID:
| ID | 定义 | 用途 |
|---|---|---|
| private ID | 32 字节随机数,hex 编码 | 用于写日志;唯一副本应留在发送日志的机器上,理想情况下在机器本地生成;生成后即可开始写日志 |
| public ID | private ID 的 SHA-256 哈希,hex 编码 | 用于读日志和 adopt;设计上可以安全地发送给同时持有 logs service API key 的服务 |
Adoption 机制与保留策略
- logs service 只会短期存储日志。要启用长期保留,需要用public ID + API key对日志执行adopt;adopt 之后日志按配置的保留期长期保留。
- 未被 adopt的 instance 日志只临时保存,用于调试场景——比如一台配置错误、用坏 ID 写日志的机器,可以通过读取日志发现它。未 adopt 的日志存储有严格上限,12 小时后删除。
三、存储 API:POST /c/<collection-name>/<private-ID>
请求体为 JSON,有两种合法形态。
单条消息
单条消息是一个 JSON 对象。客户端可以在对象里放入任意自定义属性,唯一保留属性是logtail:
- 在
logtail对象内,客户端目前只能设置client_time属性,格式为 RFC3339(Go 参考格式"2006-01-02T15:04:05.999999999Z07:00")。 - 文档预告的后续版本将支持
client_time_offset(客户端重置以来的纳秒数,整数)与client_time_reset(布尔值,置 true 时重置时间偏移计数器)。服务端收到client_time_offset后,会以第一条(或 reset 那条)事件被接收时的server_time为基准换算出client_time。 - 若在
logtail对象中设置了其他属性,它们会被移入"error"字段,消息会被保存,并返回 4xx 状态码。
批量消息
批量消息是一个 JSON 数组,元素为单条消息对象:[ { }, { }, ... ]。
- 若数组中某条目不是对象,该内容会被转成带
"logtail": { "error": ... }属性的消息保存,并返回 4xx。 - 其他不符合这两种格式的请求内容,同样被保存进 logtail error 字段并返回 4xx。
- 非法 collection 名返回
{"error": "invalid collection name"}加 403 状态码。
客户端行为建议
文档鼓励客户端:
- 尽可能快地 POST(除非受电池续航限制)。这既缩短了日志在查看器中可见的时间,也降低了日志丢失概率;
- 流式写日志时使用 HTTP/2,它更擅长维持 TLS 连接,减少后续 POST 的开销。
文档还预告未来版本将支持Content-Encoding: zstd的请求体压缩。
四、检索 API
GET /collections —— 查询 collections 与 instances
返回一个列出所有命名 collection 的 JSON 对象。调用方可通过 query 参数collection-name把结果限定到单个 collection。响应示例:
{ "collections": { "collection1.yourcompany.com": { "instances": { "<logid.PublicID>": { "first-seen": "timestamp", "size": 4096 }, "<logid.PublicID>": { "first-seen": "timestamp", "size": 512000, "orphan": true } } } } }其中 instance 以 public ID 为键;orphan: true标记未被 adopt 的孤儿实例。
GET /c/<collection_name> —— 查询已存储的日志
可调用的 query 参数:
instances—— 零个或多个 instance,限定结果范围;time-start—— 要包含的最早日志时间;- 以下三选一:
time-end—— 要包含的最新日志时间;max-count—— 最大返回条数,用于分页;stream—— 布尔值,保持响应挂起、像tail -f一样持续推送日志,与time-end不兼容。
两种响应模式:
- stream=false:单个 JSON 对象,形如
{ "logs": [ {}, {}, ... ] }(header 字段在原文档中标注为 TODO)。 - stream=true:响应先给出一个与存储格式类似的 JSON header 对象,随后是逐行一个的 JSON 日志对象
{...},服务端持续发送直到客户端关闭连接。
五、配置 API
对少量 instance 的组织,配置 API 最好由受信任的人工操作者(通常通过 GUI)调用;instance 数量多的组织则需要自动化 token 创建。
POST /collections —— 创建或删除 collection
调用方必须设置collection属性以及action=create或action=delete(form 编码或 JSON 编码均可)。字符集限制为[a-zA-Z0-9-_.]+。collection 名是全局命名空间,通常就是一个域名。
POST /instances —— 将 instance adopt 进 collection
必须发送以下属性(form 或 JSON 编码):
collection—— 合法 FQDN([a-zA-Z0-9-_.]+);instances—— instance 的 public ID,hex 编码。
collection 名必须已被调用方所属的 group 认领。(collection-name, instance-public-ID)这一对可能已经有关联日志、也可能还没有。失败时返回 4xx/5xx 状态码和{"error": "what went wrong"}。
六、客户端侧实现:仓库内 logtail 库如何对接这套 API
以上接口的 Go 客户端就在仓库中,可作为接口语义的逐条印证。
URL 构造与写入路径
config.go 定义了DefaultHost = "log.tailscale.com"和Config结构体,字段与 API 概念一一对应:Collection(collection 域名)、PrivateID(写入用的私有 ID)、CopyPrivateID(一个覆盖本日志流的超集流的私有 ID)、BaseURL(留空时回落到默认 host)等。
logtail.go 的 NewLogger 中可以直接看到文档中POST /c/<collection-name>/<private-ID>路由的客户端形态:
url: cfg.BaseURL + "/c/" + cfg.Collection + "/" + cfg.PrivateID.String() + urlSuffix,若设置了CopyPrivateID,还会附加?copyId=...查询参数,把同一批日志复制到超集流中。
批量数组格式与大小约束
drainPending 证实了文档中"批量消息是 JSON 数组"的说法:客户端把缓冲行拼成[开头、,分隔、]结尾的数组一次性上传。相关常量:
maxSize = 256 << 10:单条日志及单次上传体的上限 256KiB;maxTextSize = 16 << 10:纯文本日志的上限,超出的部分会被截断并在字符串末尾追加省略标记(见 appendTruncatedString);lowMemRatio = 4:低内存模式下上述上限除以 4,倾向"多次小批上传"而非攒大请求,避免 OOM。
Buffer抽象(buffer.go)是一个环形缓冲:TryReadLine读出日志行,无数据返回 nil、关闭返回 io.EOF;默认的MemoryBuffer以容量 256 的 channel 实现(低内存模式为 64)。
logtail 保留属性的客户端侧强制
文档规定logtail对象内只能出现client_time,客户端库同样执行该规则。appendMetadata 会写入client_time(RFC3339Nano)、proc_id(Config.IncludeProcID时为进程生成的一次性随机 ID)、proc_seq(IncludeProcSequence时的进程内自增序号),以及出错时的error.detail/error.bad_data子字段。
appendTextOrJSONLocked 则实现了"客户端消息中保留logtail属性"的语义:若用户传入的 JSON 对象自身含有顶层logtail键,库会将其剥离并改写为"logtail": {"error": {"detail": "duplicate logtail member", "bad_data": ...}}——这与文档中"多余 logtail 属性被移入 error 字段"的服务端规则互为镜像。
压缩、重试与网络感知
- upload 对应文档预告的
Content-Encoding: zstd:当Config.CompressLogs开启且响应体压缩后确有收益(节省超过 64 字节,扣除头部开销)时,会附带Content-Encoding: zstd与Orig-Content-Length头上传 zstd 帧。 - 失败重试遵循标准语义:响应非 200 时解析
Retry-After头作为等待时长,缺失时回退到 30~60 秒的随机退避;每次上传带 45 秒超时。 - 库还会通过 netmon/eventbus 感知网络是否可达(awaitInternetUp),断网时暂停上传并等待"internet back up"事件,正好呼应文档中"尽快 POST 以降低丢日志概率"的建议——缓冲 + 唤醒机制让写操作不因网络阻塞。
刷新延迟与关闭语义
config.go 中defaultFlushDelay = 2 * time.Second,FlushDelayFn可自定义:返回 0 或负值立即上传,正值则按该延迟攒批。Logger.Shutdown会阻塞式地完成剩余上传(受传入 context 控制),Close是它的废弃别名;Disable()则可以在进程生命周期内全局停止上传。
七、Sliver 中的实际落点:tsnet 传输如何间接使用 logtail
Sliver 自身代码并未直接 importtailscale.com/logtail,而是经由tailscale.com/tsnet间接触达该服务,这决定了它的定位:logtail 在 Sliver 中服务于tsnet/Tailscale 传输通道,而不是 Sliver 自身的审计日志(后者走server/log/下的本地审计与日志轮转)。
- server/transport/tailscale.go 实现了 tsnet gRPC 监听:要求环境变量
TS_AUTHKEY,在tsnet应用目录下创建tsnet.Server并Listen出 gRPC 端点。 - 被依赖的 tsnet.go 在启动时构建
logpolicy.Config并调用logtail.NewLogger(L749-L777),集合名使用logtail.CollectionNode(即tailnode.log.tailscale.io,见 config.go),日志写入通过logf委托给s.logtail.Logf,并在退出时执行logtail.Shutdown。
因此,阅读 api.md 时应把握两点适用前提:其一,该接口是 Tailscale 官方日志服务的公有 API 规范,base URL 与认证方式以文档为准;其二,在本仓库的语境下,它是 tsnet 传输组件的遥测通道,开发者若需要为 Sliver 配置 Tailscale 传输,只需关注TS_AUTHKEY与 tsnet 目录,logtail 的写入行为由依赖库自动完成。
八、小结
- 数据模型:Collection(域名,全局命名空间,需先注册)→ Instance(每台写日志的机器一个,private/public 双 ID);private ID 只留本机用于写入,public ID 配合 API key 用于读取与 adopt,未 adopt 的日志 12 小时后删除。
- 存储:
POST /c/<collection>/<private-ID>,单对象或对象数组,logtail为保留属性(当前仅client_time),非法内容被降级为logtail.error并返回 4xx。 - 检索:
GET /collections查拓扑,GET /c/<collection>按instances/time-start加time-end/max-count/stream分页或流式拉取。 - 配置:
POST /collections建/删 collection,POST /instances完成 adopt。 - 仓库内的 logtail.go、config.go、buffer.go 是这套接口的完整参考客户端,Sliver 通过 tsnet 集成 在 tsnet 传输中实际使用它。
- 网络安全
【免费下载链接】sliver
Adversary Emulation Framework
相关推荐
微服务容器日志:paascloud-master中日志轮转配置
微服务容器日志:paascloud master中日志轮转配置 在分布式微服务架构中,日志管理是保障系统稳定运行的关键环节。paascloud master作为
后端微服务电商认证鉴权Druid连接池日志配置详解:日志输出优化
Druid连接池日志配置详解:日志输出优化 引言 在数据库应用开发中,连接池是提升性能和可靠性的关键组件。Druid(德鲁伊)作为阿里巴巴开源的数据库连接池,不
数据库后端微服务容器日志:paascloud-master中Docker日志驱动配置
微服务容器日志:paascloud master中Docker日志驱动配置 日志架构概述 paascloud master作为Spring Cloud微服务架构
后端微服务电商认证鉴权
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考