fix(bedrock): authorize runtime/agent actions under the bedrock: IAM namespace (#820)

The gateway derived each request's IAM action prefix from the wire-service
name, so bedrock-runtime, bedrock-agent and bedrock-agent-runtime calls were
checked as bedrock-runtime:ConverseStream, bedrock-agent-runtime:Retrieve, etc.
AWS namespaces every Bedrock action under bedrock:, so an AWS-shaped scoped
policy never matched and only a wildcard admitted the call.

Collapse the Bedrock family to the shared bedrock: namespace in IAMAction, the
single choke point for policy decisions; recordResolvedAction keeps the wire
name for telemetry.
This commit is contained in:
Josh Siviour
2026-08-18 14:21:38 +10:00
committed by GitHub
parent 703f72bb82
commit bce832c63b
2 changed files with 51 additions and 3 deletions
+24 -3
View File
@@ -1,6 +1,27 @@
package policy
// IAMAction formats a service and action as the IAM policy string "service:ActionName".
func IAMAction(service, action string) string {
return service + ":" + action
// bedrockIAMFamily lists the Bedrock wire/signing service names that all
// authorize under the single "bedrock:" IAM namespace in AWS (runtime, agent,
// and agent-runtime actions are namespaced as bedrock: on AWS).
var bedrockIAMFamily = map[string]bool{
"bedrock-runtime": true,
"bedrock-agent": true,
"bedrock-agent-runtime": true,
}
// iamNamespace maps a wire service name to its IAM policy namespace, collapsing
// the Bedrock family to the shared "bedrock:" namespace. Every other service
// keeps its own name.
func iamNamespace(service string) string {
if bedrockIAMFamily[service] {
return "bedrock"
}
return service
}
// IAMAction formats a service and action as the IAM policy string
// "namespace:ActionName", collapsing the Bedrock family to the shared bedrock:
// namespace so AWS-shaped policies authorize as they do on AWS.
func IAMAction(service, action string) string {
return iamNamespace(service) + ":" + action
}
+27
View File
@@ -0,0 +1,27 @@
package policy
import "testing"
// TestIAMAction_BedrockNamespace asserts the Bedrock wire-service family
// collapses to the shared "bedrock:" IAM namespace, while other services keep
// their own prefix, so AWS-shaped policies authorize as they do on AWS.
func TestIAMAction_BedrockNamespace(t *testing.T) {
cases := []struct {
service, action, want string
}{
{"bedrock", "Converse", "bedrock:Converse"},
{"bedrock-runtime", "ConverseStream", "bedrock:ConverseStream"},
{"bedrock-runtime", "InvokeModel", "bedrock:InvokeModel"},
{"bedrock-agent", "CreateKnowledgeBase", "bedrock:CreateKnowledgeBase"},
{"bedrock-agent-runtime", "Retrieve", "bedrock:Retrieve"},
{"bedrock-agent-runtime", "RetrieveAndGenerate", "bedrock:RetrieveAndGenerate"},
{"ecr", "GetAuthorizationToken", "ecr:GetAuthorizationToken"},
{"ecs", "RunTask", "ecs:RunTask"},
{"s3", "GetObject", "s3:GetObject"},
}
for _, c := range cases {
if got := IAMAction(c.service, c.action); got != c.want {
t.Errorf("IAMAction(%q, %q) = %q, want %q", c.service, c.action, got, c.want)
}
}
}