Skip to content

Commit 24450ea

Browse files
lou-lanrikatz
andauthored
Add custom code handling for temporal redirect (#10651)
Co-authored-by: Ricardo Katz <[email protected]>
1 parent ffee96c commit 24450ea

File tree

4 files changed

+70
-6
lines changed

4 files changed

+70
-6
lines changed

docs/user-guide/nginx-configuration/annotations-risk.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
| Redirect | permanent-redirect | Medium | location |
109109
| Redirect | permanent-redirect-code | Low | location |
110110
| Redirect | temporal-redirect | Medium | location |
111+
| Redirect | temporal-redirect-code | Low | location |
111112
| Rewrite | app-root | Medium | location |
112113
| Rewrite | force-ssl-redirect | Medium | location |
113114
| Rewrite | preserve-trailing-slash | Medium | location |

docs/user-guide/nginx-configuration/annotations.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
7171
|[nginx.ingress.kubernetes.io/permanent-redirect](#permanent-redirect)|string|
7272
|[nginx.ingress.kubernetes.io/permanent-redirect-code](#permanent-redirect-code)|number|
7373
|[nginx.ingress.kubernetes.io/temporal-redirect](#temporal-redirect)|string|
74+
|[nginx.ingress.kubernetes.io/temporal-redirect-code](#temporal-redirect-code)|number|
7475
|[nginx.ingress.kubernetes.io/preserve-trailing-slash](#server-side-https-enforcement-through-redirect)|"true" or "false"|
7576
|[nginx.ingress.kubernetes.io/proxy-body-size](#custom-max-body-size)|string|
7677
|[nginx.ingress.kubernetes.io/proxy-cookie-domain](#proxy-cookie-domain)|string|
@@ -610,6 +611,10 @@ This annotation allows you to modify the status code used for permanent redirect
610611
### Temporal Redirect
611612
This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream. For example `nginx.ingress.kubernetes.io/temporal-redirect: https://www.google.com` would redirect everything to Google with a Return Code of 302 (Moved Temporarily)
612613

614+
### Temporal Redirect Code
615+
616+
This annotation allows you to modify the status code used for temporal redirects. For example `nginx.ingress.kubernetes.io/temporal-redirect-code: '307'` would return your temporal-redirect with a 307.
617+
613618
### SSL Passthrough
614619
615620
The annotation `nginx.ingress.kubernetes.io/ssl-passthrough` instructs the controller to send TLS connections directly

internal/ingress/annotations/redirect/redirect.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ import (
2828
"k8s.io/ingress-nginx/internal/ingress/resolver"
2929
)
3030

31-
const defaultPermanentRedirectCode = http.StatusMovedPermanently
31+
const (
32+
defaultPermanentRedirectCode = http.StatusMovedPermanently
33+
defaultTemporalRedirectCode = http.StatusFound
34+
)
3235

3336
// Config returns the redirect configuration for an Ingress rule
3437
type Config struct {
@@ -40,6 +43,7 @@ type Config struct {
4043
const (
4144
fromToWWWRedirAnnotation = "from-to-www-redirect"
4245
temporalRedirectAnnotation = "temporal-redirect"
46+
temporalRedirectAnnotationCode = "temporal-redirect-code"
4347
permanentRedirectAnnotation = "permanent-redirect"
4448
permanentRedirectAnnotationCode = "permanent-redirect-code"
4549
)
@@ -60,6 +64,12 @@ var redirectAnnotations = parser.Annotation{
6064
Documentation: `This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream.
6165
For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`,
6266
},
67+
temporalRedirectAnnotationCode: {
68+
Validator: parser.ValidateInt,
69+
Scope: parser.AnnotationScopeLocation,
70+
Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options
71+
Documentation: `This annotation allows you to modify the status code used for temporal redirects.`,
72+
},
6373
permanentRedirectAnnotation: {
6474
Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
6575
Scope: parser.AnnotationScopeLocation,
@@ -105,13 +115,22 @@ func (r redirect) Parse(ing *networking.Ingress) (interface{}, error) {
105115
}
106116

107117
if tr != "" {
118+
trc, err := parser.GetIntAnnotation(temporalRedirectAnnotationCode, ing, r.annotationConfig.Annotations)
119+
if err != nil && !errors.IsMissingAnnotations(err) {
120+
return nil, err
121+
}
122+
123+
if trc < http.StatusMultipleChoices || trc > http.StatusTemporaryRedirect {
124+
trc = defaultTemporalRedirectCode
125+
}
126+
108127
if err := isValidURL(tr); err != nil {
109128
return nil, err
110129
}
111130

112131
return &Config{
113132
URL: tr,
114-
Code: http.StatusFound,
133+
Code: trc,
115134
FromToWWW: r3w,
116135
}, nil
117136
}

internal/ingress/annotations/redirect/redirect_test.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestPermanentRedirectWithCustomCode(t *testing.T) {
103103
}
104104
}
105105

106-
func TestTemporalRedirect(t *testing.T) {
106+
func TestTemporalRedirectWithDefaultCode(t *testing.T) {
107107
rp := NewParser(resolver.Mock{})
108108
if rp == nil {
109109
t.Fatalf("Expected a parser.IngressAnnotation but returned nil")
@@ -128,10 +128,49 @@ func TestTemporalRedirect(t *testing.T) {
128128
t.Errorf("Expected %v as redirect but returned %s", defRedirectURL, redirect.URL)
129129
}
130130
if redirect.Code != http.StatusFound {
131-
t.Errorf("Expected %v as redirect to have a code %d but had %d", defRedirectURL, defaultPermanentRedirectCode, redirect.Code)
131+
t.Errorf("Expected %v as redirect to have a code %d but had %d", defRedirectURL, http.StatusFound, redirect.Code)
132+
}
133+
}
134+
135+
func TestTemporalRedirectWithCustomCode(t *testing.T) {
136+
rp := NewParser(resolver.Mock{})
137+
if rp == nil {
138+
t.Fatalf("Expected a parser.IngressAnnotation but returned nil")
139+
}
140+
141+
testCases := map[string]struct {
142+
input int
143+
expectOutput int
144+
}{
145+
"valid code": {http.StatusTemporaryRedirect, http.StatusTemporaryRedirect},
146+
"invalid code": {http.StatusTeapot, http.StatusFound},
132147
}
133-
if redirect.FromToWWW != true {
134-
t.Errorf("Expected %v as redirect to have from-to-www as %v but got %v", defRedirectURL, true, redirect.FromToWWW)
148+
149+
for n, tc := range testCases {
150+
t.Run(n, func(t *testing.T) {
151+
ing := new(networking.Ingress)
152+
153+
data := make(map[string]string, 2)
154+
data[parser.GetAnnotationWithPrefix(fromToWWWRedirAnnotation)] = "true"
155+
data[parser.GetAnnotationWithPrefix(temporalRedirectAnnotation)] = defRedirectURL
156+
data[parser.GetAnnotationWithPrefix(temporalRedirectAnnotationCode)] = strconv.Itoa(tc.input)
157+
ing.SetAnnotations(data)
158+
159+
i, err := rp.Parse(ing)
160+
if err != nil {
161+
t.Errorf("Unexpected error with ingress: %v", err)
162+
}
163+
redirect, ok := i.(*Config)
164+
if !ok {
165+
t.Errorf("Expected a Redirect type")
166+
}
167+
if redirect.URL != defRedirectURL {
168+
t.Errorf("Expected %v as redirect but returned %s", defRedirectURL, redirect.URL)
169+
}
170+
if redirect.Code != tc.expectOutput {
171+
t.Errorf("Expected %v as redirect to have a code %d but had %d", defRedirectURL, tc.expectOutput, redirect.Code)
172+
}
173+
})
135174
}
136175
}
137176

0 commit comments

Comments
 (0)