Skip to content

Commit

Permalink
fix(parser) handle same SNI across different Ingress rules
Browse files Browse the repository at this point in the history
When the same SNI is associated with different Kubernetes Secrets.
When using Kong with a database, this results in the same SNI being
associated with a different certificate and the controller fails to
update the SNI in Kong.

This fix reduces the likelihood of this happening for some cases but
will not fix the problem for all cases.

See
Kong/deck@890404c
for the root cause of this issue.

Fix #510
From #523
  • Loading branch information
hbagdi authored Jan 31, 2020
1 parent 8aea90b commit 564c9ac
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 14 deletions.
58 changes: 44 additions & 14 deletions internal/ingress/controller/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,52 @@ func (p *Parser) fillConsumersAndCredentials(state *KongState) error {
return nil
}

func filterHosts(secretNameToSNIs map[string][]string, hosts []string) []string {
hostsToAdd := []string{}
seenHosts := map[string]bool{}
for _, hosts := range secretNameToSNIs {
for _, host := range hosts {
seenHosts[host] = true
}
}
for _, host := range hosts {
if !seenHosts[host] {
hostsToAdd = append(hostsToAdd, host)
}
}
return hostsToAdd
}

func processTLSSections(tlsSections []networking.IngressTLS,
namespace string, secretNameToSNIs map[string][]string) {
// TODO: optmize: collect all TLS sections and process at the same
// time to avoid regenerating the seen map; or use a seen map in the
// parser struct itself.
for _, tls := range tlsSections {
if len(tls.Hosts) == 0 {
continue
}
if tls.SecretName == "" {
continue
}
hosts := tls.Hosts
secretName := namespace + "/" + tls.SecretName
hosts = filterHosts(secretNameToSNIs, hosts)
if secretNameToSNIs[secretName] != nil {
hosts = append(hosts, secretNameToSNIs[secretName]...)
}
secretNameToSNIs[secretName] = hosts
}
}

func (p *Parser) parseIngressRules(
ingressList []*networking.Ingress) (*parsedIngressRules, error) {

sort.SliceStable(ingressList, func(i, j int) bool {
return ingressList[i].CreationTimestamp.Before(
&ingressList[j].CreationTimestamp)
})

// generate the following:
// Services and Routes
var allDefaultBackends []networking.Ingress
Expand All @@ -396,20 +439,7 @@ func (p *Parser) parseIngressRules(

}

for _, tls := range ingressSpec.TLS {
if len(tls.Hosts) == 0 {
continue
}
if tls.SecretName == "" {
continue
}
hosts := tls.Hosts
secretName := ingress.Namespace + "/" + tls.SecretName
if secretNameToSNIs[secretName] != nil {
hosts = append(hosts, secretNameToSNIs[secretName]...)
}
secretNameToSNIs[secretName] = hosts
}
processTLSSections(ingressSpec.TLS, ingress.Namespace, secretNameToSNIs)

for i, rule := range ingressSpec.Rules {
host := rule.Host
Expand Down
72 changes: 72 additions & 0 deletions internal/ingress/controller/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2635,3 +2635,75 @@ func Test_getCombinations(t *testing.T) {
})
}
}

func Test_processTLSSections(t *testing.T) {
type args struct {
tlsSections []networking.IngressTLS
namespace string
}
tests := []struct {
name string
args args
want map[string][]string
}{
{
args: args{
tlsSections: []networking.IngressTLS{
{
Hosts: []string{
"1.example.com",
"2.example.com",
},
SecretName: "sooper-secret",
},
{
Hosts: []string{
"3.example.com",
"4.example.com",
},
SecretName: "sooper-secret2",
},
},
namespace: "foo",
},
want: map[string][]string{
"foo/sooper-secret": {"1.example.com", "2.example.com"},
"foo/sooper-secret2": {"3.example.com", "4.example.com"},
},
},
{
args: args{
tlsSections: []networking.IngressTLS{
{
Hosts: []string{
"1.example.com",
},
SecretName: "sooper-secret",
},
{
Hosts: []string{
"3.example.com",
"1.example.com",
"4.example.com",
},
SecretName: "sooper-secret2",
},
},
namespace: "foo",
},
want: map[string][]string{
"foo/sooper-secret": {"1.example.com"},
"foo/sooper-secret2": {"3.example.com", "4.example.com"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := map[string][]string{}
processTLSSections(tt.args.tlsSections, tt.args.namespace, got)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("processTLSSections() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 564c9ac

Please sign in to comment.