mirror of
https://github.com/traefik/traefik
synced 2026-02-03 11:10:33 +00:00
Add support for app-root nginx annotation
This commit is contained in:
@@ -316,6 +316,7 @@ The following annotations are organized by category for easier navigation.
|
|||||||
|
|
||||||
| Annotation | Limitations / Notes |
|
| Annotation | Limitations / Notes |
|
||||||
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||||
|
| <a id="opt-nginx-ingress-kubernetes-ioapp-root" href="#opt-nginx-ingress-kubernetes-ioapp-root" title="#opt-nginx-ingress-kubernetes-ioapp-root">`nginx.ingress.kubernetes.io/app-root`</a> | |
|
||||||
| <a id="opt-nginx-ingress-kubernetes-iouse-regex" href="#opt-nginx-ingress-kubernetes-iouse-regex" title="#opt-nginx-ingress-kubernetes-iouse-regex">`nginx.ingress.kubernetes.io/use-regex`</a> | |
|
| <a id="opt-nginx-ingress-kubernetes-iouse-regex" href="#opt-nginx-ingress-kubernetes-iouse-regex" title="#opt-nginx-ingress-kubernetes-iouse-regex">`nginx.ingress.kubernetes.io/use-regex`</a> | |
|
||||||
| <a id="opt-nginx-ingress-kubernetes-iorewrite-target" href="#opt-nginx-ingress-kubernetes-iorewrite-target" title="#opt-nginx-ingress-kubernetes-iorewrite-target">`nginx.ingress.kubernetes.io/rewrite-target`</a> | |
|
| <a id="opt-nginx-ingress-kubernetes-iorewrite-target" href="#opt-nginx-ingress-kubernetes-iorewrite-target" title="#opt-nginx-ingress-kubernetes-iorewrite-target">`nginx.ingress.kubernetes.io/rewrite-target`</a> | |
|
||||||
| <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect">`nginx.ingress.kubernetes.io/permanent-redirect`</a> | Defaults to a 301 Moved Permanently status code. |
|
| <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect">`nginx.ingress.kubernetes.io/permanent-redirect`</a> | Defaults to a 301 Moved Permanently status code. |
|
||||||
@@ -356,7 +357,6 @@ The following annotations are organized by category for easier navigation.
|
|||||||
|
|
||||||
| Annotation | Notes |
|
| Annotation | Notes |
|
||||||
|-----------------------------------------------------------------------------|------------------------------------------------------|
|
|-----------------------------------------------------------------------------|------------------------------------------------------|
|
||||||
| <a id="opt-nginx-ingress-kubernetes-ioapp-root" href="#opt-nginx-ingress-kubernetes-ioapp-root" title="#opt-nginx-ingress-kubernetes-ioapp-root">`nginx.ingress.kubernetes.io/app-root`</a> | |
|
|
||||||
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | |
|
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | |
|
||||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-signin" href="#opt-nginx-ingress-kubernetes-ioauth-signin" title="#opt-nginx-ingress-kubernetes-ioauth-signin">`nginx.ingress.kubernetes.io/auth-signin`</a> | |
|
| <a id="opt-nginx-ingress-kubernetes-ioauth-signin" href="#opt-nginx-ingress-kubernetes-ioauth-signin" title="#opt-nginx-ingress-kubernetes-ioauth-signin">`nginx.ingress.kubernetes.io/auth-signin`</a> | |
|
||||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | |
|
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | |
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type ingressConfig struct {
|
|||||||
|
|
||||||
UseRegex *bool `annotation:"nginx.ingress.kubernetes.io/use-regex"`
|
UseRegex *bool `annotation:"nginx.ingress.kubernetes.io/use-regex"`
|
||||||
RewriteTarget *string `annotation:"nginx.ingress.kubernetes.io/rewrite-target"`
|
RewriteTarget *string `annotation:"nginx.ingress.kubernetes.io/rewrite-target"`
|
||||||
|
AppRoot *string `annotation:"nginx.ingress.kubernetes.io/app-root"`
|
||||||
|
|
||||||
PermanentRedirect *string `annotation:"nginx.ingress.kubernetes.io/permanent-redirect"`
|
PermanentRedirect *string `annotation:"nginx.ingress.kubernetes.io/permanent-redirect"`
|
||||||
PermanentRedirectCode *int `annotation:"nginx.ingress.kubernetes.io/permanent-redirect-code"`
|
PermanentRedirectCode *int `annotation:"nginx.ingress.kubernetes.io/permanent-redirect-code"`
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: ingress-with-app-root
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/app-root: foo
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: app-root.localhost
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: whoami
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: ingress-with-app-root
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/app-root: /foo
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: app-root.localhost
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: whoami
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -793,6 +793,12 @@ func (p *Provider) loadCertificates(ctx context.Context, ingress *netv1.Ingress,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) applyMiddlewares(namespace, routerKey, rulePath string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) error {
|
func (p *Provider) applyMiddlewares(namespace, routerKey, rulePath string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) error {
|
||||||
|
applyAppRootConfiguration(routerKey, ingressConfig, rt, conf)
|
||||||
|
|
||||||
|
// Apply SSL redirect is mandatory to be applied after all other middlewares.
|
||||||
|
// TODO: check how to remove this, and create the HTTP router elsewhere.
|
||||||
|
p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
|
||||||
|
|
||||||
if err := p.applyBasicAuthConfiguration(namespace, routerKey, ingressConfig, rt, conf); err != nil {
|
if err := p.applyBasicAuthConfiguration(namespace, routerKey, ingressConfig, rt, conf); err != nil {
|
||||||
return fmt.Errorf("applying basic auth configuration: %w", err)
|
return fmt.Errorf("applying basic auth configuration: %w", err)
|
||||||
}
|
}
|
||||||
@@ -807,10 +813,6 @@ func (p *Provider) applyMiddlewares(namespace, routerKey, rulePath string, ingre
|
|||||||
|
|
||||||
applyRewriteTargetConfiguration(rulePath, routerKey, ingressConfig, rt, conf)
|
applyRewriteTargetConfiguration(rulePath, routerKey, ingressConfig, rt, conf)
|
||||||
|
|
||||||
// Apply SSL redirect is mandatory to be applied after all other middlewares.
|
|
||||||
// TODO: check how to remove this, and create the HTTP router elsewhere.
|
|
||||||
p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
|
|
||||||
|
|
||||||
applyRedirect(routerKey, ingressConfig, rt, conf)
|
applyRedirect(routerKey, ingressConfig, rt, conf)
|
||||||
|
|
||||||
applyUpstreamVhost(routerKey, ingressConfig, rt, conf)
|
applyUpstreamVhost(routerKey, ingressConfig, rt, conf)
|
||||||
@@ -910,6 +912,22 @@ func applyRewriteTargetConfiguration(rulePath, routerName string, ingressConfig
|
|||||||
rt.Middlewares = append(rt.Middlewares, rewriteTargetMiddlewareName)
|
rt.Middlewares = append(rt.Middlewares, rewriteTargetMiddlewareName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyAppRootConfiguration(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) {
|
||||||
|
if ingressConfig.AppRoot == nil || !strings.HasPrefix(*ingressConfig.AppRoot, "/") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
appRootMiddlewareName := routerName + "-app-root"
|
||||||
|
conf.HTTP.Middlewares[appRootMiddlewareName] = &dynamic.Middleware{
|
||||||
|
RedirectRegex: &dynamic.RedirectRegex{
|
||||||
|
Regex: `^(https?://[^/]+)/$`,
|
||||||
|
Replacement: "$1" + *ingressConfig.AppRoot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.Middlewares = append(rt.Middlewares, appRootMiddlewareName)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) applyBasicAuthConfiguration(namespace, routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error {
|
func (p *Provider) applyBasicAuthConfiguration(namespace, routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error {
|
||||||
if ingressConfig.AuthType == nil {
|
if ingressConfig.AuthType == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -1125,7 +1143,7 @@ func (p *Provider) applySSLRedirectConfiguration(routerName string, ingressConfi
|
|||||||
ForcePermanentRedirect: true,
|
ForcePermanentRedirect: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rt.Middlewares = append([]string{redirectMiddlewareName}, rt.Middlewares...)
|
rt.Middlewares = append(rt.Middlewares, redirectMiddlewareName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Ingress that is not forcing sslRedirect and has no TLS configuration does not redirect,
|
// An Ingress that is not forcing sslRedirect and has no TLS configuration does not redirect,
|
||||||
|
|||||||
@@ -809,6 +809,104 @@ func TestLoadIngresses(t *testing.T) {
|
|||||||
TLS: &dynamic.TLSConfiguration{},
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "App Root",
|
||||||
|
paths: []string{
|
||||||
|
"services.yml",
|
||||||
|
"ingressclasses.yml",
|
||||||
|
"ingresses/18-ingress-with-app-root.yml",
|
||||||
|
},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"default-ingress-with-app-root-rule-0-path-0": {
|
||||||
|
Rule: "Host(`app-root.localhost`) && (Path(`/bar`) || PathPrefix(`/bar/`))",
|
||||||
|
RuleSyntax: "default",
|
||||||
|
Service: "default-ingress-with-app-root-whoami-80",
|
||||||
|
Middlewares: []string{"default-ingress-with-app-root-rule-0-path-0-app-root"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{
|
||||||
|
"default-ingress-with-app-root-rule-0-path-0-app-root": {
|
||||||
|
RedirectRegex: &dynamic.RedirectRegex{
|
||||||
|
Regex: `^(https?://[^/]+)/$`,
|
||||||
|
Replacement: "$1/foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"default-ingress-with-app-root-whoami-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Strategy: "wrr",
|
||||||
|
PassHostHeader: ptr.To(true),
|
||||||
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
FlushInterval: dynamic.DefaultFlushInterval,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "App Root - no prefix slash",
|
||||||
|
paths: []string{
|
||||||
|
"services.yml",
|
||||||
|
"ingressclasses.yml",
|
||||||
|
"ingresses/18-ingress-with-app-root-wrong.yml",
|
||||||
|
},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"default-ingress-with-app-root-rule-0-path-0": {
|
||||||
|
Rule: "Host(`app-root.localhost`) && (Path(`/bar`) || PathPrefix(`/bar/`))",
|
||||||
|
RuleSyntax: "default",
|
||||||
|
Service: "default-ingress-with-app-root-whoami-80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"default-ingress-with-app-root-whoami-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Strategy: "wrr",
|
||||||
|
PassHostHeader: ptr.To(true),
|
||||||
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
FlushInterval: dynamic.DefaultFlushInterval,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "Default Backend",
|
desc: "Default Backend",
|
||||||
defaultBackendServiceName: "whoami",
|
defaultBackendServiceName: "whoami",
|
||||||
|
|||||||
Reference in New Issue
Block a user