From 3e80262fc92f358ddf95aa322a4ddd004487967c Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Thu, 18 Jul 2024 19:20:52 -0300 Subject: [PATCH 01/17] Bootstrap initial crossplane work (#11653) * Bootstrap initial crossplane work * Disable some tests to speed up stuff --- .golangci.yml | 1 - go.mod | 2 + go.sum | 6 + go.work.sum | 21 +- .../controller/template/crossplane/config.go | 38 ++++ .../template/crossplane/crossplane.go | 70 +++++++ .../crossplane/crossplane_internal_test.go | 90 +++++++++ .../crossplane_internal_utils_test.go | 58 ++++++ .../template/crossplane/crossplane_test.go | 28 +++ .../controller/template/crossplane/events.go | 36 ++++ .../controller/template/crossplane/http.go | 187 ++++++++++++++++++ .../controller/template/crossplane/utils.go | 100 ++++++++++ magefiles/go.mod | 7 +- magefiles/go.sum | 36 +--- 14 files changed, 646 insertions(+), 34 deletions(-) create mode 100644 internal/ingress/controller/template/crossplane/config.go create mode 100644 internal/ingress/controller/template/crossplane/crossplane.go create mode 100644 internal/ingress/controller/template/crossplane/crossplane_internal_test.go create mode 100644 internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go create mode 100644 internal/ingress/controller/template/crossplane/crossplane_test.go create mode 100644 internal/ingress/controller/template/crossplane/events.go create mode 100644 internal/ingress/controller/template/crossplane/http.go create mode 100644 internal/ingress/controller/template/crossplane/utils.go diff --git a/.golangci.yml b/.golangci.yml index 2d73e14e77..afbe3b8251 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,7 +25,6 @@ linters: - ginkgolinter - gocheckcompilerdirectives - goconst - - gocritic - gocyclo - godox - gofmt diff --git a/go.mod b/go.mod index 6335b6f895..58616be3e2 100644 --- a/go.mod +++ b/go.mod @@ -48,8 +48,10 @@ require ( require ( github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/jstemmer/go-junit-report v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/moby/sys/userns v0.1.0 // indirect + github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect diff --git a/go.sum b/go.sum index 8de6c6bda1..2231efd129 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v1.0.0 h1:8X1gzZpR+nVQLAht+L/foqOeX2l9DTZoaIPbEQHxsds= +github.com/jstemmer/go-junit-report v1.0.0/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -127,6 +129,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM= +github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -191,6 +195,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= diff --git a/go.work.sum b/go.work.sum index 0bc5d32bce..cd5d5e0627 100644 --- a/go.work.sum +++ b/go.work.sum @@ -49,8 +49,11 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -68,7 +71,6 @@ github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= @@ -79,6 +81,7 @@ github.com/ncabatoff/fakescraper v0.0.0-20201102132415-4b37ba603d65/go.mod h1:Tx github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= @@ -86,6 +89,7 @@ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= @@ -108,6 +112,7 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= @@ -123,23 +128,37 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfC go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/kms v0.32.0/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go new file mode 100644 index 0000000000..007bdf1a82 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/config.go @@ -0,0 +1,38 @@ +/* +Copyright 2024 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 crossplane + +import ( + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" +) + +func (c *crossplaneTemplate) buildConfig() { + // Write basic directives + config := &ngx_crossplane.Config{ + Parsed: ngx_crossplane.Directives{ + buildDirective("pid", c.tplConfig.PID), + buildDirective("daemon", "off"), + buildDirective("worker_processes", c.tplConfig.Cfg.WorkerProcesses), + buildDirective("worker_rlimit_nofile", c.tplConfig.Cfg.MaxWorkerOpenFiles), + buildDirective("worker_shutdown_timeout", c.tplConfig.Cfg.WorkerShutdownTimeout), + }, + } + if c.tplConfig.Cfg.WorkerCPUAffinity != "" { + config.Parsed = append(config.Parsed, buildDirective("worker_cpu_affinity", c.tplConfig.Cfg.WorkerCPUAffinity)) + } + c.config = config +} diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go new file mode 100644 index 0000000000..c674cc6fe7 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -0,0 +1,70 @@ +/* +Copyright 2024 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 crossplane + +import ( + "bytes" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" +) + +/* +Unsupported directives: +- opentelemetry +- modsecurity +- any stream directive (TCP/UDP forwarding) +- geoip2 +*/ + +// On this case we will try to use the go ngx_crossplane to write the template instead of the template renderer + +type crossplaneTemplate struct { + options *ngx_crossplane.BuildOptions + config *ngx_crossplane.Config + tplConfig *config.TemplateConfig +} + +func NewCrossplaneTemplate() *crossplaneTemplate { + lua := ngx_crossplane.Lua{} + return &crossplaneTemplate{ + options: &ngx_crossplane.BuildOptions{ + Builders: []ngx_crossplane.RegisterBuilder{ + lua.RegisterBuilder(), + }, + }, + } +} + +func (c *crossplaneTemplate) Write(conf *config.TemplateConfig) ([]byte, error) { + c.tplConfig = conf + + // build root directives + c.buildConfig() + + // build events directive + c.buildEvents() + + // build http directive + c.buildHTTP() + + var buf bytes.Buffer + + err := ngx_crossplane.Build(&buf, *c.config, &ngx_crossplane.BuildOptions{}) + return buf.Bytes(), err +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go new file mode 100644 index 0000000000..6479b197eb --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2024 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 crossplane + +import ( + "testing" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "github.com/stretchr/testify/require" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" +) + +// THIS FILE SHOULD BE USED JUST FOR INTERNAL TESTS - Private functions + +func Test_Internal_buildEvents(t *testing.T) { + t.Run("should fill correctly events directives with defaults", func(t *testing.T) { + c := ngx_crossplane.Config{} + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + + expectedEvents := &ngx_crossplane.Config{ + File: "", + Parsed: ngx_crossplane.Directives{ + { + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", 16384), + buildDirective("use", "epool"), + buildDirective("multi_accept", true), + }, + }, + }, + } + + cplane := NewCrossplaneTemplate() + cplane.config = &c + cplane.tplConfig = tplConfig + cplane.buildEvents() + require.Equal(t, expectedEvents, cplane.config) + }) + + t.Run("should fill correctly events directives with specific values", func(t *testing.T) { + c := ngx_crossplane.Config{} + tplConfig := &config.TemplateConfig{ + Cfg: config.Configuration{ + MaxWorkerConnections: 50, + EnableMultiAccept: false, + DebugConnections: []string{"127.0.0.1/32", "192.168.0.10"}, + }, + } + + expectedEvents := &ngx_crossplane.Config{ + File: "", + Parsed: ngx_crossplane.Directives{ + { + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", 50), + buildDirective("use", "epool"), + buildDirective("multi_accept", false), + buildDirective("debug_connection", "127.0.0.1/32"), + buildDirective("debug_connection", "192.168.0.10"), + }, + }, + }, + } + + cplane := NewCrossplaneTemplate() + cplane.config = &c + cplane.tplConfig = tplConfig + cplane.buildEvents() + require.Equal(t, expectedEvents, cplane.config) + }) +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go new file mode 100644 index 0000000000..e9ac966918 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 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 crossplane + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/ingress-nginx/internal/ingress/controller/config" +) + +// THIS FILE SHOULD BE USED JUST FOR INTERNAL TESTS - Private functions + +func Test_Internal_buildDirectives(t *testing.T) { + t.Run("should be able to run a directive with a single argument", func(t *testing.T) { + directive := buildDirective("somedirective", "bla") + require.Equal(t, directive.Directive, "somedirective", []string{"bla"}) + }) + t.Run("should be able to run a directive with multiple different arguments", func(t *testing.T) { + directive := buildDirective("somedirective", "bla", 5, true, seconds(10), []string{"xpto", "bla"}) + require.Equal(t, directive.Directive, "somedirective", []string{"bla", "5", "on", "10s", "xpto", "bla"}) + }) +} + +func Test_Internal_boolToStr(t *testing.T) { + require.Equal(t, boolToStr(true), "on") + require.Equal(t, boolToStr(false), "off") +} + +func Test_Internal_buildLuaDictionaries(t *testing.T) { + t.Skip("Maps are not sorted, need to fix this") + cfg := &config.Configuration{ + LuaSharedDicts: map[string]int{ + "somedict": 1024, + "otherdict": 1025, + }, + } + directives := buildLuaSharedDictionaries(cfg) + require.Len(t, directives, 2) + require.Equal(t, "lua_shared_dict", directives[0].Directive) + require.Equal(t, []string{"somedict", "1M"}, directives[0].Args) + require.Equal(t, "lua_shared_dict", directives[1].Directive) + require.Equal(t, []string{"otherdict", "1025K"}, directives[1].Args) +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go new file mode 100644 index 0000000000..bc83cc79ac --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2024 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 crossplane_test + +import "testing" + +// TestCrossplaneTemplate should be a roundtrip test. +// We should initialize the scenarios based on the template configuration +// Then Parse and write a crossplane configuration, and roundtrip/parse back to check +// if the directives matches +// we should ignore line numbers and comments +func TestCrossplaneTemplate(t *testing.T) { + // implement +} diff --git a/internal/ingress/controller/template/crossplane/events.go b/internal/ingress/controller/template/crossplane/events.go new file mode 100644 index 0000000000..179358a5f8 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/events.go @@ -0,0 +1,36 @@ +/* +Copyright 2024 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 crossplane + +import ( + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" +) + +func (c *crossplaneTemplate) buildEvents() { + events := &ngx_crossplane.Directive{ + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", c.tplConfig.Cfg.MaxWorkerConnections), + buildDirective("use", "epool"), + buildDirective("multi_accept", c.tplConfig.Cfg.EnableMultiAccept), + }, + } + for k := range c.tplConfig.Cfg.DebugConnections { + events.Block = append(events.Block, buildDirective("debug_connection", c.tplConfig.Cfg.DebugConnections[k])) + } + c.config.Parsed = append(c.config.Parsed, events) +} diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go new file mode 100644 index 0000000000..a8343d9966 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/http.go @@ -0,0 +1,187 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + "strconv" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" +) + +func (c *crossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { + cfg := c.tplConfig.Cfg + httpBlock := ngx_crossplane.Directives{ + buildDirective("lua_package_path", "/etc/nginx/lua/?.lua;;"), + buildDirective("include", "/etc/nginx/mime.types"), + buildDirective("default_type", cfg.DefaultType), + buildDirective("real_ip_recursive", "on"), + buildDirective("aio", "threads"), + buildDirective("aio_write", cfg.EnableAioWrite), + buildDirective("server_tokens", cfg.ShowServerTokens), + buildDirective("resolver", buildResolversInternal(cfg.Resolver, cfg.DisableIpv6DNS)), + buildDirective("tcp_nopush", "on"), + buildDirective("tcp_nodelay", "on"), + buildDirective("log_subrequest", "on"), + buildDirective("reset_timedout_connection", "on"), + buildDirective("keepalive_timeout", seconds(cfg.KeepAlive)), + buildDirective("keepalive_requests", cfg.KeepAliveRequests), + buildDirective("client_body_temp_path", "/tmp/nginx/client-body"), + buildDirective("fastcgi_temp_path", "/tmp/nginx/fastcgi-temp"), + buildDirective("proxy_temp_path", "/tmp/nginx/proxy-temp"), + buildDirective("client_header_buffer_size", cfg.ClientHeaderBufferSize), + buildDirective("client_header_timeout", seconds(cfg.ClientHeaderTimeout)), + buildDirective("large_client_header_buffers", cfg.LargeClientHeaderBuffers), + buildDirective("client_body_buffer_size", cfg.ClientBodyBufferSize), + buildDirective("client_body_timeout", seconds(cfg.ClientBodyTimeout)), + buildDirective("types_hash_max_size", "2048"), + buildDirective("server_names_hash_max_size", cfg.ServerNameHashMaxSize), + buildDirective("server_names_hash_bucket_size", cfg.ServerNameHashBucketSize), + buildDirective("map_hash_bucket_size", cfg.MapHashBucketSize), + buildDirective("proxy_headers_hash_max_size", cfg.ProxyHeadersHashMaxSize), + buildDirective("proxy_headers_hash_bucket_size", cfg.ProxyHeadersHashBucketSize), + buildDirective("variables_hash_bucket_size", cfg.VariablesHashBucketSize), + buildDirective("variables_hash_max_size", cfg.VariablesHashMaxSize), + buildDirective("underscores_in_headers", cfg.EnableUnderscoresInHeaders), + buildDirective("ignore_invalid_headers", cfg.IgnoreInvalidHeaders), + buildDirective("limit_req_status", cfg.LimitReqStatusCode), + buildDirective("limit_conn_status", cfg.LimitConnStatusCode), + buildDirective("uninitialized_variable_warn", "off"), + buildDirective("server_name_in_redirect", "off"), + buildDirective("port_in_redirect", "off"), + buildDirective("ssl_protocols", strings.Split(cfg.SSLProtocols, " ")), + buildDirective("ssl_early_data", cfg.SSLEarlyData), + buildDirective("ssl_session_tickets", cfg.SSLSessionTickets), + buildDirective("ssl_buffer_size", cfg.SSLBufferSize), + buildDirective("ssl_ecdh_curve", cfg.SSLECDHCurve), + buildDirective("ssl_certificate", cfg.DefaultSSLCertificate.PemFileName), + buildDirective("ssl_certificate_key", cfg.DefaultSSLCertificate.PemFileName), + buildDirective("proxy_ssl_session_reuse", "on"), + buildDirective("proxy_cache_path", []string{ + "/tmp/nginx/nginx-cache-auth", "levels=1:2", "keys_zone=auth_cache:10m", + "max_size=128m", "inactive=30m", "use_temp_path=off", + }), + } + return httpBlock +} + +func (c *crossplaneTemplate) buildHTTP() { + cfg := c.tplConfig.Cfg + httpBlock := c.initHTTPDirectives() + httpBlock = append(httpBlock, buildLuaSharedDictionaries(&c.tplConfig.Cfg)...) + + // Real IP dealing + if (cfg.UseForwardedHeaders || cfg.UseProxyProtocol) || cfg.EnableRealIP { + if cfg.UseProxyProtocol { + httpBlock = append(httpBlock, buildDirective("real_ip_header", "proxy_protocol")) + } else { + httpBlock = append(httpBlock, buildDirective("real_ip_header", cfg.ForwardedForHeader)) + } + + for k := range cfg.ProxyRealIPCIDR { + httpBlock = append(httpBlock, buildDirective("set_real_ip_from", cfg.ProxyRealIPCIDR[k])) + } + } + + if cfg.GRPCBufferSizeKb > 0 { + httpBlock = append(httpBlock, buildDirective("grpc_buffer_size", strconv.Itoa(cfg.GRPCBufferSizeKb)+"k")) + } + + // HTTP2 Configuration + if cfg.HTTP2MaxHeaderSize != "" && cfg.HTTP2MaxFieldSize != "" { + httpBlock = append(httpBlock, buildDirective("http2_max_field_size", cfg.HTTP2MaxFieldSize)) + httpBlock = append(httpBlock, buildDirective("http2_max_header_size", cfg.HTTP2MaxHeaderSize)) + if cfg.HTTP2MaxRequests > 0 { + httpBlock = append(httpBlock, buildDirective("http2_max_requests", cfg.HTTP2MaxRequests)) + } + } + + if cfg.UseGzip { + httpBlock = append(httpBlock, buildDirective("gzip", "on")) + httpBlock = append(httpBlock, buildDirective("gzip_comp_level", cfg.GzipLevel)) + httpBlock = append(httpBlock, buildDirective("gzip_http_version", "1.1")) + httpBlock = append(httpBlock, buildDirective("gzip_min_length", cfg.GzipMinLength)) + httpBlock = append(httpBlock, buildDirective("gzip_types", cfg.GzipTypes)) + httpBlock = append(httpBlock, buildDirective("gzip_proxied", "any")) + httpBlock = append(httpBlock, buildDirective("gzip_vary", "on")) + + if cfg.GzipDisable != "" { + httpBlock = append(httpBlock, buildDirective("gzip_disable", strings.Split(cfg.GzipDisable, ""))) + } + } + + if !cfg.ShowServerTokens { + httpBlock = append(httpBlock, buildDirective("more_clear_headers", "Server")) + } + + if len(c.tplConfig.AddHeaders) > 0 { + additionalHeaders := make([]string, 0) + for headerName, headerValue := range c.tplConfig.AddHeaders { + additionalHeaders = append(additionalHeaders, fmt.Sprintf("%s: %s", headerName, headerValue)) + } + httpBlock = append(httpBlock, buildDirective("more_set_headers", additionalHeaders)) + } + + escape := "" + if cfg.LogFormatEscapeNone { + escape = "escape=none" + } else if cfg.LogFormatEscapeJSON { + escape = "escape=json" + } + + httpBlock = append(httpBlock, buildDirective("log_format", "upstreaminfo", escape, cfg.LogFormatUpstream)) + + // buildMap directive + mapLogDirective := &ngx_crossplane.Directive{ + Directive: "map", + Args: []string{"$request_uri", "$loggable"}, + Block: make(ngx_crossplane.Directives, 0), + } + for k := range cfg.SkipAccessLogURLs { + mapLogDirective.Block = append(mapLogDirective.Block, buildDirective(cfg.SkipAccessLogURLs[k], "0")) + } + mapLogDirective.Block = append(mapLogDirective.Block, buildDirective("default", "1")) + httpBlock = append(httpBlock, mapLogDirective) + // end of build mapLog + + if cfg.DisableAccessLog || cfg.DisableHTTPAccessLog { + httpBlock = append(httpBlock, buildDirective("access_log", "off")) + } else { + logDirectives := []string{"upstreaminfo", "if=$loggable"} + if cfg.EnableSyslog { + httpBlock = append(httpBlock, buildDirective("access_log", fmt.Sprintf("syslog:server%s:%d", cfg.SyslogHost, cfg.SyslogPort), logDirectives)) + } else { + accessLog := cfg.AccessLogPath + if cfg.HTTPAccessLogPath != "" { + accessLog = cfg.HTTPAccessLogPath + } + httpBlock = append(httpBlock, buildDirective("access_log", accessLog, logDirectives)) + } + } + + if cfg.EnableSyslog { + httpBlock = append(httpBlock, buildDirective("error_log", fmt.Sprintf("syslog:server%s:%d", cfg.SyslogHost, cfg.SyslogPort), cfg.ErrorLogLevel)) + } else { + httpBlock = append(httpBlock, buildDirective("error_log", cfg.ErrorLogPath, cfg.ErrorLogLevel)) + } + + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ + Directive: "http", + Block: httpBlock, + }) +} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go new file mode 100644 index 0000000000..8c07b61c28 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -0,0 +1,100 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + "net" + "strconv" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" + ing_net "k8s.io/ingress-nginx/internal/net" +) + +type seconds int + +func buildDirective(directive string, args ...any) *ngx_crossplane.Directive { + argsVal := make([]string, 0) + for k := range args { + switch v := args[k].(type) { + case string: + argsVal = append(argsVal, v) + case []string: + argsVal = append(argsVal, v...) + case int: + argsVal = append(argsVal, strconv.Itoa(v)) + case bool: + argsVal = append(argsVal, boolToStr(v)) + case seconds: + argsVal = append(argsVal, strconv.Itoa(int(v))+"s") + } + } + return &ngx_crossplane.Directive{ + Directive: directive, + Args: argsVal, + } +} + +func buildLuaSharedDictionaries(cfg *config.Configuration) []*ngx_crossplane.Directive { + out := make([]*ngx_crossplane.Directive, 0, len(cfg.LuaSharedDicts)) + for name, size := range cfg.LuaSharedDicts { + sizeStr := dictKbToStr(size) + out = append(out, buildDirective("lua_shared_dict", name, sizeStr)) + } + + return out +} + +// TODO: The utils below should be moved to a level where they can be consumed by any template writer + +// buildResolvers returns the resolvers reading the /etc/resolv.conf file +func buildResolversInternal(res []net.IP, disableIpv6 bool) []string { + r := make([]string, 0) + for _, ns := range res { + if ing_net.IsIPV6(ns) { + if disableIpv6 { + continue + } + r = append(r, fmt.Sprintf("[%s]", ns)) + } else { + r = append(r, ns.String()) + } + } + r = append(r, "valid=30s") + + if disableIpv6 { + r = append(r, "ipv6=off") + } + + return r +} + +func boolToStr(b bool) string { + if b { + return "on" + } + return "off" +} + +func dictKbToStr(size int) string { + if size%1024 == 0 { + return fmt.Sprintf("%dM", size/1024) + } + return fmt.Sprintf("%dK", size) +} diff --git a/magefiles/go.mod b/magefiles/go.mod index 9ee2bf3639..ad5b5f2eae 100644 --- a/magefiles/go.mod +++ b/magefiles/go.mod @@ -8,7 +8,7 @@ require ( github.com/helm/helm v2.17.0+incompatible github.com/magefile/mage v1.15.0 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/oauth2 v0.18.0 + golang.org/x/oauth2 v0.22.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -24,8 +24,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.30.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/onsi/gomega v1.34.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/stretchr/testify v1.9.0 // indirect golang.org/x/crypto v0.31.0 // indirect @@ -33,6 +32,6 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apimachinery v0.29.3 // indirect + k8s.io/apimachinery v0.31.0 // indirect k8s.io/helm v2.17.0+incompatible // indirect ) diff --git a/magefiles/go.sum b/magefiles/go.sum index 8684d47015..959371f923 100644 --- a/magefiles/go.sum +++ b/magefiles/go.sum @@ -8,8 +8,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 h1:aRd8M7HJVZOqn/vhOzrGcQH0lNAMkqMn+pXUYkatmcA= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -28,15 +28,12 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE= @@ -67,12 +64,10 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= @@ -84,7 +79,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -92,22 +86,16 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -116,7 +104,6 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -134,23 +121,17 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -168,7 +149,6 @@ gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= From 40dabdcfb2af28010d0c21e3f10e9afe927d97ee Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 21 Jul 2024 21:12:21 -0300 Subject: [PATCH 02/17] Add crossplane test and more directives (#11670) * Add roundtrip test * Add more directives and fix lint --- .../controller/template/crossplane/config.go | 10 +- .../template/crossplane/crossplane.go | 14 +- .../crossplane/crossplane_internal_test.go | 4 +- .../crossplane_internal_utils_test.go | 11 + .../template/crossplane/crossplane_test.go | 65 +- .../controller/template/crossplane/events.go | 2 +- .../controller/template/crossplane/http.go | 98 +- .../template/crossplane/testdata/README.md | 8 + .../template/crossplane/testdata/nginx.tmpl | 1250 +++++++++++++++++ .../controller/template/crossplane/utils.go | 9 + 10 files changed, 1445 insertions(+), 26 deletions(-) create mode 100644 internal/ingress/controller/template/crossplane/testdata/README.md create mode 100644 internal/ingress/controller/template/crossplane/testdata/nginx.tmpl diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go index 007bdf1a82..369ce2fb3b 100644 --- a/internal/ingress/controller/template/crossplane/config.go +++ b/internal/ingress/controller/template/crossplane/config.go @@ -20,7 +20,7 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *crossplaneTemplate) buildConfig() { +func (c *Template) buildConfig() { // Write basic directives config := &ngx_crossplane.Config{ Parsed: ngx_crossplane.Directives{ @@ -34,5 +34,13 @@ func (c *crossplaneTemplate) buildConfig() { if c.tplConfig.Cfg.WorkerCPUAffinity != "" { config.Parsed = append(config.Parsed, buildDirective("worker_cpu_affinity", c.tplConfig.Cfg.WorkerCPUAffinity)) } + + if c.tplConfig.Cfg.EnableBrotli { + config.Parsed = append(config.Parsed, + buildDirective("load_module", "/etc/nginx/modules/ngx_http_brotli_filter_module.so"), + buildDirective("load_module", "/etc/nginx/modules/ngx_http_brotli_static_module.so"), + ) + } + c.config = config } diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go index c674cc6fe7..1307de2f7a 100644 --- a/internal/ingress/controller/template/crossplane/crossplane.go +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -34,15 +34,17 @@ Unsupported directives: // On this case we will try to use the go ngx_crossplane to write the template instead of the template renderer -type crossplaneTemplate struct { +type Template struct { options *ngx_crossplane.BuildOptions config *ngx_crossplane.Config tplConfig *config.TemplateConfig + mimeFile string } -func NewCrossplaneTemplate() *crossplaneTemplate { +func NewTemplate() *Template { lua := ngx_crossplane.Lua{} - return &crossplaneTemplate{ + return &Template{ + mimeFile: "/etc/nginx/mime.types", options: &ngx_crossplane.BuildOptions{ Builders: []ngx_crossplane.RegisterBuilder{ lua.RegisterBuilder(), @@ -51,7 +53,11 @@ func NewCrossplaneTemplate() *crossplaneTemplate { } } -func (c *crossplaneTemplate) Write(conf *config.TemplateConfig) ([]byte, error) { +func (c *Template) SetMimeFile(file string) { + c.mimeFile = file +} + +func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { c.tplConfig = conf // build root directives diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go index 6479b197eb..b8bc3258d0 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go @@ -48,7 +48,7 @@ func Test_Internal_buildEvents(t *testing.T) { }, } - cplane := NewCrossplaneTemplate() + cplane := NewTemplate() cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() @@ -81,7 +81,7 @@ func Test_Internal_buildEvents(t *testing.T) { }, } - cplane := NewCrossplaneTemplate() + cplane := NewTemplate() cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go index e9ac966918..f31701781d 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -19,6 +19,7 @@ package crossplane import ( "testing" + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" "github.com/stretchr/testify/require" "k8s.io/ingress-nginx/internal/ingress/controller/config" ) @@ -36,6 +37,16 @@ func Test_Internal_buildDirectives(t *testing.T) { }) } +func Test_Internal_buildMapDirectives(t *testing.T) { + t.Run("should be able to run build a map directive with empty block", func(t *testing.T) { + directive := buildMapDirective("somedirective", "bla", ngx_crossplane.Directives{buildDirective("something", "otherstuff")}) + require.Equal(t, directive.Directive, "map") + require.Equal(t, directive.Args, []string{"somedirective", "bla"}) + require.Equal(t, directive.Block[0].Directive, "something") + require.Equal(t, directive.Block[0].Args, []string{"otherstuff"}) + }) +} + func Test_Internal_boolToStr(t *testing.T) { require.Equal(t, boolToStr(true), "on") require.Equal(t, boolToStr(false), "off") diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index bc83cc79ac..9b6b2fa9a5 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -16,13 +16,72 @@ limitations under the License. package crossplane_test -import "testing" +import ( + "os" + "testing" -// TestCrossplaneTemplate should be a roundtrip test. + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "github.com/stretchr/testify/require" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +const mockMimeTypes = ` +types { + text/html html htm shtml; + text/css css; + text/xml xml; +} +` + +// TestTemplate should be a roundtrip test. // We should initialize the scenarios based on the template configuration // Then Parse and write a crossplane configuration, and roundtrip/parse back to check // if the directives matches // we should ignore line numbers and comments func TestCrossplaneTemplate(t *testing.T) { - // implement + lua := ngx_crossplane.Lua{} + options := ngx_crossplane.ParseOptions{ + ErrorOnUnknownDirectives: true, + StopParsingOnError: true, + IgnoreDirectives: []string{"more_clear_headers"}, + DirectiveSources: []ngx_crossplane.MatchFunc{ngx_crossplane.DefaultDirectivesMatchFunc, ngx_crossplane.LuaDirectivesMatchFn}, + LexOptions: ngx_crossplane.LexOptions{ + Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, + }, + } + defaultCertificate := &ingress.SSLCert{ + PemFileName: "bla.crt", + PemCertKey: "bla.key", + } + + mimeFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = mimeFile.WriteString(mockMimeTypes) + require.NoError(t, err) + require.NoError(t, mimeFile.Close()) + + t.Run("it should be able to marshall and unmarshall the current configuration", func(t *testing.T) { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + tplConfig.Cfg.EnableBrotli = true + + tpl := crossplane.NewTemplate() + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) } diff --git a/internal/ingress/controller/template/crossplane/events.go b/internal/ingress/controller/template/crossplane/events.go index 179358a5f8..fa0c599e5e 100644 --- a/internal/ingress/controller/template/crossplane/events.go +++ b/internal/ingress/controller/template/crossplane/events.go @@ -20,7 +20,7 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *crossplaneTemplate) buildEvents() { +func (c *Template) buildEvents() { events := &ngx_crossplane.Directive{ Directive: "events", Block: ngx_crossplane.Directives{ diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index a8343d9966..c8b49d8345 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -24,11 +24,11 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *crossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { +func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { cfg := c.tplConfig.Cfg httpBlock := ngx_crossplane.Directives{ buildDirective("lua_package_path", "/etc/nginx/lua/?.lua;;"), - buildDirective("include", "/etc/nginx/mime.types"), + buildDirective("include", c.mimeFile), buildDirective("default_type", cfg.DefaultType), buildDirective("real_ip_recursive", "on"), buildDirective("aio", "threads"), @@ -46,7 +46,7 @@ func (c *crossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { buildDirective("proxy_temp_path", "/tmp/nginx/proxy-temp"), buildDirective("client_header_buffer_size", cfg.ClientHeaderBufferSize), buildDirective("client_header_timeout", seconds(cfg.ClientHeaderTimeout)), - buildDirective("large_client_header_buffers", cfg.LargeClientHeaderBuffers), + buildDirective("large_client_header_buffers", strings.Split(cfg.LargeClientHeaderBuffers, " ")), buildDirective("client_body_buffer_size", cfg.ClientBodyBufferSize), buildDirective("client_body_timeout", seconds(cfg.ClientBodyTimeout)), buildDirective("types_hash_max_size", "2048"), @@ -64,6 +64,7 @@ func (c *crossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { buildDirective("uninitialized_variable_warn", "off"), buildDirective("server_name_in_redirect", "off"), buildDirective("port_in_redirect", "off"), + buildDirective("http2_max_concurrent_streams", cfg.HTTP2MaxConcurrentStreams), buildDirective("ssl_protocols", strings.Split(cfg.SSLProtocols, " ")), buildDirective("ssl_early_data", cfg.SSLEarlyData), buildDirective("ssl_session_tickets", cfg.SSLSessionTickets), @@ -80,7 +81,7 @@ func (c *crossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { return httpBlock } -func (c *crossplaneTemplate) buildHTTP() { +func (c *Template) buildHTTP() { cfg := c.tplConfig.Cfg httpBlock := c.initHTTPDirectives() httpBlock = append(httpBlock, buildLuaSharedDictionaries(&c.tplConfig.Cfg)...) @@ -121,7 +122,7 @@ func (c *crossplaneTemplate) buildHTTP() { httpBlock = append(httpBlock, buildDirective("gzip_vary", "on")) if cfg.GzipDisable != "" { - httpBlock = append(httpBlock, buildDirective("gzip_disable", strings.Split(cfg.GzipDisable, ""))) + httpBlock = append(httpBlock, buildDirective("gzip_disable", strings.Split(cfg.GzipDisable, " "))) } } @@ -146,18 +147,12 @@ func (c *crossplaneTemplate) buildHTTP() { httpBlock = append(httpBlock, buildDirective("log_format", "upstreaminfo", escape, cfg.LogFormatUpstream)) - // buildMap directive - mapLogDirective := &ngx_crossplane.Directive{ - Directive: "map", - Args: []string{"$request_uri", "$loggable"}, - Block: make(ngx_crossplane.Directives, 0), - } + loggableMap := make(ngx_crossplane.Directives, 0) for k := range cfg.SkipAccessLogURLs { - mapLogDirective.Block = append(mapLogDirective.Block, buildDirective(cfg.SkipAccessLogURLs[k], "0")) + loggableMap = append(loggableMap, buildDirective(cfg.SkipAccessLogURLs[k], "0")) } - mapLogDirective.Block = append(mapLogDirective.Block, buildDirective("default", "1")) - httpBlock = append(httpBlock, mapLogDirective) - // end of build mapLog + loggableMap = append(loggableMap, buildDirective("default", "1")) + httpBlock = append(httpBlock, buildMapDirective("$request_uri", "$loggable", loggableMap)) if cfg.DisableAccessLog || cfg.DisableHTTPAccessLog { httpBlock = append(httpBlock, buildDirective("access_log", "off")) @@ -180,6 +175,79 @@ func (c *crossplaneTemplate) buildHTTP() { httpBlock = append(httpBlock, buildDirective("error_log", cfg.ErrorLogPath, cfg.ErrorLogLevel)) } + if cfg.SSLSessionCache { + httpBlock = append(httpBlock, + buildDirective("ssl_session_cache", fmt.Sprintf("shared:SSL:%s", cfg.SSLSessionCacheSize)), + buildDirective("ssl_session_timeout", cfg.SSLSessionTimeout), + ) + } + + if cfg.SSLSessionTicketKey != "" { + httpBlock = append(httpBlock, buildDirective("ssl_session_ticket_key", "/etc/ingress-controller/tickets.key")) + } + + if cfg.SSLCiphers != "" { + httpBlock = append(httpBlock, + buildDirective("ssl_ciphers", cfg.SSLCiphers), + buildDirective("ssl_prefer_server_ciphers", "on"), + ) + } + + if cfg.SSLDHParam != "" { + httpBlock = append(httpBlock, buildDirective("ssl_dhparam", cfg.SSLDHParam)) + } + + if len(cfg.CustomHTTPErrors) > 0 && !cfg.DisableProxyInterceptErrors { + httpBlock = append(httpBlock, buildDirective("proxy_intercept_errors", "on")) + } + + httpUpgradeMap := ngx_crossplane.Directives{buildDirective("default", "upgrade")} + if cfg.UpstreamKeepaliveConnections < 1 { + httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "close")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_upgrade", "$connection_upgrade", httpUpgradeMap)) + + reqIDMap := ngx_crossplane.Directives{buildDirective("default", "$http_x_request_id")} + if cfg.GenerateRequestID { + reqIDMap = append(reqIDMap, buildDirective("", "$request_id")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_request_id", "$req_id", reqIDMap)) + + if cfg.UseForwardedHeaders && cfg.ComputeFullForwardedFor { + forwardForMap := make(ngx_crossplane.Directives, 0) + if cfg.UseProxyProtocol { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $proxy_protocol_addr"), + buildDirective("", "$http_x_forwarded_for, $proxy_protocol_addr"), + ) + } else { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $realip_remote_addr"), + buildDirective("", "$realip_remote_addr"), + ) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_forwarded_for", "$full_x_forwarded_for", forwardForMap)) + } + + if cfg.AllowBackendServerHeader { + httpBlock = append(httpBlock, buildDirective("proxy_pass_header", "Server")) + } + + if cfg.EnableBrotli { + httpBlock = append(httpBlock, + buildDirective("brotli", "on"), + buildDirective("brotli_comp_level", cfg.BrotliLevel), + buildDirective("brotli_min_length", cfg.BrotliMinLength), + buildDirective("brotli_types", cfg.BrotliTypes), + ) + } + + if len(cfg.HideHeaders) > 0 { + for k := range cfg.HideHeaders { + httpBlock = append(httpBlock, buildDirective("proxy_hide_header", cfg.HideHeaders[k])) + } + } + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", Block: httpBlock, diff --git a/internal/ingress/controller/template/crossplane/testdata/README.md b/internal/ingress/controller/template/crossplane/testdata/README.md new file mode 100644 index 0000000000..4f44e3c98e --- /dev/null +++ b/internal/ingress/controller/template/crossplane/testdata/README.md @@ -0,0 +1,8 @@ +This directory contains the following files: + +* nginx.tmpl - Should be used to track migrated directives. We will test later to see +if the ending result of it is the same as the one parsed by go-crossplane +* various nginx.conf - Will be used to see if the template parser reacts as +expected, creating files that matches and can be parsed by go-crossplane + +TODO: move files to embed.FS \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl new file mode 100644 index 0000000000..cf9d200065 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl @@ -0,0 +1,1250 @@ +{{ $all := . }} +{{ $servers := .Servers }} +{{ $cfg := .Cfg }} +{{ $IsIPV6Enabled := .IsIPV6Enabled }} +{{ $healthzURI := .HealthzURI }} +{{ $backends := .Backends }} +{{ $proxyHeaders := .ProxySetHeaders }} +{{ $addHeaders := .AddHeaders }} + +# MIGRATED +pid {{ .PID }}; + +# MODULES ARE NOT MIGRATED YET! +{{ if $cfg.EnableBrotli }} +load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; +load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; +{{ end }} + +{{ if (shouldLoadAuthDigestModule $servers) }} +load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; +{{ end }} + +{{ if (shouldLoadOpentelemetryModule $cfg $servers) }} +load_module /etc/nginx/modules/otel_ngx_module.so; +{{ end }} + +# MIGRATED 1 +daemon off; + +worker_processes {{ $cfg.WorkerProcesses }}; +{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} +worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; +{{ end }} + +worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; + +{{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}} +{{/* avoid waiting too long during a reload */}} +worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; + +events { + multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; + worker_connections {{ $cfg.MaxWorkerConnections }}; + use epoll; + {{ range $index , $v := $cfg.DebugConnections }} + debug_connection {{ $v }}; + {{ end }} +} + +# END MIGRATED 1 + +http { + {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} + opentelemetry_config {{ $cfg.OpentelemetryConfig }}; + {{ end }} + + # MIGRATED + lua_package_path "/etc/nginx/lua/?.lua;;"; + + # MIGRATED + {{ buildLuaSharedDictionaries $cfg $servers }} + + # NOT MIGRATED + init_by_lua_block { + collectgarbage("collect") + + -- init modules + local ok, res + + ok, res = pcall(require, "lua_ingress") + if not ok then + error("require failed: " .. tostring(res)) + else + lua_ingress = res + lua_ingress.set_config({{ configForLua $all }}) + end + + ok, res = pcall(require, "configuration") + if not ok then + error("require failed: " .. tostring(res)) + else + configuration = res + configuration.prohibited_localhost_port = '{{ .StatusPort }}' + end + + ok, res = pcall(require, "balancer") + if not ok then + error("require failed: " .. tostring(res)) + else + balancer = res + end + + {{ if $all.EnableMetrics }} + ok, res = pcall(require, "monitor") + if not ok then + error("require failed: " .. tostring(res)) + else + monitor = res + end + {{ end }} + + ok, res = pcall(require, "certificate") + if not ok then + error("require failed: " .. tostring(res)) + else + certificate = res + certificate.is_ocsp_stapling_enabled = {{ $cfg.EnableOCSP }} + end + + ok, res = pcall(require, "plugins") + if not ok then + error("require failed: " .. tostring(res)) + else + plugins = res + end + -- load all plugins that'll be used here + plugins.init({ {{ range $idx, $plugin := $cfg.Plugins }}{{ if $idx }},{{ end }}{{ $plugin | quote }}{{ end }} }) + } + + init_worker_by_lua_block { + lua_ingress.init_worker() + balancer.init_worker() + {{ if $all.EnableMetrics }} + monitor.init_worker({{ $all.MonitorMaxBatchSize }}) + {{ end }} + + plugins.run() + } + + # MIGRATED VARIOUS 1 + {{/* 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 }} + {{ if $cfg.UseProxyProtocol }} + real_ip_header proxy_protocol; + {{ else }} + real_ip_header {{ $cfg.ForwardedForHeader }}; + {{ end }} + + real_ip_recursive on; + {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} + set_real_ip_from {{ $trusted_ip }}; + {{ end }} + {{ end }} + + aio threads; + + {{ if $cfg.EnableAioWrite }} + aio_write on; + {{ end }} + + tcp_nopush on; + tcp_nodelay on; + + log_subrequest on; + + reset_timedout_connection on; + + keepalive_timeout {{ $cfg.KeepAlive }}s; + keepalive_requests {{ $cfg.KeepAliveRequests }}; + + client_body_temp_path /tmp/nginx/client-body; + fastcgi_temp_path /tmp/nginx/fastcgi-temp; + proxy_temp_path /tmp/nginx/proxy-temp; + + client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }}; + client_header_timeout {{ $cfg.ClientHeaderTimeout }}s; + large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }}; + client_body_buffer_size {{ $cfg.ClientBodyBufferSize }}; + client_body_timeout {{ $cfg.ClientBodyTimeout }}s; + + {{ if gt $cfg.GRPCBufferSizeKb 0 }} + grpc_buffer_size {{ $cfg.GRPCBufferSizeKb }}k; + {{ end }} + + {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} + http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; + http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; + {{ end }} + + {{ if (gt $cfg.HTTP2MaxRequests 0) }} + http2_max_requests {{ $cfg.HTTP2MaxRequests }}; + {{ end }} + + http2_max_concurrent_streams {{ $cfg.HTTP2MaxConcurrentStreams }}; + + types_hash_max_size 2048; + server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }}; + server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }}; + map_hash_bucket_size {{ $cfg.MapHashBucketSize }}; + + proxy_headers_hash_max_size {{ $cfg.ProxyHeadersHashMaxSize }}; + proxy_headers_hash_bucket_size {{ $cfg.ProxyHeadersHashBucketSize }}; + + variables_hash_bucket_size {{ $cfg.VariablesHashBucketSize }}; + variables_hash_max_size {{ $cfg.VariablesHashMaxSize }}; + + underscores_in_headers {{ if $cfg.EnableUnderscoresInHeaders }}on{{ else }}off{{ end }}; + ignore_invalid_headers {{ if $cfg.IgnoreInvalidHeaders }}on{{ else }}off{{ end }}; + + limit_req_status {{ $cfg.LimitReqStatusCode }}; + limit_conn_status {{ $cfg.LimitConnStatusCode }}; + + include /etc/nginx/mime.types; + default_type {{ $cfg.DefaultType }}; + + {{ if $cfg.UseGzip }} + gzip on; + gzip_comp_level {{ $cfg.GzipLevel }}; + {{- if $cfg.GzipDisable }} + gzip_disable "{{ $cfg.GzipDisable }}"; + {{- end }} + gzip_http_version 1.1; + gzip_min_length {{ $cfg.GzipMinLength}}; + gzip_types {{ $cfg.GzipTypes }}; + gzip_proxied any; + gzip_vary on; + {{ end }} + + # Custom headers for response + {{ range $k, $v := $addHeaders }} + more_set_headers {{ printf "%s: %s" $k $v | quote }}; + {{ end }} + + server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }}; + {{ if not $cfg.ShowServerTokens }} + more_clear_headers Server; + {{ end }} + + # disable warnings + uninitialized_variable_warn off; + + # Additional available variables: + # $namespace + # $ingress_name + # $service_name + # $service_port + log_format upstreaminfo {{ if $cfg.LogFormatEscapeNone }}escape=none {{ else if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ $cfg.LogFormatUpstream }}'; + + {{/* map urls that should not appear in access.log */}} + {{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}} + map $request_uri $loggable { + {{ range $reqUri := $cfg.SkipAccessLogURLs }} + {{ $reqUri }} 0;{{ end }} + default 1; + } + + {{ if or $cfg.DisableAccessLog $cfg.DisableHTTPAccessLog }} + access_log off; + {{ else }} + {{ if $cfg.EnableSyslog }} + access_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} upstreaminfo if=$loggable; + {{ else }} + access_log {{ or $cfg.HTTPAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; + {{ end }} + {{ end }} + + {{ if $cfg.EnableSyslog }} + error_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} {{ $cfg.ErrorLogLevel }}; + {{ else }} + error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; + {{ end }} + + {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} + + server_name_in_redirect off; + port_in_redirect off; + + ssl_protocols {{ $cfg.SSLProtocols }}; + + ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; + + # allow configuring ssl session tickets + ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; + + # slightly reduce the time-to-first-byte + ssl_buffer_size {{ $cfg.SSLBufferSize }}; + + ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; + # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} + ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; + ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; + + proxy_ssl_session_reuse on; + + # See https://www.nginx.com/blog/websocket-nginx + map $http_upgrade $connection_upgrade { + default upgrade; + {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} + # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive + '' ''; + {{ else }} + '' close; + {{ end }} + } + + # Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server. + # If no such header is provided, it can provide a random value. + map $http_x_request_id $req_id { + default $http_x_request_id; + {{ if $cfg.GenerateRequestID }} + "" $request_id; + {{ end }} + } + + # Cache for internal auth checks + proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; + + {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} + # We can't use $proxy_add_x_forwarded_for because the realip module + # replaces the remote_addr too soon + map $http_x_forwarded_for $full_x_forwarded_for { + {{ if $all.Cfg.UseProxyProtocol }} + default "$http_x_forwarded_for, $proxy_protocol_addr"; + '' "$proxy_protocol_addr"; + {{ else }} + default "$http_x_forwarded_for, $realip_remote_addr"; + '' "$realip_remote_addr"; + {{ end}} + } + + {{ end }} + + # turn on session caching to drastically improve performance + {{ if $cfg.SSLSessionCache }} + ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; + ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; + {{ end }} + + {{ if not (empty $cfg.SSLSessionTicketKey ) }} + ssl_session_ticket_key /etc/ingress-controller/tickets.key; + {{ end }} + + {{ if not (empty $cfg.SSLCiphers) }} + # allow configuring custom ssl ciphers + ssl_ciphers '{{ $cfg.SSLCiphers }}'; + ssl_prefer_server_ciphers on; + {{ end }} + + {{ if not (empty $cfg.SSLDHParam) }} + # allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam + ssl_dhparam {{ $cfg.SSLDHParam }}; + {{ end }} + + {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} + proxy_intercept_errors on; + {{ end }} + + {{ if $cfg.EnableBrotli }} + brotli on; + brotli_comp_level {{ $cfg.BrotliLevel }}; + brotli_min_length {{ $cfg.BrotliMinLength }}; + brotli_types {{ $cfg.BrotliTypes }}; + {{ end }} + + {{ if $cfg.AllowBackendServerHeader }} + proxy_pass_header Server; + {{ end }} + + {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; + {{ end }} + + # END MIGRATED VARIOUS 1 + + {{ buildOpentelemetry $cfg $servers }} + + # Create a variable that contains the literal $ character. + # This works because the geo module will not resolve variables. + geo $literal_dollar { + default "$"; + } + + {{ range $errCode := $cfg.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} + + upstream upstream_balancer { + server 0.0.0.1; # placeholder + + balancer_by_lua_block { + balancer.balance() + } + + {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} + keepalive {{ $cfg.UpstreamKeepaliveConnections }}; + keepalive_time {{ $cfg.UpstreamKeepaliveTime }}; + keepalive_timeout {{ $cfg.UpstreamKeepaliveTimeout }}s; + keepalive_requests {{ $cfg.UpstreamKeepaliveRequests }}; + {{ end }} + } + + {{ range $rl := (filterRateLimits $servers ) }} + # Ratelimit {{ $rl.Name }} + geo $remote_addr $allowlist_{{ $rl.ID }} { + default 0; + {{ range $ip := $rl.Allowlist }} + {{ $ip }} 1;{{ end }} + } + + # Ratelimit {{ $rl.Name }} + map $allowlist_{{ $rl.ID }} $limit_{{ $rl.ID }} { + 0 {{ $cfg.LimitConnZoneVariable }}; + 1 ""; + } + {{ end }} + + {{/* 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 $servers) }} + {{ $zone }} + {{ end }} + + # Global filters + {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; + {{ end }} + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + map $http_user_agent $block_ua { + default 0; + + {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; + {{ end }} + } + {{ end }} + + {{ if gt (len $cfg.BlockReferers) 0 }} + map $http_referer $block_ref { + default 0; + + {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; + {{ end }} + } + {{ end }} + + {{/* Build server redirects (from/to www) */}} + {{ range $redirect := .RedirectServers }} + ## start server {{ $redirect.From }} + server { + server_name {{ $redirect.From }}; + + {{ buildHTTPListener $all $redirect.From }} + {{ buildHTTPSListener $all $redirect.From }} + + ssl_certificate_by_lua_block { + certificate.call() + } + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + if ($block_ua) { + return 403; + } + {{ end }} + {{ if gt (len $cfg.BlockReferers) 0 }} + if ($block_ref) { + return 403; + } + {{ end }} + + set_by_lua_block $redirect_to { + local request_uri = ngx.var.request_uri + if string.sub(request_uri, -1) == "/" then + request_uri = string.sub(request_uri, 1, -2) + end + + {{ if $cfg.UseForwardedHeaders }} + local redirectScheme + if not ngx.var.http_x_forwarded_proto then + redirectScheme = ngx.var.scheme + else + redirectScheme = ngx.var.http_x_forwarded_proto + end + {{ else }} + local redirectScheme = ngx.var.scheme + {{ end }} + + {{ if ne $all.ListenPorts.HTTPS 443 }} + {{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }} + return string.format("%s://%s%s%s", redirectScheme, "{{ $redirect.To }}", "{{ $redirect_port }}", request_uri) + {{ else }} + return string.format("%s://%s%s", redirectScheme, "{{ $redirect.To }}", request_uri) + {{ end }} + } + + return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; + } + ## end server {{ $redirect.From }} + {{ end }} + + {{ range $server := $servers }} + {{ range $location := $server.Locations }} + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} + upstream {{ buildAuthUpstreamName $location $server.Hostname }} { + {{- $externalAuth := $location.ExternalAuth }} + server {{ extractHostPort $externalAuth.URL }}; + + keepalive {{ $externalAuth.KeepaliveConnections }}; + keepalive_requests {{ $externalAuth.KeepaliveRequests }}; + keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; + } + ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} + {{ end }} + {{ end }} + {{ end }} + + {{ range $server := $servers }} + ## start server {{ $server.Hostname }} + server { + server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; + + {{ if $cfg.UseHTTP2 }} + http2 on; + {{ end }} + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + if ($block_ua) { + return 403; + } + {{ end }} + {{ if gt (len $cfg.BlockReferers) 0 }} + if ($block_ref) { + return 403; + } + {{ end }} + + {{ template "SERVER" serverConfig $all $server }} + + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics) }} + } + ## end server {{ $server.Hostname }} + + {{ end }} + + # backend for when default-backend-service is not configured or it does not have endpoints + server { + listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; + {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} + set $proxy_upstream_name "internal"; + + access_log off; + + location / { + return 404; + } + } + + # default server, used for NGINX healthcheck and access to nginx stats + server { + listen 127.0.0.1:{{ .StatusPort }}; + set $proxy_upstream_name "internal"; + + keepalive_timeout 0; + gzip off; + + access_log off; + + {{ if $cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + location {{ $healthzURI }} { + return 200; + } + + location /is-dynamic-lb-initialized { + content_by_lua_block { + local configuration = require("configuration") + local backend_data = configuration.get_backends_data() + if not backend_data then + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + return + end + + ngx.say("OK") + ngx.exit(ngx.HTTP_OK) + } + } + + location {{ .StatusPath }} { + stub_status on; + } + + location /configuration { + client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; + client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; + proxy_buffering off; + + content_by_lua_block { + configuration.call() + } + } + + location / { + content_by_lua_block { + ngx.exit(ngx.HTTP_NOT_FOUND) + } + } + } +} + +{{/* definition of templates to avoid repetitions */}} +{{ define "CUSTOM_ERRORS" }} + {{ $enableMetrics := .EnableMetrics }} + {{ $upstreamName := .UpstreamName }} + {{ range $errCode := .ErrorCodes }} + location @custom_{{ $upstreamName }}_{{ $errCode }} { + internal; + + proxy_intercept_errors off; + + proxy_set_header X-Code {{ $errCode }}; + proxy_set_header X-Format $http_accept; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Namespace $namespace; + proxy_set_header X-Ingress-Name $ingress_name; + proxy_set_header X-Service-Name $service_name; + proxy_set_header X-Service-Port $service_port; + proxy_set_header X-Request-ID $req_id; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $best_http_host; + + set $proxy_upstream_name {{ $upstreamName | quote }}; + + rewrite (.*) / break; + + proxy_pass http://upstream_balancer; + log_by_lua_block { + {{ if $enableMetrics }} + monitor.call() + {{ end }} + } + } + {{ end }} +{{ end }} + +{{/* 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 }}'; + } + + 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; + } +{{ end }} + +{{/* definition of server-template to avoid repetitions with server-alias */}} +{{ define "SERVER" }} + {{ $all := .First }} + {{ $server := .Second }} + + {{ buildHTTPListener $all $server.Hostname }} + {{ buildHTTPSListener $all $server.Hostname }} + + set $proxy_upstream_name "-"; + + {{ if not ( empty $server.CertificateAuth.MatchCN ) }} + {{ if gt (len $server.CertificateAuth.MatchCN) 0 }} + if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { + return 403 "client certificate unauthorized"; + } + {{ end }} + {{ end }} + + {{ if eq $server.Hostname "_" }} + ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; + {{ end }} + + ssl_certificate_by_lua_block { + certificate.call() + } + + {{ if not (empty $server.AuthTLSError) }} + # {{ $server.AuthTLSError }} + return 403; + {{ else }} + + {{ if not (empty $server.CertificateAuth.CAFileName) }} + # PEM sha: {{ $server.CertificateAuth.CASHA }} + ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; + ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; + ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; + + {{ if not (empty $server.CertificateAuth.CRLFileName) }} + # PEM sha: {{ $server.CertificateAuth.CRLSHA }} + ssl_crl {{ $server.CertificateAuth.CRLFileName }}; + {{ end }} + + {{ if not (empty $server.CertificateAuth.ErrorPage)}} + error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; + {{ end }} + {{ end }} + + {{ if not (empty $server.ProxySSL.CAFileName) }} + # PEM sha: {{ $server.ProxySSL.CASHA }} + proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; + proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; + proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; + proxy_ssl_verify {{ $server.ProxySSL.Verify }}; + proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; + {{ if not (empty $server.ProxySSL.ProxySSLName) }} + proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; + proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; + {{ end }} + {{ end }} + + {{ if not (empty $server.ProxySSL.PemFileName) }} + proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; + proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; + {{ end }} + + {{ if not (empty $server.SSLCiphers) }} + ssl_ciphers {{ $server.SSLCiphers }}; + {{ end }} + + {{ if not (empty $server.SSLPreferServerCiphers) }} + ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; + {{ end }} + + {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics) }} + {{ end }} + + {{ buildMirrorLocations $server.Locations }} + + {{ $enforceRegex := enforceRegexModifier $server.Locations }} + {{ range $location := $server.Locations }} + {{ $path := buildLocation $location $enforceRegex }} + {{ $proxySetHeader := proxySetHeader $location }} + {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + + {{ $externalAuth := $location.ExternalAuth }} + {{ if eq $applyGlobalAuth true }} + {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} + {{ end }} + + {{ if not (empty $location.Rewrite.AppRoot) }} + if ($uri = /) { + return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; + } + {{ end }} + + {{ if $authPath }} + location = {{ $authPath }} { + internal; + + {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} + opentelemetry on; + opentelemetry_propagate; + {{ end }} + + {{ if not $all.Cfg.EnableAuthAccessLog }} + access_log off; + {{ end }} + + {{ if $externalAuth.AuthCacheKey }} + set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; + set $cache_key ''; + + rewrite_by_lua_block { + ngx.var.cache_key = ngx.encode_base64(ngx.sha1_bin(ngx.var.tmp_cache_key)) + } + + proxy_cache auth_cache; + + {{- range $dur := $externalAuth.AuthCacheDuration }} + proxy_cache_valid {{ $dur }}; + {{- end }} + + proxy_cache_key "$cache_key"; + {{ end }} + + # ngx_auth_request module overrides variables in the parent request, + # therefore we have to explicitly set this variable again so that when the parent request + # resumes it has the correct value set for this variable so that Lua can pick backend correctly + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Forwarded-Proto ""; + proxy_set_header X-Request-ID $req_id; + + {{ if $externalAuth.Method }} + proxy_method {{ $externalAuth.Method }}; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Scheme $pass_access_scheme; + {{ end }} + + proxy_set_header Host {{ $externalAuth.Host }}; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Original-Method $request_method; + proxy_set_header X-Sent-From "nginx-ingress-controller"; + proxy_set_header X-Real-IP $remote_addr; + {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} + proxy_set_header X-Forwarded-For $full_x_forwarded_for; + {{ else }} + proxy_set_header X-Forwarded-For $remote_addr; + {{ end }} + + {{ if $externalAuth.RequestRedirect }} + proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; + {{ else }} + proxy_set_header X-Auth-Request-Redirect $request_uri; + {{ end }} + + {{ if $externalAuth.AuthCacheKey }} + proxy_buffering "on"; + {{ else }} + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; + {{ end }} + proxy_buffer_size {{ $location.Proxy.BufferSize }}; + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; + + proxy_ssl_server_name on; + proxy_pass_request_headers on; + {{ if isValidByteSize $location.Proxy.BodySize true }} + client_max_body_size {{ $location.Proxy.BodySize }}; + {{ end }} + {{ if isValidByteSize $location.ClientBodyBufferSize false }} + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; + {{ end }} + + # Pass the extracted client certificate to the auth provider + {{ if not (empty $server.CertificateAuth.CAFileName) }} + {{ if $server.CertificateAuth.PassCertToUpstream }} + proxy_set_header ssl-client-cert $ssl_client_escaped_cert; + {{ end }} + proxy_set_header ssl-client-verify $ssl_client_verify; + proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; + proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; + {{ end }} + + {{- range $line := buildAuthProxySetHeaders $externalAuth.ProxySetHeaders}} + {{ $line }} + {{- end }} + + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} + # The target is an upstream with HTTP keepalive, that is why the + # Connection header is cleared and the HTTP version is set to 1.1 as + # the Nginx documentation suggests: + # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive + proxy_http_version 1.1; + proxy_set_header Connection ""; + set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; + {{ else }} + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; + set $target {{ $externalAuth.URL }}; + {{ end }} + proxy_pass $target; + } + {{ end }} + + {{ if isLocationAllowed $location }} + {{ if $externalAuth.SigninURL }} + location {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }} { + internal; + + add_header Set-Cookie $auth_cookie; + + {{ if $location.CorsConfig.CorsEnabled }} + {{ template "CORS" $location }} + {{ end }} + + return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; + } + {{ end }} + {{ end }} + + location {{ $path }} { + {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.IngressPath) }} + set $namespace {{ $ing.Namespace | quote}}; + set $ingress_name {{ $ing.Rule | quote }}; + set $service_name {{ $ing.Service | quote }}; + set $service_port {{ $ing.ServicePort | quote }}; + set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; + set $global_rate_limit_exceeding n; + + {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} + + {{ if $location.Mirror.Source }} + mirror {{ $location.Mirror.Source }}; + mirror_request_body {{ $location.Mirror.RequestBody }}; + {{ end }} + + rewrite_by_lua_block { + lua_ingress.rewrite({{ locationConfigForLua $location $all }}) + balancer.rewrite() + plugins.run() + } + + # be careful with `access_by_lua_block` and `satisfy any` directives as satisfy any + # will always succeed when there's `access_by_lua_block` that does not have any lua code doing `ngx.exit(ngx.DECLINED)` + # other authentication method such as basic auth or external auth useless - all requests will be allowed. + #access_by_lua_block { + #} + + header_filter_by_lua_block { + lua_ingress.header() + plugins.run() + } + + body_filter_by_lua_block { + plugins.run() + } + + log_by_lua_block { + balancer.log() + {{ if $all.EnableMetrics }} + monitor.call() + {{ end }} + + plugins.run() + } + + {{ if not $location.Logs.Access }} + access_log off; + {{ end }} + + {{ if $location.Logs.Rewrite }} + rewrite_log on; + {{ end }} + + {{ if $location.HTTP2PushPreload }} + http2_push_preload on; + {{ end }} + + port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; + + set $balancer_ewma_score -1; + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; + set $proxy_host $proxy_upstream_name; + set $pass_access_scheme $scheme; + + {{ if $all.Cfg.UseProxyProtocol }} + set $pass_server_port $proxy_protocol_server_port; + {{ else }} + set $pass_server_port $server_port; + {{ end }} + + set $best_http_host $http_host; + set $pass_port $pass_server_port; + + set $proxy_alternative_upstream_name ""; + + {{ if isLocationAllowed $location }} + {{ if gt (len $location.Denylist.CIDR) 0 }} + {{ range $ip := $location.Denylist.CIDR }} + deny {{ $ip }};{{ end }} + {{ end }} + {{ if gt (len $location.Allowlist.CIDR) 0 }} + {{ range $ip := $location.Allowlist.CIDR }} + allow {{ $ip }};{{ end }} + deny all; + {{ end }} + + {{ if $location.CorsConfig.CorsEnabled }} + {{ template "CORS" $location }} + {{ end }} + + {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} + {{ if $authPath }} + # this location requires authentication + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + set $auth_cookie ''; + add_header Set-Cookie $auth_cookie; + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} + {{ $line }} + {{- end }} + # `auth_request` module does not support HTTP keepalives in upstream block: + # https://trac.nginx.org/nginx/ticket/1579 + access_by_lua_block { + local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) + if res.status == ngx.HTTP_OK then + ngx.var.auth_cookie = res.header['Set-Cookie'] + {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} + {{ $line }} + {{- end }} + return + end + if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then + ngx.exit(res.status) + end + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + } + {{ else }} + auth_request {{ $authPath }}; + auth_request_set $auth_cookie $upstream_http_set_cookie; + {{ if $externalAuth.AlwaysSetCookie }} + add_header Set-Cookie $auth_cookie always; + {{ else }} + add_header Set-Cookie $auth_cookie; + {{ end }} + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} + {{ $line }} + {{- end }} + {{ end }} + {{ end }} + + {{ if $externalAuth.SigninURL }} + set_escape_uri $escaped_request_uri $request_uri; + error_page 401 = {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }}; + {{ end }} + + {{ if $location.BasicDigestAuth.Secured }} + {{ if eq $location.BasicDigestAuth.Type "basic" }} + auth_basic {{ $location.BasicDigestAuth.Realm | quote }}; + auth_basic_user_file {{ $location.BasicDigestAuth.File }}; + {{ else }} + auth_digest {{ $location.BasicDigestAuth.Realm | quote }}; + auth_digest_user_file {{ $location.BasicDigestAuth.File }}; + {{ end }} + {{ $proxySetHeader }} Authorization ""; + {{ end }} + {{ end }} + + {{/* if the location contains a rate limit annotation, create one */}} + {{ $limits := buildRateLimit $location }} + {{ range $limit := $limits }} + {{ $limit }}{{ end }} + + {{ if isValidByteSize $location.Proxy.BodySize true }} + client_max_body_size {{ $location.Proxy.BodySize }}; + {{ end }} + {{ if isValidByteSize $location.ClientBodyBufferSize false }} + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; + {{ end }} + + {{/* By default use vhost as Host to upstream, but allow overrides */}} + {{ if not (empty $location.UpstreamVhost) }} + {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; + {{ else }} + {{ $proxySetHeader }} Host $best_http_host; + {{ end }} + + # Pass the extracted client certificate to the backend + {{ if not (empty $server.CertificateAuth.CAFileName) }} + {{ if $server.CertificateAuth.PassCertToUpstream }} + {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; + {{ end }} + {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; + {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; + {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; + {{ end }} + + # Allow websocket connections + {{ $proxySetHeader }} Upgrade $http_upgrade; + {{ if $location.Connection.Enabled}} + {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; + {{ else }} + {{ $proxySetHeader }} Connection $connection_upgrade; + {{ end }} + + {{ $proxySetHeader }} X-Request-ID $req_id; + {{ $proxySetHeader }} X-Real-IP $remote_addr; + {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} + {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; + {{ else }} + {{ $proxySetHeader }} X-Forwarded-For $remote_addr; + {{ end }} + {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; + {{ $proxySetHeader }} X-Forwarded-Port $pass_port; + {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; + {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; + {{ if $all.Cfg.ProxyAddOriginalURIHeader }} + {{ $proxySetHeader }} X-Original-URI $request_uri; + {{ end }} + {{ $proxySetHeader }} X-Scheme $pass_access_scheme; + + # Pass the original X-Forwarded-For + {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; + + # mitigate HTTPoxy Vulnerability + # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ + {{ $proxySetHeader }} Proxy ""; + + # Custom headers to proxied server + {{ range $k, $v := $all.ProxySetHeaders }} + {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; + {{ end }} + + proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; + proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; + proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; + + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; + proxy_buffer_size {{ $location.Proxy.BufferSize }}; + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; + {{ if isValidByteSize $location.Proxy.ProxyMaxTempFileSize true }} + proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; + {{ end }} + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; + + 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 $all.Cfg.RetryNonIdempotent }}; + proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; + + {{ if or (eq $location.BackendProtocol "GRPC") (eq $location.BackendProtocol "GRPCS") }} + # Grpc settings + grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; + grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; + grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; + {{ end }} + + {{ if $location.CustomHeaders }} + # Custom Response Headers + {{ range $k, $v := $location.CustomHeaders.Headers }} + more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; + {{ end }} + {{ end }} + + {{/* if we are sending the request to a custom default backend, we add the required headers */}} + {{ if (hasPrefix $location.Backend "custom-default-backend-") }} + proxy_set_header X-Code 503; + proxy_set_header X-Format $http_accept; + proxy_set_header X-Namespace $namespace; + proxy_set_header X-Ingress-Name $ingress_name; + proxy_set_header X-Service-Name $service_name; + proxy_set_header X-Service-Port $service_port; + proxy_set_header X-Request-ID $req_id; + {{ end }} + + {{ if $location.Satisfy }} + satisfy {{ $location.Satisfy }}; + {{ end }} + + {{/* if a location-specific error override is set, add the proxy_intercept here */}} + {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} + # Custom error pages per ingress + proxy_intercept_errors on; + {{ end }} + + {{ range $errCode := $location.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} + + {{ if (eq $location.BackendProtocol "FCGI") }} + include /etc/nginx/fastcgi_params; + {{ end }} + {{- if $location.FastCGI.Index -}} + fastcgi_index {{ $location.FastCGI.Index | quote }}; + {{- end -}} + {{ range $k, $v := $location.FastCGI.Params }} + fastcgi_param {{ $k }} {{ $v | quote }}; + {{ end }} + + {{ if not (empty $location.Redirect.URL) }} + return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; + {{ end }} + + {{ buildProxyPass $server.Hostname $all.Backends $location }} + {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; + {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; + {{ end }} + {{ else }} + # Location denied. Reason: {{ $location.Denied | quote }} + return 503; + {{ end }} + {{ if not (empty $location.ProxySSL.CAFileName) }} + # PEM sha: {{ $location.ProxySSL.CASHA }} + proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; + proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; + proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; + proxy_ssl_verify {{ $location.ProxySSL.Verify }}; + proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; + {{ end }} + + {{ if not (empty $location.ProxySSL.ProxySSLName) }} + proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; + {{ end }} + {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} + proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; + {{ end }} + + {{ if not (empty $location.ProxySSL.PemFileName) }} + proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; + proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; + {{ end }} + } + {{ end }} + {{ end }} + + {{ if eq $server.Hostname "_" }} + # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} + location {{ $all.HealthzURI }} { + + {{ if $all.Cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + + 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 { + + {{ if $all.Cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + + {{ range $v := $all.NginxStatusIpv4Whitelist }} + allow {{ $v }}; + {{ end }} + {{ if $all.IsIPV6Enabled -}} + {{ range $v := $all.NginxStatusIpv6Whitelist }} + allow {{ $v }}; + {{ end }} + {{ end -}} + deny all; + + access_log off; + stub_status on; + } + + {{ end }} + +{{ end }} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index 8c07b61c28..437a59ef5c 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -85,6 +85,15 @@ func buildResolversInternal(res []net.IP, disableIpv6 bool) []string { return r } +// buildMapDirective is used to build a map directive +func buildMapDirective(name, variable string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { + return &ngx_crossplane.Directive{ + Directive: "map", + Args: []string{name, variable}, + Block: block, + } +} + func boolToStr(b bool) string { if b { return "on" From c8328ff13376e7c8afe3b899671d2a5486ff40ae Mon Sep 17 00:00:00 2001 From: Erlison Santos <98214640+MrErlison@users.noreply.github.com> Date: Fri, 26 Jul 2024 20:02:57 -0300 Subject: [PATCH 03/17] Fix move nested if statement (#11688) --- internal/ingress/controller/template/crossplane/http.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index c8b49d8345..f5f39e1d51 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -107,9 +107,10 @@ func (c *Template) buildHTTP() { if cfg.HTTP2MaxHeaderSize != "" && cfg.HTTP2MaxFieldSize != "" { httpBlock = append(httpBlock, buildDirective("http2_max_field_size", cfg.HTTP2MaxFieldSize)) httpBlock = append(httpBlock, buildDirective("http2_max_header_size", cfg.HTTP2MaxHeaderSize)) - if cfg.HTTP2MaxRequests > 0 { - httpBlock = append(httpBlock, buildDirective("http2_max_requests", cfg.HTTP2MaxRequests)) - } + } + + if cfg.HTTP2MaxRequests > 0 { + httpBlock = append(httpBlock, buildDirective("http2_max_requests", cfg.HTTP2MaxRequests)) } if cfg.UseGzip { From 69a3a4d6d2198e39f5f384483dd4f9319e3ba307 Mon Sep 17 00:00:00 2001 From: Erlison Santos <98214640+MrErlison@users.noreply.github.com> Date: Fri, 26 Jul 2024 20:08:22 -0300 Subject: [PATCH 04/17] Add tests for tags with custom values (#11686) * Add tests for tags with custom values * Fix typo in comments --- .../template/crossplane/crossplane_test.go | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 9b6b2fa9a5..71765d677f 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -63,14 +63,61 @@ func TestCrossplaneTemplate(t *testing.T) { require.NoError(t, err) require.NoError(t, mimeFile.Close()) - t.Run("it should be able to marshall and unmarshall the current configuration", func(t *testing.T) { - tplConfig := &config.TemplateConfig{ - Cfg: config.NewDefault(), - } + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + tpl := crossplane.NewTemplate() + + t.Run("it should be able to marshall and unmarshall the default configuration", func(t *testing.T) { tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) + + t.Run("it should be able to marshall and unmarshall the specified configuration", func(t *testing.T) { + tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + + tplConfig.Cfg.UseProxyProtocol = true + + tplConfig.Cfg.GRPCBufferSizeKb = 10 // default 0 + + tplConfig.Cfg.HTTP2MaxHeaderSize = "10" // default "" + tplConfig.Cfg.HTTP2MaxFieldSize = "10" // default "" + tplConfig.Cfg.HTTP2MaxRequests = 1 // default 0 + + tplConfig.Cfg.UseGzip = true // default false + tplConfig.Cfg.GzipDisable = "enable" + + tplConfig.Cfg.ShowServerTokens = true // default false + + tplConfig.Cfg.DisableAccessLog = false // TODO: test true + tplConfig.Cfg.DisableHTTPAccessLog = false + tplConfig.Cfg.EnableSyslog = true + tplConfig.Cfg.SyslogHost = "localhost" + + // Example: openssl rand 80 | openssl enc -A -base64 + tplConfig.Cfg.SSLSessionTicketKey = "lOj3+7Xe21K9GapKqqPIw/gCQm5S4C2lK8pVne6drEik0QqOQHAw1AaPSMdbAvXx2zZKKPCEG98+g3hzftmrfnePSIvokIIE+hHto3Kj1HQ=" + + tplConfig.Cfg.CustomHTTPErrors = []int{1024, 2048} + + tplConfig.Cfg.AllowBackendServerHeader = true // default false + tplConfig.Cfg.BlockCIDRs = []string{"192.168.0.0/24", " 200.200.0.0/16 "} // default 0 + tplConfig.Cfg.BlockUserAgents = []string{"someuseragent", " another/user-agent "} // default 0 - tpl := crossplane.NewTemplate() + tpl = crossplane.NewTemplate() tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) require.NoError(t, err) From c39de84a951d1e285c44dc0d721ea9728f84006d Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 28 Jul 2024 19:19:35 -0300 Subject: [PATCH 05/17] Add brotli module parser (#11690) --- .../controller/template/crossplane/config.go | 4 +- .../template/crossplane/crossplane_test.go | 66 ++++++++++++++-- .../crossplane/extramodules/README.md | 10 +++ .../crossplane/extramodules/analyze.go | 78 +++++++++++++++++++ .../crossplane/extramodules/brotli.go | 55 +++++++++++++ 5 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 internal/ingress/controller/template/crossplane/extramodules/README.md create mode 100644 internal/ingress/controller/template/crossplane/extramodules/analyze.go create mode 100644 internal/ingress/controller/template/crossplane/extramodules/brotli.go diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go index 369ce2fb3b..b754f55dc7 100644 --- a/internal/ingress/controller/template/crossplane/config.go +++ b/internal/ingress/controller/template/crossplane/config.go @@ -17,6 +17,8 @@ limitations under the License. package crossplane import ( + "strings" + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) @@ -32,7 +34,7 @@ func (c *Template) buildConfig() { }, } if c.tplConfig.Cfg.WorkerCPUAffinity != "" { - config.Parsed = append(config.Parsed, buildDirective("worker_cpu_affinity", c.tplConfig.Cfg.WorkerCPUAffinity)) + config.Parsed = append(config.Parsed, buildDirective("worker_cpu_affinity", strings.Split(c.tplConfig.Cfg.WorkerCPUAffinity, " "))) } if c.tplConfig.Cfg.EnableBrotli { diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 71765d677f..24b98f1328 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -17,6 +17,7 @@ limitations under the License. package crossplane_test import ( + "net" "os" "testing" @@ -25,6 +26,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/controller/config" "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane/extramodules" "k8s.io/ingress-nginx/pkg/apis/ingress" ) @@ -36,6 +38,8 @@ types { } ` +var resolvers = []net.IP{net.ParseIP("::1"), net.ParseIP("192.168.20.10")} + // TestTemplate should be a roundtrip test. // We should initialize the scenarios based on the template configuration // Then Parse and write a crossplane configuration, and roundtrip/parse back to check @@ -46,8 +50,12 @@ func TestCrossplaneTemplate(t *testing.T) { options := ngx_crossplane.ParseOptions{ ErrorOnUnknownDirectives: true, StopParsingOnError: true, - IgnoreDirectives: []string{"more_clear_headers"}, - DirectiveSources: []ngx_crossplane.MatchFunc{ngx_crossplane.DefaultDirectivesMatchFunc, ngx_crossplane.LuaDirectivesMatchFn}, + IgnoreDirectives: []string{"more_clear_headers", "more_set_headers"}, // TODO: Add more_set_headers + DirectiveSources: []ngx_crossplane.MatchFunc{ + ngx_crossplane.DefaultDirectivesMatchFunc, + ngx_crossplane.LuaDirectivesMatchFn, + extramodules.BrotliMatchFn, + }, LexOptions: ngx_crossplane.LexOptions{ Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, }, @@ -63,15 +71,43 @@ func TestCrossplaneTemplate(t *testing.T) { require.NoError(t, err) require.NoError(t, mimeFile.Close()) - tplConfig := &config.TemplateConfig{ - Cfg: config.NewDefault(), - } tpl := crossplane.NewTemplate() t.Run("it should be able to marshall and unmarshall the default configuration", func(t *testing.T) { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate tplConfig.Cfg.EnableBrotli = true tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = true + tplConfig.Cfg.UseForwardedHeaders = true + tplConfig.Cfg.LogFormatEscapeNone = true + tplConfig.Cfg.DisableAccessLog = true + tplConfig.Cfg.UpstreamKeepaliveConnections = 0 + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) + + t.Run("it should set the right logging configs", func(t *testing.T) { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + tplConfig.Cfg.DisableAccessLog = false + tplConfig.Cfg.HTTPAccessLogPath = "/lalala.log" tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) @@ -88,9 +124,21 @@ func TestCrossplaneTemplate(t *testing.T) { }) t.Run("it should be able to marshall and unmarshall the specified configuration", func(t *testing.T) { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + tplConfig.Cfg.WorkerCPUAffinity = "0001 0010 0100 1000" + tplConfig.Cfg.LuaSharedDicts = map[string]int{ + "configuration_data": 10240, + "certificate_data": 50, + } + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = false tplConfig.Cfg.UseProxyProtocol = true + tplConfig.Cfg.ProxyRealIPCIDR = []string{"192.168.0.20", "200.200.200.200"} + tplConfig.Cfg.LogFormatEscapeJSON = true tplConfig.Cfg.GRPCBufferSizeKb = 10 // default 0 @@ -107,6 +155,8 @@ func TestCrossplaneTemplate(t *testing.T) { tplConfig.Cfg.DisableHTTPAccessLog = false tplConfig.Cfg.EnableSyslog = true tplConfig.Cfg.SyslogHost = "localhost" + tplConfig.Cfg.SkipAccessLogURLs = []string{"aaa.a", "bbb.b"} + tplConfig.Cfg.SSLDHParam = "/some/dh.pem" // Example: openssl rand 80 | openssl enc -A -base64 tplConfig.Cfg.SSLSessionTicketKey = "lOj3+7Xe21K9GapKqqPIw/gCQm5S4C2lK8pVne6drEik0QqOQHAw1AaPSMdbAvXx2zZKKPCEG98+g3hzftmrfnePSIvokIIE+hHto3Kj1HQ=" @@ -117,6 +167,11 @@ func TestCrossplaneTemplate(t *testing.T) { tplConfig.Cfg.BlockCIDRs = []string{"192.168.0.0/24", " 200.200.0.0/16 "} // default 0 tplConfig.Cfg.BlockUserAgents = []string{"someuseragent", " another/user-agent "} // default 0 + tplConfig.AddHeaders = map[string]string{ + "someheader": "xpto", + "anotherheader": "blabla", + } + tpl = crossplane.NewTemplate() tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) @@ -130,5 +185,6 @@ func TestCrossplaneTemplate(t *testing.T) { _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) require.NoError(t, err) + // require.Equal(t, " ", string(content)) }) } diff --git a/internal/ingress/controller/template/crossplane/extramodules/README.md b/internal/ingress/controller/template/crossplane/extramodules/README.md new file mode 100644 index 0000000000..6bd8a4e6ba --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/README.md @@ -0,0 +1,10 @@ +# Extra modules +This folder contains the extra modules used by ingress-nginx and not yet +supported by nginx-go-crossplane + +The generation of the files is done using go-crossplane generator + +## Brotli +``` +go run ./cmd/generate/ -src-path=ngx_brotli/ -directive-map-name=brotliDirectives -match-func-name=BrotliMatchFn > ../ingress-crossplane/internal/ingress/controller/template/crossplane/extramodules/brotli.go +``` \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/extramodules/analyze.go b/internal/ingress/controller/template/crossplane/extramodules/analyze.go new file mode 100644 index 0000000000..72efe24749 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/analyze.go @@ -0,0 +1,78 @@ +/* +Copyright 2024 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. +*/ + +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// This file is an extraction from https://github.com/nginxinc/nginx-go-crossplane/blob/main/analyze.go +// +//nolint:unused +package extramodules + +// bit masks for different directive argument styles. +const ( + ngxConfNoArgs = 0x00000001 // 0 args + ngxConfTake1 = 0x00000002 // 1 args + ngxConfTake2 = 0x00000004 // 2 args + ngxConfTake3 = 0x00000008 // 3 args + ngxConfTake4 = 0x00000010 // 4 args + ngxConfTake5 = 0x00000020 // 5 args + ngxConfTake6 = 0x00000040 // 6 args + // ngxConfTake7 = 0x00000080 // 7 args (currently unused). + ngxConfBlock = 0x00000100 // followed by block + ngxConfExpr = 0x00000200 // directive followed by expression in parentheses `()` + ngxConfFlag = 0x00000400 // 'on' or 'off' + ngxConfAny = 0x00000800 // >=0 args + ngxConf1More = 0x00001000 // >=1 args + ngxConf2More = 0x00002000 // >=2 args + + // some helpful argument style aliases. + ngxConfTake12 = ngxConfTake1 | ngxConfTake2 + ngxConfTake13 = ngxConfTake1 | ngxConfTake3 + ngxConfTake23 = ngxConfTake2 | ngxConfTake3 + ngxConfTake34 = ngxConfTake3 | ngxConfTake4 + ngxConfTake123 = ngxConfTake12 | ngxConfTake3 + ngxConfTake1234 = ngxConfTake123 | ngxConfTake4 + + // bit masks for different directive locations. + ngxDirectConf = 0x00010000 // main file (not used) + ngxMgmtMainConf = 0x00020000 // mgmt // unique bitmask that may not match NGINX source + ngxMainConf = 0x00040000 // main context + ngxEventConf = 0x00080000 // events + ngxMailMainConf = 0x00100000 // mail + ngxMailSrvConf = 0x00200000 // mail > server + ngxStreamMainConf = 0x00400000 // stream + ngxStreamSrvConf = 0x00800000 // stream > server + ngxStreamUpsConf = 0x01000000 // stream > upstream + ngxHTTPMainConf = 0x02000000 // http + ngxHTTPSrvConf = 0x04000000 // http > server + ngxHTTPLocConf = 0x08000000 // http > location + ngxHTTPUpsConf = 0x10000000 // http > upstream + ngxHTTPSifConf = 0x20000000 // http > server > if + ngxHTTPLifConf = 0x40000000 // http > location > if + ngxHTTPLmtConf = 0x80000000 // http > location > limit_except +) + +// helpful directive location alias describing "any" context +// doesn't include ngxHTTPSifConf, ngxHTTPLifConf, ngxHTTPLmtConf, or ngxMgmtMainConf. +const ngxAnyConf = ngxMainConf | ngxEventConf | ngxMailMainConf | ngxMailSrvConf | + ngxStreamMainConf | ngxStreamSrvConf | ngxStreamUpsConf | + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxHTTPUpsConf | + ngxHTTPSifConf | ngxHTTPLifConf | ngxHTTPLmtConf diff --git a/internal/ingress/controller/template/crossplane/extramodules/brotli.go b/internal/ingress/controller/template/crossplane/extramodules/brotli.go new file mode 100644 index 0000000000..0e2762f985 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/brotli.go @@ -0,0 +1,55 @@ +/* +Copyright 2024 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. +*/ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package extramodules + +var brotliDirectives = map[string][]uint{ + "brotli": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, + }, + "brotli_buffers": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake2, + }, + "brotli_comp_level": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_min_length": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_static": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_types": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConf1More, + }, + "brotli_window": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, +} + + +func BrotliMatchFn(directive string) ([]uint, bool) { + m, ok := brotliDirectives[directive] + return m, ok +} \ No newline at end of file From abb87ca750d18700f4008f02fabee478adb18761 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 28 Jul 2024 20:56:57 -0300 Subject: [PATCH 06/17] Add upstream directives and some others (#11681) --- .../template/crossplane/crossplane_test.go | 14 +++++ .../controller/template/crossplane/http.go | 37 +++++++++++- .../template/crossplane/testdata/nginx.tmpl | 59 ++++++++++--------- .../controller/template/crossplane/utils.go | 13 ++-- 4 files changed, 89 insertions(+), 34 deletions(-) diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 24b98f1328..20be1af059 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -166,12 +166,26 @@ func TestCrossplaneTemplate(t *testing.T) { tplConfig.Cfg.AllowBackendServerHeader = true // default false tplConfig.Cfg.BlockCIDRs = []string{"192.168.0.0/24", " 200.200.0.0/16 "} // default 0 tplConfig.Cfg.BlockUserAgents = []string{"someuseragent", " another/user-agent "} // default 0 + tplConfig.Cfg.BlockReferers = []string{"someref", " anotherref", "escape\nref"} tplConfig.AddHeaders = map[string]string{ "someheader": "xpto", "anotherheader": "blabla", } + tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.BrotliLevel = 7 + tplConfig.Cfg.BrotliMinLength = 2 + tplConfig.Cfg.BrotliTypes = "application/xml+rss application/atom+xml" + + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.UpstreamKeepaliveConnections = 15 + + tplConfig.Cfg.UpstreamKeepaliveConnections = 200 + tplConfig.Cfg.UpstreamKeepaliveTime = "60s" + tplConfig.Cfg.UpstreamKeepaliveTimeout = 200 + tplConfig.Cfg.UpstreamKeepaliveRequests = 15 + tpl = crossplane.NewTemplate() tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index f5f39e1d51..d6dd81c133 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -81,6 +81,7 @@ func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { return httpBlock } +//nolint:gocyclo func (c *Template) buildHTTP() { cfg := c.tplConfig.Cfg httpBlock := c.initHTTPDirectives() @@ -239,7 +240,7 @@ func (c *Template) buildHTTP() { buildDirective("brotli", "on"), buildDirective("brotli_comp_level", cfg.BrotliLevel), buildDirective("brotli_min_length", cfg.BrotliMinLength), - buildDirective("brotli_types", cfg.BrotliTypes), + buildDirective("brotli_types", strings.Split(cfg.BrotliTypes, " ")), ) } @@ -249,6 +250,40 @@ func (c *Template) buildHTTP() { } } + blockUpstreamDirectives := ngx_crossplane.Directives{ + buildDirective("server", "0.0.0.1"), + buildBlockDirective("balancer_by_lua_block", nil, ngx_crossplane.Directives{buildDirective("balancer.balance()")}), + } + if c.tplConfig.Cfg.UpstreamKeepaliveConnections > 0 { + blockUpstreamDirectives = append(blockUpstreamDirectives, + buildDirective("keepalive", c.tplConfig.Cfg.UpstreamKeepaliveConnections), + buildDirective("keepalive_time", c.tplConfig.Cfg.UpstreamKeepaliveTime), + buildDirective("keepalive_timeout", seconds(c.tplConfig.Cfg.UpstreamKeepaliveTimeout)), + buildDirective("keepalive_requests", c.tplConfig.Cfg.UpstreamKeepaliveRequests), + ) + } + httpBlock = append(httpBlock, buildBlockDirective("upstream", []string{"upstream_balancer"}, blockUpstreamDirectives)) + + for i := range cfg.BlockCIDRs { + httpBlock = append(httpBlock, buildDirective("deny", strings.TrimSpace(cfg.BlockCIDRs[i]))) + } + + if len(cfg.BlockUserAgents) > 0 { + uaDirectives := ngx_crossplane.Directives{buildDirective("default", 0)} + for i := range cfg.BlockUserAgents { + uaDirectives = append(uaDirectives, buildDirective(strings.TrimSpace(cfg.BlockUserAgents[i]), 1)) + } + httpBlock = append(httpBlock, buildMapDirective("$http_user_agent", "$block_ua", uaDirectives)) + } + + if len(cfg.BlockReferers) > 0 { + refDirectives := ngx_crossplane.Directives{buildDirective("default", 0)} + for i := range cfg.BlockReferers { + refDirectives = append(refDirectives, buildDirective(strings.TrimSpace(cfg.BlockReferers[i]), 1)) + } + httpBlock = append(httpBlock, buildMapDirective("$http_referer", "$block_ref", refDirectives)) + } + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", Block: httpBlock, diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl index cf9d200065..c65a400c45 100644 --- a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl +++ b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl @@ -360,18 +360,27 @@ http { {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; {{ end }} - # END MIGRATED VARIOUS 1 + # Global filters + {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; + {{ end }} - {{ buildOpentelemetry $cfg $servers }} + {{ if gt (len $cfg.BlockUserAgents) 0 }} + map $http_user_agent $block_ua { + default 0; - # Create a variable that contains the literal $ character. - # This works because the geo module will not resolve variables. - geo $literal_dollar { - default "$"; + {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; + {{ end }} } + {{ end }} - {{ range $errCode := $cfg.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} + {{ if gt (len $cfg.BlockReferers) 0 }} + map $http_referer $block_ref { + default 0; + + {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; + {{ end }} + } + {{ end }} upstream upstream_balancer { server 0.0.0.1; # placeholder @@ -388,6 +397,19 @@ http { {{ end }} } + # END MIGRATED VARIOUS 1 + + {{ buildOpentelemetry $cfg $servers }} + + # Create a variable that contains the literal $ character. + # This works because the geo module will not resolve variables. + geo $literal_dollar { + default "$"; + } + + {{ range $errCode := $cfg.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} + {{ range $rl := (filterRateLimits $servers ) }} # Ratelimit {{ $rl.Name }} geo $remote_addr $allowlist_{{ $rl.ID }} { @@ -409,27 +431,6 @@ http { {{ $zone }} {{ end }} - # Global filters - {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - map $http_user_agent $block_ua { - default 0; - - {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; - {{ end }} - } - {{ end }} - - {{ if gt (len $cfg.BlockReferers) 0 }} - map $http_referer $block_ref { - default 0; - - {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; - {{ end }} - } - {{ end }} {{/* Build server redirects (from/to www) */}} {{ range $redirect := .RedirectServers }} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index 437a59ef5c..b10363a6fa 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -85,15 +85,20 @@ func buildResolversInternal(res []net.IP, disableIpv6 bool) []string { return r } -// buildMapDirective is used to build a map directive -func buildMapDirective(name, variable string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { +// buildBlockDirective is used to build a block directive +func buildBlockDirective(blockName string, args []string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { return &ngx_crossplane.Directive{ - Directive: "map", - Args: []string{name, variable}, + Directive: blockName, + Args: args, Block: block, } } +// buildMapDirective is used to build a map directive +func buildMapDirective(name, variable string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { + return buildBlockDirective("map", []string{name, variable}, block) +} + func boolToStr(b bool) string { if b { return "on" From 1ef5295ffed4db20b3a790e5b6a75b224e5790be Mon Sep 17 00:00:00 2001 From: Erlison Santos <98214640+MrErlison@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:57:00 -0300 Subject: [PATCH 07/17] Add CustomHTTPErrors directive (#11699) * Add CustomHTTPErrors directive * Fix: Add missing '=' in error_page directive --- internal/ingress/controller/template/crossplane/http.go | 5 +++++ .../controller/template/crossplane/testdata/nginx.tmpl | 1 + 2 files changed, 6 insertions(+) diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index d6dd81c133..09d6814116 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -284,6 +284,11 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildMapDirective("$http_referer", "$block_ref", refDirectives)) } + for _, v := range cfg.CustomHTTPErrors { + httpBlock = append(httpBlock, buildDirective("error_page", v, "=", + fmt.Sprintf("@custom_upstream-default-backend_%d", v))) + } + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", Block: httpBlock, diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl index c65a400c45..ff68b1d9ae 100644 --- a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl +++ b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl @@ -407,6 +407,7 @@ http { default "$"; } + # MIGRATED {{ range $errCode := $cfg.CustomHTTPErrors }} error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} From 6c457501745ca1573341a06fd488be46a99a2504 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Tue, 10 Sep 2024 20:20:56 -0300 Subject: [PATCH 08/17] Continue go crossplane (#11964) --- go.mod | 2 +- go.sum | 4 +- go.work.sum | 3 + images/kube-webhook-certgen/rootfs/go.mod | 2 +- images/kube-webhook-certgen/rootfs/go.sum | 3 +- .../controller/template/crossplane/config.go | 14 + .../template/crossplane/crossplane_test.go | 2 +- .../controller/template/crossplane/http.go | 38 +- .../{nginx.tmpl => nginx-new-orig.tmpl} | 674 +++++--- .../crossplane/testdata/nginx-new.tmpl | 1419 +++++++++++++++++ .../controller/template/crossplane/utils.go | 29 + magefiles/go.mod | 10 +- magefiles/go.sum | 14 +- 13 files changed, 1947 insertions(+), 267 deletions(-) rename internal/ingress/controller/template/crossplane/testdata/{nginx.tmpl => nginx-new-orig.tmpl} (73%) create mode 100644 internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl diff --git a/go.mod b/go.mod index 58616be3e2..efa9f575a0 100644 --- a/go.mod +++ b/go.mod @@ -50,8 +50,8 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/jstemmer/go-junit-report v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect diff --git a/go.sum b/go.sum index 2231efd129..c6f0fe38bb 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM= -github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I= +github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 h1:9WsegDYiSKtZXru+NcOB4z7iqb00n4atjmQlyy5TRXI= +github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0/go.mod h1:TeVdzh+5QB5IpWDJAU/uviXA6kOg9yXzLrrjeLKJXqY= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= diff --git a/go.work.sum b/go.work.sum index cd5d5e0627..f0fa139ce1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -25,6 +25,7 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -82,6 +83,7 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= @@ -160,5 +162,6 @@ google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/kms v0.32.0/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= diff --git a/images/kube-webhook-certgen/rootfs/go.mod b/images/kube-webhook-certgen/rootfs/go.mod index d1fcf49092..651e584dbc 100644 --- a/images/kube-webhook-certgen/rootfs/go.mod +++ b/images/kube-webhook-certgen/rootfs/go.mod @@ -14,7 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.3 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect diff --git a/images/kube-webhook-certgen/rootfs/go.sum b/images/kube-webhook-certgen/rootfs/go.sum index 429552b0e2..a0ceaac6e8 100644 --- a/images/kube-webhook-certgen/rootfs/go.sum +++ b/images/kube-webhook-certgen/rootfs/go.sum @@ -3,8 +3,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.3 h1:yagOQz/38xJmcNeZJtrUcKjkHRltIaIFXKWeG1SkWGE= -github.com/emicklei/go-restful/v3 v3.11.3/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go index b754f55dc7..5ad5467652 100644 --- a/internal/ingress/controller/template/crossplane/config.go +++ b/internal/ingress/controller/template/crossplane/config.go @@ -44,5 +44,19 @@ func (c *Template) buildConfig() { ) } + if shouldLoadAuthDigestModule(c.tplConfig.Servers) { + config.Parsed = append(config.Parsed, buildDirective("load_module", "/etc/nginx/modules/ngx_http_auth_digest_module.so")) + } + + if c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers) { + config.Parsed = append(config.Parsed, buildDirective("load_module", "/etc/nginx/modules/otel_ngx_module.so")) + } + + if c.tplConfig.Cfg.UseGeoIP2 { + config.Parsed = append(config.Parsed, + buildDirective("load_module", "/etc/nginx/modules/ngx_http_geoip2_module.so"), + ) + } + c.config = config } diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 20be1af059..08ea94276d 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -53,7 +53,7 @@ func TestCrossplaneTemplate(t *testing.T) { IgnoreDirectives: []string{"more_clear_headers", "more_set_headers"}, // TODO: Add more_set_headers DirectiveSources: []ngx_crossplane.MatchFunc{ ngx_crossplane.DefaultDirectivesMatchFunc, - ngx_crossplane.LuaDirectivesMatchFn, + ngx_crossplane.MatchLuaLatest, extramodules.BrotliMatchFn, }, LexOptions: ngx_crossplane.LexOptions{ diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index 09d6814116..dc3e43dcee 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -28,9 +28,11 @@ func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { cfg := c.tplConfig.Cfg httpBlock := ngx_crossplane.Directives{ buildDirective("lua_package_path", "/etc/nginx/lua/?.lua;;"), + buildDirective("lua_shared_dict", "luaconfig", "5m"), + buildDirective("init_by_lua_file", "/etc/nginx/lua/ngx_conf_init.lua"), + buildDirective("init_worker_by_lua_file", "/etc/nginx/lua/ngx_conf_init_worker.lua"), buildDirective("include", c.mimeFile), buildDirective("default_type", cfg.DefaultType), - buildDirective("real_ip_recursive", "on"), buildDirective("aio", "threads"), buildDirective("aio_write", cfg.EnableAioWrite), buildDirective("server_tokens", cfg.ShowServerTokens), @@ -85,8 +87,11 @@ func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { func (c *Template) buildHTTP() { cfg := c.tplConfig.Cfg httpBlock := c.initHTTPDirectives() - httpBlock = append(httpBlock, buildLuaSharedDictionaries(&c.tplConfig.Cfg)...) + httpBlock = append(httpBlock, buildLuaSharedDictionaries(&cfg)...) + if c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers) { + httpBlock = append(httpBlock, buildDirective("opentelemetry_config", cfg.OpentelemetryConfig)) + } // Real IP dealing if (cfg.UseForwardedHeaders || cfg.UseProxyProtocol) || cfg.EnableRealIP { if cfg.UseProxyProtocol { @@ -94,7 +99,7 @@ func (c *Template) buildHTTP() { } else { httpBlock = append(httpBlock, buildDirective("real_ip_header", cfg.ForwardedForHeader)) } - + httpBlock = append(httpBlock, buildDirective("real_ip_recursive", "on")) for k := range cfg.ProxyRealIPCIDR { httpBlock = append(httpBlock, buildDirective("set_real_ip_from", cfg.ProxyRealIPCIDR[k])) } @@ -128,10 +133,25 @@ func (c *Template) buildHTTP() { } } + if cfg.EnableBrotli { + httpBlock = append(httpBlock, buildDirective("brotli", "on")) + httpBlock = append(httpBlock, buildDirective("brotli_comp_level", cfg.BrotliLevel)) + httpBlock = append(httpBlock, buildDirective("brotli_min_length", cfg.BrotliMinLength)) + httpBlock = append(httpBlock, buildDirective("brotli_types", cfg.BrotliTypes)) + } + if !cfg.ShowServerTokens { httpBlock = append(httpBlock, buildDirective("more_clear_headers", "Server")) } + httpBlock = append(httpBlock, buildBlockDirective( + "geo", + []string{"$literal_dollar"}, + ngx_crossplane.Directives{ + buildDirective("default", "$"), + }, + )) + if len(c.tplConfig.AddHeaders) > 0 { additionalHeaders := make([]string, 0) for headerName, headerValue := range c.tplConfig.AddHeaders { @@ -206,6 +226,8 @@ func (c *Template) buildHTTP() { httpUpgradeMap := ngx_crossplane.Directives{buildDirective("default", "upgrade")} if cfg.UpstreamKeepaliveConnections < 1 { httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "close")) + } else { + httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "")) } httpBlock = append(httpBlock, buildMapDirective("$http_upgrade", "$connection_upgrade", httpUpgradeMap)) @@ -220,7 +242,7 @@ func (c *Template) buildHTTP() { if cfg.UseProxyProtocol { forwardForMap = append(forwardForMap, buildDirective("default", "$http_x_forwarded_for, $proxy_protocol_addr"), - buildDirective("", "$http_x_forwarded_for, $proxy_protocol_addr"), + buildDirective("", "$proxy_protocol_addr"), ) } else { forwardForMap = append(forwardForMap, @@ -244,15 +266,13 @@ func (c *Template) buildHTTP() { ) } - if len(cfg.HideHeaders) > 0 { - for k := range cfg.HideHeaders { - httpBlock = append(httpBlock, buildDirective("proxy_hide_header", cfg.HideHeaders[k])) - } + for k := range cfg.HideHeaders { + httpBlock = append(httpBlock, buildDirective("proxy_hide_header", cfg.HideHeaders[k])) } blockUpstreamDirectives := ngx_crossplane.Directives{ buildDirective("server", "0.0.0.1"), - buildBlockDirective("balancer_by_lua_block", nil, ngx_crossplane.Directives{buildDirective("balancer.balance()")}), + buildDirective("balancer_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_balancer.lua"), } if c.tplConfig.Cfg.UpstreamKeepaliveConnections > 0 { blockUpstreamDirectives = append(blockUpstreamDirectives, diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl similarity index 73% rename from internal/ingress/controller/template/crossplane/testdata/nginx.tmpl rename to internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl index ff68b1d9ae..b7fa803829 100644 --- a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl +++ b/internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl @@ -7,11 +7,16 @@ {{ $proxyHeaders := .ProxySetHeaders }} {{ $addHeaders := .AddHeaders }} -# MIGRATED -pid {{ .PID }}; +# Configuration checksum: {{ $all.Cfg.Checksum }} -# MODULES ARE NOT MIGRATED YET! -{{ if $cfg.EnableBrotli }} +# setup custom paths that do not require root access +pid {{ .PID }}; # OK + +{{ if $cfg.UseGeoIP2 }} #OK +load_module /etc/nginx/modules/ngx_http_geoip2_module.so; +{{ end }} + +{{ if $cfg.EnableBrotli }} #OK load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; {{ end }} @@ -20,114 +25,56 @@ load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; {{ end }} +{{ if (shouldLoadModSecurityModule $cfg $servers) }} +load_module /etc/nginx/modules/ngx_http_modsecurity_module.so; +{{ end }} + {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} load_module /etc/nginx/modules/otel_ngx_module.so; {{ end }} -# MIGRATED 1 -daemon off; +daemon off; # OK -worker_processes {{ $cfg.WorkerProcesses }}; -{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} +worker_processes {{ $cfg.WorkerProcesses }}; # OK +{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} # OK worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; {{ end }} -worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; +worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; # OK {{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}} {{/* avoid waiting too long during a reload */}} -worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; +worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; # OK + +# REMOVED +# {{ if not (empty $cfg.MainSnippet) }} +# {{ $cfg.MainSnippet }} +# {{ end }} events { - multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; - worker_connections {{ $cfg.MaxWorkerConnections }}; - use epoll; - {{ range $index , $v := $cfg.DebugConnections }} - debug_connection {{ $v }}; + multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; # OK + worker_connections {{ $cfg.MaxWorkerConnections }}; # OK + use epoll; # OK + {{ range $index , $v := $cfg.DebugConnections }} # OK + debug_connection {{ $v }}; # OK {{ end }} } -# END MIGRATED 1 - http { {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} opentelemetry_config {{ $cfg.OpentelemetryConfig }}; {{ end }} - # MIGRATED - lua_package_path "/etc/nginx/lua/?.lua;;"; - - # MIGRATED - {{ buildLuaSharedDictionaries $cfg $servers }} - - # NOT MIGRATED - init_by_lua_block { - collectgarbage("collect") - - -- init modules - local ok, res - - ok, res = pcall(require, "lua_ingress") - if not ok then - error("require failed: " .. tostring(res)) - else - lua_ingress = res - lua_ingress.set_config({{ configForLua $all }}) - end - - ok, res = pcall(require, "configuration") - if not ok then - error("require failed: " .. tostring(res)) - else - configuration = res - configuration.prohibited_localhost_port = '{{ .StatusPort }}' - end - - ok, res = pcall(require, "balancer") - if not ok then - error("require failed: " .. tostring(res)) - else - balancer = res - end - - {{ if $all.EnableMetrics }} - ok, res = pcall(require, "monitor") - if not ok then - error("require failed: " .. tostring(res)) - else - monitor = res - end - {{ end }} + lua_package_path "/etc/nginx/lua/?.lua;;"; # OK - ok, res = pcall(require, "certificate") - if not ok then - error("require failed: " .. tostring(res)) - else - certificate = res - certificate.is_ocsp_stapling_enabled = {{ $cfg.EnableOCSP }} - end - - ok, res = pcall(require, "plugins") - if not ok then - error("require failed: " .. tostring(res)) - else - plugins = res - end - -- load all plugins that'll be used here - plugins.init({ {{ range $idx, $plugin := $cfg.Plugins }}{{ if $idx }},{{ end }}{{ $plugin | quote }}{{ end }} }) - } + {{ buildLuaSharedDictionaries $cfg $servers }} # OK - init_worker_by_lua_block { - lua_ingress.init_worker() - balancer.init_worker() - {{ if $all.EnableMetrics }} - monitor.init_worker({{ $all.MonitorMaxBatchSize }}) - {{ end }} + lua_shared_dict luaconfig 5m; # OK - plugins.run() - } + init_by_lua_file /etc/nginx/lua/ngx_conf_init.lua; # OK + + init_worker_by_lua_file /etc/nginx/lua/ngx_conf_init_worker.lua; # OK - # MIGRATED VARIOUS 1 {{/* 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 }} @@ -143,6 +90,162 @@ http { {{ end }} {{ end }} + {{ if $all.Cfg.EnableModsecurity }} + modsecurity on; + + {{ if (not (empty $all.Cfg.ModsecuritySnippet)) }} + modsecurity_rules ' + {{ $all.Cfg.ModsecuritySnippet }} + '; + {{ else }} + modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf; + {{ end }} + + {{ if $all.Cfg.EnableOWASPCoreRules }} + modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf; + {{ end }} + + {{ end }} + + {{ if $cfg.UseGeoIP2 }} + # https://github.com/leev/ngx_http_geoip2_module#example-usage + + {{ range $index, $file := $all.MaxmindEditionFiles }} + {{ if eq $file "GeoLite2-Country.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_country_code source=$remote_addr country iso_code; + $geoip2_country_name source=$remote_addr country names en; + $geoip2_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_continent_code source=$remote_addr continent code; + $geoip2_continent_name source=$remote_addr continent names en; + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + } + {{ end }} + + {{ if eq $file "GeoIP2-Country.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_country_code source=$remote_addr country iso_code; + $geoip2_country_name source=$remote_addr country names en; + $geoip2_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_continent_code source=$remote_addr continent code; + $geoip2_continent_name source=$remote_addr continent names en; + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + } + {{ end }} + + {{ if eq $file "GeoLite2-City.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_city_country_code source=$remote_addr country iso_code; + $geoip2_city_country_name source=$remote_addr country names en; + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_city source=$remote_addr city names en; + $geoip2_city_geoname_id source=$remote_addr city geoname_id; + $geoip2_postal_code source=$remote_addr postal code; + $geoip2_dma_code source=$remote_addr location metro_code; + $geoip2_latitude source=$remote_addr location latitude; + $geoip2_longitude source=$remote_addr location longitude; + $geoip2_time_zone source=$remote_addr location time_zone; + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; + $geoip2_region_name source=$remote_addr subdivisions 0 names en; + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; + } + {{ end }} + + {{ if eq $file "GeoIP2-City.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_city_country_code source=$remote_addr country iso_code; + $geoip2_city_country_name source=$remote_addr country names en; + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_city source=$remote_addr city names en; + $geoip2_city_geoname_id source=$remote_addr city geoname_id; + $geoip2_postal_code source=$remote_addr postal code; + $geoip2_dma_code source=$remote_addr location metro_code; + $geoip2_latitude source=$remote_addr location latitude; + $geoip2_longitude source=$remote_addr location longitude; + $geoip2_time_zone source=$remote_addr location time_zone; + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; + $geoip2_region_name source=$remote_addr subdivisions 0 names en; + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; + } + {{ end }} + + {{ if eq $file "GeoLite2-ASN.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_asn source=$remote_addr autonomous_system_number; + $geoip2_org source=$remote_addr autonomous_system_organization; + } + {{ end }} + + {{ if eq $file "GeoIP2-ASN.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_asn source=$remote_addr autonomous_system_number; + $geoip2_org source=$remote_addr autonomous_system_organization; + } + {{ end }} + + {{ if eq $file "GeoIP2-ISP.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_isp source=$remote_addr isp; + $geoip2_isp_org source=$remote_addr organization; + $geoip2_asn source=$remote_addr default=0 autonomous_system_number; + } + {{ end }} + + {{ if eq $file "GeoIP2-Connection-Type.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Connection-Type.mmdb { + $geoip2_connection_type connection_type; + } + {{ end }} + + {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_is_anon source=$remote_addr is_anonymous; + $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; + $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; + $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; + $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; + $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; + } + {{ end }} + + {{ end }} + + {{ end }} + aio threads; {{ if $cfg.EnableAioWrite }} @@ -201,9 +304,18 @@ http { limit_req_status {{ $cfg.LimitReqStatusCode }}; limit_conn_status {{ $cfg.LimitConnStatusCode }}; + {{ buildOpentelemetry $cfg $servers }} + include /etc/nginx/mime.types; default_type {{ $cfg.DefaultType }}; + {{ if $cfg.EnableBrotli }} + brotli on; + brotli_comp_level {{ $cfg.BrotliLevel }}; + brotli_min_length {{ $cfg.BrotliMinLength }}; + brotli_types {{ $cfg.BrotliTypes }}; + {{ end }} + {{ if $cfg.UseGzip }} gzip on; gzip_comp_level {{ $cfg.GzipLevel }}; @@ -263,26 +375,6 @@ http { {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} - server_name_in_redirect off; - port_in_redirect off; - - ssl_protocols {{ $cfg.SSLProtocols }}; - - ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; - - # allow configuring ssl session tickets - ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; - - # slightly reduce the time-to-first-byte - ssl_buffer_size {{ $cfg.SSLBufferSize }}; - - ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; - # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} - ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; - ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; - - proxy_ssl_session_reuse on; - # See https://www.nginx.com/blog/websocket-nginx map $http_upgrade $connection_upgrade { default upgrade; @@ -303,9 +395,6 @@ http { {{ end }} } - # Cache for internal auth checks - proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; - {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} # We can't use $proxy_add_x_forwarded_for because the realip module # replaces the remote_addr too soon @@ -321,16 +410,35 @@ http { {{ end }} - # turn on session caching to drastically improve performance + # Create a variable that contains the literal $ character. + # This works because the geo module will not resolve variables. + geo $literal_dollar { + default "$"; + } + + server_name_in_redirect off; + port_in_redirect off; + + ssl_protocols {{ $cfg.SSLProtocols }}; + + ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; + + # turn on session caching to drastically improve performance {{ if $cfg.SSLSessionCache }} ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; {{ end }} + # allow configuring ssl session tickets + ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; + {{ if not (empty $cfg.SSLSessionTicketKey ) }} ssl_session_ticket_key /etc/ingress-controller/tickets.key; {{ end }} + # slightly reduce the time-to-first-byte + ssl_buffer_size {{ $cfg.SSLBufferSize }}; + {{ if not (empty $cfg.SSLCiphers) }} # allow configuring custom ssl ciphers ssl_ciphers '{{ $cfg.SSLCiphers }}'; @@ -342,16 +450,20 @@ http { ssl_dhparam {{ $cfg.SSLDHParam }}; {{ end }} + ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; + + # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} + ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; + ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; + {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} proxy_intercept_errors on; {{ end }} - {{ if $cfg.EnableBrotli }} - brotli on; - brotli_comp_level {{ $cfg.BrotliLevel }}; - brotli_min_length {{ $cfg.BrotliMinLength }}; - brotli_types {{ $cfg.BrotliTypes }}; - {{ end }} + {{ range $errCode := $cfg.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} + + proxy_ssl_session_reuse on; {{ if $cfg.AllowBackendServerHeader }} proxy_pass_header Server; @@ -360,34 +472,26 @@ http { {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; {{ end }} - # Global filters - {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - map $http_user_agent $block_ua { - default 0; - - {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; - {{ end }} - } - {{ end }} - - {{ if gt (len $cfg.BlockReferers) 0 }} - map $http_referer $block_ref { - default 0; - - {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; - {{ end }} - } + {{ if not (empty $cfg.HTTPSnippet) }} + # Custom code snippet configured in the configuration configmap + {{ $cfg.HTTPSnippet }} {{ end }} upstream upstream_balancer { + ### Attention!!! + # + # We no longer create "upstream" section for every backend. + # Backends are handled dynamically using Lua. If you would like to debug + # and see what backends ingress-nginx has in its memory you can + # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin. + # Once you have the plugin you can use "kubectl ingress-nginx backends" command to + # inspect current backends. + # + ### + server 0.0.0.1; # placeholder - balancer_by_lua_block { - balancer.balance() - } + balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer.lua; {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} keepalive {{ $cfg.UpstreamKeepaliveConnections }}; @@ -397,20 +501,6 @@ http { {{ end }} } - # END MIGRATED VARIOUS 1 - - {{ buildOpentelemetry $cfg $servers }} - - # Create a variable that contains the literal $ character. - # This works because the geo module will not resolve variables. - geo $literal_dollar { - default "$"; - } - - # MIGRATED - {{ range $errCode := $cfg.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} - {{ range $rl := (filterRateLimits $servers ) }} # Ratelimit {{ $rl.Name }} geo $remote_addr $allowlist_{{ $rl.ID }} { @@ -432,6 +522,30 @@ http { {{ $zone }} {{ end }} + # Cache for internal auth checks + proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; + + # Global filters + {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; + {{ end }} + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + map $http_user_agent $block_ua { + default 0; + + {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; + {{ end }} + } + {{ end }} + + {{ if gt (len $cfg.BlockReferers) 0 }} + map $http_referer $block_ref { + default 0; + + {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; + {{ end }} + } + {{ end }} {{/* Build server redirects (from/to www) */}} {{ range $redirect := .RedirectServers }} @@ -442,9 +556,7 @@ http { {{ buildHTTPListener $all $redirect.From }} {{ buildHTTPSListener $all $redirect.From }} - ssl_certificate_by_lua_block { - certificate.call() - } + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; {{ if gt (len $cfg.BlockUserAgents) 0 }} if ($block_ua) { @@ -457,30 +569,7 @@ http { } {{ end }} - set_by_lua_block $redirect_to { - local request_uri = ngx.var.request_uri - if string.sub(request_uri, -1) == "/" then - request_uri = string.sub(request_uri, 1, -2) - end - - {{ if $cfg.UseForwardedHeaders }} - local redirectScheme - if not ngx.var.http_x_forwarded_proto then - redirectScheme = ngx.var.scheme - else - redirectScheme = ngx.var.http_x_forwarded_proto - end - {{ else }} - local redirectScheme = ngx.var.scheme - {{ end }} - - {{ if ne $all.ListenPorts.HTTPS 443 }} - {{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }} - return string.format("%s://%s%s%s", redirectScheme, "{{ $redirect.To }}", "{{ $redirect_port }}", request_uri) - {{ else }} - return string.format("%s://%s%s", redirectScheme, "{{ $redirect.To }}", request_uri) - {{ end }} - } + set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; } @@ -528,7 +617,12 @@ http { {{ template "SERVER" serverConfig $all $server }} - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics) }} + {{ if not (empty $cfg.ServerSnippet) }} + # Custom code snippet configured in the configuration configmap + {{ $cfg.ServerSnippet }} + {{ end }} + + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics $cfg.EnableModsecurity) }} } ## end server {{ $server.Hostname }} @@ -549,6 +643,11 @@ http { # default server, used for NGINX healthcheck and access to nginx stats server { + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + listen 127.0.0.1:{{ .StatusPort }}; set $proxy_upstream_name "internal"; @@ -565,17 +664,7 @@ http { } location /is-dynamic-lb-initialized { - content_by_lua_block { - local configuration = require("configuration") - local backend_data = configuration.get_backends_data() - if not backend_data then - ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) - return - end - - ngx.say("OK") - ngx.exit(ngx.HTTP_OK) - } + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; } location {{ .StatusPath }} { @@ -587,27 +676,136 @@ http { client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; proxy_buffering off; - content_by_lua_block { - configuration.call() - } + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; } location / { - content_by_lua_block { - ngx.exit(ngx.HTTP_NOT_FOUND) - } + return 404; + } + } +} + +stream { + lua_package_path "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;"; + + lua_shared_dict tcp_udp_configuration_data 5M; + + {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} + + init_by_lua_file /etc/nginx/lua/ngx_conf_init_stream.lua; + + init_worker_by_lua_file /etc/nginx/lua/nginx/ngx_conf_init_tcp_udp.lua; + + lua_add_variable $proxy_upstream_name; + + log_format log_stream '{{ $cfg.LogFormatStream }}'; + + {{ if or $cfg.DisableAccessLog $cfg.DisableStreamAccessLog }} + access_log off; + {{ else }} + access_log {{ or $cfg.StreamAccessLogPath $cfg.AccessLogPath }} log_stream {{ $cfg.AccessLogParams }}; + {{ end }} + + + error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; + {{ if $cfg.EnableRealIP }} + {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} + set_real_ip_from {{ $trusted_ip }}; + {{ end }} + {{ end }} + + upstream upstream_balancer { + server 0.0.0.1:1234; # placeholder + balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer_tcp_udp.lua; + } + + server { + listen 127.0.0.1:{{ .StreamPort }}; + + access_log off; + + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_content_tcp_udp.lua; + } + + # TCP services + {{ range $tcpServer := .TCPBackends }} + server { + preread_by_lua_block { + ngx.var.proxy_upstream_name="tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}"; + } + + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ else }} + listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ else }} + listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ end }} + {{ end }} + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; + proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; + + proxy_pass upstream_balancer; + {{ if $tcpServer.Backend.ProxyProtocol.Encode }} + proxy_protocol on; + {{ end }} + } + {{ end }} + + # UDP services + {{ range $udpServer := .UDPBackends }} + server { + preread_by_lua_block { + ngx.var.proxy_upstream_name="udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}"; } + + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} + listen {{ $udpServer.Port }} udp; + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} + listen [::]:{{ $udpServer.Port }} udp; + {{ end }} + {{ end }} + proxy_responses {{ $cfg.ProxyStreamResponses }}; + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; + proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; + proxy_pass upstream_balancer; } + {{ end }} + + # Stream Snippets + {{ range $snippet := .StreamSnippets }} + {{ $snippet }} + {{ end }} } {{/* definition of templates to avoid repetitions */}} {{ define "CUSTOM_ERRORS" }} {{ $enableMetrics := .EnableMetrics }} + {{ $modsecurityEnabled := .ModsecurityEnabled }} {{ $upstreamName := .UpstreamName }} {{ range $errCode := .ErrorCodes }} location @custom_{{ $upstreamName }}_{{ $errCode }} { internal; + # Ensure that modsecurity will not run on custom error pages or they might be blocked + {{ if $modsecurityEnabled }} + modsecurity off; + {{ end }} + proxy_intercept_errors off; proxy_set_header X-Code {{ $errCode }}; @@ -626,11 +824,9 @@ http { rewrite (.*) / break; proxy_pass http://upstream_balancer; - log_by_lua_block { - {{ if $enableMetrics }} - monitor.call() - {{ end }} - } + {{ if $enableMetrics }} + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; + {{ end }} } {{ end }} {{ end }} @@ -690,9 +886,7 @@ http { ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; {{ end }} - ssl_certificate_by_lua_block { - certificate.call() - } + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; {{ if not (empty $server.AuthTLSError) }} # {{ $server.AuthTLSError }} @@ -741,8 +935,13 @@ http { ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; {{ end }} + {{ if not (empty $server.ServerSnippet) }} + # Custom code snippet configured for host {{ $server.Hostname }} + {{ $server.ServerSnippet }} + {{ end }} + {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics) }} + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics $all.Cfg.EnableModsecurity) }} {{ end }} {{ buildMirrorLocations $server.Locations }} @@ -779,13 +978,16 @@ http { access_log off; {{ end }} + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + {{ if $externalAuth.AuthCacheKey }} set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; set $cache_key ''; - rewrite_by_lua_block { - ngx.var.cache_key = ngx.encode_base64(ngx.sha1_bin(ngx.var.tmp_cache_key)) - } + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; proxy_cache auth_cache; @@ -861,6 +1063,10 @@ http { {{ $line }} {{- end }} + {{ if not (empty $externalAuth.AuthSnippet) }} + {{ $externalAuth.AuthSnippet }} + {{ end }} + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} # The target is an upstream with HTTP keepalive, that is why the @@ -889,6 +1095,11 @@ http { {{ template "CORS" $location }} {{ end }} + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; } {{ end }} @@ -901,7 +1112,6 @@ http { set $service_name {{ $ing.Service | quote }}; set $service_port {{ $ing.ServicePort | quote }}; set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; - set $global_rate_limit_exceeding n; {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} @@ -910,35 +1120,13 @@ http { mirror_request_body {{ $location.Mirror.RequestBody }}; {{ end }} - rewrite_by_lua_block { - lua_ingress.rewrite({{ locationConfigForLua $location $all }}) - balancer.rewrite() - plugins.run() - } + {{ locationConfigForLua $location $all }} - # be careful with `access_by_lua_block` and `satisfy any` directives as satisfy any - # will always succeed when there's `access_by_lua_block` that does not have any lua code doing `ngx.exit(ngx.DECLINED)` - # other authentication method such as basic auth or external auth useless - all requests will be allowed. - #access_by_lua_block { - #} + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; - header_filter_by_lua_block { - lua_ingress.header() - plugins.run() - } + header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; - body_filter_by_lua_block { - plugins.run() - } - - log_by_lua_block { - balancer.log() - {{ if $all.EnableMetrics }} - monitor.call() - {{ end }} - - plugins.run() - } + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; {{ if not $location.Logs.Access }} access_log off; @@ -970,6 +1158,8 @@ http { set $proxy_alternative_upstream_name ""; + {{ buildModSecurityForLocation $all.Cfg $location }} + {{ if isLocationAllowed $location }} {{ if gt (len $location.Denylist.CIDR) 0 }} {{ range $ip := $location.Denylist.CIDR }} @@ -1134,6 +1324,14 @@ http { grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; {{ end }} + {{/* Add any additional configuration defined */}} + {{ $location.ConfigurationSnippet }} + + {{ if not (empty $all.Cfg.LocationSnippet) }} + # Custom code snippet configured in the configuration configmap + {{ $all.Cfg.LocationSnippet }} + {{ end }} + {{ if $location.CustomHeaders }} # Custom Response Headers {{ range $k, $v := $location.CustomHeaders.Headers }} diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl new file mode 100644 index 0000000000..64e5a0c3f9 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl @@ -0,0 +1,1419 @@ +{{ $all := . }} +{{ $servers := .Servers }} +{{ $cfg := .Cfg }} +{{ $IsIPV6Enabled := .IsIPV6Enabled }} +{{ $healthzURI := .HealthzURI }} +{{ $backends := .Backends }} +{{ $proxyHeaders := .ProxySetHeaders }} +{{ $addHeaders := .AddHeaders }} + +# Configuration checksum: {{ $all.Cfg.Checksum }} + +# setup custom paths that do not require root access +pid {{ .PID }}; # OK + +{{ if $cfg.UseGeoIP2 }} +load_module /etc/nginx/modules/ngx_http_geoip2_module.so; # OK +{{ end }} + +{{ if $cfg.EnableBrotli }} +load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; # OK +load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; # OK +{{ end }} + +{{ if (shouldLoadAuthDigestModule $servers) }} +load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; # OK +{{ end }} + +{{ if (shouldLoadOpentelemetryModule $cfg $servers) }} +load_module /etc/nginx/modules/otel_ngx_module.so; # OK +{{ end }} + +daemon off; # OK + +worker_processes {{ $cfg.WorkerProcesses }}; # OK +{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} +worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; # OK +{{ end }} + +worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; # OK + +{{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}} +{{/* avoid waiting too long during a reload */}} +worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; # OK + +events { + multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; # OK + worker_connections {{ $cfg.MaxWorkerConnections }}; # OK + use epoll; # OK + {{ range $index , $v := $cfg.DebugConnections }} + debug_connection {{ $v }}; # OK + {{ end }} +} + +http { + {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} + opentelemetry_config {{ $cfg.OpentelemetryConfig }}; # OK + {{ end }} + + lua_package_path "/etc/nginx/lua/?.lua;;"; # OK + + {{ buildLuaSharedDictionaries $cfg $servers }} # OK + + lua_shared_dict luaconfig 5m; # OK + + init_by_lua_file /etc/nginx/lua/ngx_conf_init.lua; # OK + + init_worker_by_lua_file /etc/nginx/lua/ngx_conf_init_worker.lua; # OK + + {{/* 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 }} + {{ if $cfg.UseProxyProtocol }} + real_ip_header proxy_protocol; # OK + {{ else }} + real_ip_header {{ $cfg.ForwardedForHeader }}; # OK + {{ end }} + + real_ip_recursive on; # OK + {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} + set_real_ip_from {{ $trusted_ip }}; # OK + {{ end }} + {{ end }} + + {{ if $cfg.UseGeoIP2 }} + # https://github.com/leev/ngx_http_geoip2_module#example-usage + + {{ range $index, $file := $all.MaxmindEditionFiles }} + {{ if eq $file "GeoLite2-Country.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_country_code source=$remote_addr country iso_code; + $geoip2_country_name source=$remote_addr country names en; + $geoip2_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_continent_code source=$remote_addr continent code; + $geoip2_continent_name source=$remote_addr continent names en; + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + } + {{ end }} + + {{ if eq $file "GeoIP2-Country.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_country_code source=$remote_addr country iso_code; + $geoip2_country_name source=$remote_addr country names en; + $geoip2_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_continent_code source=$remote_addr continent code; + $geoip2_continent_name source=$remote_addr continent names en; + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + } + {{ end }} + + {{ if eq $file "GeoLite2-City.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_city_country_code source=$remote_addr country iso_code; + $geoip2_city_country_name source=$remote_addr country names en; + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_city source=$remote_addr city names en; + $geoip2_city_geoname_id source=$remote_addr city geoname_id; + $geoip2_postal_code source=$remote_addr postal code; + $geoip2_dma_code source=$remote_addr location metro_code; + $geoip2_latitude source=$remote_addr location latitude; + $geoip2_longitude source=$remote_addr location longitude; + $geoip2_time_zone source=$remote_addr location time_zone; + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; + $geoip2_region_name source=$remote_addr subdivisions 0 names en; + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; + } + {{ end }} + + {{ if eq $file "GeoIP2-City.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_city_country_code source=$remote_addr country iso_code; + $geoip2_city_country_name source=$remote_addr country names en; + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; + $geoip2_city source=$remote_addr city names en; + $geoip2_city_geoname_id source=$remote_addr city geoname_id; + $geoip2_postal_code source=$remote_addr postal code; + $geoip2_dma_code source=$remote_addr location metro_code; + $geoip2_latitude source=$remote_addr location latitude; + $geoip2_longitude source=$remote_addr location longitude; + $geoip2_time_zone source=$remote_addr location time_zone; + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; + $geoip2_region_name source=$remote_addr subdivisions 0 names en; + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; + $geoip2_city_continent_code source=$remote_addr continent code; + $geoip2_city_continent_name source=$remote_addr continent names en; + } + {{ end }} + + {{ if eq $file "GeoLite2-ASN.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_asn source=$remote_addr autonomous_system_number; + $geoip2_org source=$remote_addr autonomous_system_organization; + } + {{ end }} + + {{ if eq $file "GeoIP2-ASN.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_asn source=$remote_addr autonomous_system_number; + $geoip2_org source=$remote_addr autonomous_system_organization; + } + {{ end }} + + {{ if eq $file "GeoIP2-ISP.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_isp source=$remote_addr isp; + $geoip2_isp_org source=$remote_addr organization; + $geoip2_asn source=$remote_addr default=0 autonomous_system_number; + } + {{ end }} + + {{ if eq $file "GeoIP2-Connection-Type.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Connection-Type.mmdb { + $geoip2_connection_type connection_type; + } + {{ end }} + + {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} + geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { + {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + {{ end }} + $geoip2_is_anon source=$remote_addr is_anonymous; + $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; + $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; + $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; + $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; + $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; + } + {{ end }} + + {{ end }} + + {{ end }} + + aio threads; # OK + + {{ if $cfg.EnableAioWrite }} + aio_write on; # OK + {{ end }} + + tcp_nopush on; # OK + tcp_nodelay on; # OK + + log_subrequest on; # OK + + reset_timedout_connection on; # OK + + keepalive_timeout {{ $cfg.KeepAlive }}s; # OK + keepalive_requests {{ $cfg.KeepAliveRequests }}; # OK + + client_body_temp_path /tmp/nginx/client-body; # OK + fastcgi_temp_path /tmp/nginx/fastcgi-temp; # OK + proxy_temp_path /tmp/nginx/proxy-temp; # OK + + client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }}; # OK + client_header_timeout {{ $cfg.ClientHeaderTimeout }}s; # OK + large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }}; # OK + client_body_buffer_size {{ $cfg.ClientBodyBufferSize }}; # OK + client_body_timeout {{ $cfg.ClientBodyTimeout }}s; # OK + + {{ if gt $cfg.GRPCBufferSizeKb 0 }} + grpc_buffer_size {{ $cfg.GRPCBufferSizeKb }}k; # OK + {{ end }} + + {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} # OK + http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; # OK + http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; # OK + {{ end }} + + {{ if (gt $cfg.HTTP2MaxRequests 0) }} # OK + http2_max_requests {{ $cfg.HTTP2MaxRequests }}; # OK + {{ end }} + + http2_max_concurrent_streams {{ $cfg.HTTP2MaxConcurrentStreams }}; # OK + + types_hash_max_size 2048; # OK + server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }}; # OK + server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }}; # OK + map_hash_bucket_size {{ $cfg.MapHashBucketSize }}; # OK + + proxy_headers_hash_max_size {{ $cfg.ProxyHeadersHashMaxSize }}; # OK + proxy_headers_hash_bucket_size {{ $cfg.ProxyHeadersHashBucketSize }}; # OK + + variables_hash_bucket_size {{ $cfg.VariablesHashBucketSize }}; # OK + variables_hash_max_size {{ $cfg.VariablesHashMaxSize }}; # OK + + underscores_in_headers {{ if $cfg.EnableUnderscoresInHeaders }}on{{ else }}off{{ end }}; # OK + ignore_invalid_headers {{ if $cfg.IgnoreInvalidHeaders }}on{{ else }}off{{ end }}; # OK + + limit_req_status {{ $cfg.LimitReqStatusCode }}; # OK + limit_conn_status {{ $cfg.LimitConnStatusCode }}; # OK + + {{ buildOpentelemetry $cfg $servers }} + + include /etc/nginx/mime.types; # OK + default_type {{ $cfg.DefaultType }}; # OK + + {{ if $cfg.EnableBrotli }} + brotli on; # OK + brotli_comp_level {{ $cfg.BrotliLevel }}; # OK + brotli_min_length {{ $cfg.BrotliMinLength }}; # OK + brotli_types {{ $cfg.BrotliTypes }}; # OK + {{ end }} + + {{ if $cfg.UseGzip }} + gzip on; # OK + gzip_comp_level {{ $cfg.GzipLevel }}; # OK + {{- if $cfg.GzipDisable }} + gzip_disable "{{ $cfg.GzipDisable }}"; # OK + {{- end }} + gzip_http_version 1.1; # OK + gzip_min_length {{ $cfg.GzipMinLength}}; # OK + gzip_types {{ $cfg.GzipTypes }}; # OK + gzip_proxied any; # OK + gzip_vary on; # OK + {{ end }} + + # Custom headers for response + {{ range $k, $v := $addHeaders }} + more_set_headers {{ printf "%s: %s" $k $v | quote }}; # OK + {{ end }} + + server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }}; # OK + {{ if not $cfg.ShowServerTokens }} + more_clear_headers Server; # OK + {{ end }} + + # disable warnings + uninitialized_variable_warn off; # OK + + # Additional available variables: + # $namespace + # $ingress_name + # $service_name + # $service_port + log_format upstreaminfo {{ if $cfg.LogFormatEscapeNone }}escape=none {{ else if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ $cfg.LogFormatUpstream }}'; # OK + + {{/* map urls that should not appear in access.log */}} + {{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}} + map $request_uri $loggable { # OK + {{ range $reqUri := $cfg.SkipAccessLogURLs }} + {{ $reqUri }} 0;{{ end }} # OK + default 1; # OK + } + + {{ if or $cfg.DisableAccessLog $cfg.DisableHTTPAccessLog }} + access_log off; # OK + {{ else }} + {{ if $cfg.EnableSyslog }} + access_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} upstreaminfo if=$loggable; # OK + {{ else }} + access_log {{ or $cfg.HTTPAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; # OK + {{ end }} + {{ end }} + + {{ if $cfg.EnableSyslog }} + error_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} {{ $cfg.ErrorLogLevel }}; # OK + {{ else }} + error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; # OK + {{ end }} + + {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} # OK + + # See https://www.nginx.com/blog/websocket-nginx + map $http_upgrade $connection_upgrade { # OK + default upgrade; # OK + {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} + # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive + '' ''; # OK + {{ else }} + '' close; # OK + {{ end }} + } + + # Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server. + # If no such header is provided, it can provide a random value. + map $http_x_request_id $req_id { # OK + default $http_x_request_id; # OK + {{ if $cfg.GenerateRequestID }} + "" $request_id; # OK + {{ end }} + } + + {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} + # We can't use $proxy_add_x_forwarded_for because the realip module + # replaces the remote_addr too soon + map $http_x_forwarded_for $full_x_forwarded_for { # OK + {{ if $all.Cfg.UseProxyProtocol }} + default "$http_x_forwarded_for, $proxy_protocol_addr"; # OK + '' "$proxy_protocol_addr"; # OK + {{ else }} + default "$http_x_forwarded_for, $realip_remote_addr"; # OK + '' "$realip_remote_addr"; # OK + {{ end}} + } + + {{ end }} + + # Create a variable that contains the literal $ character. + # This works because the geo module will not resolve variables. + geo $literal_dollar { # OK + default "$"; # OK + } + + server_name_in_redirect off; # OK + port_in_redirect off; # OK + + ssl_protocols {{ $cfg.SSLProtocols }}; # OK + + ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; # OK + + # turn on session caching to drastically improve performance + {{ if $cfg.SSLSessionCache }} + ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; # OK + ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; # OK + {{ end }} + + # allow configuring ssl session tickets + ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; # OK + + {{ if not (empty $cfg.SSLSessionTicketKey ) }} + ssl_session_ticket_key /etc/ingress-controller/tickets.key; # OK + {{ end }} + + # slightly reduce the time-to-first-byte + ssl_buffer_size {{ $cfg.SSLBufferSize }}; # OK + + {{ if not (empty $cfg.SSLCiphers) }} + # allow configuring custom ssl ciphers + ssl_ciphers '{{ $cfg.SSLCiphers }}'; # OK + ssl_prefer_server_ciphers on; # OK + {{ end }} + + {{ if not (empty $cfg.SSLDHParam) }} + # allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam + ssl_dhparam {{ $cfg.SSLDHParam }}; # OK + {{ end }} + + ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; # OK + + # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} + ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; # OK + ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; # OK + + {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} + proxy_intercept_errors on; # OK + {{ end }} + + {{ range $errCode := $cfg.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} # OK + + proxy_ssl_session_reuse on; # OK + + {{ if $cfg.AllowBackendServerHeader }} + proxy_pass_header Server; # OK + {{ end }} + + {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; # OK + {{ end }} + + upstream upstream_balancer { + ### Attention!!! + # + # We no longer create "upstream" section for every backend. + # Backends are handled dynamically using Lua. If you would like to debug + # and see what backends ingress-nginx has in its memory you can + # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin. + # Once you have the plugin you can use "kubectl ingress-nginx backends" command to + # inspect current backends. + # + ### + + server 0.0.0.1; # OK + + balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer.lua; # OK + + {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} + keepalive {{ $cfg.UpstreamKeepaliveConnections }}; # OK + keepalive_time {{ $cfg.UpstreamKeepaliveTime }}; # OK + keepalive_timeout {{ $cfg.UpstreamKeepaliveTimeout }}s; # OK + keepalive_requests {{ $cfg.UpstreamKeepaliveRequests }}; # OK + {{ end }} + } + + {{ range $rl := (filterRateLimits $servers ) }} + # Ratelimit {{ $rl.Name }} + geo $remote_addr $allowlist_{{ $rl.ID }} { + default 0; + {{ range $ip := $rl.Allowlist }} + {{ $ip }} 1;{{ end }} + } + + # Ratelimit {{ $rl.Name }} + map $allowlist_{{ $rl.ID }} $limit_{{ $rl.ID }} { + 0 {{ $cfg.LimitConnZoneVariable }}; + 1 ""; + } + {{ end }} + + {{/* 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 $servers) }} + {{ $zone }} + {{ end }} + + # Cache for internal auth checks + proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; # OK + + # Global filters + {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; # OK + {{ end }} + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + map $http_user_agent $block_ua { # OK + default 0; # OK + + {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; # OK + {{ end }} + } + {{ end }} + + {{ if gt (len $cfg.BlockReferers) 0 }} + map $http_referer $block_ref { # OK + default 0; # OK + + {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; # OK + {{ end }} + } + {{ end }} + + {{/* Build server redirects (from/to www) */}} + {{ range $redirect := .RedirectServers }} + ## start server {{ $redirect.From }} + server { + server_name {{ $redirect.From }}; + + {{ buildHTTPListener $all $redirect.From }} + {{ buildHTTPSListener $all $redirect.From }} + + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + if ($block_ua) { + return 403; + } + {{ end }} + {{ if gt (len $cfg.BlockReferers) 0 }} + if ($block_ref) { + return 403; + } + {{ end }} + + set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; + + return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; + } + ## end server {{ $redirect.From }} + {{ end }} + + {{ range $server := $servers }} + {{ range $location := $server.Locations }} + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} + upstream {{ buildAuthUpstreamName $location $server.Hostname }} { + {{- $externalAuth := $location.ExternalAuth }} + server {{ extractHostPort $externalAuth.URL }}; + + keepalive {{ $externalAuth.KeepaliveConnections }}; + keepalive_requests {{ $externalAuth.KeepaliveRequests }}; + keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; + } + ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} + {{ end }} + {{ end }} + {{ end }} + + {{ range $server := $servers }} + ## start server {{ $server.Hostname }} + server { + server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; + + {{ if $cfg.UseHTTP2 }} + http2 on; + {{ end }} + + {{ if gt (len $cfg.BlockUserAgents) 0 }} + if ($block_ua) { + return 403; + } + {{ end }} + {{ if gt (len $cfg.BlockReferers) 0 }} + if ($block_ref) { + return 403; + } + {{ end }} + + {{ template "SERVER" serverConfig $all $server }} + + {{ if not (empty $cfg.ServerSnippet) }} + # Custom code snippet configured in the configuration configmap + {{ $cfg.ServerSnippet }} + {{ end }} + + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics $cfg.EnableModsecurity) }} + } + ## end server {{ $server.Hostname }} + + {{ end }} + + # backend for when default-backend-service is not configured or it does not have endpoints + server { + listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; + {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} + set $proxy_upstream_name "internal"; + + access_log off; + + location / { + return 404; + } + } + + # default server, used for NGINX healthcheck and access to nginx stats + server { + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + + listen 127.0.0.1:{{ .StatusPort }}; + set $proxy_upstream_name "internal"; + + keepalive_timeout 0; + gzip off; + + access_log off; + + {{ if $cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + location {{ $healthzURI }} { + return 200; + } + + location /is-dynamic-lb-initialized { + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; + } + + location {{ .StatusPath }} { + stub_status on; + } + + location /configuration { + client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; + client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; + proxy_buffering off; + + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; + } + + location / { + return 404; + } + } +} + +stream { + lua_package_path "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;"; + + lua_shared_dict tcp_udp_configuration_data 5M; + + {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} + + init_by_lua_file /etc/nginx/lua/ngx_conf_init_stream.lua; + + init_worker_by_lua_file /etc/nginx/lua/nginx/ngx_conf_init_tcp_udp.lua; + + lua_add_variable $proxy_upstream_name; + + log_format log_stream '{{ $cfg.LogFormatStream }}'; + + {{ if or $cfg.DisableAccessLog $cfg.DisableStreamAccessLog }} + access_log off; + {{ else }} + access_log {{ or $cfg.StreamAccessLogPath $cfg.AccessLogPath }} log_stream {{ $cfg.AccessLogParams }}; + {{ end }} + + + error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; + {{ if $cfg.EnableRealIP }} + {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} + set_real_ip_from {{ $trusted_ip }}; + {{ end }} + {{ end }} + + upstream upstream_balancer { + server 0.0.0.1:1234; # placeholder + balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer_tcp_udp.lua; + } + + server { + listen 127.0.0.1:{{ .StreamPort }}; + + access_log off; + + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_content_tcp_udp.lua; + } + + # TCP services + {{ range $tcpServer := .TCPBackends }} + server { + preread_by_lua_block { + ngx.var.proxy_upstream_name="tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}"; + } + + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ else }} + listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ else }} + listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; + {{ end }} + {{ end }} + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; + proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; + + proxy_pass upstream_balancer; + {{ if $tcpServer.Backend.ProxyProtocol.Encode }} + proxy_protocol on; + {{ end }} + } + {{ end }} + + # UDP services + {{ range $udpServer := .UDPBackends }} + server { + preread_by_lua_block { + ngx.var.proxy_upstream_name="udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}"; + } + + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} + listen {{ $udpServer.Port }} udp; + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} + listen [::]:{{ $udpServer.Port }} udp; + {{ end }} + {{ end }} + proxy_responses {{ $cfg.ProxyStreamResponses }}; + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; + proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; + proxy_pass upstream_balancer; + } + {{ end }} + + # Stream Snippets + {{ range $snippet := .StreamSnippets }} + {{ $snippet }} + {{ end }} +} + +{{/* definition of templates to avoid repetitions */}} +{{ define "CUSTOM_ERRORS" }} + {{ $enableMetrics := .EnableMetrics }} + {{ $modsecurityEnabled := .ModsecurityEnabled }} + {{ $upstreamName := .UpstreamName }} + {{ range $errCode := .ErrorCodes }} + location @custom_{{ $upstreamName }}_{{ $errCode }} { + internal; + + # Ensure that modsecurity will not run on custom error pages or they might be blocked + {{ if $modsecurityEnabled }} + modsecurity off; + {{ end }} + + proxy_intercept_errors off; + + proxy_set_header X-Code {{ $errCode }}; + proxy_set_header X-Format $http_accept; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Namespace $namespace; + proxy_set_header X-Ingress-Name $ingress_name; + proxy_set_header X-Service-Name $service_name; + proxy_set_header X-Service-Port $service_port; + proxy_set_header X-Request-ID $req_id; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $best_http_host; + + set $proxy_upstream_name {{ $upstreamName | quote }}; + + rewrite (.*) / break; + + proxy_pass http://upstream_balancer; + {{ if $enableMetrics }} + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; + {{ end }} + } + {{ end }} +{{ end }} + +{{/* 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 }}'; + } + + 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; + } +{{ end }} + +{{/* definition of server-template to avoid repetitions with server-alias */}} +{{ define "SERVER" }} + {{ $all := .First }} + {{ $server := .Second }} + + {{ buildHTTPListener $all $server.Hostname }} + {{ buildHTTPSListener $all $server.Hostname }} + + set $proxy_upstream_name "-"; + + {{ if not ( empty $server.CertificateAuth.MatchCN ) }} + {{ if gt (len $server.CertificateAuth.MatchCN) 0 }} + if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { + return 403 "client certificate unauthorized"; + } + {{ end }} + {{ end }} + + {{ if eq $server.Hostname "_" }} + ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; + {{ end }} + + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; + + {{ if not (empty $server.AuthTLSError) }} + # {{ $server.AuthTLSError }} + return 403; + {{ else }} + + {{ if not (empty $server.CertificateAuth.CAFileName) }} + # PEM sha: {{ $server.CertificateAuth.CASHA }} + ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; + ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; + ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; + + {{ if not (empty $server.CertificateAuth.CRLFileName) }} + # PEM sha: {{ $server.CertificateAuth.CRLSHA }} + ssl_crl {{ $server.CertificateAuth.CRLFileName }}; + {{ end }} + + {{ if not (empty $server.CertificateAuth.ErrorPage)}} + error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; + {{ end }} + {{ end }} + + {{ if not (empty $server.ProxySSL.CAFileName) }} + # PEM sha: {{ $server.ProxySSL.CASHA }} + proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; + proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; + proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; + proxy_ssl_verify {{ $server.ProxySSL.Verify }}; + proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; + {{ if not (empty $server.ProxySSL.ProxySSLName) }} + proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; + proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; + {{ end }} + {{ end }} + + {{ if not (empty $server.ProxySSL.PemFileName) }} + proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; + proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; + {{ end }} + + {{ if not (empty $server.SSLCiphers) }} + ssl_ciphers {{ $server.SSLCiphers }}; + {{ end }} + + {{ if not (empty $server.SSLPreferServerCiphers) }} + ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; + {{ end }} + + {{ if not (empty $server.ServerSnippet) }} + # Custom code snippet configured for host {{ $server.Hostname }} + {{ $server.ServerSnippet }} + {{ end }} + + {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics $all.Cfg.EnableModsecurity) }} + {{ end }} + + {{ buildMirrorLocations $server.Locations }} + + {{ $enforceRegex := enforceRegexModifier $server.Locations }} + {{ range $location := $server.Locations }} + {{ $path := buildLocation $location $enforceRegex }} + {{ $proxySetHeader := proxySetHeader $location }} + {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + + {{ $externalAuth := $location.ExternalAuth }} + {{ if eq $applyGlobalAuth true }} + {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} + {{ end }} + + {{ if not (empty $location.Rewrite.AppRoot) }} + if ($uri = /) { + return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; + } + {{ end }} + + {{ if $authPath }} + location = {{ $authPath }} { + internal; + + {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} + opentelemetry on; + opentelemetry_propagate; + {{ end }} + + {{ if not $all.Cfg.EnableAuthAccessLog }} + access_log off; + {{ end }} + + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + + {{ if $externalAuth.AuthCacheKey }} + set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; + set $cache_key ''; + + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; + + proxy_cache auth_cache; + + {{- range $dur := $externalAuth.AuthCacheDuration }} + proxy_cache_valid {{ $dur }}; + {{- end }} + + proxy_cache_key "$cache_key"; + {{ end }} + + # ngx_auth_request module overrides variables in the parent request, + # therefore we have to explicitly set this variable again so that when the parent request + # resumes it has the correct value set for this variable so that Lua can pick backend correctly + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Forwarded-Proto ""; + proxy_set_header X-Request-ID $req_id; + + {{ if $externalAuth.Method }} + proxy_method {{ $externalAuth.Method }}; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Scheme $pass_access_scheme; + {{ end }} + + proxy_set_header Host {{ $externalAuth.Host }}; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Original-Method $request_method; + proxy_set_header X-Sent-From "nginx-ingress-controller"; + proxy_set_header X-Real-IP $remote_addr; + {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} + proxy_set_header X-Forwarded-For $full_x_forwarded_for; + {{ else }} + proxy_set_header X-Forwarded-For $remote_addr; + {{ end }} + + {{ if $externalAuth.RequestRedirect }} + proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; + {{ else }} + proxy_set_header X-Auth-Request-Redirect $request_uri; + {{ end }} + + {{ if $externalAuth.AuthCacheKey }} + proxy_buffering "on"; + {{ else }} + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; + {{ end }} + proxy_buffer_size {{ $location.Proxy.BufferSize }}; + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; + + proxy_ssl_server_name on; + proxy_pass_request_headers on; + {{ if isValidByteSize $location.Proxy.BodySize true }} + client_max_body_size {{ $location.Proxy.BodySize }}; + {{ end }} + {{ if isValidByteSize $location.ClientBodyBufferSize false }} + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; + {{ end }} + + # Pass the extracted client certificate to the auth provider + {{ if not (empty $server.CertificateAuth.CAFileName) }} + {{ if $server.CertificateAuth.PassCertToUpstream }} + proxy_set_header ssl-client-cert $ssl_client_escaped_cert; + {{ end }} + proxy_set_header ssl-client-verify $ssl_client_verify; + proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; + proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; + {{ end }} + + {{- range $line := buildAuthProxySetHeaders $externalAuth.ProxySetHeaders}} + {{ $line }} + {{- end }} + + {{ if not (empty $externalAuth.AuthSnippet) }} + {{ $externalAuth.AuthSnippet }} + {{ end }} + + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} + # The target is an upstream with HTTP keepalive, that is why the + # Connection header is cleared and the HTTP version is set to 1.1 as + # the Nginx documentation suggests: + # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive + proxy_http_version 1.1; + proxy_set_header Connection ""; + set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; + {{ else }} + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; + set $target {{ $externalAuth.URL }}; + {{ end }} + proxy_pass $target; + } + {{ end }} + + {{ if isLocationAllowed $location }} + {{ if $externalAuth.SigninURL }} + location {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }} { + internal; + + add_header Set-Cookie $auth_cookie; + + {{ if $location.CorsConfig.CorsEnabled }} + {{ template "CORS" $location }} + {{ end }} + + # Ensure that modsecurity will not run on an internal location as this is not accessible from outside + {{ if $all.Cfg.EnableModsecurity }} + modsecurity off; + {{ end }} + + return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; + } + {{ end }} + {{ end }} + + location {{ $path }} { + {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.IngressPath) }} + set $namespace {{ $ing.Namespace | quote}}; + set $ingress_name {{ $ing.Rule | quote }}; + set $service_name {{ $ing.Service | quote }}; + set $service_port {{ $ing.ServicePort | quote }}; + set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; + + {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} + + {{ if $location.Mirror.Source }} + mirror {{ $location.Mirror.Source }}; + mirror_request_body {{ $location.Mirror.RequestBody }}; + {{ end }} + + {{ locationConfigForLua $location $all }} + + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; + + header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; + + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; + + {{ if not $location.Logs.Access }} + access_log off; + {{ end }} + + {{ if $location.Logs.Rewrite }} + rewrite_log on; + {{ end }} + + {{ if $location.HTTP2PushPreload }} + http2_push_preload on; + {{ end }} + + port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; + + set $balancer_ewma_score -1; + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; + set $proxy_host $proxy_upstream_name; + set $pass_access_scheme $scheme; + + {{ if $all.Cfg.UseProxyProtocol }} + set $pass_server_port $proxy_protocol_server_port; + {{ else }} + set $pass_server_port $server_port; + {{ end }} + + set $best_http_host $http_host; + set $pass_port $pass_server_port; + + set $proxy_alternative_upstream_name ""; + + {{ buildModSecurityForLocation $all.Cfg $location }} + + {{ if isLocationAllowed $location }} + {{ if gt (len $location.Denylist.CIDR) 0 }} + {{ range $ip := $location.Denylist.CIDR }} + deny {{ $ip }};{{ end }} + {{ end }} + {{ if gt (len $location.Allowlist.CIDR) 0 }} + {{ range $ip := $location.Allowlist.CIDR }} + allow {{ $ip }};{{ end }} + deny all; + {{ end }} + + {{ if $location.CorsConfig.CorsEnabled }} + {{ template "CORS" $location }} + {{ end }} + + {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} + {{ if $authPath }} + # this location requires authentication + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + set $auth_cookie ''; + add_header Set-Cookie $auth_cookie; + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} + {{ $line }} + {{- end }} + # `auth_request` module does not support HTTP keepalives in upstream block: + # https://trac.nginx.org/nginx/ticket/1579 + access_by_lua_block { + local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) + if res.status == ngx.HTTP_OK then + ngx.var.auth_cookie = res.header['Set-Cookie'] + {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} + {{ $line }} + {{- end }} + return + end + if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then + ngx.exit(res.status) + end + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + } + {{ else }} + auth_request {{ $authPath }}; + auth_request_set $auth_cookie $upstream_http_set_cookie; + {{ if $externalAuth.AlwaysSetCookie }} + add_header Set-Cookie $auth_cookie always; + {{ else }} + add_header Set-Cookie $auth_cookie; + {{ end }} + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} + {{ $line }} + {{- end }} + {{ end }} + {{ end }} + + {{ if $externalAuth.SigninURL }} + set_escape_uri $escaped_request_uri $request_uri; + error_page 401 = {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }}; + {{ end }} + + {{ if $location.BasicDigestAuth.Secured }} + {{ if eq $location.BasicDigestAuth.Type "basic" }} + auth_basic {{ $location.BasicDigestAuth.Realm | quote }}; + auth_basic_user_file {{ $location.BasicDigestAuth.File }}; + {{ else }} + auth_digest {{ $location.BasicDigestAuth.Realm | quote }}; + auth_digest_user_file {{ $location.BasicDigestAuth.File }}; + {{ end }} + {{ $proxySetHeader }} Authorization ""; + {{ end }} + {{ end }} + + {{/* if the location contains a rate limit annotation, create one */}} + {{ $limits := buildRateLimit $location }} + {{ range $limit := $limits }} + {{ $limit }}{{ end }} + + {{ if isValidByteSize $location.Proxy.BodySize true }} + client_max_body_size {{ $location.Proxy.BodySize }}; + {{ end }} + {{ if isValidByteSize $location.ClientBodyBufferSize false }} + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; + {{ end }} + + {{/* By default use vhost as Host to upstream, but allow overrides */}} + {{ if not (empty $location.UpstreamVhost) }} + {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; + {{ else }} + {{ $proxySetHeader }} Host $best_http_host; + {{ end }} + + # Pass the extracted client certificate to the backend + {{ if not (empty $server.CertificateAuth.CAFileName) }} + {{ if $server.CertificateAuth.PassCertToUpstream }} + {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; + {{ end }} + {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; + {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; + {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; + {{ end }} + + # Allow websocket connections + {{ $proxySetHeader }} Upgrade $http_upgrade; + {{ if $location.Connection.Enabled}} + {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; + {{ else }} + {{ $proxySetHeader }} Connection $connection_upgrade; + {{ end }} + + {{ $proxySetHeader }} X-Request-ID $req_id; + {{ $proxySetHeader }} X-Real-IP $remote_addr; + {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} + {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; + {{ else }} + {{ $proxySetHeader }} X-Forwarded-For $remote_addr; + {{ end }} + {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; + {{ $proxySetHeader }} X-Forwarded-Port $pass_port; + {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; + {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; + {{ if $all.Cfg.ProxyAddOriginalURIHeader }} + {{ $proxySetHeader }} X-Original-URI $request_uri; + {{ end }} + {{ $proxySetHeader }} X-Scheme $pass_access_scheme; + + # Pass the original X-Forwarded-For + {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; + + # mitigate HTTPoxy Vulnerability + # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ + {{ $proxySetHeader }} Proxy ""; + + # Custom headers to proxied server + {{ range $k, $v := $all.ProxySetHeaders }} + {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; + {{ end }} + + proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; + proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; + proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; + + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; + proxy_buffer_size {{ $location.Proxy.BufferSize }}; + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; + {{ if isValidByteSize $location.Proxy.ProxyMaxTempFileSize true }} + proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; + {{ end }} + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; + + 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 $all.Cfg.RetryNonIdempotent }}; + proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; + proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; + + {{ if or (eq $location.BackendProtocol "GRPC") (eq $location.BackendProtocol "GRPCS") }} + # Grpc settings + grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; + grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; + grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; + {{ end }} + + {{/* Add any additional configuration defined */}} + {{ $location.ConfigurationSnippet }} + + {{ if not (empty $all.Cfg.LocationSnippet) }} + # Custom code snippet configured in the configuration configmap + {{ $all.Cfg.LocationSnippet }} + {{ end }} + + {{ if $location.CustomHeaders }} + # Custom Response Headers + {{ range $k, $v := $location.CustomHeaders.Headers }} + more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; + {{ end }} + {{ end }} + + {{/* if we are sending the request to a custom default backend, we add the required headers */}} + {{ if (hasPrefix $location.Backend "custom-default-backend-") }} + proxy_set_header X-Code 503; + proxy_set_header X-Format $http_accept; + proxy_set_header X-Namespace $namespace; + proxy_set_header X-Ingress-Name $ingress_name; + proxy_set_header X-Service-Name $service_name; + proxy_set_header X-Service-Port $service_port; + proxy_set_header X-Request-ID $req_id; + {{ end }} + + {{ if $location.Satisfy }} + satisfy {{ $location.Satisfy }}; + {{ end }} + + {{/* if a location-specific error override is set, add the proxy_intercept here */}} + {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} + # Custom error pages per ingress + proxy_intercept_errors on; + {{ end }} + + {{ range $errCode := $location.CustomHTTPErrors }} + error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} + + {{ if (eq $location.BackendProtocol "FCGI") }} + include /etc/nginx/fastcgi_params; + {{ end }} + {{- if $location.FastCGI.Index -}} + fastcgi_index {{ $location.FastCGI.Index | quote }}; + {{- end -}} + {{ range $k, $v := $location.FastCGI.Params }} + fastcgi_param {{ $k }} {{ $v | quote }}; + {{ end }} + + {{ if not (empty $location.Redirect.URL) }} + return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; + {{ end }} + + {{ buildProxyPass $server.Hostname $all.Backends $location }} + {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; + {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; + {{ end }} + {{ else }} + # Location denied. Reason: {{ $location.Denied | quote }} + return 503; + {{ end }} + {{ if not (empty $location.ProxySSL.CAFileName) }} + # PEM sha: {{ $location.ProxySSL.CASHA }} + proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; + proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; + proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; + proxy_ssl_verify {{ $location.ProxySSL.Verify }}; + proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; + {{ end }} + + {{ if not (empty $location.ProxySSL.ProxySSLName) }} + proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; + {{ end }} + {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} + proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; + {{ end }} + + {{ if not (empty $location.ProxySSL.PemFileName) }} + proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; + proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; + {{ end }} + } + {{ end }} + {{ end }} + + {{ if eq $server.Hostname "_" }} + # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} + location {{ $all.HealthzURI }} { + + {{ if $all.Cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + + 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 { + + {{ if $all.Cfg.EnableOpentelemetry }} + opentelemetry off; + {{ end }} + + {{ range $v := $all.NginxStatusIpv4Whitelist }} + allow {{ $v }}; + {{ end }} + {{ if $all.IsIPV6Enabled -}} + {{ range $v := $all.NginxStatusIpv6Whitelist }} + allow {{ $v }}; + {{ end }} + {{ end -}} + deny all; + + access_log off; + stub_status on; + } + + {{ end }} + +{{ end }} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index b10363a6fa..2b1b3a2dfc 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -25,6 +25,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/controller/config" ing_net "k8s.io/ingress-nginx/internal/net" + "k8s.io/ingress-nginx/pkg/apis/ingress" ) type seconds int @@ -112,3 +113,31 @@ func dictKbToStr(size int) string { } return fmt.Sprintf("%dK", size) } + +func shouldLoadAuthDigestModule(servers []*ingress.Server) bool { + for _, server := range servers { + for _, location := range server.Locations { + if !location.BasicDigestAuth.Secured { + continue + } + + if location.BasicDigestAuth.Type == "digest" { + return true + } + } + } + return false +} + +// shouldLoadOpentelemetryModule determines whether or not the Opentelemetry module needs to be loaded. +// It checks if `enable-opentelemetry` is set in the ConfigMap. +func shouldLoadOpentelemetryModule(servers []*ingress.Server) bool { + for _, server := range servers { + for _, location := range server.Locations { + if location.Opentelemetry.Enabled { + return true + } + } + } + return false +} diff --git a/magefiles/go.mod b/magefiles/go.mod index ad5b5f2eae..6f1a41e415 100644 --- a/magefiles/go.mod +++ b/magefiles/go.mod @@ -8,23 +8,23 @@ require ( github.com/helm/helm v2.17.0+incompatible github.com/magefile/mage v1.15.0 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/oauth2 v0.22.0 + golang.org/x/oauth2 v0.23.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.34.1 // indirect + github.com/onsi/gomega v1.34.2 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/stretchr/testify v1.9.0 // indirect golang.org/x/crypto v0.31.0 // indirect @@ -32,6 +32,6 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apimachinery v0.31.0 // indirect + k8s.io/apimachinery v0.31.2 // indirect k8s.io/helm v2.17.0+incompatible // indirect ) diff --git a/magefiles/go.sum b/magefiles/go.sum index 959371f923..fa79914875 100644 --- a/magefiles/go.sum +++ b/magefiles/go.sum @@ -5,8 +5,7 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -14,8 +13,7 @@ github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 h1:aRd8M7HJVZOqn/v github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -64,7 +62,7 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -91,8 +89,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -149,6 +147,6 @@ gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= From 8bfe36472b4626b3322327438aed59a0533973f9 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 15 Sep 2024 19:21:34 -0300 Subject: [PATCH 09/17] Bootstrap server configs and a bunch of other configs --- internal/ingress/controller/nginx.go | 12 +- .../template/crossplane/authlocation.go | 251 ++++ .../controller/template/crossplane/cors.go | 81 ++ .../template/crossplane/crossplane.go | 50 +- .../crossplane/crossplane_internal_test.go | 10 +- .../crossplane_internal_utils_test.go | 34 + .../template/crossplane/crossplane_test.go | 191 +++- .../controller/template/crossplane/events.go | 2 +- .../crossplane/extramodules/README.md | 2 +- .../crossplane/extramodules/opentelemetry.go | 71 ++ .../controller/template/crossplane/http.go | 113 +- .../template/crossplane/location.go | 756 +++++++++++++ .../controller/template/crossplane/server.go | 268 +++++ .../crossplane/testdata/nginx-new.tmpl | 1005 +++++++---------- .../controller/template/crossplane/utils.go | 595 ++++++++++ test/e2e-image/e2e.sh | 2 +- test/e2e/admission/admission.go | 9 + test/e2e/annotations/affinity.go | 27 +- test/e2e/annotations/affinitymode.go | 4 +- test/e2e/annotations/auth.go | 15 +- test/e2e/annotations/canary.go | 17 +- test/e2e/annotations/cors.go | 25 +- test/e2e/annotations/fastcgi.go | 9 +- test/e2e/annotations/fromtowwwredirect.go | 13 +- test/e2e/annotations/http2pushpreload.go | 4 + test/e2e/annotations/limitconnections.go | 2 +- test/e2e/annotations/mirror.go | 3 +- .../annotations/modsecurity/modsecurity.go | 3 + test/e2e/annotations/rewrite.go | 13 +- test/e2e/annotations/serversnippet.go | 3 + test/e2e/annotations/snippet.go | 3 + test/e2e/annotations/streamsnippet.go | 3 + test/e2e/annotations/upstreamhashby.go | 2 +- test/e2e/annotations/upstreamvhost.go | 3 +- test/e2e/annotations/xforwardedprefix.go | 3 +- .../defaultbackend/custom_default_backend.go | 3 +- test/e2e/framework/framework.go | 8 +- test/e2e/ingress/multiple_rules.go | 34 +- test/e2e/ingress/pathtype_exact.go | 51 +- test/e2e/ingress/pathtype_mixed.go | 79 +- test/e2e/ingress/without_host.go | 20 +- test/e2e/lua/dynamic_configuration.go | 2 +- test/e2e/metrics/metrics.go | 2 +- test/e2e/run-e2e-suite.sh | 1 + test/e2e/run-kind-e2e.sh | 2 + test/e2e/security/request_smuggling.go | 3 + test/e2e/settings/access_log.go | 16 + test/e2e/settings/badannotationvalues.go | 11 +- test/e2e/settings/configmap_change.go | 21 +- test/e2e/settings/custom_header.go | 6 +- test/e2e/settings/default_ssl_certificate.go | 7 +- test/e2e/settings/disable_catch_all.go | 6 +- test/e2e/settings/global_external_auth.go | 3 + test/e2e/settings/gzip.go | 2 +- test/e2e/settings/limit_rate.go | 2 +- test/e2e/settings/main_snippet.go | 3 + .../modsecurity/modsecurity_snippet.go | 3 + .../e2e/settings/no_tls_redirect_locations.go | 4 +- test/e2e/settings/proxy_host.go | 27 +- test/e2e/settings/proxy_protocol.go | 3 + test/e2e/settings/server_snippet.go | 3 + test/e2e/settings/ssl_ciphers.go | 2 +- test/e2e/settings/ssl_passthrough.go | 3 + test/e2e/settings/stream_snippet.go | 3 + test/e2e/settings/tls.go | 2 +- test/e2e/tcpudp/tcp.go | 3 + 66 files changed, 3131 insertions(+), 808 deletions(-) create mode 100644 internal/ingress/controller/template/crossplane/authlocation.go create mode 100644 internal/ingress/controller/template/crossplane/cors.go create mode 100644 internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go create mode 100644 internal/ingress/controller/template/crossplane/location.go create mode 100644 internal/ingress/controller/template/crossplane/server.go diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index 20fad5afb8..6f138d7540 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -52,6 +52,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/controller/process" "k8s.io/ingress-nginx/internal/ingress/controller/store" ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" "k8s.io/ingress-nginx/internal/ingress/metric" "k8s.io/ingress-nginx/internal/ingress/status" ing_net "k8s.io/ingress-nginx/internal/net" @@ -158,7 +159,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro } onTemplateChange := func() { - template, err := ngx_template.NewTemplate(nginx.TemplatePath) + template, err := crossplane.NewTemplate() if err != nil { // this error is different from the rest because it must be clear why nginx is not working klog.ErrorS(err, "Error loading new template") @@ -170,7 +171,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro n.syncQueue.EnqueueTask(task.GetDummyObject("template-change")) } - ngxTpl, err := ngx_template.NewTemplate(nginx.TemplatePath) + ngxTpl, err := crossplane.NewTemplate() if err != nil { klog.Fatalf("Invalid NGINX configuration template: %v", err) } @@ -700,7 +701,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { err = n.testTemplate(content) if err != nil { - return err + return fmt.Errorf("err %s content %s", err, string(content)) } if klog.V(2).Enabled() { @@ -868,13 +869,14 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro } } - streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) + // TODO: (ricardo) - Disable in case this is crossplane, we don't support stream on this mode + /*streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) if streamConfigurationChanged { err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) if err != nil { return err } - } + }*/ serversChanged := !reflect.DeepEqual(n.runningConfig.Servers, pcfg.Servers) if serversChanged { diff --git a/internal/ingress/controller/template/crossplane/authlocation.go b/internal/ingress/controller/template/crossplane/authlocation.go new file mode 100644 index 0000000000..64c15eeb65 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/authlocation.go @@ -0,0 +1,251 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/ingress-nginx/internal/ingress/annotations/authreq" + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +type externalAuth struct { + URL string `json:"url"` + // Host contains the hostname defined in the URL + Host string `json:"host"` + SigninURL string `json:"signinUrl"` + SigninURLRedirectParam string `json:"signinUrlRedirectParam,omitempty"` + Method string `json:"method"` + ResponseHeaders []string `json:"responseHeaders,omitempty"` + RequestRedirect string `json:"requestRedirect"` + AuthSnippet string `json:"authSnippet"` + AuthCacheKey string `json:"authCacheKey"` + AuthCacheDuration []string `json:"authCacheDuration"` + KeepaliveConnections int `json:"keepaliveConnections"` + KeepaliveShareVars bool `json:"keepaliveShareVars"` + KeepaliveRequests int `json:"keepaliveRequests"` + KeepaliveTimeout int `json:"keepaliveTimeout"` + ProxySetHeaders map[string]string `json:"proxySetHeaders,omitempty"` + AlwaysSetCookie bool `json:"alwaysSetCookie,omitempty"` +} + +func buildExternalAuth(cfg any) *externalAuth { + switch v := cfg.(type) { + case config.GlobalExternalAuth: + return &externalAuth{ + AlwaysSetCookie: v.AlwaysSetCookie, + AuthCacheKey: v.AuthCacheKey, + AuthCacheDuration: v.AuthCacheDuration, + Method: v.Method, + Host: v.Host, + RequestRedirect: v.RequestRedirect, + ProxySetHeaders: v.ProxySetHeaders, + ResponseHeaders: v.ResponseHeaders, + URL: v.URL, + SigninURL: v.SigninURL, + SigninURLRedirectParam: v.SigninURLRedirectParam, + } + case authreq.Config: + return &externalAuth{ + AlwaysSetCookie: v.AlwaysSetCookie, + AuthCacheKey: v.AuthCacheKey, + AuthCacheDuration: v.AuthCacheDuration, + Method: v.Method, + Host: v.Host, + RequestRedirect: v.RequestRedirect, + ProxySetHeaders: v.ProxySetHeaders, + ResponseHeaders: v.ResponseHeaders, + URL: v.URL, + SigninURL: v.SigninURL, + SigninURLRedirectParam: v.SigninURLRedirectParam, + KeepaliveShareVars: v.KeepaliveShareVars, + KeepaliveConnections: v.KeepaliveConnections, + KeepaliveRequests: v.KeepaliveRequests, + KeepaliveTimeout: v.KeepaliveTimeout, + } + default: + return nil + } +} + +func (c *Template) buildAuthLocation(server *ingress.Server, + location *ingress.Location, locationConfig locationCfg) *ngx_crossplane.Directive { + locationDirectives := ngx_crossplane.Directives{ + buildDirective("internal"), + } + + if c.tplConfig.Cfg.EnableOpentelemetry || location.Opentelemetry.Enabled { + locationDirectives = append(locationDirectives, + buildDirective("opentelemetry", "on"), + buildDirective("opentelemetry_propagate"), + ) + } + + if !c.tplConfig.Cfg.EnableAuthAccessLog { + locationDirectives = append(locationDirectives, buildDirective("access_log", "off")) + } + + if locationConfig.externalAuth.AuthCacheKey != "" { + locationDirectives = append(locationDirectives, + buildDirective("set", "$tmp_cache_key", fmt.Sprintf("%s%s%s", server.Hostname, locationConfig.authPath, locationConfig.externalAuth.AuthCacheKey)), + buildDirective("set", "$cache_key", ""), + buildDirective("rewrite_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua"), + buildDirective("proxy_cache", "auth_cache"), + buildDirective("proxy_cache_key", "$cache_key"), + ) + for i := range locationConfig.externalAuth.AuthCacheDuration { + locationDirectives = append(locationDirectives, + buildDirective("proxy_cache_valid", strings.Split(locationConfig.externalAuth.AuthCacheDuration[i], " ")), + ) + } + } + + /* + ngx_auth_request module overrides variables in the parent request, + therefore we have to explicitly set this variable again so that when the parent request + resumes it has the correct value set for this variable so that Lua can pick backend correctly + */ + locationDirectives = append(locationDirectives, + buildDirective("set", "$proxy_upstream_name", location.Backend), + ) + + locationDirectives = append(locationDirectives, + buildDirective("proxy_pass_request_body", "off")) + + locationDirectives = append(locationDirectives, + buildDirective("proxy_ssl_server_name", "on")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_pass_request_headers", "on")) + + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "Content-Length", "")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Forwarded-Proto", "")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Request-ID", "$req_id")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "Host", locationConfig.externalAuth.Host)) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-URL", "$scheme://$http_host$request_uri")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-Method", "$request_method")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Sent-From", "nginx-ingress-controller")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Real-IP", "$remote_addr")) + + if locationConfig.externalAuth.Method != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_method", locationConfig.externalAuth.Method)) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) + } + + if c.tplConfig.Cfg.UseForwardedHeaders && c.tplConfig.Cfg.ComputeFullForwardedFor { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Forwarded-For", "$full_x_forwarded_for")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Forwarded-For", "$remote_addr")) + } + + if locationConfig.externalAuth.RequestRedirect != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Auth-Request-Redirect", locationConfig.externalAuth.RequestRedirect)) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Auth-Request-Redirect", "$request_uri")) + } + + if locationConfig.externalAuth.Method != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) + } + + if locationConfig.externalAuth.AuthCacheKey != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffering", "on")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffering", location.Proxy.ProxyBuffering)) + } + + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffer_size", location.Proxy.BufferSize)) + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize)) + locationDirectives = append(locationDirectives, + buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering)) + + if isValidByteSize(location.Proxy.BodySize, true) { + locationDirectives = append(locationDirectives, + buildDirective("client_max_body_size", location.Proxy.BodySize)) + } + + if isValidByteSize(location.ClientBodyBufferSize, false) { + locationDirectives = append(locationDirectives, + buildDirective("client_body_buffer_size", location.ClientBodyBufferSize)) + } + + if server.CertificateAuth.CAFileName != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-verify", "$ssl_client_verify")) + + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-subject-dn", "$ssl_client_s_dn")) + + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-issuer-dn", "$ssl_client_i_dn")) + + if server.CertificateAuth.PassCertToUpstream { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-cert", "$ssl_client_escaped_cert")) + } + } + + for name, value := range locationConfig.externalAuth.ProxySetHeaders { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", name, value)) + } + + if locationConfig.applyAuthUpstream && locationConfig.applyGlobalAuth { + locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", "1.1")) + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "Connection", "")) + locationDirectives = append(locationDirectives, + buildDirective("set", "$target", + changeHostPort(locationConfig.externalAuth.URL, buildAuthUpstreamName(location, server.Hostname)))) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion)) + locationDirectives = append(locationDirectives, + buildDirective("set", "$target", locationConfig.externalAuth.URL)) + } + locationDirectives = append(locationDirectives, + buildDirective("proxy_pass", "$target")) + + return buildBlockDirective("location", + []string{"=", locationConfig.authPath}, locationDirectives) +} diff --git a/internal/ingress/controller/template/crossplane/cors.go b/internal/ingress/controller/template/crossplane/cors.go new file mode 100644 index 0000000000..932c2489bf --- /dev/null +++ b/internal/ingress/controller/template/crossplane/cors.go @@ -0,0 +1,81 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/ingress-nginx/internal/ingress/annotations/cors" +) + +func buildCorsDirectives(locationcors cors.Config) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, 0) + if len(locationcors.CorsAllowOrigin) > 0 { + directives = append(directives, buildCorsOriginRegex(locationcors.CorsAllowOrigin)...) + + } + directives = append(directives, + buildBlockDirective("if", + []string{"$request_method", "=", "OPTIONS"}, ngx_crossplane.Directives{ + buildDirective("set", "$cors", "${cors}options"), + }, + ), + ) + + directives = append(directives, + commonCorsDirective(locationcors, false), + commonCorsDirective(locationcors, true), + ) + return directives +} + +// commonCorsDirective builds the common cors directives for a location +func commonCorsDirective(cfg cors.Config, options bool) *ngx_crossplane.Directive { + corsDir := "true" + if options { + corsDir = "trueoptions" + } + corsBlock := buildBlockDirective("if", []string{"$cors", "=", corsDir}, + ngx_crossplane.Directives{ + buildDirective("more_set_headers", "Access-Control-Allow-Origin: $http_origin"), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Methods: %s", cfg.CorsAllowMethods)), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Headers: %s", cfg.CorsAllowHeaders)), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Max-Age: %d", cfg.CorsMaxAge)), + }, + ) + + if cfg.CorsAllowCredentials { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Credentials: %t", cfg.CorsAllowCredentials)), + ) + } + if cfg.CorsExposeHeaders != "" { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Expose-Headers: %s", cfg.CorsExposeHeaders)), + ) + } + + if options { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", "Content-Type: text/plain charset=UTF-8"), + buildDirective("more_set_headers", "Content-Length: 0"), + buildDirective("return", "204"), + ) + } + return corsBlock +} diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go index 1307de2f7a..13796fdb8d 100644 --- a/internal/ingress/controller/template/crossplane/crossplane.go +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -18,10 +18,12 @@ package crossplane import ( "bytes" + "os" ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane/extramodules" ) /* @@ -41,7 +43,7 @@ type Template struct { mimeFile string } -func NewTemplate() *Template { +func NewTemplate() (*Template, error) { lua := ngx_crossplane.Lua{} return &Template{ mimeFile: "/etc/nginx/mime.types", @@ -50,7 +52,7 @@ func NewTemplate() *Template { lua.RegisterBuilder(), }, }, - } + }, nil } func (c *Template) SetMimeFile(file string) { @@ -72,5 +74,49 @@ func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { var buf bytes.Buffer err := ngx_crossplane.Build(&buf, *c.config, &ngx_crossplane.BuildOptions{}) + if err != nil { + return nil, err + } + + lua := ngx_crossplane.Lua{} + options := ngx_crossplane.ParseOptions{ + ParseComments: true, + ErrorOnUnknownDirectives: true, + StopParsingOnError: true, + DirectiveSources: []ngx_crossplane.MatchFunc{ + ngx_crossplane.DefaultDirectivesMatchFunc, + ngx_crossplane.MatchLuaLatest, + ngx_crossplane.MatchHeadersMoreLatest, + extramodules.BrotliMatchFn, + extramodules.OpentelemetryMatchFn, + ngx_crossplane.MatchGeoip2Latest, + }, + LexOptions: ngx_crossplane.LexOptions{ + Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, + }, + // Modules that needs to be ported: + // // https://github.com/openresty/set-misc-nginx-module?tab=readme-ov-file#set_escape_uri + IgnoreDirectives: []string{"set_escape_uri"}, + } + + tmpFile, err := os.CreateTemp("", "") + if err != nil { + return nil, err + } + defer func() { + _ = os.Remove(tmpFile.Name()) + _ = tmpFile.Close() + }() + + _, err = tmpFile.Write(buf.Bytes()) + if err != nil { + return nil, err + } + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + if err != nil { + return nil, err + } + return buf.Bytes(), err } diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go index b8bc3258d0..73093c1a8e 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go @@ -41,14 +41,15 @@ func Test_Internal_buildEvents(t *testing.T) { Directive: "events", Block: ngx_crossplane.Directives{ buildDirective("worker_connections", 16384), - buildDirective("use", "epool"), + buildDirective("use", "epoll"), buildDirective("multi_accept", true), }, }, }, } - cplane := NewTemplate() + cplane, err := NewTemplate() + require.NoError(t, err) cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() @@ -72,7 +73,7 @@ func Test_Internal_buildEvents(t *testing.T) { Directive: "events", Block: ngx_crossplane.Directives{ buildDirective("worker_connections", 50), - buildDirective("use", "epool"), + buildDirective("use", "epoll"), buildDirective("multi_accept", false), buildDirective("debug_connection", "127.0.0.1/32"), buildDirective("debug_connection", "192.168.0.10"), @@ -81,7 +82,8 @@ func Test_Internal_buildEvents(t *testing.T) { }, } - cplane := NewTemplate() + cplane, err := NewTemplate() + require.NoError(t, err) cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go index f31701781d..b5c2b69627 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -17,6 +17,7 @@ limitations under the License. package crossplane import ( + "reflect" "testing" ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" @@ -67,3 +68,36 @@ func Test_Internal_buildLuaDictionaries(t *testing.T) { require.Equal(t, "lua_shared_dict", directives[1].Directive) require.Equal(t, []string{"otherdict", "1025K"}, directives[1].Args) } + +func Test_Internal_buildCorsOriginRegex(t *testing.T) { + tests := []struct { + name string + corsOrigins []string + want ngx_crossplane.Directives + }{ + { + name: "wildcard returns a single directive", + corsOrigins: []string{"*"}, + want: ngx_crossplane.Directives{ + buildDirective("set", "$http_origin", "*"), + buildDirective("set", "$cors", "true"), + }, + }, + { + name: "multiple hosts should be changed properly", + corsOrigins: []string{"*.xpto.com", " lalala.com"}, + want: ngx_crossplane.Directives{ + buildBlockDirective("if", []string{"$http_origin", "~*", "([A-Za-z0-9\\-]+\\.xpto\\.com)", "|", "(lalala\\.com)"}, + ngx_crossplane.Directives{buildDirective("set", "$cors", "true")}, + ), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := buildCorsOriginRegex(tt.corsOrigins); !reflect.DeepEqual(got, tt.want) { + t.Errorf("buildCorsOriginRegex() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 08ea94276d..3d3fa745dd 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -24,10 +24,18 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" "github.com/stretchr/testify/require" + "k8s.io/ingress-nginx/internal/ingress/annotations/authreq" + "k8s.io/ingress-nginx/internal/ingress/annotations/authtls" + "k8s.io/ingress-nginx/internal/ingress/annotations/cors" + "k8s.io/ingress-nginx/internal/ingress/annotations/mirror" + "k8s.io/ingress-nginx/internal/ingress/annotations/proxy" + "k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl" "k8s.io/ingress-nginx/internal/ingress/controller/config" "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane/extramodules" + "k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/pkg/apis/ingress" + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" ) const mockMimeTypes = ` @@ -38,6 +46,28 @@ types { } ` +func defaultConfig() *config.TemplateConfig { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + tplConfig.ListenPorts = &config.ListenPorts{ + HTTP: 80, + HTTPS: 443, + Health: 10245, + Default: 8080, + SSLProxy: 442, + } + defaultCertificate := &ingress.SSLCert{ + PemFileName: "bla.crt", + PemCertKey: "bla.key", + } + tplConfig.StatusPort = 10246 + tplConfig.StatusPath = "/status" + tplConfig.HealthzURI = "/healthz" + tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + return tplConfig +} + var resolvers = []net.IP{net.ParseIP("::1"), net.ParseIP("192.168.20.10")} // TestTemplate should be a roundtrip test. @@ -48,21 +78,21 @@ var resolvers = []net.IP{net.ParseIP("::1"), net.ParseIP("192.168.20.10")} func TestCrossplaneTemplate(t *testing.T) { lua := ngx_crossplane.Lua{} options := ngx_crossplane.ParseOptions{ + ParseComments: true, ErrorOnUnknownDirectives: true, StopParsingOnError: true, - IgnoreDirectives: []string{"more_clear_headers", "more_set_headers"}, // TODO: Add more_set_headers DirectiveSources: []ngx_crossplane.MatchFunc{ ngx_crossplane.DefaultDirectivesMatchFunc, ngx_crossplane.MatchLuaLatest, + ngx_crossplane.MatchHeadersMoreLatest, extramodules.BrotliMatchFn, + extramodules.OpentelemetryMatchFn, + ngx_crossplane.MatchGeoip2Latest, }, LexOptions: ngx_crossplane.LexOptions{ Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, }, - } - defaultCertificate := &ingress.SSLCert{ - PemFileName: "bla.crt", - PemCertKey: "bla.key", + IgnoreDirectives: []string{"set_escape_uri"}, } mimeFile, err := os.CreateTemp("", "") @@ -71,13 +101,11 @@ func TestCrossplaneTemplate(t *testing.T) { require.NoError(t, err) require.NoError(t, mimeFile.Close()) - tpl := crossplane.NewTemplate() + tpl, err := crossplane.NewTemplate() + require.NoError(t, err) t.Run("it should be able to marshall and unmarshall the default configuration", func(t *testing.T) { - tplConfig := &config.TemplateConfig{ - Cfg: config.NewDefault(), - } - tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + tplConfig := defaultConfig() tplConfig.Cfg.EnableBrotli = true tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} tplConfig.Cfg.Resolver = resolvers @@ -101,11 +129,137 @@ func TestCrossplaneTemplate(t *testing.T) { require.NoError(t, err) }) - t.Run("it should set the right logging configs", func(t *testing.T) { - tplConfig := &config.TemplateConfig{ - Cfg: config.NewDefault(), + t.Run("it should be able to marshall and unmarshall with server config", func(t *testing.T) { + tplConfig := defaultConfig() + tplConfig.EnableMetrics = true + tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.EnableOpentelemetry = true + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = true + tplConfig.IsIPV6Enabled = true + tplConfig.Cfg.BindAddressIpv6 = []string{"[::cabe:ca]"} + tplConfig.Cfg.BlockReferers = []string{"testlala.com"} + tplConfig.Cfg.ReusePort = true + tplConfig.BacklogSize = 5 + tplConfig.Cfg.BlockUserAgents = []string{"somebrowser"} + tplConfig.Cfg.UseForwardedHeaders = true + tplConfig.Cfg.LogFormatEscapeNone = true + tplConfig.Cfg.DisableAccessLog = true + tplConfig.Cfg.UpstreamKeepaliveConnections = 0 + tplConfig.Cfg.CustomHTTPErrors = []int{411, 412, 413} // Duplicated on purpose + tplConfig.RedirectServers = []*utilingress.Redirect{ + { + From: "www.xpto123.com", + To: "www.abcdefg.tld", + }, + } + tplConfig.Servers = []*ingress.Server{ + { + Hostname: "_", + }, + { + Hostname: "*.something.com", + Aliases: []string{"abc.com", "def.com"}, + Locations: []*ingress.Location{ + { + Mirror: mirror.Config{ + Source: "/mirror", + Host: "something.com", + Target: "http://www.mymirror.com", + RequestBody: "off", + }, + }, + { + DefaultBackendUpstreamName: "something", + CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! + }, + { + DefaultBackendUpstreamName: "otherthing", + CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! + }, + { + CorsConfig: cors.Config{ + CorsEnabled: true, + CorsAllowOrigin: []string{"xpto.com", "*.bla.com"}, + CorsAllowMethods: "GET,POST", + CorsAllowHeaders: "XPTO", + CorsMaxAge: 600, + CorsAllowCredentials: true, + CorsExposeHeaders: "XPTO", + }, + Backend: "somebackend", + ClientBodyBufferSize: "512k", + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + BuffersNumber: 10, + BufferSize: "1024k", + ProxyHTTPVersion: "1.1", + NextUpstream: "10.10.10.10", + }, + ExternalAuth: authreq.Config{ + AuthCacheDuration: []string{"60s"}, + Host: "someauth.com", + URL: "http://someauth.com", + Method: "GET", + ProxySetHeaders: map[string]string{ + "someheader": "something", + }, + AuthCacheKey: "blabla", + SigninURL: "http://externallogin.tld", + }, + Path: "/xpto123", + }, + }, + }, + { + Hostname: "otherthing.com", + Aliases: []string{"abcde.com", "xpto.com"}, + CertificateAuth: authtls.Config{ + MatchCN: "CN=bla; listen xpto\"", + AuthSSLCert: resolver.AuthSSLCert{ + CAFileName: "/something/xpto.crt", + CRLFileName: "/something/xpto.crt", + }, + VerifyClient: "optional", + ValidationDepth: 2, + ErrorPage: "/xpto.html", + }, + ProxySSL: proxyssl.Config{ + AuthSSLCert: resolver.AuthSSLCert{ + CAFileName: "/something/xpto.crt", + PemFileName: "/something/mycert.crt", + }, + Ciphers: "HIGH:!aNULL:!MD5", + Protocols: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3", + Verify: "on", + VerifyDepth: 2, + ProxySSLName: "xpto.com", + ProxySSLServerName: "on", + }, + SSLCiphers: "HIGH:!aNULL:", + SSLPreferServerCiphers: "on", + }, } - tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + require.Equal(t, "bla", string(content)) + }) + + t.Run("it should set the right logging configs", func(t *testing.T) { + tplConfig := defaultConfig() tplConfig.Cfg.DisableAccessLog = false tplConfig.Cfg.HTTPAccessLogPath = "/lalala.log" @@ -124,10 +278,7 @@ func TestCrossplaneTemplate(t *testing.T) { }) t.Run("it should be able to marshall and unmarshall the specified configuration", func(t *testing.T) { - tplConfig := &config.TemplateConfig{ - Cfg: config.NewDefault(), - } - tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + tplConfig := defaultConfig() tplConfig.Cfg.WorkerCPUAffinity = "0001 0010 0100 1000" tplConfig.Cfg.LuaSharedDicts = map[string]int{ "configuration_data": 10240, @@ -186,7 +337,9 @@ func TestCrossplaneTemplate(t *testing.T) { tplConfig.Cfg.UpstreamKeepaliveTimeout = 200 tplConfig.Cfg.UpstreamKeepaliveRequests = 15 - tpl = crossplane.NewTemplate() + tpl, err = crossplane.NewTemplate() + require.NoError(t, err) + tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) require.NoError(t, err) diff --git a/internal/ingress/controller/template/crossplane/events.go b/internal/ingress/controller/template/crossplane/events.go index fa0c599e5e..ba0a76328a 100644 --- a/internal/ingress/controller/template/crossplane/events.go +++ b/internal/ingress/controller/template/crossplane/events.go @@ -25,7 +25,7 @@ func (c *Template) buildEvents() { Directive: "events", Block: ngx_crossplane.Directives{ buildDirective("worker_connections", c.tplConfig.Cfg.MaxWorkerConnections), - buildDirective("use", "epool"), + buildDirective("use", "epoll"), buildDirective("multi_accept", c.tplConfig.Cfg.EnableMultiAccept), }, } diff --git a/internal/ingress/controller/template/crossplane/extramodules/README.md b/internal/ingress/controller/template/crossplane/extramodules/README.md index 6bd8a4e6ba..b632776873 100644 --- a/internal/ingress/controller/template/crossplane/extramodules/README.md +++ b/internal/ingress/controller/template/crossplane/extramodules/README.md @@ -6,5 +6,5 @@ The generation of the files is done using go-crossplane generator ## Brotli ``` -go run ./cmd/generate/ -src-path=ngx_brotli/ -directive-map-name=brotliDirectives -match-func-name=BrotliMatchFn > ../ingress-crossplane/internal/ingress/controller/template/crossplane/extramodules/brotli.go +go run ./cmd/generate/ -src-path=ngx_brotli/ -directive-map-name=brotliDirectives -match-func-name=BrotliMatchFn > ../ingress-nginx/internal/ingress/controller/template/crossplane/extramodules/brotli.go ``` \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go new file mode 100644 index 0000000000..f3a9cde9a3 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go @@ -0,0 +1,71 @@ +/* +Copyright 2024 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. +*/ + +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package extramodules + +var opentelemetryDirectives = map[string][]uint{ + "opentelemetry": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_attribute": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake2, + }, + "opentelemetry_capture_headers": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_config": { + ngxHTTPMainConf | ngxConfTake1, + }, + "opentelemetry_ignore_paths": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_operation_name": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_propagate": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfNoArgs | ngxConfTake1, + }, + "opentelemetry_sensitive_header_names": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_sensitive_header_values": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_trust_incoming_spans": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, +} + + +func OpentelemetryMatchFn(directive string) ([]uint, bool) { + m, ok := opentelemetryDirectives[directive] + return m, ok +} diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index dc3e43dcee..5da0b248a2 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -22,6 +22,8 @@ import ( "strings" ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" ) func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { @@ -124,7 +126,7 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildDirective("gzip_comp_level", cfg.GzipLevel)) httpBlock = append(httpBlock, buildDirective("gzip_http_version", "1.1")) httpBlock = append(httpBlock, buildDirective("gzip_min_length", cfg.GzipMinLength)) - httpBlock = append(httpBlock, buildDirective("gzip_types", cfg.GzipTypes)) + httpBlock = append(httpBlock, buildDirective("gzip_types", strings.Split(cfg.GzipTypes, " "))) httpBlock = append(httpBlock, buildDirective("gzip_proxied", "any")) httpBlock = append(httpBlock, buildDirective("gzip_vary", "on")) @@ -140,10 +142,23 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildDirective("brotli_types", cfg.BrotliTypes)) } + if (c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers)) && + cfg.OpentelemetryOperationName != "" { + httpBlock = append(httpBlock, buildDirective("opentelemetry_operation_name", cfg.OpentelemetryOperationName)) + } + if !cfg.ShowServerTokens { httpBlock = append(httpBlock, buildDirective("more_clear_headers", "Server")) } + if cfg.UseGeoIP2 && c.tplConfig.MaxmindEditionFiles != nil && len(*c.tplConfig.MaxmindEditionFiles) > 0 { + geoipDirectives := buildGeoIPDirectives(cfg.GeoIP2AutoReloadMinutes, *c.tplConfig.MaxmindEditionFiles) + // We do this to avoid adding empty blocks + if len(geoipDirectives) > 0 { + httpBlock = append(httpBlock, geoipDirectives...) + } + } + httpBlock = append(httpBlock, buildBlockDirective( "geo", []string{"$literal_dollar"}, @@ -153,11 +168,9 @@ func (c *Template) buildHTTP() { )) if len(c.tplConfig.AddHeaders) > 0 { - additionalHeaders := make([]string, 0) for headerName, headerValue := range c.tplConfig.AddHeaders { - additionalHeaders = append(additionalHeaders, fmt.Sprintf("%s: %s", headerName, headerValue)) + httpBlock = append(httpBlock, buildDirective("more_set_headers", fmt.Sprintf("%s: %s", headerName, headerValue))) } - httpBlock = append(httpBlock, buildDirective("more_set_headers", additionalHeaders)) } escape := "" @@ -257,15 +270,6 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildDirective("proxy_pass_header", "Server")) } - if cfg.EnableBrotli { - httpBlock = append(httpBlock, - buildDirective("brotli", "on"), - buildDirective("brotli_comp_level", cfg.BrotliLevel), - buildDirective("brotli_min_length", cfg.BrotliMinLength), - buildDirective("brotli_types", strings.Split(cfg.BrotliTypes, " ")), - ) - } - for k := range cfg.HideHeaders { httpBlock = append(httpBlock, buildDirective("proxy_hide_header", cfg.HideHeaders[k])) } @@ -284,6 +288,30 @@ func (c *Template) buildHTTP() { } httpBlock = append(httpBlock, buildBlockDirective("upstream", []string{"upstream_balancer"}, blockUpstreamDirectives)) + // Adding Rate limit + for _, rl := range filterRateLimits(c.tplConfig.Servers) { + id := fmt.Sprintf("$allowlist_%s", rl.ID) + httpBlock = append(httpBlock, buildDirective("#", "Ratelimit", rl.Name)) + rlDirectives := ngx_crossplane.Directives{ + buildDirective("default", 0), + } + for _, ip := range rl.Allowlist { + rlDirectives = append(rlDirectives, buildDirective(ip, "1")) + } + mapRateLimitDirective := buildMapDirective(id, fmt.Sprintf("$limit_%s", rl.ID), ngx_crossplane.Directives{ + buildDirective("0", cfg.LimitConnZoneVariable), + buildDirective("1", ""), + }) + httpBlock = append(httpBlock, buildBlockDirective("geo", []string{"$remote_addr", id}, rlDirectives), mapRateLimitDirective) + } + + zoneRL := buildRateLimitZones(c.tplConfig.Servers) + if len(zoneRL) > 0 { + httpBlock = append(httpBlock, zoneRL...) + } + + // End of Rate limit configs + for i := range cfg.BlockCIDRs { httpBlock = append(httpBlock, buildDirective("deny", strings.TrimSpace(cfg.BlockCIDRs[i]))) } @@ -309,6 +337,65 @@ func (c *Template) buildHTTP() { fmt.Sprintf("@custom_upstream-default-backend_%d", v))) } + if redirectServers, ok := c.tplConfig.RedirectServers.([]*utilingress.Redirect); ok { + for _, server := range redirectServers { + httpBlock = append(httpBlock, buildStartServer(server.From)) + serverBlock := c.buildRedirectServer(server) + httpBlock = append(httpBlock, serverBlock) + httpBlock = append(httpBlock, buildEndServer(server.From)) + } + } + + /* + {{ range $server := $servers }} + {{ range $location := $server.Locations }} + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} + upstream {{ buildAuthUpstreamName $location $server.Hostname }} { + {{- $externalAuth := $location.ExternalAuth }} + server {{ extractHostPort $externalAuth.URL }}; + + keepalive {{ $externalAuth.KeepaliveConnections }}; + keepalive_requests {{ $externalAuth.KeepaliveRequests }}; + keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; + } + ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} + {{ end }} + {{ end }} + {{ end }} + */ + for _, server := range c.tplConfig.Servers { + for _, location := range server.Locations { + if shouldApplyAuthUpstream(location, cfg) && !shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL) { + authUpstreamBlock := buildBlockDirective("upstream", + []string{buildAuthUpstreamName(location, server.Hostname)}, ngx_crossplane.Directives{ + buildDirective("server", extractHostPort(location.ExternalAuth.URL)), + buildDirective("keepalive", location.ExternalAuth.KeepaliveConnections), + buildDirective("keepalive_requests", location.ExternalAuth.KeepaliveRequests), + buildDirective("keepalive_timeout", seconds(location.ExternalAuth.KeepaliveTimeout)), + }, + ) + httpBlock = append(httpBlock, + buildStartAuthUpstream(server.Hostname, location.Path), + authUpstreamBlock, + buildEndAuthUpstream(server.Hostname, location.Path), + ) + } + } + } + + for _, server := range c.tplConfig.Servers { + httpBlock = append(httpBlock, buildStartServer(server.Hostname)) + serverBlock := c.buildServerDirective(server) + httpBlock = append(httpBlock, serverBlock) + httpBlock = append(httpBlock, buildEndServer(server.Hostname)) + } + + httpBlock = append(httpBlock, c.buildDefaultBackend()) + httpBlock = append(httpBlock, c.buildHealthAndStatsServer()) + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", Block: httpBlock, diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go new file mode 100644 index 0000000000..af2ee433ba --- /dev/null +++ b/internal/ingress/controller/template/crossplane/location.go @@ -0,0 +1,756 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + "sort" + "strconv" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/apimachinery/pkg/util/sets" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +func buildMirrorLocationDirective(locs []*ingress.Location) ngx_crossplane.Directives { + mirrorDirectives := make(ngx_crossplane.Directives, 0) + + mapped := sets.Set[string]{} + + for _, loc := range locs { + if loc.Mirror.Source == "" || loc.Mirror.Target == "" || loc.Mirror.Host == "" { + continue + } + + if mapped.Has(loc.Mirror.Source) { + continue + } + + mapped.Insert(loc.Mirror.Source) + mirrorDirectives = append(mirrorDirectives, buildBlockDirective("location", + []string{"=", loc.Mirror.Source}, + ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("proxy_set_header", "Host", loc.Mirror.Host), + buildDirective("proxy_pass", loc.Mirror.Target), + })) + } + return mirrorDirectives +} + +// buildCustomErrorLocationsPerServer is a utility function which will collect all +// custom error codes for all locations of a server block, deduplicates them, +// and returns a set which is unique by default-upstream and error code. It returns an array +// of errorLocations, each of which contain the upstream name and a list of +// error codes for that given upstream, so that sufficiently unique +// @custom error location blocks can be created in the template +func buildCustomErrorLocationsPerServer(server *ingress.Server, enableMetrics bool) ngx_crossplane.Directives { + type errorLocation struct { + UpstreamName string + Codes []int + } + + codesMap := make(map[string]map[int]bool) + for _, loc := range server.Locations { + backendUpstream := loc.DefaultBackendUpstreamName + + var dedupedCodes map[int]bool + if existingMap, ok := codesMap[backendUpstream]; ok { + dedupedCodes = existingMap + } else { + dedupedCodes = make(map[int]bool) + } + + for _, code := range loc.CustomHTTPErrors { + dedupedCodes[code] = true + } + codesMap[backendUpstream] = dedupedCodes + } + + errorLocations := []errorLocation{} + + for upstream, dedupedCodes := range codesMap { + codesForUpstream := []int{} + for code := range dedupedCodes { + codesForUpstream = append(codesForUpstream, code) + } + sort.Ints(codesForUpstream) + errorLocations = append(errorLocations, errorLocation{ + UpstreamName: upstream, + Codes: codesForUpstream, + }) + } + + sort.Slice(errorLocations, func(i, j int) bool { + return errorLocations[i].UpstreamName < errorLocations[j].UpstreamName + }) + + errorLocationsDirectives := make(ngx_crossplane.Directives, 0) + for i := range errorLocations { + errorLocationsDirectives = append(errorLocationsDirectives, buildCustomErrorLocation(errorLocations[i].UpstreamName, errorLocations[i].Codes, enableMetrics)...) + } + return errorLocationsDirectives + +} + +func buildCustomErrorLocation(upstreamName string, errorCodes []int, enableMetrics bool) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, len(errorCodes)) + for i := range errorCodes { + locationDirectives := ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("proxy_intercept_errors", "off"), + buildDirective("proxy_set_header", "X-Code", errorCodes[i]), + buildDirective("proxy_set_header", "X-Format", "$http_accept"), + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), + buildDirective("proxy_set_header", "X-Namespace", "$namespace"), + buildDirective("proxy_set_header", "X-Ingress-Name", "$ingress_name"), + buildDirective("proxy_set_header", "X-Service-Name", "$service_name"), + buildDirective("proxy_set_header", "X-Service-Port", "$service_port"), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + buildDirective("proxy_set_header", "X-Forwarded-For", "$remote_addr"), + buildDirective("proxy_set_header", "Host", "$best_http_host"), + buildDirective("set", "$proxy_upstream_name", upstreamName), + buildDirective("rewrite", "(.*)", "/", "break"), + buildDirective("proxy_pass", "http://upstream_balancer"), + } + + if enableMetrics { + locationDirectives = append(locationDirectives, buildDirective("log_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_log.lua")) + } + locationName := fmt.Sprintf("@custom_%s_%d", upstreamName, errorCodes[i]) + directives[i] = buildBlockDirective("location", []string{locationName}, locationDirectives) + } + + return directives +} + +type locationCfg struct { + pathLocation []string + authPath string + externalAuth *externalAuth + proxySetHeader string + applyGlobalAuth bool + applyAuthUpstream bool +} + +func (c *Template) buildServerLocations(server *ingress.Server, locations []*ingress.Location) ngx_crossplane.Directives { + serverLocations := make(ngx_crossplane.Directives, 0) + + cfg := c.tplConfig.Cfg + enforceRegexModifier := false + needsRewrite := func(loc *ingress.Location) bool { + return loc.Rewrite.Target != "" && + loc.Rewrite.Target != loc.Path + } + + for _, location := range locations { + if needsRewrite(location) || location.Rewrite.UseRegex { + enforceRegexModifier = true + break + } + } + + for _, location := range locations { + locationConfig := locationCfg{ + pathLocation: buildLocation(location, enforceRegexModifier), + proxySetHeader: getProxySetHeader(location), + authPath: buildAuthLocation(location, cfg.GlobalExternalAuth.URL), + applyGlobalAuth: shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL), + applyAuthUpstream: shouldApplyAuthUpstream(location, cfg), + externalAuth: &externalAuth{}, + } + + if location.Rewrite.AppRoot != "" { + serverLocations = append(serverLocations, + buildBlockDirective("if", []string{"$uri", "=", "/"}, + ngx_crossplane.Directives{ + buildDirective("return", "302", fmt.Sprintf("$scheme://$http_host%s", location.Rewrite.AppRoot)), + })) + } + + if locationConfig.applyGlobalAuth { + locationConfig.externalAuth = buildExternalAuth(cfg.GlobalExternalAuth) + } else { + locationConfig.externalAuth = buildExternalAuth(location.ExternalAuth) + } + if locationConfig.authPath != "" { + serverLocations = append(serverLocations, c.buildAuthLocation(server, location, locationConfig)) + } + if location.Denied == nil && locationConfig.externalAuth != nil && locationConfig.externalAuth.SigninURL != "" { + directives := ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("add_header", "Set-Cookie", "$auth_cookie"), + } + if location.CorsConfig.CorsEnabled { + directives = append(directives, buildCorsDirectives(location.CorsConfig)...) + } + directives = append(directives, + buildDirective("return", + "302", + buildAuthSignURL(locationConfig.externalAuth.SigninURL, locationConfig.externalAuth.SigninURLRedirectParam))) + + serverLocations = append(serverLocations, buildBlockDirective("location", + []string{buildAuthSignURLLocation(location.Path, locationConfig.externalAuth.SigninURL)}, directives)) + + } + serverLocations = append(serverLocations, c.buildLocation(server, location, locationConfig)) + + } + + return serverLocations +} + +func (c *Template) buildLocation(server *ingress.Server, + location *ingress.Location, locationConfig locationCfg) *ngx_crossplane.Directive { + ing := getIngressInformation(location.Ingress, server.Hostname, location.IngressPath) + cfg := c.tplConfig + locationDirectives := ngx_crossplane.Directives{ + buildDirective("set", "$namespace", ing.Namespace), + buildDirective("set", "$ingress_name", ing.Rule), + buildDirective("set", "$service_name", ing.Service), + buildDirective("set", "$service_port", ing.ServicePort), + buildDirective("set", "$balancer_ewma_score", "-1"), + buildDirective("set", "$proxy_upstream_name", location.Backend), + buildDirective("set", "$proxy_host", "$proxy_upstream_name"), + buildDirective("set", "$pass_access_scheme", "$scheme"), + buildDirective("set", "$best_http_host", "$http_host"), + buildDirective("set", "$pass_port", "$pass_server_port"), + buildDirective("set", "$proxy_alternative_upstream_name", ""), + buildDirective("set", "$location_path", strings.ReplaceAll(ing.Path, `$`, `${literal_dollar}`)), + } + + locationDirectives = append(locationDirectives, locationConfigForLua(location, *c.tplConfig)...) + locationDirectives = append(locationDirectives, buildCertificateDirectives(location)...) + + if cfg.Cfg.UseProxyProtocol { + locationDirectives = append(locationDirectives, + buildDirective("set", "$pass_server_port", "$proxy_protocol_server_port")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("set", "$pass_server_port", "$server_port")) + } + + locationDirectives = append(locationDirectives, + buildOpentelemetryForLocationDirectives(cfg.Cfg.EnableOpentelemetry, cfg.Cfg.OpentelemetryTrustIncomingSpan, location)...) + + locationDirectives = append(locationDirectives, + buildDirective("rewrite_by_lua_file", "/etc/nginx/lua/nginx/ngx_rewrite.lua"), + buildDirective("header_filter_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua"), + buildDirective("log_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_log_block.lua"), + buildDirective("rewrite_log", location.Logs.Rewrite), + // buildDirective("http2_push_preload", location.HTTP2PushPreload), // This directive is deprecated, keeping out of new crossplane + buildDirective("port_in_redirect", location.UsePortInRedirects)) + + if location.Mirror.Source != "" { + locationDirectives = append(locationDirectives, + buildDirective("mirror", location.Mirror.Source), + buildDirective("mirror_request_body", location.Mirror.RequestBody), + ) + } + + if !location.Logs.Access { + locationDirectives = append(locationDirectives, + buildDirective("access_log", "off"), + ) + } + if location.Denied != nil { + locationDirectives = append(locationDirectives, + buildDirectiveWithComment("return", fmt.Sprintf("Location denied. Reason: %s", *location.Denied), "503")) + } else { + locationDirectives = append(locationDirectives, c.buildAllowedLocation(server, location, locationConfig)...) + } + + return buildBlockDirective("location", locationConfig.pathLocation, locationDirectives) +} + +func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingress.Location, locationConfig locationCfg) ngx_crossplane.Directives { + dir := make(ngx_crossplane.Directives, 0) + proxySetHeader := locationConfig.proxySetHeader + for _, ip := range location.Denylist.CIDR { + dir = append(dir, buildDirective("deny", ip)) + } + if len(location.Allowlist.CIDR) > 0 { + for _, ip := range location.Allowlist.CIDR { + dir = append(dir, buildDirective("allow", ip)) + } + dir = append(dir, buildDirective("deny", "all")) + } + + if location.CorsConfig.CorsEnabled { + dir = append(dir, buildCorsDirectives(location.CorsConfig)...) + } + // TODO: Implement the build Auth Location + if !isLocationInLocationList(location, c.tplConfig.Cfg.NoAuthLocations) { + dir = append(dir, buildAuthLocationConfig(location, locationConfig)...) + } + + dir = append(dir, buildRateLimit(location)...) + + if isValidByteSize(location.Proxy.BodySize, true) { + dir = append(dir, buildDirective("client_max_body_size", location.Proxy.BodySize)) + } + if isValidByteSize(location.ClientBodyBufferSize, false) { + dir = append(dir, buildDirective("client_body_buffer_size", location.ClientBodyBufferSize)) + } + + if location.UpstreamVhost != "" { + dir = append(dir, buildDirective(proxySetHeader, "Host", location.UpstreamVhost)) + } else { + dir = append(dir, buildDirective(proxySetHeader, "Host", "$best_http_host")) + } + + if server.CertificateAuth.CAFileName != "" { + dir = append(dir, + buildDirective(proxySetHeader, "ssl-client-verify", "$ssl_client_verify"), + buildDirective(proxySetHeader, "ssl-client-subject-dn", "$ssl_client_s_dn"), + buildDirective(proxySetHeader, "ssl-client-issuer-dn", "$ssl_client_i_dn"), + ) + + if server.CertificateAuth.PassCertToUpstream { + dir = append(dir, buildDirective(proxySetHeader, "ssl-client-cert", "$ssl_client_escaped_cert")) + } + } + + dir = append(dir, + buildDirective(proxySetHeader, "Upgrade", "$http_upgrade"), + buildDirective(proxySetHeader, "X-Request-ID", "$req_id"), + buildDirective(proxySetHeader, "X-Real-IP", "$remote_addr"), + buildDirective(proxySetHeader, "X-Forwarded-Host", "$best_http_host"), + buildDirective(proxySetHeader, "X-Forwarded-Port", "$pass_port"), + buildDirective(proxySetHeader, "X-Forwarded-Proto", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Forwarded-Scheme", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Real-IP", "$remote_addr"), + buildDirective(proxySetHeader, "X-Scheme", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Original-Forwarded-For", + fmt.Sprintf("$http_%s", strings.ToLower(strings.ReplaceAll(c.tplConfig.Cfg.ForwardedForHeader, "-", "_")))), + buildDirectiveWithComment(proxySetHeader, + "mitigate HTTProxy Vulnerability - https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/", "Proxy", ""), + buildDirective("proxy_connect_timeout", seconds(location.Proxy.ConnectTimeout)), + buildDirective("proxy_read_timeout", seconds(location.Proxy.ReadTimeout)), + buildDirective("proxy_send_timeout", seconds(location.Proxy.SendTimeout)), + buildDirective("proxy_buffering", location.Proxy.ProxyBuffering), + buildDirective("proxy_buffer_size", location.Proxy.BufferSize), + buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), + buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion), + buildDirective("proxy_cookie_domain", strings.Split(location.Proxy.CookieDomain, " ")), + buildDirective("proxy_cookie_path", strings.Split(location.Proxy.CookiePath, " ")), + buildDirective("proxy_next_upstream_timeout", location.Proxy.NextUpstreamTimeout), + buildDirective("proxy_next_upstream_tries", location.Proxy.NextUpstreamTries), + buildDirective("proxy_next_upstream", buildNextUpstream(location.Proxy.NextUpstream, c.tplConfig.Cfg.RetryNonIdempotent)), + ) + + if isValidByteSize(location.Proxy.ProxyMaxTempFileSize, true) { + dir = append(dir, buildDirective("proxy_max_temp_file_size", location.Proxy.ProxyMaxTempFileSize)) + } + + if c.tplConfig.Cfg.UseForwardedHeaders && c.tplConfig.Cfg.ComputeFullForwardedFor { + dir = append(dir, buildDirective(proxySetHeader, "X-Forwarded-For", "$full_x_forwarded_for")) + } else { + dir = append(dir, buildDirective(proxySetHeader, "X-Forwarded-For", "$remote_addr")) + } + + if c.tplConfig.Cfg.ProxyAddOriginalURIHeader { + dir = append(dir, buildDirective(proxySetHeader, "X-Original-URI", "$request_uri")) + } + + if location.Connection.Enabled { + dir = append(dir, buildDirective(proxySetHeader, "Connection", location.Connection.Header)) + } else { + dir = append(dir, buildDirective(proxySetHeader, "Connection", "$connection_upgrade")) + } + + for k, v := range c.tplConfig.ProxySetHeaders { + dir = append(dir, buildDirective(proxySetHeader, k, v)) + } + + for k, v := range location.CustomHeaders.Headers { + dir = append(dir, buildDirective("more_set_headers", fmt.Sprintf("%s: %s", k, strings.ReplaceAll(v, `$`, `${literal_dollar}`)))) + } + + if strings.HasPrefix(location.Backend, "custom-default-backend-") { + dir = append(dir, + buildDirective("proxy_set_header", "X-Code", "503"), + buildDirective("proxy_set_header", "X-Format", "$http_accept"), + buildDirective("proxy_set_header", "X-Namespace", "$namespace"), + buildDirective("proxy_set_header", "X-Ingress-Name", "$ingress_name"), + buildDirective("proxy_set_header", "X-Service-Name", "$service_name"), + buildDirective("proxy_set_header", "X-Service-Port", "$service_port"), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + ) + } + + if location.Satisfy != "" { + dir = append(dir, buildDirective("satisfy", location.Satisfy)) + } + + if len(location.CustomHTTPErrors) > 0 && !location.DisableProxyInterceptErrors { + dir = append(dir, buildDirective("proxy_intercept_errors", "on")) + } + + for _, errorcode := range location.CustomHTTPErrors { + dir = append(dir, buildDirective( + "error_page", + errorcode, "=", + fmt.Sprintf("@custom_%s_%d", location.DefaultBackendUpstreamName, errorcode)), + ) + } + + switch location.BackendProtocol { + case "GRPC", "GRPCS": + dir = append(dir, + buildDirective("grpc_connect_timeout", seconds(location.Proxy.ConnectTimeout)), + buildDirective("grpc_send_timeout", seconds(location.Proxy.SendTimeout)), + buildDirective("grpc_read_timeout", seconds(location.Proxy.ReadTimeout)), + ) + case "FCGI": + dir = append(dir, buildDirective("include", "/etc/nginx/fastcgi_params")) + if location.FastCGI.Index != "" { + dir = append(dir, buildDirective("fastcgi_index", location.FastCGI.Index)) + } + for k, v := range location.FastCGI.Params { + dir = append(dir, buildDirective("fastcgi_param", k, v)) + } + } + + if location.Redirect.URL != "" { + dir = append(dir, buildDirective("return", location.Redirect.Code, location.Redirect.URL)) + } + + dir = append(dir, buildProxyPass(c.tplConfig.Backends, location)...) + + if location.Proxy.ProxyRedirectFrom == "default" || location.Proxy.ProxyRedirectFrom == "off" { + dir = append(dir, buildDirective("proxy_redirect", location.Proxy.ProxyRedirectFrom)) + } else if location.Proxy.ProxyRedirectTo != "off" { + dir = append(dir, buildDirective("proxy_redirect", location.Proxy.ProxyRedirectFrom, location.Proxy.ProxyRedirectTo)) + } + + return dir +} + +func buildCertificateDirectives(location *ingress.Location) ngx_crossplane.Directives { + cert := make(ngx_crossplane.Directives, 0) + if location.ProxySSL.CAFileName != "" { + cert = append(cert, + buildDirectiveWithComment( + "proxy_ssl_trusted_certificate", + fmt.Sprintf("#PEM sha: %s", location.ProxySSL.CASHA), + location.ProxySSL.CAFileName, + ), + buildDirective("proxy_ssl_ciphers", location.ProxySSL.Ciphers), + buildDirective("proxy_ssl_protocols", strings.Split(location.ProxySSL.Protocols, " ")), + buildDirective("proxy_ssl_verify", location.ProxySSL.Verify), + buildDirective("proxy_ssl_verify_depth", location.ProxySSL.VerifyDepth), + ) + } + if location.ProxySSL.ProxySSLName != "" { + cert = append(cert, buildDirective("proxy_ssl_name", location.ProxySSL.ProxySSLName)) + } + if location.ProxySSL.ProxySSLServerName != "" { + cert = append(cert, buildDirective("proxy_ssl_server_name", location.ProxySSL.ProxySSLServerName)) + } + if location.ProxySSL.PemFileName != "" { + cert = append(cert, + buildDirective("proxy_ssl_certificate", location.ProxySSL.PemFileName), + buildDirective("proxy_ssl_certificate_key", location.ProxySSL.PemFileName), + ) + } + return cert +} + +type ingressInformation struct { + Namespace string + Path string + Rule string + Service string + ServicePort string + Annotations map[string]string +} + +func getIngressInformation(ing *ingress.Ingress, hostname, ingressPath string) *ingressInformation { + if ing == nil { + return &ingressInformation{} + } + + info := &ingressInformation{ + Namespace: ing.GetNamespace(), + Rule: ing.GetName(), + Annotations: ing.Annotations, + Path: ingressPath, + } + + if ingressPath == "" { + ingressPath = "/" + info.Path = "/" + } + + if ing.Spec.DefaultBackend != nil && ing.Spec.DefaultBackend.Service != nil { + info.Service = ing.Spec.DefaultBackend.Service.Name + if ing.Spec.DefaultBackend.Service.Port.Number > 0 { + info.ServicePort = strconv.Itoa(int(ing.Spec.DefaultBackend.Service.Port.Number)) + } else { + info.ServicePort = ing.Spec.DefaultBackend.Service.Port.Name + } + } + + for _, rule := range ing.Spec.Rules { + if rule.HTTP == nil { + continue + } + + if hostname != "_" && rule.Host == "" { + continue + } + + host := "_" + if rule.Host != "" { + host = rule.Host + } + + if hostname != host { + continue + } + + for _, rPath := range rule.HTTP.Paths { + if ingressPath != rPath.Path { + continue + } + + if rPath.Backend.Service == nil { + continue + } + + if info.Service != "" && rPath.Backend.Service.Name == "" { + // empty rule. Only contains a Path and PathType + return info + } + + info.Service = rPath.Backend.Service.Name + if rPath.Backend.Service.Port.Number > 0 { + info.ServicePort = strconv.Itoa(int(rPath.Backend.Service.Port.Number)) + } else { + info.ServicePort = rPath.Backend.Service.Port.Name + } + + return info + } + } + + return info +} + +func buildOpentelemetryForLocationDirectives(isOTEnabled, isOTTrustSet bool, location *ingress.Location) ngx_crossplane.Directives { + isOTEnabledInLoc := location.Opentelemetry.Enabled + isOTSetInLoc := location.Opentelemetry.Set + directives := make(ngx_crossplane.Directives, 0) + if isOTEnabled { + if isOTSetInLoc && !isOTEnabledInLoc { + return ngx_crossplane.Directives{ + buildDirective("opentelemetry", "off"), + } + } + } else if !isOTSetInLoc || !isOTEnabledInLoc { + return directives + } + + if location != nil { + directives = append(directives, + buildDirective("opentelemetry", "on"), + buildDirective("opentelemetry_propagate"), + ) + if location.Opentelemetry.OperationName != "" { + directives = append(directives, + buildDirective("opentelemetry_operation_name", location.Opentelemetry.OperationName)) + } + + if (!isOTTrustSet && !location.Opentelemetry.TrustSet) || + (location.Opentelemetry.TrustSet && !location.Opentelemetry.TrustEnabled) { + directives = append(directives, + buildDirective("opentelemetry_trust_incoming_spans", "off"), + ) + } else { + directives = append(directives, + buildDirective("opentelemetry_trust_incoming_spans", "on"), + ) + } + } + + return directives +} + +// buildRateLimit produces an array of limit_req to be used inside the Path of +// Ingress rules. The order: connections by IP first, then RPS, and RPM last. +func buildRateLimit(loc *ingress.Location) ngx_crossplane.Directives { + limits := make(ngx_crossplane.Directives, 0) + + if loc.RateLimit.Connections.Limit > 0 { + limits = append(limits, buildDirective("limit_conn", loc.RateLimit.Connections.Name, loc.RateLimit.Connections.Limit)) + } + + if loc.RateLimit.RPS.Limit > 0 { + limits = append(limits, + buildDirective( + "limit_req", + fmt.Sprintf("zone=%s", loc.RateLimit.RPS.Name), + fmt.Sprintf("burst=%d", loc.RateLimit.RPS.Burst), + "nodelay", + ), + ) + } + + if loc.RateLimit.RPM.Limit > 0 { + limits = append(limits, + buildDirective( + "limit_req", + fmt.Sprintf("zone=%s", loc.RateLimit.RPM.Name), + fmt.Sprintf("burst=%d", loc.RateLimit.RPM.Burst), + "nodelay", + ), + ) + } + + if loc.RateLimit.LimitRateAfter > 0 { + limits = append(limits, + buildDirective( + "limit_rate_after", + fmt.Sprintf("%dk", loc.RateLimit.LimitRateAfter), + ), + ) + } + + if loc.RateLimit.LimitRate > 0 { + limits = append(limits, + buildDirective( + "limit_rate", + fmt.Sprintf("%dk", loc.RateLimit.LimitRate), + ), + ) + } + + return limits +} + +// locationConfigForLua formats some location specific configuration into Lua table represented as string +func locationConfigForLua(location *ingress.Location, all config.TemplateConfig) ngx_crossplane.Directives { + /* Lua expects the following vars + force_ssl_redirect = string_to_bool(ngx.var.force_ssl_redirect), + ssl_redirect = string_to_bool(ngx.var.ssl_redirect), + force_no_ssl_redirect = string_to_bool(ngx.var.force_no_ssl_redirect), + preserve_trailing_slash = string_to_bool(ngx.var.preserve_trailing_slash), + use_port_in_redirects = string_to_bool(ngx.var.use_port_in_redirects), + */ + + return ngx_crossplane.Directives{ + buildDirective("set", "$force_ssl_redirect", strconv.FormatBool(location.Rewrite.ForceSSLRedirect)), + buildDirective("set", "$ssl_redirect", strconv.FormatBool(location.Rewrite.SSLRedirect)), + buildDirective("set", "$force_no_ssl_redirect", strconv.FormatBool(isLocationInLocationList(location, all.Cfg.NoTLSRedirectLocations))), + buildDirective("set", "$preserve_trailing_slash", strconv.FormatBool(location.Rewrite.PreserveTrailingSlash)), + buildDirective("set", "$use_port_in_redirects", strconv.FormatBool(location.UsePortInRedirects)), + } +} + +func isLocationInLocationList(loc *ingress.Location, rawLocationList string) bool { + locationList := strings.Split(rawLocationList, ",") + + for _, locationListItem := range locationList { + locationListItem = strings.Trim(locationListItem, " ") + if locationListItem == "" { + continue + } + if strings.HasPrefix(loc.Path, locationListItem) { + return true + } + } + + return false +} + +func buildAuthLocationConfig(location *ingress.Location, locationConfig locationCfg) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, 0) + if locationConfig.authPath != "" { + if locationConfig.applyAuthUpstream && !locationConfig.applyGlobalAuth { + directives = append(directives, buildDirective("set", "$auth_cookie", "")) + directives = append(directives, buildDirective("add_header", "Set-Cookie", "$auth_cookie")) + directives = append(directives, buildAuthResponseHeaders(locationConfig.proxySetHeader, locationConfig.externalAuth.ResponseHeaders, true)...) + if len(locationConfig.externalAuth.ResponseHeaders) > 0 { + directives = append(directives, buildDirective("set", "$auth_response_headers", strings.Join(locationConfig.externalAuth.ResponseHeaders, ","))) + } + directives = append(directives, + buildDirective("set", "$auth_path", locationConfig.authPath), + buildDirective("set", "$auth_keepalive_share_vars", strconv.FormatBool(locationConfig.externalAuth.KeepaliveShareVars)), + buildDirective("access_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_external_auth.lua"), + ) + } else { + directives = append(directives, + buildDirective("auth_request", locationConfig.authPath), + buildDirective("auth_request_set", "$auth_cookie", "$upstream_http_set_cookie"), + ) + cookieDirective := buildDirective("add_header", "Set-Cookie", "$auth_cookie") + if locationConfig.externalAuth.AlwaysSetCookie { + cookieDirective.Args = append(cookieDirective.Args, "always") + } + directives = append(directives, cookieDirective) + directives = append(directives, buildAuthResponseHeaders(locationConfig.proxySetHeader, locationConfig.externalAuth.ResponseHeaders, false)...) + } + } + + if locationConfig.externalAuth.SigninURL != "" { + directives = append(directives, + buildDirective("set_escape_uri", "$escaped_request_uri", "$request_uri"), + buildDirective("error_page", "401", "=", buildAuthSignURLLocation(location.Path, locationConfig.externalAuth.SigninURL)), + ) + } + if location.BasicDigestAuth.Secured { + var authDirective, authFileDirective string + if location.BasicDigestAuth.Type == "basic" { + authDirective, authFileDirective = "auth_basic", "auth_basic_user_file" + } else { + authDirective, authFileDirective = "auth_digest", "auth_digest_user_file" + } + + directives = append(directives, + buildDirective(authDirective, location.BasicDigestAuth.Realm), + buildDirective(authFileDirective, location.BasicDigestAuth.File), + buildDirective(locationConfig.proxySetHeader, "Authorization", ""), + ) + } + + return directives + /* + Missing this Lua script + # `auth_request` module does not support HTTP keepalives in upstream block: + # https://trac.nginx.org/nginx/ticket/1579 + access_by_lua_block { + local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) + if res.status == ngx.HTTP_OK then + ngx.var.auth_cookie = res.header['Set-Cookie'] + {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} # IF 4 + {{ $line }} + {{- end }} # END IF 4 + return + end + if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then + ngx.exit(res.status) + end + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + } + + */ +} diff --git a/internal/ingress/controller/template/crossplane/server.go b/internal/ingress/controller/template/crossplane/server.go new file mode 100644 index 0000000000..541e156f0f --- /dev/null +++ b/internal/ingress/controller/template/crossplane/server.go @@ -0,0 +1,268 @@ +/* +Copyright 2024 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 crossplane + +import ( + "fmt" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + "k8s.io/ingress-nginx/pkg/apis/ingress" + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" + "k8s.io/utils/ptr" +) + +func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane.Directive { + cfg := c.tplConfig.Cfg + serverName := buildServerName(server.Hostname) + serverBlock := ngx_crossplane.Directives{ + buildDirective("server_name", serverName, server.Aliases), + buildDirective("http2", cfg.UseHTTP2), + buildDirective("set", "$proxy_upstream_name", "-"), + buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), + } + + serverBlock = append(serverBlock, buildListener(*c.tplConfig, server.Hostname)...) + serverBlock = append(serverBlock, c.buildBlockers()...) + + if server.Hostname == "_" { + serverBlock = append(serverBlock, buildDirective("ssl_reject_handshake", cfg.SSLRejectHandshake)) + } + + if server.CertificateAuth.MatchCN != "" { + matchCNBlock := buildBlockDirective("if", + []string{"$ssl_client_s_dn", "!~", server.CertificateAuth.MatchCN}, + ngx_crossplane.Directives{ + buildDirective("return", "403", "client certificate unauthorized"), + }) + serverBlock = append(serverBlock, matchCNBlock) + } + + if server.AuthTLSError != "" { + serverBlock = append(serverBlock, buildDirective("return", 403)) + } else { + + serverBlock = append(serverBlock, c.buildCertificateDirectives(server)...) + serverBlock = append(serverBlock, buildCustomErrorLocationsPerServer(server, c.tplConfig.EnableMetrics)...) + serverBlock = append(serverBlock, buildMirrorLocationDirective(server.Locations)...) + + // The other locations should come here! + serverBlock = append(serverBlock, c.buildServerLocations(server, server.Locations)...) + + } + + // "/healthz" location + if server.Hostname == "_" { + dirs := ngx_crossplane.Directives{ + buildDirective("access_log", "off"), + buildDirective("return", "200"), + } + if cfg.EnableOpentelemetry { + dirs = append(dirs, buildDirective("opentelemetry", "off")) + } + healthLocation := buildBlockDirective("location", + []string{c.tplConfig.HealthzURI}, dirs) + serverBlock = append(serverBlock, healthLocation) + + // "/nginx_status" location + statusLocationDirs := ngx_crossplane.Directives{} + if cfg.EnableOpentelemetry { + statusLocationDirs = append(statusLocationDirs, buildDirective("opentelemetry", "off")) + } + + for _, v := range c.tplConfig.NginxStatusIpv4Whitelist { + statusLocationDirs = append(statusLocationDirs, buildDirective("allow", v)) + } + + if c.tplConfig.IsIPV6Enabled { + for _, v := range c.tplConfig.NginxStatusIpv6Whitelist { + statusLocationDirs = append(statusLocationDirs, buildDirective("allow", v)) + } + } + statusLocationDirs = append(statusLocationDirs, + buildDirective("deny", "all"), + buildDirective("access_log", "off"), + buildDirective("stub_status", "on")) + + // End of "nginx_status" location + + serverBlock = append(serverBlock, buildBlockDirective("location", []string{"/nginx_status"}, statusLocationDirs)) + + } + + // DO NOT MOVE! THIS IS THE END DIRECTIVE OF SERVERS + serverBlock = append(serverBlock, buildCustomErrorLocation("upstream-default-backend", cfg.CustomHTTPErrors, c.tplConfig.EnableMetrics)...) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildCertificateDirectives(server *ingress.Server) ngx_crossplane.Directives { + certDirectives := make(ngx_crossplane.Directives, 0) + + if server.CertificateAuth.CAFileName != "" { + certAuth := server.CertificateAuth + certDirectives = append(certDirectives, buildDirective("ssl_client_certificate", certAuth.CAFileName)) + certDirectives = append(certDirectives, buildDirective("ssl_verify_client", certAuth.VerifyClient)) + certDirectives = append(certDirectives, buildDirective("ssl_verify_depth", certAuth.ValidationDepth)) + if certAuth.CRLFileName != "" { + certDirectives = append(certDirectives, buildDirective("ssl_crl", certAuth.CRLFileName)) + } + if certAuth.ErrorPage != "" { + certDirectives = append(certDirectives, buildDirective("error_page", "495", "496", "=", certAuth.ErrorPage)) + } + } + + prxSSL := server.ProxySSL + if prxSSL.CAFileName != "" { + certDirectives = append(certDirectives, buildDirective("proxy_ssl_trusted_certificate", prxSSL.CAFileName)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_ciphers", prxSSL.Ciphers)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_protocols", strings.Split(prxSSL.Protocols, " "))) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_verify", prxSSL.Verify)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_verify_depth", prxSSL.VerifyDepth)) + if prxSSL.ProxySSLName != "" { + certDirectives = append(certDirectives, buildDirective("proxy_ssl_name", prxSSL.ProxySSLName)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_server_name", prxSSL.ProxySSLServerName)) + } + } + if prxSSL.PemFileName != "" { + certDirectives = append(certDirectives, buildDirective("proxy_ssl_certificate", prxSSL.PemFileName)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_certificate_key", prxSSL.PemFileName)) + } + if server.SSLCiphers != "" { + certDirectives = append(certDirectives, buildDirective("ssl_ciphers", server.SSLCiphers)) + } + + if server.SSLPreferServerCiphers != "" { + certDirectives = append(certDirectives, buildDirective("ssl_prefer_server_ciphers", server.SSLPreferServerCiphers)) + } + + return certDirectives +} + +// buildRedirectServer builds the server blocks for redirections +func (c *Template) buildRedirectServer(server *utilingress.Redirect) *ngx_crossplane.Directive { + serverBlock := ngx_crossplane.Directives{ + buildDirective("server_name", server.From), + buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), + buildDirective("set_by_lua_file", "$redirect_to", "/etc/nginx/lua/nginx/ngx_srv_redirect.lua", server.To), + } + serverBlock = append(serverBlock, buildListener(*c.tplConfig, server.From)...) + serverBlock = append(serverBlock, c.buildBlockers()...) + serverBlock = append(serverBlock, buildDirective("return", c.tplConfig.Cfg.HTTPRedirectCode, "$redirect_to")) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +// buildDefaultBackend builds the default catch all server +func (c *Template) buildDefaultBackend() *ngx_crossplane.Directive { + var reusePort *string + if c.tplConfig.Cfg.ReusePort { + reusePort = ptr.To("reuseport") + } + serverBlock := ngx_crossplane.Directives{ + buildDirective("listen", c.tplConfig.ListenPorts.Default, "default_server", reusePort, fmt.Sprintf("backlog=%d", c.tplConfig.BacklogSize)), + } + if c.tplConfig.IsIPV6Enabled { + serverBlock = append(serverBlock, buildDirective( + "listen", + fmt.Sprintf("[::]:%d", c.tplConfig.ListenPorts.Default), + "default_server", reusePort, + fmt.Sprintf("backlog=%d", c.tplConfig.BacklogSize), + )) + } + serverBlock = append(serverBlock, buildDirective("set", "$proxy_upstream_name", "internal")) + serverBlock = append(serverBlock, buildDirective("access_log", "off")) + serverBlock = append(serverBlock, buildBlockDirective("location", []string{"/"}, ngx_crossplane.Directives{ + buildDirective("return", "404"), + })) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildHealthAndStatsServer() *ngx_crossplane.Directive { + serverBlock := ngx_crossplane.Directives{ + buildDirective("listen", fmt.Sprintf("127.0.0.1:%d", c.tplConfig.StatusPort)), + buildDirective("set", "$proxy_upstream_name", "internal"), + buildDirective("keepalive_timeout", "0"), + buildDirective("gzip", "off"), + buildDirective("access_log", "off"), + buildBlockDirective( + "location", + []string{c.tplConfig.HealthzURI}, ngx_crossplane.Directives{ + buildDirective("return", "200"), + }), + buildBlockDirective( + "location", + []string{"/is-dynamic-lb-initialized"}, ngx_crossplane.Directives{ + buildDirective("content_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua"), + }), + buildBlockDirective( + "location", + []string{c.tplConfig.StatusPath}, ngx_crossplane.Directives{ + buildDirective("stub_status", "on"), + }), + buildBlockDirective( + "location", + []string{"/configuration"}, ngx_crossplane.Directives{ + buildDirective("client_max_body_size", luaConfigurationRequestBodySize(c.tplConfig.Cfg)), + buildDirective("client_body_buffer_size", luaConfigurationRequestBodySize(c.tplConfig.Cfg)), + buildDirective("proxy_buffering", "off"), + buildDirective("content_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_configuration.lua"), + }), + buildBlockDirective( + "location", + []string{"/"}, ngx_crossplane.Directives{ + buildDirective("return", "404"), + }), + } + if c.tplConfig.Cfg.EnableOpentelemetry { + serverBlock = append(serverBlock, buildDirective("opentelemetry", "off")) + } + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildBlockers() ngx_crossplane.Directives { + blockers := make(ngx_crossplane.Directives, 0) + if len(c.tplConfig.Cfg.BlockUserAgents) > 0 { + uaDirectives := buildBlockDirective("if", []string{"$block_ua"}, ngx_crossplane.Directives{ + buildDirective("return", "403"), + }) + blockers = append(blockers, uaDirectives) + } + + if len(c.tplConfig.Cfg.BlockReferers) > 0 { + refDirectives := buildBlockDirective("if", []string{"$block_ref"}, ngx_crossplane.Directives{ + buildDirective("return", "403"), + }) + blockers = append(blockers, refDirectives) + } + return blockers +} diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl index 64e5a0c3f9..547fd12dac 100644 --- a/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl +++ b/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl @@ -86,113 +86,113 @@ http { {{ range $index, $file := $all.MaxmindEditionFiles }} {{ if eq $file "GeoLite2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; - $geoip2_country_name source=$remote_addr country names en; - $geoip2_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_continent_code source=$remote_addr continent code; - $geoip2_continent_name source=$remote_addr continent names en; - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + $geoip2_country_code source=$remote_addr country iso_code; # OK + $geoip2_country_name source=$remote_addr country names en; # OK + $geoip2_country_geoname_id source=$remote_addr country geoname_id; # OK + $geoip2_continent_code source=$remote_addr continent code; # OK + $geoip2_continent_name source=$remote_addr continent names en; # OK + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; # OK } {{ end }} {{ if eq $file "GeoIP2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; - $geoip2_country_name source=$remote_addr country names en; - $geoip2_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_continent_code source=$remote_addr continent code; - $geoip2_continent_name source=$remote_addr continent names en; - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; + $geoip2_country_code source=$remote_addr country iso_code; # OK + $geoip2_country_name source=$remote_addr country names en; # OK + $geoip2_country_geoname_id source=$remote_addr country geoname_id; # OK + $geoip2_continent_code source=$remote_addr continent code; # OK + $geoip2_continent_name source=$remote_addr continent names en; # OK + $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; # OK } {{ end }} {{ if eq $file "GeoLite2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; - $geoip2_city_country_name source=$remote_addr country names en; - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_city source=$remote_addr city names en; - $geoip2_city_geoname_id source=$remote_addr city geoname_id; - $geoip2_postal_code source=$remote_addr postal code; - $geoip2_dma_code source=$remote_addr location metro_code; - $geoip2_latitude source=$remote_addr location latitude; - $geoip2_longitude source=$remote_addr location longitude; - $geoip2_time_zone source=$remote_addr location time_zone; - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; - $geoip2_region_name source=$remote_addr subdivisions 0 names en; - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; - $geoip2_city_continent_code source=$remote_addr continent code; - $geoip2_city_continent_name source=$remote_addr continent names en; + $geoip2_city_country_code source=$remote_addr country iso_code; # OK + $geoip2_city_country_name source=$remote_addr country names en; # OK + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; # OK + $geoip2_city source=$remote_addr city names en; # OK + $geoip2_city_geoname_id source=$remote_addr city geoname_id; # OK + $geoip2_postal_code source=$remote_addr postal code; # OK + $geoip2_dma_code source=$remote_addr location metro_code; # OK + $geoip2_latitude source=$remote_addr location latitude; # OK + $geoip2_longitude source=$remote_addr location longitude; # OK + $geoip2_time_zone source=$remote_addr location time_zone; # OK + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; # OK + $geoip2_region_name source=$remote_addr subdivisions 0 names en; # OK + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; # OK + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; # OK + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; # OK + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; # OK + $geoip2_city_continent_code source=$remote_addr continent code; # OK + $geoip2_city_continent_name source=$remote_addr continent names en; # OK } {{ end }} {{ if eq $file "GeoIP2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; - $geoip2_city_country_name source=$remote_addr country names en; - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_city source=$remote_addr city names en; - $geoip2_city_geoname_id source=$remote_addr city geoname_id; - $geoip2_postal_code source=$remote_addr postal code; - $geoip2_dma_code source=$remote_addr location metro_code; - $geoip2_latitude source=$remote_addr location latitude; - $geoip2_longitude source=$remote_addr location longitude; - $geoip2_time_zone source=$remote_addr location time_zone; - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; - $geoip2_region_name source=$remote_addr subdivisions 0 names en; - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; - $geoip2_city_continent_code source=$remote_addr continent code; - $geoip2_city_continent_name source=$remote_addr continent names en; + $geoip2_city_country_code source=$remote_addr country iso_code; # OK + $geoip2_city_country_name source=$remote_addr country names en; # OK + $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; # OK + $geoip2_city source=$remote_addr city names en; # OK + $geoip2_city_geoname_id source=$remote_addr city geoname_id; # OK + $geoip2_postal_code source=$remote_addr postal code; # OK + $geoip2_dma_code source=$remote_addr location metro_code; # OK + $geoip2_latitude source=$remote_addr location latitude; # OK + $geoip2_longitude source=$remote_addr location longitude; # OK + $geoip2_time_zone source=$remote_addr location time_zone; # OK + $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; # OK + $geoip2_region_name source=$remote_addr subdivisions 0 names en; # OK + $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; # OK + $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; # OK + $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; # OK + $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; # OK + $geoip2_city_continent_code source=$remote_addr continent code; # OK + $geoip2_city_continent_name source=$remote_addr continent names en; # OK } {{ end }} {{ if eq $file "GeoLite2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; - $geoip2_org source=$remote_addr autonomous_system_organization; + $geoip2_asn source=$remote_addr autonomous_system_number; # OK + $geoip2_org source=$remote_addr autonomous_system_organization; # OK } {{ end }} {{ if eq $file "GeoIP2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; - $geoip2_org source=$remote_addr autonomous_system_organization; + $geoip2_asn source=$remote_addr autonomous_system_number; # OK + $geoip2_org source=$remote_addr autonomous_system_organization; # OK } {{ end }} {{ if eq $file "GeoIP2-ISP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_isp source=$remote_addr isp; - $geoip2_isp_org source=$remote_addr organization; - $geoip2_asn source=$remote_addr default=0 autonomous_system_number; + $geoip2_isp source=$remote_addr isp; # OK + $geoip2_isp_org source=$remote_addr organization; # OK + $geoip2_asn source=$remote_addr default=0 autonomous_system_number; # OK } {{ end }} @@ -203,16 +203,16 @@ http { {{ end }} {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { + geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { # OK {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; + auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK {{ end }} - $geoip2_is_anon source=$remote_addr is_anonymous; - $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; - $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; - $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; - $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; - $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; + $geoip2_is_anon source=$remote_addr is_anonymous; # OK + $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; # OK + $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; # OK + $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; # OK + $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; # OK + $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; # OK } {{ end }} @@ -252,7 +252,7 @@ http { {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} # OK http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; # OK - http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; # OK + http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; # OK {{ end }} {{ if (gt $cfg.HTTP2MaxRequests 0) }} # OK @@ -519,28 +519,28 @@ http { {{/* Build server redirects (from/to www) */}} {{ range $redirect := .RedirectServers }} ## start server {{ $redirect.From }} - server { - server_name {{ $redirect.From }}; + server { #OK + server_name {{ $redirect.From }}; # OK - {{ buildHTTPListener $all $redirect.From }} - {{ buildHTTPSListener $all $redirect.From }} + {{ buildHTTPListener $all $redirect.From }} # OK + {{ buildHTTPSListener $all $redirect.From }} # OK - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; # OK {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { - return 403; + if ($block_ua) { # OK + return 403; # OK } {{ end }} {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { - return 403; + if ($block_ref) { # OK + return 403; #OK } {{ end }} - set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; + set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; # OK - return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; + return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; # OK } ## end server {{ $redirect.From }} {{ end }} @@ -567,234 +567,111 @@ http { {{ range $server := $servers }} ## start server {{ $server.Hostname }} server { - server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; + server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; OK {{ if $cfg.UseHTTP2 }} - http2 on; + http2 on; OK {{ end }} {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { - return 403; + if ($block_ua) { OK + return 403; OK } {{ end }} {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { - return 403; + if ($block_ref) { OK + return 403; OK } {{ end }} {{ template "SERVER" serverConfig $all $server }} - {{ if not (empty $cfg.ServerSnippet) }} - # Custom code snippet configured in the configuration configmap - {{ $cfg.ServerSnippet }} - {{ end }} - - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics $cfg.EnableModsecurity) }} + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics) }} # OK } ## end server {{ $server.Hostname }} {{ end }} # backend for when default-backend-service is not configured or it does not have endpoints - server { - listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; - {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} - set $proxy_upstream_name "internal"; + server { # OK + listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; # OK + {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} # OK + set $proxy_upstream_name "internal"; # OK - access_log off; + access_log off; # OK - location / { - return 404; + location / { # OK + return 404; # OK } } # default server, used for NGINX healthcheck and access to nginx stats - server { - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; - {{ end }} + server { # OK - listen 127.0.0.1:{{ .StatusPort }}; - set $proxy_upstream_name "internal"; + listen 127.0.0.1:{{ .StatusPort }}; # OK + set $proxy_upstream_name "internal"; # OK - keepalive_timeout 0; - gzip off; + keepalive_timeout 0; # OK + gzip off; # OK - access_log off; + access_log off; # OK {{ if $cfg.EnableOpentelemetry }} - opentelemetry off; + opentelemetry off; # OK {{ end }} - location {{ $healthzURI }} { - return 200; + location {{ $healthzURI }} { # OK + return 200; # OK } - location /is-dynamic-lb-initialized { - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; + location /is-dynamic-lb-initialized { # OK + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; # OK } - location {{ .StatusPath }} { - stub_status on; + location {{ .StatusPath }} { # OK + stub_status on; # OK } - location /configuration { - client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; - client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; - proxy_buffering off; + location /configuration { # OK + client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; # OK + client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; # OK + proxy_buffering off; # OK - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; + content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; # OK } - location / { - return 404; + location / { # OK + return 404; # OK } } } -stream { - lua_package_path "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;"; - - lua_shared_dict tcp_udp_configuration_data 5M; - - {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} - - init_by_lua_file /etc/nginx/lua/ngx_conf_init_stream.lua; - - init_worker_by_lua_file /etc/nginx/lua/nginx/ngx_conf_init_tcp_udp.lua; - - lua_add_variable $proxy_upstream_name; - - log_format log_stream '{{ $cfg.LogFormatStream }}'; - - {{ if or $cfg.DisableAccessLog $cfg.DisableStreamAccessLog }} - access_log off; - {{ else }} - access_log {{ or $cfg.StreamAccessLogPath $cfg.AccessLogPath }} log_stream {{ $cfg.AccessLogParams }}; - {{ end }} - - - error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; - {{ if $cfg.EnableRealIP }} - {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} - set_real_ip_from {{ $trusted_ip }}; - {{ end }} - {{ end }} - - upstream upstream_balancer { - server 0.0.0.1:1234; # placeholder - balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer_tcp_udp.lua; - } - - server { - listen 127.0.0.1:{{ .StreamPort }}; - - access_log off; - - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_content_tcp_udp.lua; - } - - # TCP services - {{ range $tcpServer := .TCPBackends }} - server { - preread_by_lua_block { - ngx.var.proxy_upstream_name="tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}"; - } - - {{ range $address := $all.Cfg.BindAddressIpv4 }} - listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ else }} - listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ end }} - {{ if $IsIPV6Enabled }} - {{ range $address := $all.Cfg.BindAddressIpv6 }} - listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ else }} - listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ end }} - {{ end }} - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; - proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; - proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; - - proxy_pass upstream_balancer; - {{ if $tcpServer.Backend.ProxyProtocol.Encode }} - proxy_protocol on; - {{ end }} - } - {{ end }} - - # UDP services - {{ range $udpServer := .UDPBackends }} - server { - preread_by_lua_block { - ngx.var.proxy_upstream_name="udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}"; - } - - {{ range $address := $all.Cfg.BindAddressIpv4 }} - listen {{ $address }}:{{ $udpServer.Port }} udp; - {{ else }} - listen {{ $udpServer.Port }} udp; - {{ end }} - {{ if $IsIPV6Enabled }} - {{ range $address := $all.Cfg.BindAddressIpv6 }} - listen {{ $address }}:{{ $udpServer.Port }} udp; - {{ else }} - listen [::]:{{ $udpServer.Port }} udp; - {{ end }} - {{ end }} - proxy_responses {{ $cfg.ProxyStreamResponses }}; - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; - proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; - proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; - proxy_pass upstream_balancer; - } - {{ end }} - - # Stream Snippets - {{ range $snippet := .StreamSnippets }} - {{ $snippet }} - {{ end }} -} - {{/* definition of templates to avoid repetitions */}} {{ define "CUSTOM_ERRORS" }} {{ $enableMetrics := .EnableMetrics }} - {{ $modsecurityEnabled := .ModsecurityEnabled }} {{ $upstreamName := .UpstreamName }} {{ range $errCode := .ErrorCodes }} - location @custom_{{ $upstreamName }}_{{ $errCode }} { - internal; - - # Ensure that modsecurity will not run on custom error pages or they might be blocked - {{ if $modsecurityEnabled }} - modsecurity off; - {{ end }} - - proxy_intercept_errors off; - - proxy_set_header X-Code {{ $errCode }}; - proxy_set_header X-Format $http_accept; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Namespace $namespace; - proxy_set_header X-Ingress-Name $ingress_name; - proxy_set_header X-Service-Name $service_name; - proxy_set_header X-Service-Port $service_port; - proxy_set_header X-Request-ID $req_id; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $best_http_host; - - set $proxy_upstream_name {{ $upstreamName | quote }}; - - rewrite (.*) / break; - - proxy_pass http://upstream_balancer; + location @custom_{{ $upstreamName }}_{{ $errCode }} { # OK + internal; # OK + proxy_intercept_errors off; # OK + + proxy_set_header X-Code {{ $errCode }}; # OK + proxy_set_header X-Format $http_accept; # OK + proxy_set_header X-Original-URI $request_uri; # OK + proxy_set_header X-Namespace $namespace; # OK + proxy_set_header X-Ingress-Name $ingress_name; # OK + proxy_set_header X-Service-Name $service_name;# OK + proxy_set_header X-Service-Port $service_port; # OK + proxy_set_header X-Request-ID $req_id;# OK + proxy_set_header X-Forwarded-For $remote_addr; # OK + proxy_set_header Host $best_http_host; # OK + + set $proxy_upstream_name {{ $upstreamName | quote }}; # OK + + rewrite (.*) / break; # OK + + proxy_pass http://upstream_balancer; # OK {{ if $enableMetrics }} - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; # OK {{ end }} } {{ end }} @@ -807,29 +684,29 @@ stream { {{ if $cors.CorsAllowOrigin }} {{ buildCorsOriginRegex $cors.CorsAllowOrigin }} {{ end }} - if ($request_method = 'OPTIONS') { - set $cors ${cors}options; + if ($request_method = 'OPTIONS') { # OK + set $cors ${cors}options; # OK } - 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 }}'; + if ($cors = "true") { # OK + more_set_headers 'Access-Control-Allow-Origin: $http_origin'; # OK + {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }} # OK + more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}'; # OK + more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}'; # OK + {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }} # OK + more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}'; # OK } - 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 ($cors = "trueoptions") { # OK + more_set_headers 'Access-Control-Allow-Origin: $http_origin'; # OK + {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }} # OK + more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}'; # OK + more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}'; # OK + {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }} # OK + more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}'; # OK + more_set_headers 'Content-Type: text/plain charset=UTF-8'; # OK + more_set_headers 'Content-Length: 0'; # OK + return 204; # OK } {{ end }} @@ -838,321 +715,300 @@ stream { {{ $all := .First }} {{ $server := .Second }} - {{ buildHTTPListener $all $server.Hostname }} - {{ buildHTTPSListener $all $server.Hostname }} + {{ buildHTTPListener $all $server.Hostname }} # OK + {{ buildHTTPSListener $all $server.Hostname }} # OK - set $proxy_upstream_name "-"; + set $proxy_upstream_name "-"; # OK {{ if not ( empty $server.CertificateAuth.MatchCN ) }} {{ if gt (len $server.CertificateAuth.MatchCN) 0 }} - if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { - return 403 "client certificate unauthorized"; + if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { # OK + return 403 "client certificate unauthorized"; # OK } {{ end }} {{ end }} {{ if eq $server.Hostname "_" }} - ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; + ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; # OK {{ end }} - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; + ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; # OK {{ if not (empty $server.AuthTLSError) }} - # {{ $server.AuthTLSError }} + # {{ $server.AuthTLSError }} # NOT, WHERE THIS IF ENDS?? Couldn't reproduce this config on my ingress return 403; {{ else }} {{ if not (empty $server.CertificateAuth.CAFileName) }} # PEM sha: {{ $server.CertificateAuth.CASHA }} - ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; - ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; - ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; + ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; # OK + ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; # OK + ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; # OK {{ if not (empty $server.CertificateAuth.CRLFileName) }} # PEM sha: {{ $server.CertificateAuth.CRLSHA }} - ssl_crl {{ $server.CertificateAuth.CRLFileName }}; + ssl_crl {{ $server.CertificateAuth.CRLFileName }}; # OK {{ end }} {{ if not (empty $server.CertificateAuth.ErrorPage)}} - error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; + error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; # OK {{ end }} {{ end }} {{ if not (empty $server.ProxySSL.CAFileName) }} # PEM sha: {{ $server.ProxySSL.CASHA }} - proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; - proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; - proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; - proxy_ssl_verify {{ $server.ProxySSL.Verify }}; - proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; + proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; # OK + proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; # OK + proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; # OK + proxy_ssl_verify {{ $server.ProxySSL.Verify }}; # OK + proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; # OK {{ if not (empty $server.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; - proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; + proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; # OK + proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; # OK {{ end }} {{ end }} {{ if not (empty $server.ProxySSL.PemFileName) }} - proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; - proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; + proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; # OK + proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; # OK {{ end }} {{ if not (empty $server.SSLCiphers) }} - ssl_ciphers {{ $server.SSLCiphers }}; + ssl_ciphers {{ $server.SSLCiphers }}; # OK {{ end }} {{ if not (empty $server.SSLPreferServerCiphers) }} - ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; - {{ end }} - - {{ if not (empty $server.ServerSnippet) }} - # Custom code snippet configured for host {{ $server.Hostname }} - {{ $server.ServerSnippet }} + ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; # OK {{ end }} - {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics $all.Cfg.EnableModsecurity) }} + {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} # OK + {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics) }} # OK {{ end }} - {{ buildMirrorLocations $server.Locations }} + {{ buildMirrorLocations $server.Locations }} # OK - {{ $enforceRegex := enforceRegexModifier $server.Locations }} + {{ $enforceRegex := enforceRegexModifier $server.Locations }} # OK {{ range $location := $server.Locations }} - {{ $path := buildLocation $location $enforceRegex }} - {{ $proxySetHeader := proxySetHeader $location }} - {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} + {{ $path := buildLocation $location $enforceRegex }} # OK + {{ $proxySetHeader := proxySetHeader $location }} # OK + {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} # OK + {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} # OK + {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} # OK - {{ $externalAuth := $location.ExternalAuth }} + {{ $externalAuth := $location.ExternalAuth }} # OK {{ if eq $applyGlobalAuth true }} - {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} + {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} # OK {{ end }} {{ if not (empty $location.Rewrite.AppRoot) }} - if ($uri = /) { - return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; + if ($uri = /) { # OK + return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; # OK } {{ end }} {{ if $authPath }} location = {{ $authPath }} { - internal; + internal; # OK {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} - opentelemetry on; - opentelemetry_propagate; + opentelemetry on; # OK + opentelemetry_propagate; # OK {{ end }} {{ if not $all.Cfg.EnableAuthAccessLog }} - access_log off; - {{ end }} - - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; + access_log off; # OK {{ end }} {{ if $externalAuth.AuthCacheKey }} - set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; - set $cache_key ''; + set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; # OK + set $cache_key ''; # OK - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; # OK - proxy_cache auth_cache; + proxy_cache auth_cache; # OK {{- range $dur := $externalAuth.AuthCacheDuration }} - proxy_cache_valid {{ $dur }}; + proxy_cache_valid {{ $dur }}; # OK {{- end }} - proxy_cache_key "$cache_key"; + proxy_cache_key "$cache_key"; # OK {{ end }} # ngx_auth_request module overrides variables in the parent request, # therefore we have to explicitly set this variable again so that when the parent request # resumes it has the correct value set for this variable so that Lua can pick backend correctly - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; # OK - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - proxy_set_header X-Forwarded-Proto ""; - proxy_set_header X-Request-ID $req_id; + proxy_pass_request_body off; # OK + proxy_set_header Content-Length ""; # OK + proxy_set_header X-Forwarded-Proto ""; # OK + proxy_set_header X-Request-ID $req_id; # OK {{ if $externalAuth.Method }} - proxy_method {{ $externalAuth.Method }}; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Scheme $pass_access_scheme; + proxy_method {{ $externalAuth.Method }}; + proxy_set_header X-Original-URI $request_uri; # OK + proxy_set_header X-Scheme $pass_access_scheme; # OK {{ end }} - proxy_set_header Host {{ $externalAuth.Host }}; - proxy_set_header X-Original-URL $scheme://$http_host$request_uri; - proxy_set_header X-Original-Method $request_method; - proxy_set_header X-Sent-From "nginx-ingress-controller"; - proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host {{ $externalAuth.Host }}; # OK + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; # OK + proxy_set_header X-Original-Method $request_method; # OK + proxy_set_header X-Sent-From "nginx-ingress-controller"; # OK + proxy_set_header X-Real-IP $remote_addr; # OK {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - proxy_set_header X-Forwarded-For $full_x_forwarded_for; + proxy_set_header X-Forwarded-For $full_x_forwarded_for; # OK {{ else }} - proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; # OK {{ end }} {{ if $externalAuth.RequestRedirect }} - proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; + proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; # OK {{ else }} - proxy_set_header X-Auth-Request-Redirect $request_uri; + proxy_set_header X-Auth-Request-Redirect $request_uri; # OK {{ end }} {{ if $externalAuth.AuthCacheKey }} - proxy_buffering "on"; + proxy_buffering "on"; # OK {{ else }} - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; # OK {{ end }} - proxy_buffer_size {{ $location.Proxy.BufferSize }}; - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; + proxy_buffer_size {{ $location.Proxy.BufferSize }}; # OK + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; # OK + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; # OK - proxy_ssl_server_name on; - proxy_pass_request_headers on; + proxy_ssl_server_name on; # OK + proxy_pass_request_headers on; # OK {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; + client_max_body_size {{ $location.Proxy.BodySize }}; # OK {{ end }} {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; # OK {{ end }} # Pass the extracted client certificate to the auth provider {{ if not (empty $server.CertificateAuth.CAFileName) }} {{ if $server.CertificateAuth.PassCertToUpstream }} - proxy_set_header ssl-client-cert $ssl_client_escaped_cert; + proxy_set_header ssl-client-cert $ssl_client_escaped_cert; # OK {{ end }} - proxy_set_header ssl-client-verify $ssl_client_verify; - proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; - proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; + proxy_set_header ssl-client-verify $ssl_client_verify; # OK + proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; # OK + proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; # OK {{ end }} {{- range $line := buildAuthProxySetHeaders $externalAuth.ProxySetHeaders}} - {{ $line }} + {{ $line }} # OK {{- end }} - {{ if not (empty $externalAuth.AuthSnippet) }} - {{ $externalAuth.AuthSnippet }} - {{ end }} - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} # The target is an upstream with HTTP keepalive, that is why the # Connection header is cleared and the HTTP version is set to 1.1 as # the Nginx documentation suggests: # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive - proxy_http_version 1.1; - proxy_set_header Connection ""; - set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; + proxy_http_version 1.1; # OK + proxy_set_header Connection ""; # OK + set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; # OK {{ else }} - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; - set $target {{ $externalAuth.URL }}; + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; # OK + set $target {{ $externalAuth.URL }}; # OK {{ end }} - proxy_pass $target; + proxy_pass $target; # OK } {{ end }} {{ if isLocationAllowed $location }} {{ if $externalAuth.SigninURL }} location {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }} { - internal; + internal; # OK - add_header Set-Cookie $auth_cookie; + add_header Set-Cookie $auth_cookie; # OK {{ if $location.CorsConfig.CorsEnabled }} - {{ template "CORS" $location }} - {{ end }} - - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; + {{ template "CORS" $location }} # OK {{ end }} - return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; + return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; # OK } {{ end }} {{ end }} location {{ $path }} { {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.IngressPath) }} - set $namespace {{ $ing.Namespace | quote}}; - set $ingress_name {{ $ing.Rule | quote }}; - set $service_name {{ $ing.Service | quote }}; - set $service_port {{ $ing.ServicePort | quote }}; - set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; + set $namespace {{ $ing.Namespace | quote}}; # OK + set $ingress_name {{ $ing.Rule | quote }}; # OK + set $service_name {{ $ing.Service | quote }}; # OK + set $service_port {{ $ing.ServicePort | quote }}; # OK + set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; # OK - {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} + {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} # OK {{ if $location.Mirror.Source }} - mirror {{ $location.Mirror.Source }}; - mirror_request_body {{ $location.Mirror.RequestBody }}; + mirror {{ $location.Mirror.Source }}; # OK + mirror_request_body {{ $location.Mirror.RequestBody }}; # OK {{ end }} - {{ locationConfigForLua $location $all }} + {{ locationConfigForLua $location $all }} # OK - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; + rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; # OK - header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; + header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; # OK - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; + log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; # OK {{ if not $location.Logs.Access }} - access_log off; + access_log off; # OK {{ end }} {{ if $location.Logs.Rewrite }} - rewrite_log on; + rewrite_log on; # OK {{ end }} {{ if $location.HTTP2PushPreload }} - http2_push_preload on; + http2_push_preload on; # OK {{ end }} - port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; + port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; # OK - set $balancer_ewma_score -1; - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; - set $proxy_host $proxy_upstream_name; - set $pass_access_scheme $scheme; + set $balancer_ewma_score -1; # OK + set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; # OK + set $proxy_host $proxy_upstream_name; # OK + set $pass_access_scheme $scheme; # OK {{ if $all.Cfg.UseProxyProtocol }} - set $pass_server_port $proxy_protocol_server_port; + set $pass_server_port $proxy_protocol_server_port; # OK {{ else }} - set $pass_server_port $server_port; + set $pass_server_port $server_port; # OK {{ end }} - set $best_http_host $http_host; - set $pass_port $pass_server_port; + set $best_http_host $http_host; # OK + set $pass_port $pass_server_port; # OK - set $proxy_alternative_upstream_name ""; + set $proxy_alternative_upstream_name ""; # OK - {{ buildModSecurityForLocation $all.Cfg $location }} + {{ if isLocationAllowed $location }} # 1 + {{ if gt (len $location.Denylist.CIDR) 0 }} # 2 + {{ range $ip := $location.Denylist.CIDR }} # 3 + deny {{ $ip }};{{ end }} # 2 # OK + {{ end }} # 1 + {{ if gt (len $location.Allowlist.CIDR) 0 }} # 2 + {{ range $ip := $location.Allowlist.CIDR }} # 3 + allow {{ $ip }};{{ end }} # 2 # OK + deny all; # OK + {{ end }} # 1 - {{ if isLocationAllowed $location }} - {{ if gt (len $location.Denylist.CIDR) 0 }} - {{ range $ip := $location.Denylist.CIDR }} - deny {{ $ip }};{{ end }} - {{ end }} - {{ if gt (len $location.Allowlist.CIDR) 0 }} - {{ range $ip := $location.Allowlist.CIDR }} - allow {{ $ip }};{{ end }} - deny all; - {{ end }} + {{ if $location.CorsConfig.CorsEnabled }} # 2 + {{ template "CORS" $location }} # OK + {{ end }} # 1 - {{ if $location.CorsConfig.CorsEnabled }} - {{ template "CORS" $location }} - {{ end }} - - {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} - {{ if $authPath }} + {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} # 2 + {{ if $authPath }} # 3 # this location requires authentication - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} + {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} # 4 set $auth_cookie ''; add_header Set-Cookie $auth_cookie; - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} # 5 {{ $line }} - {{- end }} + {{- end }} # 4 # `auth_request` module does not support HTTP keepalives in upstream block: # https://trac.nginx.org/nginx/ticket/1579 access_by_lua_block { @@ -1172,224 +1028,217 @@ stream { {{ else }} auth_request {{ $authPath }}; auth_request_set $auth_cookie $upstream_http_set_cookie; - {{ if $externalAuth.AlwaysSetCookie }} + {{ if $externalAuth.AlwaysSetCookie }} # 5 add_header Set-Cookie $auth_cookie always; {{ else }} add_header Set-Cookie $auth_cookie; - {{ end }} - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} + {{ end }} # 4 + {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} # 5 {{ $line }} - {{- end }} - {{ end }} - {{ end }} + {{- end }} # 4 + {{ end }} # 3 + {{ end }} # 2 - {{ if $externalAuth.SigninURL }} + {{ if $externalAuth.SigninURL }} # 3 set_escape_uri $escaped_request_uri $request_uri; error_page 401 = {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }}; - {{ end }} + {{ end }} # 2 - {{ if $location.BasicDigestAuth.Secured }} - {{ if eq $location.BasicDigestAuth.Type "basic" }} + {{ if $location.BasicDigestAuth.Secured }} # 3 + {{ if eq $location.BasicDigestAuth.Type "basic" }} # 4 auth_basic {{ $location.BasicDigestAuth.Realm | quote }}; auth_basic_user_file {{ $location.BasicDigestAuth.File }}; {{ else }} auth_digest {{ $location.BasicDigestAuth.Realm | quote }}; auth_digest_user_file {{ $location.BasicDigestAuth.File }}; - {{ end }} + {{ end }} # 3 {{ $proxySetHeader }} Authorization ""; - {{ end }} - {{ end }} + {{ end }} # 2 + {{ end }} # 1 {{/* if the location contains a rate limit annotation, create one */}} {{ $limits := buildRateLimit $location }} - {{ range $limit := $limits }} - {{ $limit }}{{ end }} + {{ range $limit := $limits }} # 2 # OK + {{ $limit }}{{ end }} # 1 {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; + client_max_body_size {{ $location.Proxy.BodySize }}; # OK {{ end }} {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; - {{ end }} + client_body_buffer_size {{ $location.ClientBodyBufferSize }}; # OK + {{ end }} # 1 {{/* By default use vhost as Host to upstream, but allow overrides */}} {{ if not (empty $location.UpstreamVhost) }} - {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; + {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; # OK {{ else }} - {{ $proxySetHeader }} Host $best_http_host; - {{ end }} + {{ $proxySetHeader }} Host $best_http_host; # OK + {{ end }} # 1 # Pass the extracted client certificate to the backend - {{ if not (empty $server.CertificateAuth.CAFileName) }} - {{ if $server.CertificateAuth.PassCertToUpstream }} - {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; - {{ end }} - {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; - {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; - {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; - {{ end }} + {{ if not (empty $server.CertificateAuth.CAFileName) }} # 2 + {{ if $server.CertificateAuth.PassCertToUpstream }} # 3 + {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; # OK + {{ end }} # 2 + {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; # OK + {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; # OK + {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; # OK + {{ end }} # 1 # Allow websocket connections - {{ $proxySetHeader }} Upgrade $http_upgrade; + {{ $proxySetHeader }} Upgrade $http_upgrade; # OK {{ if $location.Connection.Enabled}} - {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; + {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; # OK {{ else }} - {{ $proxySetHeader }} Connection $connection_upgrade; + {{ $proxySetHeader }} Connection $connection_upgrade; # OK {{ end }} - {{ $proxySetHeader }} X-Request-ID $req_id; - {{ $proxySetHeader }} X-Real-IP $remote_addr; + {{ $proxySetHeader }} X-Request-ID $req_id; # OK + {{ $proxySetHeader }} X-Real-IP $remote_addr; # OK {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; + {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; # OK {{ else }} - {{ $proxySetHeader }} X-Forwarded-For $remote_addr; - {{ end }} - {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; - {{ $proxySetHeader }} X-Forwarded-Port $pass_port; - {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; - {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; + {{ $proxySetHeader }} X-Forwarded-For $remote_addr; # OK + {{ end }} # 1 + {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; # OK + {{ $proxySetHeader }} X-Forwarded-Port $pass_port; # OK + {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; # OK + {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; # OK {{ if $all.Cfg.ProxyAddOriginalURIHeader }} - {{ $proxySetHeader }} X-Original-URI $request_uri; - {{ end }} - {{ $proxySetHeader }} X-Scheme $pass_access_scheme; + {{ $proxySetHeader }} X-Original-URI $request_uri; # OK + {{ end }} # 1 + {{ $proxySetHeader }} X-Scheme $pass_access_scheme; # OK # Pass the original X-Forwarded-For - {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; + {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; # OK # mitigate HTTPoxy Vulnerability # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ - {{ $proxySetHeader }} Proxy ""; + {{ $proxySetHeader }} Proxy ""; # OK # Custom headers to proxied server {{ range $k, $v := $all.ProxySetHeaders }} - {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; - {{ end }} + {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; # OK + {{ end }} # 1 - proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; - proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; - proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; + proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; OK + proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; # OK + proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; # OK - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; - proxy_buffer_size {{ $location.Proxy.BufferSize }}; - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; + proxy_buffering {{ $location.Proxy.ProxyBuffering }}; # OK + proxy_buffer_size {{ $location.Proxy.BufferSize }}; # OK + proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; # OK {{ if isValidByteSize $location.Proxy.ProxyMaxTempFileSize true }} - proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; + proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; # OK {{ end }} - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; + proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; # OK + proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; # OK - proxy_cookie_domain {{ $location.Proxy.CookieDomain }}; - proxy_cookie_path {{ $location.Proxy.CookiePath }}; + proxy_cookie_domain {{ $location.Proxy.CookieDomain }}; # OK + proxy_cookie_path {{ $location.Proxy.CookiePath }}; # OK # In case of errors try the next upstream server before returning an error - proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }}; - proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; + proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }}; # OK + proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; # OK + proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; # OK {{ if or (eq $location.BackendProtocol "GRPC") (eq $location.BackendProtocol "GRPCS") }} # Grpc settings - grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; - grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; - grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; - {{ end }} + grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; # OK + grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; # OK + grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; # OK + {{ end }} # 1 - {{/* Add any additional configuration defined */}} - {{ $location.ConfigurationSnippet }} - - {{ if not (empty $all.Cfg.LocationSnippet) }} - # Custom code snippet configured in the configuration configmap - {{ $all.Cfg.LocationSnippet }} - {{ end }} - - {{ if $location.CustomHeaders }} + + {{ if $location.CustomHeaders }} # 2 # Custom Response Headers - {{ range $k, $v := $location.CustomHeaders.Headers }} - more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; - {{ end }} - {{ end }} + {{ range $k, $v := $location.CustomHeaders.Headers }} # 3 + more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; # OK + {{ end }} # 2 + {{ end }} # 1 {{/* if we are sending the request to a custom default backend, we add the required headers */}} {{ if (hasPrefix $location.Backend "custom-default-backend-") }} - proxy_set_header X-Code 503; - proxy_set_header X-Format $http_accept; - proxy_set_header X-Namespace $namespace; - proxy_set_header X-Ingress-Name $ingress_name; - proxy_set_header X-Service-Name $service_name; - proxy_set_header X-Service-Port $service_port; - proxy_set_header X-Request-ID $req_id; - {{ end }} + proxy_set_header X-Code 503; # OK + proxy_set_header X-Format $http_accept; # OK + proxy_set_header X-Namespace $namespace; # OK + proxy_set_header X-Ingress-Name $ingress_name; # OK + proxy_set_header X-Service-Name $service_name; # OK + proxy_set_header X-Service-Port $service_port; # OK + proxy_set_header X-Request-ID $req_id; # OK + {{ end }} # 1 {{ if $location.Satisfy }} - satisfy {{ $location.Satisfy }}; - {{ end }} + satisfy {{ $location.Satisfy }}; # OK + {{ end }} # 1 {{/* if a location-specific error override is set, add the proxy_intercept here */}} {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} # Custom error pages per ingress - proxy_intercept_errors on; - {{ end }} + proxy_intercept_errors on; # OK + {{ end }} # 1 {{ range $errCode := $location.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} + error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} # OK {{ if (eq $location.BackendProtocol "FCGI") }} - include /etc/nginx/fastcgi_params; - {{ end }} + include /etc/nginx/fastcgi_params; OK + {{ end }} # 1 {{- if $location.FastCGI.Index -}} - fastcgi_index {{ $location.FastCGI.Index | quote }}; - {{- end -}} + fastcgi_index {{ $location.FastCGI.Index | quote }}; # OK + {{- end -}} # 1 {{ range $k, $v := $location.FastCGI.Params }} - fastcgi_param {{ $k }} {{ $v | quote }}; - {{ end }} + fastcgi_param {{ $k }} {{ $v | quote }}; # OK + {{ end }} # 1 {{ if not (empty $location.Redirect.URL) }} - return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; - {{ end }} + return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; # OK + {{ end }} # 1 - {{ buildProxyPass $server.Hostname $all.Backends $location }} + {{ buildProxyPass $server.Hostname $all.Backends $location }} # OK {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; - {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; - {{ end }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; # OK + {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} + proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; # OK + {{ end }} # 1 {{ else }} - # Location denied. Reason: {{ $location.Denied | quote }} - return 503; - {{ end }} - {{ if not (empty $location.ProxySSL.CAFileName) }} - # PEM sha: {{ $location.ProxySSL.CASHA }} - proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; - proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; - proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; - proxy_ssl_verify {{ $location.ProxySSL.Verify }}; - proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; - {{ end }} - - {{ if not (empty $location.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; - {{ end }} - {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} - proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; - {{ end }} - - {{ if not (empty $location.ProxySSL.PemFileName) }} - proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; - proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; - {{ end }} + # Location denied. Reason: {{ $location.Denied | quote }} # OK + return 503; # OK + {{ end }} # 0 + {{ if not (empty $location.ProxySSL.CAFileName) }} # 1 + # PEM sha: {{ $location.ProxySSL.CASHA }} # OK + proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; # OK + proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; # OK + proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; # OK + proxy_ssl_verify {{ $location.ProxySSL.Verify }}; # OK + proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; # OK + {{ end }} # 0 + + {{ if not (empty $location.ProxySSL.ProxySSLName) }} # 1 + proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; # OK + {{ end }} # 0 + {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} # 1 + proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; # OK + {{ end }} # 0 + + {{ if not (empty $location.ProxySSL.PemFileName) }} # 1 + proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; # OK + proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; # OK + {{ end }} # 0 } - {{ end }} - {{ end }} + {{ end }} # End range + {{ end }} # Maybe that missing if... {{ if eq $server.Hostname "_" }} # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} - location {{ $all.HealthzURI }} { + location {{ $all.HealthzURI }} { # OK {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; + opentelemetry off; # OK {{ end }} - access_log off; - return 200; + access_log off; # OK + return 200; # OK } # this is required to avoid error if nginx is being monitored @@ -1397,21 +1246,21 @@ stream { location /nginx_status { {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; + opentelemetry off; # OK {{ end }} {{ range $v := $all.NginxStatusIpv4Whitelist }} - allow {{ $v }}; + allow {{ $v }}; # OK {{ end }} {{ if $all.IsIPV6Enabled -}} {{ range $v := $all.NginxStatusIpv6Whitelist }} - allow {{ $v }}; + allow {{ $v }}; # OK {{ end }} {{ end -}} - deny all; + deny all; # OK - access_log off; - stub_status on; + access_log off; # OK + stub_status on; # OK } {{ end }} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index 2b1b3a2dfc..ea0ade3c9b 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -17,18 +17,72 @@ limitations under the License. package crossplane import ( + "crypto/sha1" + "encoding/base64" + "encoding/hex" "fmt" "net" + "net/url" + "regexp" "strconv" + "strings" ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit" "k8s.io/ingress-nginx/internal/ingress/controller/config" ing_net "k8s.io/ingress-nginx/internal/net" "k8s.io/ingress-nginx/pkg/apis/ingress" ) +const ( + slash = "/" + nonIdempotent = "non_idempotent" + defBufferSize = 65535 + writeIndentOnEmptyLines = true // backward-compatibility + httpProtocol = "HTTP" + autoHTTPProtocol = "AUTO_HTTP" + httpsProtocol = "HTTPS" + grpcProtocol = "GRPC" + grpcsProtocol = "GRPCS" + fcgiProtocol = "FCGI" +) + +var ( + nginxSizeRegex = regexp.MustCompile(`^\d+[kKmM]?$`) + nginxOffsetRegex = regexp.MustCompile(`^\d+[kKmMgG]?$`) + defaultGlobalAuthRedirectParam = "rd" +) + type seconds int +type minutes int + +func buildDirectiveWithComment(directive string, comment string, args ...any) *ngx_crossplane.Directive { + dir := buildDirective(directive, args...) + dir.Comment = ptr.To(comment) + return dir +} + +func buildStartServer(name string) *ngx_crossplane.Directive { + return buildDirective("##", "start", "server", name) +} + +func buildEndServer(name string) *ngx_crossplane.Directive { + return buildDirective("##", "end", "server", name) +} + +func buildStartAuthUpstream(name, location string) *ngx_crossplane.Directive { + return buildDirective("##", "start", "auth", "upstream", name, location) +} + +func buildEndAuthUpstream(name, location string) *ngx_crossplane.Directive { + return buildDirective("##", "end", "auth", "upstream", name, location) +} func buildDirective(directive string, args ...any) *ngx_crossplane.Directive { argsVal := make([]string, 0) @@ -36,6 +90,10 @@ func buildDirective(directive string, args ...any) *ngx_crossplane.Directive { switch v := args[k].(type) { case string: argsVal = append(argsVal, v) + case *string: + if v != nil { + argsVal = append(argsVal, *v) + } case []string: argsVal = append(argsVal, v...) case int: @@ -44,6 +102,8 @@ func buildDirective(directive string, args ...any) *ngx_crossplane.Directive { argsVal = append(argsVal, boolToStr(v)) case seconds: argsVal = append(argsVal, strconv.Itoa(int(v))+"s") + case minutes: + argsVal = append(argsVal, strconv.Itoa(int(v))+"m") } } return &ngx_crossplane.Directive{ @@ -141,3 +201,538 @@ func shouldLoadOpentelemetryModule(servers []*ingress.Server) bool { } return false } + +func buildServerName(hostname string) string { + if !strings.HasPrefix(hostname, "*") { + return hostname + } + + hostname = strings.Replace(hostname, "*.", "", 1) + parts := strings.Split(hostname, ".") + + return `~^(?[\w-]+)\.` + strings.Join(parts, "\\.") + `$` +} + +func buildListener(tc config.TemplateConfig, hostname string) ngx_crossplane.Directives { + listenDirectives := make(ngx_crossplane.Directives, 0) + + co := commonListenOptions(&tc, hostname) + + addrV4 := []string{""} + if len(tc.Cfg.BindAddressIpv4) > 0 { + addrV4 = tc.Cfg.BindAddressIpv4 + } + listenDirectives = append(listenDirectives, httpListener(addrV4, co, &tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV4, co, &tc, true)...) + + if tc.IsIPV6Enabled { + addrV6 := []string{"[::]"} + if len(tc.Cfg.BindAddressIpv6) > 0 { + addrV6 = tc.Cfg.BindAddressIpv6 + } + listenDirectives = append(listenDirectives, httpListener(addrV6, co, &tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV6, co, &tc, true)...) + } + + return listenDirectives +} + +// commonListenOptions defines the common directives that should be added to NGINX listeners +func commonListenOptions(template *config.TemplateConfig, hostname string) []string { + var out []string + + if template.Cfg.UseProxyProtocol { + out = append(out, "proxy_protocol") + } + + if hostname != "_" { + return out + } + + out = append(out, "default_server") + + if template.Cfg.ReusePort { + out = append(out, "reuseport") + } + out = append(out, fmt.Sprintf("backlog=%d", template.BacklogSize)) + return out +} + +func httpListener(addresses []string, co []string, tc *config.TemplateConfig, ssl bool) ngx_crossplane.Directives { + listeners := make(ngx_crossplane.Directives, 0) + port := tc.ListenPorts.HTTP + isTLSProxy := tc.IsSSLPassthroughEnabled + // If this is a SSL listener we should mutate the port properly + if ssl { + port = tc.ListenPorts.HTTPS + if isTLSProxy { + port = tc.ListenPorts.SSLProxy + } + } + for _, address := range addresses { + var listenAddress string + if address == "" { + listenAddress = fmt.Sprintf("%d", port) + } else { + listenAddress = fmt.Sprintf("%s:%d", address, port) + } + if ssl { + if isTLSProxy { + co = append(co, "proxy_protocol") + } + co = append(co, "ssl") + } + listenDirective := buildDirective("listen", listenAddress, co) + listeners = append(listeners, listenDirective) + } + + return listeners +} + +func luaConfigurationRequestBodySize(cfg config.Configuration) string { + size := cfg.LuaSharedDicts["configuration_data"] + if size < cfg.LuaSharedDicts["certificate_data"] { + size = cfg.LuaSharedDicts["certificate_data"] + } + size += 1024 + + return dictKbToStr(size) +} + +func buildLocation(location *ingress.Location, enforceRegex bool) []string { + path := location.Path + if enforceRegex { + return []string{"~*", fmt.Sprintf("^%s", path)} + } + + if location.PathType != nil && *location.PathType == networkingv1.PathTypeExact { + return []string{"=", path} + } + + return []string{path} +} + +func getProxySetHeader(location *ingress.Location) string { + if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol { + return "grpc_set_header" + } + + return "proxy_set_header" +} + +func buildAuthLocation(location *ingress.Location, globalExternalAuthURL string) string { + if (location.ExternalAuth.URL == "") && (!shouldApplyGlobalAuth(location, globalExternalAuthURL)) { + return "" + } + + str := base64.URLEncoding.EncodeToString([]byte(location.Path)) + // removes "=" after encoding + str = strings.ReplaceAll(str, "=", "") + + pathType := "default" + if location.PathType != nil { + pathType = string(*location.PathType) + } + + return fmt.Sprintf("/_external-auth-%v-%v", str, pathType) +} + +// shouldApplyGlobalAuth returns true only in case when ExternalAuth.URL is not set and +// GlobalExternalAuth is set and enabled +func shouldApplyGlobalAuth(location *ingress.Location, globalExternalAuthURL string) bool { + return location.ExternalAuth.URL == "" && + globalExternalAuthURL != "" && + location.EnableGlobalAuth +} + +// shouldApplyAuthUpstream returns true only in case when ExternalAuth.URL and +// ExternalAuth.KeepaliveConnections are all set +func shouldApplyAuthUpstream(location *ingress.Location, cfg config.Configuration) bool { + if location.ExternalAuth.URL == "" || location.ExternalAuth.KeepaliveConnections == 0 { + return false + } + + // Unfortunately, `auth_request` module ignores keepalive in upstream block: https://trac.nginx.org/nginx/ticket/1579 + // The workaround is to use `ngx.location.capture` Lua subrequests but it is not supported with HTTP/2 + if cfg.UseHTTP2 { + return false + } + return true +} + +func isValidByteSize(s string, isOffset bool) bool { + s = strings.TrimSpace(s) + if s == "" { + return false + } + + if isOffset { + return nginxOffsetRegex.MatchString(s) + } + + return nginxSizeRegex.MatchString(s) +} + +func buildAuthUpstreamName(input *ingress.Location, host string) string { + authPath := buildAuthLocation(input, "") + if authPath == "" || host == "" { + return "" + } + + return fmt.Sprintf("%s-%s", host, authPath[2:]) +} + +// changeHostPort will change the host:port part of the url to value +func changeHostPort(newURL, value string) string { + if newURL == "" { + return "" + } + + authURL, err := parser.StringToURL(newURL) + if err != nil { + klog.Errorf("expected a valid URL but %s was returned", newURL) + return "" + } + + authURL.Host = value + + return authURL.String() +} + +func buildAuthSignURLLocation(location, authSignURL string) string { + hasher := sha1.New() // #nosec + hasher.Write([]byte(location)) + hasher.Write([]byte(authSignURL)) + return "@" + hex.EncodeToString(hasher.Sum(nil)) +} + +func buildAuthSignURL(authSignURL, authRedirectParam string) string { + u, err := url.Parse(authSignURL) + if err != nil { + klog.Errorf("error parsing authSignURL: %v", err) + return "" + } + q := u.Query() + if authRedirectParam == "" { + authRedirectParam = defaultGlobalAuthRedirectParam + } + if len(q) == 0 { + return fmt.Sprintf("%v?%v=$pass_access_scheme://$http_host$escaped_request_uri", authSignURL, authRedirectParam) + } + + if q.Get(authRedirectParam) != "" { + return authSignURL + } + + return fmt.Sprintf("%v&%v=$pass_access_scheme://$http_host$escaped_request_uri", authSignURL, authRedirectParam) +} + +func buildCorsOriginRegex(corsOrigins []string) ngx_crossplane.Directives { + if len(corsOrigins) == 1 && corsOrigins[0] == "*" { + return ngx_crossplane.Directives{ + buildDirective("set", "$http_origin", "*"), + buildDirective("set", "$cors", "true"), + } + } + + originsArray := []string{"("} + for i, origin := range corsOrigins { + originTrimmed := strings.TrimSpace(origin) + if originTrimmed != "" { + originsArray = append(originsArray, buildOriginRegex(originTrimmed)) + } + if i != len(corsOrigins)-1 { + originsArray = append(originsArray, "|") + } + } + originsArray = append(originsArray, ")$") + + // originsArray should be converted to a single string, as it is a single directive for if. + origins := strings.Join(originsArray, "") + return ngx_crossplane.Directives{ + buildBlockDirective("if", []string{"$http_origin", "~*", origins}, ngx_crossplane.Directives{ + buildDirective("set", "$cors", "true"), + }), + } +} + +func buildOriginRegex(origin string) string { + origin = regexp.QuoteMeta(origin) + origin = strings.Replace(origin, "\\*", `[A-Za-z0-9\-]+`, 1) + return fmt.Sprintf("(%s)", origin) +} + +func buildNextUpstream(nextUpstream string, retryNonIdempotent bool) []string { + parts := strings.Split(nextUpstream, " ") + + nextUpstreamCodes := make([]string, 0, len(parts)) + for _, v := range parts { + if v != "" && v != nonIdempotent { + nextUpstreamCodes = append(nextUpstreamCodes, v) + } + + if v == nonIdempotent { + retryNonIdempotent = true + } + } + + if retryNonIdempotent { + nextUpstreamCodes = append(nextUpstreamCodes, nonIdempotent) + } + + return nextUpstreamCodes +} + +func buildProxyPass(backends []*ingress.Backend, location *ingress.Location) ngx_crossplane.Directives { + path := location.Path + proto := "http://" + proxyPass := "proxy_pass" + + switch strings.ToUpper(location.BackendProtocol) { + case autoHTTPProtocol: + proto = "$scheme://" + case httpsProtocol: + proto = "https://" + case grpcProtocol: + proto = "grpc://" + proxyPass = "grpc_pass" + case grpcsProtocol: + proto = "grpcs://" + proxyPass = "grpc_pass" + case fcgiProtocol: + proto = "" + proxyPass = "fastcgi_pass" + } + + upstreamName := "upstream_balancer" + + for _, backend := range backends { + if backend.Name == location.Backend { + if backend.SSLPassthrough { + proto = "https://" + + if location.BackendProtocol == grpcsProtocol { + proto = "grpcs://" + } + } + + break + } + } + + if location.Backend == "upstream-default-backend" { + proto = "http://" + proxyPass = "proxy_pass" + } + + // defProxyPass returns the default proxy_pass, just the name of the upstream + defProxyPass := buildDirective(proxyPass, fmt.Sprintf("%s%s", proto, upstreamName)) + + // if the path in the ingress rule is equals to the target: no special rewrite + if path == location.Rewrite.Target { + return ngx_crossplane.Directives{defProxyPass} + } + + if location.Rewrite.Target != "" { + proxySetHeader := "proxy_set_header" + dir := make(ngx_crossplane.Directives, 0) + if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol { + proxySetHeader = "grpc_set_header" + } + + if location.XForwardedPrefix != "" { + dir = append(dir, + buildDirective(proxySetHeader, "X-Forwarded-Prefix", location.XForwardedPrefix), + ) + } + + dir = append(dir, + buildDirective("rewrite", fmt.Sprintf("(?i)%s", path), location.Rewrite.Target, "break"), + buildDirective(proxyPass, fmt.Sprintf("%s%s", proto, upstreamName)), + ) + return dir + } + + // default proxy_pass + return ngx_crossplane.Directives{defProxyPass} +} + +func buildGeoIPDirectives(reloadTime int, files []string) ngx_crossplane.Directives { + + directives := make(ngx_crossplane.Directives, 0) + buildGeoIPBlock := func(file string, directives ngx_crossplane.Directives) *ngx_crossplane.Directive { + if reloadTime > 0 && file != "GeoIP2-Connection-Type.mmdb" { + directives = append(directives, buildDirective("auto_reload", minutes(reloadTime))) + } + fileName := fmt.Sprintf("/etc/ingress-controller/geoip/%s", file) + return buildBlockDirective("geoip2", []string{fileName}, directives) + } + + for _, file := range files { + if file == "GeoLite2-Country.mmdb" || file == "GeoIP2-Country.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_country_code", "source=$remote_addr", "country", "iso_code"), + buildDirective("$geoip2_country_name", "source=$remote_addr", "country", "names", "en"), + buildDirective("$geoip2_country_geoname_id", "source=$remote_addr", "country", "geoname_id"), + buildDirective("$geoip2_continent_code", "source=$remote_addr", "continent", "code"), + buildDirective("$geoip2_continent_name", "source=$remote_addr", "continent", "names", "en"), + buildDirective("$geoip2_continent_geoname_id", "source=$remote_addr", "continent", "geoname_id"), + })) + } + if file == "GeoLite2-City.mmdb" || file == "GeoIP2-City.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_city_country_code", "source=$remote_addr", "country", "iso_code"), + buildDirective("$geoip2_city_country_name", "source=$remote_addr", "country", "names", "en"), + buildDirective("$geoip2_city_country_geoname_id", "source=$remote_addr", "country", "geoname_id"), + buildDirective("$geoip2_city_continent_code", "source=$remote_addr", "continent", "code"), + buildDirective("$geoip2_city_continent_name", "source=$remote_addr", "continent", "names", "en"), + buildDirective("$geoip2_city", "source=$remote_addr", "city", "names", "en"), + buildDirective("$geoip2_city_geoname_id", "source=$remote_addr", "city", "geoname_id"), + buildDirective("$geoip2_postal_code", "source=$remote_addr", "postal", "code"), + buildDirective("$geoip2_dma_code", "source=$remote_addr", "location", "metro_code"), + buildDirective("$geoip2_latitude", "source=$remote_addr", "location", "latitude"), + buildDirective("$geoip2_longitude", "source=$remote_addr", "location", "longitude"), + buildDirective("$geoip2_time_zone", "source=$remote_addr", "location", "time_zone"), + buildDirective("$geoip2_region_code", "source=$remote_addr", "subdivisions", "0", "iso_code"), + buildDirective("$geoip2_region_name", "source=$remote_addr", "subdivisions", "0", "names", "en"), + buildDirective("$geoip2_region_geoname_id", "source=$remote_addr", "subdivisions", "0", "geoname_id"), + buildDirective("$geoip2_subregion_code", "source=$remote_addr", "subdivisions", "1", "iso_code"), + buildDirective("$geoip2_subregion_name", "source=$remote_addr", "subdivisions", "1", "names", "en"), + buildDirective("$geoip2_subregion_geoname_id", "source=$remote_addr", "subdivisions", "1", "geoname_id"), + })) + } + if file == "GeoLite2-ASN.mmdb" || file == "GeoIP2-ASN.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_asn", "source=$remote_addr", "autonomous_system_number"), + buildDirective("$geoip2_org", "source=$remote_addr", "autonomous_system_organization"), + })) + } + if file == "GeoIP2-ISP.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_isp", "source=$remote_addr", "isp"), + buildDirective("$geoip2_isp_org", "source=$remote_addr", "organization"), + buildDirective("$geoip2_asn", "source=$remote_addr", "autonomous_system_number"), + })) + } + if file == "GeoIP2-Anonymous-IP.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_is_anon", "source=$remote_addr", "is_anonymous"), + buildDirective("$geoip2_is_anonymous", "source=$remote_addr", "default=0", "is_anonymous"), + buildDirective("$geoip2_is_anonymous_vpn", "source=$remote_addr", "default=0", "is_anonymous_vpn"), + buildDirective("$geoip2_is_hosting_provider", "source=$remote_addr", "default=0", "is_hosting_provider"), + buildDirective("$geoip2_is_public_proxy", "source=$remote_addr", "default=0", "is_public_proxy"), + buildDirective("$geoip2_is_tor_exit_node", "source=$remote_addr", "default=0", "is_tor_exit_node"), + })) + } + if file == "GeoIP2-Connection-Type.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_connection_type", "connection_type"), + })) + } + } + return directives +} + +func filterRateLimits(servers []*ingress.Server) []ratelimit.Config { + ratelimits := []ratelimit.Config{} + found := sets.Set[string]{} + + for _, server := range servers { + for _, loc := range server.Locations { + if loc.RateLimit.ID != "" && !found.Has(loc.RateLimit.ID) { + found.Insert(loc.RateLimit.ID) + ratelimits = append(ratelimits, loc.RateLimit) + } + } + } + return ratelimits +} + +// buildRateLimitZones produces an array of limit_conn_zone in order to allow +// rate limiting of request. Each Ingress rule could have up to three zones, one +// for connection limit by IP address, one for limiting requests per minute, and +// one for limiting requests per second. +func buildRateLimitZones(servers []*ingress.Server) ngx_crossplane.Directives { + zones := make(map[string]bool) + directives := make(ngx_crossplane.Directives, 0) + for _, server := range servers { + for _, loc := range server.Locations { + zoneID := fmt.Sprintf("$limit_%s", loc.RateLimit.ID) + if loc.RateLimit.Connections.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.Connections.Name, loc.RateLimit.Connections.SharedSize) + zone := fmt.Sprintf("limit_conn_zone %s %s", zoneID, zoneArg) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_conn_zone", zoneID, zoneArg)) + } + } + + if loc.RateLimit.RPM.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.RPM.Name, loc.RateLimit.RPM.SharedSize) + zoneRate := fmt.Sprintf("rate=%dr/m", loc.RateLimit.RPM.Limit) + zone := fmt.Sprintf("limit_req_zone %s %s %s", zoneID, zoneArg, zoneRate) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_req_zone", zoneID, zoneArg, zoneRate)) + } + } + + if loc.RateLimit.RPS.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.RPS.Name, loc.RateLimit.RPS.SharedSize) + zoneRate := fmt.Sprintf("rate=%dr/s", loc.RateLimit.RPS.Limit) + zone := fmt.Sprintf("limit_req_zone %s %s %s", zoneID, zoneArg, zoneRate) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_req_zone", zoneID, zoneArg, zoneRate)) + } + } + } + } + return directives +} + +// buildAuthResponseHeaders sets HTTP response headers when `auth-url` is used. +// Based on `auth-keepalive` value we use auth_request_set Nginx directives, or +// we use Lua and Nginx variables instead. +// +// NOTE: Unfortunately auth_request module ignores the keepalive directive (see: +// https://trac.nginx.org/nginx/ticket/1579), that is why we mimic the same +// functionality with access_by_lua_block. +// TODO: This function is duplicated with the non-crossplane and we should consolidate +func buildAuthResponseHeaders(proxySetHeader string, headers []string, lua bool) ngx_crossplane.Directives { + res := make(ngx_crossplane.Directives, 0) + + if len(headers) == 0 { + return res + } + + for i, h := range headers { + authHeader := fmt.Sprintf("$authHeader%d", i) + if lua { + res = append(res, buildDirective("set", authHeader, "")) + } else { + hvar := strings.ToLower(h) + hvar = strings.NewReplacer("-", "_").Replace(hvar) + res = append(res, buildDirective("auth_request_set", + authHeader, + fmt.Sprintf("$upstream_http_%s", hvar))) + } + res = append(res, buildDirective(proxySetHeader, h, authHeader)) + } + return res +} + +// extractHostPort will extract the host:port part from the URL specified by url +func extractHostPort(newURL string) string { + if newURL == "" { + return "" + } + + authURL, err := parser.StringToURL(newURL) + if err != nil { + klog.Errorf("expected a valid URL but %s was returned", newURL) + return "" + } + + return authURL.Host +} diff --git a/test/e2e-image/e2e.sh b/test/e2e-image/e2e.sh index f8ecd5337d..f793c76819 100755 --- a/test/e2e-image/e2e.sh +++ b/test/e2e-image/e2e.sh @@ -24,7 +24,7 @@ E2E_CHECK_LEAKS=${E2E_CHECK_LEAKS:-""} reportFile="report-e2e-test-suite.xml" ginkgo_args=( - "--fail-fast" + # "--fail-fast" "--flake-attempts=2" "--junit-report=${reportFile}" "--nodes=${E2E_NODES}" diff --git a/test/e2e/admission/admission.go b/test/e2e/admission/admission.go index 873e6719da..870df21b27 100644 --- a/test/e2e/admission/admission.go +++ b/test/e2e/admission/admission.go @@ -100,6 +100,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if there is an error validating the ingress definition", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -208,6 +211,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if the Ingress V1 definition contains invalid annotations", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crissplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -222,6 +228,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should not return an error for an invalid Ingress when it has unknown class", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() out, err := createIngress(f.Namespace, invalidV1IngressWithOtherClass) diff --git a/test/e2e/annotations/affinity.go b/test/e2e/annotations/affinity.go index b64581ef62..d2adc86a5f 100644 --- a/test/e2e/annotations/affinity.go +++ b/test/e2e/annotations/affinity.go @@ -58,7 +58,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -80,7 +80,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -115,7 +115,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -181,7 +181,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -212,7 +212,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) local, err := time.LoadLocation("GMT") @@ -243,7 +243,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -265,7 +265,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -289,7 +289,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -312,7 +312,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -393,7 +393,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { // server alias sort by sort.Strings(), see: internal/ingress/annotations/alias/main.go:60 - return strings.Contains(server, fmt.Sprintf("server_name %s %s %s ;", host, alias1, alias2)) + return strings.Contains(server, fmt.Sprintf("server_name %s %s %s ;", host, alias1, alias2)) || + strings.Contains(server, fmt.Sprintf("server_name %s %s %s;", host, alias1, alias2)) }) f.HTTPTestClient(). @@ -430,7 +431,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -453,7 +454,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.HTTPTestClient(). @@ -475,7 +476,7 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && strings.Contains(server, "listen 443") }) diff --git a/test/e2e/annotations/affinitymode.go b/test/e2e/annotations/affinitymode.go index e6253b6ffa..a49a767219 100644 --- a/test/e2e/annotations/affinitymode.go +++ b/test/e2e/annotations/affinitymode.go @@ -56,7 +56,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) // Check configuration @@ -89,7 +89,7 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) // Check configuration diff --git a/test/e2e/annotations/auth.go b/test/e2e/annotations/auth.go index ddda1dce5f..fd1734f354 100644 --- a/test/e2e/annotations/auth.go +++ b/test/e2e/annotations/auth.go @@ -270,6 +270,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should set snippet "proxy_set_header My-Custom-Header 42;" when external auth is configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } host := authHost annotations := map[string]string{ @@ -290,6 +293,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should not set snippet "proxy_set_header My-Custom-Header 42;" when external auth is not configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } host := authHost disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -325,7 +331,8 @@ var _ = framework.DescribeAnnotation("auth-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `proxy_set_header 'My-Custom-Header' '42';`) + return strings.Contains(server, `proxy_set_header 'My-Custom-Header' '42';`) || + strings.Contains(server, `proxy_set_header My-Custom-Header 42;`) }) }) @@ -531,7 +538,8 @@ http { f.UpdateIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("proxy_set_header '%s' $authHeader0;", rewriteHeader)) + return strings.Contains(server, fmt.Sprintf("proxy_set_header '%s' $authHeader0;", rewriteHeader)) || + strings.Contains(server, fmt.Sprintf("proxy_set_header %s $authHeader0;", rewriteHeader)) }) f.HTTPTestClient(). @@ -893,6 +901,9 @@ http { }) ginkgo.It("should add error to the config", func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not allows injecting invalid configuration") + } f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, "could not parse auth-url annotation: invalid url host") }) diff --git a/test/e2e/annotations/canary.go b/test/e2e/annotations/canary.go index ea733dbf43..443a3a486e 100644 --- a/test/e2e/annotations/canary.go +++ b/test/e2e/annotations/canary.go @@ -1091,13 +1091,22 @@ var _ = framework.DescribeAnnotation("canary-*", func() { f.WaitForNginxServer("_", func(server string) bool { - upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, framework.HTTPBunService, "80") - canaryUpstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, canaryService, "80") + upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-80";`, f.Namespace, framework.HTTPBunService) + upstreamNameCrossplane := fmt.Sprintf(`set $proxy_upstream_name %s-%s-80;`, f.Namespace, framework.HTTPBunService) - return strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, host)) && + canaryUpstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-80";`, f.Namespace, canaryService) + canaryUpstreamNameCrossplane := fmt.Sprintf(`set $proxy_upstream_name %s-%s-80;`, f.Namespace, canaryService) + + return (strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, host)) && !strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend";`) && !strings.Contains(server, canaryUpstreamName) && - strings.Contains(server, upstreamName) + strings.Contains(server, upstreamName)) || + // Crossplane assertion + (strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, host)) && + !strings.Contains(server, `set $proxy_upstream_name "pstream-default-backend;`) && + !strings.Contains(server, canaryUpstreamNameCrossplane) && + strings.Contains(server, upstreamNameCrossplane)) + }) }) diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go index 58f4445f70..1db7d53741 100644 --- a/test/e2e/annotations/cors.go +++ b/test/e2e/annotations/cors.go @@ -48,13 +48,21 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';") && + return (strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: $http_origin';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';") && strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 1728000';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Credentials: true';") && strings.Contains(server, "set $http_origin *;") && - strings.Contains(server, "$cors 'true';") + strings.Contains(server, "$cors 'true';")) || + // Assertions for crossplane mode + (strings.Contains(server, `more_set_headers "Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Origin: $http_origin";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";`) && + strings.Contains(server, `more_set_headers "Access-Control-Max-Age: 1728000";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Credentials: true";`) && + strings.Contains(server, "set $http_origin *;") && + strings.Contains(server, "$cors true;")) }) f.HTTPTestClient(). @@ -76,7 +84,9 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: POST, GET';") + return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: POST, GET';") || + strings.Contains(server, `more_set_headers "Access-Control-Allow-Methods: POST, GET";`) + }) }) @@ -92,7 +102,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 200';") + return strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 200';") || + strings.Contains(server, `more_set_headers "Access-Control-Max-Age: 200";`) }) }) @@ -151,7 +162,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT, User-Agent';") + return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT, User-Agent';") || + strings.Contains(server, `more_set_headers "Access-Control-Allow-Headers: DNT, User-Agent";`) }) }) @@ -167,7 +179,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader';") + return strings.Contains(server, "more_set_headers 'Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader';") || + strings.Contains(server, `more_set_headers "Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader";`) }) }) diff --git a/test/e2e/annotations/fastcgi.go b/test/e2e/annotations/fastcgi.go index bcf1c3487b..b4684faefc 100644 --- a/test/e2e/annotations/fastcgi.go +++ b/test/e2e/annotations/fastcgi.go @@ -64,7 +64,8 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "fastcgi_index \"index.php\";") + return strings.Contains(server, "fastcgi_index \"index.php\";") || + strings.Contains(server, "fastcgi_index index.php;") }) }) @@ -94,8 +95,10 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"$fastcgi_script_name\";") && - strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";") + return (strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"$fastcgi_script_name\";") && + strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";")) || + (strings.Contains(server, "fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;") && + strings.Contains(server, "fastcgi_param REDIRECT_STATUS 200;")) }) }) diff --git a/test/e2e/annotations/fromtowwwredirect.go b/test/e2e/annotations/fromtowwwredirect.go index a3fb3b9b5d..2f56c23150 100644 --- a/test/e2e/annotations/fromtowwwredirect.go +++ b/test/e2e/annotations/fromtowwwredirect.go @@ -62,17 +62,20 @@ var _ = framework.DescribeAnnotation("from-to-www-redirect", func() { }) ginkgo.It("should redirect from www HTTPS to HTTPS", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - ginkgo.By("setting up server for redirect from www") + h := make(map[string]string) + h["ExpectedHost"] = "$http_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) + fromHost := fmt.Sprintf("%s.nip.io", f.GetNginxIP()) toHost := fmt.Sprintf("www.%s", fromHost) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/from-to-www-redirect": "true", - "nginx.ingress.kubernetes.io/configuration-snippet": "more_set_headers \"ExpectedHost: $http_host\";", + "nginx.ingress.kubernetes.io/from-to-www-redirect": "true", } ing := framework.NewSingleIngressWithTLS(fromHost, "/", fromHost, []string{fromHost, toHost}, f.Namespace, framework.EchoService, 80, annotations) diff --git a/test/e2e/annotations/http2pushpreload.go b/test/e2e/annotations/http2pushpreload.go index b15d2df179..2c317eef8d 100644 --- a/test/e2e/annotations/http2pushpreload.go +++ b/test/e2e/annotations/http2pushpreload.go @@ -25,6 +25,10 @@ import ( ) var _ = framework.DescribeAnnotation("http2-push-preload", func() { + if framework.IsCrossplane() { + // Http2 Push preload is removed from crossplane as it is deprecated + return + } f := framework.NewDefaultFramework("http2pushpreload") ginkgo.BeforeEach(func() { diff --git a/test/e2e/annotations/limitconnections.go b/test/e2e/annotations/limitconnections.go index 7d00b6df06..d44cb169c3 100644 --- a/test/e2e/annotations/limitconnections.go +++ b/test/e2e/annotations/limitconnections.go @@ -41,7 +41,7 @@ var _ = framework.DescribeAnnotation("Annotation - limit-connections", func() { ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.SlowEchoService, 80, nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) // limit connections diff --git a/test/e2e/annotations/mirror.go b/test/e2e/annotations/mirror.go index 787cbfa3bc..c6cd028c71 100644 --- a/test/e2e/annotations/mirror.go +++ b/test/e2e/annotations/mirror.go @@ -60,7 +60,8 @@ var _ = framework.DescribeAnnotation("mirror-*", func() { func(server string) bool { return strings.Contains(server, fmt.Sprintf("mirror /_mirror-%v;", ing.UID)) && strings.Contains(server, "mirror_request_body on;") && - strings.Contains(server, `proxy_pass "https://test.env.com/$request_uri";`) + (strings.Contains(server, `proxy_pass "https://test.env.com/$request_uri";`) || + strings.Contains(server, `proxy_pass https://test.env.com/$request_uri;`)) }) }) diff --git a/test/e2e/annotations/modsecurity/modsecurity.go b/test/e2e/annotations/modsecurity/modsecurity.go index 730fc76e78..606a50ede2 100644 --- a/test/e2e/annotations/modsecurity/modsecurity.go +++ b/test/e2e/annotations/modsecurity/modsecurity.go @@ -38,6 +38,9 @@ const ( var _ = framework.DescribeAnnotation("modsecurity owasp", func() { f := framework.NewDefaultFramework("modsecuritylocation") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 173df29f00..416e0190e6 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -97,8 +97,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/" {`) && - strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) + return (strings.Contains(server, `location ~* "^/" {`) || strings.Contains(server, `location ~* ^/ {`)) && + (strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) || strings.Contains(server, `location ~* ^/.well-known/acme/challenge {`)) }) ginkgo.By("making a second request to the non-rewritten location") @@ -132,8 +132,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo" {`) && - strings.Contains(server, `location ~* "^/foo.+" {`) + return (strings.Contains(server, `location ~* "^/foo" {`) || strings.Contains(server, `location ~* ^/foo {`)) && + (strings.Contains(server, `location ~* "^/foo.+" {`) || strings.Contains(server, `location ~* ^/foo.+ {`)) }) ginkgo.By("ensuring '/foo' matches '~* ^/foo'") @@ -174,7 +174,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo/bar/bar" {`) && + return (strings.Contains(server, `location ~* "^/foo/bar/bar" {`) || + strings.Contains(server, `location ~* ^/foo/bar/bar {`)) && strings.Contains(server, `location ~* "^/foo/bar/[a-z]{3}" {`) }) @@ -202,7 +203,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo/bar/(.+)" {`) + return strings.Contains(server, `location ~* "^/foo/bar/(.+)" {`) || strings.Contains(server, `location ~* ^/foo/bar/(.+) {`) }) ginkgo.By("check that '/foo/bar/bar' redirects to custom rewrite") diff --git a/test/e2e/annotations/serversnippet.go b/test/e2e/annotations/serversnippet.go index c94960a3da..0a6c091048 100644 --- a/test/e2e/annotations/serversnippet.go +++ b/test/e2e/annotations/serversnippet.go @@ -27,6 +27,9 @@ import ( var _ = framework.DescribeAnnotation("server-snippet", func() { f := framework.NewDefaultFramework("serversnippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/snippet.go b/test/e2e/annotations/snippet.go index 9e3160dcc4..178db4202d 100644 --- a/test/e2e/annotations/snippet.go +++ b/test/e2e/annotations/snippet.go @@ -30,6 +30,9 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() { "configurationsnippet", framework.WithHTTPBunEnabled(), ) + if framework.IsCrossplane() { + return + } ginkgo.It("set snippet more_set_headers in all locations", func() { host := "configurationsnippet.foo.com" diff --git a/test/e2e/annotations/streamsnippet.go b/test/e2e/annotations/streamsnippet.go index f91cdc34e3..1a5f7fd5de 100644 --- a/test/e2e/annotations/streamsnippet.go +++ b/test/e2e/annotations/streamsnippet.go @@ -33,6 +33,9 @@ import ( var _ = framework.DescribeSetting("stream-snippet", func() { f := framework.NewDefaultFramework("stream-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/upstreamhashby.go b/test/e2e/annotations/upstreamhashby.go index 1b81066627..e5e3c5846e 100644 --- a/test/e2e/annotations/upstreamhashby.go +++ b/test/e2e/annotations/upstreamhashby.go @@ -36,7 +36,7 @@ func startIngress(f *framework.Framework, annotations map[string]string) map[str f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated diff --git a/test/e2e/annotations/upstreamvhost.go b/test/e2e/annotations/upstreamvhost.go index e091e7c9fd..fb763ba1d5 100644 --- a/test/e2e/annotations/upstreamvhost.go +++ b/test/e2e/annotations/upstreamvhost.go @@ -42,7 +42,8 @@ var _ = framework.DescribeAnnotation("upstream-vhost", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `proxy_set_header Host "upstreamvhost.bar.com";`) + return strings.Contains(server, `proxy_set_header Host "upstreamvhost.bar.com";`) || + strings.Contains(server, `proxy_set_header Host upstreamvhost.bar.com;`) }) }) }) diff --git a/test/e2e/annotations/xforwardedprefix.go b/test/e2e/annotations/xforwardedprefix.go index 35f2eb4262..3ca4497570 100644 --- a/test/e2e/annotations/xforwardedprefix.go +++ b/test/e2e/annotations/xforwardedprefix.go @@ -43,7 +43,8 @@ var _ = framework.DescribeAnnotation("x-forwarded-prefix", func() { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, host) && - strings.Contains(server, "proxy_set_header X-Forwarded-Prefix \"/test/value\";") + (strings.Contains(server, "proxy_set_header X-Forwarded-Prefix \"/test/value\";") || + strings.Contains(server, "proxy_set_header X-Forwarded-Prefix /test/value;")) }) f.HTTPTestClient(). diff --git a/test/e2e/defaultbackend/custom_default_backend.go b/test/e2e/defaultbackend/custom_default_backend.go index 1e30b9269f..37b8c8ab26 100644 --- a/test/e2e/defaultbackend/custom_default_backend.go +++ b/test/e2e/defaultbackend/custom_default_backend.go @@ -47,7 +47,8 @@ var _ = framework.IngressNginxDescribe("[Default Backend] custom service", func( f.WaitForNginxServer("_", func(server string) bool { - return strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend"`) + return strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(server, `set $proxy_upstream_name upstream-default-backend`) }) f.HTTPTestClient(). diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 204da7df05..02cc088164 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "time" @@ -119,6 +120,11 @@ func NewSimpleFramework(baseName string, opts ...func(*Framework)) *Framework { return f } +func IsCrossplane() bool { + isCrossplane, ok := os.LookupEnv("IS_CROSSPLANE") + return ok && isCrossplane == "true" +} + func (f *Framework) CreateEnvironment() { var err error @@ -315,7 +321,7 @@ func (f *Framework) matchNginxConditions(name string, matcher func(cfg string) b if name == "" { cmd = "cat /etc/nginx/nginx.conf" } else { - cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %v/,/## end server %v/'", name, name) + cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %s;/,/## end server %s;/'", name, name) } o, err := f.ExecCommand(f.pod, cmd) diff --git a/test/e2e/ingress/multiple_rules.go b/test/e2e/ingress/multiple_rules.go index 9247dc1d3f..22ca08a96f 100644 --- a/test/e2e/ingress/multiple_rules.go +++ b/test/e2e/ingress/multiple_rules.go @@ -17,11 +17,11 @@ limitations under the License. package ingress import ( + "fmt" "net/http" "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -36,14 +36,19 @@ var _ = framework.IngressNginxDescribe("single ingress - multiple hosts", func() }) ginkgo.It("should set the correct $service_name NGINX variable", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + customHeader := "Service-Name" + customHeaderValue := "$service_name" - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "service-name: $service_name";`, - } + h := make(map[string]string) + h[customHeader] = customHeaderValue - ing := framework.NewSingleIngress("simh", "/", "first.host", f.Namespace, "first-service", 80, annotations) + cfgMap := "custom-headers" + + f.CreateConfigMap(cfgMap, h) + + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%v/%v", f.Namespace, cfgMap)) + + ing := framework.NewSingleIngress("simh", "/", "first.host", f.Namespace, "first-service", 80, nil) ing.Spec.Rules = append(ing.Spec.Rules, networking.IngressRule{ Host: "second.host", @@ -79,26 +84,19 @@ var _ = framework.IngressNginxDescribe("single ingress - multiple hosts", func() return strings.Contains(server, "second.host") }) - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", "first.host"). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "service-name=first-service") - assert.NotContains(ginkgo.GinkgoT(), body, "service-name=second-service") + Headers().ValueEqual("Service-Name", []string{"first-service"}) - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", "second.host"). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Service-Name", []string{"second-service"}) - assert.NotContains(ginkgo.GinkgoT(), body, "service-name=first-service") - assert.Contains(ginkgo.GinkgoT(), body, "service-name=second-service") }) }) diff --git a/test/e2e/ingress/pathtype_exact.go b/test/e2e/ingress/pathtype_exact.go index 2660e32a45..060ea9a318 100644 --- a/test/e2e/ingress/pathtype_exact.go +++ b/test/e2e/ingress/pathtype_exact.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -35,24 +34,31 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { }) ginkgo.It("should choose exact location for /exact", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - host := "exact.path" + f.UpdateNginxConfigMapData("global-allowed-response-headers", "Pathtype,duplicated") annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-exact", } + f.CreateConfigMap("custom-headers-exact", map[string]string{ + "Pathtype": "exact", + }) + + host := "exact.path" exactPathType := networking.PathTypeExact ing := framework.NewSingleIngress("exact", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-prefix", } + f.CreateConfigMap("custom-headers-prefix", map[string]string{ + "Pathtype": "prefix", + }) + ing = framework.NewSingleIngress("exact-suffix", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -63,34 +69,29 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { strings.Contains(server, "location /exact/") }) - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"exact"}) - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact/suffix"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") + Headers().ValueEqual("Pathtype", []string{"prefix"}) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": ` - more_set_input_headers "pathType: prefix"; - more_set_input_headers "duplicated: true"; - `, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-duplicated", } + f.CreateConfigMap("custom-headers-duplicated", map[string]string{ + "Pathtype": "prefix", + "duplicated": "true", + }) + ing = framework.NewSingleIngress("duplicated-prefix", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -101,16 +102,12 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { strings.Contains(server, "location /exact/") }) - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact/suffix"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"prefix"}).NotContainsKey("duplicated") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.NotContains(ginkgo.GinkgoT(), body, "duplicated=true") }) }) diff --git a/test/e2e/ingress/pathtype_mixed.go b/test/e2e/ingress/pathtype_mixed.go index 3212089c90..0573cc0f58 100644 --- a/test/e2e/ingress/pathtype_mixed.go +++ b/test/e2e/ingress/pathtype_mixed.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -37,21 +36,32 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi exactPathType := networking.PathTypeExact ginkgo.It("should choose the correct location", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - host := "mixed.path" + f.UpdateNginxConfigMapData("global-allowed-response-headers", "Pathtype,Pathheader") + annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathheader: /";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-exact", } + + f.CreateConfigMap("custom-headers-exact", map[string]string{ + "Pathtype": "exact", + "Pathheader": "/", + }) + ing := framework.NewSingleIngress("exact-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathheader: /";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-prefix", } + + f.CreateConfigMap("custom-headers-prefix", map[string]string{ + "Pathtype": "prefix", + "Pathheader": "/", + }) + ing = framework.NewSingleIngress("prefix-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -63,41 +73,42 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi }) ginkgo.By("Checking exact request to /") - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") + Headers().ValueEqual("Pathtype", []string{"exact"}).ValueEqual("Pathheader", []string{"/"}) ginkgo.By("Checking prefix request to /bar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/bar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/"}) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathheader: /foo";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-ex-foo", } + + f.CreateConfigMap("custom-headers-ex-foo", map[string]string{ + "Pathtype": "exact", + "Pathheader": "/foo", + }) ing = framework.NewSingleIngress("exact-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathheader: /foo";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-pr-foo", } + + f.CreateConfigMap("custom-headers-pr-foo", map[string]string{ + "Pathtype": "prefix", + "Pathheader": "/foo", + }) + ing = framework.NewSingleIngress("prefix-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -109,40 +120,28 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi }) ginkgo.By("Checking exact request to /foo") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foo"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/foo") + Headers().ValueEqual("Pathtype", []string{"exact"}).ValueEqual("Pathheader", []string{"/foo"}) ginkgo.By("Checking prefix request to /foo/bar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foo/bar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/foo") + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/foo"}) ginkgo.By("Checking prefix request to /foobar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foobar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/"}) - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") }) }) diff --git a/test/e2e/ingress/without_host.go b/test/e2e/ingress/without_host.go index 38f89beda1..24192a0b7d 100644 --- a/test/e2e/ingress/without_host.go +++ b/test/e2e/ingress/without_host.go @@ -39,11 +39,17 @@ var _ = framework.IngressNginxDescribe("[Ingress] definition without host", func f.WaitForNginxServer("_", func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && + return (strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, ing.Name)) && strings.Contains(server, fmt.Sprintf(`set $service_name "%v";`, framework.EchoService)) && strings.Contains(server, `set $service_port "80";`) && - strings.Contains(server, `set $location_path "/";`) + strings.Contains(server, `set $location_path "/";`)) || + // Crossplane assertions + (strings.Contains(server, fmt.Sprintf(`set $namespace %s;`, f.Namespace)) && + strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, ing.Name)) && + strings.Contains(server, fmt.Sprintf(`set $service_name %s;`, framework.EchoService)) && + strings.Contains(server, `set $service_port 80;`) && + strings.Contains(server, `set $location_path /;`)) }) f.HTTPTestClient(). @@ -81,11 +87,17 @@ var _ = framework.IngressNginxDescribe("[Ingress] definition without host", func f.WaitForNginxServer("only-backend", func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && + return (strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, ing.Name)) && strings.Contains(server, fmt.Sprintf(`set $service_name "%v";`, framework.EchoService)) && strings.Contains(server, `set $service_port "80";`) && - strings.Contains(server, `set $location_path "/";`) + strings.Contains(server, `set $location_path "/";`)) || + // Crossplane assertions + (strings.Contains(server, fmt.Sprintf(`set $namespace %s;`, f.Namespace)) && + strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, ing.Name)) && + strings.Contains(server, fmt.Sprintf(`set $service_name %s;`, framework.EchoService)) && + strings.Contains(server, `set $service_port 80;`) && + strings.Contains(server, `set $location_path /;`)) }) f.HTTPTestClient(). diff --git a/test/e2e/lua/dynamic_configuration.go b/test/e2e/lua/dynamic_configuration.go index a5e2196cef..8f1deaeb12 100644 --- a/test/e2e/lua/dynamic_configuration.go +++ b/test/e2e/lua/dynamic_configuration.go @@ -212,7 +212,7 @@ func createIngress(f *framework.Framework, host, deploymentName string) { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) } diff --git a/test/e2e/metrics/metrics.go b/test/e2e/metrics/metrics.go index bec09bb37c..3cdb88057e 100644 --- a/test/e2e/metrics/metrics.go +++ b/test/e2e/metrics/metrics.go @@ -43,7 +43,7 @@ var _ = framework.IngressNginxDescribe("[metrics] exported prometheus metrics", f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) }) diff --git a/test/e2e/run-e2e-suite.sh b/test/e2e/run-e2e-suite.sh index 909368e96b..621313610a 100755 --- a/test/e2e/run-e2e-suite.sh +++ b/test/e2e/run-e2e-suite.sh @@ -78,6 +78,7 @@ kubectl run --rm \ --env="E2E_NODES=${E2E_NODES}" \ --env="FOCUS=${FOCUS}" \ --env="IS_CHROOT=${IS_CHROOT:-false}"\ + --env="IS_CROSSPLANE=${IS_CROSSPLANE:-false}"\ --env="SKIP_OPENTELEMETRY_TESTS=${SKIP_OPENTELEMETRY_TESTS:-false}"\ --env="E2E_CHECK_LEAKS=${E2E_CHECK_LEAKS}" \ --env="NGINX_BASE_IMAGE=${NGINX_BASE_IMAGE}" \ diff --git a/test/e2e/run-kind-e2e.sh b/test/e2e/run-kind-e2e.sh index afcfe1800b..f668719529 100755 --- a/test/e2e/run-kind-e2e.sh +++ b/test/e2e/run-kind-e2e.sh @@ -39,6 +39,8 @@ fi KIND_LOG_LEVEL="1" IS_CHROOT="${IS_CHROOT:-false}" +IS_CROSSPLANE="${IS_CROSSPLANE:-false}" + export KIND_CLUSTER_NAME=${KIND_CLUSTER_NAME:-ingress-nginx-dev} DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Use 1.0.0-dev to make sure we use the latest configuration in the helm template diff --git a/test/e2e/security/request_smuggling.go b/test/e2e/security/request_smuggling.go index 5ede02d4b5..d96ba4b4c0 100644 --- a/test/e2e/security/request_smuggling.go +++ b/test/e2e/security/request_smuggling.go @@ -37,6 +37,9 @@ var _ = framework.IngressNginxDescribe("[Security] request smuggling", func() { }) ginkgo.It("should not return body content from error_page", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") // TODO: Re-add this test when we enable admin defined snippets + } host := "foo.bar.com" snippet := ` diff --git a/test/e2e/settings/access_log.go b/test/e2e/settings/access_log.go index 65b9dfda4a..33466f29f5 100644 --- a/test/e2e/settings/access_log.go +++ b/test/e2e/settings/access_log.go @@ -31,6 +31,10 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.It("use the default configuration", func() { f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /var/log/nginx/access.log upstreaminfo") || + strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 upstreaminfo") + } return (strings.Contains(cfg, "access_log /var/log/nginx/access.log upstreaminfo") && strings.Contains(cfg, "access_log /var/log/nginx/access.log log_stream")) || (strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 upstreaminfo") && @@ -42,6 +46,9 @@ var _ = framework.DescribeSetting("access-log", func() { f.UpdateNginxConfigMapData("access-log-path", "/tmp/nginx/access.log") f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /tmp/nginx/access.log upstreaminfo") + } return strings.Contains(cfg, "access_log /tmp/nginx/access.log upstreaminfo") && strings.Contains(cfg, "access_log /tmp/nginx/access.log log_stream") }) @@ -53,6 +60,9 @@ var _ = framework.DescribeSetting("access-log", func() { f.UpdateNginxConfigMapData("http-access-log-path", "/tmp/nginx/http-access.log") f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /tmp/nginx/http-access.log upstreaminfo") + } return strings.Contains(cfg, "access_log /tmp/nginx/http-access.log upstreaminfo") && (strings.Contains(cfg, "access_log /var/log/nginx/access.log log_stream") || strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 log_stream")) @@ -62,6 +72,9 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.Context("stream-access-log-path", func() { ginkgo.It("use the specified configuration", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support stream") + } f.UpdateNginxConfigMapData("stream-access-log-path", "/tmp/nginx/stream-access.log") f.WaitForNginxConfiguration( func(cfg string) bool { @@ -74,6 +87,9 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.Context("http-access-log-path & stream-access-log-path", func() { ginkgo.It("use the specified configuration", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support stream") + } f.SetNginxConfigMapData(map[string]string{ "http-access-log-path": "/tmp/nginx/http-access.log", "stream-access-log-path": "/tmp/nginx/stream-access.log", diff --git a/test/e2e/settings/badannotationvalues.go b/test/e2e/settings/badannotationvalues.go index aa9906909c..bee12e66a2 100644 --- a/test/e2e/settings/badannotationvalues.go +++ b/test/e2e/settings/badannotationvalues.go @@ -50,7 +50,7 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.WaitForNginxServer(host, @@ -87,7 +87,7 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.WaitForNginxServer(host, @@ -103,6 +103,9 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should allow an ingress if there is a default blocklist config in place", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -120,7 +123,7 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(hostValid, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", hostValid)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", hostValid)) }) f.WaitForNginxServer(hostValid, @@ -153,7 +156,7 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) f.WaitForNginxServer(host, diff --git a/test/e2e/settings/configmap_change.go b/test/e2e/settings/configmap_change.go index 3e37b62cd6..3b18780519 100644 --- a/test/e2e/settings/configmap_change.go +++ b/test/e2e/settings/configmap_change.go @@ -17,7 +17,6 @@ limitations under the License. package settings import ( - "regexp" "strings" "github.com/onsi/ginkgo/v2" @@ -43,36 +42,20 @@ var _ = framework.DescribeSetting("Configmap change", func() { f.UpdateNginxConfigMapData("whitelist-source-range", "1.1.1.1") - checksumRegex := regexp.MustCompile(`Configuration checksum:\s+(\d+)`) - checksum := "" - f.WaitForNginxConfiguration( func(cfg string) bool { - // before returning, extract the current checksum - match := checksumRegex.FindStringSubmatch(cfg) - if len(match) > 0 { - checksum = match[1] - } - return strings.Contains(cfg, "allow 1.1.1.1;") }) - assert.NotEmpty(ginkgo.GinkgoT(), checksum) ginkgo.By("changing error-log-level") f.UpdateNginxConfigMapData("error-log-level", "debug") - newChecksum := "" f.WaitForNginxConfiguration( func(cfg string) bool { - match := checksumRegex.FindStringSubmatch(cfg) - if len(match) > 0 { - newChecksum = match[1] - } - - return strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") + return strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") || + strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") }) - assert.NotEqual(ginkgo.GinkgoT(), checksum, newChecksum) logs, err := f.NginxLogs() assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") diff --git a/test/e2e/settings/custom_header.go b/test/e2e/settings/custom_header.go index 8341f7ef02..b2c8aac8d6 100644 --- a/test/e2e/settings/custom_header.go +++ b/test/e2e/settings/custom_header.go @@ -79,8 +79,10 @@ var _ = framework.DescribeSetting("add-headers", func() { f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%v/%v", f.Namespace, cfgMap)) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf("more_set_headers \"%s: %s\";", firstCustomHeader, firstCustomHeaderValue)) && - strings.Contains(server, fmt.Sprintf("more_set_headers \"%s: %s\";", secondCustomHeader, secondCustomHeaderValue)) + return (strings.Contains(server, fmt.Sprintf(`more_set_headers "%s: %s";`, firstCustomHeader, firstCustomHeaderValue)) && + strings.Contains(server, fmt.Sprintf(`more_set_headers "%s: %s";`, secondCustomHeader, secondCustomHeaderValue))) || + (strings.Contains(server, fmt.Sprintf("more_set_headers %s: %s;", firstCustomHeader, firstCustomHeaderValue)) && + strings.Contains(server, fmt.Sprintf("more_set_headers %s: %s;", secondCustomHeader, secondCustomHeaderValue))) }) resp := f.HTTPTestClient(). diff --git a/test/e2e/settings/default_ssl_certificate.go b/test/e2e/settings/default_ssl_certificate.go index c48a1e87f7..e8e2195199 100644 --- a/test/e2e/settings/default_ssl_certificate.go +++ b/test/e2e/settings/default_ssl_certificate.go @@ -69,8 +69,10 @@ var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", f ginkgo.By("making sure new ingress is deployed") expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port) + expectedConfigCrossPlane := fmt.Sprintf(`set $proxy_upstream_name %v-%v-%v;`, f.Namespace, service, port) + f.WaitForNginxServer("_", func(cfg string) bool { - return strings.Contains(cfg, expectedConfig) + return strings.Contains(cfg, expectedConfig) || strings.Contains(cfg, expectedConfigCrossPlane) }) ginkgo.By("making sure new ingress is responding") @@ -91,8 +93,9 @@ var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", f ginkgo.By("making sure new ingress is deployed") expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port) + expectedConfigCrossPlane := fmt.Sprintf(`set $proxy_upstream_name %v-%v-%v;`, f.Namespace, service, port) f.WaitForNginxServer(host, func(cfg string) bool { - return strings.Contains(cfg, expectedConfig) + return strings.Contains(cfg, expectedConfig) || strings.Contains(cfg, expectedConfigCrossPlane) }) ginkgo.By("making sure the configured default ssl certificate is being used") diff --git a/test/e2e/settings/disable_catch_all.go b/test/e2e/settings/disable_catch_all.go index 4e7a16f4de..54fbe9d8bd 100644 --- a/test/e2e/settings/disable_catch_all.go +++ b/test/e2e/settings/disable_catch_all.go @@ -62,7 +62,8 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { f.WaitForNginxServer("_", func(cfg string) bool { return strings.Contains(cfg, `set $ingress_name ""`) && - strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) + (strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(cfg, `set $proxy_upstream_name upstream-default-backend`)) }) }) @@ -74,7 +75,8 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { f.WaitForNginxServer("_", func(cfg string) bool { return strings.Contains(cfg, `set $ingress_name ""`) && - strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) + (strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(cfg, `set $proxy_upstream_name upstream-default-backend`)) }) }) diff --git a/test/e2e/settings/global_external_auth.go b/test/e2e/settings/global_external_auth.go index f589a63e94..1721c54a57 100644 --- a/test/e2e/settings/global_external_auth.go +++ b/test/e2e/settings/global_external_auth.go @@ -246,6 +246,9 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should set snippet when global external auth is configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } globalExternalAuthSnippetSetting := "global-auth-snippet" globalExternalAuthSnippet := "proxy_set_header My-Custom-Header 42;" diff --git a/test/e2e/settings/gzip.go b/test/e2e/settings/gzip.go index c7e580e079..b2972e69c3 100644 --- a/test/e2e/settings/gzip.go +++ b/test/e2e/settings/gzip.go @@ -106,7 +106,7 @@ var _ = framework.DescribeSetting("gzip", func() { f.WaitForNginxConfiguration( func(cfg string) bool { return strings.Contains(cfg, "gzip on;") && - strings.Contains(cfg, `gzip_disable "msie6";`) + (strings.Contains(cfg, `gzip_disable "msie6";`) || strings.Contains(cfg, `gzip_disable msie6;`)) }, ) diff --git a/test/e2e/settings/limit_rate.go b/test/e2e/settings/limit_rate.go index 9d79dc3582..16ce982778 100644 --- a/test/e2e/settings/limit_rate.go +++ b/test/e2e/settings/limit_rate.go @@ -41,7 +41,7 @@ var _ = framework.DescribeSetting("Configmap - limit-rate", func() { f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) }) wlKey := "limit-rate" diff --git a/test/e2e/settings/main_snippet.go b/test/e2e/settings/main_snippet.go index ff14bab9d2..50bf96caed 100644 --- a/test/e2e/settings/main_snippet.go +++ b/test/e2e/settings/main_snippet.go @@ -26,6 +26,9 @@ import ( var _ = framework.DescribeSetting("main-snippet", func() { f := framework.NewDefaultFramework("main-snippet") + if framework.IsCrossplane() { + return + } mainSnippet := "main-snippet" ginkgo.It("should add value of main-snippet setting to nginx config", func() { diff --git a/test/e2e/settings/modsecurity/modsecurity_snippet.go b/test/e2e/settings/modsecurity/modsecurity_snippet.go index 2dd92ced26..5e82ccf8e7 100644 --- a/test/e2e/settings/modsecurity/modsecurity_snippet.go +++ b/test/e2e/settings/modsecurity/modsecurity_snippet.go @@ -26,6 +26,9 @@ import ( var _ = framework.DescribeSetting("[Security] modsecurity-snippet", func() { f := framework.NewDefaultFramework("modsecurity-snippet") + if framework.IsCrossplane() { + return + } ginkgo.It("should add value of modsecurity-snippet setting to nginx config", func() { expectedComment := "# modsecurity snippet" diff --git a/test/e2e/settings/no_tls_redirect_locations.go b/test/e2e/settings/no_tls_redirect_locations.go index 18fd09e267..b9ac1bb971 100644 --- a/test/e2e/settings/no_tls_redirect_locations.go +++ b/test/e2e/settings/no_tls_redirect_locations.go @@ -33,7 +33,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.EnsureIngress(ing) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, "set $force_no_ssl_redirect \"false\"") + return strings.Contains(server, "set $force_no_ssl_redirect \"false\"") || strings.Contains(server, "set $force_no_ssl_redirect false") }) wlKey := "no-tls-redirect-locations" @@ -42,7 +42,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.UpdateNginxConfigMapData(wlKey, wlValue) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, "set $force_no_ssl_redirect \"true\"") + return strings.Contains(server, "set $force_no_ssl_redirect \"true\"") || strings.Contains(server, "set $force_no_ssl_redirect true") }) }) }) diff --git a/test/e2e/settings/proxy_host.go b/test/e2e/settings/proxy_host.go index bb5dc9c012..1fcda11a13 100644 --- a/test/e2e/settings/proxy_host.go +++ b/test/e2e/settings/proxy_host.go @@ -34,14 +34,16 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + + h := make(map[string]string) + h["Custom-Header"] = "$proxy_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Custom-Header: $proxy_host"`, - } - f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, annotations)) + f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxConfiguration( func(server string) bool { @@ -58,15 +60,20 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host using the upstream-vhost annotation value", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + + h := make(map[string]string) + h["Custom-Header"] = "$proxy_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) upstreamVHost := "different.host" annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": upstreamVHost, - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Custom-Header: $proxy_host"`, + "nginx.ingress.kubernetes.io/upstream-vhost": upstreamVHost, } + f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, annotations)) f.WaitForNginxConfiguration( diff --git a/test/e2e/settings/proxy_protocol.go b/test/e2e/settings/proxy_protocol.go index cfce68bf8a..4c2e2a1aaf 100644 --- a/test/e2e/settings/proxy_protocol.go +++ b/test/e2e/settings/proxy_protocol.go @@ -162,6 +162,9 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { }) ginkgo.It("should enable PROXY Protocol for TCP", func() { + if framework.IsCrossplane() { + return + } cmapData := map[string]string{} cmapData[setting] = "true" cmapData["enable-real-ip"] = "true" diff --git a/test/e2e/settings/server_snippet.go b/test/e2e/settings/server_snippet.go index 1e2084bd84..e162630db7 100644 --- a/test/e2e/settings/server_snippet.go +++ b/test/e2e/settings/server_snippet.go @@ -28,6 +28,9 @@ import ( var _ = framework.DescribeSetting("configmap server-snippet", func() { f := framework.NewDefaultFramework("cm-server-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() }) diff --git a/test/e2e/settings/ssl_ciphers.go b/test/e2e/settings/ssl_ciphers.go index 241dfc92b2..f80bf41a27 100644 --- a/test/e2e/settings/ssl_ciphers.go +++ b/test/e2e/settings/ssl_ciphers.go @@ -35,7 +35,7 @@ var _ = framework.DescribeSetting("ssl-ciphers", func() { f.UpdateNginxConfigMapData(wlKey, wlValue) f.WaitForNginxConfiguration(func(cfg string) bool { - return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", wlValue)) + return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", wlValue)) || strings.Contains(cfg, fmt.Sprintf("ssl_ciphers %s;", wlValue)) }) }) }) diff --git a/test/e2e/settings/ssl_passthrough.go b/test/e2e/settings/ssl_passthrough.go index b10511bde4..111c0c776d 100644 --- a/test/e2e/settings/ssl_passthrough.go +++ b/test/e2e/settings/ssl_passthrough.go @@ -35,6 +35,9 @@ import ( var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { f := framework.NewDefaultFramework("ssl-passthrough", framework.WithHTTPBunEnabled()) + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { err := f.UpdateIngressControllerDeployment(func(deployment *appsv1.Deployment) error { diff --git a/test/e2e/settings/stream_snippet.go b/test/e2e/settings/stream_snippet.go index e1552afd36..4d1757a086 100644 --- a/test/e2e/settings/stream_snippet.go +++ b/test/e2e/settings/stream_snippet.go @@ -34,6 +34,9 @@ import ( var _ = framework.DescribeSetting("configmap stream-snippet", func() { f := framework.NewDefaultFramework("cm-stream-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/settings/tls.go b/test/e2e/settings/tls.go index 1ebf358c18..2191116642 100644 --- a/test/e2e/settings/tls.go +++ b/test/e2e/settings/tls.go @@ -71,7 +71,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers", fu f.WaitForNginxConfiguration( func(cfg string) bool { - return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", testCiphers)) + return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", testCiphers)) || strings.Contains(cfg, fmt.Sprintf("ssl_ciphers %s;", testCiphers)) }) resp := f.HTTPTestClientWithTLSConfig(tlsConfig). diff --git a/test/e2e/tcpudp/tcp.go b/test/e2e/tcpudp/tcp.go index 856184d18b..de45b00351 100644 --- a/test/e2e/tcpudp/tcp.go +++ b/test/e2e/tcpudp/tcp.go @@ -37,6 +37,9 @@ import ( var _ = framework.IngressNginxDescribe("[TCP] tcp-services", func() { f := framework.NewDefaultFramework("tcp") + if framework.IsCrossplane() { + return + } var ip string ginkgo.BeforeEach(func() { From b447997777b29907805c282a2ba28c814fb26122 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 10 Nov 2024 17:13:02 -0700 Subject: [PATCH 10/17] Remove temporary templates --- .../template/crossplane/testdata/README.md | 8 - .../crossplane/testdata/nginx-new-orig.tmpl | 1450 ----------------- .../crossplane/testdata/nginx-new.tmpl | 1268 -------------- 3 files changed, 2726 deletions(-) delete mode 100644 internal/ingress/controller/template/crossplane/testdata/README.md delete mode 100644 internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl delete mode 100644 internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl diff --git a/internal/ingress/controller/template/crossplane/testdata/README.md b/internal/ingress/controller/template/crossplane/testdata/README.md deleted file mode 100644 index 4f44e3c98e..0000000000 --- a/internal/ingress/controller/template/crossplane/testdata/README.md +++ /dev/null @@ -1,8 +0,0 @@ -This directory contains the following files: - -* nginx.tmpl - Should be used to track migrated directives. We will test later to see -if the ending result of it is the same as the one parsed by go-crossplane -* various nginx.conf - Will be used to see if the template parser reacts as -expected, creating files that matches and can be parsed by go-crossplane - -TODO: move files to embed.FS \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl deleted file mode 100644 index b7fa803829..0000000000 --- a/internal/ingress/controller/template/crossplane/testdata/nginx-new-orig.tmpl +++ /dev/null @@ -1,1450 +0,0 @@ -{{ $all := . }} -{{ $servers := .Servers }} -{{ $cfg := .Cfg }} -{{ $IsIPV6Enabled := .IsIPV6Enabled }} -{{ $healthzURI := .HealthzURI }} -{{ $backends := .Backends }} -{{ $proxyHeaders := .ProxySetHeaders }} -{{ $addHeaders := .AddHeaders }} - -# Configuration checksum: {{ $all.Cfg.Checksum }} - -# setup custom paths that do not require root access -pid {{ .PID }}; # OK - -{{ if $cfg.UseGeoIP2 }} #OK -load_module /etc/nginx/modules/ngx_http_geoip2_module.so; -{{ end }} - -{{ if $cfg.EnableBrotli }} #OK -load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; -load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; -{{ end }} - -{{ if (shouldLoadAuthDigestModule $servers) }} -load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; -{{ end }} - -{{ if (shouldLoadModSecurityModule $cfg $servers) }} -load_module /etc/nginx/modules/ngx_http_modsecurity_module.so; -{{ end }} - -{{ if (shouldLoadOpentelemetryModule $cfg $servers) }} -load_module /etc/nginx/modules/otel_ngx_module.so; -{{ end }} - -daemon off; # OK - -worker_processes {{ $cfg.WorkerProcesses }}; # OK -{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} # OK -worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; -{{ end }} - -worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; # OK - -{{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}} -{{/* avoid waiting too long during a reload */}} -worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; # OK - -# REMOVED -# {{ if not (empty $cfg.MainSnippet) }} -# {{ $cfg.MainSnippet }} -# {{ end }} - -events { - multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; # OK - worker_connections {{ $cfg.MaxWorkerConnections }}; # OK - use epoll; # OK - {{ range $index , $v := $cfg.DebugConnections }} # OK - debug_connection {{ $v }}; # OK - {{ end }} -} - -http { - {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} - opentelemetry_config {{ $cfg.OpentelemetryConfig }}; - {{ end }} - - lua_package_path "/etc/nginx/lua/?.lua;;"; # OK - - {{ buildLuaSharedDictionaries $cfg $servers }} # OK - - lua_shared_dict luaconfig 5m; # OK - - init_by_lua_file /etc/nginx/lua/ngx_conf_init.lua; # OK - - init_worker_by_lua_file /etc/nginx/lua/ngx_conf_init_worker.lua; # OK - - {{/* 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 }} - {{ if $cfg.UseProxyProtocol }} - real_ip_header proxy_protocol; - {{ else }} - real_ip_header {{ $cfg.ForwardedForHeader }}; - {{ end }} - - real_ip_recursive on; - {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} - set_real_ip_from {{ $trusted_ip }}; - {{ end }} - {{ end }} - - {{ if $all.Cfg.EnableModsecurity }} - modsecurity on; - - {{ if (not (empty $all.Cfg.ModsecuritySnippet)) }} - modsecurity_rules ' - {{ $all.Cfg.ModsecuritySnippet }} - '; - {{ else }} - modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf; - {{ end }} - - {{ if $all.Cfg.EnableOWASPCoreRules }} - modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf; - {{ end }} - - {{ end }} - - {{ if $cfg.UseGeoIP2 }} - # https://github.com/leev/ngx_http_geoip2_module#example-usage - - {{ range $index, $file := $all.MaxmindEditionFiles }} - {{ if eq $file "GeoLite2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; - $geoip2_country_name source=$remote_addr country names en; - $geoip2_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_continent_code source=$remote_addr continent code; - $geoip2_continent_name source=$remote_addr continent names en; - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; - } - {{ end }} - - {{ if eq $file "GeoIP2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; - $geoip2_country_name source=$remote_addr country names en; - $geoip2_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_continent_code source=$remote_addr continent code; - $geoip2_continent_name source=$remote_addr continent names en; - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; - } - {{ end }} - - {{ if eq $file "GeoLite2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; - $geoip2_city_country_name source=$remote_addr country names en; - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_city source=$remote_addr city names en; - $geoip2_city_geoname_id source=$remote_addr city geoname_id; - $geoip2_postal_code source=$remote_addr postal code; - $geoip2_dma_code source=$remote_addr location metro_code; - $geoip2_latitude source=$remote_addr location latitude; - $geoip2_longitude source=$remote_addr location longitude; - $geoip2_time_zone source=$remote_addr location time_zone; - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; - $geoip2_region_name source=$remote_addr subdivisions 0 names en; - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; - $geoip2_city_continent_code source=$remote_addr continent code; - $geoip2_city_continent_name source=$remote_addr continent names en; - } - {{ end }} - - {{ if eq $file "GeoIP2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; - $geoip2_city_country_name source=$remote_addr country names en; - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; - $geoip2_city source=$remote_addr city names en; - $geoip2_city_geoname_id source=$remote_addr city geoname_id; - $geoip2_postal_code source=$remote_addr postal code; - $geoip2_dma_code source=$remote_addr location metro_code; - $geoip2_latitude source=$remote_addr location latitude; - $geoip2_longitude source=$remote_addr location longitude; - $geoip2_time_zone source=$remote_addr location time_zone; - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; - $geoip2_region_name source=$remote_addr subdivisions 0 names en; - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; - $geoip2_city_continent_code source=$remote_addr continent code; - $geoip2_city_continent_name source=$remote_addr continent names en; - } - {{ end }} - - {{ if eq $file "GeoLite2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; - $geoip2_org source=$remote_addr autonomous_system_organization; - } - {{ end }} - - {{ if eq $file "GeoIP2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; - $geoip2_org source=$remote_addr autonomous_system_organization; - } - {{ end }} - - {{ if eq $file "GeoIP2-ISP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_isp source=$remote_addr isp; - $geoip2_isp_org source=$remote_addr organization; - $geoip2_asn source=$remote_addr default=0 autonomous_system_number; - } - {{ end }} - - {{ if eq $file "GeoIP2-Connection-Type.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Connection-Type.mmdb { - $geoip2_connection_type connection_type; - } - {{ end }} - - {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; - {{ end }} - $geoip2_is_anon source=$remote_addr is_anonymous; - $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; - $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; - $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; - $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; - $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; - } - {{ end }} - - {{ end }} - - {{ end }} - - aio threads; - - {{ if $cfg.EnableAioWrite }} - aio_write on; - {{ end }} - - tcp_nopush on; - tcp_nodelay on; - - log_subrequest on; - - reset_timedout_connection on; - - keepalive_timeout {{ $cfg.KeepAlive }}s; - keepalive_requests {{ $cfg.KeepAliveRequests }}; - - client_body_temp_path /tmp/nginx/client-body; - fastcgi_temp_path /tmp/nginx/fastcgi-temp; - proxy_temp_path /tmp/nginx/proxy-temp; - - client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }}; - client_header_timeout {{ $cfg.ClientHeaderTimeout }}s; - large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }}; - client_body_buffer_size {{ $cfg.ClientBodyBufferSize }}; - client_body_timeout {{ $cfg.ClientBodyTimeout }}s; - - {{ if gt $cfg.GRPCBufferSizeKb 0 }} - grpc_buffer_size {{ $cfg.GRPCBufferSizeKb }}k; - {{ end }} - - {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} - http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; - http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; - {{ end }} - - {{ if (gt $cfg.HTTP2MaxRequests 0) }} - http2_max_requests {{ $cfg.HTTP2MaxRequests }}; - {{ end }} - - http2_max_concurrent_streams {{ $cfg.HTTP2MaxConcurrentStreams }}; - - types_hash_max_size 2048; - server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }}; - server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }}; - map_hash_bucket_size {{ $cfg.MapHashBucketSize }}; - - proxy_headers_hash_max_size {{ $cfg.ProxyHeadersHashMaxSize }}; - proxy_headers_hash_bucket_size {{ $cfg.ProxyHeadersHashBucketSize }}; - - variables_hash_bucket_size {{ $cfg.VariablesHashBucketSize }}; - variables_hash_max_size {{ $cfg.VariablesHashMaxSize }}; - - underscores_in_headers {{ if $cfg.EnableUnderscoresInHeaders }}on{{ else }}off{{ end }}; - ignore_invalid_headers {{ if $cfg.IgnoreInvalidHeaders }}on{{ else }}off{{ end }}; - - limit_req_status {{ $cfg.LimitReqStatusCode }}; - limit_conn_status {{ $cfg.LimitConnStatusCode }}; - - {{ buildOpentelemetry $cfg $servers }} - - include /etc/nginx/mime.types; - default_type {{ $cfg.DefaultType }}; - - {{ if $cfg.EnableBrotli }} - brotli on; - brotli_comp_level {{ $cfg.BrotliLevel }}; - brotli_min_length {{ $cfg.BrotliMinLength }}; - brotli_types {{ $cfg.BrotliTypes }}; - {{ end }} - - {{ if $cfg.UseGzip }} - gzip on; - gzip_comp_level {{ $cfg.GzipLevel }}; - {{- if $cfg.GzipDisable }} - gzip_disable "{{ $cfg.GzipDisable }}"; - {{- end }} - gzip_http_version 1.1; - gzip_min_length {{ $cfg.GzipMinLength}}; - gzip_types {{ $cfg.GzipTypes }}; - gzip_proxied any; - gzip_vary on; - {{ end }} - - # Custom headers for response - {{ range $k, $v := $addHeaders }} - more_set_headers {{ printf "%s: %s" $k $v | quote }}; - {{ end }} - - server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }}; - {{ if not $cfg.ShowServerTokens }} - more_clear_headers Server; - {{ end }} - - # disable warnings - uninitialized_variable_warn off; - - # Additional available variables: - # $namespace - # $ingress_name - # $service_name - # $service_port - log_format upstreaminfo {{ if $cfg.LogFormatEscapeNone }}escape=none {{ else if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ $cfg.LogFormatUpstream }}'; - - {{/* map urls that should not appear in access.log */}} - {{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}} - map $request_uri $loggable { - {{ range $reqUri := $cfg.SkipAccessLogURLs }} - {{ $reqUri }} 0;{{ end }} - default 1; - } - - {{ if or $cfg.DisableAccessLog $cfg.DisableHTTPAccessLog }} - access_log off; - {{ else }} - {{ if $cfg.EnableSyslog }} - access_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} upstreaminfo if=$loggable; - {{ else }} - access_log {{ or $cfg.HTTPAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; - {{ end }} - {{ end }} - - {{ if $cfg.EnableSyslog }} - error_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} {{ $cfg.ErrorLogLevel }}; - {{ else }} - error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; - {{ end }} - - {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} - - # See https://www.nginx.com/blog/websocket-nginx - map $http_upgrade $connection_upgrade { - default upgrade; - {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} - # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive - '' ''; - {{ else }} - '' close; - {{ end }} - } - - # Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server. - # If no such header is provided, it can provide a random value. - map $http_x_request_id $req_id { - default $http_x_request_id; - {{ if $cfg.GenerateRequestID }} - "" $request_id; - {{ end }} - } - - {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} - # We can't use $proxy_add_x_forwarded_for because the realip module - # replaces the remote_addr too soon - map $http_x_forwarded_for $full_x_forwarded_for { - {{ if $all.Cfg.UseProxyProtocol }} - default "$http_x_forwarded_for, $proxy_protocol_addr"; - '' "$proxy_protocol_addr"; - {{ else }} - default "$http_x_forwarded_for, $realip_remote_addr"; - '' "$realip_remote_addr"; - {{ end}} - } - - {{ end }} - - # Create a variable that contains the literal $ character. - # This works because the geo module will not resolve variables. - geo $literal_dollar { - default "$"; - } - - server_name_in_redirect off; - port_in_redirect off; - - ssl_protocols {{ $cfg.SSLProtocols }}; - - ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; - - # turn on session caching to drastically improve performance - {{ if $cfg.SSLSessionCache }} - ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; - ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; - {{ end }} - - # allow configuring ssl session tickets - ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; - - {{ if not (empty $cfg.SSLSessionTicketKey ) }} - ssl_session_ticket_key /etc/ingress-controller/tickets.key; - {{ end }} - - # slightly reduce the time-to-first-byte - ssl_buffer_size {{ $cfg.SSLBufferSize }}; - - {{ if not (empty $cfg.SSLCiphers) }} - # allow configuring custom ssl ciphers - ssl_ciphers '{{ $cfg.SSLCiphers }}'; - ssl_prefer_server_ciphers on; - {{ end }} - - {{ if not (empty $cfg.SSLDHParam) }} - # allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam - ssl_dhparam {{ $cfg.SSLDHParam }}; - {{ end }} - - ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; - - # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} - ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; - ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; - - {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} - proxy_intercept_errors on; - {{ end }} - - {{ range $errCode := $cfg.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} - - proxy_ssl_session_reuse on; - - {{ if $cfg.AllowBackendServerHeader }} - proxy_pass_header Server; - {{ end }} - - {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; - {{ end }} - - {{ if not (empty $cfg.HTTPSnippet) }} - # Custom code snippet configured in the configuration configmap - {{ $cfg.HTTPSnippet }} - {{ end }} - - upstream upstream_balancer { - ### Attention!!! - # - # We no longer create "upstream" section for every backend. - # Backends are handled dynamically using Lua. If you would like to debug - # and see what backends ingress-nginx has in its memory you can - # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin. - # Once you have the plugin you can use "kubectl ingress-nginx backends" command to - # inspect current backends. - # - ### - - server 0.0.0.1; # placeholder - - balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer.lua; - - {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} - keepalive {{ $cfg.UpstreamKeepaliveConnections }}; - keepalive_time {{ $cfg.UpstreamKeepaliveTime }}; - keepalive_timeout {{ $cfg.UpstreamKeepaliveTimeout }}s; - keepalive_requests {{ $cfg.UpstreamKeepaliveRequests }}; - {{ end }} - } - - {{ range $rl := (filterRateLimits $servers ) }} - # Ratelimit {{ $rl.Name }} - geo $remote_addr $allowlist_{{ $rl.ID }} { - default 0; - {{ range $ip := $rl.Allowlist }} - {{ $ip }} 1;{{ end }} - } - - # Ratelimit {{ $rl.Name }} - map $allowlist_{{ $rl.ID }} $limit_{{ $rl.ID }} { - 0 {{ $cfg.LimitConnZoneVariable }}; - 1 ""; - } - {{ end }} - - {{/* 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 $servers) }} - {{ $zone }} - {{ end }} - - # Cache for internal auth checks - proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; - - # Global filters - {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - map $http_user_agent $block_ua { - default 0; - - {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; - {{ end }} - } - {{ end }} - - {{ if gt (len $cfg.BlockReferers) 0 }} - map $http_referer $block_ref { - default 0; - - {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; - {{ end }} - } - {{ end }} - - {{/* Build server redirects (from/to www) */}} - {{ range $redirect := .RedirectServers }} - ## start server {{ $redirect.From }} - server { - server_name {{ $redirect.From }}; - - {{ buildHTTPListener $all $redirect.From }} - {{ buildHTTPSListener $all $redirect.From }} - - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { - return 403; - } - {{ end }} - {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { - return 403; - } - {{ end }} - - set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; - - return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; - } - ## end server {{ $redirect.From }} - {{ end }} - - {{ range $server := $servers }} - {{ range $location := $server.Locations }} - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} - upstream {{ buildAuthUpstreamName $location $server.Hostname }} { - {{- $externalAuth := $location.ExternalAuth }} - server {{ extractHostPort $externalAuth.URL }}; - - keepalive {{ $externalAuth.KeepaliveConnections }}; - keepalive_requests {{ $externalAuth.KeepaliveRequests }}; - keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; - } - ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} - {{ end }} - {{ end }} - {{ end }} - - {{ range $server := $servers }} - ## start server {{ $server.Hostname }} - server { - server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; - - {{ if $cfg.UseHTTP2 }} - http2 on; - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { - return 403; - } - {{ end }} - {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { - return 403; - } - {{ end }} - - {{ template "SERVER" serverConfig $all $server }} - - {{ if not (empty $cfg.ServerSnippet) }} - # Custom code snippet configured in the configuration configmap - {{ $cfg.ServerSnippet }} - {{ end }} - - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics $cfg.EnableModsecurity) }} - } - ## end server {{ $server.Hostname }} - - {{ end }} - - # backend for when default-backend-service is not configured or it does not have endpoints - server { - listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; - {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} - set $proxy_upstream_name "internal"; - - access_log off; - - location / { - return 404; - } - } - - # default server, used for NGINX healthcheck and access to nginx stats - server { - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; - {{ end }} - - listen 127.0.0.1:{{ .StatusPort }}; - set $proxy_upstream_name "internal"; - - keepalive_timeout 0; - gzip off; - - access_log off; - - {{ if $cfg.EnableOpentelemetry }} - opentelemetry off; - {{ end }} - location {{ $healthzURI }} { - return 200; - } - - location /is-dynamic-lb-initialized { - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; - } - - location {{ .StatusPath }} { - stub_status on; - } - - location /configuration { - client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; - client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; - proxy_buffering off; - - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; - } - - location / { - return 404; - } - } -} - -stream { - lua_package_path "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;"; - - lua_shared_dict tcp_udp_configuration_data 5M; - - {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} - - init_by_lua_file /etc/nginx/lua/ngx_conf_init_stream.lua; - - init_worker_by_lua_file /etc/nginx/lua/nginx/ngx_conf_init_tcp_udp.lua; - - lua_add_variable $proxy_upstream_name; - - log_format log_stream '{{ $cfg.LogFormatStream }}'; - - {{ if or $cfg.DisableAccessLog $cfg.DisableStreamAccessLog }} - access_log off; - {{ else }} - access_log {{ or $cfg.StreamAccessLogPath $cfg.AccessLogPath }} log_stream {{ $cfg.AccessLogParams }}; - {{ end }} - - - error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; - {{ if $cfg.EnableRealIP }} - {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} - set_real_ip_from {{ $trusted_ip }}; - {{ end }} - {{ end }} - - upstream upstream_balancer { - server 0.0.0.1:1234; # placeholder - balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer_tcp_udp.lua; - } - - server { - listen 127.0.0.1:{{ .StreamPort }}; - - access_log off; - - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_content_tcp_udp.lua; - } - - # TCP services - {{ range $tcpServer := .TCPBackends }} - server { - preread_by_lua_block { - ngx.var.proxy_upstream_name="tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}"; - } - - {{ range $address := $all.Cfg.BindAddressIpv4 }} - listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ else }} - listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ end }} - {{ if $IsIPV6Enabled }} - {{ range $address := $all.Cfg.BindAddressIpv6 }} - listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ else }} - listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }}; - {{ end }} - {{ end }} - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; - proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; - proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; - - proxy_pass upstream_balancer; - {{ if $tcpServer.Backend.ProxyProtocol.Encode }} - proxy_protocol on; - {{ end }} - } - {{ end }} - - # UDP services - {{ range $udpServer := .UDPBackends }} - server { - preread_by_lua_block { - ngx.var.proxy_upstream_name="udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}"; - } - - {{ range $address := $all.Cfg.BindAddressIpv4 }} - listen {{ $address }}:{{ $udpServer.Port }} udp; - {{ else }} - listen {{ $udpServer.Port }} udp; - {{ end }} - {{ if $IsIPV6Enabled }} - {{ range $address := $all.Cfg.BindAddressIpv6 }} - listen {{ $address }}:{{ $udpServer.Port }} udp; - {{ else }} - listen [::]:{{ $udpServer.Port }} udp; - {{ end }} - {{ end }} - proxy_responses {{ $cfg.ProxyStreamResponses }}; - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; - proxy_next_upstream {{ if $cfg.ProxyStreamNextUpstream }}on{{ else }}off{{ end }}; - proxy_next_upstream_timeout {{ $cfg.ProxyStreamNextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $cfg.ProxyStreamNextUpstreamTries }}; - proxy_pass upstream_balancer; - } - {{ end }} - - # Stream Snippets - {{ range $snippet := .StreamSnippets }} - {{ $snippet }} - {{ end }} -} - -{{/* definition of templates to avoid repetitions */}} -{{ define "CUSTOM_ERRORS" }} - {{ $enableMetrics := .EnableMetrics }} - {{ $modsecurityEnabled := .ModsecurityEnabled }} - {{ $upstreamName := .UpstreamName }} - {{ range $errCode := .ErrorCodes }} - location @custom_{{ $upstreamName }}_{{ $errCode }} { - internal; - - # Ensure that modsecurity will not run on custom error pages or they might be blocked - {{ if $modsecurityEnabled }} - modsecurity off; - {{ end }} - - proxy_intercept_errors off; - - proxy_set_header X-Code {{ $errCode }}; - proxy_set_header X-Format $http_accept; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Namespace $namespace; - proxy_set_header X-Ingress-Name $ingress_name; - proxy_set_header X-Service-Name $service_name; - proxy_set_header X-Service-Port $service_port; - proxy_set_header X-Request-ID $req_id; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $best_http_host; - - set $proxy_upstream_name {{ $upstreamName | quote }}; - - rewrite (.*) / break; - - proxy_pass http://upstream_balancer; - {{ if $enableMetrics }} - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; - {{ end }} - } - {{ end }} -{{ end }} - -{{/* 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 }}'; - } - - 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; - } -{{ end }} - -{{/* definition of server-template to avoid repetitions with server-alias */}} -{{ define "SERVER" }} - {{ $all := .First }} - {{ $server := .Second }} - - {{ buildHTTPListener $all $server.Hostname }} - {{ buildHTTPSListener $all $server.Hostname }} - - set $proxy_upstream_name "-"; - - {{ if not ( empty $server.CertificateAuth.MatchCN ) }} - {{ if gt (len $server.CertificateAuth.MatchCN) 0 }} - if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { - return 403 "client certificate unauthorized"; - } - {{ end }} - {{ end }} - - {{ if eq $server.Hostname "_" }} - ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; - {{ end }} - - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; - - {{ if not (empty $server.AuthTLSError) }} - # {{ $server.AuthTLSError }} - return 403; - {{ else }} - - {{ if not (empty $server.CertificateAuth.CAFileName) }} - # PEM sha: {{ $server.CertificateAuth.CASHA }} - ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; - ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; - ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; - - {{ if not (empty $server.CertificateAuth.CRLFileName) }} - # PEM sha: {{ $server.CertificateAuth.CRLSHA }} - ssl_crl {{ $server.CertificateAuth.CRLFileName }}; - {{ end }} - - {{ if not (empty $server.CertificateAuth.ErrorPage)}} - error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; - {{ end }} - {{ end }} - - {{ if not (empty $server.ProxySSL.CAFileName) }} - # PEM sha: {{ $server.ProxySSL.CASHA }} - proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; - proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; - proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; - proxy_ssl_verify {{ $server.ProxySSL.Verify }}; - proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; - {{ if not (empty $server.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; - proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; - {{ end }} - {{ end }} - - {{ if not (empty $server.ProxySSL.PemFileName) }} - proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; - proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; - {{ end }} - - {{ if not (empty $server.SSLCiphers) }} - ssl_ciphers {{ $server.SSLCiphers }}; - {{ end }} - - {{ if not (empty $server.SSLPreferServerCiphers) }} - ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; - {{ end }} - - {{ if not (empty $server.ServerSnippet) }} - # Custom code snippet configured for host {{ $server.Hostname }} - {{ $server.ServerSnippet }} - {{ end }} - - {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics $all.Cfg.EnableModsecurity) }} - {{ end }} - - {{ buildMirrorLocations $server.Locations }} - - {{ $enforceRegex := enforceRegexModifier $server.Locations }} - {{ range $location := $server.Locations }} - {{ $path := buildLocation $location $enforceRegex }} - {{ $proxySetHeader := proxySetHeader $location }} - {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} - - {{ $externalAuth := $location.ExternalAuth }} - {{ if eq $applyGlobalAuth true }} - {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} - {{ end }} - - {{ if not (empty $location.Rewrite.AppRoot) }} - if ($uri = /) { - return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; - } - {{ end }} - - {{ if $authPath }} - location = {{ $authPath }} { - internal; - - {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} - opentelemetry on; - opentelemetry_propagate; - {{ end }} - - {{ if not $all.Cfg.EnableAuthAccessLog }} - access_log off; - {{ end }} - - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; - {{ end }} - - {{ if $externalAuth.AuthCacheKey }} - set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; - set $cache_key ''; - - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; - - proxy_cache auth_cache; - - {{- range $dur := $externalAuth.AuthCacheDuration }} - proxy_cache_valid {{ $dur }}; - {{- end }} - - proxy_cache_key "$cache_key"; - {{ end }} - - # ngx_auth_request module overrides variables in the parent request, - # therefore we have to explicitly set this variable again so that when the parent request - # resumes it has the correct value set for this variable so that Lua can pick backend correctly - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; - - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - proxy_set_header X-Forwarded-Proto ""; - proxy_set_header X-Request-ID $req_id; - - {{ if $externalAuth.Method }} - proxy_method {{ $externalAuth.Method }}; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Scheme $pass_access_scheme; - {{ end }} - - proxy_set_header Host {{ $externalAuth.Host }}; - proxy_set_header X-Original-URL $scheme://$http_host$request_uri; - proxy_set_header X-Original-Method $request_method; - proxy_set_header X-Sent-From "nginx-ingress-controller"; - proxy_set_header X-Real-IP $remote_addr; - {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - proxy_set_header X-Forwarded-For $full_x_forwarded_for; - {{ else }} - proxy_set_header X-Forwarded-For $remote_addr; - {{ end }} - - {{ if $externalAuth.RequestRedirect }} - proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; - {{ else }} - proxy_set_header X-Auth-Request-Redirect $request_uri; - {{ end }} - - {{ if $externalAuth.AuthCacheKey }} - proxy_buffering "on"; - {{ else }} - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; - {{ end }} - proxy_buffer_size {{ $location.Proxy.BufferSize }}; - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; - - proxy_ssl_server_name on; - proxy_pass_request_headers on; - {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; - {{ end }} - {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; - {{ end }} - - # Pass the extracted client certificate to the auth provider - {{ if not (empty $server.CertificateAuth.CAFileName) }} - {{ if $server.CertificateAuth.PassCertToUpstream }} - proxy_set_header ssl-client-cert $ssl_client_escaped_cert; - {{ end }} - proxy_set_header ssl-client-verify $ssl_client_verify; - proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; - proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; - {{ end }} - - {{- range $line := buildAuthProxySetHeaders $externalAuth.ProxySetHeaders}} - {{ $line }} - {{- end }} - - {{ if not (empty $externalAuth.AuthSnippet) }} - {{ $externalAuth.AuthSnippet }} - {{ end }} - - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} - # The target is an upstream with HTTP keepalive, that is why the - # Connection header is cleared and the HTTP version is set to 1.1 as - # the Nginx documentation suggests: - # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive - proxy_http_version 1.1; - proxy_set_header Connection ""; - set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; - {{ else }} - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; - set $target {{ $externalAuth.URL }}; - {{ end }} - proxy_pass $target; - } - {{ end }} - - {{ if isLocationAllowed $location }} - {{ if $externalAuth.SigninURL }} - location {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }} { - internal; - - add_header Set-Cookie $auth_cookie; - - {{ if $location.CorsConfig.CorsEnabled }} - {{ template "CORS" $location }} - {{ end }} - - # Ensure that modsecurity will not run on an internal location as this is not accessible from outside - {{ if $all.Cfg.EnableModsecurity }} - modsecurity off; - {{ end }} - - return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; - } - {{ end }} - {{ end }} - - location {{ $path }} { - {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.IngressPath) }} - set $namespace {{ $ing.Namespace | quote}}; - set $ingress_name {{ $ing.Rule | quote }}; - set $service_name {{ $ing.Service | quote }}; - set $service_port {{ $ing.ServicePort | quote }}; - set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; - - {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} - - {{ if $location.Mirror.Source }} - mirror {{ $location.Mirror.Source }}; - mirror_request_body {{ $location.Mirror.RequestBody }}; - {{ end }} - - {{ locationConfigForLua $location $all }} - - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; - - header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; - - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; - - {{ if not $location.Logs.Access }} - access_log off; - {{ end }} - - {{ if $location.Logs.Rewrite }} - rewrite_log on; - {{ end }} - - {{ if $location.HTTP2PushPreload }} - http2_push_preload on; - {{ end }} - - port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; - - set $balancer_ewma_score -1; - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; - set $proxy_host $proxy_upstream_name; - set $pass_access_scheme $scheme; - - {{ if $all.Cfg.UseProxyProtocol }} - set $pass_server_port $proxy_protocol_server_port; - {{ else }} - set $pass_server_port $server_port; - {{ end }} - - set $best_http_host $http_host; - set $pass_port $pass_server_port; - - set $proxy_alternative_upstream_name ""; - - {{ buildModSecurityForLocation $all.Cfg $location }} - - {{ if isLocationAllowed $location }} - {{ if gt (len $location.Denylist.CIDR) 0 }} - {{ range $ip := $location.Denylist.CIDR }} - deny {{ $ip }};{{ end }} - {{ end }} - {{ if gt (len $location.Allowlist.CIDR) 0 }} - {{ range $ip := $location.Allowlist.CIDR }} - allow {{ $ip }};{{ end }} - deny all; - {{ end }} - - {{ if $location.CorsConfig.CorsEnabled }} - {{ template "CORS" $location }} - {{ end }} - - {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} - {{ if $authPath }} - # this location requires authentication - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - set $auth_cookie ''; - add_header Set-Cookie $auth_cookie; - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} - {{ $line }} - {{- end }} - # `auth_request` module does not support HTTP keepalives in upstream block: - # https://trac.nginx.org/nginx/ticket/1579 - access_by_lua_block { - local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) - if res.status == ngx.HTTP_OK then - ngx.var.auth_cookie = res.header['Set-Cookie'] - {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} - {{ $line }} - {{- end }} - return - end - if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then - ngx.exit(res.status) - end - ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) - } - {{ else }} - auth_request {{ $authPath }}; - auth_request_set $auth_cookie $upstream_http_set_cookie; - {{ if $externalAuth.AlwaysSetCookie }} - add_header Set-Cookie $auth_cookie always; - {{ else }} - add_header Set-Cookie $auth_cookie; - {{ end }} - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} - {{ $line }} - {{- end }} - {{ end }} - {{ end }} - - {{ if $externalAuth.SigninURL }} - set_escape_uri $escaped_request_uri $request_uri; - error_page 401 = {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }}; - {{ end }} - - {{ if $location.BasicDigestAuth.Secured }} - {{ if eq $location.BasicDigestAuth.Type "basic" }} - auth_basic {{ $location.BasicDigestAuth.Realm | quote }}; - auth_basic_user_file {{ $location.BasicDigestAuth.File }}; - {{ else }} - auth_digest {{ $location.BasicDigestAuth.Realm | quote }}; - auth_digest_user_file {{ $location.BasicDigestAuth.File }}; - {{ end }} - {{ $proxySetHeader }} Authorization ""; - {{ end }} - {{ end }} - - {{/* if the location contains a rate limit annotation, create one */}} - {{ $limits := buildRateLimit $location }} - {{ range $limit := $limits }} - {{ $limit }}{{ end }} - - {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; - {{ end }} - {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; - {{ end }} - - {{/* By default use vhost as Host to upstream, but allow overrides */}} - {{ if not (empty $location.UpstreamVhost) }} - {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; - {{ else }} - {{ $proxySetHeader }} Host $best_http_host; - {{ end }} - - # Pass the extracted client certificate to the backend - {{ if not (empty $server.CertificateAuth.CAFileName) }} - {{ if $server.CertificateAuth.PassCertToUpstream }} - {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; - {{ end }} - {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; - {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; - {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; - {{ end }} - - # Allow websocket connections - {{ $proxySetHeader }} Upgrade $http_upgrade; - {{ if $location.Connection.Enabled}} - {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; - {{ else }} - {{ $proxySetHeader }} Connection $connection_upgrade; - {{ end }} - - {{ $proxySetHeader }} X-Request-ID $req_id; - {{ $proxySetHeader }} X-Real-IP $remote_addr; - {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; - {{ else }} - {{ $proxySetHeader }} X-Forwarded-For $remote_addr; - {{ end }} - {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; - {{ $proxySetHeader }} X-Forwarded-Port $pass_port; - {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; - {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; - {{ if $all.Cfg.ProxyAddOriginalURIHeader }} - {{ $proxySetHeader }} X-Original-URI $request_uri; - {{ end }} - {{ $proxySetHeader }} X-Scheme $pass_access_scheme; - - # Pass the original X-Forwarded-For - {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; - - # mitigate HTTPoxy Vulnerability - # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ - {{ $proxySetHeader }} Proxy ""; - - # Custom headers to proxied server - {{ range $k, $v := $all.ProxySetHeaders }} - {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; - {{ end }} - - proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; - proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; - proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; - - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; - proxy_buffer_size {{ $location.Proxy.BufferSize }}; - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; - {{ if isValidByteSize $location.Proxy.ProxyMaxTempFileSize true }} - proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; - {{ end }} - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; - - 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 $all.Cfg.RetryNonIdempotent }}; - proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; - proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; - - {{ if or (eq $location.BackendProtocol "GRPC") (eq $location.BackendProtocol "GRPCS") }} - # Grpc settings - grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; - grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; - grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; - {{ end }} - - {{/* Add any additional configuration defined */}} - {{ $location.ConfigurationSnippet }} - - {{ if not (empty $all.Cfg.LocationSnippet) }} - # Custom code snippet configured in the configuration configmap - {{ $all.Cfg.LocationSnippet }} - {{ end }} - - {{ if $location.CustomHeaders }} - # Custom Response Headers - {{ range $k, $v := $location.CustomHeaders.Headers }} - more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; - {{ end }} - {{ end }} - - {{/* if we are sending the request to a custom default backend, we add the required headers */}} - {{ if (hasPrefix $location.Backend "custom-default-backend-") }} - proxy_set_header X-Code 503; - proxy_set_header X-Format $http_accept; - proxy_set_header X-Namespace $namespace; - proxy_set_header X-Ingress-Name $ingress_name; - proxy_set_header X-Service-Name $service_name; - proxy_set_header X-Service-Port $service_port; - proxy_set_header X-Request-ID $req_id; - {{ end }} - - {{ if $location.Satisfy }} - satisfy {{ $location.Satisfy }}; - {{ end }} - - {{/* if a location-specific error override is set, add the proxy_intercept here */}} - {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} - # Custom error pages per ingress - proxy_intercept_errors on; - {{ end }} - - {{ range $errCode := $location.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} - - {{ if (eq $location.BackendProtocol "FCGI") }} - include /etc/nginx/fastcgi_params; - {{ end }} - {{- if $location.FastCGI.Index -}} - fastcgi_index {{ $location.FastCGI.Index | quote }}; - {{- end -}} - {{ range $k, $v := $location.FastCGI.Params }} - fastcgi_param {{ $k }} {{ $v | quote }}; - {{ end }} - - {{ if not (empty $location.Redirect.URL) }} - return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; - {{ end }} - - {{ buildProxyPass $server.Hostname $all.Backends $location }} - {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; - {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; - {{ end }} - {{ else }} - # Location denied. Reason: {{ $location.Denied | quote }} - return 503; - {{ end }} - {{ if not (empty $location.ProxySSL.CAFileName) }} - # PEM sha: {{ $location.ProxySSL.CASHA }} - proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; - proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; - proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; - proxy_ssl_verify {{ $location.ProxySSL.Verify }}; - proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; - {{ end }} - - {{ if not (empty $location.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; - {{ end }} - {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} - proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; - {{ end }} - - {{ if not (empty $location.ProxySSL.PemFileName) }} - proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; - proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; - {{ end }} - } - {{ end }} - {{ end }} - - {{ if eq $server.Hostname "_" }} - # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} - location {{ $all.HealthzURI }} { - - {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; - {{ end }} - - 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 { - - {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; - {{ end }} - - {{ range $v := $all.NginxStatusIpv4Whitelist }} - allow {{ $v }}; - {{ end }} - {{ if $all.IsIPV6Enabled -}} - {{ range $v := $all.NginxStatusIpv6Whitelist }} - allow {{ $v }}; - {{ end }} - {{ end -}} - deny all; - - access_log off; - stub_status on; - } - - {{ end }} - -{{ end }} diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl deleted file mode 100644 index 547fd12dac..0000000000 --- a/internal/ingress/controller/template/crossplane/testdata/nginx-new.tmpl +++ /dev/null @@ -1,1268 +0,0 @@ -{{ $all := . }} -{{ $servers := .Servers }} -{{ $cfg := .Cfg }} -{{ $IsIPV6Enabled := .IsIPV6Enabled }} -{{ $healthzURI := .HealthzURI }} -{{ $backends := .Backends }} -{{ $proxyHeaders := .ProxySetHeaders }} -{{ $addHeaders := .AddHeaders }} - -# Configuration checksum: {{ $all.Cfg.Checksum }} - -# setup custom paths that do not require root access -pid {{ .PID }}; # OK - -{{ if $cfg.UseGeoIP2 }} -load_module /etc/nginx/modules/ngx_http_geoip2_module.so; # OK -{{ end }} - -{{ if $cfg.EnableBrotli }} -load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; # OK -load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; # OK -{{ end }} - -{{ if (shouldLoadAuthDigestModule $servers) }} -load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; # OK -{{ end }} - -{{ if (shouldLoadOpentelemetryModule $cfg $servers) }} -load_module /etc/nginx/modules/otel_ngx_module.so; # OK -{{ end }} - -daemon off; # OK - -worker_processes {{ $cfg.WorkerProcesses }}; # OK -{{ if gt (len $cfg.WorkerCPUAffinity) 0 }} -worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; # OK -{{ end }} - -worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; # OK - -{{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}} -{{/* avoid waiting too long during a reload */}} -worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; # OK - -events { - multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; # OK - worker_connections {{ $cfg.MaxWorkerConnections }}; # OK - use epoll; # OK - {{ range $index , $v := $cfg.DebugConnections }} - debug_connection {{ $v }}; # OK - {{ end }} -} - -http { - {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} - opentelemetry_config {{ $cfg.OpentelemetryConfig }}; # OK - {{ end }} - - lua_package_path "/etc/nginx/lua/?.lua;;"; # OK - - {{ buildLuaSharedDictionaries $cfg $servers }} # OK - - lua_shared_dict luaconfig 5m; # OK - - init_by_lua_file /etc/nginx/lua/ngx_conf_init.lua; # OK - - init_worker_by_lua_file /etc/nginx/lua/ngx_conf_init_worker.lua; # OK - - {{/* 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 }} - {{ if $cfg.UseProxyProtocol }} - real_ip_header proxy_protocol; # OK - {{ else }} - real_ip_header {{ $cfg.ForwardedForHeader }}; # OK - {{ end }} - - real_ip_recursive on; # OK - {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }} - set_real_ip_from {{ $trusted_ip }}; # OK - {{ end }} - {{ end }} - - {{ if $cfg.UseGeoIP2 }} - # https://github.com/leev/ngx_http_geoip2_module#example-usage - - {{ range $index, $file := $all.MaxmindEditionFiles }} - {{ if eq $file "GeoLite2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-Country.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; # OK - $geoip2_country_name source=$remote_addr country names en; # OK - $geoip2_country_geoname_id source=$remote_addr country geoname_id; # OK - $geoip2_continent_code source=$remote_addr continent code; # OK - $geoip2_continent_name source=$remote_addr continent names en; # OK - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; # OK - } - {{ end }} - - {{ if eq $file "GeoIP2-Country.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Country.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_country_code source=$remote_addr country iso_code; # OK - $geoip2_country_name source=$remote_addr country names en; # OK - $geoip2_country_geoname_id source=$remote_addr country geoname_id; # OK - $geoip2_continent_code source=$remote_addr continent code; # OK - $geoip2_continent_name source=$remote_addr continent names en; # OK - $geoip2_continent_geoname_id source=$remote_addr continent geoname_id; # OK - } - {{ end }} - - {{ if eq $file "GeoLite2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-City.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; # OK - $geoip2_city_country_name source=$remote_addr country names en; # OK - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; # OK - $geoip2_city source=$remote_addr city names en; # OK - $geoip2_city_geoname_id source=$remote_addr city geoname_id; # OK - $geoip2_postal_code source=$remote_addr postal code; # OK - $geoip2_dma_code source=$remote_addr location metro_code; # OK - $geoip2_latitude source=$remote_addr location latitude; # OK - $geoip2_longitude source=$remote_addr location longitude; # OK - $geoip2_time_zone source=$remote_addr location time_zone; # OK - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; # OK - $geoip2_region_name source=$remote_addr subdivisions 0 names en; # OK - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; # OK - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; # OK - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; # OK - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; # OK - $geoip2_city_continent_code source=$remote_addr continent code; # OK - $geoip2_city_continent_name source=$remote_addr continent names en; # OK - } - {{ end }} - - {{ if eq $file "GeoIP2-City.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-City.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_city_country_code source=$remote_addr country iso_code; # OK - $geoip2_city_country_name source=$remote_addr country names en; # OK - $geoip2_city_country_geoname_id source=$remote_addr country geoname_id; # OK - $geoip2_city source=$remote_addr city names en; # OK - $geoip2_city_geoname_id source=$remote_addr city geoname_id; # OK - $geoip2_postal_code source=$remote_addr postal code; # OK - $geoip2_dma_code source=$remote_addr location metro_code; # OK - $geoip2_latitude source=$remote_addr location latitude; # OK - $geoip2_longitude source=$remote_addr location longitude; # OK - $geoip2_time_zone source=$remote_addr location time_zone; # OK - $geoip2_region_code source=$remote_addr subdivisions 0 iso_code; # OK - $geoip2_region_name source=$remote_addr subdivisions 0 names en; # OK - $geoip2_region_geoname_id source=$remote_addr subdivisions 0 geoname_id; # OK - $geoip2_subregion_code source=$remote_addr subdivisions 1 iso_code; # OK - $geoip2_subregion_name source=$remote_addr subdivisions 1 names en; # OK - $geoip2_subregion_geoname_id source=$remote_addr subdivisions 1 geoname_id; # OK - $geoip2_city_continent_code source=$remote_addr continent code; # OK - $geoip2_city_continent_name source=$remote_addr continent names en; # OK - } - {{ end }} - - {{ if eq $file "GeoLite2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoLite2-ASN.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; # OK - $geoip2_org source=$remote_addr autonomous_system_organization; # OK - } - {{ end }} - - {{ if eq $file "GeoIP2-ASN.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ASN.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_asn source=$remote_addr autonomous_system_number; # OK - $geoip2_org source=$remote_addr autonomous_system_organization; # OK - } - {{ end }} - - {{ if eq $file "GeoIP2-ISP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-ISP.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_isp source=$remote_addr isp; # OK - $geoip2_isp_org source=$remote_addr organization; # OK - $geoip2_asn source=$remote_addr default=0 autonomous_system_number; # OK - } - {{ end }} - - {{ if eq $file "GeoIP2-Connection-Type.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Connection-Type.mmdb { - $geoip2_connection_type connection_type; - } - {{ end }} - - {{ if eq $file "GeoIP2-Anonymous-IP.mmdb" }} - geoip2 /etc/ingress-controller/geoip/GeoIP2-Anonymous-IP.mmdb { # OK - {{ if (gt $cfg.GeoIP2AutoReloadMinutes 0) }} - auto_reload {{ $cfg.GeoIP2AutoReloadMinutes }}m; # OK - {{ end }} - $geoip2_is_anon source=$remote_addr is_anonymous; # OK - $geoip2_is_anonymous source=$remote_addr default=0 is_anonymous; # OK - $geoip2_is_anonymous_vpn source=$remote_addr default=0 is_anonymous_vpn; # OK - $geoip2_is_hosting_provider source=$remote_addr default=0 is_hosting_provider; # OK - $geoip2_is_public_proxy source=$remote_addr default=0 is_public_proxy; # OK - $geoip2_is_tor_exit_node source=$remote_addr default=0 is_tor_exit_node; # OK - } - {{ end }} - - {{ end }} - - {{ end }} - - aio threads; # OK - - {{ if $cfg.EnableAioWrite }} - aio_write on; # OK - {{ end }} - - tcp_nopush on; # OK - tcp_nodelay on; # OK - - log_subrequest on; # OK - - reset_timedout_connection on; # OK - - keepalive_timeout {{ $cfg.KeepAlive }}s; # OK - keepalive_requests {{ $cfg.KeepAliveRequests }}; # OK - - client_body_temp_path /tmp/nginx/client-body; # OK - fastcgi_temp_path /tmp/nginx/fastcgi-temp; # OK - proxy_temp_path /tmp/nginx/proxy-temp; # OK - - client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }}; # OK - client_header_timeout {{ $cfg.ClientHeaderTimeout }}s; # OK - large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }}; # OK - client_body_buffer_size {{ $cfg.ClientBodyBufferSize }}; # OK - client_body_timeout {{ $cfg.ClientBodyTimeout }}s; # OK - - {{ if gt $cfg.GRPCBufferSizeKb 0 }} - grpc_buffer_size {{ $cfg.GRPCBufferSizeKb }}k; # OK - {{ end }} - - {{ if and (ne $cfg.HTTP2MaxHeaderSize "") (ne $cfg.HTTP2MaxFieldSize "") }} # OK - http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }}; # OK - http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }}; # OK - {{ end }} - - {{ if (gt $cfg.HTTP2MaxRequests 0) }} # OK - http2_max_requests {{ $cfg.HTTP2MaxRequests }}; # OK - {{ end }} - - http2_max_concurrent_streams {{ $cfg.HTTP2MaxConcurrentStreams }}; # OK - - types_hash_max_size 2048; # OK - server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }}; # OK - server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }}; # OK - map_hash_bucket_size {{ $cfg.MapHashBucketSize }}; # OK - - proxy_headers_hash_max_size {{ $cfg.ProxyHeadersHashMaxSize }}; # OK - proxy_headers_hash_bucket_size {{ $cfg.ProxyHeadersHashBucketSize }}; # OK - - variables_hash_bucket_size {{ $cfg.VariablesHashBucketSize }}; # OK - variables_hash_max_size {{ $cfg.VariablesHashMaxSize }}; # OK - - underscores_in_headers {{ if $cfg.EnableUnderscoresInHeaders }}on{{ else }}off{{ end }}; # OK - ignore_invalid_headers {{ if $cfg.IgnoreInvalidHeaders }}on{{ else }}off{{ end }}; # OK - - limit_req_status {{ $cfg.LimitReqStatusCode }}; # OK - limit_conn_status {{ $cfg.LimitConnStatusCode }}; # OK - - {{ buildOpentelemetry $cfg $servers }} - - include /etc/nginx/mime.types; # OK - default_type {{ $cfg.DefaultType }}; # OK - - {{ if $cfg.EnableBrotli }} - brotli on; # OK - brotli_comp_level {{ $cfg.BrotliLevel }}; # OK - brotli_min_length {{ $cfg.BrotliMinLength }}; # OK - brotli_types {{ $cfg.BrotliTypes }}; # OK - {{ end }} - - {{ if $cfg.UseGzip }} - gzip on; # OK - gzip_comp_level {{ $cfg.GzipLevel }}; # OK - {{- if $cfg.GzipDisable }} - gzip_disable "{{ $cfg.GzipDisable }}"; # OK - {{- end }} - gzip_http_version 1.1; # OK - gzip_min_length {{ $cfg.GzipMinLength}}; # OK - gzip_types {{ $cfg.GzipTypes }}; # OK - gzip_proxied any; # OK - gzip_vary on; # OK - {{ end }} - - # Custom headers for response - {{ range $k, $v := $addHeaders }} - more_set_headers {{ printf "%s: %s" $k $v | quote }}; # OK - {{ end }} - - server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }}; # OK - {{ if not $cfg.ShowServerTokens }} - more_clear_headers Server; # OK - {{ end }} - - # disable warnings - uninitialized_variable_warn off; # OK - - # Additional available variables: - # $namespace - # $ingress_name - # $service_name - # $service_port - log_format upstreaminfo {{ if $cfg.LogFormatEscapeNone }}escape=none {{ else if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ $cfg.LogFormatUpstream }}'; # OK - - {{/* map urls that should not appear in access.log */}} - {{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}} - map $request_uri $loggable { # OK - {{ range $reqUri := $cfg.SkipAccessLogURLs }} - {{ $reqUri }} 0;{{ end }} # OK - default 1; # OK - } - - {{ if or $cfg.DisableAccessLog $cfg.DisableHTTPAccessLog }} - access_log off; # OK - {{ else }} - {{ if $cfg.EnableSyslog }} - access_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} upstreaminfo if=$loggable; # OK - {{ else }} - access_log {{ or $cfg.HTTPAccessLogPath $cfg.AccessLogPath }} upstreaminfo {{ $cfg.AccessLogParams }} if=$loggable; # OK - {{ end }} - {{ end }} - - {{ if $cfg.EnableSyslog }} - error_log syslog:server={{ $cfg.SyslogHost }}:{{ $cfg.SyslogPort }} {{ $cfg.ErrorLogLevel }}; # OK - {{ else }} - error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }}; # OK - {{ end }} - - {{ buildResolvers $cfg.Resolver $cfg.DisableIpv6DNS }} # OK - - # See https://www.nginx.com/blog/websocket-nginx - map $http_upgrade $connection_upgrade { # OK - default upgrade; # OK - {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} - # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive - '' ''; # OK - {{ else }} - '' close; # OK - {{ end }} - } - - # Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server. - # If no such header is provided, it can provide a random value. - map $http_x_request_id $req_id { # OK - default $http_x_request_id; # OK - {{ if $cfg.GenerateRequestID }} - "" $request_id; # OK - {{ end }} - } - - {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} - # We can't use $proxy_add_x_forwarded_for because the realip module - # replaces the remote_addr too soon - map $http_x_forwarded_for $full_x_forwarded_for { # OK - {{ if $all.Cfg.UseProxyProtocol }} - default "$http_x_forwarded_for, $proxy_protocol_addr"; # OK - '' "$proxy_protocol_addr"; # OK - {{ else }} - default "$http_x_forwarded_for, $realip_remote_addr"; # OK - '' "$realip_remote_addr"; # OK - {{ end}} - } - - {{ end }} - - # Create a variable that contains the literal $ character. - # This works because the geo module will not resolve variables. - geo $literal_dollar { # OK - default "$"; # OK - } - - server_name_in_redirect off; # OK - port_in_redirect off; # OK - - ssl_protocols {{ $cfg.SSLProtocols }}; # OK - - ssl_early_data {{ if $cfg.SSLEarlyData }}on{{ else }}off{{ end }}; # OK - - # turn on session caching to drastically improve performance - {{ if $cfg.SSLSessionCache }} - ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; # OK - ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; # OK - {{ end }} - - # allow configuring ssl session tickets - ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }}; # OK - - {{ if not (empty $cfg.SSLSessionTicketKey ) }} - ssl_session_ticket_key /etc/ingress-controller/tickets.key; # OK - {{ end }} - - # slightly reduce the time-to-first-byte - ssl_buffer_size {{ $cfg.SSLBufferSize }}; # OK - - {{ if not (empty $cfg.SSLCiphers) }} - # allow configuring custom ssl ciphers - ssl_ciphers '{{ $cfg.SSLCiphers }}'; # OK - ssl_prefer_server_ciphers on; # OK - {{ end }} - - {{ if not (empty $cfg.SSLDHParam) }} - # allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam - ssl_dhparam {{ $cfg.SSLDHParam }}; # OK - {{ end }} - - ssl_ecdh_curve {{ $cfg.SSLECDHCurve }}; # OK - - # PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }} - ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }}; # OK - ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }}; # OK - - {{ if and $cfg.CustomHTTPErrors (not $cfg.DisableProxyInterceptErrors) }} - proxy_intercept_errors on; # OK - {{ end }} - - {{ range $errCode := $cfg.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} # OK - - proxy_ssl_session_reuse on; # OK - - {{ if $cfg.AllowBackendServerHeader }} - proxy_pass_header Server; # OK - {{ end }} - - {{ range $header := $cfg.HideHeaders }}proxy_hide_header {{ $header }}; # OK - {{ end }} - - upstream upstream_balancer { - ### Attention!!! - # - # We no longer create "upstream" section for every backend. - # Backends are handled dynamically using Lua. If you would like to debug - # and see what backends ingress-nginx has in its memory you can - # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin. - # Once you have the plugin you can use "kubectl ingress-nginx backends" command to - # inspect current backends. - # - ### - - server 0.0.0.1; # OK - - balancer_by_lua_file /etc/nginx/lua/nginx/ngx_conf_balancer.lua; # OK - - {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }} - keepalive {{ $cfg.UpstreamKeepaliveConnections }}; # OK - keepalive_time {{ $cfg.UpstreamKeepaliveTime }}; # OK - keepalive_timeout {{ $cfg.UpstreamKeepaliveTimeout }}s; # OK - keepalive_requests {{ $cfg.UpstreamKeepaliveRequests }}; # OK - {{ end }} - } - - {{ range $rl := (filterRateLimits $servers ) }} - # Ratelimit {{ $rl.Name }} - geo $remote_addr $allowlist_{{ $rl.ID }} { - default 0; - {{ range $ip := $rl.Allowlist }} - {{ $ip }} 1;{{ end }} - } - - # Ratelimit {{ $rl.Name }} - map $allowlist_{{ $rl.ID }} $limit_{{ $rl.ID }} { - 0 {{ $cfg.LimitConnZoneVariable }}; - 1 ""; - } - {{ end }} - - {{/* 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 $servers) }} - {{ $zone }} - {{ end }} - - # Cache for internal auth checks - proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; # OK - - # Global filters - {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; # OK - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - map $http_user_agent $block_ua { # OK - default 0; # OK - - {{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1; # OK - {{ end }} - } - {{ end }} - - {{ if gt (len $cfg.BlockReferers) 0 }} - map $http_referer $block_ref { # OK - default 0; # OK - - {{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1; # OK - {{ end }} - } - {{ end }} - - {{/* Build server redirects (from/to www) */}} - {{ range $redirect := .RedirectServers }} - ## start server {{ $redirect.From }} - server { #OK - server_name {{ $redirect.From }}; # OK - - {{ buildHTTPListener $all $redirect.From }} # OK - {{ buildHTTPSListener $all $redirect.From }} # OK - - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; # OK - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { # OK - return 403; # OK - } - {{ end }} - {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { # OK - return 403; #OK - } - {{ end }} - - set_by_lua_file $redirect_to /etc/nginx/lua/nginx/ngx_srv_redirect.lua {{ $redirect.To }}; # OK - - return {{ $all.Cfg.HTTPRedirectCode }} $redirect_to; # OK - } - ## end server {{ $redirect.From }} - {{ end }} - - {{ range $server := $servers }} - {{ range $location := $server.Locations }} - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} - upstream {{ buildAuthUpstreamName $location $server.Hostname }} { - {{- $externalAuth := $location.ExternalAuth }} - server {{ extractHostPort $externalAuth.URL }}; - - keepalive {{ $externalAuth.KeepaliveConnections }}; - keepalive_requests {{ $externalAuth.KeepaliveRequests }}; - keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; - } - ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} - {{ end }} - {{ end }} - {{ end }} - - {{ range $server := $servers }} - ## start server {{ $server.Hostname }} - server { - server_name {{ buildServerName $server.Hostname }} {{range $server.Aliases }}{{ . }} {{ end }}; OK - - {{ if $cfg.UseHTTP2 }} - http2 on; OK - {{ end }} - - {{ if gt (len $cfg.BlockUserAgents) 0 }} - if ($block_ua) { OK - return 403; OK - } - {{ end }} - {{ if gt (len $cfg.BlockReferers) 0 }} - if ($block_ref) { OK - return 403; OK - } - {{ end }} - - {{ template "SERVER" serverConfig $all $server }} - - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps "upstream-default-backend" $cfg.CustomHTTPErrors $all.EnableMetrics) }} # OK - } - ## end server {{ $server.Hostname }} - - {{ end }} - - # backend for when default-backend-service is not configured or it does not have endpoints - server { # OK - listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}; # OK - {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }} # OK - set $proxy_upstream_name "internal"; # OK - - access_log off; # OK - - location / { # OK - return 404; # OK - } - } - - # default server, used for NGINX healthcheck and access to nginx stats - server { # OK - - listen 127.0.0.1:{{ .StatusPort }}; # OK - set $proxy_upstream_name "internal"; # OK - - keepalive_timeout 0; # OK - gzip off; # OK - - access_log off; # OK - - {{ if $cfg.EnableOpentelemetry }} - opentelemetry off; # OK - {{ end }} - location {{ $healthzURI }} { # OK - return 200; # OK - } - - location /is-dynamic-lb-initialized { # OK - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua; # OK - } - - location {{ .StatusPath }} { # OK - stub_status on; # OK - } - - location /configuration { # OK - client_max_body_size {{ luaConfigurationRequestBodySize $cfg }}; # OK - client_body_buffer_size {{ luaConfigurationRequestBodySize $cfg }}; # OK - proxy_buffering off; # OK - - content_by_lua_file /etc/nginx/lua/nginx/ngx_conf_configuration.lua; # OK - } - - location / { # OK - return 404; # OK - } - } -} - -{{/* definition of templates to avoid repetitions */}} -{{ define "CUSTOM_ERRORS" }} - {{ $enableMetrics := .EnableMetrics }} - {{ $upstreamName := .UpstreamName }} - {{ range $errCode := .ErrorCodes }} - location @custom_{{ $upstreamName }}_{{ $errCode }} { # OK - internal; # OK - proxy_intercept_errors off; # OK - - proxy_set_header X-Code {{ $errCode }}; # OK - proxy_set_header X-Format $http_accept; # OK - proxy_set_header X-Original-URI $request_uri; # OK - proxy_set_header X-Namespace $namespace; # OK - proxy_set_header X-Ingress-Name $ingress_name; # OK - proxy_set_header X-Service-Name $service_name;# OK - proxy_set_header X-Service-Port $service_port; # OK - proxy_set_header X-Request-ID $req_id;# OK - proxy_set_header X-Forwarded-For $remote_addr; # OK - proxy_set_header Host $best_http_host; # OK - - set $proxy_upstream_name {{ $upstreamName | quote }}; # OK - - rewrite (.*) / break; # OK - - proxy_pass http://upstream_balancer; # OK - {{ if $enableMetrics }} - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log.lua; # OK - {{ end }} - } - {{ end }} -{{ end }} - -{{/* 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') { # OK - set $cors ${cors}options; # OK - } - - if ($cors = "true") { # OK - more_set_headers 'Access-Control-Allow-Origin: $http_origin'; # OK - {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }} # OK - more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}'; # OK - more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}'; # OK - {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }} # OK - more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}'; # OK - } - - if ($cors = "trueoptions") { # OK - more_set_headers 'Access-Control-Allow-Origin: $http_origin'; # OK - {{ if $cors.CorsAllowCredentials }} more_set_headers 'Access-Control-Allow-Credentials: {{ $cors.CorsAllowCredentials }}'; {{ end }} # OK - more_set_headers 'Access-Control-Allow-Methods: {{ $cors.CorsAllowMethods }}'; # OK - more_set_headers 'Access-Control-Allow-Headers: {{ $cors.CorsAllowHeaders }}'; # OK - {{ if not (empty $cors.CorsExposeHeaders) }} more_set_headers 'Access-Control-Expose-Headers: {{ $cors.CorsExposeHeaders }}'; {{ end }} # OK - more_set_headers 'Access-Control-Max-Age: {{ $cors.CorsMaxAge }}'; # OK - more_set_headers 'Content-Type: text/plain charset=UTF-8'; # OK - more_set_headers 'Content-Length: 0'; # OK - return 204; # OK - } -{{ end }} - -{{/* definition of server-template to avoid repetitions with server-alias */}} -{{ define "SERVER" }} - {{ $all := .First }} - {{ $server := .Second }} - - {{ buildHTTPListener $all $server.Hostname }} # OK - {{ buildHTTPSListener $all $server.Hostname }} # OK - - set $proxy_upstream_name "-"; # OK - - {{ if not ( empty $server.CertificateAuth.MatchCN ) }} - {{ if gt (len $server.CertificateAuth.MatchCN) 0 }} - if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { # OK - return 403 "client certificate unauthorized"; # OK - } - {{ end }} - {{ end }} - - {{ if eq $server.Hostname "_" }} - ssl_reject_handshake {{ if $all.Cfg.SSLRejectHandshake }}on{{ else }}off{{ end }}; # OK - {{ end }} - - ssl_certificate_by_lua_file /etc/nginx/lua/nginx/ngx_conf_certificate.lua; # OK - - {{ if not (empty $server.AuthTLSError) }} - # {{ $server.AuthTLSError }} # NOT, WHERE THIS IF ENDS?? Couldn't reproduce this config on my ingress - return 403; - {{ else }} - - {{ if not (empty $server.CertificateAuth.CAFileName) }} - # PEM sha: {{ $server.CertificateAuth.CASHA }} - ssl_client_certificate {{ $server.CertificateAuth.CAFileName }}; # OK - ssl_verify_client {{ $server.CertificateAuth.VerifyClient }}; # OK - ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }}; # OK - - {{ if not (empty $server.CertificateAuth.CRLFileName) }} - # PEM sha: {{ $server.CertificateAuth.CRLSHA }} - ssl_crl {{ $server.CertificateAuth.CRLFileName }}; # OK - {{ end }} - - {{ if not (empty $server.CertificateAuth.ErrorPage)}} - error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }}; # OK - {{ end }} - {{ end }} - - {{ if not (empty $server.ProxySSL.CAFileName) }} - # PEM sha: {{ $server.ProxySSL.CASHA }} - proxy_ssl_trusted_certificate {{ $server.ProxySSL.CAFileName }}; # OK - proxy_ssl_ciphers {{ $server.ProxySSL.Ciphers }}; # OK - proxy_ssl_protocols {{ $server.ProxySSL.Protocols }}; # OK - proxy_ssl_verify {{ $server.ProxySSL.Verify }}; # OK - proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; # OK - {{ if not (empty $server.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; # OK - proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; # OK - {{ end }} - {{ end }} - - {{ if not (empty $server.ProxySSL.PemFileName) }} - proxy_ssl_certificate {{ $server.ProxySSL.PemFileName }}; # OK - proxy_ssl_certificate_key {{ $server.ProxySSL.PemFileName }}; # OK - {{ end }} - - {{ if not (empty $server.SSLCiphers) }} - ssl_ciphers {{ $server.SSLCiphers }}; # OK - {{ end }} - - {{ if not (empty $server.SSLPreferServerCiphers) }} - ssl_prefer_server_ciphers {{ $server.SSLPreferServerCiphers }}; # OK - {{ end }} - - {{ range $errorLocation := (buildCustomErrorLocationsPerServer $server) }} # OK - {{ template "CUSTOM_ERRORS" (buildCustomErrorDeps $errorLocation.UpstreamName $errorLocation.Codes $all.EnableMetrics) }} # OK - {{ end }} - - {{ buildMirrorLocations $server.Locations }} # OK - - {{ $enforceRegex := enforceRegexModifier $server.Locations }} # OK - {{ range $location := $server.Locations }} - {{ $path := buildLocation $location $enforceRegex }} # OK - {{ $proxySetHeader := proxySetHeader $location }} # OK - {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }} # OK - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} # OK - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} # OK - - {{ $externalAuth := $location.ExternalAuth }} # OK - {{ if eq $applyGlobalAuth true }} - {{ $externalAuth = $all.Cfg.GlobalExternalAuth }} # OK - {{ end }} - - {{ if not (empty $location.Rewrite.AppRoot) }} - if ($uri = /) { # OK - return 302 $scheme://$http_host{{ $location.Rewrite.AppRoot }}; # OK - } - {{ end }} - - {{ if $authPath }} - location = {{ $authPath }} { - internal; # OK - - {{ if (or $all.Cfg.EnableOpentelemetry $location.Opentelemetry.Enabled) }} - opentelemetry on; # OK - opentelemetry_propagate; # OK - {{ end }} - - {{ if not $all.Cfg.EnableAuthAccessLog }} - access_log off; # OK - {{ end }} - - {{ if $externalAuth.AuthCacheKey }} - set $tmp_cache_key '{{ $server.Hostname }}{{ $authPath }}{{ $externalAuth.AuthCacheKey }}'; # OK - set $cache_key ''; # OK - - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua; # OK - - proxy_cache auth_cache; # OK - - {{- range $dur := $externalAuth.AuthCacheDuration }} - proxy_cache_valid {{ $dur }}; # OK - {{- end }} - - proxy_cache_key "$cache_key"; # OK - {{ end }} - - # ngx_auth_request module overrides variables in the parent request, - # therefore we have to explicitly set this variable again so that when the parent request - # resumes it has the correct value set for this variable so that Lua can pick backend correctly - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; # OK - - proxy_pass_request_body off; # OK - proxy_set_header Content-Length ""; # OK - proxy_set_header X-Forwarded-Proto ""; # OK - proxy_set_header X-Request-ID $req_id; # OK - - {{ if $externalAuth.Method }} - proxy_method {{ $externalAuth.Method }}; - proxy_set_header X-Original-URI $request_uri; # OK - proxy_set_header X-Scheme $pass_access_scheme; # OK - {{ end }} - - proxy_set_header Host {{ $externalAuth.Host }}; # OK - proxy_set_header X-Original-URL $scheme://$http_host$request_uri; # OK - proxy_set_header X-Original-Method $request_method; # OK - proxy_set_header X-Sent-From "nginx-ingress-controller"; # OK - proxy_set_header X-Real-IP $remote_addr; # OK - {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - proxy_set_header X-Forwarded-For $full_x_forwarded_for; # OK - {{ else }} - proxy_set_header X-Forwarded-For $remote_addr; # OK - {{ end }} - - {{ if $externalAuth.RequestRedirect }} - proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }}; # OK - {{ else }} - proxy_set_header X-Auth-Request-Redirect $request_uri; # OK - {{ end }} - - {{ if $externalAuth.AuthCacheKey }} - proxy_buffering "on"; # OK - {{ else }} - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; # OK - {{ end }} - proxy_buffer_size {{ $location.Proxy.BufferSize }}; # OK - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; # OK - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; # OK - - proxy_ssl_server_name on; # OK - proxy_pass_request_headers on; # OK - {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; # OK - {{ end }} - {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; # OK - {{ end }} - - # Pass the extracted client certificate to the auth provider - {{ if not (empty $server.CertificateAuth.CAFileName) }} - {{ if $server.CertificateAuth.PassCertToUpstream }} - proxy_set_header ssl-client-cert $ssl_client_escaped_cert; # OK - {{ end }} - proxy_set_header ssl-client-verify $ssl_client_verify; # OK - proxy_set_header ssl-client-subject-dn $ssl_client_s_dn; # OK - proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn; # OK - {{ end }} - - {{- range $line := buildAuthProxySetHeaders $externalAuth.ProxySetHeaders}} - {{ $line }} # OK - {{- end }} - - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - {{ $authUpstreamName := buildAuthUpstreamName $location $server.Hostname }} - # The target is an upstream with HTTP keepalive, that is why the - # Connection header is cleared and the HTTP version is set to 1.1 as - # the Nginx documentation suggests: - # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive - proxy_http_version 1.1; # OK - proxy_set_header Connection ""; # OK - set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; # OK - {{ else }} - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; # OK - set $target {{ $externalAuth.URL }}; # OK - {{ end }} - proxy_pass $target; # OK - } - {{ end }} - - {{ if isLocationAllowed $location }} - {{ if $externalAuth.SigninURL }} - location {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }} { - internal; # OK - - add_header Set-Cookie $auth_cookie; # OK - - {{ if $location.CorsConfig.CorsEnabled }} - {{ template "CORS" $location }} # OK - {{ end }} - - return 302 {{ buildAuthSignURL $externalAuth.SigninURL $externalAuth.SigninURLRedirectParam }}; # OK - } - {{ end }} - {{ end }} - - location {{ $path }} { - {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.IngressPath) }} - set $namespace {{ $ing.Namespace | quote}}; # OK - set $ingress_name {{ $ing.Rule | quote }}; # OK - set $service_name {{ $ing.Service | quote }}; # OK - set $service_port {{ $ing.ServicePort | quote }}; # OK - set $location_path {{ $ing.Path | escapeLiteralDollar | quote }}; # OK - - {{ buildOpentelemetryForLocation $all.Cfg.EnableOpentelemetry $all.Cfg.OpentelemetryTrustIncomingSpan $location }} # OK - - {{ if $location.Mirror.Source }} - mirror {{ $location.Mirror.Source }}; # OK - mirror_request_body {{ $location.Mirror.RequestBody }}; # OK - {{ end }} - - {{ locationConfigForLua $location $all }} # OK - - rewrite_by_lua_file /etc/nginx/lua/nginx/ngx_rewrite.lua; # OK - - header_filter_by_lua_file /etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua; # OK - - log_by_lua_file /etc/nginx/lua/nginx/ngx_conf_log_block.lua; # OK - - {{ if not $location.Logs.Access }} - access_log off; # OK - {{ end }} - - {{ if $location.Logs.Rewrite }} - rewrite_log on; # OK - {{ end }} - - {{ if $location.HTTP2PushPreload }} - http2_push_preload on; # OK - {{ end }} - - port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }}; # OK - - set $balancer_ewma_score -1; # OK - set $proxy_upstream_name {{ buildUpstreamName $location | quote }}; # OK - set $proxy_host $proxy_upstream_name; # OK - set $pass_access_scheme $scheme; # OK - - {{ if $all.Cfg.UseProxyProtocol }} - set $pass_server_port $proxy_protocol_server_port; # OK - {{ else }} - set $pass_server_port $server_port; # OK - {{ end }} - - set $best_http_host $http_host; # OK - set $pass_port $pass_server_port; # OK - - set $proxy_alternative_upstream_name ""; # OK - - {{ if isLocationAllowed $location }} # 1 - {{ if gt (len $location.Denylist.CIDR) 0 }} # 2 - {{ range $ip := $location.Denylist.CIDR }} # 3 - deny {{ $ip }};{{ end }} # 2 # OK - {{ end }} # 1 - {{ if gt (len $location.Allowlist.CIDR) 0 }} # 2 - {{ range $ip := $location.Allowlist.CIDR }} # 3 - allow {{ $ip }};{{ end }} # 2 # OK - deny all; # OK - {{ end }} # 1 - - {{ if $location.CorsConfig.CorsEnabled }} # 2 - {{ template "CORS" $location }} # OK - {{ end }} # 1 - - {{ if not (isLocationInLocationList $location $all.Cfg.NoAuthLocations) }} # 2 - {{ if $authPath }} # 3 - # this location requires authentication - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} # 4 - set $auth_cookie ''; - add_header Set-Cookie $auth_cookie; - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders true }} # 5 - {{ $line }} - {{- end }} # 4 - # `auth_request` module does not support HTTP keepalives in upstream block: - # https://trac.nginx.org/nginx/ticket/1579 - access_by_lua_block { - local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) - if res.status == ngx.HTTP_OK then - ngx.var.auth_cookie = res.header['Set-Cookie'] - {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} - {{ $line }} - {{- end }} - return - end - if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then - ngx.exit(res.status) - end - ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) - } - {{ else }} - auth_request {{ $authPath }}; - auth_request_set $auth_cookie $upstream_http_set_cookie; - {{ if $externalAuth.AlwaysSetCookie }} # 5 - add_header Set-Cookie $auth_cookie always; - {{ else }} - add_header Set-Cookie $auth_cookie; - {{ end }} # 4 - {{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders false }} # 5 - {{ $line }} - {{- end }} # 4 - {{ end }} # 3 - {{ end }} # 2 - - {{ if $externalAuth.SigninURL }} # 3 - set_escape_uri $escaped_request_uri $request_uri; - error_page 401 = {{ buildAuthSignURLLocation $location.Path $externalAuth.SigninURL }}; - {{ end }} # 2 - - {{ if $location.BasicDigestAuth.Secured }} # 3 - {{ if eq $location.BasicDigestAuth.Type "basic" }} # 4 - auth_basic {{ $location.BasicDigestAuth.Realm | quote }}; - auth_basic_user_file {{ $location.BasicDigestAuth.File }}; - {{ else }} - auth_digest {{ $location.BasicDigestAuth.Realm | quote }}; - auth_digest_user_file {{ $location.BasicDigestAuth.File }}; - {{ end }} # 3 - {{ $proxySetHeader }} Authorization ""; - {{ end }} # 2 - {{ end }} # 1 - - {{/* if the location contains a rate limit annotation, create one */}} - {{ $limits := buildRateLimit $location }} - {{ range $limit := $limits }} # 2 # OK - {{ $limit }}{{ end }} # 1 - - {{ if isValidByteSize $location.Proxy.BodySize true }} - client_max_body_size {{ $location.Proxy.BodySize }}; # OK - {{ end }} - {{ if isValidByteSize $location.ClientBodyBufferSize false }} - client_body_buffer_size {{ $location.ClientBodyBufferSize }}; # OK - {{ end }} # 1 - - {{/* By default use vhost as Host to upstream, but allow overrides */}} - {{ if not (empty $location.UpstreamVhost) }} - {{ $proxySetHeader }} Host {{ $location.UpstreamVhost | quote }}; # OK - {{ else }} - {{ $proxySetHeader }} Host $best_http_host; # OK - {{ end }} # 1 - - # Pass the extracted client certificate to the backend - {{ if not (empty $server.CertificateAuth.CAFileName) }} # 2 - {{ if $server.CertificateAuth.PassCertToUpstream }} # 3 - {{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert; # OK - {{ end }} # 2 - {{ $proxySetHeader }} ssl-client-verify $ssl_client_verify; # OK - {{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn; # OK - {{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn; # OK - {{ end }} # 1 - - # Allow websocket connections - {{ $proxySetHeader }} Upgrade $http_upgrade; # OK - {{ if $location.Connection.Enabled}} - {{ $proxySetHeader }} Connection {{ $location.Connection.Header }}; # OK - {{ else }} - {{ $proxySetHeader }} Connection $connection_upgrade; # OK - {{ end }} - - {{ $proxySetHeader }} X-Request-ID $req_id; # OK - {{ $proxySetHeader }} X-Real-IP $remote_addr; # OK - {{ if and $all.Cfg.UseForwardedHeaders $all.Cfg.ComputeFullForwardedFor }} - {{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for; # OK - {{ else }} - {{ $proxySetHeader }} X-Forwarded-For $remote_addr; # OK - {{ end }} # 1 - {{ $proxySetHeader }} X-Forwarded-Host $best_http_host; # OK - {{ $proxySetHeader }} X-Forwarded-Port $pass_port; # OK - {{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme; # OK - {{ $proxySetHeader }} X-Forwarded-Scheme $pass_access_scheme; # OK - {{ if $all.Cfg.ProxyAddOriginalURIHeader }} - {{ $proxySetHeader }} X-Original-URI $request_uri; # OK - {{ end }} # 1 - {{ $proxySetHeader }} X-Scheme $pass_access_scheme; # OK - - # Pass the original X-Forwarded-For - {{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }}; # OK - - # mitigate HTTPoxy Vulnerability - # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ - {{ $proxySetHeader }} Proxy ""; # OK - - # Custom headers to proxied server - {{ range $k, $v := $all.ProxySetHeaders }} - {{ $proxySetHeader }} {{ $k }} {{ $v | quote }}; # OK - {{ end }} # 1 - - proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; OK - proxy_send_timeout {{ $location.Proxy.SendTimeout }}s; # OK - proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s; # OK - - proxy_buffering {{ $location.Proxy.ProxyBuffering }}; # OK - proxy_buffer_size {{ $location.Proxy.BufferSize }}; # OK - proxy_buffers {{ $location.Proxy.BuffersNumber }} {{ $location.Proxy.BufferSize }}; # OK - {{ if isValidByteSize $location.Proxy.ProxyMaxTempFileSize true }} - proxy_max_temp_file_size {{ $location.Proxy.ProxyMaxTempFileSize }}; # OK - {{ end }} - proxy_request_buffering {{ $location.Proxy.RequestBuffering }}; # OK - proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; # OK - - proxy_cookie_domain {{ $location.Proxy.CookieDomain }}; # OK - proxy_cookie_path {{ $location.Proxy.CookiePath }}; # OK - - # In case of errors try the next upstream server before returning an error - proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }}; # OK - proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }}; # OK - proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; # OK - - {{ if or (eq $location.BackendProtocol "GRPC") (eq $location.BackendProtocol "GRPCS") }} - # Grpc settings - grpc_connect_timeout {{ $location.Proxy.ConnectTimeout }}s; # OK - grpc_send_timeout {{ $location.Proxy.SendTimeout }}s; # OK - grpc_read_timeout {{ $location.Proxy.ReadTimeout }}s; # OK - {{ end }} # 1 - - - {{ if $location.CustomHeaders }} # 2 - # Custom Response Headers - {{ range $k, $v := $location.CustomHeaders.Headers }} # 3 - more_set_headers {{ printf "%s: %s" $k $v | escapeLiteralDollar | quote }}; # OK - {{ end }} # 2 - {{ end }} # 1 - - {{/* if we are sending the request to a custom default backend, we add the required headers */}} - {{ if (hasPrefix $location.Backend "custom-default-backend-") }} - proxy_set_header X-Code 503; # OK - proxy_set_header X-Format $http_accept; # OK - proxy_set_header X-Namespace $namespace; # OK - proxy_set_header X-Ingress-Name $ingress_name; # OK - proxy_set_header X-Service-Name $service_name; # OK - proxy_set_header X-Service-Port $service_port; # OK - proxy_set_header X-Request-ID $req_id; # OK - {{ end }} # 1 - - {{ if $location.Satisfy }} - satisfy {{ $location.Satisfy }}; # OK - {{ end }} # 1 - - {{/* if a location-specific error override is set, add the proxy_intercept here */}} - {{ if and $location.CustomHTTPErrors (not $location.DisableProxyInterceptErrors) }} - # Custom error pages per ingress - proxy_intercept_errors on; # OK - {{ end }} # 1 - - {{ range $errCode := $location.CustomHTTPErrors }} - error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }} # OK - - {{ if (eq $location.BackendProtocol "FCGI") }} - include /etc/nginx/fastcgi_params; OK - {{ end }} # 1 - {{- if $location.FastCGI.Index -}} - fastcgi_index {{ $location.FastCGI.Index | quote }}; # OK - {{- end -}} # 1 - {{ range $k, $v := $location.FastCGI.Params }} - fastcgi_param {{ $k }} {{ $v | quote }}; # OK - {{ end }} # 1 - - {{ if not (empty $location.Redirect.URL) }} - return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }}; # OK - {{ end }} # 1 - - {{ buildProxyPass $server.Hostname $all.Backends $location }} # OK - {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }}; # OK - {{ else if not (eq $location.Proxy.ProxyRedirectTo "off") }} - proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }}; # OK - {{ end }} # 1 - {{ else }} - # Location denied. Reason: {{ $location.Denied | quote }} # OK - return 503; # OK - {{ end }} # 0 - {{ if not (empty $location.ProxySSL.CAFileName) }} # 1 - # PEM sha: {{ $location.ProxySSL.CASHA }} # OK - proxy_ssl_trusted_certificate {{ $location.ProxySSL.CAFileName }}; # OK - proxy_ssl_ciphers {{ $location.ProxySSL.Ciphers }}; # OK - proxy_ssl_protocols {{ $location.ProxySSL.Protocols }}; # OK - proxy_ssl_verify {{ $location.ProxySSL.Verify }}; # OK - proxy_ssl_verify_depth {{ $location.ProxySSL.VerifyDepth }}; # OK - {{ end }} # 0 - - {{ if not (empty $location.ProxySSL.ProxySSLName) }} # 1 - proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; # OK - {{ end }} # 0 - {{ if not (empty $location.ProxySSL.ProxySSLServerName) }} # 1 - proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; # OK - {{ end }} # 0 - - {{ if not (empty $location.ProxySSL.PemFileName) }} # 1 - proxy_ssl_certificate {{ $location.ProxySSL.PemFileName }}; # OK - proxy_ssl_certificate_key {{ $location.ProxySSL.PemFileName }}; # OK - {{ end }} # 0 - } - {{ end }} # End range - {{ end }} # Maybe that missing if... - - {{ if eq $server.Hostname "_" }} - # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }} - location {{ $all.HealthzURI }} { # OK - - {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; # OK - {{ end }} - - access_log off; # OK - return 200; # OK - } - - # this is required to avoid error if nginx is being monitored - # with an external software (like sysdig) - location /nginx_status { - - {{ if $all.Cfg.EnableOpentelemetry }} - opentelemetry off; # OK - {{ end }} - - {{ range $v := $all.NginxStatusIpv4Whitelist }} - allow {{ $v }}; # OK - {{ end }} - {{ if $all.IsIPV6Enabled -}} - {{ range $v := $all.NginxStatusIpv6Whitelist }} - allow {{ $v }}; # OK - {{ end }} - {{ end -}} - deny all; # OK - - access_log off; # OK - stub_status on; # OK - } - - {{ end }} - -{{ end }} From f6a110a442aebfe58ceb49726d526adcda85ad94 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Tue, 12 Nov 2024 17:17:59 -0700 Subject: [PATCH 11/17] Add crossplane flag and e2e test --- .github/workflows/ci.yaml | 15 ++ .github/workflows/zz-tmpl-k8s-e2e.yaml | 1 + .golangci.yml | 1 + charts/ingress-nginx/templates/_params.tpl | 3 + charts/ingress-nginx/values.yaml | 1 + internal/ingress/controller/controller.go | 134 +++++++----------- internal/ingress/controller/nginx.go | 53 ++++--- .../template/crossplane/crossplane.go | 89 +++++------- .../crossplane_internal_utils_test.go | 19 +-- .../template/crossplane/crossplane_test.go | 19 ++- .../template/crossplane/location.go | 2 +- .../ingress/controller/template/template.go | 9 ++ pkg/flags/flags.go | 19 ++- test/e2e/framework/exec.go | 7 +- test/e2e/wait-for-nginx.sh | 10 +- 15 files changed, 202 insertions(+), 180 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5aacdf2dde..b8faa5a27b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -320,3 +320,18 @@ jobs: with: k8s-version: ${{ matrix.k8s }} variation: "CHROOT" + + kubernetes-crossplane: + name: Kubernetes Crossplane + needs: + - changes + - build + if: | + (needs.changes.outputs.go == 'true') || (needs.changes.outputs.baseimage == 'true') || ${{ github.event.workflow_dispatch.run_e2e == 'true' }} + strategy: + matrix: + k8s: [v1.30.4, v1.31.0] + uses: ./.github/workflows/zz-tmpl-k8s-e2e.yaml + with: + k8s-version: ${{ matrix.k8s }} + variation: "CROSSPLANE" \ No newline at end of file diff --git a/.github/workflows/zz-tmpl-k8s-e2e.yaml b/.github/workflows/zz-tmpl-k8s-e2e.yaml index c46e4a9570..353cdcae0b 100644 --- a/.github/workflows/zz-tmpl-k8s-e2e.yaml +++ b/.github/workflows/zz-tmpl-k8s-e2e.yaml @@ -44,6 +44,7 @@ jobs: SKIP_INGRESS_IMAGE_CREATION: true SKIP_E2E_IMAGE_CREATION: true IS_CHROOT: ${{ inputs.variation == 'CHROOT' }} + IS_CROSSPLANE: ${{ inputs.variation == 'CROSSPLANE' }} run: | kind get kubeconfig > $HOME/.kube/kind-config-kind make kind-e2e-test diff --git a/.golangci.yml b/.golangci.yml index afbe3b8251..2d73e14e77 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,6 +25,7 @@ linters: - ginkgolinter - gocheckcompilerdirectives - goconst + - gocritic - gocyclo - godox - gofmt diff --git a/charts/ingress-nginx/templates/_params.tpl b/charts/ingress-nginx/templates/_params.tpl index 0051dc9c09..c4530ead64 100644 --- a/charts/ingress-nginx/templates/_params.tpl +++ b/charts/ingress-nginx/templates/_params.tpl @@ -60,6 +60,9 @@ {{- if .Values.controller.enableTopologyAwareRouting }} - --enable-topology-aware-routing=true {{- end }} +{{- if .Values.controller.templateEngine }} +- --configuration-template-engine={{ .Values.controller.templateEngine }} +{{- end }} {{- if .Values.controller.disableLeaderElection }} - --disable-leader-election=true {{- end }} diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml index fb0b36a1cb..1bbf112cc9 100644 --- a/charts/ingress-nginx/values.yaml +++ b/charts/ingress-nginx/values.yaml @@ -21,6 +21,7 @@ commonLabels: {} controller: name: controller + templateEngine: "go-template" enableAnnotationValidations: true image: ## Keep false as default for now! diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 652a80e498..e2f8ab5893 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -61,89 +61,59 @@ const ( // Configuration contains all the settings required by an Ingress controller type Configuration struct { - APIServerHost string - RootCAFile string - - KubeConfigFile string - - Client clientset.Interface - - ResyncPeriod time.Duration - - ConfigMapName string - DefaultService string - - Namespace string - - WatchNamespaceSelector labels.Selector - - // +optional - TCPConfigMapName string - // +optional - UDPConfigMapName string - - DefaultSSLCertificate string - - // +optional - PublishService string - PublishStatusAddress string - - UpdateStatus bool - UseNodeInternalIP bool - ElectionID string - ElectionTTL time.Duration - UpdateStatusOnShutdown bool - - HealthCheckHost string - ListenPorts *ngx_config.ListenPorts - - DisableServiceExternalName bool - - EnableSSLPassthrough bool - - DisableLeaderElection bool - - EnableProfiling bool - - EnableMetrics bool - MetricsPerHost bool - MetricsPerUndefinedHost bool - MetricsBuckets *collectors.HistogramBuckets - MetricsBucketFactor float64 - MetricsMaxBuckets uint32 - ReportStatusClasses bool - ExcludeSocketMetrics []string - - FakeCertificate *ingress.SSLCert - - SyncRateLimit float32 - - DisableCatchAll bool - - IngressClassConfiguration *ingressclass.Configuration - - ValidationWebhook string - ValidationWebhookCertPath string - ValidationWebhookKeyPath string - DisableFullValidationTest bool - - GlobalExternalAuth *ngx_config.GlobalExternalAuth - MaxmindEditionFiles *[]string - - MonitorMaxBatchSize int - - PostShutdownGracePeriod int - ShutdownGracePeriod int - - InternalLoggerAddress string - IsChroot bool - DeepInspector bool - + APIServerHost string + RootCAFile string + KubeConfigFile string + Client clientset.Interface + ResyncPeriod time.Duration + ConfigMapName string + DefaultService string + Namespace string + WatchNamespaceSelector labels.Selector + TCPConfigMapName string + UDPConfigMapName string + DefaultSSLCertificate string + PublishService string + PublishStatusAddress string + UpdateStatus bool + UseNodeInternalIP bool + ElectionID string + ElectionTTL time.Duration + UpdateStatusOnShutdown bool + HealthCheckHost string + ListenPorts *ngx_config.ListenPorts + DisableServiceExternalName bool + EnableSSLPassthrough bool + DisableLeaderElection bool + EnableProfiling bool + EnableMetrics bool + MetricsPerHost bool + MetricsPerUndefinedHost bool + MetricsBuckets *collectors.HistogramBuckets + MetricsBucketFactor float64 + MetricsMaxBuckets uint32 + ReportStatusClasses bool + ExcludeSocketMetrics []string + FakeCertificate *ingress.SSLCert + SyncRateLimit float32 + DisableCatchAll bool + IngressClassConfiguration *ingressclass.Configuration + ValidationWebhook string + ValidationWebhookCertPath string + ValidationWebhookKeyPath string + DisableFullValidationTest bool + GlobalExternalAuth *ngx_config.GlobalExternalAuth + MaxmindEditionFiles *[]string + MonitorMaxBatchSize int + PostShutdownGracePeriod int + ShutdownGracePeriod int + InternalLoggerAddress string + IsChroot bool + DeepInspector bool DynamicConfigurationRetries int - - DisableSyncEvents bool - - EnableTopologyAwareRouting bool + DisableSyncEvents bool + EnableTopologyAwareRouting bool + ConfigurationTemplateEngine string } func getIngressPodZone(svc *apiv1.Service) string { diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index 6f138d7540..dba8934594 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -159,7 +159,10 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro } onTemplateChange := func() { - template, err := crossplane.NewTemplate() + if config.ConfigurationTemplateEngine != "go-template" { + return + } + template, err := ngx_template.NewTemplate(nginx.TemplatePath) if err != nil { // this error is different from the rest because it must be clear why nginx is not working klog.ErrorS(err, "Error loading new template") @@ -171,18 +174,28 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro n.syncQueue.EnqueueTask(task.GetDummyObject("template-change")) } - ngxTpl, err := crossplane.NewTemplate() - if err != nil { - klog.Fatalf("Invalid NGINX configuration template: %v", err) + var ngxTpl ngx_template.Writer + switch config.ConfigurationTemplateEngine { + case "go-template": + ngxTpl, err = ngx_template.NewTemplate(nginx.TemplatePath) + if err != nil { + klog.Fatalf("Invalid NGINX configuration template: %v", err) + } + _, err = file.NewFileWatcher(nginx.TemplatePath, onTemplateChange) + if err != nil { + klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err) + } + case "crossplane": + ngxTpl, err = crossplane.NewTemplate() + if err != nil { + klog.Fatalf("Invalid NGINX configuration template: %v", err) + } + default: + klog.Fatal("Invalid template engine, please use 'go-template' or 'crossplane'") } n.t = ngxTpl - _, err = file.NewFileWatcher(nginx.TemplatePath, onTemplateChange) - if err != nil { - klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err) - } - filesToWatch := []string{} if err := os.Mkdir("/etc/ingress-controller/geoip/", 0o755); err != nil && !os.IsExist(err) { @@ -653,6 +666,11 @@ func (n *NGINXController) testTemplate(cfg []byte) error { if err != nil { return err } + + if err := n.t.Validate(tmpfile.Name()); err != nil { + return fmt.Errorf("error during template validation: %w", err) + } + out, err := n.command.Test(tmpfile.Name()) if err != nil { // this error is different from the rest because it must be clear why nginx is not working @@ -701,7 +719,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { err = n.testTemplate(content) if err != nil { - return fmt.Errorf("err %s content %s", err, string(content)) + return err } if klog.V(2).Enabled() { @@ -869,14 +887,15 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro } } - // TODO: (ricardo) - Disable in case this is crossplane, we don't support stream on this mode - /*streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) - if streamConfigurationChanged { - err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) - if err != nil { - return err + if n.cfg.ConfigurationTemplateEngine == "go-template" { + streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) + if streamConfigurationChanged { + err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) + if err != nil { + return err + } } - }*/ + } serversChanged := !reflect.DeepEqual(n.runningConfig.Servers, pcfg.Servers) if serversChanged { diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go index 13796fdb8d..aab90a7526 100644 --- a/internal/ingress/controller/template/crossplane/crossplane.go +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -18,7 +18,6 @@ package crossplane import ( "bytes" - "os" ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" @@ -31,55 +30,27 @@ Unsupported directives: - opentelemetry - modsecurity - any stream directive (TCP/UDP forwarding) -- geoip2 */ // On this case we will try to use the go ngx_crossplane to write the template instead of the template renderer type Template struct { - options *ngx_crossplane.BuildOptions - config *ngx_crossplane.Config - tplConfig *config.TemplateConfig - mimeFile string + options *ngx_crossplane.BuildOptions + parseOptions *ngx_crossplane.ParseOptions + config *ngx_crossplane.Config + tplConfig *config.TemplateConfig + mimeFile string } func NewTemplate() (*Template, error) { lua := ngx_crossplane.Lua{} - return &Template{ - mimeFile: "/etc/nginx/mime.types", - options: &ngx_crossplane.BuildOptions{ - Builders: []ngx_crossplane.RegisterBuilder{ - lua.RegisterBuilder(), - }, + buildOptions := &ngx_crossplane.BuildOptions{ + Builders: []ngx_crossplane.RegisterBuilder{ + lua.RegisterBuilder(), }, - }, nil -} - -func (c *Template) SetMimeFile(file string) { - c.mimeFile = file -} - -func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { - c.tplConfig = conf - - // build root directives - c.buildConfig() - - // build events directive - c.buildEvents() - - // build http directive - c.buildHTTP() - - var buf bytes.Buffer - - err := ngx_crossplane.Build(&buf, *c.config, &ngx_crossplane.BuildOptions{}) - if err != nil { - return nil, err } - lua := ngx_crossplane.Lua{} - options := ngx_crossplane.ParseOptions{ + parseOptions := &ngx_crossplane.ParseOptions{ ParseComments: true, ErrorOnUnknownDirectives: true, StopParsingOnError: true, @@ -99,24 +70,40 @@ func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { IgnoreDirectives: []string{"set_escape_uri"}, } - tmpFile, err := os.CreateTemp("", "") - if err != nil { - return nil, err - } - defer func() { - _ = os.Remove(tmpFile.Name()) - _ = tmpFile.Close() - }() + return &Template{ + mimeFile: "/etc/nginx/mime.types", + options: buildOptions, + parseOptions: parseOptions, + }, nil +} - _, err = tmpFile.Write(buf.Bytes()) - if err != nil { - return nil, err - } +func (c *Template) SetMimeFile(file string) { + c.mimeFile = file +} - _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) +func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { + c.tplConfig = conf + + // build root directives + c.buildConfig() + + // build events directive + c.buildEvents() + + // build http directive + c.buildHTTP() + + var buf bytes.Buffer + + err := ngx_crossplane.Build(&buf, *c.config, &ngx_crossplane.BuildOptions{}) if err != nil { return nil, err } return buf.Bytes(), err } + +func (c *Template) Validate(filename string) error { + _, err := ngx_crossplane.Parse(filename, c.parseOptions) + return err +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go index b5c2b69627..6d8d813368 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -22,7 +22,6 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" "github.com/stretchr/testify/require" - "k8s.io/ingress-nginx/internal/ingress/controller/config" ) // THIS FILE SHOULD BE USED JUST FOR INTERNAL TESTS - Private functions @@ -53,22 +52,6 @@ func Test_Internal_boolToStr(t *testing.T) { require.Equal(t, boolToStr(false), "off") } -func Test_Internal_buildLuaDictionaries(t *testing.T) { - t.Skip("Maps are not sorted, need to fix this") - cfg := &config.Configuration{ - LuaSharedDicts: map[string]int{ - "somedict": 1024, - "otherdict": 1025, - }, - } - directives := buildLuaSharedDictionaries(cfg) - require.Len(t, directives, 2) - require.Equal(t, "lua_shared_dict", directives[0].Directive) - require.Equal(t, []string{"somedict", "1M"}, directives[0].Args) - require.Equal(t, "lua_shared_dict", directives[1].Directive) - require.Equal(t, []string{"otherdict", "1025K"}, directives[1].Args) -} - func Test_Internal_buildCorsOriginRegex(t *testing.T) { tests := []struct { name string @@ -87,7 +70,7 @@ func Test_Internal_buildCorsOriginRegex(t *testing.T) { name: "multiple hosts should be changed properly", corsOrigins: []string{"*.xpto.com", " lalala.com"}, want: ngx_crossplane.Directives{ - buildBlockDirective("if", []string{"$http_origin", "~*", "([A-Za-z0-9\\-]+\\.xpto\\.com)", "|", "(lalala\\.com)"}, + buildBlockDirective("if", []string{"$http_origin", "~*", "(([A-Za-z0-9\\-]+\\.xpto\\.com)|(lalala\\.com))$"}, ngx_crossplane.Directives{buildDirective("set", "$cors", "true")}, ), }, diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 3d3fa745dd..02c59035ef 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -169,12 +169,27 @@ func TestCrossplaneTemplate(t *testing.T) { Target: "http://www.mymirror.com", RequestBody: "off", }, + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, }, { DefaultBackendUpstreamName: "something", - CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, + CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! }, { + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, DefaultBackendUpstreamName: "otherthing", CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! }, @@ -255,7 +270,6 @@ func TestCrossplaneTemplate(t *testing.T) { _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) require.NoError(t, err) - require.Equal(t, "bla", string(content)) }) t.Run("it should set the right logging configs", func(t *testing.T) { @@ -352,6 +366,5 @@ func TestCrossplaneTemplate(t *testing.T) { _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) require.NoError(t, err) - // require.Equal(t, " ", string(content)) }) } diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go index af2ee433ba..e2a06f85e3 100644 --- a/internal/ingress/controller/template/crossplane/location.go +++ b/internal/ingress/controller/template/crossplane/location.go @@ -296,7 +296,7 @@ func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingres if location.CorsConfig.CorsEnabled { dir = append(dir, buildCorsDirectives(location.CorsConfig)...) } - // TODO: Implement the build Auth Location + if !isLocationInLocationList(location, c.tplConfig.Cfg.NoAuthLocations) { dir = append(dir, buildAuthLocationConfig(location, locationConfig)...) } diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index ed052e4ecf..e420b9bcfb 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -71,6 +71,10 @@ type Writer interface { // NOTE: Implementors must ensure that the content of the returned slice is not modified by the implementation // after the return of this function. Write(conf *config.TemplateConfig) ([]byte, error) + // Validate is a function that can be called, containing the file name to be tested + // This function should be used just by specific cases like crossplane, otherwise it can return + // null error + Validate(filename string) error } // Template ingress template @@ -229,6 +233,11 @@ type LuaListenPorts struct { SSLProxyPort string `json:"ssl_proxy"` } +// Validate is no-op at go-template +func (t *Template) Validate(filename string) error { + return nil +} + // Write populates a buffer using a template with NGINX configuration // and the servers and upstreams created by Ingress rules func (t *Template) Write(conf *config.TemplateConfig) ([]byte, error) { diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go index ce24160fdc..ff8fa3d737 100644 --- a/pkg/flags/flags.go +++ b/pkg/flags/flags.go @@ -233,6 +233,8 @@ Takes the form ":port". If not provided, no admission controller is starte disableSyncEvents = flags.Bool("disable-sync-events", false, "Disables the creation of 'Sync' event resources") enableTopologyAwareRouting = flags.Bool("enable-topology-aware-routing", false, "Enable topology aware routing feature, needs service object annotation service.kubernetes.io/topology-mode sets to auto.") + + configurationTemplateEngine = flags.String("configuration-template-engine", "go-template", "Defines what configuration template engine should be used. Can be 'go-template' or 'crossplane'. ") ) flags.StringVar(&nginx.MaxmindMirror, "maxmind-mirror", "", `Maxmind mirror url (example: http://geoip.local/databases.`) @@ -303,6 +305,10 @@ https://blog.maxmind.com/2019/12/significant-changes-to-accessing-and-using-geol return false, nil, fmt.Errorf("flags --publish-service and --publish-status-address are mutually exclusive") } + if *enableSSLPassthrough && *configurationTemplateEngine != "go-template" { + return false, nil, fmt.Errorf("SSL Passthrough can only be enabled with 'go-template' configuration engine") + } + nginx.HealthPath = *defHealthzURL if *defHealthCheckTimeout > 0 { @@ -390,12 +396,13 @@ https://blog.maxmind.com/2019/12/significant-changes-to-accessing-and-using-geol WatchWithoutClass: *watchWithoutClass, IngressClassByName: *ingressClassByName, }, - DisableCatchAll: *disableCatchAll, - ValidationWebhook: *validationWebhook, - ValidationWebhookCertPath: *validationWebhookCert, - ValidationWebhookKeyPath: *validationWebhookKey, - InternalLoggerAddress: *internalLoggerAddress, - DisableSyncEvents: *disableSyncEvents, + DisableCatchAll: *disableCatchAll, + ValidationWebhook: *validationWebhook, + ValidationWebhookCertPath: *validationWebhookCert, + ValidationWebhookKeyPath: *validationWebhookKey, + InternalLoggerAddress: *internalLoggerAddress, + DisableSyncEvents: *disableSyncEvents, + ConfigurationTemplateEngine: *configurationTemplateEngine, } if *apiserverHost != "" { diff --git a/test/e2e/framework/exec.go b/test/e2e/framework/exec.go index 8d528c37ae..ea38b8bef8 100644 --- a/test/e2e/framework/exec.go +++ b/test/e2e/framework/exec.go @@ -117,7 +117,12 @@ func (f *Framework) newIngressController(namespace, namespaceOverlay string) err isChroot = "false" } - cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot) + isCrossplane, ok := os.LookupEnv("IS_CROSSPLANE") + if !ok { + isCrossplane = "false" + } + + cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot, isCrossplane) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("unexpected error waiting for ingress controller deployment: %v.\nLogs:\n%v", err, string(out)) diff --git a/test/e2e/wait-for-nginx.sh b/test/e2e/wait-for-nginx.sh index 73023aba15..8eb4368d0c 100755 --- a/test/e2e/wait-for-nginx.sh +++ b/test/e2e/wait-for-nginx.sh @@ -24,6 +24,12 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export NAMESPACE=$1 export NAMESPACE_OVERLAY=$2 export IS_CHROOT=$3 +export IS_CROSSPLANE=$4 + +TPL_ENGINE="go-template" +if [ "$IS_CROSSPLANE" == "true" ]; then + TPL_ENGINE="crossplane" +fi echo "deploying NGINX Ingress controller in namespace $NAMESPACE" @@ -52,12 +58,14 @@ if [[ ! -z "$NAMESPACE_OVERLAY" && -d "$DIR/namespace-overlays/$NAMESPACE_OVERLA echo "Namespace overlay $NAMESPACE_OVERLAY is being used for namespace $NAMESPACE" helm install nginx-ingress ${DIR}/charts/ingress-nginx \ --namespace=$NAMESPACE \ - --values "$DIR/namespace-overlays/$NAMESPACE_OVERLAY/values.yaml" + --values "$DIR/namespace-overlays/$NAMESPACE_OVERLAY/values.yaml" \ + --set controller.templateEngine=${TPL_ENGINE} else cat << EOF | helm install nginx-ingress ${DIR}/charts/ingress-nginx --namespace=$NAMESPACE --values - # TODO: remove the need to use fullnameOverride fullnameOverride: nginx-ingress controller: + templateEngine: ${TPL_ENGINE} image: repository: ingress-controller/controller chroot: ${IS_CHROOT} From ad70bb172f645809c6ec0cc98dde7ebb2e0a1541 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Tue, 12 Nov 2024 17:25:24 -0700 Subject: [PATCH 12/17] Add setmisc 3rd party module --- .../template/crossplane/crossplane.go | 4 +- .../crossplane/extramodules/opentelemetry.go | 7 --- .../crossplane/extramodules/setmisc.go | 61 +++++++++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 internal/ingress/controller/template/crossplane/extramodules/setmisc.go diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go index aab90a7526..200e6ec52d 100644 --- a/internal/ingress/controller/template/crossplane/crossplane.go +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -60,14 +60,12 @@ func NewTemplate() (*Template, error) { ngx_crossplane.MatchHeadersMoreLatest, extramodules.BrotliMatchFn, extramodules.OpentelemetryMatchFn, + extramodules.SetMiscMatchFn, ngx_crossplane.MatchGeoip2Latest, }, LexOptions: ngx_crossplane.LexOptions{ Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, }, - // Modules that needs to be ported: - // // https://github.com/openresty/set-misc-nginx-module?tab=readme-ov-file#set_escape_uri - IgnoreDirectives: []string{"set_escape_uri"}, } return &Template{ diff --git a/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go index f3a9cde9a3..a0039d6fde 100644 --- a/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go +++ b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go @@ -14,13 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -/** - * Copyright (c) F5, Inc. - * - * This source code is licensed under the Apache License, Version 2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - // Code generated by generator; DO NOT EDIT. // All the definitions are extracted from the source code // Each bit mask describes these behaviors: diff --git a/internal/ingress/controller/template/crossplane/extramodules/setmisc.go b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go new file mode 100644 index 0000000000..ab39ec5e84 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go @@ -0,0 +1,61 @@ +/* +Copyright 2024 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. +*/ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package extramodules + +var setMiscDirectives = map[string][]uint{ + "set_base32_alphabet": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake1, + }, + "set_base32_padding": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, + }, + "set_decode_base32": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, + }, + "set_encode_base32": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, + }, + "set_formatted_gmt_time": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake2, + }, + "set_formatted_local_time": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake2, + }, + "set_hashed_upstream": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake3, + }, + "set_local_today": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake1, + }, + "set_misc_base32_padding": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, + }, +} + + +func SetMiscMatchFn(directive string) ([]uint, bool) { + m, ok := setMiscDirectives[directive] + return m, ok +} From d6cc3fd6e108c1025dcea8241268258c412cd151 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Tue, 12 Nov 2024 17:35:52 -0700 Subject: [PATCH 13/17] Fix helm docs and fakeTemplate --- charts/ingress-nginx/README.md | 1 + charts/ingress-nginx/values.yaml | 1 + internal/ingress/controller/controller_test.go | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/charts/ingress-nginx/README.md b/charts/ingress-nginx/README.md index c542d8789e..7195192564 100644 --- a/charts/ingress-nginx/README.md +++ b/charts/ingress-nginx/README.md @@ -491,6 +491,7 @@ metadata: | controller.sysctls | object | `{}` | sysctls for controller pods # Ref: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ | | controller.tcp.annotations | object | `{}` | Annotations to be added to the tcp config configmap | | controller.tcp.configMapNamespace | string | `""` | Allows customization of the tcp-services-configmap; defaults to $(POD_NAMESPACE) | +| controller.templateEngine | string | `"go-template"` | Defines which template engine should be used when creating NGINX configuration. Can be 'go-template' or 'crossplane' | | controller.terminationGracePeriodSeconds | int | `300` | `terminationGracePeriodSeconds` to avoid killing pods before we are ready # wait up to five minutes for the drain of connections # | | controller.tolerations | list | `[]` | Node tolerations for server scheduling to nodes with taints # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ # | | controller.topologySpreadConstraints | list | `[]` | Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. # Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ # | diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml index 1bbf112cc9..d9af12664b 100644 --- a/charts/ingress-nginx/values.yaml +++ b/charts/ingress-nginx/values.yaml @@ -21,6 +21,7 @@ commonLabels: {} controller: name: controller + # -- Defines which template engine should be used when creating NGINX configuration. Can be 'go-template' or 'crossplane' templateEngine: "go-template" enableAnnotationValidations: true image: diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go index 9d3fea4708..a830a57294 100644 --- a/internal/ingress/controller/controller_test.go +++ b/internal/ingress/controller/controller_test.go @@ -157,6 +157,10 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) { type fakeTemplate struct{} +func (fakeTemplate) Validate(filename string) error { + return nil +} + func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) { r := []byte{} for _, s := range conf.Servers { From 65c047c3cba072cdc41790301bff27c35ede0bae Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Tue, 12 Nov 2024 22:54:27 -0700 Subject: [PATCH 14/17] Fix lint, extra module, test assertions --- .../ingress/annotations/annotations_test.go | 1 - .../ingress/annotations/parser/validators.go | 2 - internal/ingress/controller/nginx.go | 7 +- .../template/crossplane/authlocation.go | 74 +++++++------------ .../controller/template/crossplane/cors.go | 4 +- .../template/crossplane/crossplane_test.go | 2 +- .../crossplane/extramodules/analyze.go | 2 - .../crossplane/extramodules/setmisc.go | 44 ++--------- .../controller/template/crossplane/http.go | 59 ++++++--------- .../template/crossplane/location.go | 6 +- .../controller/template/crossplane/server.go | 44 ++++++----- .../controller/template/crossplane/utils.go | 4 +- test/e2e/annotations/affinity.go | 36 ++++++--- test/e2e/annotations/affinitymode.go | 6 +- test/e2e/annotations/auth.go | 1 - test/e2e/annotations/grpc.go | 1 - test/e2e/annotations/limitconnections.go | 3 +- test/e2e/annotations/upstreamhashby.go | 3 +- test/e2e/framework/framework.go | 2 +- test/e2e/framework/httpexpect/response.go | 1 - test/e2e/settings/disable_sync_events.go | 1 - test/e2e/settings/enable_real_ip.go | 1 - 22 files changed, 123 insertions(+), 181 deletions(-) diff --git a/internal/ingress/annotations/annotations_test.go b/internal/ingress/annotations/annotations_test.go index 5df3cdc0ee..0c041cc2ca 100644 --- a/internal/ingress/annotations/annotations_test.go +++ b/internal/ingress/annotations/annotations_test.go @@ -133,7 +133,6 @@ func TestSSLPassthrough(t *testing.T) { ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() - //nolint:goconst //already a constant fooAnns := []struct { annotations map[string]string er bool diff --git a/internal/ingress/annotations/parser/validators.go b/internal/ingress/annotations/parser/validators.go index 3c724a3110..048086c520 100644 --- a/internal/ingress/annotations/parser/validators.go +++ b/internal/ingress/annotations/parser/validators.go @@ -49,8 +49,6 @@ var ( // IsValidRegex checks if the tested string can be used as a regex, but without any weird character. // It includes regex characters for paths that may contain regexes -// -//nolint:goconst //already a constant var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$") // SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index dba8934594..9559790ed4 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -71,6 +71,7 @@ import ( const ( tempNginxPattern = "nginx-cfg" emptyUID = "-1" + goTemplateEngine = "go-template" ) // NewNGINXController creates a new NGINX Ingress controller. @@ -159,7 +160,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro } onTemplateChange := func() { - if config.ConfigurationTemplateEngine != "go-template" { + if config.ConfigurationTemplateEngine != goTemplateEngine { return } template, err := ngx_template.NewTemplate(nginx.TemplatePath) @@ -176,7 +177,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro var ngxTpl ngx_template.Writer switch config.ConfigurationTemplateEngine { - case "go-template": + case goTemplateEngine: ngxTpl, err = ngx_template.NewTemplate(nginx.TemplatePath) if err != nil { klog.Fatalf("Invalid NGINX configuration template: %v", err) @@ -887,7 +888,7 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro } } - if n.cfg.ConfigurationTemplateEngine == "go-template" { + if n.cfg.ConfigurationTemplateEngine == goTemplateEngine { streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) if streamConfigurationChanged { err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) diff --git a/internal/ingress/controller/template/crossplane/authlocation.go b/internal/ingress/controller/template/crossplane/authlocation.go index 64c15eeb65..ef0dd8e179 100644 --- a/internal/ingress/controller/template/crossplane/authlocation.go +++ b/internal/ingress/controller/template/crossplane/authlocation.go @@ -124,39 +124,23 @@ func (c *Template) buildAuthLocation(server *ingress.Server, */ locationDirectives = append(locationDirectives, buildDirective("set", "$proxy_upstream_name", location.Backend), + buildDirective("proxy_pass_request_body", "off"), + buildDirective("proxy_ssl_server_name", "on"), + buildDirective("proxy_pass_request_headers", "on"), + buildDirective("proxy_set_header", "Content-Length", ""), + buildDirective("proxy_set_header", "X-Forwarded-Proto", ""), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + buildDirective("proxy_set_header", "Host", locationConfig.externalAuth.Host), + buildDirective("proxy_set_header", "X-Original-URL", "$scheme://$http_host$request_uri"), + buildDirective("proxy_set_header", "X-Original-Method", "$request_method"), + buildDirective("proxy_set_header", "X-Sent-From", "nginx-ingress-controller"), + buildDirective("proxy_set_header", "X-Real-IP", "$remote_addr"), ) - locationDirectives = append(locationDirectives, - buildDirective("proxy_pass_request_body", "off")) - - locationDirectives = append(locationDirectives, - buildDirective("proxy_ssl_server_name", "on")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_pass_request_headers", "on")) - - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "Content-Length", "")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Forwarded-Proto", "")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Request-ID", "$req_id")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "Host", locationConfig.externalAuth.Host)) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Original-URL", "$scheme://$http_host$request_uri")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Original-Method", "$request_method")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Sent-From", "nginx-ingress-controller")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Real-IP", "$remote_addr")) - if locationConfig.externalAuth.Method != "" { locationDirectives = append(locationDirectives, - buildDirective("proxy_method", locationConfig.externalAuth.Method)) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Original-URI", "$request_uri")) - locationDirectives = append(locationDirectives, + buildDirective("proxy_method", locationConfig.externalAuth.Method), + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) } @@ -178,8 +162,7 @@ func (c *Template) buildAuthLocation(server *ingress.Server, if locationConfig.externalAuth.Method != "" { locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "X-Original-URI", "$request_uri")) - locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) } @@ -192,11 +175,10 @@ func (c *Template) buildAuthLocation(server *ingress.Server, } locationDirectives = append(locationDirectives, - buildDirective("proxy_buffer_size", location.Proxy.BufferSize)) - locationDirectives = append(locationDirectives, - buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize)) - locationDirectives = append(locationDirectives, - buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering)) + buildDirective("proxy_buffer_size", location.Proxy.BufferSize), + buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), + buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + ) if isValidByteSize(location.Proxy.BodySize, true) { locationDirectives = append(locationDirectives, @@ -210,13 +192,10 @@ func (c *Template) buildAuthLocation(server *ingress.Server, if server.CertificateAuth.CAFileName != "" { locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "ssl-client-verify", "$ssl_client_verify")) - - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "ssl-client-subject-dn", "$ssl_client_s_dn")) - - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "ssl-client-issuer-dn", "$ssl_client_i_dn")) + buildDirective("proxy_set_header", "ssl-client-verify", "$ssl_client_verify"), + buildDirective("proxy_set_header", "ssl-client-subject-dn", "$ssl_client_s_dn"), + buildDirective("proxy_set_header", "ssl-client-issuer-dn", "$ssl_client_i_dn"), + ) if server.CertificateAuth.PassCertToUpstream { locationDirectives = append(locationDirectives, @@ -231,16 +210,13 @@ func (c *Template) buildAuthLocation(server *ingress.Server, if locationConfig.applyAuthUpstream && locationConfig.applyGlobalAuth { locationDirectives = append(locationDirectives, - buildDirective("proxy_http_version", "1.1")) - locationDirectives = append(locationDirectives, - buildDirective("proxy_set_header", "Connection", "")) - locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", "1.1"), + buildDirective("proxy_set_header", "Connection", ""), buildDirective("set", "$target", changeHostPort(locationConfig.externalAuth.URL, buildAuthUpstreamName(location, server.Hostname)))) } else { locationDirectives = append(locationDirectives, - buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion)) - locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion), buildDirective("set", "$target", locationConfig.externalAuth.URL)) } locationDirectives = append(locationDirectives, diff --git a/internal/ingress/controller/template/crossplane/cors.go b/internal/ingress/controller/template/crossplane/cors.go index 932c2489bf..932f25dbbc 100644 --- a/internal/ingress/controller/template/crossplane/cors.go +++ b/internal/ingress/controller/template/crossplane/cors.go @@ -35,12 +35,10 @@ func buildCorsDirectives(locationcors cors.Config) ngx_crossplane.Directives { buildDirective("set", "$cors", "${cors}options"), }, ), - ) - - directives = append(directives, commonCorsDirective(locationcors, false), commonCorsDirective(locationcors, true), ) + return directives } diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index 02c59035ef..c52077c757 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -87,12 +87,12 @@ func TestCrossplaneTemplate(t *testing.T) { ngx_crossplane.MatchHeadersMoreLatest, extramodules.BrotliMatchFn, extramodules.OpentelemetryMatchFn, + extramodules.SetMiscMatchFn, ngx_crossplane.MatchGeoip2Latest, }, LexOptions: ngx_crossplane.LexOptions{ Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, }, - IgnoreDirectives: []string{"set_escape_uri"}, } mimeFile, err := os.CreateTemp("", "") diff --git a/internal/ingress/controller/template/crossplane/extramodules/analyze.go b/internal/ingress/controller/template/crossplane/extramodules/analyze.go index 72efe24749..22d227d30d 100644 --- a/internal/ingress/controller/template/crossplane/extramodules/analyze.go +++ b/internal/ingress/controller/template/crossplane/extramodules/analyze.go @@ -22,8 +22,6 @@ limitations under the License. */ // This file is an extraction from https://github.com/nginxinc/nginx-go-crossplane/blob/main/analyze.go -// -//nolint:unused package extramodules // bit masks for different directive argument styles. diff --git a/internal/ingress/controller/template/crossplane/extramodules/setmisc.go b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go index ab39ec5e84..fed384d469 100644 --- a/internal/ingress/controller/template/crossplane/extramodules/setmisc.go +++ b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go @@ -14,48 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by generator; DO NOT EDIT. -// All the definitions are extracted from the source code -// Each bit mask describes these behaviors: -// - how many arguments the directive can take -// - whether or not it is a block directive -// - whether this is a flag (takes one argument that's either "on" or "off") -// - which contexts it's allowed to be in +// As opposite to the other files, this wasn't auto generated but hand crafted. +// Please do not change it package extramodules var setMiscDirectives = map[string][]uint{ - "set_base32_alphabet": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake1, - }, - "set_base32_padding": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, - }, - "set_decode_base32": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, - }, - "set_encode_base32": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, - }, - "set_formatted_gmt_time": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake2, - }, - "set_formatted_local_time": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake2, - }, - "set_hashed_upstream": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake3, - }, - "set_local_today": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake1, - }, - "set_misc_base32_padding": { - ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, - }, + "set_escape_uri": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, + }, } - func SetMiscMatchFn(directive string) ([]uint, bool) { - m, ok := setMiscDirectives[directive] - return m, ok + m, ok := setMiscDirectives[directive] + return m, ok } diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index 5da0b248a2..381ef67a43 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -113,8 +113,10 @@ func (c *Template) buildHTTP() { // HTTP2 Configuration if cfg.HTTP2MaxHeaderSize != "" && cfg.HTTP2MaxFieldSize != "" { - httpBlock = append(httpBlock, buildDirective("http2_max_field_size", cfg.HTTP2MaxFieldSize)) - httpBlock = append(httpBlock, buildDirective("http2_max_header_size", cfg.HTTP2MaxHeaderSize)) + httpBlock = append(httpBlock, + buildDirective("http2_max_field_size", cfg.HTTP2MaxFieldSize), + buildDirective("http2_max_header_size", cfg.HTTP2MaxHeaderSize), + ) } if cfg.HTTP2MaxRequests > 0 { @@ -122,13 +124,15 @@ func (c *Template) buildHTTP() { } if cfg.UseGzip { - httpBlock = append(httpBlock, buildDirective("gzip", "on")) - httpBlock = append(httpBlock, buildDirective("gzip_comp_level", cfg.GzipLevel)) - httpBlock = append(httpBlock, buildDirective("gzip_http_version", "1.1")) - httpBlock = append(httpBlock, buildDirective("gzip_min_length", cfg.GzipMinLength)) - httpBlock = append(httpBlock, buildDirective("gzip_types", strings.Split(cfg.GzipTypes, " "))) - httpBlock = append(httpBlock, buildDirective("gzip_proxied", "any")) - httpBlock = append(httpBlock, buildDirective("gzip_vary", "on")) + httpBlock = append(httpBlock, + buildDirective("gzip", "on"), + buildDirective("gzip_comp_level", cfg.GzipLevel), + buildDirective("gzip_http_version", "1.1"), + buildDirective("gzip_min_length", cfg.GzipMinLength), + buildDirective("gzip_types", strings.Split(cfg.GzipTypes, " ")), + buildDirective("gzip_proxied", "any"), + buildDirective("gzip_vary", "on"), + ) if cfg.GzipDisable != "" { httpBlock = append(httpBlock, buildDirective("gzip_disable", strings.Split(cfg.GzipDisable, " "))) @@ -346,29 +350,9 @@ func (c *Template) buildHTTP() { } } - /* - {{ range $server := $servers }} - {{ range $location := $server.Locations }} - {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }} - {{ $applyAuthUpstream := shouldApplyAuthUpstream $location $all.Cfg }} - {{ if and (eq $applyAuthUpstream true) (eq $applyGlobalAuth false) }} - ## start auth upstream {{ $server.Hostname }}{{ $location.Path }} - upstream {{ buildAuthUpstreamName $location $server.Hostname }} { - {{- $externalAuth := $location.ExternalAuth }} - server {{ extractHostPort $externalAuth.URL }}; - - keepalive {{ $externalAuth.KeepaliveConnections }}; - keepalive_requests {{ $externalAuth.KeepaliveRequests }}; - keepalive_timeout {{ $externalAuth.KeepaliveTimeout }}s; - } - ## end auth upstream {{ $server.Hostname }}{{ $location.Path }} - {{ end }} - {{ end }} - {{ end }} - */ for _, server := range c.tplConfig.Servers { for _, location := range server.Locations { - if shouldApplyAuthUpstream(location, cfg) && !shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL) { + if shouldApplyAuthUpstream(location, &cfg) && !shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL) { authUpstreamBlock := buildBlockDirective("upstream", []string{buildAuthUpstreamName(location, server.Hostname)}, ngx_crossplane.Directives{ buildDirective("server", extractHostPort(location.ExternalAuth.URL)), @@ -387,14 +371,17 @@ func (c *Template) buildHTTP() { } for _, server := range c.tplConfig.Servers { - httpBlock = append(httpBlock, buildStartServer(server.Hostname)) - serverBlock := c.buildServerDirective(server) - httpBlock = append(httpBlock, serverBlock) - httpBlock = append(httpBlock, buildEndServer(server.Hostname)) + httpBlock = append(httpBlock, + buildStartServer(server.Hostname), + c.buildServerDirective(server), + buildEndServer(server.Hostname), + ) } - httpBlock = append(httpBlock, c.buildDefaultBackend()) - httpBlock = append(httpBlock, c.buildHealthAndStatsServer()) + httpBlock = append(httpBlock, + c.buildDefaultBackend(), + c.buildHealthAndStatsServer(), + ) c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go index e2a06f85e3..ddb28a7b19 100644 --- a/internal/ingress/controller/template/crossplane/location.go +++ b/internal/ingress/controller/template/crossplane/location.go @@ -173,7 +173,7 @@ func (c *Template) buildServerLocations(server *ingress.Server, locations []*ing proxySetHeader: getProxySetHeader(location), authPath: buildAuthLocation(location, cfg.GlobalExternalAuth.URL), applyGlobalAuth: shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL), - applyAuthUpstream: shouldApplyAuthUpstream(location, cfg), + applyAuthUpstream: shouldApplyAuthUpstream(location, &cfg), externalAuth: &externalAuth{}, } @@ -236,7 +236,7 @@ func (c *Template) buildLocation(server *ingress.Server, buildDirective("set", "$location_path", strings.ReplaceAll(ing.Path, `$`, `${literal_dollar}`)), } - locationDirectives = append(locationDirectives, locationConfigForLua(location, *c.tplConfig)...) + locationDirectives = append(locationDirectives, locationConfigForLua(location, c.tplConfig)...) locationDirectives = append(locationDirectives, buildCertificateDirectives(location)...) if cfg.Cfg.UseProxyProtocol { @@ -648,7 +648,7 @@ func buildRateLimit(loc *ingress.Location) ngx_crossplane.Directives { } // locationConfigForLua formats some location specific configuration into Lua table represented as string -func locationConfigForLua(location *ingress.Location, all config.TemplateConfig) ngx_crossplane.Directives { +func locationConfigForLua(location *ingress.Location, all *config.TemplateConfig) ngx_crossplane.Directives { /* Lua expects the following vars force_ssl_redirect = string_to_bool(ngx.var.force_ssl_redirect), ssl_redirect = string_to_bool(ngx.var.ssl_redirect), diff --git a/internal/ingress/controller/template/crossplane/server.go b/internal/ingress/controller/template/crossplane/server.go index 541e156f0f..c81003b0c3 100644 --- a/internal/ingress/controller/template/crossplane/server.go +++ b/internal/ingress/controller/template/crossplane/server.go @@ -56,7 +56,6 @@ func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane. if server.AuthTLSError != "" { serverBlock = append(serverBlock, buildDirective("return", 403)) } else { - serverBlock = append(serverBlock, c.buildCertificateDirectives(server)...) serverBlock = append(serverBlock, buildCustomErrorLocationsPerServer(server, c.tplConfig.EnableMetrics)...) serverBlock = append(serverBlock, buildMirrorLocationDirective(server.Locations)...) @@ -119,9 +118,10 @@ func (c *Template) buildCertificateDirectives(server *ingress.Server) ngx_crossp if server.CertificateAuth.CAFileName != "" { certAuth := server.CertificateAuth - certDirectives = append(certDirectives, buildDirective("ssl_client_certificate", certAuth.CAFileName)) - certDirectives = append(certDirectives, buildDirective("ssl_verify_client", certAuth.VerifyClient)) - certDirectives = append(certDirectives, buildDirective("ssl_verify_depth", certAuth.ValidationDepth)) + certDirectives = append(certDirectives, + buildDirective("ssl_client_certificate", certAuth.CAFileName), + buildDirective("ssl_verify_client", certAuth.VerifyClient), + buildDirective("ssl_verify_depth", certAuth.ValidationDepth)) if certAuth.CRLFileName != "" { certDirectives = append(certDirectives, buildDirective("ssl_crl", certAuth.CRLFileName)) } @@ -132,19 +132,22 @@ func (c *Template) buildCertificateDirectives(server *ingress.Server) ngx_crossp prxSSL := server.ProxySSL if prxSSL.CAFileName != "" { - certDirectives = append(certDirectives, buildDirective("proxy_ssl_trusted_certificate", prxSSL.CAFileName)) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_ciphers", prxSSL.Ciphers)) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_protocols", strings.Split(prxSSL.Protocols, " "))) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_verify", prxSSL.Verify)) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_verify_depth", prxSSL.VerifyDepth)) + certDirectives = append(certDirectives, buildDirective("proxy_ssl_trusted_certificate", prxSSL.CAFileName), + buildDirective("proxy_ssl_ciphers", prxSSL.Ciphers), + buildDirective("proxy_ssl_protocols", strings.Split(prxSSL.Protocols, " ")), + buildDirective("proxy_ssl_verify", prxSSL.Verify), + buildDirective("proxy_ssl_verify_depth", prxSSL.VerifyDepth), + ) if prxSSL.ProxySSLName != "" { - certDirectives = append(certDirectives, buildDirective("proxy_ssl_name", prxSSL.ProxySSLName)) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_server_name", prxSSL.ProxySSLServerName)) + certDirectives = append(certDirectives, + buildDirective("proxy_ssl_name", prxSSL.ProxySSLName), + buildDirective("proxy_ssl_server_name", prxSSL.ProxySSLServerName)) } } if prxSSL.PemFileName != "" { - certDirectives = append(certDirectives, buildDirective("proxy_ssl_certificate", prxSSL.PemFileName)) - certDirectives = append(certDirectives, buildDirective("proxy_ssl_certificate_key", prxSSL.PemFileName)) + certDirectives = append(certDirectives, + buildDirective("proxy_ssl_certificate", prxSSL.PemFileName), + buildDirective("proxy_ssl_certificate_key", prxSSL.PemFileName)) } if server.SSLCiphers != "" { certDirectives = append(certDirectives, buildDirective("ssl_ciphers", server.SSLCiphers)) @@ -191,11 +194,12 @@ func (c *Template) buildDefaultBackend() *ngx_crossplane.Directive { fmt.Sprintf("backlog=%d", c.tplConfig.BacklogSize), )) } - serverBlock = append(serverBlock, buildDirective("set", "$proxy_upstream_name", "internal")) - serverBlock = append(serverBlock, buildDirective("access_log", "off")) - serverBlock = append(serverBlock, buildBlockDirective("location", []string{"/"}, ngx_crossplane.Directives{ - buildDirective("return", "404"), - })) + serverBlock = append(serverBlock, + buildDirective("set", "$proxy_upstream_name", "internal"), + buildDirective("access_log", "off"), + buildBlockDirective("location", []string{"/"}, ngx_crossplane.Directives{ + buildDirective("return", "404"), + })) return &ngx_crossplane.Directive{ Directive: "server", @@ -228,8 +232,8 @@ func (c *Template) buildHealthAndStatsServer() *ngx_crossplane.Directive { buildBlockDirective( "location", []string{"/configuration"}, ngx_crossplane.Directives{ - buildDirective("client_max_body_size", luaConfigurationRequestBodySize(c.tplConfig.Cfg)), - buildDirective("client_body_buffer_size", luaConfigurationRequestBodySize(c.tplConfig.Cfg)), + buildDirective("client_max_body_size", luaConfigurationRequestBodySize(&c.tplConfig.Cfg)), + buildDirective("client_body_buffer_size", luaConfigurationRequestBodySize(&c.tplConfig.Cfg)), buildDirective("proxy_buffering", "off"), buildDirective("content_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_configuration.lua"), }), diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index ea0ade3c9b..74b09e6f07 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -289,7 +289,7 @@ func httpListener(addresses []string, co []string, tc *config.TemplateConfig, ss return listeners } -func luaConfigurationRequestBodySize(cfg config.Configuration) string { +func luaConfigurationRequestBodySize(cfg *config.Configuration) string { size := cfg.LuaSharedDicts["configuration_data"] if size < cfg.LuaSharedDicts["certificate_data"] { size = cfg.LuaSharedDicts["certificate_data"] @@ -347,7 +347,7 @@ func shouldApplyGlobalAuth(location *ingress.Location, globalExternalAuthURL str // shouldApplyAuthUpstream returns true only in case when ExternalAuth.URL and // ExternalAuth.KeepaliveConnections are all set -func shouldApplyAuthUpstream(location *ingress.Location, cfg config.Configuration) bool { +func shouldApplyAuthUpstream(location *ingress.Location, cfg *config.Configuration) bool { if location.ExternalAuth.URL == "" || location.ExternalAuth.KeepaliveConnections == 0 { return false } diff --git a/test/e2e/annotations/affinity.go b/test/e2e/annotations/affinity.go index d2adc86a5f..629277ddf1 100644 --- a/test/e2e/annotations/affinity.go +++ b/test/e2e/annotations/affinity.go @@ -58,7 +58,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -80,7 +81,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -115,7 +117,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -181,7 +184,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -212,7 +216,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) local, err := time.LoadLocation("GMT") @@ -243,7 +248,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -265,7 +271,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -289,7 +296,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -312,7 +320,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -431,7 +440,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -454,7 +464,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -476,7 +487,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "listen 443") }) diff --git a/test/e2e/annotations/affinitymode.go b/test/e2e/annotations/affinitymode.go index a49a767219..7b5f47c565 100644 --- a/test/e2e/annotations/affinitymode.go +++ b/test/e2e/annotations/affinitymode.go @@ -56,7 +56,8 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // Check configuration @@ -89,7 +90,8 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // Check configuration diff --git a/test/e2e/annotations/auth.go b/test/e2e/annotations/auth.go index fd1734f354..5ecff23b43 100644 --- a/test/e2e/annotations/auth.go +++ b/test/e2e/annotations/auth.go @@ -419,7 +419,6 @@ http { f.EnsureIngress(ing2) f.WaitForNginxServer(host, func(server string) bool { - //nolint:goconst //server_name is a constant return strings.Contains(server, "server_name "+host) }) }) diff --git a/test/e2e/annotations/grpc.go b/test/e2e/annotations/grpc.go index 2a9c5a9835..049cf931d2 100644 --- a/test/e2e/annotations/grpc.go +++ b/test/e2e/annotations/grpc.go @@ -106,7 +106,6 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { return strings.Contains(server, "grpc_pass grpc://upstream_balancer;") }) - //nolint:goconst //string interpolation conn, err := grpc.NewClient(f.GetNginxIP()+":443", grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ diff --git a/test/e2e/annotations/limitconnections.go b/test/e2e/annotations/limitconnections.go index d44cb169c3..e660a233a8 100644 --- a/test/e2e/annotations/limitconnections.go +++ b/test/e2e/annotations/limitconnections.go @@ -41,7 +41,8 @@ var _ = framework.DescribeAnnotation("Annotation - limit-connections", func() { ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.SlowEchoService, 80, nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // limit connections diff --git a/test/e2e/annotations/upstreamhashby.go b/test/e2e/annotations/upstreamhashby.go index e5e3c5846e..43670c7174 100644 --- a/test/e2e/annotations/upstreamhashby.go +++ b/test/e2e/annotations/upstreamhashby.go @@ -36,7 +36,8 @@ func startIngress(f *framework.Framework, annotations map[string]string) map[str f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 02cc088164..b3eec60e76 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -321,7 +321,7 @@ func (f *Framework) matchNginxConditions(name string, matcher func(cfg string) b if name == "" { cmd = "cat /etc/nginx/nginx.conf" } else { - cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %s;/,/## end server %s;/'", name, name) + cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %s/,/## end server %s/'", name, name) } o, err := f.ExecCommand(f.pod, cmd) diff --git a/test/e2e/framework/httpexpect/response.go b/test/e2e/framework/httpexpect/response.go index e324e94ffa..1c7624752c 100644 --- a/test/e2e/framework/httpexpect/response.go +++ b/test/e2e/framework/httpexpect/response.go @@ -234,7 +234,6 @@ func (r *HTTPResponse) checkContentType(expectedType string, expectedCharset ... } if mediaType != expectedType { - //nolint:goconst //string interpolation r.chain.fail("\nexpected \"Content-Type\" header with %q media type,"+ "\nbut got %q", expectedType, mediaType) return false diff --git a/test/e2e/settings/disable_sync_events.go b/test/e2e/settings/disable_sync_events.go index 0d55c96e48..033fd9194a 100644 --- a/test/e2e/settings/disable_sync_events.go +++ b/test/e2e/settings/disable_sync_events.go @@ -44,7 +44,6 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-sync-events", func() { return strings.Contains(server, fmt.Sprintf("server_name %v", host)) }) - //nolint:goconst //string interpolation events, err := f.KubeClientSet.CoreV1().Events(ing.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: "reason=Sync,involvedObject.name=" + host}) assert.Nil(ginkgo.GinkgoT(), err, "listing events") diff --git a/test/e2e/settings/enable_real_ip.go b/test/e2e/settings/enable_real_ip.go index bf16e1ea04..778011b9fa 100644 --- a/test/e2e/settings/enable_real_ip.go +++ b/test/e2e/settings/enable_real_ip.go @@ -47,7 +47,6 @@ var _ = framework.DescribeSetting("enable-real-ip", func() { f.WaitForNginxServer(host, func(server string) bool { - //nolint:goconst //already a const return strings.Contains(server, "server_name "+host) && !strings.Contains(server, "proxy_set_header X-Forwarded-Proto $full_x_forwarded_proto;") }) From bc232c355e475ffd212649a42de9d22376674910 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Wed, 13 Nov 2024 06:39:58 -0700 Subject: [PATCH 15/17] Fix test assertion --- test/e2e/lua/dynamic_configuration.go | 3 ++- test/e2e/metrics/metrics.go | 3 ++- test/e2e/settings/badannotationvalues.go | 12 ++++++++---- test/e2e/settings/limit_rate.go | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/test/e2e/lua/dynamic_configuration.go b/test/e2e/lua/dynamic_configuration.go index 8f1deaeb12..a63a7a37d9 100644 --- a/test/e2e/lua/dynamic_configuration.go +++ b/test/e2e/lua/dynamic_configuration.go @@ -212,7 +212,8 @@ func createIngress(f *framework.Framework, host, deploymentName string) { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) } diff --git a/test/e2e/metrics/metrics.go b/test/e2e/metrics/metrics.go index 3cdb88057e..2100eefd2a 100644 --- a/test/e2e/metrics/metrics.go +++ b/test/e2e/metrics/metrics.go @@ -43,7 +43,8 @@ var _ = framework.IngressNginxDescribe("[metrics] exported prometheus metrics", f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) }) diff --git a/test/e2e/settings/badannotationvalues.go b/test/e2e/settings/badannotationvalues.go index bee12e66a2..4459a8da8b 100644 --- a/test/e2e/settings/badannotationvalues.go +++ b/test/e2e/settings/badannotationvalues.go @@ -50,7 +50,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, @@ -87,7 +88,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, @@ -123,7 +125,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(hostValid, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", hostValid)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", hostValid)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", hostValid)) }) f.WaitForNginxServer(hostValid, @@ -156,7 +159,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, diff --git a/test/e2e/settings/limit_rate.go b/test/e2e/settings/limit_rate.go index 16ce982778..41c54da852 100644 --- a/test/e2e/settings/limit_rate.go +++ b/test/e2e/settings/limit_rate.go @@ -41,7 +41,8 @@ var _ = framework.DescribeSetting("Configmap - limit-rate", func() { f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) wlKey := "limit-rate" From a2b5bea372aeaa869d2a0361d08e4c5ab54d77b1 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Wed, 13 Nov 2024 09:55:45 -0700 Subject: [PATCH 16/17] Fix golang-lint findings --- .golangci.yml | 1 - .../ingress/controller/controller_test.go | 2 +- .../template/crossplane/authlocation.go | 3 +- .../controller/template/crossplane/cors.go | 5 ++- .../crossplane/extramodules/analyze.go | 2 ++ .../controller/template/crossplane/http.go | 30 ++++++++-------- .../template/crossplane/location.go | 35 ++++--------------- .../controller/template/crossplane/server.go | 6 ++-- .../controller/template/crossplane/utils.go | 27 +++++++------- .../ingress/controller/template/template.go | 2 +- test/e2e/annotations/canary.go | 1 - test/e2e/annotations/cors.go | 1 - test/e2e/settings/proxy_host.go | 2 -- 13 files changed, 46 insertions(+), 71 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2d73e14e77..feaf26b285 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,6 @@ linters: - errname - ginkgolinter - gocheckcompilerdirectives - - goconst - gocritic - gocyclo - godox diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go index a830a57294..022f23e8eb 100644 --- a/internal/ingress/controller/controller_test.go +++ b/internal/ingress/controller/controller_test.go @@ -157,7 +157,7 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) { type fakeTemplate struct{} -func (fakeTemplate) Validate(filename string) error { +func (fakeTemplate) Validate(_ string) error { return nil } diff --git a/internal/ingress/controller/template/crossplane/authlocation.go b/internal/ingress/controller/template/crossplane/authlocation.go index ef0dd8e179..113f288785 100644 --- a/internal/ingress/controller/template/crossplane/authlocation.go +++ b/internal/ingress/controller/template/crossplane/authlocation.go @@ -86,7 +86,8 @@ func buildExternalAuth(cfg any) *externalAuth { } func (c *Template) buildAuthLocation(server *ingress.Server, - location *ingress.Location, locationConfig locationCfg) *ngx_crossplane.Directive { + location *ingress.Location, locationConfig locationCfg, +) *ngx_crossplane.Directive { locationDirectives := ngx_crossplane.Directives{ buildDirective("internal"), } diff --git a/internal/ingress/controller/template/crossplane/cors.go b/internal/ingress/controller/template/crossplane/cors.go index 932f25dbbc..252fa17029 100644 --- a/internal/ingress/controller/template/crossplane/cors.go +++ b/internal/ingress/controller/template/crossplane/cors.go @@ -23,11 +23,10 @@ import ( "k8s.io/ingress-nginx/internal/ingress/annotations/cors" ) -func buildCorsDirectives(locationcors cors.Config) ngx_crossplane.Directives { +func buildCorsDirectives(locationcors *cors.Config) ngx_crossplane.Directives { directives := make(ngx_crossplane.Directives, 0) if len(locationcors.CorsAllowOrigin) > 0 { directives = append(directives, buildCorsOriginRegex(locationcors.CorsAllowOrigin)...) - } directives = append(directives, buildBlockDirective("if", @@ -43,7 +42,7 @@ func buildCorsDirectives(locationcors cors.Config) ngx_crossplane.Directives { } // commonCorsDirective builds the common cors directives for a location -func commonCorsDirective(cfg cors.Config, options bool) *ngx_crossplane.Directive { +func commonCorsDirective(cfg *cors.Config, options bool) *ngx_crossplane.Directive { corsDir := "true" if options { corsDir = "trueoptions" diff --git a/internal/ingress/controller/template/crossplane/extramodules/analyze.go b/internal/ingress/controller/template/crossplane/extramodules/analyze.go index 22d227d30d..0b6d2f335a 100644 --- a/internal/ingress/controller/template/crossplane/extramodules/analyze.go +++ b/internal/ingress/controller/template/crossplane/extramodules/analyze.go @@ -70,6 +70,8 @@ const ( // helpful directive location alias describing "any" context // doesn't include ngxHTTPSifConf, ngxHTTPLifConf, ngxHTTPLmtConf, or ngxMgmtMainConf. +// +//nolint:unused // This file is generated const ngxAnyConf = ngxMainConf | ngxEventConf | ngxMailMainConf | ngxMailSrvConf | ngxStreamMainConf | ngxStreamSrvConf | ngxStreamUpsConf | ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxHTTPUpsConf | diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index 381ef67a43..02ff72c9aa 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -85,7 +85,7 @@ func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { return httpBlock } -//nolint:gocyclo +//nolint:gocyclo // Function is what it is func (c *Template) buildHTTP() { cfg := c.tplConfig.Cfg httpBlock := c.initHTTPDirectives() @@ -140,10 +140,10 @@ func (c *Template) buildHTTP() { } if cfg.EnableBrotli { - httpBlock = append(httpBlock, buildDirective("brotli", "on")) - httpBlock = append(httpBlock, buildDirective("brotli_comp_level", cfg.BrotliLevel)) - httpBlock = append(httpBlock, buildDirective("brotli_min_length", cfg.BrotliMinLength)) - httpBlock = append(httpBlock, buildDirective("brotli_types", cfg.BrotliTypes)) + httpBlock = append(httpBlock, buildDirective("brotli", "on"), + buildDirective("brotli_comp_level", cfg.BrotliLevel), + buildDirective("brotli_min_length", cfg.BrotliMinLength), + buildDirective("brotli_types", cfg.BrotliTypes)) } if (c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers)) && @@ -293,16 +293,17 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildBlockDirective("upstream", []string{"upstream_balancer"}, blockUpstreamDirectives)) // Adding Rate limit - for _, rl := range filterRateLimits(c.tplConfig.Servers) { - id := fmt.Sprintf("$allowlist_%s", rl.ID) - httpBlock = append(httpBlock, buildDirective("#", "Ratelimit", rl.Name)) + rl := filterRateLimits(c.tplConfig.Servers) + for i := range rl { + id := fmt.Sprintf("$allowlist_%s", rl[i].ID) + httpBlock = append(httpBlock, buildDirective("#", "Ratelimit", rl[i].Name)) rlDirectives := ngx_crossplane.Directives{ buildDirective("default", 0), } - for _, ip := range rl.Allowlist { + for _, ip := range rl[i].Allowlist { rlDirectives = append(rlDirectives, buildDirective(ip, "1")) } - mapRateLimitDirective := buildMapDirective(id, fmt.Sprintf("$limit_%s", rl.ID), ngx_crossplane.Directives{ + mapRateLimitDirective := buildMapDirective(id, fmt.Sprintf("$limit_%s", rl[i].ID), ngx_crossplane.Directives{ buildDirective("0", cfg.LimitConnZoneVariable), buildDirective("1", ""), }) @@ -343,10 +344,11 @@ func (c *Template) buildHTTP() { if redirectServers, ok := c.tplConfig.RedirectServers.([]*utilingress.Redirect); ok { for _, server := range redirectServers { - httpBlock = append(httpBlock, buildStartServer(server.From)) - serverBlock := c.buildRedirectServer(server) - httpBlock = append(httpBlock, serverBlock) - httpBlock = append(httpBlock, buildEndServer(server.From)) + httpBlock = append(httpBlock, + buildStartServer(server.From), + c.buildRedirectServer(server), + buildEndServer(server.From), + ) } } diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go index ddb28a7b19..bc7c5685ed 100644 --- a/internal/ingress/controller/template/crossplane/location.go +++ b/internal/ingress/controller/template/crossplane/location.go @@ -107,7 +107,6 @@ func buildCustomErrorLocationsPerServer(server *ingress.Server, enableMetrics bo errorLocationsDirectives = append(errorLocationsDirectives, buildCustomErrorLocation(errorLocations[i].UpstreamName, errorLocations[i].Codes, enableMetrics)...) } return errorLocationsDirectives - } func buildCustomErrorLocation(upstreamName string, errorCodes []int, enableMetrics bool) ngx_crossplane.Directives { @@ -199,7 +198,7 @@ func (c *Template) buildServerLocations(server *ingress.Server, locations []*ing buildDirective("add_header", "Set-Cookie", "$auth_cookie"), } if location.CorsConfig.CorsEnabled { - directives = append(directives, buildCorsDirectives(location.CorsConfig)...) + directives = append(directives, buildCorsDirectives(&location.CorsConfig)...) } directives = append(directives, buildDirective("return", @@ -208,17 +207,15 @@ func (c *Template) buildServerLocations(server *ingress.Server, locations []*ing serverLocations = append(serverLocations, buildBlockDirective("location", []string{buildAuthSignURLLocation(location.Path, locationConfig.externalAuth.SigninURL)}, directives)) - } serverLocations = append(serverLocations, c.buildLocation(server, location, locationConfig)) - } - return serverLocations } func (c *Template) buildLocation(server *ingress.Server, - location *ingress.Location, locationConfig locationCfg) *ngx_crossplane.Directive { + location *ingress.Location, locationConfig locationCfg, +) *ngx_crossplane.Directive { ing := getIngressInformation(location.Ingress, server.Hostname, location.IngressPath) cfg := c.tplConfig locationDirectives := ngx_crossplane.Directives{ @@ -294,7 +291,7 @@ func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingres } if location.CorsConfig.CorsEnabled { - dir = append(dir, buildCorsDirectives(location.CorsConfig)...) + dir = append(dir, buildCorsDirectives(&location.CorsConfig)...) } if !isLocationInLocationList(location, c.tplConfig.Cfg.NoAuthLocations) { @@ -686,8 +683,8 @@ func buildAuthLocationConfig(location *ingress.Location, locationConfig location directives := make(ngx_crossplane.Directives, 0) if locationConfig.authPath != "" { if locationConfig.applyAuthUpstream && !locationConfig.applyGlobalAuth { - directives = append(directives, buildDirective("set", "$auth_cookie", "")) - directives = append(directives, buildDirective("add_header", "Set-Cookie", "$auth_cookie")) + directives = append(directives, buildDirective("set", "$auth_cookie", ""), + buildDirective("add_header", "Set-Cookie", "$auth_cookie")) directives = append(directives, buildAuthResponseHeaders(locationConfig.proxySetHeader, locationConfig.externalAuth.ResponseHeaders, true)...) if len(locationConfig.externalAuth.ResponseHeaders) > 0 { directives = append(directives, buildDirective("set", "$auth_response_headers", strings.Join(locationConfig.externalAuth.ResponseHeaders, ","))) @@ -733,24 +730,4 @@ func buildAuthLocationConfig(location *ingress.Location, locationConfig location } return directives - /* - Missing this Lua script - # `auth_request` module does not support HTTP keepalives in upstream block: - # https://trac.nginx.org/nginx/ticket/1579 - access_by_lua_block { - local res = ngx.location.capture('{{ $authPath }}', { method = ngx.HTTP_GET, body = '', share_all_vars = {{ $externalAuth.KeepaliveShareVars }} }) - if res.status == ngx.HTTP_OK then - ngx.var.auth_cookie = res.header['Set-Cookie'] - {{- range $line := buildAuthUpstreamLuaHeaders $externalAuth.ResponseHeaders }} # IF 4 - {{ $line }} - {{- end }} # END IF 4 - return - end - if res.status == ngx.HTTP_UNAUTHORIZED or res.status == ngx.HTTP_FORBIDDEN then - ngx.exit(res.status) - end - ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) - } - - */ } diff --git a/internal/ingress/controller/template/crossplane/server.go b/internal/ingress/controller/template/crossplane/server.go index c81003b0c3..a78a5a351a 100644 --- a/internal/ingress/controller/template/crossplane/server.go +++ b/internal/ingress/controller/template/crossplane/server.go @@ -37,7 +37,7 @@ func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane. buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), } - serverBlock = append(serverBlock, buildListener(*c.tplConfig, server.Hostname)...) + serverBlock = append(serverBlock, buildListener(c.tplConfig, server.Hostname)...) serverBlock = append(serverBlock, c.buildBlockers()...) if server.Hostname == "_" { @@ -62,7 +62,6 @@ func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane. // The other locations should come here! serverBlock = append(serverBlock, c.buildServerLocations(server, server.Locations)...) - } // "/healthz" location @@ -101,7 +100,6 @@ func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane. // End of "nginx_status" location serverBlock = append(serverBlock, buildBlockDirective("location", []string{"/nginx_status"}, statusLocationDirs)) - } // DO NOT MOVE! THIS IS THE END DIRECTIVE OF SERVERS @@ -167,7 +165,7 @@ func (c *Template) buildRedirectServer(server *utilingress.Redirect) *ngx_crossp buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), buildDirective("set_by_lua_file", "$redirect_to", "/etc/nginx/lua/nginx/ngx_srv_redirect.lua", server.To), } - serverBlock = append(serverBlock, buildListener(*c.tplConfig, server.From)...) + serverBlock = append(serverBlock, buildListener(c.tplConfig, server.From)...) serverBlock = append(serverBlock, c.buildBlockers()...) serverBlock = append(serverBlock, buildDirective("return", c.tplConfig.Cfg.HTTPRedirectCode, "$redirect_to")) diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index 74b09e6f07..eef9b87ca2 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -17,7 +17,7 @@ limitations under the License. package crossplane import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // We cannot move away from sha1 "encoding/base64" "encoding/hex" "fmt" @@ -59,10 +59,12 @@ var ( defaultGlobalAuthRedirectParam = "rd" ) -type seconds int -type minutes int +type ( + seconds int + minutes int +) -func buildDirectiveWithComment(directive string, comment string, args ...any) *ngx_crossplane.Directive { +func buildDirectiveWithComment(directive, comment string, args ...any) *ngx_crossplane.Directive { dir := buildDirective(directive, args...) dir.Comment = ptr.To(comment) return dir @@ -213,25 +215,25 @@ func buildServerName(hostname string) string { return `~^(?[\w-]+)\.` + strings.Join(parts, "\\.") + `$` } -func buildListener(tc config.TemplateConfig, hostname string) ngx_crossplane.Directives { +func buildListener(tc *config.TemplateConfig, hostname string) ngx_crossplane.Directives { listenDirectives := make(ngx_crossplane.Directives, 0) - co := commonListenOptions(&tc, hostname) + co := commonListenOptions(tc, hostname) addrV4 := []string{""} if len(tc.Cfg.BindAddressIpv4) > 0 { addrV4 = tc.Cfg.BindAddressIpv4 } - listenDirectives = append(listenDirectives, httpListener(addrV4, co, &tc, false)...) - listenDirectives = append(listenDirectives, httpListener(addrV4, co, &tc, true)...) + listenDirectives = append(listenDirectives, httpListener(addrV4, co, tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV4, co, tc, true)...) if tc.IsIPV6Enabled { addrV6 := []string{"[::]"} if len(tc.Cfg.BindAddressIpv6) > 0 { addrV6 = tc.Cfg.BindAddressIpv6 } - listenDirectives = append(listenDirectives, httpListener(addrV6, co, &tc, false)...) - listenDirectives = append(listenDirectives, httpListener(addrV6, co, &tc, true)...) + listenDirectives = append(listenDirectives, httpListener(addrV6, co, tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV6, co, tc, true)...) } return listenDirectives @@ -258,7 +260,7 @@ func commonListenOptions(template *config.TemplateConfig, hostname string) []str return out } -func httpListener(addresses []string, co []string, tc *config.TemplateConfig, ssl bool) ngx_crossplane.Directives { +func httpListener(addresses, co []string, tc *config.TemplateConfig, ssl bool) ngx_crossplane.Directives { listeners := make(ngx_crossplane.Directives, 0) port := tc.ListenPorts.HTTP isTLSProxy := tc.IsSSLPassthroughEnabled @@ -400,7 +402,7 @@ func changeHostPort(newURL, value string) string { } func buildAuthSignURLLocation(location, authSignURL string) string { - hasher := sha1.New() // #nosec + hasher := sha1.New() //nolint:gosec // We cannot move away from sha1 hasher.Write([]byte(location)) hasher.Write([]byte(authSignURL)) return "@" + hex.EncodeToString(hasher.Sum(nil)) @@ -558,7 +560,6 @@ func buildProxyPass(backends []*ingress.Backend, location *ingress.Location) ngx } func buildGeoIPDirectives(reloadTime int, files []string) ngx_crossplane.Directives { - directives := make(ngx_crossplane.Directives, 0) buildGeoIPBlock := func(file string, directives ngx_crossplane.Directives) *ngx_crossplane.Directive { if reloadTime > 0 && file != "GeoIP2-Connection-Type.mmdb" { diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index e420b9bcfb..b976ec142a 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -234,7 +234,7 @@ type LuaListenPorts struct { } // Validate is no-op at go-template -func (t *Template) Validate(filename string) error { +func (t *Template) Validate(_ string) error { return nil } diff --git a/test/e2e/annotations/canary.go b/test/e2e/annotations/canary.go index 443a3a486e..3632ed6967 100644 --- a/test/e2e/annotations/canary.go +++ b/test/e2e/annotations/canary.go @@ -1106,7 +1106,6 @@ var _ = framework.DescribeAnnotation("canary-*", func() { !strings.Contains(server, `set $proxy_upstream_name "pstream-default-backend;`) && !strings.Contains(server, canaryUpstreamNameCrossplane) && strings.Contains(server, upstreamNameCrossplane)) - }) }) diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go index 1db7d53741..57206b2e67 100644 --- a/test/e2e/annotations/cors.go +++ b/test/e2e/annotations/cors.go @@ -86,7 +86,6 @@ var _ = framework.DescribeAnnotation("cors-*", func() { func(server string) bool { return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: POST, GET';") || strings.Contains(server, `more_set_headers "Access-Control-Allow-Methods: POST, GET";`) - }) }) diff --git a/test/e2e/settings/proxy_host.go b/test/e2e/settings/proxy_host.go index 1fcda11a13..afb37c372c 100644 --- a/test/e2e/settings/proxy_host.go +++ b/test/e2e/settings/proxy_host.go @@ -34,7 +34,6 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host", func() { - h := make(map[string]string) h["Custom-Header"] = "$proxy_host" cfgMap := "add-headers-configmap" @@ -60,7 +59,6 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host using the upstream-vhost annotation value", func() { - h := make(map[string]string) h["Custom-Header"] = "$proxy_host" cfgMap := "add-headers-configmap" From f64c255d0356ba76d67d3c260fa935fbad1fcbcc Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Sun, 12 Jan 2025 20:08:27 +0000 Subject: [PATCH 17/17] Add missing directives --- go.mod | 9 +++++---- go.sum | 14 ++++++++------ .../controller/template/crossplane/authlocation.go | 1 + .../ingress/controller/template/crossplane/http.go | 4 ++++ .../controller/template/crossplane/location.go | 5 +++++ 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index efa9f575a0..5bd093e0cf 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/moul/pb v0.0.0-20220425114252-bca18df4138c github.com/ncabatoff/process-exporter v0.8.5 + github.com/nginxinc/nginx-go-crossplane v0.4.70 github.com/onsi/ginkgo/v2 v2.22.2 github.com/opencontainers/runc v1.2.4 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 @@ -50,7 +51,7 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/jstemmer/go-junit-report v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect - github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 // indirect + github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.31.0 // indirect @@ -116,14 +117,14 @@ require ( github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.29.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect @@ -131,7 +132,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/kustomize/api v0.18.0 // indirect sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect diff --git a/go.sum b/go.sum index c6f0fe38bb..217b834d23 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 h1:9WsegDYiSKtZXru+NcOB4z7iqb00n4atjmQlyy5TRXI= -github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0/go.mod h1:TeVdzh+5QB5IpWDJAU/uviXA6kOg9yXzLrrjeLKJXqY= +github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU= +github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -160,6 +160,8 @@ github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 h1:t4WWQ9I797y7QU github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833/go.mod h1:0CznHmXSjMEqs5Tezj/w2emQoM41wzYM9KpDKUHPYag= github.com/ncabatoff/process-exporter v0.8.5 h1:Hk1sflgRWn0Xrh/OsupQLVVCTW01kv0YYrGxu7NvkmM= github.com/ncabatoff/process-exporter v0.8.5/go.mod h1:IZndG/m2Y++D90y99NhDJfg0SOkpbx/Fl6MlnBr4SC0= +github.com/nginxinc/nginx-go-crossplane v0.4.70 h1:zrmF8rk97TgB3uy5QtRZ4mY0tFAdhKG97H3XAM2LOBA= +github.com/nginxinc/nginx-go-crossplane v0.4.70/go.mod h1:o0dpGb2Nw1nYKHp8+b2dCxwrMWXGkjaVsZL1Jm3ouvQ= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -263,8 +265,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -302,8 +304,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/ingress/controller/template/crossplane/authlocation.go b/internal/ingress/controller/template/crossplane/authlocation.go index 113f288785..395880d0a4 100644 --- a/internal/ingress/controller/template/crossplane/authlocation.go +++ b/internal/ingress/controller/template/crossplane/authlocation.go @@ -179,6 +179,7 @@ func (c *Template) buildAuthLocation(server *ingress.Server, buildDirective("proxy_buffer_size", location.Proxy.BufferSize), buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + buildDirective("proxy_busy_buffers_size", location.Proxy.BusyBuffersSize), ) if isValidByteSize(location.Proxy.BodySize, true) { diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index 02ff72c9aa..efa5d6623e 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -240,6 +240,10 @@ func (c *Template) buildHTTP() { httpBlock = append(httpBlock, buildDirective("proxy_intercept_errors", "on")) } + if cfg.RelativeRedirects { + httpBlock = append(httpBlock, buildDirective("absolute_redirect", false)) + } + httpUpgradeMap := ngx_crossplane.Directives{buildDirective("default", "upgrade")} if cfg.UpstreamKeepaliveConnections < 1 { httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "close")) diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go index bc7c5685ed..6e967f77d7 100644 --- a/internal/ingress/controller/template/crossplane/location.go +++ b/internal/ingress/controller/template/crossplane/location.go @@ -346,6 +346,7 @@ func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingres buildDirective("proxy_buffer_size", location.Proxy.BufferSize), buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + buildDirective("proxy_busy_buffers_size", location.Proxy.BusyBuffersSize), buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion), buildDirective("proxy_cookie_domain", strings.Split(location.Proxy.CookieDomain, " ")), buildDirective("proxy_cookie_path", strings.Split(location.Proxy.CookiePath, " ")), @@ -398,6 +399,10 @@ func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingres dir = append(dir, buildDirective("satisfy", location.Satisfy)) } + if location.Redirect.Relative { + dir = append(dir, buildDirective("absolute_redirect", false)) + } + if len(location.CustomHTTPErrors) > 0 && !location.DisableProxyInterceptErrors { dir = append(dir, buildDirective("proxy_intercept_errors", "on")) }