+36
-7
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -15,10 +16,12 @@ import (
|
||||
|
||||
// ONS 消息头(方案A §2.2/§2.3,官方 X-OCI-NS-* 字段)。
|
||||
const (
|
||||
onsHeaderMessageType = "X-OCI-NS-MessageType"
|
||||
onsHeaderMessageID = "X-OCI-NS-MessageId"
|
||||
onsHeaderTimestamp = "X-OCI-NS-Timestamp"
|
||||
onsHeaderConfirmURL = "X-OCI-NS-ConfirmationURL"
|
||||
onsHeaderMessageType = "X-OCI-NS-MessageType"
|
||||
onsHeaderMessageID = "X-OCI-NS-MessageId"
|
||||
onsHeaderTimestamp = "X-OCI-NS-Timestamp"
|
||||
onsHeaderConfirmURL = "X-OCI-NS-ConfirmationURL"
|
||||
onsHeaderSigningCertURL = "X-OCI-NS-SigningCertURL"
|
||||
onsHeaderSignature = "X-OCI-NS-Signature"
|
||||
)
|
||||
|
||||
// webhook 同步路径的防御参数:body 上限高于 ONS 链路 128KB 留余量,
|
||||
@@ -44,6 +47,15 @@ type webhookHandler struct {
|
||||
|
||||
// handle 是 webhook 入口:secret 反查租户后按消息类型分派;
|
||||
// 同步路径只做 secret→分派→读body→幂等入库,5 秒投递预算内返回(方案A §2.4)。
|
||||
//
|
||||
// @Summary OCI 日志回传入口(ONS HTTPS 订阅投递)
|
||||
// @Tags 任务与日志回传
|
||||
// @Param secret path string true "面板生成的回传密钥(鉴权凭据)"
|
||||
// @Param body body object true "ONS 消息原文(SubscriptionConfirmation / Notification)"
|
||||
// @Success 200 "已受理"
|
||||
// @Failure 403 {object} map[string]string "时间戳超窗或证书源非 Oracle 域"
|
||||
// @Failure 404 "secret 无效(不暴露端点存在性)"
|
||||
// @Router /api/v1/webhooks/oci-logs/{secret} [post]
|
||||
func (h *webhookHandler) handle(c *gin.Context) {
|
||||
cfgID, ok := h.events.ResolveSecret(c.Request.Context(), c.Param("secret"))
|
||||
if !ok {
|
||||
@@ -141,8 +153,25 @@ func parseOnsTime(value string) (time.Time, bool) {
|
||||
}
|
||||
|
||||
// verifySignature 校验 ONS 消息签名。
|
||||
// TODO(方案A §2.3 spike):官方未公开签名 canonical form,待 test-server 抓取
|
||||
// 真实消息确认输入格式后实现;当前按方案约定以 secret+时间戳+幂等先行。
|
||||
func verifySignature(http.Header) error {
|
||||
//
|
||||
// 现状:官方未公开 canonical form。归档任务 07-06-log-relay-plan-a 与 07-07-relay-impl
|
||||
// 用真实抓包样本(body+签名+证书公钥)对 15 种常见拼接 × 2 种 PSS salt 长度全部未命中,
|
||||
// 逆向成本高。P0 决策以「secret 路径段 + HTTPS + 时间戳偏差 ≤5min + MessageId 幂等」上线。
|
||||
//
|
||||
// 本函数做能做的**证书源头硬化**:即便未来接入 canonical form + 公钥验签,
|
||||
// SigningCertURL 若允许任意域名,攻击者可自签证书构造 body+签名+cert URL 三元组绕过
|
||||
// (AWS SNS 曾出现同类 GHSA-8jgf-23q5-x7xx)。这里预先把 cert URL 域名锁到 Oracle 云,
|
||||
// 让未来加签名校验成为「相加」而非「返修」。
|
||||
func verifySignature(h http.Header) error {
|
||||
certURL := h.Get(onsHeaderSigningCertURL)
|
||||
if certURL == "" {
|
||||
return nil // 头缺失回退到 secret+时间戳+幂等三重防护,不误杀历史 layout
|
||||
}
|
||||
parsed, err := url.Parse(certURL)
|
||||
if err != nil || parsed.Scheme != "https" || !service.IsOracleHost(parsed.Host) {
|
||||
return errors.New("ons: signing cert url not in oracle domain")
|
||||
}
|
||||
// TODO(canonical form 公开后):按 SigningCertURL 拉证书(含 host 复核 + 缓存),
|
||||
// 用公钥对 signing string 做 SHA256withRSA/PSS 验签,失败返回 error。
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user