Skip to content

Commit

Permalink
Forbid custom namespaces in various resources (#49830)
Browse files Browse the repository at this point in the history
* Forbid Database and App updates with custom namespaces

* Forbid custom namespaces on api/types.KeepAlive

* Forbid custom namespaces in api/types.Metadata

* Forbid custom namespaces in api/types.RoleV6

* Use a share validation helper, improve error message
  • Loading branch information
codingllama authored Dec 5, 2024
1 parent d9c4906 commit 8e723cc
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 13 deletions.
2 changes: 1 addition & 1 deletion api/types/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions api/types/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 4 additions & 0 deletions api/types/presence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 4 additions & 0 deletions api/types/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion api/types/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down
31 changes: 27 additions & 4 deletions api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions lib/services/local/databaseservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 14 additions & 6 deletions lib/services/local/presence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/services/matchers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 8e723cc

Please sign in to comment.