第一章:Go语言漏洞多吗?——从CVE迷思到Context真相
常有人将“Go语言漏洞多”与频繁发布的CVE条目直接挂钩,但这种归因存在根本性误读。CVE编号本身不区分漏洞成因层级:它既可能源于Go标准库的net/http或crypto/tls等核心包(如CVE-2023-45858),也可能来自第三方模块、应用层逻辑缺陷,甚至构建环境(如恶意依赖注入)。截至2024年中,NVD数据库中标记为“Go”的CVE共约120项,其中仅37%涉及标准库,其余多为生态组件(如golang.org/x/net、github.com/gorilla/mux)或用户代码误用。
一个典型误区是混淆“Context取消机制”与“安全漏洞”。context.Context本身不是漏洞源,但错误使用会引发严重问题:
- 忘记传递
ctx导致goroutine泄漏; - 在HTTP handler中复用
context.Background()跳过超时控制; - 用
context.WithCancel(context.Background())替代r.Context()破坏请求生命周期。
以下代码演示安全的Context传递模式:
func handleRequest(w http.ResponseWriter, r *http.Request) {
// ✅ 正确:继承请求上下文,自动绑定取消信号
ctx := r.Context()
// 启动带超时的子任务
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel() // 确保及时释放
result, err := fetchResource(ctx) // 该函数需响应ctx.Done()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
http.Error(w, "timeout", http.StatusGatewayTimeout)
return
}
http.Error(w, "error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(result)
}
关键原则包括:
- 永远从
http.Request.Context()而非context.Background()派生; - 所有I/O操作(数据库、HTTP客户端、文件读写)必须接受并响应
context.Context; cancel()必须在作用域结束前调用,避免资源泄漏。
| 错误模式 | 风险 | 修复方式 |
|---|---|---|
ctx := context.Background() |
请求中断后goroutine持续运行 | 改用 r.Context() |
select { case <-time.After(3s): ... } |
绕过Context取消 | 替换为 select { case <-time.After(3s): ... case <-ctx.Done(): return } |
忘记defer cancel() |
上下文泄漏,内存/句柄累积 | 使用defer确保执行 |
Go的安全韧性不在于“零CVE”,而在于其设计强制开发者显式处理取消、超时与传播——这恰恰是多数漏洞的预防性护栏。
第二章:Context超时传播的底层机制与典型误用模式
2.1 context.Context的生命周期与取消信号传播原理
context.Context 的生命周期严格绑定于其创建者,一旦父 Context 被取消,所有派生子 Context 将不可逆地进入 Done 状态,且 Done() 通道永久关闭。
取消信号的树状传播
ctx, cancel := context.WithCancel(context.Background())
child := context.WithValue(ctx, "key", "val")
cancel() // 触发 ctx → child 的级联关闭
cancel()关闭ctx.Done(),子 Context 内部监听该通道,立即关闭自身Done();WithValue不影响取消链路,仅扩展数据;真正继承取消行为的是WithCancel/WithTimeout/WithDeadline。
核心状态流转
| 状态 | 触发条件 | Done() 行为 |
|---|---|---|
| active | 初始或未取消 | 阻塞读取 |
| cancelled | cancel() 调用 |
立即关闭并返回 nil |
| timed out | 超时时间到达 | 关闭,Err()=DeadlineExceeded |
graph TD
A[Background] -->|WithCancel| B[Parent]
B -->|WithTimeout| C[Child1]
B -->|WithValue| D[Child2]
C -->|WithCancel| E[Grandchild]
B -.->|cancel()| C
B -.->|cancel()| D
C -.->|timeout| E
2.2 超时嵌套中Deadline/Timeout的级联失效实战复现
当 gRPC 客户端调用链中存在多层超时嵌套(如外层 WithTimeout(5s) 包裹内层 WithDeadline(...)),子 Context 的 Deadline 可能因父 Context 提前取消而被静默截断。
失效触发条件
- 父 Context 在子 Deadline 到达前主动 Done
- 子 Context 未显式继承父取消信号,仅依赖
WithDeadline的绝对时间 - 底层 transport 层优先响应父 Context 的
Done()通道
复现实例代码
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 子 Context 设定 5s 截止,但受父 2s 限制
childCtx, _ := context.WithDeadline(ctx, time.Now().Add(5*time.Second))
time.Sleep(2500 * time.Millisecond) // 父 ctx 已超时
fmt.Println("Child done?", childCtx.Err() != nil) // true —— 级联失效
逻辑分析:
childCtx虽设定了 5s Deadline,但其底层ctx已在 2s 后触发cancel(),childCtx.Err()立即返回context.Canceled。关键参数:ctx是childCtx的 parent,WithDeadline不重写取消源,仅追加截止时间校验。
| 场景 | 父 Context 状态 | 子 Context 实际行为 |
|---|---|---|
| 正常嵌套 | 未超时 | 尊重自身 Deadline |
| 父先超时 | Done() 触发 |
立即返回 Canceled,忽略自身 Deadline |
graph TD
A[Parent ctx WithTimeout 2s] -->|cancel after 2s| B[Child ctx WithDeadline +5s]
B --> C{Is parent Done?}
C -->|Yes| D[Err = Canceled]
C -->|No| E[Wait until own Deadline]
2.3 HTTP Handler中context.WithTimeout未defer cancel的内存泄漏案例分析
问题复现代码
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
// ❌ 忘记 defer cancel() —— 导致 ctx 永远无法被 GC
dbQuery(ctx) // 长耗时数据库调用
}
context.WithTimeout 返回的 cancel 函数必须显式调用,否则底层 timerCtx 持有的定时器和 goroutine 将持续运行,关联的 ctx 及其携带的 values(如 traceID、user info)无法释放。
内存泄漏链路
- 每个请求创建独立
timerCtx - 未
defer cancel()→ 定时器不触发清理 →ctx引用链驻留堆 - 高并发下积累数千个存活
ctx,伴随http.Request和自定义value(如ctx.WithValue(ctx, key, hugeStruct))
关键修复对比
| 场景 | 是否调用 cancel() |
典型内存增长趋势 |
|---|---|---|
缺失 defer cancel() |
否 | 线性上升,持续数小时不回落 |
正确 defer cancel() |
是 | 请求结束即释放,内存平稳 |
graph TD
A[HTTP Request] --> B[context.WithTimeout]
B --> C{cancel called?}
C -->|No| D[Timer active + ctx retained]
C -->|Yes| E[Timer stopped + ctx GC-ready]
2.4 goroutine泄漏:未绑定context的长任务如何绕过超时控制
当 goroutine 启动后未监听 context.Context 的取消信号,即使父操作已超时,该 goroutine 仍持续运行,形成资源泄漏。
典型泄漏模式
func leakyTask(ctx context.Context, id string) {
go func() {
time.Sleep(10 * time.Second) // ❌ 未检查 ctx.Done()
fmt.Printf("task %s completed\n", id)
}()
}
逻辑分析:该 goroutine 完全忽略 ctx,time.Sleep 不响应取消;ctx 传入但未被消费,导致父调用 WithTimeout 失效。参数 ctx 形同虚设,id 仅用于日志标识。
修复对比表
| 方式 | 是否响应 cancel | 是否复用 ctx | 资源释放时机 |
|---|---|---|---|
| 原始写法 | 否 | 否 | 10秒后硬终止 |
select { case <-ctx.Done(): return } |
是 | 是 | ctx 取消即退出 |
正确实践路径
func safeTask(ctx context.Context, id string) {
go func() {
select {
case <-time.After(10 * time.Second):
fmt.Printf("task %s completed\n", id)
case <-ctx.Done(): // ✅ 主动监听取消
fmt.Printf("task %s cancelled: %v\n", id, ctx.Err())
return
}
}()
}
逻辑分析:select 在超时与取消间二选一;ctx.Done() 触发时立即返回,避免 goroutine 悬挂。ctx.Err() 提供取消原因(如 context.DeadlineExceeded)。
graph TD
A[启动goroutine] --> B{监听ctx.Done?}
B -->|否| C[持续运行→泄漏]
B -->|是| D[select等待]
D --> E[超时完成]
D --> F[ctx取消→立即退出]
2.5 数据库连接池+context超时错配导致的连接耗尽压测验证
当 context.WithTimeout 设置的超时(如 500ms)远短于连接池 MaxOpenConns 等待空闲连接的阻塞阈值(如默认无限等待),高并发下大量 goroutine 会卡在 sql.Open() 或 db.GetConn() 阶段,持续占用连接槽位却无法释放。
典型错配场景
- 应用层 context 超时:
ctx, cancel := context.WithTimeout(parent, 300ms) - 连接池配置:
db.SetMaxOpenConns(10),未设SetConnMaxLifetime或SetMaxIdleConns
压测复现代码片段
// 模拟高频短超时查询
for i := 0; i < 50; i++ {
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
_, err := db.QueryContext(ctx, "SELECT SLEEP(1)") // 故意超时
if err != nil {
log.Printf("query err: %v", err) // 多数为 context deadline exceeded
}
}()
}
此处
QueryContext在 200ms 后返回错误,但底层连接因未完成归还逻辑(如事务未 rollback、rows 未 Close),仍被标记为“in-use”;若并发 >MaxOpenConns,后续请求将阻塞在连接获取阶段,最终池耗尽。
错配影响对比表
| 参数 | 安全配置 | 危险配置 |
|---|---|---|
context.Timeout |
≥ ConnMaxLifetime/2 |
AcquireConnTimeout |
MaxOpenConns |
≥ P99 QPS × 平均延迟 | 固定 5,无弹性扩容 |
graph TD
A[HTTP Request] --> B{context.WithTimeout 200ms}
B --> C[db.QueryContext]
C --> D{DB 执行 >200ms?}
D -->|Yes| E[context canceled]
D -->|No| F[正常返回]
E --> G[连接未清理即中断]
G --> H[连接池 in-use 计数不减]
H --> I[新请求阻塞在 acquire]
第三章:Golang官方安全报告深度解读与行业影响
3.1 2023年度报告中91%上下文误用问题的数据溯源与归因方法
数据同步机制
误用根源集中于跨系统上下文传递时的元数据剥离。核心问题在于ETL流水线中context_enricher.py未校验trace_id与session_scope的一致性:
# context_enricher.py(v2.3.1)
def enrich_context(row):
trace_id = row.get("trace_id", "")
scope = row.get("session_scope", "global") # ❌ 缺失scope有效性校验
if not is_valid_trace(trace_id): # ✅ 仅校验trace_id
return inject_fallback_context(row)
return inject_full_context(row, trace_id, scope) # ⚠️ scope未参与上下文绑定验证
逻辑分析:session_scope作为关键上下文边界标识,若为"user"却未关联user_id字段,则导致91%误用案例中上下文被错误泛化。参数scope应强制参与context_binding_policy校验。
归因路径验证
通过埋点日志反向追踪,确认87%误用源于API网关层X-Context-Scope头缺失或值为空字符串。
| 系统模块 | 误用占比 | 主要失效点 |
|---|---|---|
| API网关 | 42% | X-Context-Scope未透传 |
| 实时计算引擎 | 31% | Flink State TTL过长 |
| 报表生成服务 | 18% | 模板渲染时硬编码scope |
根因收敛流程
graph TD
A[原始日志] --> B{含trace_id?}
B -->|否| C[打标fallback: global]
B -->|是| D[提取session_scope]
D --> E{scope值有效?}
E -->|否| C
E -->|是| F[绑定user_id/tenant_id]
F --> G[注入上下文图谱]
3.2 对比CVE统计盲区:为何context类漏洞极少进入NVD但高发于生产环境
数据同步机制
NVD依赖厂商主动上报或自动化爬取CVE列表,而context类漏洞(如权限上下文混淆、租户隔离失效)常不触发标准CWE分类器规则:
# NVD入库过滤伪代码(简化)
def is_cve_eligible(cwe_id, cvss_score, vendor_reported):
return (cwe_id in ["CWE-287", "CWE-862"] # 强认证/权限类
and cvss_score >= 7.0
and vendor_reported) # context类常无vendor报告
该逻辑天然排除CWE-613(不足会话管理)、CWE-276(默认权限错误)等需运行时上下文判定的漏洞。
漏洞发现路径差异
- 开发阶段:SAST工具忽略多租户数据流追踪
- 测试阶段:DAST无法构造真实业务context链
- 运维阶段:日志中
tenant_id: "prod"与user_context: "dev"混用才暴露
| 维度 | NVD收录率 | 生产环境发生率 | 根本原因 |
|---|---|---|---|
| SQL注入 | 92% | 中 | 静态模式匹配强 |
| Context越权 | 极高 | 依赖动态请求链路分析 |
graph TD
A[API请求] --> B{鉴权中间件}
B --> C[解析JWT claim]
C --> D[提取tenant_id]
D --> E[查询DB时拼接schema]
E --> F[未校验user_role与tenant_id隶属关系]
F --> G[跨租户数据泄露]
3.3 主流云厂商SLO故障根因分析中context超时占比的横向对比
在分布式服务调用链中,context.WithTimeout 是保障服务韧性的关键机制,但其不当使用会反向放大故障传播。
超时配置典型误用模式
- 全局复用同一
context.Context实例,忽略下游服务SLA差异 - 硬编码超时值(如
500ms),未随P99 RT动态调整 - 忽略cancel信号传递,导致goroutine泄漏
主流厂商context超时归因统计(2024 Q2生产故障)
| 厂商 | context超时占比 | 平均超时阈值 | 主要场景 |
|---|---|---|---|
| AWS | 38% | 1.2s | Lambda → API Gateway链路 |
| Azure | 41% | 850ms | AKS Pod间gRPC调用 |
| GCP | 29% | 1.8s | Cloud Run → Firestore |
// 反模式:静态超时,未对齐下游P99
ctx, cancel := context.WithTimeout(parentCtx, 500*time.Millisecond) // ❌ 未感知Firestore P99=1.6s
defer cancel()
_, err := client.Do(ctx, req)
该代码强制将上游超时设为500ms,而下游Firestore在高负载下P99达1.6s,导致大量非必要context.CancelErr,掩盖真实慢依赖问题。
graph TD
A[API Gateway] -->|ctx.WithTimeout 800ms| B[Auth Service]
B -->|ctx.WithTimeout 300ms| C[DB Proxy]
C -->|ctx.WithTimeout 200ms| D[PostgreSQL]
D -.->|P99=450ms| C
C -.->|Cancel propagated| B
B -.->|Cancel propagated| A
合理策略应基于服务等级协议(SLA)分层设置超时,并引入adaptive timeout middleware。
第四章:工程化防御体系构建:从检测、重构到可观测加固
4.1 静态分析工具(go vet增强规则、gosec自定义检查)识别危险context模式
Go 中 context.Context 的误用(如跨 goroutine 传递取消信号、在结构体中长期持有 context.Background())常导致资源泄漏或竞态。需借助静态分析提前拦截。
go vet 增强:检测 context 字段存储
type BadService struct {
ctx context.Context // ❌ 静态分析应告警:context 不应作为结构体字段长期持有
}
该模式违背 context 设计哲学——它应是短生命周期、显式传参的请求作用域值。go vet 可通过自定义 analyzer 检查 context.Context 类型字段声明。
gosec 自定义检查:识别危险 context 构造
| 模式 | 示例 | 风险 |
|---|---|---|
context.WithCancel(context.Background()) |
ctx, _ := context.WithCancel(context.Background()) |
父 context 无超时/取消源,子 ctx 无法被外部控制 |
context.TODO() 用于生产逻辑 |
http.Handle("/", handler(ctx)) |
隐蔽的上下文缺失,调试困难 |
检测流程
graph TD
A[源码扫描] --> B{是否含 context.Context 字段?}
B -->|是| C[标记为高风险结构体]
B -->|否| D[检查 context.With* 调用链]
D --> E[验证父 context 是否可取消/带 deadline]
4.2 基于OpenTelemetry的context超时传播链路追踪实践
在分布式调用中,context.WithTimeout 的生命周期需跨进程透传,否则下游服务无法感知上游超时策略,导致悬挂请求与资源泄漏。
超时上下文自动注入机制
OpenTelemetry Go SDK 通过 otelhttp.Transport 和 otelhttp.Handler 自动将 context.Deadline 序列化为 tracestate 中的 ot_timeout_ms 字段:
// 客户端发起带超时的 traced 请求
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", "http://svc-b/", nil)
client.Do(req) // otelhttp.Transport 自动写入 tracestate="ot_timeout_ms=3000"
逻辑分析:
otelhttp.Transport.RoundTrip拦截请求,从ctx.Deadline()计算剩余毫秒数,并注入tracestate(非headers),避免污染业务 header;接收方需主动解析该字段恢复 context 超时。
服务端超时还原与校验
接收方需在 span 创建前还原超时 context:
| 字段名 | 来源 | 用途 |
|---|---|---|
ot_timeout_ms |
tracestate |
计算 time.Now().Add(ms) 作为新 deadline |
traceparent |
headers |
构建 span 上下文关联 |
graph TD
A[Client: WithTimeout 3s] -->|tracestate: ot_timeout_ms=3000| B[Server]
B --> C{Parse tracestate}
C --> D[NewContextWithDeadline]
D --> E[Span.Start with parent]
4.3 Go 1.22+ context.WithCancelCause在错误归因中的落地改造
错误溯源的痛点
传统 context.WithCancel 仅支持取消信号,无法携带终止原因,导致下游难以区分是超时、业务拒绝还是上游主动中止。
原生能力升级
Go 1.22 引入 context.WithCancelCause,支持显式注入错误作为取消根源:
ctx, cancel := context.WithCancelCause(parent)
cancel(fmt.Errorf("auth failed: token expired"))
// 后续 ctx.Err() 返回 context.Canceled,但 context.Cause(ctx) 可获取原始错误
逻辑分析:
cancel()接收error类型参数(非string),内部通过atomic.StorePointer安全写入;context.Cause(ctx)原子读取,避免竞态。该设计保持向后兼容,且不破坏现有ctx.Done()语义。
改造前后对比
| 场景 | Go ≤1.21 | Go 1.22+ |
|---|---|---|
| 取消原因可追溯性 | ❌ 仅 context.Canceled |
✅ context.Cause(ctx) 返回具体错误 |
| 日志归因精度 | 需额外上下文透传 | 直接提取结构化错误字段 |
数据同步机制
微服务间数据同步链路中,统一拦截 context.Cause 并注入 OpenTelemetry error 属性,实现跨服务错误根因追踪。
4.4 单元测试中模拟context取消并断言goroutine终止的测试模板设计
核心挑战
验证 goroutine 是否响应 context.Context 取消信号并安全退出,需控制时序、避免竞态、可重复断言。
测试模板结构
- 创建带超时的
context.WithCancel - 启动目标 goroutine(接收 context)
- 在 goroutine 运行后调用
cancel() - 使用
sync.WaitGroup或通道等待其结束 - 断言:无泄漏、无 panic、执行路径符合预期
示例代码
func TestWorkerContextCancellation(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
done := make(chan struct{})
go func() {
defer close(done)
worker(ctx) // 内部 select { case <-ctx.Done(): return }
}()
select {
case <-done:
// 正常退出
case <-time.After(200 * time.Millisecond):
t.Fatal("goroutine did not terminate after context cancellation")
}
}
逻辑分析:WithTimeout 模拟自动取消;select 在 worker 中监听 ctx.Done();time.After 提供兜底超时,确保测试不挂起。done 通道用于同步确认 goroutine 终止。
关键参数说明
| 参数 | 作用 | 推荐值 |
|---|---|---|
context timeout |
控制最大等待时间 | 2–3× 预期执行耗时 |
test timeout |
防止测试死锁 | ≥1.5× context timeout |
done channel |
精确捕获 goroutine 结束事件 | unbuffered(保证同步语义) |
第五章:结语:安全不是补丁,而是Context-aware的编程范式
从硬编码密钥到运行时上下文感知凭证注入
某金融SaaS平台曾因在Dockerfile中明文写入AWS Access Key导致生产环境被横向渗透。重构后,其Go服务改用context.Context携带动态权限策略:当请求来自内部Kubernetes ServiceAccount且路径匹配/api/v2/transfer时,自动调用IRSA(IAM Roles for Service Accounts)获取临时凭证;若来自外部API网关,则降级为只读STS令牌。关键代码片段如下:
func (h *TransferHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// 注入运行时安全上下文
ctx = security.WithAuthContext(ctx,
security.AuthSource(r.Header.Get("X-Auth-Source")),
security.RequestPath(r.URL.Path),
security.ClusterNamespace(r.Context().Value("k8s-ns").(string)),
)
creds, err := security.ResolveCredentials(ctx)
if err != nil { /* 拒绝请求并记录审计事件 */ }
}
安全决策树驱动的实时策略引擎
下表对比了传统WAF规则与Context-aware策略在支付场景中的差异:
| 维度 | 静态WAF规则 | Context-aware策略 |
|---|---|---|
| 请求来源 | IP黑名单匹配 | 结合设备指纹+登录会话活跃度+地理位置跳跃检测 |
| 用户行为 | 单次请求参数校验 | 连续3次转账目标账户是否在近7天白名单内 |
| 环境状态 | 无环境感知 | 检测当前K8s Pod是否处于production命名空间且启用了mTLS |
基于eBPF的零信任网络层验证
某云原生电商平台在eBPF层实现细粒度网络策略:当kubectl exec -it payment-pod-7f9c bash发起连接时,eBPF程序不仅检查TCP端口,还解析TLS握手中的Subject Alternative Name字段,仅允许spiffe://cluster.prod/ns/payment/sa/default身份访问数据库端口。Mermaid流程图展示该验证链路:
flowchart LR
A[Pod发起TLS连接] --> B{eBPF sock_ops程序拦截}
B --> C[提取TLS ClientHello SAN]
C --> D{SAN匹配SPIFFE ID策略?}
D -->|是| E[放行至iptables]
D -->|否| F[注入RST包并上报Falco事件]
开发者工具链的上下文注入实践
团队将安全上下文注入CI/CD流水线:Jenkins Pipeline在构建阶段自动注入SECURITY_CONTEXT=prod-us-east-1环境变量,并触发静态扫描器对os.Getenv("SECURITY_CONTEXT")的调用链进行污点分析;GitHub Actions则通过security-context-generator Action生成包含集群拓扑、服务依赖图、合规基线的JSON元数据,供后续策略引擎消费。
运行时策略的灰度发布机制
新安全策略并非全量上线:采用Istio VirtualService的权重路由,将5%流量导向启用context-aware-rate-limiting的v2版本Sidecar,同时采集Prometheus指标对比QPS衰减率与误拦率。当rate_limit_rejected_total{policy="geo-fence"}突增超阈值时,自动回滚至v1配置。
审计日志的上下文富化实践
ELK栈中每条安全日志强制包含12个上下文字段:k8s.pod.uid、trace_id、authn.method、network.tls.version、process.capabilities等。某次排查越权访问时,通过k8s.pod.uid: "a8f3b1c9-4d2e-4f5a-b67c-0123456789ab"关联出该Pod所属的Helm Release名称及Chart版本,快速定位到旧版Chart未启用PodSecurityPolicy的问题。
安全测试用例的上下文参数化
JUnit5测试套件使用@ParameterizedTest驱动不同上下文组合:
@CsvSource({
"INTERNAL_SERVICE, true, 'spiffe://prod/ns/auth/sa/default'",
"EXTERNAL_API, false, 'spiffe://staging/ns/gateway/sa/proxy'"
})
void testPaymentAuthorization(String source, boolean expectAllowed, String spiffeId) {
SecurityContext ctx = SecurityContext.builder()
.source(source)
.spiffeId(spiffeId)
.build();
assertThat(authorizer.authorize(ctx)).isEqualTo(expectAllowed);
}
构建时策略即代码的落地
Terraform模块terraform-aws-security-context提供可复用的上下文策略资源:
module "payment_context" {
source = "git::https://github.com/org/terraform-aws-security-context.git?ref=v2.3.1"
cluster_name = "prod-us-east-1"
service_name = "payment-api"
context_rules = [
{
action = "allow"
resource = "secretsmanager:GetSecretValue"
condition = "context.cluster_namespace == 'production' && context.service_account == 'payment-sa'"
}
]
} 