Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unprivileged port validation #7034

Merged
merged 8 commits into from
Jan 6, 2025
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
17 changes: 5 additions & 12 deletions cmd/nginx-ingress/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"regexp"
"strings"

internalValidation "github.com/nginxinc/kubernetes-ingress/internal/validation"
api_v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/validation"
Expand Down Expand Up @@ -345,22 +346,22 @@
nl.Fatalf(l, "Invalid value for leader-election-lock-name: %v", statusLockNameValidationError)
}

statusPortValidationError := validatePort(*nginxStatusPort)
statusPortValidationError := internalValidation.ValidateUnprivilegedPort(*nginxStatusPort)

Check warning on line 349 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L349

Added line #L349 was not covered by tests
if statusPortValidationError != nil {
nl.Fatalf(l, "Invalid value for nginx-status-port: %v", statusPortValidationError)
}

metricsPortValidationError := validatePort(*prometheusMetricsListenPort)
metricsPortValidationError := internalValidation.ValidateUnprivilegedPort(*prometheusMetricsListenPort)

Check warning on line 354 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L354

Added line #L354 was not covered by tests
if metricsPortValidationError != nil {
nl.Fatalf(l, "Invalid value for prometheus-metrics-listen-port: %v", metricsPortValidationError)
}

readyStatusPortValidationError := validatePort(*readyStatusPort)
readyStatusPortValidationError := internalValidation.ValidateUnprivilegedPort(*readyStatusPort)

Check warning on line 359 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L359

Added line #L359 was not covered by tests
if readyStatusPortValidationError != nil {
nl.Fatalf(l, "Invalid value for ready-status-port: %v", readyStatusPortValidationError)
}

healthProbePortValidationError := validatePort(*serviceInsightListenPort)
healthProbePortValidationError := internalValidation.ValidateUnprivilegedPort(*serviceInsightListenPort)

Check warning on line 364 in cmd/nginx-ingress/flags.go

View check run for this annotation

Codecov / codecov/patch

cmd/nginx-ingress/flags.go#L364

Added line #L364 was not covered by tests
if healthProbePortValidationError != nil {
nl.Fatalf(l, "Invalid value for service-insight-listen-port: %v", metricsPortValidationError)
}
Expand Down Expand Up @@ -464,14 +465,6 @@
return nil
}

// validatePort makes sure a given port is inside the valid port range for its usage
func validatePort(port int) error {
if port < 1024 || port > 65535 {
return fmt.Errorf("port outside of valid port range [1024 - 65535]: %v", port)
}
return nil
}

// validateLogLevel makes sure a given logLevel is one of the allowed values
func validateLogLevel(logLevel string) error {
switch strings.ToLower(logLevel) {
Expand Down
18 changes: 0 additions & 18 deletions cmd/nginx-ingress/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,6 @@ import (
"testing"
)

func TestValidatePort(t *testing.T) {
badPorts := []int{80, 443, 1, 1023, 65536}
for _, badPort := range badPorts {
err := validatePort(badPort)
if err == nil {
t.Errorf("Expected error for port %v\n", badPort)
}
}

goodPorts := []int{8080, 8081, 8082, 1024, 65535}
for _, goodPort := range goodPorts {
err := validatePort(goodPort)
if err != nil {
t.Errorf("Error for valid port: %v err: %v\n", goodPort, err)
}
}
}

func TestParseNginxStatusAllowCIDRs(t *testing.T) {
badCIDRs := []struct {
input string
Expand Down
22 changes: 15 additions & 7 deletions internal/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@
)

// ValidatePort ensure port matches rfc6335 https://www.rfc-editor.org/rfc/rfc6335.html
func ValidatePort(value string) error {
port, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("error parsing port number: %w", err)
func ValidatePort(value int) error {
if value > 65535 || value < 1 {
return fmt.Errorf("error parsing port: %d not a valid port number", value)
}
if port > 65535 || port < 1 {
return fmt.Errorf("error parsing port: %v not a valid port number", port)
return nil
}

// ValidateUnprivilegedPort ensure port is in the 1024-65535 range
func ValidateUnprivilegedPort(value int) error {
if value > 65535 || value < 1023 {
return fmt.Errorf("port outside of valid port range [1024 - 65535]: %d", value)
}
return nil
}
Expand All @@ -34,7 +38,11 @@
if validIPRegex.MatchString(host) || validDNSRegex.MatchString(host) || validHostnameRegex.MatchString(host) {
chunks := strings.Split(host, ":")
if len(chunks) > 1 {
err := ValidatePort(chunks[1])
port, err := strconv.Atoi(chunks[1])
if err != nil {
return err
}

Check warning on line 44 in internal/validation/validation.go

View check run for this annotation

Codecov / codecov/patch

internal/validation/validation.go#L43-L44

Added lines #L43 - L44 were not covered by tests
err = ValidatePort(port)
if err != nil {
return fmt.Errorf("invalid port: %w", err)
}
Expand Down
28 changes: 21 additions & 7 deletions internal/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,42 @@ import (
func TestValidatePort_IsValidOnValidInput(t *testing.T) {
t.Parallel()

ports := []string{"1", "65535"}
ports := []int{1, 65535}
for _, p := range ports {
if err := ValidatePort(p); err != nil {
t.Error(err)
}
}
}

func TestValidatePort_ErrorsOnInvalidString(t *testing.T) {
func TestValidatePort_ErrorsOnInvalidRange(t *testing.T) {
t.Parallel()

if err := ValidatePort(""); err == nil {
t.Error("want error, got nil")
ports := []int{0, -1, 65536}
for _, p := range ports {
if err := ValidatePort(p); err == nil {
t.Error("want error, got nil")
}
}
}

func TestValidatePort_ErrorsOnInvalidRange(t *testing.T) {
func TestValidateUnprivilegedPort_IsValidOnValidInput(t *testing.T) {
t.Parallel()

ports := []string{"0", "-1", "65536"}
ports := []int{1024, 65535}
for _, p := range ports {
if err := ValidatePort(p); err == nil {
if err := ValidateUnprivilegedPort(p); err != nil {
t.Error(err)
}
}
}

func TestValidateUnprivilegedPort_ErrorsOnInvalidRange(t *testing.T) {
t.Parallel()

ports := []int{0, -1, 80, 443, 65536}
for _, p := range ports {
if err := ValidateUnprivilegedPort(p); err == nil {
t.Error("want error, got nil")
}
}
Expand Down
7 changes: 6 additions & 1 deletion pkg/apis/dos/validation/dos.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"net"
"net/url"
"regexp"
"strconv"
"strings"

internalValidation "github.com/nginxinc/kubernetes-ingress/internal/validation"
Expand Down Expand Up @@ -128,7 +129,11 @@
}
if validIPRegex.MatchString(dstAntn) || validDNSRegex.MatchString(dstAntn) || validLocalhostRegex.MatchString(dstAntn) {
chunks := strings.Split(dstAntn, ":")
err := internalValidation.ValidatePort(chunks[1])
port, err := strconv.Atoi(chunks[1])
if err != nil {
return err
}

Check warning on line 135 in pkg/apis/dos/validation/dos.go

View check run for this annotation

Codecov / codecov/patch

pkg/apis/dos/validation/dos.go#L134-L135

Added lines #L134 - L135 were not covered by tests
err = internalValidation.ValidatePort(port)
if err != nil {
return fmt.Errorf("invalid log destination: %w", err)
}
Expand Down
Loading