Sitelet https://github.com/kubernetes/kops/pull/18393/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/kops-controller/pkg/server/node_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (
"encoding/json"
"fmt"

"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/klog/v2"
kopsvalidation "k8s.io/kops/pkg/apis/kops/validation"
"k8s.io/kops/pkg/apis/nodeup"
"k8s.io/kops/pkg/bootstrap"
"k8s.io/kops/pkg/commands"
Expand All @@ -34,6 +36,9 @@ func (s *Server) getNodeConfig(ctx context.Context, req *nodeup.BootstrapRequest
if instanceGroupName == "" {
return nil, fmt.Errorf("did not find InstanceGroup for node %q", identity.NodeName)
}
if errs := kopsvalidation.ValidateInstanceGroupName(instanceGroupName, field.NewPath("instanceGroupName")); len(errs) > 0 {
return nil, fmt.Errorf("invalid InstanceGroup name: %v", errs.ToAggregate())
}

nodeConfig := &nodeup.NodeConfig{}

Expand Down
15 changes: 15 additions & 0 deletions pkg/apis/kops/validation/instancegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/aws/aws-sdk-go-v2/aws/arn"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"

Expand All @@ -34,6 +35,20 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
)

// ValidateInstanceGroupName validates that an InstanceGroup name
// is a valid Kubernetes ObjectMeta name (DNS subdomain).
func ValidateInstanceGroupName(name string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if name == "" {
allErrs = append(allErrs, field.Required(fldPath, ""))
return allErrs
}
for _, msg := range apivalidation.NameIsDNSSubdomain(name, false) {
allErrs = append(allErrs, field.Invalid(fldPath, name, msg))
}
return allErrs
}

// ValidateInstanceGroup is responsible for validating the configuration of a instancegroup
func ValidateInstanceGroup(g *kops.InstanceGroup, cloud fi.Cloud, strict bool) field.ErrorList {
allErrs := field.ErrorList{}
Expand Down
48 changes: 48 additions & 0 deletions pkg/apis/kops/validation/instancegroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,51 @@ func createMinimalInstanceGroup() *kops.InstanceGroup {
}
return ig
}

func TestValidateInstanceGroupName(t *testing.T) {
cases := []struct {
name string
input string
wantError bool
}{
// Empty is rejected; the validator does not have a special-case bypass.
// Callers that allow an empty name in a specific branch (e.g. the
// CAPI synthesis path in kops-controller) must skip the call.
{name: "empty", input: "", wantError: true},

// Valid DNS1123 subdomain names.
{name: "simple", input: "nodes", wantError: false},
{name: "with hyphen", input: "nodes-us-east-1a", wantError: false},
{name: "control plane", input: "control-plane-us-east-1a", wantError: false},
{name: "fqdn style", input: "nodes.example.k8s.local", wantError: false},
{name: "numeric", input: "nodes1", wantError: false},

// Path-traversal payloads.
{name: "parent traversal", input: "..", wantError: true},
{name: "parent traversal with target", input: "../master-foo", wantError: true},
{name: "embedded traversal", input: "nodes/../master-foo", wantError: true},
{name: "forward slash", input: "nodes/foo", wantError: true},
{name: "backslash", input: "nodes\\foo", wantError: true},
{name: "absolute path", input: "/etc/passwd", wantError: true},

// Other invalid DNS1123 subdomain inputs.
{name: "leading dot", input: ".nodes", wantError: true},
{name: "trailing dot", input: "nodes.", wantError: true},
{name: "uppercase", input: "Nodes", wantError: true},
{name: "whitespace", input: "nodes foo", wantError: true},
{name: "null byte", input: "nodes\x00", wantError: true},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
errs := ValidateInstanceGroupName(tc.input, field.NewPath("name"))
if tc.wantError {
if len(errs) == 0 {
t.Fatalf("expected errors for input %q, got none", tc.input)
}
} else if len(errs) > 0 {
t.Fatalf("unexpected errors for input %q: %v", tc.input, errs.ToAggregate())
}
})
}
}
Loading