Add support for Server Alias in Nginx

Adds support for server alias in nginx. Adds a new annotation
which allows us to specify a server alias that will be appended
to the server name.
This commit is contained in:
Fernando Diaz 2017-08-09 22:22:54 -05:00
parent 198c926bb9
commit ac504bdbc0
8 changed files with 146 additions and 4 deletions

View file

@ -64,7 +64,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
@ -155,7 +155,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: |
@ -167,6 +167,17 @@ 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: "<server-name_0>:<server-alias_0>;...;<server-name_n>:<server-alias_n>"`.
This will append a server-alias to the end of the server_name in the NGINX server. A server-alias can accept wildcards, but
it cannot accept port numbers.
The server-name must match a valid server within the ingress resource for it to append the server-alias. Multiple server-aliases
can be added for multiple server-names using `;` as a delimiter.
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.

View file

@ -452,6 +452,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
IP: svc.Spec.ClusterIP,
Port: port,
ProxyProtocol: false,
})
}

View file

@ -295,7 +295,7 @@ http {
{{ $backlogSize := .BacklogSize }}
{{ range $index, $server := .Servers }}
server {
server_name {{ $server.Hostname }};
server_name {{ $server.Hostname }} {{ $server.Alias }};
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 "-";

View file

@ -0,0 +1,42 @@
/*
Copyright 2016 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 CORS annotation parser
func NewParser() parser.IngressAnnotation {
return alias{}
}
// Parse parses the annotations contained in the ingress rule
// used to indicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules
func (a alias) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(annotation, ing)
}

View 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)
}
}
}

View file

@ -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)

View file

@ -1055,19 +1055,37 @@ 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-aliases based on annotations
aliasMap := map[string]string{}
aliasAnnotation := ic.annotations.Alias(ing)
// Here we parse the annotation string in the following format:
// ingress.kubernetes.io/server-alias: "host_0:alias_0;...;host_n:alias_n"
aliases := strings.Split(aliasAnnotation, ";")
for _, alias := range aliases {
aliasParts := strings.Split(alias, ":")
if len(aliasParts) == 2 {
// aliasMap[host] = alias
aliasMap[aliasParts[0]] = aliasParts[1]
}
}
for _, rule := range ing.Spec.Rules {
host := rule.Host
if host == "" {
host = defServerName
}
// setup server aliases
servers[host].Alias = aliasMap[host]
// only add a certificate if the server does not have one previously configured
if len(ing.Spec.TLS) == 0 || servers[host].SSLCertificate != "" {
continue

View file

@ -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.