Merge pull request #1123 from diazjf/server-alias
Add support for Server Alias in Nginx
This commit is contained in:
commit
f6413e292f
9 changed files with 360 additions and 206 deletions
|
@ -65,7 +65,7 @@ The following annotations are supported:
|
|||
|[ingress.kubernetes.io/upstream-max-fails](#custom-nginx-upstream-checks)|number|
|
||||
|[ingress.kubernetes.io/upstream-fail-timeout](#custom-nginx-upstream-checks)|number|
|
||||
|[ingress.kubernetes.io/whitelist-source-range](#whitelist-source-range)|CIDR|
|
||||
|
||||
|[ingress.kubernetes.io/server-alias](#server-alias)|string|
|
||||
|
||||
|
||||
#### Custom NGINX template
|
||||
|
@ -156,7 +156,7 @@ Please check the [tls-auth](/examples/auth/client-certs/nginx/README.md) example
|
|||
|
||||
### Configuration snippet
|
||||
|
||||
Using this annotion you can add additional configuration to the NGINX location. For example:
|
||||
Using this annotation you can add additional configuration to the NGINX location. For example:
|
||||
|
||||
```
|
||||
ingress.kubernetes.io/configuration-snippet: |
|
||||
|
@ -168,6 +168,13 @@ ingress.kubernetes.io/configuration-snippet: |
|
|||
To enable Cross-Origin Resource Sharing (CORS) in an Ingress rule add the annotation `ingress.kubernetes.io/enable-cors: "true"`. This will add a section in the server location enabling this functionality.
|
||||
For more information please check https://enable-cors.org/server_nginx.html
|
||||
|
||||
### Server Alias
|
||||
|
||||
To add Server Aliases to an Ingress rule add the annotation `ingress.kubernetes.io/server-alias: "<alias>"`.
|
||||
This will create a server with the same configuration, but a different server_name as the provided host.
|
||||
|
||||
For more information please see http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
|
||||
|
||||
### External Authentication
|
||||
|
||||
To use an existing service that provides authentication the Ingress rule can be annotated with `ingress.kubernetes.io/auth-url` to indicate the URL where the HTTP request should be sent.
|
||||
|
|
|
@ -446,6 +446,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
|||
IP: svc.Spec.ClusterIP,
|
||||
Port: port,
|
||||
ProxyProtocol: false,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,9 @@ var (
|
|||
"toLower": strings.ToLower,
|
||||
"formatIP": formatIP,
|
||||
"buildNextUpstream": buildNextUpstream,
|
||||
"serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} {
|
||||
return struct { First, Second interface{} } { all, server }
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{ $all := . }}
|
||||
{{ $servers := .Servers }}
|
||||
{{ $cfg := .Cfg }}
|
||||
{{ $IsIPV6Enabled := .IsIPV6Enabled }}
|
||||
{{ $healthzURI := .HealthzURI }}
|
||||
|
@ -266,7 +267,7 @@ http {
|
|||
{{ end }}
|
||||
|
||||
{{/* build the maps that will be use to validate the Whitelist */}}
|
||||
{{ range $index, $server := .Servers }}
|
||||
{{ range $index, $server := $servers }}
|
||||
{{ range $location := $server.Locations }}
|
||||
{{ $path := buildLocation $location }}
|
||||
|
||||
|
@ -285,215 +286,24 @@ http {
|
|||
|
||||
{{/* build all the required rate limit zones. Each annotation requires a dedicated zone */}}
|
||||
{{/* 1MB -> 16 thousand 64-byte states or about 8 thousand 128-byte states */}}
|
||||
{{ range $zone := (buildRateLimitZones $cfg.LimitConnZoneVariable .Servers) }}
|
||||
{{ range $zone := (buildRateLimitZones $cfg.LimitConnZoneVariable $servers) }}
|
||||
{{ $zone }}
|
||||
{{ end }}
|
||||
|
||||
{{ $backlogSize := .BacklogSize }}
|
||||
{{ range $index, $server := .Servers }}
|
||||
{{ range $index, $server := $servers }}
|
||||
server {
|
||||
server_name {{ $server.Hostname }};
|
||||
listen 80{{ if $cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{end}};
|
||||
{{ if $IsIPV6Enabled }}listen [::]:80{{ if $cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{ end }};{{ end }}
|
||||
set $proxy_upstream_name "-";
|
||||
|
||||
{{/* Listen on 442 because port 443 is used in the TLS sni server */}}
|
||||
{{/* This listener must always have proxy_protocol enabled, because the SNI listener forwards on source IP info in it. */}}
|
||||
{{ if not (empty $server.SSLCertificate) }}listen 442 proxy_protocol{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{end}} ssl {{ if $cfg.UseHTTP2 }}http2{{ end }};
|
||||
{{ if $IsIPV6Enabled }}{{ if not (empty $server.SSLCertificate) }}listen [::]:442 proxy_protocol{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{end}} ssl {{ if $cfg.UseHTTP2 }}http2{{ end }};{{ end }}
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $server.SSLPemChecksum }}
|
||||
ssl_certificate {{ $server.SSLCertificate }};
|
||||
ssl_certificate_key {{ $server.SSLCertificate }};
|
||||
{{ end }}
|
||||
|
||||
{{ if (and (not (empty $server.SSLCertificate)) $cfg.HSTS) }}
|
||||
more_set_headers "Strict-Transport-Security: max-age={{ $cfg.HSTSMaxAge }}{{ if $cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }};{{ if $cfg.HSTSPreload }} preload{{ end }}";
|
||||
{{ end }}
|
||||
|
||||
{{ if $cfg.EnableVtsStatus }}vhost_traffic_status_filter_by_set_key $geoip_country_code country::$server_name;{{ end }}
|
||||
|
||||
{{ range $location := $server.Locations }}
|
||||
{{ $path := buildLocation $location }}
|
||||
{{ $authPath := buildAuthLocation $location }}
|
||||
|
||||
{{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }}
|
||||
# PEM sha: {{ $location.CertificateAuth.AuthSSLCert.PemSHA }}
|
||||
ssl_client_certificate {{ $location.CertificateAuth.AuthSSLCert.CAFileName }};
|
||||
ssl_verify_client on;
|
||||
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $location.Redirect.AppRoot)}}
|
||||
if ($uri = /) {
|
||||
return 302 {{ $location.Redirect.AppRoot }};
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $authPath) }}
|
||||
location = {{ $authPath }} {
|
||||
internal;
|
||||
set $proxy_upstream_name "internal";
|
||||
|
||||
{{ if not $location.ExternalAuth.SendBody }}
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
{{ end }}
|
||||
{{ if not (empty $location.ExternalAuth.Method) }}
|
||||
proxy_method {{ $location.ExternalAuth.Method }};
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Scheme $pass_access_scheme;
|
||||
{{ end }}
|
||||
proxy_pass_request_headers on;
|
||||
proxy_set_header Host {{ $location.ExternalAuth.Host }};
|
||||
proxy_ssl_server_name on;
|
||||
|
||||
client_max_body_size "{{ $location.Proxy.BodySize }}";
|
||||
|
||||
set $target {{ $location.ExternalAuth.URL }};
|
||||
proxy_pass $target;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
location {{ $path }} {
|
||||
set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $backends $location }}";
|
||||
|
||||
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
|
||||
# enforce ssl on server side
|
||||
if ($pass_access_scheme = http) {
|
||||
return 301 https://$best_http_host$request_uri;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if isLocationAllowed $location }}
|
||||
{{ if gt (len $location.Whitelist.CIDR) 0 }}
|
||||
if ({{ buildDenyVariable (print $server.Hostname "_" $path) }}) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }};
|
||||
|
||||
{{ if not (empty $authPath) }}
|
||||
# this location requires authentication
|
||||
auth_request {{ $authPath }};
|
||||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
{{- range $idx, $line := buildAuthResponseHeaders $location }}
|
||||
{{ $line }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $location.ExternalAuth.SigninURL) }}
|
||||
error_page 401 = {{ $location.ExternalAuth.SigninURL }}?rd=$request_uri;
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{/* if the location contains a rate limit annotation, create one */}}
|
||||
{{ $limits := buildRateLimit $location }}
|
||||
{{ range $limit := $limits }}
|
||||
{{ $limit }}{{ end }}
|
||||
|
||||
{{ if $location.BasicDigestAuth.Secured }}
|
||||
{{ if eq $location.BasicDigestAuth.Type "basic" }}
|
||||
auth_basic "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_basic_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ else }}
|
||||
auth_digest "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_digest_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ end }}
|
||||
proxy_set_header Authorization "";
|
||||
{{ end }}
|
||||
|
||||
{{ if $location.EnableCORS }}
|
||||
{{ template "CORS" }}
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size "{{ $location.Proxy.BodySize }}";
|
||||
|
||||
proxy_set_header Host $best_http_host;
|
||||
|
||||
# Pass the extracted client certificate to the backend
|
||||
{{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }}
|
||||
proxy_set_header ssl-client-cert $ssl_client_cert;
|
||||
{{ end }}
|
||||
|
||||
# Allow websocket connections
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_set_header X-Real-IP $the_real_ip;
|
||||
proxy_set_header X-Forwarded-For $the_real_ip;
|
||||
proxy_set_header X-Forwarded-Host $best_http_host;
|
||||
proxy_set_header X-Forwarded-Port $pass_port;
|
||||
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Scheme $pass_access_scheme;
|
||||
|
||||
# mitigate HTTPoxy Vulnerability
|
||||
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
|
||||
proxy_set_header Proxy "";
|
||||
|
||||
# Custom headers to proxied server
|
||||
{{ range $k, $v := $proxyHeaders }}
|
||||
proxy_set_header {{ $k }} "{{ $v }}";
|
||||
{{ end }}
|
||||
|
||||
proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s;
|
||||
proxy_send_timeout {{ $location.Proxy.SendTimeout }}s;
|
||||
proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_buffering off;
|
||||
proxy_buffer_size "{{ $location.Proxy.BufferSize }}";
|
||||
proxy_buffers 4 "{{ $location.Proxy.BufferSize }}";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_cookie_domain {{ $location.Proxy.CookieDomain }};
|
||||
proxy_cookie_path {{ $location.Proxy.CookiePath }};
|
||||
|
||||
# In case of errors try the next upstream server before returning an error
|
||||
proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream }}{{ if $cfg.RetryNonIdempotent }} non_idempotent{{ end }};
|
||||
|
||||
{{/* rewrite only works if the content is not compressed */}}
|
||||
{{ if $location.Redirect.AddBaseURL }}
|
||||
proxy_set_header Accept-Encoding "";
|
||||
{{ end }}
|
||||
|
||||
{{/* Add any additional configuration defined */}}
|
||||
{{ $location.ConfigurationSnippet }}
|
||||
|
||||
{{ buildProxyPass $server.Hostname $backends $location }}
|
||||
{{ else }}
|
||||
#{{ $location.Denied }}
|
||||
return 503;
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq $server.Hostname "_" }}
|
||||
# health checks in cloud providers require the use of port 80
|
||||
location {{ $healthzURI }} {
|
||||
access_log off;
|
||||
return 200;
|
||||
}
|
||||
|
||||
# this is required to avoid error if nginx is being monitored
|
||||
# with an external software (like sysdig)
|
||||
location /nginx_status {
|
||||
allow 127.0.0.1;
|
||||
{{ if $IsIPV6Enabled }}allow ::1;{{ end }}
|
||||
deny all;
|
||||
|
||||
access_log off;
|
||||
stub_status on;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ template "SERVER" serverConfig $all $server }}
|
||||
{{ template "CUSTOM_ERRORS" $all }}
|
||||
}
|
||||
|
||||
{{ if $server.Alias }}
|
||||
server {
|
||||
server_name {{ $server.Alias }};
|
||||
{{ template "SERVER" serverConfig $all $server }}
|
||||
{{ template "CUSTOM_ERRORS" $all }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
# default server, used for NGINX healthcheck and access to nginx stats
|
||||
|
@ -664,3 +474,209 @@ stream {
|
|||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* definition of server-template to avoid repetitions with server-alias */}}
|
||||
{{ define "SERVER" }}
|
||||
{{ $all := .First }}
|
||||
{{ $server := .Second }}
|
||||
listen 80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}};
|
||||
{{ if $all.IsIPV6Enabled }}listen [::]:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }};{{ end }}
|
||||
set $proxy_upstream_name "-";
|
||||
|
||||
{{/* Listen on 442 because port 443 is used in the TLS sni server */}}
|
||||
{{/* This listener must always have proxy_protocol enabled, because the SNI listener forwards on source IP info in it. */}}
|
||||
{{ if not (empty $server.SSLCertificate) }}listen 442 proxy_protocol{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
|
||||
{{ if $all.IsIPV6Enabled }}{{ if not (empty $server.SSLCertificate) }}listen [::]:442 proxy_protocol{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};{{ end }}
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $server.SSLPemChecksum }}
|
||||
ssl_certificate {{ $server.SSLCertificate }};
|
||||
ssl_certificate_key {{ $server.SSLCertificate }};
|
||||
{{ end }}
|
||||
|
||||
{{ if (and (not (empty $server.SSLCertificate)) $all.Cfg.HSTS) }}
|
||||
more_set_headers "Strict-Transport-Security: max-age={{ $all.Cfg.HSTSMaxAge }}{{ if $all.Cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }};{{ if $all.Cfg.HSTSPreload }} preload{{ end }}";
|
||||
{{ end }}
|
||||
|
||||
{{ if $all.Cfg.EnableVtsStatus }}vhost_traffic_status_filter_by_set_key $geoip_country_code country::$server_name;{{ end }}
|
||||
|
||||
{{ range $location := $server.Locations }}
|
||||
{{ $path := buildLocation $location }}
|
||||
{{ $authPath := buildAuthLocation $location }}
|
||||
|
||||
{{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }}
|
||||
# PEM sha: {{ $location.CertificateAuth.AuthSSLCert.PemSHA }}
|
||||
ssl_client_certificate {{ $location.CertificateAuth.AuthSSLCert.CAFileName }};
|
||||
ssl_verify_client on;
|
||||
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $location.Redirect.AppRoot)}}
|
||||
if ($uri = /) {
|
||||
return 302 {{ $location.Redirect.AppRoot }};
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $authPath) }}
|
||||
location = {{ $authPath }} {
|
||||
internal;
|
||||
set $proxy_upstream_name "internal";
|
||||
|
||||
{{ if not $location.ExternalAuth.SendBody }}
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
{{ end }}
|
||||
{{ if not (empty $location.ExternalAuth.Method) }}
|
||||
proxy_method {{ $location.ExternalAuth.Method }};
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Scheme $pass_access_scheme;
|
||||
{{ end }}
|
||||
proxy_pass_request_headers on;
|
||||
proxy_set_header Host {{ $location.ExternalAuth.Host }};
|
||||
proxy_ssl_server_name on;
|
||||
|
||||
client_max_body_size "{{ $location.Proxy.BodySize }}";
|
||||
|
||||
|
||||
set $target {{ $location.ExternalAuth.URL }};
|
||||
proxy_pass $target;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
location {{ $path }} {
|
||||
set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $all.Backends $location }}";
|
||||
|
||||
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
|
||||
# enforce ssl on server side
|
||||
if ($pass_access_scheme = http) {
|
||||
return 301 https://$best_http_host$request_uri;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if isLocationAllowed $location }}
|
||||
{{ if gt (len $location.Whitelist.CIDR) 0 }}
|
||||
if ({{ buildDenyVariable (print .Hostname "_" $path) }}) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }};
|
||||
|
||||
{{ if not (empty $authPath) }}
|
||||
# this location requires authentication
|
||||
auth_request {{ $authPath }};
|
||||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
{{- range $idx, $line := buildAuthResponseHeaders $location }}
|
||||
{{ $line }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $location.ExternalAuth.SigninURL) }}
|
||||
error_page 401 = {{ $location.ExternalAuth.SigninURL }}?rd=$request_uri;
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{/* if the location contains a rate limit annotation, create one */}}
|
||||
{{ $limits := buildRateLimit $location }}
|
||||
{{ range $limit := $limits }}
|
||||
{{ $limit }}{{ end }}
|
||||
|
||||
{{ if $location.BasicDigestAuth.Secured }}
|
||||
{{ if eq $location.BasicDigestAuth.Type "basic" }}
|
||||
auth_basic "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_basic_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ else }}
|
||||
auth_digest "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_digest_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ end }}
|
||||
proxy_set_header Authorization "";
|
||||
{{ end }}
|
||||
|
||||
{{ if $location.EnableCORS }}
|
||||
{{ template "CORS" }}
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size "{{ $location.Proxy.BodySize }}";
|
||||
|
||||
proxy_set_header Host $best_http_host;
|
||||
|
||||
# Pass the extracted client certificate to the backend
|
||||
{{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }}
|
||||
proxy_set_header ssl-client-cert $ssl_client_cert;
|
||||
{{ end }}
|
||||
|
||||
# Allow websocket connections
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_set_header X-Real-IP $the_real_ip;
|
||||
proxy_set_header X-Forwarded-For $the_real_ip;
|
||||
proxy_set_header X-Forwarded-Host $best_http_host;
|
||||
proxy_set_header X-Forwarded-Port $pass_port;
|
||||
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Scheme $pass_access_scheme;
|
||||
|
||||
# mitigate HTTPoxy Vulnerability
|
||||
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
|
||||
proxy_set_header Proxy "";
|
||||
|
||||
# Custom headers to proxied server
|
||||
{{ range $k, $v := $all.ProxySetHeaders }}
|
||||
proxy_set_header {{ $k }} "{{ $v }}";
|
||||
{{ end }}
|
||||
|
||||
proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s;
|
||||
proxy_send_timeout {{ $location.Proxy.SendTimeout }}s;
|
||||
proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_buffering off;
|
||||
proxy_buffer_size "{{ $location.Proxy.BufferSize }}";
|
||||
proxy_buffers 4 "{{ $location.Proxy.BufferSize }}";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_cookie_domain {{ $location.Proxy.CookieDomain }};
|
||||
proxy_cookie_path {{ $location.Proxy.CookiePath }};
|
||||
|
||||
# In case of errors try the next upstream server before returning an error
|
||||
proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream }}{{ if $all.Cfg.RetryNonIdempotent }} non_idempotent{{ end }};
|
||||
|
||||
{{/* rewrite only works if the content is not compressed */}}
|
||||
{{ if $location.Redirect.AddBaseURL }}
|
||||
proxy_set_header Accept-Encoding "";
|
||||
{{ end }}
|
||||
|
||||
{{/* Add any additional configuration defined */}}
|
||||
{{ $location.ConfigurationSnippet }}
|
||||
|
||||
{{ buildProxyPass $server.Hostname $all.Backends $location }}
|
||||
{{ else }}
|
||||
#{{ $location.Denied }}
|
||||
return 503;
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq $server.Hostname "_" }}
|
||||
# health checks in cloud providers require the use of port 80
|
||||
location {{ $all.HealthzURI }} {
|
||||
access_log off;
|
||||
return 200;
|
||||
}
|
||||
|
||||
# this is required to avoid error if nginx is being monitored
|
||||
# with an external software (like sysdig)
|
||||
location /nginx_status {
|
||||
allow 127.0.0.1;
|
||||
{{ if $all.IsIPV6Enabled }}allow ::1;{{ end }}
|
||||
deny all;
|
||||
|
||||
access_log off;
|
||||
stub_status on;
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
|
41
core/pkg/ingress/annotations/alias/main.go
Normal file
41
core/pkg/ingress/annotations/alias/main.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package alias
|
||||
|
||||
import (
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
annotation = "ingress.kubernetes.io/server-alias"
|
||||
)
|
||||
|
||||
type alias struct {
|
||||
}
|
||||
|
||||
// NewParser creates a new Alias annotation parser
|
||||
func NewParser() parser.IngressAnnotation {
|
||||
return alias{}
|
||||
}
|
||||
|
||||
// Parse parses the annotations contained in the ingress rule
|
||||
// used to add an alias to the provided hosts
|
||||
func (a alias) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||
return parser.GetStringAnnotation(annotation, ing)
|
||||
}
|
60
core/pkg/ingress/annotations/alias/main_test.go
Normal file
60
core/pkg/ingress/annotations/alias/main_test.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package alias
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
ap := NewParser()
|
||||
if ap == nil {
|
||||
t.Fatalf("expected a parser.IngressAnnotation but returned nil")
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
annotations map[string]string
|
||||
expected string
|
||||
}{
|
||||
{map[string]string{annotation: "www.example.com"}, "www.example.com"},
|
||||
{map[string]string{annotation: "*.example.com www.example.*"}, "*.example.com www.example.*"},
|
||||
{map[string]string{annotation: `~^www\d+\.example\.com$`}, `~^www\d+\.example\.com$`},
|
||||
{map[string]string{annotation: ""}, ""},
|
||||
{map[string]string{}, ""},
|
||||
{nil, ""},
|
||||
}
|
||||
|
||||
ing := &extensions.Ingress{
|
||||
ObjectMeta: meta_v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.IngressSpec{},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
ing.SetAnnotations(testCase.annotations)
|
||||
result, _ := ap.Parse(ing)
|
||||
if result != testCase.expected {
|
||||
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ import (
|
|||
"k8s.io/ingress/core/pkg/ingress/annotations/sslpassthrough"
|
||||
"k8s.io/ingress/core/pkg/ingress/errors"
|
||||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/alias"
|
||||
)
|
||||
|
||||
type extractorConfig interface {
|
||||
|
@ -69,6 +70,7 @@ func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
|||
"SessionAffinity": sessionaffinity.NewParser(),
|
||||
"SSLPassthrough": sslpassthrough.NewParser(),
|
||||
"ConfigurationSnippet": snippet.NewParser(),
|
||||
"Alias": alias.NewParser(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +109,7 @@ const (
|
|||
sslPassthrough = "SSLPassthrough"
|
||||
sessionAffinity = "SessionAffinity"
|
||||
serviceUpstream = "ServiceUpstream"
|
||||
serverAlias = "Alias"
|
||||
)
|
||||
|
||||
func (e *annotationExtractor) ServiceUpstream(ing *extensions.Ingress) bool {
|
||||
|
@ -133,6 +136,11 @@ func (e *annotationExtractor) SSLPassthrough(ing *extensions.Ingress) bool {
|
|||
return val.(bool)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) Alias(ing *extensions.Ingress) string {
|
||||
val, _ := e.annotations[serverAlias].Parse(ing)
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) SessionAffinity(ing *extensions.Ingress) *sessionaffinity.AffinityConfig {
|
||||
val, _ := e.annotations[sessionAffinity].Parse(ing)
|
||||
return val.(*sessionaffinity.AffinityConfig)
|
||||
|
|
|
@ -610,6 +610,16 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
upstreams := ic.createUpstreams(ings)
|
||||
servers := ic.createServers(ings, upstreams)
|
||||
|
||||
// If a server has a hostname equivalent to a pre-existing alias, then we remove the alias
|
||||
for _, server := range servers {
|
||||
for j, alias := range servers {
|
||||
if server.Hostname == alias.Alias {
|
||||
glog.Warningf("There is a conflict with hostname '%v' and alias of `%v`.", server.Hostname, alias.Hostname)
|
||||
servers[j].Alias = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ingIf := range ings {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
|
||||
|
@ -1066,19 +1076,25 @@ func (ic *GenericController) createServers(data []interface{},
|
|||
}
|
||||
}
|
||||
|
||||
// configure default location and SSL
|
||||
// configure default location, alias, and SSL
|
||||
for _, ingIf := range data {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
if !class.IsValid(ing, ic.cfg.IngressClass, ic.cfg.DefaultIngressClass) {
|
||||
continue
|
||||
}
|
||||
|
||||
// setup server-alias based on annotations
|
||||
aliasAnnotation := ic.annotations.Alias(ing)
|
||||
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
host := rule.Host
|
||||
if host == "" {
|
||||
host = defServerName
|
||||
}
|
||||
|
||||
// setup server aliases
|
||||
servers[host].Alias = aliasAnnotation
|
||||
|
||||
// only add a certificate if the server does not have one previously configured
|
||||
if len(ing.Spec.TLS) == 0 || servers[host].SSLCertificate != "" {
|
||||
continue
|
||||
|
|
|
@ -220,6 +220,8 @@ type Server struct {
|
|||
SSLPemChecksum string `json:"sslPemChecksum"`
|
||||
// Locations list of URIs configured in the server.
|
||||
Locations []*Location `json:"locations,omitempty"`
|
||||
// return the alias of the server name
|
||||
Alias string `json:"alias,omitempty"`
|
||||
}
|
||||
|
||||
// Location describes an URI inside a server.
|
||||
|
|
Loading…
Reference in a new issue