basic validation for hostname in port publishing

This commit is contained in:
Pavel Sviderski
2024-12-10 17:24:29 +10:00
parent 848abbd2f8
commit 8bfb3b80ed
2 changed files with 40 additions and 18 deletions
+18 -5
View File
@@ -45,12 +45,17 @@ func (p *PortSpec) Validate() error {
} }
switch p.Mode { switch p.Mode {
case PortModeIngress: case "", PortModeIngress: // Default mode is ingress if not specified.
if p.HostIP.IsValid() { if p.HostIP.IsValid() {
return fmt.Errorf("host IP cannot be specified in %s mode", PortModeIngress) return fmt.Errorf("host IP cannot be specified in %s mode", PortModeIngress)
} }
if p.Hostname != "" && p.Protocol != ProtocolHTTP && p.Protocol != ProtocolHTTPS { if p.Hostname != "" {
return fmt.Errorf("hostname is only valid with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS) if p.Protocol != ProtocolHTTP && p.Protocol != ProtocolHTTPS {
return fmt.Errorf("hostname is only valid with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS)
}
if err := validateHostname(p.Hostname); err != nil {
return fmt.Errorf("invalid hostname '%s': %w", p.Hostname, err)
}
} }
if p.Hostname == "" && (p.Protocol == ProtocolHTTP || p.Protocol == ProtocolHTTPS) { if p.Hostname == "" && (p.Protocol == ProtocolHTTP || p.Protocol == ProtocolHTTPS) {
return fmt.Errorf("hostname is required with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS) return fmt.Errorf("hostname is required with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS)
@@ -136,7 +141,6 @@ func ParsePortSpec(port string) (PortSpec, error) {
if parts[0] == "" { if parts[0] == "" {
return spec, fmt.Errorf("hostname must not be empty") return spec, fmt.Errorf("hostname must not be empty")
} }
// TODO: validate hostname?
spec.Hostname = parts[0] spec.Hostname = parts[0]
} }
@@ -170,7 +174,6 @@ func ParsePortSpec(port string) (PortSpec, error) {
if parts[0] == "" { if parts[0] == "" {
return spec, fmt.Errorf("hostname must not be empty") return spec, fmt.Errorf("hostname must not be empty")
} }
// TODO: validate hostname?
spec.Hostname = parts[0] spec.Hostname = parts[0]
} }
@@ -208,3 +211,13 @@ func parsePort(s string) (uint16, error) {
} }
return uint16(port), nil return uint16(port), nil
} }
func validateHostname(hostname string) error {
if hostname == "" {
return fmt.Errorf("must not be empty")
}
if !strings.Contains(hostname, ".") {
return fmt.Errorf("must be a valid domain name containing at least one dot")
}
return nil
}
+22 -13
View File
@@ -214,7 +214,7 @@ func TestParsePortSpec(t *testing.T) {
}, },
{ {
name: "invalid published port", name: "invalid published port",
port: "test:invalid:8080", port: "app.example.com:invalid:8080",
wantErr: "invalid published port", wantErr: "invalid published port",
}, },
{ {
@@ -232,6 +232,11 @@ func TestParsePortSpec(t *testing.T) {
port: "8000:8080/https", port: "8000:8080/https",
wantErr: "hostname is required", wantErr: "hostname is required",
}, },
{
name: "invalid hostname",
port: "app:8080/http",
wantErr: "invalid hostname 'app': must be a valid domain name containing at least one dot",
},
{ {
name: "hostname with tcp protocol", name: "hostname with tcp protocol",
port: "app.example.com:8080/tcp", port: "app.example.com:8080/tcp",
@@ -324,13 +329,19 @@ func TestPortSpec_Validate(t *testing.T) {
Mode: PortModeIngress, Mode: PortModeIngress,
}, },
}, },
{
name: "ingress mode tcp",
spec: PortSpec{
ContainerPort: 8080,
Protocol: ProtocolTCP,
},
},
{ {
name: "ingress mode with published tcp port", name: "ingress mode with published tcp port",
spec: PortSpec{ spec: PortSpec{
PublishedPort: 80, PublishedPort: 80,
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolTCP, Protocol: ProtocolTCP,
Mode: PortModeIngress,
}, },
}, },
{ {
@@ -338,7 +349,6 @@ func TestPortSpec_Validate(t *testing.T) {
spec: PortSpec{ spec: PortSpec{
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolUDP, Protocol: ProtocolUDP,
Mode: PortModeIngress,
}, },
}, },
{ {
@@ -347,7 +357,6 @@ func TestPortSpec_Validate(t *testing.T) {
PublishedPort: 80, PublishedPort: 80,
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolUDP, Protocol: ProtocolUDP,
Mode: PortModeIngress,
}, },
}, },
{ {
@@ -356,7 +365,6 @@ func TestPortSpec_Validate(t *testing.T) {
Hostname: "app.example.com", Hostname: "app.example.com",
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolHTTP, Protocol: ProtocolHTTP,
Mode: PortModeIngress,
}, },
}, },
{ {
@@ -365,7 +373,6 @@ func TestPortSpec_Validate(t *testing.T) {
Hostname: "app.example.com", Hostname: "app.example.com",
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolHTTPS, Protocol: ProtocolHTTPS,
Mode: PortModeIngress,
}, },
}, },
{ {
@@ -375,7 +382,6 @@ func TestPortSpec_Validate(t *testing.T) {
PublishedPort: 6443, PublishedPort: 6443,
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolHTTPS, Protocol: ProtocolHTTPS,
Mode: PortModeIngress,
}, },
}, },
@@ -415,7 +421,6 @@ func TestPortSpec_Validate(t *testing.T) {
name: "missing container port", name: "missing container port",
spec: PortSpec{ spec: PortSpec{
Protocol: ProtocolTCP, Protocol: ProtocolTCP,
Mode: PortModeIngress,
}, },
wantErr: "container port must be non-zero", wantErr: "container port must be non-zero",
}, },
@@ -424,7 +429,6 @@ func TestPortSpec_Validate(t *testing.T) {
spec: PortSpec{ spec: PortSpec{
ContainerPort: 8080, ContainerPort: 8080,
Protocol: "invalid", Protocol: "invalid",
Mode: PortModeIngress,
}, },
wantErr: "invalid protocol: 'invalid'", wantErr: "invalid protocol: 'invalid'",
}, },
@@ -443,16 +447,23 @@ func TestPortSpec_Validate(t *testing.T) {
Hostname: "app.example.com", Hostname: "app.example.com",
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolTCP, Protocol: ProtocolTCP,
Mode: PortModeIngress,
}, },
wantErr: "hostname is only valid with 'http' or 'https' protocols", wantErr: "hostname is only valid with 'http' or 'https' protocols",
}, },
{
name: "invalid hostname",
spec: PortSpec{
Hostname: "app",
ContainerPort: 8080,
Protocol: ProtocolHTTPS,
},
wantErr: "invalid hostname 'app': must be a valid domain name containing at least one dot",
},
{ {
name: "missing hostname with http", name: "missing hostname with http",
spec: PortSpec{ spec: PortSpec{
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolHTTP, Protocol: ProtocolHTTP,
Mode: PortModeIngress,
}, },
wantErr: "hostname is required with 'http' or 'https' protocols", wantErr: "hostname is required with 'http' or 'https' protocols",
}, },
@@ -461,7 +472,6 @@ func TestPortSpec_Validate(t *testing.T) {
spec: PortSpec{ spec: PortSpec{
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolHTTPS, Protocol: ProtocolHTTPS,
Mode: PortModeIngress,
}, },
wantErr: "hostname is required with 'http' or 'https' protocols", wantErr: "hostname is required with 'http' or 'https' protocols",
}, },
@@ -471,7 +481,6 @@ func TestPortSpec_Validate(t *testing.T) {
HostIP: netip.MustParseAddr("127.0.0.1"), HostIP: netip.MustParseAddr("127.0.0.1"),
ContainerPort: 8080, ContainerPort: 8080,
Protocol: ProtocolTCP, Protocol: ProtocolTCP,
Mode: PortModeIngress,
}, },
wantErr: "host IP cannot be specified in ingress mode", wantErr: "host IP cannot be specified in ingress mode",
}, },