Validate hosts input in ingress gateway config entry

We can only allow host names that are valid domain names because we put
these hosts into a DNSSAN. In addition, we validate that the wildcard
specifier '*' is only present as the leftmost label to allow for a
wildcard DNSSAN and associated wildcard Host routing in the ingress
gateway proxy.
This commit is contained in:
Chris Piraino 2020-04-28 15:20:53 -05:00
parent bd6bb3bf2d
commit 91586b9228
2 changed files with 96 additions and 1 deletions

View File

@ -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)

View File

@ -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 {