/
ingress_rules.go
181 lines (154 loc) · 6.82 KB
/
ingress_rules.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// -------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// --------------------------------------------------------------------------------------------
package appgw
import (
n "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-03-01/network"
networking "k8s.io/api/networking/v1"
"k8s.io/klog/v2"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/annotations"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/environment"
)
func (c *appGwConfigBuilder) getListenersFromIngress(ingress *networking.Ingress, env environment.EnvVariables) map[listenerIdentifier]listenerAzConfig {
listeners := make(map[listenerIdentifier]listenerAzConfig)
// if ingress has only backend configured
if ingress.Spec.DefaultBackend != nil && len(ingress.Spec.Rules) == 0 {
return listeners
}
// process ingress rules with TLS and Waf policy
policy, _ := annotations.WAFPolicy(ingress)
for ruleIdx := range ingress.Spec.Rules {
rule := &ingress.Spec.Rules[ruleIdx]
if rule.HTTP == nil {
continue
}
_, ruleListeners := c.processIngressRuleWithTLS(rule, ingress, env)
applyToListener := false
if policy != "" {
applyToListener = c.applyToListener(rule)
}
for k, v := range ruleListeners {
if applyToListener {
klog.V(3).Infof("Attach WAF policy: %s to listener: %s", policy, generateListenerName(k))
v.FirewallPolicy = policy
}
listeners[k] = v
}
}
return listeners
}
func (c *appGwConfigBuilder) applyToListener(rule *networking.IngressRule) bool {
for pathIdx := range rule.HTTP.Paths {
path := &rule.HTTP.Paths[pathIdx]
// if there is path that is /, /* , empty string, then apply the waf policy to the listener.
if isPathCatchAll(path.Path, path.PathType) {
return true
}
}
return false
}
func (c *appGwConfigBuilder) processIngressRuleWithTLS(rule *networking.IngressRule, ingress *networking.Ingress, env environment.EnvVariables) (map[Port]interface{}, map[listenerIdentifier]listenerAzConfig) {
frontendPorts := make(map[Port]interface{})
// certificate from ingress TLS spec
ingressHostNamesecretIDMap := c.newHostToSecretMap(ingress)
listeners := make(map[listenerIdentifier]listenerAzConfig)
// Override the defaults 80,443 ports use for the listener
overrideFrontendPortFromAnnotation, _ := annotations.OverrideFrontendPort(ingress)
overrideFrontendPortForIngress := Port(overrideFrontendPortFromAnnotation)
// Private IP is used when either annotation use-private-ip or USE_PRIVATE_IP env variable is true.
usePrivateIPFromAnnotation, _ := annotations.UsePrivateIP(ingress)
usePrivateIPForIngress := usePrivateIPFromAnnotation || env.UsePrivateIP
appgwCertName, _ := annotations.GetAppGwSslCertificate(ingress)
if len(appgwCertName) > 0 {
// logging to see the namespace of the ingress annotated with appgw-ssl-certificate
klog.V(5).Infof("Found annotation appgw-ssl-certificate: %s in ingress %s/%s", appgwCertName, ingress.Namespace, ingress.Name)
}
appgwProfileName, _ := annotations.GetAppGwSslProfile(ingress)
if len(appgwProfileName) > 0 {
// logging to see the namespace of the ingress annotated with appgw-ssl-certificate
klog.V(5).Infof("Found annotation appgw-ssl-profile: %s in ingress %s/%s", appgwProfileName, ingress.Namespace, ingress.Name)
}
cert, secID := c.getCertificate(ingress, rule.Host, ingressHostNamesecretIDMap)
hasTLS := (cert != nil || len(appgwCertName) > 0)
sslRedirect, _ := annotations.IsSslRedirect(ingress)
// If a certificate is available we enable only HTTPS; unless ingress is annotated with ssl-redirect - then
// we enable HTTPS as well as HTTP, and redirect HTTP to HTTPS;
if hasTLS {
listenerID := generateListenerID(ingress, rule, n.ApplicationGatewayProtocolHTTPS, &overrideFrontendPortForIngress, usePrivateIPForIngress)
frontendPorts[Port(listenerID.FrontendPort)] = nil
// Only associate the Listener with a Redirect if redirect is enabled
redirect := ""
if sslRedirect {
redirect = generateSSLRedirectConfigurationName(listenerID)
}
azConf := listenerAzConfig{
Protocol: n.ApplicationGatewayProtocolHTTPS,
SslRedirectConfigurationName: redirect,
}
// appgw-ssl-certificate annotation will be ignored if TLS spec found
if cert != nil {
azConf.Secret = *secID
} else if len(appgwCertName) > 0 {
// the cert annotated can be referred across namespace,
// set namespace to "" to ignore namespace
azConf.Secret = secretIdentifier{
Name: appgwCertName,
Namespace: "",
}
}
if len(appgwProfileName) > 0 {
azConf.SslProfile = appgwProfileName
}
listeners[listenerID] = azConf
}
// Enable HTTP only if HTTPS is not configured OR if ingress annotated with 'ssl-redirect'
if sslRedirect || !hasTLS {
listenerID := generateListenerID(ingress, rule, n.ApplicationGatewayProtocolHTTP, &overrideFrontendPortForIngress, usePrivateIPForIngress)
frontendPorts[Port(listenerID.FrontendPort)] = nil
listeners[listenerID] = listenerAzConfig{
Protocol: n.ApplicationGatewayProtocolHTTP,
}
}
return frontendPorts, listeners
}
func (c *appGwConfigBuilder) newBackendIdsFiltered(cbCtx *ConfigBuilderContext) map[backendIdentifier]interface{} {
if c.mem.backendIDs != nil {
return *c.mem.backendIDs
}
backendIDs := make(map[backendIdentifier]interface{})
for _, ingress := range cbCtx.IngressList {
if ingress.Spec.DefaultBackend != nil {
backendID := generateBackendID(ingress, nil, nil, ingress.Spec.DefaultBackend)
klog.V(3).Info("Found default backend:", backendID.serviceKey())
backendIDs[backendID] = nil
}
for ruleIdx := range ingress.Spec.Rules {
rule := &ingress.Spec.Rules[ruleIdx]
if rule.HTTP == nil {
// skip no http rule
klog.V(5).Infof("[%s] Skip rule #%d for host '%s' - it has no HTTP rules.", ingress.Namespace, ruleIdx+1, rule.Host)
continue
}
for pathIdx := range rule.HTTP.Paths {
path := &rule.HTTP.Paths[pathIdx]
backendID := generateBackendID(ingress, rule, path, &path.Backend)
klog.V(5).Info("Found backend:", backendID.serviceKey())
backendIDs[backendID] = nil
}
}
}
finalBackendIDs := make(map[backendIdentifier]interface{})
serviceSet := newServiceSet(&cbCtx.ServiceList)
// Filter out backends, where Ingresses reference non-existent Services
for be := range backendIDs {
if _, exists := serviceSet[be.serviceKey()]; !exists {
klog.Errorf("Ingress %s/%s references non existent Service %s. Please correct the Service section of your Kubernetes YAML", be.Ingress.Namespace, be.Ingress.Name, be.serviceKey())
// TODO(draychev): Enable this filter when we are certain this won't break anything!
// continue
}
finalBackendIDs[be] = nil
}
c.mem.backendIDs = &finalBackendIDs
return finalBackendIDs
}