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
38 changes: 23 additions & 15 deletions internal/gatewayapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/gatewayapi/status"
"github.com/envoyproxy/gateway/internal/utils/net"
)

func (t *Translator) ProcessBackends(backends []*egv1a1.Backend, backendTLSPolicies []*gwapiv1.BackendTLSPolicy) []*egv1a1.Backend {
Expand Down Expand Up @@ -69,17 +68,9 @@ func validateBackend(backend *egv1a1.Backend, backendTLSPolicies []*gwapiv1.Back
return routeErr
}
} else if ep.IP != nil {
ip, err := netip.ParseAddr(ep.IP.Address)
if err != nil {
return status.NewRouteStatusError(
fmt.Errorf("IP address %s is invalid", ep.IP.Address),
status.RouteReasonInvalidAddress,
)
} else if ip.IsLoopback() && !runningOnHost {
return status.NewRouteStatusError(
fmt.Errorf("IP address %s in the loopback range is only supported when using the Host infrastructure", ep.IP.Address),
status.RouteReasonInvalidAddress,
)
routeErr := validateIP(ep.IP, runningOnHost)
if routeErr != nil {
return routeErr
}
}
}
Expand Down Expand Up @@ -170,16 +161,17 @@ func validateBackendTLSSettings(backend *egv1a1.Backend, backendTLSPolicies []*g
return nil
}

func validateHostname(hostname, typeName string, allowLocalhost bool) *status.RouteStatusError {
func validateHostname(hostname, typeName string, runningOnHost bool) *status.RouteStatusError {
// must be a valid hostname
if errs := validation.IsDNS1123Subdomain(hostname); errs != nil {
return status.NewRouteStatusError(
fmt.Errorf("hostname %s is not a valid %s", hostname, typeName),
status.RouteReasonInvalidAddress,
)
}
isLocalHostname := allowLocalhost && hostname == net.DefaultLocalAddress
if !isLocalHostname && len(strings.Split(hostname, ".")) < 2 {
// Host mode is a dev configuration, so we do not enforce domain rules.
// Doing so would interfere with docker hostnames (e.g. "jaeger") or literal IPs.
if !runningOnHost && len(strings.Split(hostname, ".")) < 2 {
return status.NewRouteStatusError(
fmt.Errorf("hostname %s should be a domain with at least two segments separated by dots", hostname),
status.RouteReasonInvalidAddress,
Expand All @@ -195,3 +187,19 @@ func validateHostname(hostname, typeName string, allowLocalhost bool) *status.Ro

return nil
}

func validateIP(epIP *egv1a1.IPEndpoint, runningOnHost bool) status.Error {
ip, err := netip.ParseAddr(epIP.Address)
if err != nil {
return status.NewRouteStatusError(
fmt.Errorf("IP address %s is invalid", epIP.Address),
status.RouteReasonInvalidAddress,
)
} else if ip.IsLoopback() && !runningOnHost {
return status.NewRouteStatusError(
fmt.Errorf("IP address %s in the loopback range is only supported when using the Host infrastructure", epIP.Address),
status.RouteReasonInvalidAddress,
)
}
return nil
}
100 changes: 100 additions & 0 deletions internal/gatewayapi/backend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package gatewayapi

import (
"testing"

"github.com/stretchr/testify/require"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
)

func TestValidateHostnameRunningOnHost(t *testing.T) {
cases := []struct {
name string
hostname string
runningOnHost bool
expectedErr string
}{
{
name: "domain ok in any case",
hostname: "httpbin.org",
runningOnHost: false,
},
{
name: "single label not ok when in k8s",
hostname: "otel-tui",
runningOnHost: false,
expectedErr: "hostname otel-tui should be a domain with at least two segments separated by dots",
},
{
name: "single label ok when running on host",
hostname: "otel-tui",
runningOnHost: true,
},
{
name: "IP not ok in any case",
hostname: "127.0.0.1",
runningOnHost: true,
expectedErr: "hostname 127.0.0.1 is an IP address",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := validateHostname(tc.hostname, "hostname", tc.runningOnHost)
if tc.expectedErr == "" {
require.Nil(t, err)
return
}
require.EqualError(t, err, tc.expectedErr)
})
}
}

func TestValidateIPRunningOnHost(t *testing.T) {
cases := []struct {
name string
address string
runningOnHost bool
expectedErr string
}{
{
name: "address ok in any case",
address: "10.0.0.1",
runningOnHost: false,
},
{
name: "loopback not ok when in k8s",
address: "127.0.0.1",
runningOnHost: false,
expectedErr: "IP address 127.0.0.1 in the loopback range is only supported when using the Host infrastructure",
},
{
name: "loopback ok when running on host",
address: "127.0.0.1",
runningOnHost: true,
},
{
name: "invalid IP not ok in any case",
address: "300.0.0.1",
runningOnHost: true,
expectedErr: "IP address 300.0.0.1 is invalid",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := validateIP(&egv1a1.IPEndpoint{Address: tc.address}, tc.runningOnHost)
if tc.expectedErr == "" {
require.Nil(t, err)
return
}
require.EqualError(t, err, tc.expectedErr)
})
}
}
1 change: 1 addition & 0 deletions release-notes/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ security updates: |
new features: |

bug fixes: |
Allowed single-label backend hostnames when running with the Host infrastructure, enabling Docker Compose service names for telemetry backends.

# Enhancements that improve performance.
performance improvements: |
Expand Down
Loading