Skip to content

Commit b56e1cb

Browse files
committed
MINOR: switch CORS rules to http-after-response
1 parent 1b138f3 commit b56e1cb

File tree

8 files changed

+130
-55
lines changed

8 files changed

+130
-55
lines changed

.aspell.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ allowed:
4040
- configmaps
4141
- namespace
4242
- namespaces
43+
- http
44+
- CORS

pkg/annotations/annotations.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ func (a annImpl) Frontend(i *store.Ingress, r *rules.List, m maps.Maps) []Annota
141141
resSetCORS.NewAnnotation("cors-allow-headers"),
142142
resSetCORS.NewAnnotation("cors-max-age"),
143143
resSetCORS.NewAnnotation("cors-allow-credentials"),
144+
resSetCORS.NewAnnotation("cors-respond-to-options"),
144145
}
145146
}
146147

@@ -248,34 +249,35 @@ func Timeout(name string, annotations ...map[string]string) (out *int64, err err
248249
// SpecificAnnotations is a set of annotations that uses rules to produce specific configuration with rule ID in configuration file.
249250
// These annotations in an ingress can't be merged with other ingresses annotations when these ingresses point to the same service because specific paths must be treated specifically.
250251
var SpecificAnnotations = map[string]struct{}{
251-
"backend-config-snippet": {},
252-
"deny-list": {},
253-
"blacklist": {},
254-
"allow-list": {},
255-
"whitelist": {},
256-
"src-ip-header": {},
257-
"auth-type": {},
258-
"auth-realm": {},
259-
"auth-secret": {},
260-
"ssl-redirect": {},
261-
"ssl-redirect-port": {},
262-
"ssl-redirect-code": {},
263-
"request-redirect": {},
264-
"request-redirect-code": {},
265-
"request-capture": {},
266-
"request-capture-len": {},
267-
"path-rewrite": {},
268-
"rate-limit-requests": {},
269-
"rate-limit-period": {},
270-
"rate-limit-size": {},
271-
"rate-limit-status-code": {},
272-
"request-set-header": {},
273-
"response-set-header": {},
274-
"set-host": {},
275-
"cors-enable": {},
276-
"cors-allow-origin": {},
277-
"cors-allow-methods": {},
278-
"cors-allow-headers": {},
279-
"cors-max-age": {},
280-
"cors-allow-credentials": {},
252+
"backend-config-snippet": {},
253+
"deny-list": {},
254+
"blacklist": {},
255+
"allow-list": {},
256+
"whitelist": {},
257+
"src-ip-header": {},
258+
"auth-type": {},
259+
"auth-realm": {},
260+
"auth-secret": {},
261+
"ssl-redirect": {},
262+
"ssl-redirect-port": {},
263+
"ssl-redirect-code": {},
264+
"request-redirect": {},
265+
"request-redirect-code": {},
266+
"request-capture": {},
267+
"request-capture-len": {},
268+
"path-rewrite": {},
269+
"rate-limit-requests": {},
270+
"rate-limit-period": {},
271+
"rate-limit-size": {},
272+
"rate-limit-status-code": {},
273+
"request-set-header": {},
274+
"response-set-header": {},
275+
"set-host": {},
276+
"cors-enable": {},
277+
"cors-allow-origin": {},
278+
"cors-allow-methods": {},
279+
"cors-allow-headers": {},
280+
"cors-max-age": {},
281+
"cors-allow-credentials": {},
282+
"cors-respond-to-options": {},
281283
}

pkg/annotations/ingress/resSetCORS.go

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ func (a ResSetCORSAnn) Process(k store.K8s, annotations ...map[string]string) (e
7474
origin = "%[var(txn." + corsVarName + ")]"
7575
}
7676
a.parent.rules.Add(&rules.SetHdr{
77-
HdrName: "Access-Control-Allow-Origin",
78-
HdrFormat: origin,
79-
Response: true,
80-
CondTest: a.parent.acl,
81-
Cond: "if",
77+
HdrName: "Access-Control-Allow-Origin",
78+
HdrFormat: origin,
79+
AfterResponse: true,
80+
CondTest: a.parent.acl,
81+
Cond: "if",
8282
})
8383
case "cors-allow-methods":
8484
if a.parent.acl == "" {
@@ -96,23 +96,23 @@ func (a ResSetCORSAnn) Process(k store.K8s, annotations ...map[string]string) (e
9696
input = "\"" + strings.Join(methods, ", ") + "\""
9797
}
9898
a.parent.rules.Add(&rules.SetHdr{
99-
HdrName: "Access-Control-Allow-Methods",
100-
HdrFormat: input,
101-
Response: true,
102-
CondTest: a.parent.acl,
103-
Cond: "if",
99+
HdrName: "Access-Control-Allow-Methods",
100+
HdrFormat: input,
101+
AfterResponse: true,
102+
CondTest: a.parent.acl,
103+
Cond: "if",
104104
})
105105
case "cors-allow-headers":
106106
if a.parent.acl == "" {
107107
return
108108
}
109109
input = strings.Join(strings.Fields(input), "") // strip spaces
110110
a.parent.rules.Add(rules.SetHdr{
111-
HdrName: "Access-Control-Allow-Headers",
112-
HdrFormat: "\"" + input + "\"",
113-
Response: true,
114-
CondTest: a.parent.acl,
115-
Cond: "if",
111+
HdrName: "Access-Control-Allow-Headers",
112+
HdrFormat: "\"" + input + "\"",
113+
AfterResponse: true,
114+
CondTest: a.parent.acl,
115+
Cond: "if",
116116
})
117117
case "cors-max-age":
118118
if a.parent.acl == "" {
@@ -128,22 +128,29 @@ func (a ResSetCORSAnn) Process(k store.K8s, annotations ...map[string]string) (e
128128
return fmt.Errorf("invalid cors-max-age value %d", maxage)
129129
}
130130
a.parent.rules.Add(&rules.SetHdr{
131-
HdrName: "Access-Control-Max-Age",
132-
HdrFormat: fmt.Sprintf("\"%d\"", maxage),
133-
Response: true,
134-
CondTest: a.parent.acl,
135-
Cond: "if",
131+
HdrName: "Access-Control-Max-Age",
132+
HdrFormat: fmt.Sprintf("\"%d\"", maxage),
133+
AfterResponse: true,
134+
CondTest: a.parent.acl,
135+
Cond: "if",
136136
})
137137
case "cors-allow-credentials":
138138
if a.parent.acl == "" || input != "true" {
139139
return
140140
}
141141
a.parent.rules.Add(&rules.SetHdr{
142-
HdrName: "Access-Control-Allow-Credentials",
143-
HdrFormat: "\"true\"",
144-
Response: true,
145-
CondTest: a.parent.acl,
146-
Cond: "if",
142+
HdrName: "Access-Control-Allow-Credentials",
143+
HdrFormat: "\"true\"",
144+
AfterResponse: true,
145+
CondTest: a.parent.acl,
146+
Cond: "if",
147+
})
148+
case "cors-respond-to-options":
149+
if a.parent.acl == "" || input != "true" {
150+
return
151+
}
152+
a.parent.rules.Add(&rules.ReqReturnStatus{
153+
StatusCode: 204,
147154
})
148155
default:
149156
err = fmt.Errorf("unknown cors annotation '%s'", a.name)

pkg/haproxy/api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ type HTTPRequestRule interface {
169169
HTTPRequestRulesReplace(parentType, parentName string, rules models.HTTPRequestRules) error
170170
FrontendHTTPRequestRuleCreate(id int64, frontend string, rule models.HTTPRequestRule, ingressACL string) error
171171
FrontendHTTPResponseRuleCreate(id int64, frontend string, rule models.HTTPResponseRule, ingressACL string) error
172+
FrontendHTTPAfterResponseRuleCreate(id int64, frontend string, rule models.HTTPAfterResponseRule, ingressACL string) error
172173
BackendHTTPRequestRuleCreate(id int64, backend string, rule models.HTTPRequestRule) error
173174
}
174175

pkg/haproxy/api/frontend.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,12 @@ func (c *clientNative) FrontendRuleDeleteAll(frontend string) {
221221
break
222222
}
223223
}
224+
for {
225+
err := configuration.DeleteHTTPAfterResponseRule(0, string(parser.Frontends), frontend, c.activeTransaction, 0)
226+
if err != nil {
227+
break
228+
}
229+
}
224230
// No usage of TCPResponseRules yet.
225231
}
226232

@@ -251,3 +257,15 @@ func (c *clientNative) PeerEntryDelete(peerSection, entry string) error {
251257
}
252258
return cfg.DeletePeerEntry(entry, peerSection, c.activeTransaction, 0)
253259
}
260+
261+
func (c *clientNative) FrontendHTTPAfterResponseRuleCreate(id int64, frontend string, rule models.HTTPAfterResponseRule, ingressACL string) error {
262+
configuration, err := c.nativeAPI.Configuration()
263+
if err != nil {
264+
return err
265+
}
266+
if ingressACL != "" {
267+
rule.Cond = "if"
268+
rule.CondTest = fmt.Sprintf("%s %s", ingressACL, rule.CondTest)
269+
}
270+
return configuration.CreateHTTPAfterResponseRule(id, string(parser.Frontends), frontend, &rule, c.activeTransaction, 0)
271+
}

pkg/haproxy/rules/reqReturnStatus.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package rules
2+
3+
import (
4+
"errors"
5+
6+
"github.com/haproxytech/client-native/v6/models"
7+
"github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api"
8+
)
9+
10+
//nolint:golint,stylecheck
11+
var MIME_TYPE_TEXT_PLAIN string = "text/plain"
12+
13+
type ReqReturnStatus struct {
14+
StatusCode int64
15+
}
16+
17+
func (r ReqReturnStatus) GetType() Type {
18+
return REQ_RETURN_STATUS
19+
}
20+
21+
func (r ReqReturnStatus) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error {
22+
if frontend.Mode == "tcp" {
23+
return errors.New("HTTP status cannot be set in TCP mode")
24+
}
25+
httpRule := models.HTTPRequestRule{
26+
ReturnStatusCode: &r.StatusCode,
27+
Type: "return",
28+
}
29+
ingressACL += " METH_OPTIONS"
30+
return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL)
31+
}

pkg/haproxy/rules/setHdr.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type SetHdr struct {
1515
Cond string
1616
Type Type
1717
Response bool
18+
AfterResponse bool
1819
ForwardedProto bool
1920
}
2021

@@ -52,6 +53,17 @@ func (r SetHdr) Create(client api.HAProxyClient, frontend *models.Frontend, ingr
5253
}
5354
return client.FrontendHTTPResponseRuleCreate(0, frontend.Name, httpRule, ingressACL)
5455
}
56+
57+
if r.AfterResponse {
58+
httpRule := models.HTTPAfterResponseRule{
59+
Type: "set-header",
60+
HdrName: r.HdrName,
61+
HdrFormat: r.HdrFormat,
62+
CondTest: r.CondTest,
63+
Cond: r.Cond,
64+
}
65+
return client.FrontendHTTPAfterResponseRuleCreate(0, frontend.Name, httpRule, ingressACL)
66+
}
5567
// REQ_SET_HEADER
5668
httpRule := models.HTTPRequestRule{
5769
Type: "set-header",

pkg/haproxy/rules/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
REQ_SET_HEADER
2222
REQ_SET_HOST
2323
REQ_PATH_REWRITE
24+
REQ_RETURN_STATUS
2425
RES_SET_HEADER
2526
)
2627

@@ -41,4 +42,5 @@ var constLookup = map[Type]string{
4142
REQ_SET_HOST: "REQ_SET_HOST",
4243
REQ_PATH_REWRITE: "REQ_PATH_REWRITE",
4344
RES_SET_HEADER: "RES_SET_HEADER",
45+
REQ_RETURN_STATUS: "REQ_RETURN_STATUS",
4446
}

0 commit comments

Comments
 (0)