diff --git a/agent/structs/config_entry_gateways.go b/agent/structs/config_entry_gateways.go index a014a56686..f1411bdba7 100644 --- a/agent/structs/config_entry_gateways.go +++ b/agent/structs/config_entry_gateways.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/lib" + "github.com/miekg/dns" ) // IngressGatewayConfigEntry manages the configuration for an ingress service @@ -163,12 +164,14 @@ func (e *IngressGatewayConfigEntry) Validate() error { return fmt.Errorf("Wildcard namespace is not supported for ingress services (listener on port %d)", listener.Port) } - // TODO(ingress): Validate Hosts are valid? for _, h := range s.Hosts { if declaredHosts[h] { return fmt.Errorf("Hosts must be unique within a specific listener (listener on port %d)", listener.Port) } declaredHosts[h] = true + if err := validateHost(h); err != nil { + return err + } } } } @@ -176,6 +179,19 @@ func (e *IngressGatewayConfigEntry) Validate() error { return nil } +func validateHost(host string) error { + wildcardPrefix := "*." + if _, ok := dns.IsDomainName(host); !ok { + return fmt.Errorf("Host %q must be a valid DNS hostname", host) + } + + if strings.ContainsRune(strings.TrimPrefix(host, wildcardPrefix), '*') { + return fmt.Errorf("Host %q is not valid, a wildcard specifier is only allowed as the leftmost label", host) + } + + return nil +} + func (e *IngressGatewayConfigEntry) CanRead(authz acl.Authorizer) bool { var authzContext acl.AuthorizerContext e.FillAuthzContext(&authzContext) diff --git a/agent/structs/config_entry_gateways_test.go b/agent/structs/config_entry_gateways_test.go index e00ae0a2d7..0657f651a0 100644 --- a/agent/structs/config_entry_gateways_test.go +++ b/agent/structs/config_entry_gateways_test.go @@ -316,6 +316,85 @@ func TestIngressConfigEntry_Validate(t *testing.T) { }, expectErr: "Hosts must be unique within a specific listener", }, + { + name: "hosts must be a valid DNS name", + entry: IngressGatewayConfigEntry{ + Kind: "ingress-gateway", + Name: "ingress-web", + Listeners: []IngressListener{ + { + Port: 1111, + Protocol: "http", + Services: []IngressService{ + { + Name: "db", + Hosts: []string{"example..com"}, + }, + }, + }, + }, + }, + expectErr: `Host "example..com" must be a valid DNS hostname`, + }, + { + name: "wildcard specifier is only allowed in the leftmost label", + entry: IngressGatewayConfigEntry{ + Kind: "ingress-gateway", + Name: "ingress-web", + Listeners: []IngressListener{ + { + Port: 1111, + Protocol: "http", + Services: []IngressService{ + { + Name: "db", + Hosts: []string{"*.example.com"}, + }, + }, + }, + }, + }, + }, + { + name: "wildcard specifier is not allowed in non-leftmost labels", + entry: IngressGatewayConfigEntry{ + Kind: "ingress-gateway", + Name: "ingress-web", + Listeners: []IngressListener{ + { + Port: 1111, + Protocol: "http", + Services: []IngressService{ + { + Name: "db", + Hosts: []string{"example.*.com"}, + }, + }, + }, + }, + }, + expectErr: `Host "example.*.com" is not valid, a wildcard specifier is only allowed as the leftmost label`, + }, + { + name: "wildcard specifier is not allowed in leftmost labels as a partial", + entry: IngressGatewayConfigEntry{ + Kind: "ingress-gateway", + Name: "ingress-web", + Listeners: []IngressListener{ + { + Port: 1111, + Protocol: "http", + Services: []IngressService{ + { + Name: "db", + Hosts: []string{"*-test.example.com"}, + }, + }, + }, + }, + }, + expectErr: `Host "*-test.example.com" is not valid, a wildcard specifier is only allowed as the leftmost label`, + }, } for _, test := range cases {