Skip to content

Commit

Permalink
Replace CORS handling logic with NJS scripting (avoid using Nginx 'if…
Browse files Browse the repository at this point in the history
…' directives)
  • Loading branch information
elizabeth-dev committed Nov 27, 2024
1 parent 31b7074 commit c291656
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 379 deletions.
7 changes: 4 additions & 3 deletions internal/ingress/controller/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ var funcMap = text_template.FuncMap{
"getenv": os.Getenv,
"contains": strings.Contains,
"split": strings.Split,
"join": strings.Join,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"trimSpace": strings.TrimSpace,
Expand Down Expand Up @@ -1675,10 +1676,10 @@ func buildOriginRegex(origin string) string {

func buildCorsOriginRegex(corsOrigins []string) string {
if len(corsOrigins) == 1 && corsOrigins[0] == "*" {
return "set $http_origin *;\nset $cors 'true';"
return ".*"
}

originsRegex := "if ($http_origin ~* ("
originsRegex := "("
for i, origin := range corsOrigins {
originTrimmed := strings.TrimSpace(origin)
if originTrimmed != "" {
Expand All @@ -1689,6 +1690,6 @@ func buildCorsOriginRegex(corsOrigins []string) string {
}
}
}
originsRegex += ")$ ) { set $cors 'true'; }"
originsRegex += ")"
return originsRegex
}
33 changes: 33 additions & 0 deletions internal/ingress/controller/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1953,3 +1953,36 @@ func TestCleanConf(t *testing.T) {
t.Errorf("cleanConf result don't match with expected: %s", diff)
}
}

func TestBuildCorsOriginRegex(t *testing.T) {
origins := []string{"http://foo.bar "}

result := buildCorsOriginRegex(origins)

expected := `((http://foo\.bar))`
if result != expected {
t.Errorf("expected '%v' but returned '%v'", expected, result)
}
}

func TestBuildCorsOriginRegexWithMultipleOrigins(t *testing.T) {
origins := []string{" http://foo.bar", "http://*.bar"}

result := buildCorsOriginRegex(origins)

expected := `((http://foo\.bar)|(http://[A-Za-z0-9\-]+\.bar))`
if result != expected {
t.Errorf("expected '%v' but returned '%v'", expected, result)
}
}

func TestBuildCorsOriginRegexWithWildcard(t *testing.T) {
origins := []string{"*"}

result := buildCorsOriginRegex(origins)

expected := `.*`
if result != expected {
t.Errorf("expected '%v' but returned '%v'", expected, result)
}
}
21 changes: 21 additions & 0 deletions rootfs/etc/nginx/js/nginx/ngx_handle_cors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function handle_cors(req) {
const originsRegex = new RegExp(`${req.variables.cors_origins_regex}$`, 'i');

if (originsRegex.test(req.headersIn['Origin'])) {
const allowedOrigins = req.variables.cors_allowed_origins.split(',');

req.headersOut['Access-Control-Allow-Origin'] = allowedOrigins.length === 1 && allowedOrigins[0] === '*' ? '*' : req.headersIn['Origin'];
req.headersOut['Access-Control-Allow-Methods'] = req.variables.cors_allow_methods;
req.headersOut['Access-Control-Allow-Headers'] = req.variables.cors_allow_headers;
req.headersOut['Access-Control-Max-Age'] = req.variables.cors_max_age;
if (req.variables.cors_allow_credentials) req.headersOut['Access-Control-Allow-Credentials'] = req.variables.cors_allow_credentials;
if (req.variables.cors_expose_headers) req.headersOut['Access-Control-Expose-Headers'] = req.variables.cors_expose_headers;

if (req.method === 'OPTIONS') {
req.headersOut['Content-Type'] = 'text/plain charset=UTF-8';
req.headersOut['Content-Length'] = '0';
}
}
}

export default {handle_cors};
38 changes: 13 additions & 25 deletions rootfs/etc/nginx/template/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ http {

init_worker_by_lua_file /etc/nginx/lua/ngx_conf_init_worker.lua;

js_import njs_handle_cors from /etc/nginx/js/nginx/ngx_handle_cors.js;

{{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}}
{{/* we use the value of the real IP for the geo_ip module */}}
{{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIP }}
Expand Down Expand Up @@ -837,33 +839,19 @@ stream {
{{/* CORS support from https://michielkalkman.com/snippets/nginx-cors-open-configuration.html */}}
{{ define "CORS" }}
{{ $cors := .CorsConfig }}
# Cors Preflight methods needs additional options and different Return Code
{{ if $cors.CorsAllowOrigin }}
{{ buildCorsOriginRegex $cors.CorsAllowOrigin }}
{{ end }}
if ($request_method = 'OPTIONS') {
set $cors ${cors}options;
}

if ($cors = "true") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
{{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }}
more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}';
more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}';
{{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }}
more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}';
}
set $cors_origins_regex '{{ buildCorsOriginRegex $cors.CorsAllowOrigin }}';
set $cors_allowed_origins '{{ join $cors.CorsAllowOrigin "," }}';
set $cors_allow_methods '{{ $cors.CorsAllowMethods }}';
set $cors_allow_headers '{{ $cors.CorsAllowHeaders }}';
set $cors_max_age '{{ $cors.CorsMaxAge }}';
{{ if $cors.CorsAllowCredentials }} set $cors_allow_credentials {{ $cors.CorsAllowCredentials }}; {{ end }}
{{ if not (empty $cors.CorsExposeHeaders) }} set $cors_expose_headers '{{ $cors.CorsExposeHeaders }}'; {{ end }}

js_header_filter njs_handle_cors.handle_cors;

if ($cors = "trueoptions") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
{{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }}
more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}';
more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}';
{{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }}
more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}';
more_set_headers 'Content-Type: text/plain charset=UTF-8';
more_set_headers 'Content-Length: 0';
return 204;
if ($request_method = 'OPTIONS') {
return 204;
}
{{ end }}

Expand Down
Loading

0 comments on commit c291656

Please sign in to comment.