diff --git a/ingress/controllers/nginx/controller.go b/ingress/controllers/nginx/controller.go index 1a08e8d953..c1118e5693 100644 --- a/ingress/controllers/nginx/controller.go +++ b/ingress/controllers/nginx/controller.go @@ -43,6 +43,7 @@ import ( "k8s.io/contrib/ingress/controllers/nginx/nginx" "k8s.io/contrib/ingress/controllers/nginx/nginx/auth" "k8s.io/contrib/ingress/controllers/nginx/nginx/config" + "k8s.io/contrib/ingress/controllers/nginx/nginx/cors" "k8s.io/contrib/ingress/controllers/nginx/nginx/healthcheck" "k8s.io/contrib/ingress/controllers/nginx/nginx/ingress" "k8s.io/contrib/ingress/controllers/nginx/nginx/ipwhitelist" @@ -717,6 +718,11 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuratio glog.V(3).Infof("error reading white list annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } + eCORS, err := cors.ParseAnnotations(ing) + if err != nil { + glog.V(3).Infof("error reading CORS annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) + } + host := rule.Host if host == "" { host = defServerName @@ -749,6 +755,7 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuratio loc.Redirect = *locRew loc.SecureUpstream = secUpstream loc.Whitelist = *wl + loc.EnableCORS = eCORS addLoc = false continue @@ -763,7 +770,6 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuratio } if addLoc { - server.Locations = append(server.Locations, &ingress.Location{ Path: nginxPath, Upstream: *ups, @@ -772,6 +778,7 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuratio Redirect: *locRew, SecureUpstream: secUpstream, Whitelist: *wl, + EnableCORS: eCORS, }) } } diff --git a/ingress/controllers/nginx/nginx.tmpl b/ingress/controllers/nginx/nginx.tmpl index ff521b5eef..c4e5676171 100644 --- a/ingress/controllers/nginx/nginx.tmpl +++ b/ingress/controllers/nginx/nginx.tmpl @@ -228,6 +228,10 @@ http { proxy_set_header Authorization ""; {{- end }} + {{ if $location.EnableCORS }} + {{ template "CORS" }} + {{ end }} + proxy_set_header Host $host; # Pass Real IP @@ -374,3 +378,38 @@ stream { } {{ end }} {{ end }} + +{{/* CORS support from https://michielkalkman.com/snippets/nginx-cors-open-configuration.html */}} +{{ define "CORS" }} + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + # + # Om nom nom cookies + # + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + # + # Custom headers and headers various browsers *should* be OK with but aren't + # + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + # + # Tell client that this pre-flight info is valid for 20 days + # + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + if ($request_method = 'POST') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + } + if ($request_method = 'GET') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + } +{{ end }} diff --git a/ingress/controllers/nginx/nginx/cors/main.go b/ingress/controllers/nginx/nginx/cors/main.go new file mode 100644 index 0000000000..a77b35472b --- /dev/null +++ b/ingress/controllers/nginx/nginx/cors/main.go @@ -0,0 +1,50 @@ +/* +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 cors + +import ( + "errors" + "strconv" + + "k8s.io/kubernetes/pkg/apis/extensions" +) + +const ( + cors = "ingress.kubernetes.io/enable-cors" +) + +type ingAnnotations map[string]string + +func (a ingAnnotations) cors() bool { + val, ok := a[cors] + if ok { + if b, err := strconv.ParseBool(val); err == nil { + return b + } + } + return false +} + +// ParseAnnotations parses the annotations contained in the ingress +// rule used to indicate if the upstream servers should use SSL +func ParseAnnotations(ing *extensions.Ingress) (bool, error) { + if ing.GetAnnotations() == nil { + return false, errors.New("no annotations present") + } + + return ingAnnotations(ing.GetAnnotations()).cors(), nil +} diff --git a/ingress/controllers/nginx/nginx/ingress/nginx.go b/ingress/controllers/nginx/nginx/ingress/nginx.go index d428fc9f31..132b7b5855 100644 --- a/ingress/controllers/nginx/nginx/ingress/nginx.go +++ b/ingress/controllers/nginx/nginx/ingress/nginx.go @@ -101,6 +101,7 @@ type Location struct { Redirect rewrite.Redirect SecureUpstream bool Whitelist ipwhitelist.SourceRange + EnableCORS bool } // LocationByPath sorts location by path