Skip to content

Commit 3177084

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

File tree

8 files changed

+237
-6
lines changed

8 files changed

+237
-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 {

api/v1alpha1/agentgateway/zz_generated.deepcopy.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

install/helm/kgateway-crds/templates/agentgateway.dev_agentgatewaybackends.yaml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,30 @@ spec:
137137
bedrock:
138138
description: Bedrock provider
139139
properties:
140+
auth:
141+
description: |-
142+
Auth specifies an explicit AWS authentication method for the backend.
143+
When omitted, we will try to use the default AWS SDK authentication methods.
144+
properties:
145+
secretRef:
146+
description: |-
147+
SecretRef references a Kubernetes Secret containing the AWS credentials.
148+
The Secret must have keys "accessKey", "secretKey", and optionally "sessionToken".
149+
properties:
150+
name:
151+
default: ""
152+
description: |-
153+
Name of the referent.
154+
This field is effectively required, but due to backwards compatibility is
155+
allowed to be empty. Instances of this type with an empty value here are
156+
almost certainly wrong.
157+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
158+
type: string
159+
type: object
160+
x-kubernetes-map-type: atomic
161+
required:
162+
- secretRef
163+
type: object
140164
guardrail:
141165
description: |-
142166
Guardrail configures the Guardrail policy to use for the backend. See <https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html>
@@ -1545,6 +1569,30 @@ spec:
15451569
bedrock:
15461570
description: Bedrock provider
15471571
properties:
1572+
auth:
1573+
description: |-
1574+
Auth specifies an explicit AWS authentication method for the backend.
1575+
When omitted, we will try to use the default AWS SDK authentication methods.
1576+
properties:
1577+
secretRef:
1578+
description: |-
1579+
SecretRef references a Kubernetes Secret containing the AWS credentials.
1580+
The Secret must have keys "accessKey", "secretKey", and optionally "sessionToken".
1581+
properties:
1582+
name:
1583+
default: ""
1584+
description: |-
1585+
Name of the referent.
1586+
This field is effectively required, but due to backwards compatibility is
1587+
allowed to be empty. Instances of this type with an empty value here are
1588+
almost certainly wrong.
1589+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
1590+
type: string
1591+
type: object
1592+
x-kubernetes-map-type: atomic
1593+
required:
1594+
- secretRef
1595+
type: object
15481596
guardrail:
15491597
description: |-
15501598
Guardrail configures the Guardrail policy to use for the backend. See <https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html>

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: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/kgateway-dev/kgateway/v2/api/v1alpha1/agentgateway"
1616
"github.com/kgateway-dev/kgateway/v2/pkg/agentgateway/plugins"
1717
"github.com/kgateway-dev/kgateway/v2/pkg/agentgateway/utils"
18+
"github.com/kgateway-dev/kgateway/v2/pkg/kgateway/wellknown"
1819
"github.com/kgateway-dev/kgateway/v2/pkg/logging"
1920
"github.com/kgateway-dev/kgateway/v2/pkg/utils/kubeutils"
2021
)
@@ -224,11 +225,17 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
224225

225226
aiBackend := &api.AIBackend{}
226227
if llm := ai.LLM; llm != nil {
227-
provider, err := translateLLMProvider(llm, utils.SingularLLMProviderSubBackendName)
228+
provider, auth, err := translateLLMProvider(ctx, llm, utils.SingularLLMProviderSubBackendName, be.Namespace)
228229
if err != nil {
229230
return nil, fmt.Errorf("failed to translate LLM provider: %w", err)
230231
}
231-
232+
if auth != nil {
233+
inlinePolicies = append(inlinePolicies, &api.BackendPolicySpec{
234+
Kind: &api.BackendPolicySpec_Auth{
235+
Auth: auth,
236+
},
237+
})
238+
}
232239
aiBackend.ProviderGroups = []*api.AIBackend_ProviderGroup{{
233240
Providers: []*api.AIBackend_Provider{provider},
234241
}}
@@ -237,7 +244,7 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
237244
providerGroup := &api.AIBackend_ProviderGroup{}
238245

239246
for _, provider := range group.Providers {
240-
tp, err := translateLLMProvider(&provider.LLMProvider, string(provider.Name))
247+
tp, auth, err := translateLLMProvider(ctx, &provider.LLMProvider, string(provider.Name), be.Namespace)
241248
if err != nil {
242249
return nil, fmt.Errorf("failed to translate LLM provider: %w", err)
243250
}
@@ -247,6 +254,13 @@ func translateAIBackends(ctx plugins.PolicyCtx, be *agentgateway.AgentgatewayBac
247254
logger.Warn("failed to translate AI backend policies", "err", err)
248255
}
249256
tp.InlinePolicies = pol
257+
if auth != nil {
258+
tp.InlinePolicies = append(tp.InlinePolicies, &api.BackendPolicySpec{
259+
Kind: &api.BackendPolicySpec_Auth{
260+
Auth: auth,
261+
},
262+
})
263+
}
250264

251265
providerGroup.Providers = append(providerGroup.Providers, tp)
252266
}
@@ -303,7 +317,7 @@ func translateAIBackendPolicies(
303317
})
304318
}
305319

306-
func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*api.AIBackend_Provider, error) {
320+
func translateLLMProvider(ctx plugins.PolicyCtx, llm *agentgateway.LLMProvider, providerName, namespace string) (*api.AIBackend_Provider, *api.BackendAuthPolicy, error) {
307321
provider := &api.AIBackend_Provider{
308322
Name: providerName,
309323
}
@@ -318,6 +332,7 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
318332
if llm.Path != "" {
319333
provider.PathOverride = &llm.Path
320334
}
335+
var auth *api.BackendAuthPolicy
321336

322337
// Extract auth token and model based on provider
323338
if llm.OpenAI != nil {
@@ -363,6 +378,12 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
363378
guardrailVersion = &llm.Bedrock.Guardrail.GuardrailVersion
364379
}
365380

381+
var err error
382+
auth, err = buildBedrockAuthPolicy(ctx.Krt, region, llm.Bedrock.Auth, ctx.Collections.Secrets, namespace)
383+
if err != nil {
384+
return nil, nil, err
385+
}
386+
366387
provider.Provider = &api.AIBackend_Provider_Bedrock{
367388
Bedrock: &api.AIBackend_Bedrock{
368389
Model: llm.Bedrock.Model,
@@ -372,10 +393,10 @@ func translateLLMProvider(llm *agentgateway.LLMProvider, providerName string) (*
372393
},
373394
}
374395
} else {
375-
return nil, fmt.Errorf("no supported LLM provider configured")
396+
return nil, nil, fmt.Errorf("no supported LLM provider configured")
376397
}
377398

378-
return provider, nil
399+
return provider, auth, nil
379400
}
380401

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

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)