diff --git a/lib/backend/dynamo/dynamodbbk.go b/lib/backend/dynamo/dynamodbbk.go
index 0cb6636b384a1..dd2c445e1680c 100644
--- a/lib/backend/dynamo/dynamodbbk.go
+++ b/lib/backend/dynamo/dynamodbbk.go
@@ -47,9 +47,9 @@ import (
"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/defaults"
- "github.com/gravitational/teleport/lib/modules"
awsmetrics "github.com/gravitational/teleport/lib/observability/metrics/aws"
dynamometrics "github.com/gravitational/teleport/lib/observability/metrics/dynamo"
+ awsutils "github.com/gravitational/teleport/lib/utils/aws"
"github.com/gravitational/teleport/lib/utils/aws/endpoint"
)
@@ -293,7 +293,7 @@ func New(ctx context.Context, params backend.Params) (*Backend, error) {
// FIPS settings are applied on the individual service instead of the aws config,
// as DynamoDB Streams and Application Auto Scaling do not yet have FIPS endpoints in non-GovCloud.
// See also: https://aws.amazon.com/compliance/fips/#FIPS_Endpoints_by_Service
- if modules.GetModules().IsBoringBinary() {
+ if awsutils.UseFIPSForDynamoDB() {
dynamoOpts = append(dynamoOpts, func(o *dynamodb.Options) {
o.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled
})
diff --git a/lib/events/dynamoevents/dynamoevents.go b/lib/events/dynamoevents/dynamoevents.go
index 68d411f2796a3..077f029985ec3 100644
--- a/lib/events/dynamoevents/dynamoevents.go
+++ b/lib/events/dynamoevents/dynamoevents.go
@@ -59,10 +59,10 @@ import (
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
- "github.com/gravitational/teleport/lib/modules"
awsmetrics "github.com/gravitational/teleport/lib/observability/metrics/aws"
dynamometrics "github.com/gravitational/teleport/lib/observability/metrics/dynamo"
"github.com/gravitational/teleport/lib/utils"
+ awsutils "github.com/gravitational/teleport/lib/utils/aws"
"github.com/gravitational/teleport/lib/utils/aws/endpoint"
)
@@ -330,7 +330,7 @@ func New(ctx context.Context, cfg Config) (*Log, error) {
// FIPS settings are applied on the individual service instead of the aws config,
// as DynamoDB Streams and Application Auto Scaling do not yet have FIPS endpoints in non-GovCloud.
// See also: https://aws.amazon.com/compliance/fips/#FIPS_Endpoints_by_Service
- if modules.GetModules().IsBoringBinary() && cfg.UseFIPSEndpoint == types.ClusterAuditConfigSpecV2_FIPS_ENABLED {
+ if awsutils.UseFIPSForDynamoDB() && cfg.UseFIPSEndpoint == types.ClusterAuditConfigSpecV2_FIPS_ENABLED {
dynamoOpts = append(dynamoOpts, func(o *dynamodb.Options) {
o.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled
})
diff --git a/lib/events/dynamoevents/dynamoevents_test.go b/lib/events/dynamoevents/dynamoevents_test.go
index 28eb81c1f4653..87c86819b41cb 100644
--- a/lib/events/dynamoevents/dynamoevents_test.go
+++ b/lib/events/dynamoevents/dynamoevents_test.go
@@ -48,6 +48,7 @@ import (
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/utils"
+ awsutils "github.com/gravitational/teleport/lib/utils/aws"
)
const dynamoDBLargeQueryRetries int = 10
@@ -608,12 +609,23 @@ func randStringAlpha(n int) string {
func TestEndpoints(t *testing.T) {
tests := []struct {
- name string
- fips bool
+ name string
+ fips bool
+ env map[string]string
+ wantFIPSError bool
}{
{
- name: "fips",
+ name: "fips",
+ fips: true,
+ wantFIPSError: true,
+ },
+ {
+ name: "fips with env skip",
fips: true,
+ env: map[string]string{
+ awsutils.EnvVarDisableDynamoDBFIPS: "yes",
+ },
+ wantFIPSError: false,
},
{
name: "without fips",
@@ -631,6 +643,10 @@ func TestEndpoints(t *testing.T) {
})
}
+ for k, v := range tt.env {
+ t.Setenv(k, v)
+ }
+
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTeapot)
@@ -655,15 +671,13 @@ func TestEndpoints(t *testing.T) {
})
// FIPS mode should fail because it is a violation to enable FIPS
// while also setting a custom endpoint.
- if tt.fips {
- assert.Error(t, err)
- require.ErrorContains(t, err, "FIPS")
+ if tt.wantFIPSError {
+ assert.ErrorContains(t, err, "FIPS")
return
}
- assert.Error(t, err)
- assert.Nil(t, b)
- require.ErrorContains(t, err, fmt.Sprintf("StatusCode: %d", http.StatusTeapot))
+ assert.ErrorContains(t, err, fmt.Sprintf("StatusCode: %d", http.StatusTeapot))
+ assert.Nil(t, b, "backend not nil")
})
}
}
diff --git a/lib/utils/aws/dynamo_fips.go b/lib/utils/aws/dynamo_fips.go
new file mode 100644
index 0000000000000..63909979a5b7b
--- /dev/null
+++ b/lib/utils/aws/dynamo_fips.go
@@ -0,0 +1,49 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package aws
+
+import (
+ "os"
+ "strconv"
+
+ "github.com/gravitational/teleport/lib/modules"
+)
+
+// EnvVarDisableDynamoDBFIPS holds the name of the environment variable that
+// disables FIPS for DynamoDB access in builds where FIPS would otherwise be
+// required.
+const EnvVarDisableDynamoDBFIPS = "TELEPORT_UNSTABLE_DISABLE_DYNAMODB_FIPS"
+
+// UseFIPSForDynamoDB is a DynamoDB-specific check that builds on
+// [modules.Modules.IsBoringBinary].
+//
+// FIPS is enabled by default for boring/FIPS teleport binaries, unless the
+// TELEPORT_UNSTABLE_DISABLE_DYNAMODB_FIPS env variable is set to "yes" (or an
+// equivalent boolean).
+func UseFIPSForDynamoDB() bool {
+ // If the skip toggle is set we don't use FIPS DynamoDB.
+ if val := os.Getenv(EnvVarDisableDynamoDBFIPS); val != "" {
+ if val == "yes" {
+ return false
+ }
+ if b, _ := strconv.ParseBool(val); b {
+ return false
+ }
+ }
+
+ return modules.GetModules().IsBoringBinary()
+}
diff --git a/lib/utils/aws/dynamo_fips_test.go b/lib/utils/aws/dynamo_fips_test.go
new file mode 100644
index 0000000000000..d3006c3ef47c8
--- /dev/null
+++ b/lib/utils/aws/dynamo_fips_test.go
@@ -0,0 +1,82 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package aws_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/gravitational/teleport/lib/modules"
+ awsutils "github.com/gravitational/teleport/lib/utils/aws"
+)
+
+func TestUseFIPSForDynamoDB(t *testing.T) {
+ // Don't t.Parallel(), uses both modules.SetTestModules and t.Setenv.
+
+ tests := []struct {
+ name string
+ fips bool
+ env map[string]string
+ want bool
+ }{
+ {
+ name: "non-FIPS binary",
+ want: false,
+ },
+ {
+ name: "non-FIPS binary with env skip",
+ env: map[string]string{
+ awsutils.EnvVarDisableDynamoDBFIPS: "yes",
+ },
+ want: false,
+ },
+ {
+ name: "FIPS binary",
+ fips: true,
+ want: true,
+ },
+ {
+ name: "FIPS binary with env skip",
+ fips: true,
+ env: map[string]string{
+ awsutils.EnvVarDisableDynamoDBFIPS: "yes",
+ },
+ want: false,
+ },
+ {
+ name: "FIPS binary with env skip (strconv.ParseBool)",
+ fips: true,
+ env: map[string]string{
+ awsutils.EnvVarDisableDynamoDBFIPS: "1",
+ },
+ want: false,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ modules.SetTestModules(t, &modules.TestModules{
+ FIPS: test.fips,
+ })
+ for k, v := range test.env {
+ t.Setenv(k, v)
+ }
+
+ assert.Equal(t, test.want, awsutils.UseFIPSForDynamoDB(), "UseFIPSForDynamoDB mismatch")
+ })
+ }
+}