github.com/qiniu/go-sdk/v7@v7.27.0/examples/sandbox_request_injections/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "os" 8 "strings" 9 10 "github.com/qiniu/go-sdk/v7/sandbox" 11 ) 12 13 func main() { 14 // 确保设置了环境变量 QINIU_API_KEY 15 apiKey := os.Getenv("QINIU_API_KEY") 16 if apiKey == "" { 17 log.Fatal("请设置 QINIU_API_KEY 环境变量") 18 } 19 20 apiURL := os.Getenv("QINIU_SANDBOX_API_URL") 21 22 ctx := context.Background() 23 24 // 初始化客户端 25 config := &sandbox.Config{ 26 APIKey: apiKey, 27 Endpoint: apiURL, 28 } 29 client, err := sandbox.NewClient(config) 30 if err != nil { 31 log.Fatalf("Failed to create client: %v", err) 32 } 33 if err := run(ctx, client); err != nil { 34 log.Fatal(err) 35 } 36 } 37 38 func run(ctx context.Context, client *sandbox.Client) error { 39 log.Println("Creating sandbox with request injections...") 40 41 // 设置针对特定域名的请求注入规则 42 // 这个规则告诉底层网络架构,当请求向 "httpbingo.org" 发起时, 43 // 如果它是 HTTPS,拦截掉该请求并将 header 中的 "Authorization" 44 // 重写为 "Bearer real_xxx" 45 headers := map[string]string{ 46 "Authorization": "Bearer real_xxx", 47 } 48 ifHeaders := map[string]string{ 49 "X-Injection-Scope": "demo", 50 } 51 ifQueries := map[string]string{ 52 "inject": "true", 53 } 54 55 // 准备创建参数(HTTP 自定义注入)。如果设置了 QINIU_GITHUB_TOKEN, 56 // 同时演示 GitHub 凭证注入和运行时 token 更新。 57 injections := []sandbox.SandboxInjectionSpec{ 58 { 59 HTTP: &sandbox.HTTPInjection{ 60 BaseURL: "https://httpbingo.org", 61 Headers: &headers, 62 IfHeaders: &ifHeaders, 63 IfQueries: &ifQueries, 64 }, 65 }, 66 } 67 githubToken := os.Getenv("QINIU_GITHUB_TOKEN") 68 if githubToken != "" { 69 injections = append(injections, sandbox.SandboxInjectionSpec{ 70 Github: &sandbox.GithubInjection{Token: &githubToken}, 71 }) 72 } 73 74 params := sandbox.CreateParams{ 75 TemplateID: "base", 76 Injections: &injections, 77 } 78 79 // 也可以使用已知 API 协议的快捷注入(OpenAI、Anthropic、Gemini) 80 // openaiKey := "sk-real-openai-key" 81 // params := sandbox.CreateParams{ 82 // TemplateID: "base", 83 // Injections: &[]sandbox.SandboxInjectionSpec{ 84 // { 85 // OpenAI: &sandbox.OpenAIInjection{ 86 // APIKey: &openaiKey, 87 // // BaseURL 可选,默认 api.openai.com 88 // }, 89 // }, 90 // }, 91 // } 92 93 // 也可以通过注入规则 ID 引用已保存的注入规则 94 // injectionRuleID := "<injection-rule-id>" 95 // params := sandbox.CreateParams{ 96 // TemplateID: "base", 97 // Injections: &[]sandbox.SandboxInjectionSpec{ 98 // {ByID: &injectionRuleID}, 99 // }, 100 // } 101 102 // 创建并等待沙箱就绪 103 sb, info, err := client.CreateAndWait(ctx, params) 104 if err != nil { 105 return fmt.Errorf("failed to create sandbox: %w", err) 106 } 107 defer func() { 108 log.Println("Killing sandbox...") 109 if err := sb.Kill(ctx); err != nil { 110 log.Printf("Failed to kill sandbox: %v", err) 111 } 112 }() 113 114 log.Printf("Sandbox created successfully! ID: %s, State: %s\n", sb.ID(), info.State) 115 116 // 查询当前运行时注入。敏感字段由服务端脱敏,返回值不能直接用于更新。 117 current, err := sb.GetInjections(ctx) 118 if err != nil { 119 return fmt.Errorf("failed to get runtime injections: %w", err) 120 } 121 logMaskedInjections("Current runtime injections", current) 122 123 // 在沙箱内执行使用假 APIKEY 的请求 124 // 沙箱内的程序完全感觉不到外部注入;只有请求同时满足 IfHeaders / IfQueries 125 // 匹配条件时,外层才会拦截并替换为真实的 Key。 126 fakeTokenCmd := `curl -sSL -X GET "https://httpbingo.org/bearer?inject=true" -H "accept: application/json" -H "Authorization: Bearer fake_xxx" -H "X-Injection-Scope: demo"` 127 if err := runBearerCheck(ctx, sb, fakeTokenCmd, "real_xxx"); err != nil { 128 return err 129 } 130 131 // 替换运行中沙箱的全部注入规则。更新接口需要重新提供真实敏感值。 132 updatedHeaders := map[string]string{ 133 "Authorization": "Bearer updated_xxx", 134 } 135 updatedInjections := []sandbox.SandboxInjectionSpec{ 136 { 137 HTTP: &sandbox.HTTPInjection{ 138 BaseURL: "https://httpbingo.org", 139 Headers: &updatedHeaders, 140 IfHeaders: &ifHeaders, 141 IfQueries: &ifQueries, 142 }, 143 }, 144 } 145 if githubToken != "" { 146 updatedInjections = append(updatedInjections, sandbox.SandboxInjectionSpec{ 147 Github: &sandbox.GithubInjection{Token: &githubToken}, 148 }) 149 } 150 if err := sb.UpdateInjections(ctx, updatedInjections); err != nil { 151 return fmt.Errorf("failed to update runtime injections: %w", err) 152 } 153 current, err = sb.GetInjections(ctx) 154 if err != nil { 155 return fmt.Errorf("failed to get updated runtime injections: %w", err) 156 } 157 logMaskedInjections("Updated runtime injections", current) 158 if err := runBearerCheck(ctx, sb, fakeTokenCmd, "updated_xxx"); err != nil { 159 return err 160 } 161 162 // 已配置 GitHub 注入时,可独立轮换 GitHub token,无需替换其他注入规则。 163 if updatedGitHubToken := os.Getenv("QINIU_GITHUB_TOKEN_UPDATED"); updatedGitHubToken != "" { 164 if githubToken == "" { 165 return fmt.Errorf("设置 QINIU_GITHUB_TOKEN_UPDATED 时也必须设置 QINIU_GITHUB_TOKEN") 166 } 167 if err := sb.UpdateGitHubToken(ctx, updatedGitHubToken); err != nil { 168 return fmt.Errorf("failed to update GitHub token: %w", err) 169 } 170 log.Println("GitHub token updated successfully") 171 } 172 return nil 173 } 174 175 func runBearerCheck(ctx context.Context, sb *sandbox.Sandbox, command, expectedToken string) error { 176 log.Printf("Executing command in sandbox:\n$ %s\n", command) 177 result, err := sb.Commands().Run(ctx, command) 178 if err != nil { 179 return fmt.Errorf("failed to run command: %w", err) 180 } 181 if result.ExitCode != 0 { 182 return fmt.Errorf("command failed with exit code %d: %s", result.ExitCode, result.Stderr) 183 } 184 if !strings.Contains(result.Stdout, `"token": "`+expectedToken+`"`) { 185 return fmt.Errorf("request injection did not replace the bearer token, stdout: %s", result.Stdout) 186 } 187 log.Printf("Request injection verified with token %q", expectedToken) 188 return nil 189 } 190 191 func logMaskedInjections(label string, injections []sandbox.MaskedSandboxInjection) { 192 log.Printf("%s (%d):", label, len(injections)) 193 for i, injection := range injections { 194 switch { 195 case injection.ByID != nil: 196 log.Printf(" %d. rule ID: %s", i+1, *injection.ByID) 197 case injection.OpenAI != nil: 198 log.Printf(" %d. OpenAI injection", i+1) 199 case injection.Anthropic != nil: 200 log.Printf(" %d. Anthropic injection", i+1) 201 case injection.Gemini != nil: 202 log.Printf(" %d. Gemini injection", i+1) 203 case injection.Qiniu != nil: 204 log.Printf(" %d. Qiniu injection", i+1) 205 case injection.Github != nil: 206 log.Printf(" %d. GitHub injection", i+1) 207 case injection.HTTP != nil: 208 log.Printf(" %d. HTTP injection for %s", i+1, injection.HTTP.BaseURL) 209 default: 210 log.Printf(" %d. unknown injection", i+1) 211 } 212 } 213 }