Skip to content

Commit ccb0a61

Browse files
committed
bring back bed rock auth
Signed-off-by: Yuval Kohavi <[email protected]>
1 parent 636f2ec commit ccb0a61

File tree

6 files changed

+165
-6
lines changed

6 files changed

+165
-6
lines changed

api/v1alpha1/agentgateway/agentgateway_backend_types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package agentgateway
22

33
import (
4+
corev1 "k8s.io/api/core/v1"
45
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
56
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
67
)
@@ -288,6 +289,20 @@ type BedrockConfig struct {
288289
// If not specified, the AWS Guardrail policy will not be used.
289290
// +optional
290291
Guardrail *AWSGuardrailConfig `json:"guardrail,omitempty"`
292+
293+
// Auth specifies an explicit AWS authentication method for the backend.
294+
// When omitted, we will try to use the default AWS SDK authentication methods.
295+
//
296+
// +optional
297+
Auth *AwsAuth `json:"auth,omitempty"`
298+
}
299+
300+
// AwsAuth specifies the authentication method to use for the backend.
301+
type AwsAuth struct {
302+
// SecretRef references a Kubernetes Secret containing the AWS credentials.
303+
// The Secret must have keys "accessKey", "secretKey", and optionally "sessionToken".
304+
// +required
305+
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
291306
}
292307

293308
type AWSGuardrailConfig struct {

pkg/kgateway/agentgatewaysyncer/backend/testdata/Bedrock_backend_with_new_route_types_(responses_and_anthropic_token_count).yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ inlinePolicies:
1212
/v1/messages/count_tokens: ANTHROPIC_TOKEN_COUNT
1313
/v1/models: MODELS
1414
/v1/responses: RESPONSES
15+
- auth:
16+
aws:
17+
implicit: {}
1518
key: test-ns/bedrock-with-new-routes
1619
name:
1720
name: bedrock-with-new-routes
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
ai:
2+
providerGroups:
3+
- providers:
4+
- bedrock:
5+
region: us-east-1
6+
name: backend
7+
inlinePolicies:
8+
- auth:
9+
aws:
10+
explicitConfig:
11+
accessKeyId: secret-accessKey
12+
region: us-east-1
13+
secretAccessKey: secret-secretKey
14+
sessionToken: secret-sessionToken
15+
key: test-ns/bedrock-with-secret-ref
16+
name:
17+
name: bedrock-with-secret-ref
18+
namespace: test-ns

pkg/kgateway/agentgatewaysyncer/backend/testdata/Valid_Bedrock_backend_with_custom_region_and_guardrail.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ ai:
77
model: anthropic.claude-3-haiku-20240307-v1:0
88
region: eu-west-1
99
name: backend
10+
inlinePolicies:
11+
- auth:
12+
aws:
13+
implicit: {}
1014
key: test-ns/bedrock-backend-custom
1115
name:
1216
name: bedrock-backend-custom

pkg/kgateway/agentgatewaysyncer/backend/translate.go

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/agentgateway/agentgateway/go/api"
88
"istio.io/istio/pkg/kube/krt"
99
"istio.io/istio/pkg/ptr"
10+
1011
corev1 "k8s.io/api/core/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
"k8s.io/apimachinery/pkg/labels"
@@ -15,6 +16,7 @@ import (
1516
"github.com/kgateway-dev/kgateway/v2/api/v1alpha1/agentgateway"
1617
"github.com/kgateway-dev/kgateway/v2/pkg/agentgateway/plugins"
1718
"github.com/kgateway-dev/kgateway/v2/pkg/agentgateway/utils"
19+
"github.com/kgateway-dev/kgateway/v2/pkg/kgateway/wellknown"
1820
"github.com/kgateway-dev/kgateway/v2/pkg/logging"
1921
"github.com/kgateway-dev/kgateway/v2/pkg/utils/kubeutils"
2022
)
@@ -224,11 +226,17 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
224226

225227
aiBackend := &api.AIBackend{}
226228
if llm := ai.LLM; llm != nil {
227-
provider, err := translateLLMProvider(llm, utils.SingularLLMProviderSubBackendName)
229+
provider, auth, err := translateLLMProvider(ctx, llm, utils.SingularLLMProviderSubBackendName, be.Namespace)
228230
if err != nil {
229231
return nil, fmt.Errorf("failed to translate LLM provider: %w", err)
230232
}
231-
233+
if auth != nil {
234+
inlinePolicies = append(inlinePolicies, &api.BackendPolicySpec{
235+
Kind: &api.BackendPolicySpec_Auth{
236+
Auth: auth,
237+
},
238+
})
239+
}
232240
aiBackend.ProviderGroups = []*api.AIBackend_ProviderGroup{{
233241
Providers: []*api.AIBackend_Provider{provider},
234242
}}
@@ -237,7 +245,7 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
237245
providerGroup := &api.AIBackend_ProviderGroup{}
238246

239247
for _, provider := range group.Providers {
240-
tp, err := translateLLMProvider(&provider.LLMProvider, string(provider.Name))
248+
tp, auth, err := translateLLMProvider(ctx, &provider.LLMProvider, string(provider.Name), be.Namespace)
241249
if err != nil {
242250
return nil, fmt.Errorf("failed to translate LLM provider: %w", err)
243251
}
@@ -247,6 +255,13 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
247255
logger.Warn("failed to translate AI backend policies", "err", err)
248256
}
249257
tp.InlinePolicies = pol
258+
if auth != nil {
259+
tp.InlinePolicies = append(tp.InlinePolicies, &api.BackendPolicySpec{
260+
Kind: &api.BackendPolicySpec_Auth{
261+
Auth: auth,
262+
},
263+
})
264+
}
250265

251266
providerGroup.Providers = append(providerGroup.Providers, tp)
252267
}
@@ -303,7 +318,7 @@ func translateAIBackendPolicies(
303318
})
304319
}
305320

306-
func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*api.AIBackend_Provider, error) {
321+
func translateLLMProvider(ctx plugins.PolicyCtx, llm *agentgateway.LLMProvider, providerName, namespace string) (*api.AIBackend_Provider, *api.BackendAuthPolicy, error) {
307322
provider := &api.AIBackend_Provider{
308323
Name: providerName,
309324
}
@@ -318,6 +333,7 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
318333
if llm.Path != "" {
319334
provider.PathOverride = &llm.Path
320335
}
336+
var auth *api.BackendAuthPolicy
321337

322338
// Extract auth token and model based on provider
323339
if llm.OpenAI != nil {
@@ -363,6 +379,12 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
363379
guardrailVersion = &llm.Bedrock.Guardrail.GuardrailVersion
364380
}
365381

382+
var err error
383+
auth, err = buildBedrockAuthPolicy(ctx.Krt, region, llm.Bedrock.Auth, ctx.Collections.Secrets, namespace)
384+
if err != nil {
385+
return nil, nil, err
386+
}
387+
366388
provider.Provider = &api.AIBackend_Provider_Bedrock{
367389
Bedrock: &api.AIBackend_Bedrock{
368390
Model: llm.Bedrock.Model,
@@ -372,10 +394,10 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
372394
},
373395
}
374396
} else {
375-
return nil, fmt.Errorf("no supported LLM provider configured")
397+
return nil, nil, fmt.Errorf("no supported LLM provider configured")
376398
}
377399

378-
return provider, nil
400+
return provider, auth, nil
379401
}
380402

381403
func toMCPProtocol(appProtocol string) api.MCPTarget_Protocol {
@@ -391,3 +413,70 @@ func toMCPProtocol(appProtocol string) api.MCPTarget_Protocol {
391413
return api.MCPTarget_UNDEFINED
392414
}
393415
}
416+
417+
func buildBedrockAuthPolicy(krtctx krt.HandlerContext, region string, auth *agentgateway.AwsAuth, secrets krt.Collection[*corev1.Secret], namespace string) (*api.BackendAuthPolicy, error) {
418+
var errs []error
419+
if auth == nil {
420+
logger.Warn("using implicit AWS auth for AI backend")
421+
return &api.BackendAuthPolicy{
422+
Kind: &api.BackendAuthPolicy_Aws{
423+
Aws: &api.Aws{
424+
Kind: &api.Aws_Implicit{
425+
Implicit: &api.AwsImplicit{},
426+
},
427+
},
428+
},
429+
}, nil
430+
}
431+
432+
if auth.SecretRef == nil {
433+
logger.Warn("not using any auth for AWS - it's most likely not what you want")
434+
return nil, nil
435+
}
436+
437+
// Get secret using the SecretIndex
438+
secret, err := kubeutils.GetSecret(secrets, krtctx, auth.SecretRef.Name, namespace)
439+
if err != nil {
440+
// Return nil auth policy if secret not found - this will be handled upstream
441+
// TODO(npolshak): Add backend status errors https://github.com/kgateway-dev/kgateway/issues/11966
442+
return nil, err
443+
}
444+
445+
var accessKeyId, secretAccessKey string
446+
var sessionToken *string
447+
448+
// Extract access key
449+
if value, exists := kubeutils.GetSecretValue(secret, wellknown.AccessKey); !exists {
450+
errs = append(errs, errors.New("accessKey is missing or not a valid string"))
451+
} else {
452+
accessKeyId = value
453+
}
454+
455+
// Extract secret key
456+
if value, exists := kubeutils.GetSecretValue(secret, wellknown.SecretKey); !exists {
457+
errs = append(errs, errors.New("secretKey is missing or not a valid string"))
458+
} else {
459+
secretAccessKey = value
460+
}
461+
462+
// Extract session token (optional)
463+
if value, exists := kubeutils.GetSecretValue(secret, wellknown.SessionToken); exists {
464+
sessionToken = ptr.Of(value)
465+
}
466+
467+
return &api.BackendAuthPolicy{
468+
Kind: &api.BackendAuthPolicy_Aws{
469+
Aws: &api.Aws{
470+
Kind: &api.Aws_ExplicitConfig{
471+
ExplicitConfig: &api.AwsExplicitConfig{
472+
AccessKeyId: accessKeyId,
473+
SecretAccessKey: secretAccessKey,
474+
SessionToken: sessionToken,
475+
Region: region,
476+
},
477+
},
478+
},
479+
},
480+
}, errors.Join(errs...)
481+
482+
}

pkg/kgateway/agentgatewaysyncer/backend/translate_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,36 @@ func TestBuildAIBackend(t *testing.T) {
491491
},
492492
},
493493
},
494+
{
495+
name: "Bedrock backend with new secret ref",
496+
backend: &agentgateway.AgentgatewayBackend{
497+
ObjectMeta: metav1.ObjectMeta{
498+
Name: "bedrock-with-secret-ref",
499+
Namespace: "test-ns",
500+
},
501+
Spec: agentgateway.AgentgatewayBackendSpec{
502+
AI: &agentgateway.AIBackend{
503+
LLM: &agentgateway.LLMProvider{
504+
Bedrock: &agentgateway.BedrockConfig{
505+
Region: "us-east-1",
506+
Auth: &agentgateway.AwsAuth{
507+
SecretRef: &corev1.LocalObjectReference{
508+
Name: "bedrock-secret",
509+
},
510+
},
511+
},
512+
},
513+
},
514+
},
515+
},
516+
inputs: []any{
517+
createMockSecret("test-ns", "bedrock-secret", map[string]string{
518+
"accessKey": "secret-accessKey",
519+
"secretKey": "secret-secretKey",
520+
"sessionToken": "secret-sessionToken",
521+
}),
522+
},
523+
},
494524
}
495525
for _, tt := range tests {
496526
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)