diff --git a/api/types/kubernetes_test.go b/api/types/kubernetes_test.go index 5d3a81210be..1763da9e114 100644 --- a/api/types/kubernetes_test.go +++ b/api/types/kubernetes_test.go @@ -30,7 +30,7 @@ func TestKubeClustersSorter(t *testing.T) { servers := make([]KubeCluster, len(testVals)) for i := 0; i < len(testVals); i++ { var err error - servers[i], err = NewKubernetesClusterV3FromLegacyCluster("_", &KubernetesCluster{ + servers[i], err = NewKubernetesClusterV3FromLegacyCluster("", &KubernetesCluster{ Name: testVals[i], }) require.NoError(t, err) diff --git a/api/types/namespace.go b/api/types/namespace.go index 41e4b3e4fe7..a6fbd02de22 100644 --- a/api/types/namespace.go +++ b/api/types/namespace.go @@ -143,3 +143,17 @@ func IsValidNamespace(s string) bool { } var validNamespace = regexp.MustCompile(`^[A-Za-z0-9]+$`) + +// ValidateNamespaceDefault ensures that the namespace is the "default" +// namespace. +// This is a precursor to a hard-removal of namespaces. +func ValidateNamespaceDefault(ns string) error { + if ns == defaults.Namespace { + return nil + } + + const message = "" + + "namespace %q invalid, custom namespaces are deprecated; " + + "the namespace field should be omitted or set to %q" + return trace.BadParameter(message, ns, defaults.Namespace) +} diff --git a/api/types/presence.go b/api/types/presence.go index 764f4671998..b27b3b232cc 100644 --- a/api/types/presence.go +++ b/api/types/presence.go @@ -67,9 +67,13 @@ func (s *KeepAlive) CheckAndSetDefaults() error { if s.IsEmpty() { return trace.BadParameter("missing resource name") } + if s.Namespace == "" { s.Namespace = defaults.Namespace } + if err := ValidateNamespaceDefault(s.Namespace); err != nil { + return trace.Wrap(err) + } return nil } diff --git a/api/types/resource.go b/api/types/resource.go index f7465a84e8b..ad5beaceb78 100644 --- a/api/types/resource.go +++ b/api/types/resource.go @@ -478,9 +478,13 @@ func (m *Metadata) CheckAndSetDefaults() error { if m.Name == "" { return trace.BadParameter("missing parameter Name") } + if m.Namespace == "" { m.Namespace = defaults.Namespace } + if err := ValidateNamespaceDefault(m.Namespace); err != nil { + return trace.Wrap(err) + } // adjust expires time to UTC if it's set if m.Expires != nil { diff --git a/api/types/resource_test.go b/api/types/resource_test.go index 896ee3b78ec..53b38ef33e1 100644 --- a/api/types/resource_test.go +++ b/api/types/resource_test.go @@ -266,7 +266,7 @@ func TestMatchSearch_ResourceSpecific(t *testing.T) { name: "kube cluster", matchingSearchVals: []string{"foo", "prod", "env"}, newResource: func(t *testing.T) ResourceWithLabels { - kc, err := NewKubernetesClusterV3FromLegacyCluster("_", &KubernetesCluster{ + kc, err := NewKubernetesClusterV3FromLegacyCluster("", &KubernetesCluster{ Name: "foo", StaticLabels: labels, }) diff --git a/api/types/role.go b/api/types/role.go index d95562d5d6f..6551921bc9a 100644 --- a/api/types/role.go +++ b/api/types/role.go @@ -1071,8 +1071,9 @@ func (r *RoleV6) CheckAndSetDefaults() error { if len(r.Spec.Options.BPF) == 0 { r.Spec.Options.BPF = defaults.EnhancedEvents() } - if r.Spec.Allow.Namespaces == nil { - r.Spec.Allow.Namespaces = []string{defaults.Namespace} + if err := checkAndSetRoleConditionNamespaces(&r.Spec.Allow.Namespaces); err != nil { + // Using trace.BadParameter instead of trace.Wrap for a better error message. + return trace.BadParameter("allow: %s", err) } if r.Spec.Options.RecordSession == nil { r.Spec.Options.RecordSession = &RecordSession{ @@ -1175,8 +1176,9 @@ func (r *RoleV6) CheckAndSetDefaults() error { return trace.BadParameter("unrecognized role version: %v", r.Version) } - if r.Spec.Deny.Namespaces == nil { - r.Spec.Deny.Namespaces = []string{defaults.Namespace} + if err := checkAndSetRoleConditionNamespaces(&r.Spec.Deny.Namespaces); err != nil { + // Using trace.BadParameter instead of trace.Wrap for a better error message. + return trace.BadParameter("deny: %s", err) } // Validate request.kubernetes_resources fields are all valid. @@ -1322,6 +1324,27 @@ func (r *RoleV6) CheckAndSetDefaults() error { return nil } +func checkAndSetRoleConditionNamespaces(namespaces *[]string) error { + // If nil use the default. + // This distinguishes between nil and empty (in accordance to legacy code). + if *namespaces == nil { + *namespaces = []string{defaults.Namespace} + return nil + } + + for i, ns := range *namespaces { + if ns == Wildcard { + continue // OK, wildcard is accepted. + } + if err := ValidateNamespaceDefault(ns); err != nil { + // Using trace.BadParameter instead of trace.Wrap for a better error message. + return trace.BadParameter("namespaces[%d]: %s", i, err) + } + } + + return nil +} + // String returns the human readable representation of a role. func (r *RoleV6) String() string { options, _ := json.Marshal(r.Spec.Options) diff --git a/lib/services/local/databaseservice.go b/lib/services/local/databaseservice.go index ad7f064e35b..255f777eba6 100644 --- a/lib/services/local/databaseservice.go +++ b/lib/services/local/databaseservice.go @@ -44,6 +44,10 @@ func (s *DatabaseServicesService) UpsertDatabaseService(ctx context.Context, ser if err := services.CheckAndSetDefaults(service); err != nil { return nil, trace.Wrap(err) } + if err := types.ValidateNamespaceDefault(service.GetNamespace()); err != nil { + return nil, trace.Wrap(err) + } + rev := service.GetRevision() value, err := services.MarshalDatabaseService(service) if err != nil { diff --git a/lib/services/local/presence.go b/lib/services/local/presence.go index e7424e500f0..19b391029cd 100644 --- a/lib/services/local/presence.go +++ b/lib/services/local/presence.go @@ -345,10 +345,10 @@ func (s *PresenceService) UpsertNode(ctx context.Context, server types.Server) ( if server.GetNamespace() == "" { server.SetNamespace(apidefaults.Namespace) } - - if n := server.GetNamespace(); n != apidefaults.Namespace { - return nil, trace.BadParameter("cannot place node in namespace %q, custom namespaces are deprecated", n) + if err := types.ValidateNamespaceDefault(server.GetNamespace()); err != nil { + return nil, trace.Wrap(err) } + rev := server.GetRevision() value, err := services.MarshalServer(server) if err != nil { @@ -377,10 +377,10 @@ func (s *PresenceService) UpdateNode(ctx context.Context, server types.Server) ( if server.GetNamespace() == "" { server.SetNamespace(apidefaults.Namespace) } - - if n := server.GetNamespace(); n != apidefaults.Namespace { - return nil, trace.BadParameter("cannot place node in namespace %q, custom namespaces are deprecated", n) + if err := types.ValidateNamespaceDefault(server.GetNamespace()); err != nil { + return nil, trace.Wrap(err) } + rev := server.GetRevision() value, err := services.MarshalServer(server) if err != nil { @@ -1003,6 +1003,10 @@ func (s *PresenceService) UpsertDatabaseServer(ctx context.Context, server types if err := services.CheckAndSetDefaults(server); err != nil { return nil, trace.Wrap(err) } + if err := types.ValidateNamespaceDefault(server.GetNamespace()); err != nil { + return nil, trace.Wrap(err) + } + rev := server.GetRevision() value, err := services.MarshalDatabaseServer(server) if err != nil { @@ -1096,6 +1100,10 @@ func (s *PresenceService) UpsertApplicationServer(ctx context.Context, server ty if err := services.CheckAndSetDefaults(server); err != nil { return nil, trace.Wrap(err) } + if err := types.ValidateNamespaceDefault(server.GetNamespace()); err != nil { + return nil, trace.Wrap(err) + } + rev := server.GetRevision() value, err := services.MarshalAppServer(server) if err != nil { diff --git a/lib/services/matchers_test.go b/lib/services/matchers_test.go index 68c52858dde..7238c099d60 100644 --- a/lib/services/matchers_test.go +++ b/lib/services/matchers_test.go @@ -499,7 +499,7 @@ func TestMatchResourceByFilters(t *testing.T) { { name: "kube cluster", resource: func() types.ResourceWithLabels { - cluster, err := types.NewKubernetesClusterV3FromLegacyCluster("_", &types.KubernetesCluster{ + cluster, err := types.NewKubernetesClusterV3FromLegacyCluster("", &types.KubernetesCluster{ Name: "foo", }) require.NoError(t, err)