Fix golangci-lint errors (#10196)

* Fix golangci-lint errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix dupl errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix comments

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix errcheck lint errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix assert in e2e test

Signed-off-by: z1cheng <imchench@gmail.com>

* Not interrupt the waitForPodsReady

Signed-off-by: z1cheng <imchench@gmail.com>

* Replace string with constant

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix comments

Signed-off-by: z1cheng <imchench@gmail.com>

* Revert write file permision

Signed-off-by: z1cheng <imchench@gmail.com>

---------

Signed-off-by: z1cheng <imchench@gmail.com>
This commit is contained in:
Chen Chen 2023-08-31 15:36:48 +08:00 committed by GitHub
parent 46d87d3462
commit b3060bfbd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
253 changed files with 2434 additions and 2113 deletions

View file

@ -18,11 +18,12 @@ package main
import ( import (
"fmt" "fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"net/http" "net/http"
"os" "os"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/ingress-nginx/internal/ingress/controller" "k8s.io/ingress-nginx/internal/ingress/controller"

View file

@ -114,7 +114,6 @@ func main() {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
} }
func backendsAll() { func backendsAll() {
@ -155,10 +154,16 @@ func backendsList() {
fmt.Println(unmarshalErr) fmt.Println(unmarshalErr)
return return
} }
backends := f.([]interface{}) backends, ok := f.([]interface{})
if !ok {
fmt.Printf("unexpected type: %T", f)
}
for _, backendi := range backends { for _, backendi := range backends {
backend := backendi.(map[string]interface{}) backend, ok := backendi.(map[string]interface{})
if !ok {
fmt.Printf("unexpected type: %T", backendi)
}
fmt.Println(backend["name"].(string)) fmt.Println(backend["name"].(string))
} }
} }
@ -180,12 +185,22 @@ func backendsGet(name string) {
fmt.Println(unmarshalErr) fmt.Println(unmarshalErr)
return return
} }
backends := f.([]interface{}) backends, ok := f.([]interface{})
if !ok {
fmt.Printf("unexpected type: %T", f)
}
for _, backendi := range backends { for _, backendi := range backends {
backend := backendi.(map[string]interface{}) backend, ok := backendi.(map[string]interface{})
if !ok {
fmt.Printf("unexpected type: %T", backendi)
}
if backend["name"].(string) == name { if backend["name"].(string) == name {
printed, _ := json.MarshalIndent(backend, "", " ") printed, err := json.MarshalIndent(backend, "", " ")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(printed)) fmt.Println(string(printed))
return return
} }
@ -213,18 +228,7 @@ func certGet(host string) {
} }
func general() { func general() {
//TODO: refactor to obtain ingress-nginx pod count from the api server // TODO: refactor to obtain ingress-nginx pod count from the api server
/*
statusCode, body, requestErr := nginx.NewGetStatusRequest(generalPath)
if requestErr != nil {
fmt.Println(requestErr)
return
}
if statusCode != 200 {
fmt.Printf("Nginx returned code %v\n", statusCode)
return
}
*/
var prettyBuffer bytes.Buffer var prettyBuffer bytes.Buffer
indentErr := json.Indent(&prettyBuffer, []byte("{}"), "", " ") indentErr := json.Indent(&prettyBuffer, []byte("{}"), "", " ")

View file

@ -47,5 +47,4 @@ func logger(address string) {
server.Wait() server.Wait()
klog.Infof("Stopping logger") klog.Infof("Stopping logger")
} }

View file

@ -153,7 +153,6 @@ func main() {
if errExists == nil { if errExists == nil {
conf.IsChroot = true conf.IsChroot = true
go logger(conf.InternalLoggerAddress) go logger(conf.InternalLoggerAddress)
} }
go metrics.StartHTTPServer(conf.HealthCheckHost, conf.ListenPorts.Health, mux) go metrics.StartHTTPServer(conf.HealthCheckHost, conf.ListenPorts.Health, mux)
@ -282,10 +281,10 @@ func checkService(key string, kubeClient *kubernetes.Clientset) error {
} }
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
return fmt.Errorf("No service with name %v found in namespace %v: %v", name, ns, err) return fmt.Errorf("no service with name %v found in namespace %v: %v", name, ns, err)
} }
return fmt.Errorf("Unexpected error searching service with name %v in namespace %v: %v", name, ns, err) return fmt.Errorf("unexpected error searching service with name %v in namespace %v: %v", name, ns, err)
} }
return nil return nil

View file

@ -47,7 +47,7 @@ func TestCreateApiserverClient(t *testing.T) {
func init() { func init() {
// the default value of nginx.TemplatePath assumes the template exists in // the default value of nginx.TemplatePath assumes the template exists in
// the root filesystem and not in the rootfs directory // the root filesystem and not in the rootfs directory
path, err := filepath.Abs(filepath.Join("../../rootfs/", nginx.TemplatePath)) path, err := filepath.Abs(filepath.Join("..", "..", "rootfs", nginx.TemplatePath))
if err == nil { if err == nil {
nginx.TemplatePath = path nginx.TemplatePath = path
} }
@ -87,14 +87,14 @@ func TestHandleSigterm(t *testing.T) {
ingressflags.ResetForTesting(func() { t.Fatal("bad parse") }) ingressflags.ResetForTesting(func() { t.Fatal("bad parse") })
os.Setenv("POD_NAME", podName) t.Setenv("POD_NAME", podName)
os.Setenv("POD_NAMESPACE", namespace) t.Setenv("POD_NAMESPACE", namespace)
oldArgs := os.Args oldArgs := os.Args
defer func() { defer func() {
os.Setenv("POD_NAME", "") t.Setenv("POD_NAME", "")
os.Setenv("POD_NAMESPACE", "") t.Setenv("POD_NAMESPACE", "")
os.Args = oldArgs os.Args = oldArgs
}() }()

View file

@ -63,13 +63,14 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
return cmd return cmd
} }
func backends(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, backend string, onlyList bool) error { func backends(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container, backend string, onlyList bool) error {
var command []string var command []string
if onlyList { switch {
case onlyList:
command = []string{"/dbg", "backends", "list"} command = []string{"/dbg", "backends", "list"}
} else if backend != "" { case backend != "":
command = []string{"/dbg", "backends", "get", backend} command = []string{"/dbg", "backends", "get", backend}
} else { default:
command = []string{"/dbg", "backends", "all"} command = []string{"/dbg", "backends", "all"}
} }

View file

@ -59,7 +59,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
return cmd return cmd
} }
func certs(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, host string) error { func certs(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container, host string) error {
command := []string{"/dbg", "certs", "get", host} command := []string{"/dbg", "certs", "get", host}
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)

View file

@ -55,7 +55,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
return cmd return cmd
} }
func conf(flags *genericclioptions.ConfigFlags, host string, podName string, deployment string, selector string, container string) error { func conf(flags *genericclioptions.ConfigFlags, host, podName, deployment, selector, container string) error {
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)
if err != nil { if err != nil {
return err return err

View file

@ -55,7 +55,7 @@ type execFlags struct {
Stdin bool Stdin bool
} }
func exec(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, cmd []string, opts execFlags) error { func exec(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string, cmd []string, opts execFlags) error {
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)
if err != nil { if err != nil {
return err return err

View file

@ -47,7 +47,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
return cmd return cmd
} }
func general(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string) error { func general(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string) error {
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)
if err != nil { if err != nil {
return err return err

View file

@ -74,9 +74,9 @@ func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces
if host != "" { if host != "" {
rowsWithHost := make([]ingressRow, 0) rowsWithHost := make([]ingressRow, 0)
for _, row := range rows { for i := range rows {
if row.Host == host { if rows[i].Host == host {
rowsWithHost = append(rowsWithHost, row) rowsWithHost = append(rowsWithHost, rows[i])
} }
} }
rows = rowsWithHost rows = rowsWithHost
@ -91,7 +91,8 @@ func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces
fmt.Fprintln(printer, "INGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT\tENDPOINTS") fmt.Fprintln(printer, "INGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT\tENDPOINTS")
} }
for _, row := range rows { for i := range rows {
row := &rows[i]
var tlsMsg string var tlsMsg string
if row.TLS { if row.TLS {
tlsMsg = "YES" tlsMsg = "YES"
@ -134,8 +135,8 @@ type ingressRow struct {
func getIngressRows(ingresses *[]networking.Ingress) []ingressRow { func getIngressRows(ingresses *[]networking.Ingress) []ingressRow {
rows := make([]ingressRow, 0) rows := make([]ingressRow, 0)
for _, ing := range *ingresses { for i := range *ingresses {
ing := &(*ingresses)[i]
address := "" address := ""
for _, lbIng := range ing.Status.LoadBalancer.Ingress { for _, lbIng := range ing.Status.LoadBalancer.Ingress {
if len(lbIng.IP) > 0 { if len(lbIng.IP) > 0 {
@ -182,7 +183,7 @@ func getIngressRows(ingresses *[]networking.Ingress) []ingressRow {
for _, rule := range ing.Spec.Rules { for _, rule := range ing.Spec.Rules {
_, hasTLS := tlsHosts[rule.Host] _, hasTLS := tlsHosts[rule.Host]
//Handle ingress with no paths // Handle ingress with no paths
if rule.HTTP == nil { if rule.HTTP == nil {
row := ingressRow{ row := ingressRow{
Namespace: ing.Namespace, Namespace: ing.Namespace,

View file

@ -24,7 +24,6 @@ import (
) )
func TestGetIngressInformation(t *testing.T) { func TestGetIngressInformation(t *testing.T) {
testcases := map[string]struct { testcases := map[string]struct {
ServiceBackend *networking.IngressServiceBackend ServiceBackend *networking.IngressServiceBackend
wantName string wantName string

View file

@ -111,11 +111,13 @@ type lintOptions struct {
} }
func (opts *lintOptions) Validate() error { func (opts *lintOptions) Validate() error {
//nolint:dogsled // Ignore 3 blank identifiers
_, _, _, err := util.ParseVersionString(opts.versionFrom) _, _, _, err := util.ParseVersionString(opts.versionFrom)
if err != nil { if err != nil {
return err return err
} }
//nolint:dogsled // Ignore 3 blank identifiers
_, _, _, err = util.ParseVersionString(opts.versionTo) _, _, _, err = util.ParseVersionString(opts.versionTo)
if err != nil { if err != nil {
return err return err
@ -131,9 +133,9 @@ type lint interface {
Version() string Version() string
} }
func checkObjectArray(lints []lint, objects []kmeta.Object, opts lintOptions) { func checkObjectArray(allLints []lint, objects []kmeta.Object, opts lintOptions) {
usedLints := make([]lint, 0) usedLints := make([]lint, 0)
for _, lint := range lints { for _, lint := range allLints {
lintVersion := lint.Version() lintVersion := lint.Version()
if lint.Version() == "" { if lint.Version() == "" {
lintVersion = "0.0.0" lintVersion = "0.0.0"
@ -189,7 +191,7 @@ func ingresses(opts lintOptions) error {
return err return err
} }
var iLints []lints.IngressLint = lints.GetIngressLints() iLints := lints.GetIngressLints()
genericLints := make([]lint, len(iLints)) genericLints := make([]lint, len(iLints))
for i := range iLints { for i := range iLints {
genericLints[i] = iLints[i] genericLints[i] = iLints[i]
@ -216,7 +218,7 @@ func deployments(opts lintOptions) error {
return err return err
} }
var iLints []lints.DeploymentLint = lints.GetDeploymentLints() iLints := lints.GetDeploymentLints()
genericLints := make([]lint, len(iLints)) genericLints := make([]lint, len(iLints))
for i := range iLints { for i := range iLints {
genericLints[i] = iLints[i] genericLints[i] = iLints[i]

View file

@ -95,7 +95,7 @@ func (o *logsFlags) toStrings() []string {
return r return r
} }
func logs(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string, opts logsFlags) error { func logs(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string, opts logsFlags) error {
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)
if err != nil { if err != nil {
return err return err

View file

@ -45,7 +45,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
return cmd return cmd
} }
func ssh(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string, container string) error { func ssh(flags *genericclioptions.ConfigFlags, podName, deployment, selector, container string) error {
pod, err := request.ChoosePod(flags, podName, deployment, selector) pod, err := request.ChoosePod(flags, podName, deployment, selector)
if err != nil { if err != nil {
return err return err

View file

@ -38,11 +38,11 @@ func PodExecString(flags *genericclioptions.ConfigFlags, pod *apiv1.Pod, contain
// ExecToString runs a kubectl subcommand and returns stdout as a string // ExecToString runs a kubectl subcommand and returns stdout as a string
func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, error) { func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, error) {
kArgs := getKubectlConfigFlags(flags) kubectlArgs := getKubectlConfigFlags(flags)
kArgs = append(kArgs, args...) kubectlArgs = append(kubectlArgs, args...)
buf := bytes.NewBuffer(make([]byte, 0)) buf := bytes.NewBuffer(make([]byte, 0))
err := execToWriter(append([]string{"kubectl"}, kArgs...), buf) err := execToWriter(append([]string{"kubectl"}, kubectlArgs...), buf)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -51,9 +51,9 @@ func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string,
// Exec replaces the current process with a kubectl invocation // Exec replaces the current process with a kubectl invocation
func Exec(flags *genericclioptions.ConfigFlags, args []string) error { func Exec(flags *genericclioptions.ConfigFlags, args []string) error {
kArgs := getKubectlConfigFlags(flags) kubectlArgs := getKubectlConfigFlags(flags)
kArgs = append(kArgs, args...) kubectlArgs = append(kubectlArgs, args...)
return execCommand(append([]string{"kubectl"}, kArgs...)) return execCommand(append([]string{"kubectl"}, kubectlArgs...))
} }
// Replaces the currently running process with the given command // Replaces the currently running process with the given command
@ -70,6 +70,7 @@ func execCommand(args []string) error {
// Runs a command and returns stdout // Runs a command and returns stdout
func execToWriter(args []string, writer io.Writer) error { func execToWriter(args []string, writer io.Writer) error {
//nolint:gosec // Ignore G204 error
cmd := exec.Command(args[0], args[1:]...) cmd := exec.Command(args[0], args[1:]...)
op, err := cmd.StdoutPipe() op, err := cmd.StdoutPipe()
@ -78,7 +79,7 @@ func execToWriter(args []string, writer io.Writer) error {
} }
go func() { go func() {
io.Copy(writer, op) //nolint:errcheck io.Copy(writer, op) //nolint:errcheck // Ignore the error
}() }()
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
@ -106,7 +107,6 @@ func getKubectlConfigFlags(flags *genericclioptions.ConfigFlags) []string {
appendStringFlag(o, flags.Password, "password") appendStringFlag(o, flags.Password, "password")
appendStringFlag(o, flags.ClusterName, "cluster") appendStringFlag(o, flags.ClusterName, "cluster")
appendStringFlag(o, flags.AuthInfoName, "user") appendStringFlag(o, flags.AuthInfoName, "user")
//appendStringFlag(o, flags.Namespace, "namespace")
appendStringFlag(o, flags.Context, "context") appendStringFlag(o, flags.Context, "context")
appendStringFlag(o, flags.APIServer, "server") appendStringFlag(o, flags.APIServer, "server")
appendBoolFlag(o, flags.Insecure, "insecure-skip-tls-verify") appendBoolFlag(o, flags.Insecure, "insecure-skip-tls-verify")
@ -128,7 +128,7 @@ func appendBoolFlag(out *[]string, in *bool, flag string) {
} }
} }
func appendStringArrayFlag(out *[]string, in *[]string, flag string) { func appendStringArrayFlag(out, in *[]string, flag string) {
if in != nil && len(*in) > 0 { if in != nil && len(*in) > 0 {
*out = append(*out, fmt.Sprintf("--%v=%v'", flag, strings.Join(*in, ","))) *out = append(*out, fmt.Sprintf("--%v=%v'", flag, strings.Join(*in, ",")))
} }

View file

@ -35,7 +35,10 @@ type DeploymentLint struct {
// Check returns true if the lint detects an issue // Check returns true if the lint detects an issue
func (lint DeploymentLint) Check(obj kmeta.Object) bool { func (lint DeploymentLint) Check(obj kmeta.Object) bool {
cmp := obj.(*v1.Deployment) cmp, ok := obj.(*v1.Deployment)
if !ok {
util.PrintError(fmt.Errorf("unexpected type: %T", obj))
}
return lint.f(*cmp) return lint.f(*cmp)
} }
@ -72,11 +75,11 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint {
issue: issueNumber, issue: issueNumber,
version: version, version: version,
f: func(dep v1.Deployment) bool { f: func(dep v1.Deployment) bool {
if !isIngressNginxDeployment(dep) { if !isIngressNginxDeployment(&dep) {
return false return false
} }
args := getNginxArgs(dep) args := getNginxArgs(&dep)
for _, arg := range args { for _, arg := range args {
if strings.HasPrefix(arg, fmt.Sprintf("--%v", flag)) { if strings.HasPrefix(arg, fmt.Sprintf("--%v", flag)) {
return true return true
@ -88,8 +91,9 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint {
} }
} }
func getNginxArgs(dep v1.Deployment) []string { func getNginxArgs(dep *v1.Deployment) []string {
for _, container := range dep.Spec.Template.Spec.Containers { for i := range dep.Spec.Template.Spec.Containers {
container := &dep.Spec.Template.Spec.Containers[i]
if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" { if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" {
return container.Args return container.Args
} }
@ -97,10 +101,10 @@ func getNginxArgs(dep v1.Deployment) []string {
return make([]string, 0) return make([]string, 0)
} }
func isIngressNginxDeployment(dep v1.Deployment) bool { func isIngressNginxDeployment(dep *v1.Deployment) bool {
containers := dep.Spec.Template.Spec.Containers containers := dep.Spec.Template.Spec.Containers
for _, container := range containers { for i := range containers {
if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" { if len(containers[i].Args) > 0 && containers[i].Args[0] == "/nginx-ingress-controller" {
return true return true
} }
} }

View file

@ -30,13 +30,16 @@ type IngressLint struct {
message string message string
issue int issue int
version string version string
f func(ing networking.Ingress) bool f func(ing *networking.Ingress) bool
} }
// Check returns true if the lint detects an issue // Check returns true if the lint detects an issue
func (lint IngressLint) Check(obj kmeta.Object) bool { func (lint IngressLint) Check(obj kmeta.Object) bool {
ing := obj.(*networking.Ingress) ing, ok := obj.(*networking.Ingress)
return lint.f(*ing) if !ok {
util.PrintError(fmt.Errorf("unexpected type: %T", obj))
}
return lint.f(ing)
} }
// Message is a description of the lint // Message is a description of the lint
@ -94,7 +97,7 @@ func GetIngressLints() []IngressLint {
} }
} }
func xForwardedPrefixIsBool(ing networking.Ingress) bool { func xForwardedPrefixIsBool(ing *networking.Ingress) bool {
for name, val := range ing.Annotations { for name, val := range ing.Annotations {
if strings.HasSuffix(name, "/x-forwarded-prefix") && (val == "true" || val == "false") { if strings.HasSuffix(name, "/x-forwarded-prefix") && (val == "true" || val == "false") {
return true return true
@ -103,7 +106,7 @@ func xForwardedPrefixIsBool(ing networking.Ingress) bool {
return false return false
} }
func annotationPrefixIsNginxCom(ing networking.Ingress) bool { func annotationPrefixIsNginxCom(ing *networking.Ingress) bool {
for name := range ing.Annotations { for name := range ing.Annotations {
if strings.HasPrefix(name, "nginx.com/") { if strings.HasPrefix(name, "nginx.com/") {
return true return true
@ -112,7 +115,7 @@ func annotationPrefixIsNginxCom(ing networking.Ingress) bool {
return false return false
} }
func annotationPrefixIsNginxOrg(ing networking.Ingress) bool { func annotationPrefixIsNginxOrg(ing *networking.Ingress) bool {
for name := range ing.Annotations { for name := range ing.Annotations {
if strings.HasPrefix(name, "nginx.org/") { if strings.HasPrefix(name, "nginx.org/") {
return true return true
@ -121,7 +124,7 @@ func annotationPrefixIsNginxOrg(ing networking.Ingress) bool {
return false return false
} }
func rewriteTargetWithoutCaptureGroup(ing networking.Ingress) bool { func rewriteTargetWithoutCaptureGroup(ing *networking.Ingress) bool {
for name, val := range ing.Annotations { for name, val := range ing.Annotations {
if strings.HasSuffix(name, "/rewrite-target") && !strings.Contains(val, "$1") { if strings.HasSuffix(name, "/rewrite-target") && !strings.Contains(val, "$1") {
return true return true
@ -135,7 +138,7 @@ func removedAnnotation(annotationName string, issueNumber int, version string) I
message: fmt.Sprintf("Contains the removed %v annotation.", annotationName), message: fmt.Sprintf("Contains the removed %v annotation.", annotationName),
issue: issueNumber, issue: issueNumber,
version: version, version: version,
f: func(ing networking.Ingress) bool { f: func(ing *networking.Ingress) bool {
for annotation := range ing.Annotations { for annotation := range ing.Annotations {
if strings.HasSuffix(annotation, "/"+annotationName) { if strings.HasSuffix(annotation, "/"+annotationName) {
return true return true
@ -146,7 +149,7 @@ func removedAnnotation(annotationName string, issueNumber int, version string) I
} }
} }
func satisfyDirective(ing networking.Ingress) bool { func satisfyDirective(ing *networking.Ingress) bool {
for name, val := range ing.Annotations { for name, val := range ing.Annotations {
if strings.HasSuffix(name, "/configuration-snippet") { if strings.HasSuffix(name, "/configuration-snippet") {
return strings.Contains(val, "satisfy") return strings.Contains(val, "satisfy")

View file

@ -24,7 +24,7 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
//Just importing this is supposed to allow cloud authentication // Just importing this is supposed to allow cloud authentication
// eg GCP, AWS, Azure ... // eg GCP, AWS, Azure ...
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"

View file

@ -35,7 +35,7 @@ import (
) )
// ChoosePod finds a pod either by deployment or by name // ChoosePod finds a pod either by deployment or by name
func ChoosePod(flags *genericclioptions.ConfigFlags, podName string, deployment string, selector string) (apiv1.Pod, error) { func ChoosePod(flags *genericclioptions.ConfigFlags, podName, deployment, selector string) (apiv1.Pod, error) {
if podName != "" { if podName != "" {
return GetNamedPod(flags, podName) return GetNamedPod(flags, podName)
} }
@ -54,9 +54,9 @@ func GetNamedPod(flags *genericclioptions.ConfigFlags, name string) (apiv1.Pod,
return apiv1.Pod{}, err return apiv1.Pod{}, err
} }
for _, pod := range allPods { for i := range allPods {
if pod.Name == name { if allPods[i].Name == name {
return pod, nil return allPods[i], nil
} }
} }
@ -132,7 +132,7 @@ func GetIngressDefinitions(flags *genericclioptions.ConfigFlags, namespace strin
} }
// GetNumEndpoints counts the number of endpointslices adresses for the service with the given name // GetNumEndpoints counts the number of endpointslices adresses for the service with the given name
func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, serviceName string) (*int, error) { func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace, serviceName string) (*int, error) {
epss, err := GetEndpointSlicesByName(flags, namespace, serviceName) epss, err := GetEndpointSlicesByName(flags, namespace, serviceName)
if err != nil { if err != nil {
return nil, err return nil, err
@ -143,25 +143,26 @@ func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, ser
} }
ret := 0 ret := 0
for _, eps := range epss { for i := range epss {
for _, ep := range eps.Endpoints { eps := &epss[i]
ret += len(ep.Addresses) for j := range eps.Endpoints {
ret += len(eps.Endpoints[j].Addresses)
} }
} }
return &ret, nil return &ret, nil
} }
// GetEndpointSlicesByName returns the endpointSlices for the service with the given name // GetEndpointSlicesByName returns the endpointSlices for the service with the given name
func GetEndpointSlicesByName(flags *genericclioptions.ConfigFlags, namespace string, name string) ([]discoveryv1.EndpointSlice, error) { func GetEndpointSlicesByName(flags *genericclioptions.ConfigFlags, namespace, name string) ([]discoveryv1.EndpointSlice, error) {
allEndpointsSlices, err := getEndpointSlices(flags, namespace) allEndpointsSlices, err := getEndpointSlices(flags, namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var eps []discoveryv1.EndpointSlice var eps []discoveryv1.EndpointSlice
for _, slice := range allEndpointsSlices { for i := range allEndpointsSlices {
if svcName, ok := slice.ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok { if svcName, ok := allEndpointsSlices[i].ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok {
if svcName == name { if svcName == name {
eps = append(eps, slice) eps = append(eps, allEndpointsSlices[i])
} }
} }
} }
@ -182,7 +183,7 @@ func getEndpointSlices(flags *genericclioptions.ConfigFlags, namespace string) (
tryAllNamespacesEndpointSlicesCache(flags) tryAllNamespacesEndpointSlicesCache(flags)
} }
cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(flags, namespace) cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(namespace)
if cachedEndpointSlices != nil { if cachedEndpointSlices != nil {
return *cachedEndpointSlices, nil return *cachedEndpointSlices, nil
@ -217,13 +218,13 @@ func tryAllNamespacesEndpointSlicesCache(flags *genericclioptions.ConfigFlags) {
} }
} }
func tryFilteringEndpointSlicesFromAllNamespacesCache(flags *genericclioptions.ConfigFlags, namespace string) *[]discoveryv1.EndpointSlice { func tryFilteringEndpointSlicesFromAllNamespacesCache(namespace string) *[]discoveryv1.EndpointSlice {
allEndpointSlices := endpointSlicesCache[""] allEndpointSlices := endpointSlicesCache[""]
if allEndpointSlices != nil { if allEndpointSlices != nil {
endpointSlices := make([]discoveryv1.EndpointSlice, 0) endpointSlices := make([]discoveryv1.EndpointSlice, 0)
for _, slice := range *allEndpointSlices { for i := range *allEndpointSlices {
if slice.Namespace == namespace { if (*allEndpointSlices)[i].Namespace == namespace {
endpointSlices = append(endpointSlices, slice) endpointSlices = append(endpointSlices, (*allEndpointSlices)[i])
} }
} }
endpointSlicesCache[namespace] = &endpointSlices endpointSlicesCache[namespace] = &endpointSlices
@ -242,9 +243,9 @@ func GetServiceByName(flags *genericclioptions.ConfigFlags, name string, service
services = &servicesArray services = &servicesArray
} }
for _, svc := range *services { for i := range *services {
if svc.Name == name { if (*services)[i].Name == name {
return svc, nil return (*services)[i], nil
} }
} }
@ -288,7 +289,6 @@ func getLabeledPods(flags *genericclioptions.ConfigFlags, label string) ([]apiv1
pods, err := api.Pods(namespace).List(context.TODO(), metav1.ListOptions{ pods, err := api.Pods(namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: label, LabelSelector: label,
}) })
if err != nil { if err != nil {
return make([]apiv1.Pod, 0), err return make([]apiv1.Pod, 0), err
} }
@ -303,9 +303,9 @@ func getDeploymentPods(flags *genericclioptions.ConfigFlags, deployment string)
} }
ingressPods := make([]apiv1.Pod, 0) ingressPods := make([]apiv1.Pod, 0)
for _, pod := range pods { for i := range pods {
if util.PodInDeployment(pod, deployment) { if util.PodInDeployment(&pods[i], deployment) {
ingressPods = append(ingressPods, pod) ingressPods = append(ingressPods, pods[i])
} }
} }
@ -331,5 +331,4 @@ func getServices(flags *genericclioptions.ConfigFlags) ([]apiv1.Service, error)
} }
return services.Items, nil return services.Items, nil
} }

View file

@ -47,17 +47,25 @@ func PrintError(e error) {
} }
// ParseVersionString returns the major, minor, and patch numbers of a version string // ParseVersionString returns the major, minor, and patch numbers of a version string
func ParseVersionString(v string) (int, int, int, error) { func ParseVersionString(v string) (major, minor, patch int, err error) {
parts := versionRegex.FindStringSubmatch(v) parts := versionRegex.FindStringSubmatch(v)
if len(parts) != 4 { if len(parts) != 4 {
return 0, 0, 0, fmt.Errorf("could not parse %v as a version string (like 0.20.3)", v) return 0, 0, 0, fmt.Errorf("could not parse %v as a version string (like 0.20.3)", v)
} }
major, _ := strconv.Atoi(parts[1]) major, err = strconv.Atoi(parts[1])
minor, _ := strconv.Atoi(parts[2]) if err != nil {
patch, _ := strconv.Atoi(parts[3]) return 0, 0, 0, err
}
minor, err = strconv.Atoi(parts[2])
if err != nil {
return 0, 0, 0, err
}
patch, err = strconv.Atoi(parts[3])
if err != nil {
return 0, 0, 0, err
}
return major, minor, patch, nil return major, minor, patch, nil
} }
@ -90,7 +98,7 @@ func isVersionLessThan(a, b string) bool {
// PodInDeployment returns whether a pod is part of a deployment with the given name // PodInDeployment returns whether a pod is part of a deployment with the given name
// a pod is considered to be in {deployment} if it is owned by a replicaset with a name of format {deployment}-otherchars // a pod is considered to be in {deployment} if it is owned by a replicaset with a name of format {deployment}-otherchars
func PodInDeployment(pod apiv1.Pod, deployment string) bool { func PodInDeployment(pod *apiv1.Pod, deployment string) bool {
for _, owner := range pod.OwnerReferences { for _, owner := range pod.OwnerReferences {
if owner.Controller == nil || !*owner.Controller || owner.Kind != "ReplicaSet" { if owner.Controller == nil || !*owner.Controller || owner.Kind != "ReplicaSet" {
continue continue
@ -138,7 +146,7 @@ func AddContainerFlag(cmd *cobra.Command) *string {
// GetNamespace takes a set of kubectl flag values and returns the namespace we should be operating in // GetNamespace takes a set of kubectl flag values and returns the namespace we should be operating in
func GetNamespace(flags *genericclioptions.ConfigFlags) string { func GetNamespace(flags *genericclioptions.ConfigFlags) string {
namespace, _, err := flags.ToRawKubeConfigLoader().Namespace() namespace, _, err := flags.ToRawKubeConfigLoader().Namespace()
if err != nil || len(namespace) == 0 { if err != nil || namespace == "" {
namespace = apiv1.NamespaceDefault namespace = apiv1.NamespaceDefault
} }
return namespace return namespace

View file

@ -716,7 +716,7 @@ Do not try to edit it manually.
### [[Flag] watch namespace selector](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L30) ### [[Flag] watch namespace selector](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L30)
- [should ingore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L63) - [should ignore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/namespace_selector.go#L63)
### [[Security] no-auth-locations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L33) ### [[Security] no-auth-locations](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/settings/no_auth_locations.go#L33)

View file

@ -16,13 +16,13 @@ func hello(w http.ResponseWriter, r *http.Request) {
} }
key := keys[0] key := keys[0]
fmt.Fprintf(w, "Hello "+string(key)+"!") fmt.Fprintf(w, "Hello "+key+"!")
} }
func main() { func main() {
http.HandleFunc("/hello", hello) http.HandleFunc("/hello", hello)
l, err := net.Listen("tcp", "0.0.0.0:9000") l, err := net.Listen("tcp", "0.0.0.0:9000") //nolint:gosec // Ignore the gosec error since it's a hello server
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -41,7 +41,7 @@ type hwServer struct {
} }
// SayHello implements helloworld.GreeterServer // SayHello implements helloworld.GreeterServer
func (s *hwServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) { func (s *hwServer) SayHello(_ context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) {
return &hwpb.HelloReply{Message: "Hello " + in.Name}, nil return &hwpb.HelloReply{Message: "Hello " + in.Name}, nil
} }
@ -49,7 +49,7 @@ type ecServer struct {
ecpb.UnimplementedEchoServer ecpb.UnimplementedEchoServer
} }
func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) { func (s *ecServer) UnaryEcho(_ context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) {
return &ecpb.EchoResponse{Message: req.Message}, nil return &ecpb.EchoResponse{Message: req.Message}, nil
} }

View file

@ -42,19 +42,16 @@ type IngressAdmission struct {
Checker Checker Checker Checker
} }
var ( var ingressResource = metav1.GroupVersionKind{
ingressResource = metav1.GroupVersionKind{
Group: networking.GroupName, Group: networking.GroupName,
Version: "v1", Version: "v1",
Kind: "Ingress", Kind: "Ingress",
} }
)
// HandleAdmission populates the admission Response // HandleAdmission populates the admission Response
// with Allowed=false if the Object is an ingress that would prevent nginx to reload the configuration // with Allowed=false if the Object is an ingress that would prevent nginx to reload the configuration
// with Allowed=true otherwise // with Allowed=true otherwise
func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object, error) { func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object, error) {
review, isV1 := obj.(*admissionv1.AdmissionReview) review, isV1 := obj.(*admissionv1.AdmissionReview)
if !isV1 { if !isV1 {
return nil, fmt.Errorf("request is not of type AdmissionReview v1 or v1beta1") return nil, fmt.Errorf("request is not of type AdmissionReview v1 or v1beta1")

View file

@ -33,12 +33,12 @@ type failTestChecker struct {
t *testing.T t *testing.T
} }
func (ftc failTestChecker) CheckIngress(ing *networking.Ingress) error { func (ftc failTestChecker) CheckIngress(_ *networking.Ingress) error {
ftc.t.Error("checker should not be called") ftc.t.Error("checker should not be called")
return nil return nil
} }
func (ftc failTestChecker) CheckWarning(ing *networking.Ingress) ([]string, error) { func (ftc failTestChecker) CheckWarning(_ *networking.Ingress) ([]string, error) {
ftc.t.Error("checker should not be called") ftc.t.Error("checker should not be called")
return nil, nil return nil, nil
} }

View file

@ -26,9 +26,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
) )
var ( var scheme = runtime.NewScheme()
scheme = runtime.NewScheme()
)
func init() { func init() {
if err := admissionv1.AddToScheme(scheme); err != nil { if err := admissionv1.AddToScheme(scheme); err != nil {

View file

@ -72,7 +72,7 @@ func (a alias) Parse(ing *networking.Ingress) (interface{}, error) {
aliases := sets.NewString() aliases := sets.NewString()
for _, alias := range strings.Split(val, ",") { for _, alias := range strings.Split(val, ",") {
alias = strings.TrimSpace(alias) alias = strings.TrimSpace(alias)
if len(alias) == 0 { if alias == "" {
continue continue
} }

View file

@ -65,9 +65,9 @@ func TestParse(t *testing.T) {
if testCase.skipValidation { if testCase.skipValidation {
parser.EnableAnnotationValidation = false parser.EnableAnnotationValidation = false
} }
defer func() { t.Cleanup(func() {
parser.EnableAnnotationValidation = true parser.EnableAnnotationValidation = true
}() })
result, err := ap.Parse(ing) result, err := ap.Parse(ing)
if (err != nil) != testCase.wantErr { if (err != nil) != testCase.wantErr {
t.Errorf("ParseAliasAnnotation() annotation: %s, error = %v, wantErr %v", testCase.annotations, err, testCase.wantErr) t.Errorf("ParseAliasAnnotation() annotation: %s, error = %v, wantErr %v", testCase.annotations, err, testCase.wantErr)

View file

@ -86,7 +86,6 @@ type Ingress struct {
CorsConfig cors.Config CorsConfig cors.Config
CustomHTTPErrors []int CustomHTTPErrors []int
DefaultBackend *apiv1.Service DefaultBackend *apiv1.Service
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
FastCGI fastcgi.Config FastCGI fastcgi.Config
Denied *string Denied *string
ExternalAuth authreq.Config ExternalAuth authreq.Config

View file

@ -64,7 +64,11 @@ func (m mockCfg) GetService(name string) (*apiv1.Service, error) {
} }
func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if secret, _ := m.GetSecret(name); secret != nil { secret, err := m.GetSecret(name)
if err != nil {
return nil, err
}
if secret != nil {
return &resolver.AuthSSLCert{ return &resolver.AuthSSLCert{
Secret: name, Secret: name,
CAFileName: "/opt/ca.pem", CAFileName: "/opt/ca.pem",
@ -270,9 +274,9 @@ func TestCors(t *testing.T) {
if r.CorsAllowCredentials != foo.credentials { if r.CorsAllowCredentials != foo.credentials {
t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials) t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials)
} }
} }
} }
func TestCustomHTTPErrors(t *testing.T) { func TestCustomHTTPErrors(t *testing.T) {
ec := NewAnnotationExtractor(mockCfg{}) ec := NewAnnotationExtractor(mockCfg{})
ing := buildIngress() ing := buildIngress()

View file

@ -50,7 +50,7 @@ var (
) )
var AuthSecretConfig = parser.AnnotationConfig{ var AuthSecretConfig = parser.AnnotationConfig{
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the name of the Secret that contains the usernames and passwords which are granted access to the paths defined in the Ingress rules. `, Documentation: `This annotation defines the name of the Secret that contains the usernames and passwords which are granted access to the paths defined in the Ingress rules. `,
@ -61,20 +61,20 @@ var authSecretAnnotations = parser.Annotation{
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
AuthSecretAnnotation: AuthSecretConfig, AuthSecretAnnotation: AuthSecretConfig,
authSecretTypeAnnotation: { authSecretTypeAnnotation: {
Validator: parser.ValidateRegex(*authSecretTypeRegex, true), Validator: parser.ValidateRegex(authSecretTypeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation what is the format of auth-secret value. Can be "auth-file" that defines the content of an htpasswd file, or "auth-map" where each key Documentation: `This annotation what is the format of auth-secret value. Can be "auth-file" that defines the content of an htpasswd file, or "auth-map" where each key
is a user and each value is the password.`, is a user and each value is the password.`,
}, },
authRealmAnnotation: { authRealmAnnotation: {
Validator: parser.ValidateRegex(*parser.CharsWithSpace, false), Validator: parser.ValidateRegex(parser.CharsWithSpace, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the realm (message) that should be shown to user when authentication is requested.`, Documentation: `This annotation defines the realm (message) that should be shown to user when authentication is requested.`,
}, },
authTypeAnnotation: { authTypeAnnotation: {
Validator: parser.ValidateRegex(*authTypeRegex, true), Validator: parser.ValidateRegex(authTypeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines the basic authentication type. Should be "basic" or "digest"`, Documentation: `This annotation defines the basic authentication type. Should be "basic" or "digest"`,
@ -167,14 +167,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation(AuthSecretAnnotation, ing, a.annotationConfig.Annotations) s, err := parser.GetStringAnnotation(AuthSecretAnnotation, ing, a.annotationConfig.Annotations)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading secret name from annotation: %w", err), Reason: fmt.Errorf("error reading secret name from annotation: %w", err),
} }
} }
sns, sname, err := cache.SplitMetaNamespaceKey(s) sns, sname, err := cache.SplitMetaNamespaceKey(s)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading secret name from annotation: %w", err), Reason: fmt.Errorf("error reading secret name from annotation: %w", err),
} }
} }
@ -185,7 +185,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
secCfg := a.r.GetSecurityConfiguration() secCfg := a.r.GetSecurityConfiguration()
// We don't accept different namespaces for secrets. // We don't accept different namespaces for secrets.
if !secCfg.AllowCrossNamespaceResources && sns != ing.Namespace { if !secCfg.AllowCrossNamespaceResources && sns != ing.Namespace {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"), Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"),
} }
} }
@ -193,7 +193,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
name := fmt.Sprintf("%v/%v", sns, sname) name := fmt.Sprintf("%v/%v", sns, sname)
secret, err := a.r.GetSecret(name) secret, err := a.r.GetSecret(name)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error reading secret %s: %w", name, err), Reason: fmt.Errorf("unexpected error reading secret %s: %w", name, err),
} }
} }
@ -217,7 +217,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
return nil, err return nil, err
} }
default: default:
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("invalid auth-secret-type in annotation, must be 'auth-file' or 'auth-map': %w", err), Reason: fmt.Errorf("invalid auth-secret-type in annotation, must be 'auth-file' or 'auth-map': %w", err),
} }
} }
@ -238,14 +238,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
func dumpSecretAuthFile(filename string, secret *api.Secret) error { func dumpSecretAuthFile(filename string, secret *api.Secret) error {
val, ok := secret.Data["auth"] val, ok := secret.Data["auth"]
if !ok { if !ok {
return ing_errors.LocationDenied{ return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the secret %s does not contain a key with value auth", secret.Name), Reason: fmt.Errorf("the secret %s does not contain a key with value auth", secret.Name),
} }
} }
err := os.WriteFile(filename, val, file.ReadWriteByUser) err := os.WriteFile(filename, val, file.ReadWriteByUser)
if err != nil { if err != nil {
return ing_errors.LocationDenied{ return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error creating password file: %w", err), Reason: fmt.Errorf("unexpected error creating password file: %w", err),
} }
} }
@ -264,7 +264,7 @@ func dumpSecretAuthMap(filename string, secret *api.Secret) error {
err := os.WriteFile(filename, []byte(builder.String()), file.ReadWriteByUser) err := os.WriteFile(filename, []byte(builder.String()), file.ReadWriteByUser)
if err != nil { if err != nil {
return ing_errors.LocationDenied{ return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error creating password file: %w", err), Reason: fmt.Errorf("unexpected error creating password file: %w", err),
} }
} }

View file

@ -32,6 +32,15 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
//nolint:gosec // Ignore hardcoded credentials error in testing
const (
authType = "basic"
authRealm = "-realm-"
defaultDemoSecret = "default/demo-secret"
othernsDemoSecret = "otherns/demo-secret"
demoSecret = "demo-secret"
)
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -80,7 +89,7 @@ type mockSecret struct {
} }
func (m mockSecret) GetSecret(name string) (*api.Secret, error) { func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
if name != "default/demo-secret" && name != "otherns/demo-secret" { if name != defaultDemoSecret && name != othernsDemoSecret {
return nil, fmt.Errorf("there is no secret with name %v", name) return nil, fmt.Errorf("there is no secret with name %v", name)
} }
@ -92,7 +101,7 @@ func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
return &api.Secret{ return &api.Secret{
ObjectMeta: meta_v1.ObjectMeta{ ObjectMeta: meta_v1.ObjectMeta{
Namespace: ns, Namespace: ns,
Name: "demo-secret", Name: demoSecret,
}, },
Data: map[string][]byte{"auth": []byte("foo:$apr1$OFG3Xybp$ckL0FHDAkoXYIlH9.cysT0")}, Data: map[string][]byte{"auth": []byte("foo:$apr1$OFG3Xybp$ckL0FHDAkoXYIlH9.cysT0")},
}, nil }, nil
@ -129,9 +138,9 @@ func TestIngressInvalidRealm(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "something weird ; location trying to { break }" data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "something weird ; location trying to { break }"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -148,14 +157,14 @@ func TestIngressInvalidDifferentNamespace(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{ expected := ing_errors.LocationDeniedError{
Reason: errors.New("cross namespace usage of secrets is not allowed"), Reason: errors.New("cross namespace usage of secrets is not allowed"),
} }
_, err := NewParser(dir, &mockSecret{}).Parse(ing) _, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -168,8 +177,8 @@ func TestIngressInvalidDifferentNamespaceAllowed(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -187,14 +196,14 @@ func TestIngressInvalidSecretName(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret;xpto" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret;xpto"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{ expected := ing_errors.LocationDeniedError{
Reason: errors.New("error reading secret name from annotation: annotation nginx.ingress.kubernetes.io/auth-secret contains invalid value"), Reason: errors.New("error reading secret name from annotation: annotation nginx.ingress.kubernetes.io/auth-secret contains invalid value"),
} }
_, err := NewParser(dir, &mockSecret{}).Parse(ing) _, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -207,13 +216,13 @@ func TestInvalidIngressAuthNoSecret(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{ expected := ing_errors.LocationDeniedError{
Reason: errors.New("error reading secret name from annotation: ingress rule without annotations"), Reason: errors.New("error reading secret name from annotation: ingress rule without annotations"),
} }
_, err := NewParser(dir, &mockSecret{}).Parse(ing) _, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -226,9 +235,9 @@ func TestIngressAuth(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-" data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -242,10 +251,10 @@ func TestIngressAuth(t *testing.T) {
if !ok { if !ok {
t.Errorf("expected a BasicDigest type") t.Errorf("expected a BasicDigest type")
} }
if auth.Type != "basic" { if auth.Type != authType {
t.Errorf("Expected basic as auth type but returned %s", auth.Type) t.Errorf("Expected basic as auth type but returned %s", auth.Type)
} }
if auth.Realm != "-realm-" { if auth.Realm != authRealm {
t.Errorf("Expected -realm- as realm but returned %s", auth.Realm) t.Errorf("Expected -realm- as realm but returned %s", auth.Realm)
} }
if !auth.Secured { if !auth.Secured {
@ -257,9 +266,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "invalid-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "invalid-secret"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-" data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -275,10 +284,10 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic" data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret" data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
data[parser.GetAnnotationWithPrefix(authSecretTypeAnnotation)] = "invalid-type" data[parser.GetAnnotationWithPrefix(authSecretTypeAnnotation)] = "invalid-type"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-" data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -290,7 +299,7 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) {
} }
} }
func dummySecretContent(t *testing.T) (string, string, *api.Secret) { func dummySecretContent(t *testing.T) (fileName, dir string, s *api.Secret) {
dir, err := os.MkdirTemp("", fmt.Sprintf("%v", time.Now().Unix())) dir, err := os.MkdirTemp("", fmt.Sprintf("%v", time.Now().Unix()))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -301,7 +310,10 @@ func dummySecretContent(t *testing.T) (string, string, *api.Secret) {
t.Error(err) t.Error(err)
} }
defer tmpfile.Close() defer tmpfile.Close()
s, _ := mockSecret{}.GetSecret("default/demo-secret") s, err = mockSecret{}.GetSecret(defaultDemoSecret)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
return tmpfile.Name(), dir, s return tmpfile.Name(), dir, s
} }

View file

@ -57,25 +57,25 @@ var authReqAnnotations = parser.Annotation{
Group: "authentication", Group: "authentication",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
authReqURLAnnotation: { authReqURLAnnotation: {
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true), Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation allows to indicate the URL where the HTTP request should be sent`, Documentation: `This annotation allows to indicate the URL where the HTTP request should be sent`,
}, },
authReqMethodAnnotation: { authReqMethodAnnotation: {
Validator: parser.ValidateRegex(*methodsRegex, true), Validator: parser.ValidateRegex(methodsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation allows to specify the HTTP method to use`, Documentation: `This annotation allows to specify the HTTP method to use`,
}, },
authReqSigninAnnotation: { authReqSigninAnnotation: {
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true), Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation allows to specify the location of the error page`, Documentation: `This annotation allows to specify the location of the error page`,
}, },
authReqSigninRedirParamAnnotation: { authReqSigninRedirParamAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the URL parameter in the error page which should contain the original URL for a failed signin request`, Documentation: `This annotation allows to specify the URL parameter in the error page which should contain the original URL for a failed signin request`,
@ -87,7 +87,7 @@ var authReqAnnotations = parser.Annotation{
Documentation: `This annotation allows to specify a custom snippet to use with external authentication`, Documentation: `This annotation allows to specify a custom snippet to use with external authentication`,
}, },
authReqCacheKeyAnnotation: { authReqCacheKeyAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true), Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation enables caching for auth requests.`, Documentation: `This annotation enables caching for auth requests.`,
@ -117,26 +117,26 @@ var authReqAnnotations = parser.Annotation{
Documentation: `This annotation specifies a duration in seconds which an idle keepalive connection to an upstream server will stay open`, Documentation: `This annotation specifies a duration in seconds which an idle keepalive connection to an upstream server will stay open`,
}, },
authReqCacheDuration: { authReqCacheDuration: {
Validator: parser.ValidateRegex(*parser.ExtendedCharsRegex, false), Validator: parser.ValidateRegex(parser.ExtendedCharsRegex, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify a caching time for auth responses based on their response codes, e.g. 200 202 30m`, Documentation: `This annotation allows to specify a caching time for auth responses based on their response codes, e.g. 200 202 30m`,
}, },
authReqResponseHeadersAnnotation: { authReqResponseHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.HeadersVariable, true), Validator: parser.ValidateRegex(parser.HeadersVariable, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the headers to pass to backend once authentication request completes. They should be separated by comma.`, Documentation: `This annotation sets the headers to pass to backend once authentication request completes. They should be separated by comma.`,
}, },
authReqProxySetHeadersAnnotation: { authReqProxySetHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the name of a ConfigMap that specifies headers to pass to the authentication service. Documentation: `This annotation sets the name of a ConfigMap that specifies headers to pass to the authentication service.
Only ConfigMaps on the same namespace are allowed`, Only ConfigMaps on the same namespace are allowed`,
}, },
authReqRequestRedirectAnnotation: { authReqRequestRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the X-Auth-Request-Redirect header value`, Documentation: `This annotation allows to specify the X-Auth-Request-Redirect header value`,
@ -249,8 +249,8 @@ func (e1 *Config) Equal(e2 *Config) bool {
var ( var (
methodsRegex = regexp.MustCompile("(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)") methodsRegex = regexp.MustCompile("(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)")
headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`) headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`)
statusCodeRegex = regexp.MustCompile(`^[\d]{3}$`) statusCodeRegex = regexp.MustCompile(`^\d{3}$`)
durationRegex = regexp.MustCompile(`^[\d]+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html durationRegex = regexp.MustCompile(`^\d+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html
) )
// ValidMethod checks is the provided string a valid HTTP method // ValidMethod checks is the provided string a valid HTTP method
@ -273,7 +273,7 @@ func ValidCacheDuration(duration string) bool {
seenDuration := false seenDuration := false
for _, element := range elements { for _, element := range elements {
if len(element) == 0 { if element == "" {
continue continue
} }
if statusCodeRegex.MatchString(element) { if statusCodeRegex.MatchString(element) {
@ -304,6 +304,8 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to use an Config URL as source for authentication // rule used to use an Config URL as source for authentication
//
//nolint:gocyclo // Ignore function complexity error
func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
// Required Parameters // Required Parameters
urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations) urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations)
@ -313,7 +315,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
authURL, err := parser.StringToURL(urlString) authURL, err := parser.StringToURL(urlString)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)} return nil, ing_errors.LocationDeniedError{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)}
} }
authMethod, err := parser.GetStringAnnotation(authReqMethodAnnotation, ing, a.annotationConfig.Annotations) authMethod, err := parser.GetStringAnnotation(authReqMethodAnnotation, ing, a.annotationConfig.Annotations)
@ -410,7 +412,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
if err != nil && ing_errors.IsValidationError(err) { if err != nil && ing_errors.IsValidationError(err) {
return nil, ing_errors.NewLocationDenied("validation error") return nil, ing_errors.NewLocationDenied("validation error")
} }
if len(hstr) != 0 { if hstr != "" {
harr := strings.Split(hstr, ",") harr := strings.Split(hstr, ",")
for _, header := range harr { for _, header := range harr {
header = strings.TrimSpace(header) header = strings.TrimSpace(header)
@ -430,7 +432,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
cns, _, err := cache.SplitMetaNamespaceKey(proxySetHeaderMap) cns, _, err := cache.SplitMetaNamespaceKey(proxySetHeaderMap)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading configmap name %s from annotation: %w", proxySetHeaderMap, err), Reason: fmt.Errorf("error reading configmap name %s from annotation: %w", proxySetHeaderMap, err),
} }
} }
@ -442,7 +444,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
secCfg := a.r.GetSecurityConfiguration() secCfg := a.r.GetSecurityConfiguration()
// We don't accept different namespaces for secrets. // We don't accept different namespaces for secrets.
if !secCfg.AllowCrossNamespaceResources && cns != ing.Namespace { if !secCfg.AllowCrossNamespaceResources && cns != ing.Namespace {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"), Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"),
} }
} }
@ -499,7 +501,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
// It will always return at least one duration (the default duration) // It will always return at least one duration (the default duration)
func ParseStringToCacheDurations(input string) ([]string, error) { func ParseStringToCacheDurations(input string) ([]string, error) {
authCacheDuration := []string{} authCacheDuration := []string{}
if len(input) != 0 { if input != "" {
arr := strings.Split(input, ",") arr := strings.Split(input, ",")
for _, duration := range arr { for _, duration := range arr {
duration = strings.TrimSpace(duration) duration = strings.TrimSpace(duration)

View file

@ -17,7 +17,6 @@ limitations under the License.
package authreq package authreq
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
@ -113,7 +112,7 @@ func TestAnnotations(t *testing.T) {
data[parser.GetAnnotationWithPrefix("auth-url")] = test.url data[parser.GetAnnotationWithPrefix("auth-url")] = test.url
data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL
data[parser.GetAnnotationWithPrefix("auth-signin-redirect-param")] = test.signinURLRedirectParam data[parser.GetAnnotationWithPrefix("auth-signin-redirect-param")] = test.signinURLRedirectParam
data[parser.GetAnnotationWithPrefix("auth-method")] = fmt.Sprintf("%v", test.method) data[parser.GetAnnotationWithPrefix("auth-method")] = test.method
data[parser.GetAnnotationWithPrefix("auth-request-redirect")] = test.requestRedirect data[parser.GetAnnotationWithPrefix("auth-request-redirect")] = test.requestRedirect
data[parser.GetAnnotationWithPrefix("auth-snippet")] = test.authSnippet data[parser.GetAnnotationWithPrefix("auth-snippet")] = test.authSnippet
data[parser.GetAnnotationWithPrefix("auth-cache-key")] = test.authCacheKey data[parser.GetAnnotationWithPrefix("auth-cache-key")] = test.authCacheKey
@ -331,7 +330,6 @@ func TestKeepaliveAnnotations(t *testing.T) {
} }
func TestParseStringToCacheDurations(t *testing.T) { func TestParseStringToCacheDurations(t *testing.T) {
tests := []struct { tests := []struct {
title string title string
duration string duration string
@ -346,7 +344,6 @@ func TestParseStringToCacheDurations(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
dur, err := ParseStringToCacheDurations(test.duration) dur, err := ParseStringToCacheDurations(test.duration)
if test.expErr { if test.expErr {
if err == nil { if err == nil {

View file

@ -55,7 +55,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to enable or disable global external authentication // rule used to enable or disable global external authentication
func (a authReqGlobal) Parse(ing *networking.Ingress) (interface{}, error) { func (a authReqGlobal) Parse(ing *networking.Ingress) (interface{}, error) {
enableGlobalAuth, err := parser.GetBoolAnnotation(enableGlobalAuthAnnotation, ing, a.annotationConfig.Annotations) enableGlobalAuth, err := parser.GetBoolAnnotation(enableGlobalAuthAnnotation, ing, a.annotationConfig.Annotations)
if err != nil { if err != nil {
enableGlobalAuth = true enableGlobalAuth = true

View file

@ -77,7 +77,10 @@ func TestAnnotation(t *testing.T) {
data[parser.GetAnnotationWithPrefix("enable-global-auth")] = "false" data[parser.GetAnnotationWithPrefix("enable-global-auth")] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(&resolver.Mock{}).Parse(ing) i, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
u, ok := i.(bool) u, ok := i.(bool)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -18,11 +18,10 @@ package authtls
import ( import (
"fmt" "fmt"
"regexp"
networking "k8s.io/api/networking/v1" networking "k8s.io/api/networking/v1"
"regexp"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
@ -45,20 +44,20 @@ var (
regexChars = regexp.QuoteMeta(`()|=`) regexChars = regexp.QuoteMeta(`()|=`)
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`) authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)
commonNameRegex = regexp.MustCompile(`^CN=[/\-.\_\~a-zA-Z0-9` + regexChars + `]*$`) commonNameRegex = regexp.MustCompile(`^CN=[/\-.\_\~a-zA-Z0-9` + regexChars + `]*$`)
redirectRegex = regexp.MustCompile(`^((https?://)?[A-Za-z0-9\-\.]*(:[0-9]+)?/[A-Za-z0-9\-\.]*)?$`) redirectRegex = regexp.MustCompile(`^((https?://)?[A-Za-z0-9\-.]*(:\d+)?/[A-Za-z0-9\-.]*)?$`)
) )
var authTLSAnnotations = parser.Annotation{ var authTLSAnnotations = parser.Annotation{
Group: "authentication", Group: "authentication",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
annotationAuthTLSSecret: { annotationAuthTLSSecret: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the secret that contains the certificate chain of allowed certs`, Documentation: `This annotation defines the secret that contains the certificate chain of allowed certs`,
}, },
annotationAuthTLSVerifyClient: { annotationAuthTLSVerifyClient: {
Validator: parser.ValidateRegex(*authVerifyClientRegex, true), Validator: parser.ValidateRegex(authVerifyClientRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation enables verification of client certificates. Can be "on", "off", "optional" or "optional_no_ca"`, Documentation: `This annotation enables verification of client certificates. Can be "on", "off", "optional" or "optional_no_ca"`,
@ -70,7 +69,7 @@ var authTLSAnnotations = parser.Annotation{
Documentation: `This annotation defines validation depth between the provided client certificate and the Certification Authority chain.`, Documentation: `This annotation defines validation depth between the provided client certificate and the Certification Authority chain.`,
}, },
annotationAuthTLSErrorPage: { annotationAuthTLSErrorPage: {
Validator: parser.ValidateRegex(*redirectRegex, true), Validator: parser.ValidateRegex(redirectRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation defines the URL/Page that user should be redirected in case of a Certificate Authentication Error`, Documentation: `This annotation defines the URL/Page that user should be redirected in case of a Certificate Authentication Error`,
@ -82,7 +81,7 @@ var authTLSAnnotations = parser.Annotation{
Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`, Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`,
}, },
annotationAuthTLSMatchCN: { annotationAuthTLSMatchCN: {
Validator: parser.ValidateRegex(*commonNameRegex, true), Validator: parser.ValidateRegex(commonNameRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation adds a sanity check for the CN of the client certificate that is sent over using a string / regex starting with "CN="`, Documentation: `This annotation adds a sanity check for the CN of the client certificate that is sent over using a string / regex starting with "CN="`,
@ -130,9 +129,9 @@ func (assl1 *Config) Equal(assl2 *Config) bool {
} }
// NewParser creates a new TLS authentication annotation parser // NewParser creates a new TLS authentication annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return authTLS{ return authTLS{
r: resolver, r: r,
annotationConfig: authTLSAnnotations, annotationConfig: authTLSAnnotations,
} }
} }
@ -169,7 +168,7 @@ func (a authTLS) Parse(ing *networking.Ingress) (interface{}, error) {
authCert, err := a.r.GetAuthCertificate(tlsauthsecret) authCert, err := a.r.GetAuthCertificate(tlsauthsecret)
if err != nil { if err != nil {
e := fmt.Errorf("error obtaining certificate: %w", err) e := fmt.Errorf("error obtaining certificate: %w", err)
return &Config{}, ing_errors.LocationDenied{Reason: e} return &Config{}, ing_errors.LocationDeniedError{Reason: e}
} }
config.AuthSSLCert = *authCert config.AuthSSLCert = *authCert

View file

@ -27,6 +27,11 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
defaultDemoSecret = "default/demo-secret"
off = "off"
)
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -77,23 +82,22 @@ type mockSecret struct {
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS // GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS
func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if name != "default/demo-secret" { if name != defaultDemoSecret {
return nil, errors.Errorf("there is no secret with name %v", name) return nil, errors.Errorf("there is no secret with name %v", name)
} }
return &resolver.AuthSSLCert{ return &resolver.AuthSSLCert{
Secret: "default/demo-secret", Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt", CAFileName: "/ssl/ca.crt",
CASHA: "abc", CASHA: "abc",
}, nil }, nil
} }
func TestAnnotations(t *testing.T) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "default/demo-secret" data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = defaultDemoSecret
ing.SetAnnotations(data) ing.SetAnnotations(data)
@ -108,7 +112,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected *Config but got %v", u) t.Errorf("expected *Config but got %v", u)
} }
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret") secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
if err != nil { if err != nil {
t.Errorf("unexpected error getting secret %v", err) t.Errorf("unexpected error getting secret %v", err)
} }
@ -132,7 +136,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected empty string, but got %v", u.MatchCN) t.Errorf("expected empty string, but got %v", u.MatchCN)
} }
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = "off" data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = off
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2" data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error" data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true" data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true"
@ -153,8 +157,8 @@ func TestAnnotations(t *testing.T) {
if u.AuthSSLCert.Secret != secret.Secret { if u.AuthSSLCert.Secret != secret.Secret {
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret) t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
} }
if u.VerifyClient != "off" { if u.VerifyClient != off {
t.Errorf("expected %v but got %v", "off", u.VerifyClient) t.Errorf("expected %v but got %v", off, u.VerifyClient)
} }
if u.ValidationDepth != 2 { if u.ValidationDepth != 2 {
t.Errorf("expected %v but got %v", 2, u.ValidationDepth) t.Errorf("expected %v but got %v", 2, u.ValidationDepth)
@ -262,28 +266,21 @@ func TestInvalidAnnotations(t *testing.T) {
if u.MatchCN != "" { if u.MatchCN != "" {
t.Errorf("expected empty string but got %v", u.MatchCN) t.Errorf("expected empty string but got %v", u.MatchCN)
} }
} }
func TestEquals(t *testing.T) { func TestEquals(t *testing.T) {
cfg1 := &Config{} cfg1 := &Config{}
cfg2 := &Config{} cfg2 := &Config{}
// Same config
result := cfg1.Equal(cfg1)
if result != true {
t.Errorf("Expected true")
}
// compare nil // compare nil
result = cfg1.Equal(nil) result := cfg1.Equal(nil)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
} }
// Different Certs // Different Certs
sslCert1 := resolver.AuthSSLCert{ sslCert1 := resolver.AuthSSLCert{
Secret: "default/demo-secret", Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt", CAFileName: "/ssl/ca.crt",
CASHA: "abc", CASHA: "abc",
} }
@ -302,7 +299,7 @@ func TestEquals(t *testing.T) {
// Different Verify Client // Different Verify Client
cfg1.VerifyClient = "on" cfg1.VerifyClient = "on"
cfg2.VerifyClient = "off" cfg2.VerifyClient = off
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")

View file

@ -25,9 +25,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
var ( var validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
)
const ( const (
http = "HTTP" http = "HTTP"

View file

@ -44,6 +44,7 @@ func buildIngress() *networking.Ingress {
}, },
} }
} }
func TestParseInvalidAnnotations(t *testing.T) { func TestParseInvalidAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
@ -56,7 +57,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok { if !ok {
t.Errorf("expected a string type") t.Errorf("expected a string type")
} }
if val != "HTTP" { if val != http {
t.Errorf("expected HTTPS but %v returned", val) t.Errorf("expected HTTPS but %v returned", val)
} }
@ -72,7 +73,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok { if !ok {
t.Errorf("expected a string type") t.Errorf("expected a string type")
} }
if val != "HTTP" { if val != http {
t.Errorf("expected HTTPS but %v returned", val) t.Errorf("expected HTTPS but %v returned", val)
} }
@ -88,7 +89,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok { if !ok {
t.Errorf("expected a string type") t.Errorf("expected a string type")
} }
if val != "HTTP" { if val != http {
t.Errorf("expected HTTPS but %v returned", val) t.Errorf("expected HTTPS but %v returned", val)
} }
} }

View file

@ -57,7 +57,7 @@ var CanaryAnnotations = parser.Annotation{
Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`, Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`,
}, },
canaryByHeaderAnnotation: { canaryByHeaderAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress. Documentation: `This annotation defines the header that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -65,7 +65,7 @@ var CanaryAnnotations = parser.Annotation{
For any other value, the header will be ignored and the request compared against the other canary rules by precedence`, For any other value, the header will be ignored and the request compared against the other canary rules by precedence`,
}, },
canaryByHeaderValueAnnotation: { canaryByHeaderValueAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header value to match for notifying the Ingress to route the request to the service specified in the Canary Ingress. Documentation: `This annotation defines the header value to match for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -74,7 +74,7 @@ var CanaryAnnotations = parser.Annotation{
It doesn't have any effect if the 'canary-by-header' annotation is not defined`, It doesn't have any effect if the 'canary-by-header' annotation is not defined`,
}, },
canaryByHeaderPatternAnnotation: { canaryByHeaderPatternAnnotation: {
Validator: parser.ValidateRegex(*parser.IsValidRegex, false), Validator: parser.ValidateRegex(parser.IsValidRegex, false),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation works the same way as canary-by-header-value except it does PCRE Regex matching. Documentation: `This annotation works the same way as canary-by-header-value except it does PCRE Regex matching.
@ -82,7 +82,7 @@ var CanaryAnnotations = parser.Annotation{
When the given Regex causes error during request processing, the request will be considered as not matching.`, When the given Regex causes error during request processing, the request will be considered as not matching.`,
}, },
canaryByCookieAnnotation: { canaryByCookieAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the cookie that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress. Documentation: `This annotation defines the cookie that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -189,7 +189,7 @@ func (c canary) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations return c.annotationConfig.Annotations
} }
func (a canary) Validate(anns map[string]string) error { func (c canary) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations)
} }

View file

@ -17,6 +17,7 @@ limitations under the License.
package canary package canary
import ( import (
"strconv"
"testing" "testing"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
@ -24,8 +25,6 @@ import (
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"strconv"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -93,7 +92,6 @@ func TestCanaryInvalid(t *testing.T) {
if val.Weight != 0 { if val.Weight != 0 {
t.Errorf("Expected %v but got %v", 0, val.Weight) t.Errorf("Expected %v but got %v", 0, val.Weight)
} }
} }
func TestAnnotations(t *testing.T) { func TestAnnotations(t *testing.T) {
@ -133,11 +131,10 @@ func TestAnnotations(t *testing.T) {
} }
continue continue
} else { }
if err != nil { if err != nil {
t.Errorf("%v: expected nil but returned error %v", test.title, err) t.Errorf("%v: expected nil but returned error %v", test.title, err)
} }
}
canaryConfig, ok := i.(*Config) canaryConfig, ok := i.(*Config)
if !ok { if !ok {

View file

@ -31,7 +31,7 @@ var clientBodyBufferSizeConfig = parser.Annotation{
Group: "backend", Group: "backend",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
clientBodyBufferSizeAnnotation: { clientBodyBufferSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true), Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options
Documentation: `Sets buffer size for reading client request body per location. Documentation: `Sets buffer size for reading client request body per location.
@ -65,7 +65,7 @@ func (cbbs clientBodyBufferSize) Parse(ing *networking.Ingress) (interface{}, er
return parser.GetStringAnnotation(clientBodyBufferSizeAnnotation, ing, cbbs.annotationConfig.Annotations) return parser.GetStringAnnotation(clientBodyBufferSizeAnnotation, ing, cbbs.annotationConfig.Annotations)
} }
func (a clientBodyBufferSize) Validate(anns map[string]string) error { func (cbbs clientBodyBufferSize) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(cbbs.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, clientBodyBufferSizeConfig.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, clientBodyBufferSizeConfig.Annotations)
} }

View file

@ -57,6 +57,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -29,15 +29,13 @@ const (
connectionProxyHeaderAnnotation = "connection-proxy-header" connectionProxyHeaderAnnotation = "connection-proxy-header"
) )
var ( var validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
)
var connectionHeadersAnnotations = parser.Annotation{ var connectionHeadersAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
connectionProxyHeaderAnnotation: { connectionProxyHeaderAnnotation: {
Validator: parser.ValidateRegex(*validConnectionHeaderValue, true), Validator: parser.ValidateRegex(validConnectionHeaderValue, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation allows setting a specific value for "proxy_set_header Connection" directive. Right now it is restricted to "close" or "keep-alive"`, Documentation: `This annotation allows setting a specific value for "proxy_set_header Connection" directive. Right now it is restricted to "close" or "keep-alive"`,

View file

@ -66,6 +66,5 @@ func TestParse(t *testing.T) {
if !p.Equal(testCase.expected) { if !p.Equal(testCase.expected) {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, p, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, p, testCase.annotations)
} }
} }
} }

View file

@ -43,9 +43,9 @@ var (
// * Sets a group that can be (https?://)?*?.something.com:port? // * Sets a group that can be (https?://)?*?.something.com:port?
// * Allows this to be repeated as much as possible, and separated by comma // * Allows this to be repeated as much as possible, and separated by comma
// Otherwise it should be '*' // Otherwise it should be '*'
corsOriginRegexValidator = regexp.MustCompile(`^((((https?://)?(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?,?)+)|\*)?$`) corsOriginRegexValidator = regexp.MustCompile(`^((((https?://)?(\*\.)?[A-Za-z0-9\-.]*(:\d+)?,?)+)|\*)?$`)
// corsOriginRegex defines the regex for validation inside Parse // corsOriginRegex defines the regex for validation inside Parse
corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?|\*)?$`) corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-.]*(:\d+)?|\*)?$`)
// Method must contain valid methods list (PUT, GET, POST, BLA) // Method must contain valid methods list (PUT, GET, POST, BLA)
// May contain or not spaces between each verb // May contain or not spaces between each verb
corsMethodsRegex = regexp.MustCompile(`^([A-Za-z]+,?\s?)+$`) corsMethodsRegex = regexp.MustCompile(`^([A-Za-z]+,?\s?)+$`)
@ -74,7 +74,7 @@ var corsAnnotation = parser.Annotation{
Documentation: `This annotation enables Cross-Origin Resource Sharing (CORS) in an Ingress rule`, Documentation: `This annotation enables Cross-Origin Resource Sharing (CORS) in an Ingress rule`,
}, },
corsAllowOriginAnnotation: { corsAllowOriginAnnotation: {
Validator: parser.ValidateRegex(*corsOriginRegexValidator, true), Validator: parser.ValidateRegex(corsOriginRegexValidator, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls what's the accepted Origin for CORS. Documentation: `This annotation controls what's the accepted Origin for CORS.
@ -82,14 +82,14 @@ var corsAnnotation = parser.Annotation{
It also supports single level wildcard subdomains and follows this format: http(s)://*.foo.bar, http(s)://*.bar.foo:8080 or http(s)://*.abc.bar.foo:9000`, It also supports single level wildcard subdomains and follows this format: http(s)://*.foo.bar, http(s)://*.bar.foo:8080 or http(s)://*.abc.bar.foo:9000`,
}, },
corsAllowHeadersAnnotation: { corsAllowHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.HeadersVariable, true), Validator: parser.ValidateRegex(parser.HeadersVariable, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which headers are accepted. Documentation: `This annotation controls which headers are accepted.
This is a multi-valued field, separated by ',' and accepts letters, numbers, _ and -`, This is a multi-valued field, separated by ',' and accepts letters, numbers, _ and -`,
}, },
corsAllowMethodsAnnotation: { corsAllowMethodsAnnotation: {
Validator: parser.ValidateRegex(*corsMethodsRegex, true), Validator: parser.ValidateRegex(corsMethodsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which methods are accepted. Documentation: `This annotation controls which methods are accepted.
@ -102,7 +102,7 @@ var corsAnnotation = parser.Annotation{
Documentation: `This annotation controls if credentials can be passed during CORS operations.`, Documentation: `This annotation controls if credentials can be passed during CORS operations.`,
}, },
corsExposeHeadersAnnotation: { corsExposeHeadersAnnotation: {
Validator: parser.ValidateRegex(*corsExposeHeadersRegex, true), Validator: parser.ValidateRegex(corsExposeHeadersRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which headers are exposed to response. Documentation: `This annotation controls which headers are exposed to response.
@ -260,7 +260,7 @@ func (c cors) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations return c.annotationConfig.Annotations
} }
func (a cors) Validate(anns map[string]string) error { func (c cors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, corsAnnotation.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, corsAnnotation.Annotations)
} }

View file

@ -31,16 +31,14 @@ const (
customHTTPErrorsAnnotation = "custom-http-errors" customHTTPErrorsAnnotation = "custom-http-errors"
) )
var ( // We accept anything between 400 and 599, on a comma separated.
// We accept anything between 400 and 599, on a comma separated. var arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5]\d{2},?)*$`)
arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5][0-9][0-9],?)*$`)
)
var customHTTPErrorsAnnotations = parser.Annotation{ var customHTTPErrorsAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
customHTTPErrorsAnnotation: { customHTTPErrorsAnnotation: {
Validator: parser.ValidateRegex(*arrayOfHTTPErrors, true), Validator: parser.ValidateRegex(arrayOfHTTPErrors, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `If a default backend annotation is specified on the ingress, the errors code specified on this annotation Documentation: `If a default backend annotation is specified on the ingress, the errors code specified on this annotation
@ -72,7 +70,7 @@ func (e customhttperrors) Parse(ing *networking.Ingress) (interface{}, error) {
} }
cSplit := strings.Split(c, ",") cSplit := strings.Split(c, ",")
var codes []int codes := make([]int, 0, len(cSplit))
for _, i := range cSplit { for _, i := range cSplit {
num, err := strconv.Atoi(i) num, err := strconv.Atoi(i)
if err != nil { if err != nil {
@ -88,7 +86,7 @@ func (e customhttperrors) GetDocumentation() parser.AnnotationFields {
return e.annotationConfig.Annotations return e.annotationConfig.Annotations
} }
func (a customhttperrors) Validate(anns map[string]string) error { func (e customhttperrors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(e.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, customHTTPErrorsAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, customHTTPErrorsAnnotations.Annotations)
} }

View file

@ -57,14 +57,14 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress to use // Parse parses the annotations contained in the ingress to use
// a custom default backend // a custom default backend
func (db backend) Parse(ing *networking.Ingress) (interface{}, error) { func (b backend) Parse(ing *networking.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, db.annotationConfig.Annotations) s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, b.annotationConfig.Annotations)
if err != nil { if err != nil {
return nil, err return nil, err
} }
name := fmt.Sprintf("%v/%v", ing.Namespace, s) name := fmt.Sprintf("%v/%v", ing.Namespace, s)
svc, err := db.r.GetService(name) svc, err := b.r.GetService(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("unexpected error reading service %s: %w", name, err) return nil, fmt.Errorf("unexpected error reading service %s: %w", name, err)
} }
@ -72,11 +72,11 @@ func (db backend) Parse(ing *networking.Ingress) (interface{}, error) {
return svc, nil return svc, nil
} }
func (db backend) GetDocumentation() parser.AnnotationFields { func (b backend) GetDocumentation() parser.AnnotationFields {
return db.annotationConfig.Annotations return b.annotationConfig.Annotations
} }
func (a backend) Validate(anns map[string]string) error { func (b backend) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(b.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, defaultBackendAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, defaultBackendAnnotations.Annotations)
} }

View file

@ -35,22 +35,20 @@ const (
fastCGIParamsAnnotation = "fastcgi-params-configmap" fastCGIParamsAnnotation = "fastcgi-params-configmap"
) )
var ( // fast-cgi valid parameters is just a single file name (like index.php)
// fast-cgi valid parameters is just a single file name (like index.php) var regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9.\-\_]+$`)
regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9\.\-\_]+$`)
)
var fastCGIAnnotations = parser.Annotation{ var fastCGIAnnotations = parser.Annotation{
Group: "fastcgi", Group: "fastcgi",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
fastCGIIndexAnnotation: { fastCGIIndexAnnotation: {
Validator: parser.ValidateRegex(*regexValidIndexAnnotationAndKey, true), Validator: parser.ValidateRegex(regexValidIndexAnnotationAndKey, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation can be used to specify an index file`, Documentation: `This annotation can be used to specify an index file`,
}, },
fastCGIParamsAnnotation: { fastCGIParamsAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation can be used to specify a ConfigMap containing the fastcgi parameters as a key/value. Documentation: `This annotation can be used to specify a ConfigMap containing the fastcgi parameters as a key/value.
@ -98,7 +96,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to indicate the fastcgiConfig. // rule used to indicate the fastcgiConfig.
func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) { func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
fcgiConfig := Config{} fcgiConfig := Config{}
if ing.GetAnnotations() == nil { if ing.GetAnnotations() == nil {
@ -125,7 +122,7 @@ func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
cmns, cmn, err := cache.SplitMetaNamespaceKey(cm) cmns, cmn, err := cache.SplitMetaNamespaceKey(cm)
if err != nil { if err != nil {
return fcgiConfig, ing_errors.LocationDenied{ return fcgiConfig, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading configmap name from annotation: %w", err), Reason: fmt.Errorf("error reading configmap name from annotation: %w", err),
} }
} }
@ -139,7 +136,7 @@ func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
cm = fmt.Sprintf("%v/%v", ing.Namespace, cmn) cm = fmt.Sprintf("%v/%v", ing.Namespace, cmn)
cmap, err := a.r.GetConfigMap(cm) cmap, err := a.r.GetConfigMap(cm)
if err != nil { if err != nil {
return fcgiConfig, ing_errors.LocationDenied{ return fcgiConfig, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err), Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err),
} }
} }

View file

@ -155,7 +155,6 @@ func TestParseFastCGIInvalidParamsConfigMapAnnotation(t *testing.T) {
invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"} invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"}
for _, configmap := range invalidConfigMapList { for _, configmap := range invalidConfigMapList {
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap
ing.SetAnnotations(data) ing.SetAnnotations(data)
@ -239,11 +238,9 @@ func TestParseFastCGIParamsConfigMapAnnotationWithDifferentNS(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("Different namespace configmap should return an error") t.Errorf("Different namespace configmap should return an error")
} }
} }
func TestConfigEquality(t *testing.T) { func TestConfigEquality(t *testing.T) {
var nilConfig *Config var nilConfig *Config
config := Config{ config := Config{
@ -297,7 +294,6 @@ func TestConfigEquality(t *testing.T) {
} }
func Test_fastcgi_Parse(t *testing.T) { func Test_fastcgi_Parse(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
index string index string
@ -378,7 +374,6 @@ func Test_fastcgi_Parse(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}

View file

@ -25,7 +25,6 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/net" "k8s.io/ingress-nginx/internal/net"
@ -57,7 +56,7 @@ var globalRateLimitAnnotationConfig = parser.Annotation{
Documentation: `Configures a time window (i.e 1m) that the limit is applied`, Documentation: `Configures a time window (i.e 1m) that the limit is applied`,
}, },
globalRateLimitKeyAnnotation: { globalRateLimitKeyAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true), Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation Configures a key for counting the samples. Defaults to $remote_addr. Documentation: `This annotation Configures a key for counting the samples. Defaults to $remote_addr.
@ -123,23 +122,23 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
config := &Config{} config := &Config{}
limit, err := parser.GetIntAnnotation(globalRateLimitAnnotation, ing, a.annotationConfig.Annotations) limit, err := parser.GetIntAnnotation(globalRateLimitAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsInvalidContent(err) { if err != nil && ing_errors.IsInvalidContent(err) {
return nil, err return nil, err
} }
rawWindowSize, err := parser.GetStringAnnotation(globalRateLimitWindowAnnotation, ing, a.annotationConfig.Annotations) rawWindowSize, err := parser.GetStringAnnotation(globalRateLimitWindowAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsValidationError(err) { if err != nil && ing_errors.IsValidationError(err) {
return config, ing_errors.LocationDenied{ return config, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err), Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err),
} }
} }
if limit == 0 || len(rawWindowSize) == 0 { if limit == 0 || rawWindowSize == "" {
return config, nil return config, nil
} }
windowSize, err := time.ParseDuration(rawWindowSize) windowSize, err := time.ParseDuration(rawWindowSize)
if err != nil { if err != nil {
return config, ing_errors.LocationDenied{ return config, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err), Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err),
} }
} }
@ -148,12 +147,12 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
if err != nil { if err != nil {
klog.Warningf("invalid %s, defaulting to %s", globalRateLimitKeyAnnotation, defaultKey) klog.Warningf("invalid %s, defaulting to %s", globalRateLimitKeyAnnotation, defaultKey)
} }
if len(key) == 0 { if key == "" {
key = defaultKey key = defaultKey
} }
rawIgnoredCIDRs, err := parser.GetStringAnnotation(globalRateLimitIgnoredCidrsAnnotation, ing, a.annotationConfig.Annotations) rawIgnoredCIDRs, err := parser.GetStringAnnotation(globalRateLimitIgnoredCidrsAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsInvalidContent(err) { if err != nil && ing_errors.IsInvalidContent(err) {
return nil, err return nil, err
} }
ignoredCIDRs, err := net.ParseCIDRs(rawIgnoredCIDRs) ignoredCIDRs, err := net.ParseCIDRs(rawIgnoredCIDRs)
@ -161,7 +160,7 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
return nil, err return nil, err
} }
config.Namespace = strings.Replace(string(ing.UID), "-", "", -1) config.Namespace = strings.ReplaceAll(string(ing.UID), "-", "")
config.Limit = limit config.Limit = limit
config.WindowSize = int(windowSize.Seconds()) config.WindowSize = int(windowSize.Seconds())
config.Key = key config.Key = key

View file

@ -30,8 +30,10 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e" const (
const expectedUID = "31285d47b1504dcfbd6f12c46d769f6e" UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e"
expectedUID = "31285d47b1504dcfbd6f12c46d769f6e"
)
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
@ -190,10 +192,19 @@ func TestGlobalRateLimiting(t *testing.T) {
t.Errorf("expected error '%v' but got '%v'", testCase.expectedErr, actualErr) t.Errorf("expected error '%v' but got '%v'", testCase.expectedErr, actualErr)
} }
actualConfig := i.(*Config) actualConfig, ok := i.(*Config)
if !ok {
t.Errorf("expected Config type but got %T", i)
}
if !testCase.expectedConfig.Equal(actualConfig) { if !testCase.expectedConfig.Equal(actualConfig) {
expectedJSON, _ := json.Marshal(testCase.expectedConfig) expectedJSON, err := json.Marshal(testCase.expectedConfig)
actualJSON, _ := json.Marshal(actualConfig) if err != nil {
t.Errorf("failed to marshal expected config: %v", err)
}
actualJSON, err := json.Marshal(actualConfig)
if err != nil {
t.Errorf("failed to marshal actual config: %v", err)
}
t.Errorf("%v: expected config '%s' but got '%s'", testCase.title, expectedJSON, actualJSON) t.Errorf("%v: expected config '%s' but got '%s'", testCase.title, expectedJSON, actualJSON)
} }
} }

View file

@ -62,7 +62,7 @@ func (h2pp http2PushPreload) GetDocumentation() parser.AnnotationFields {
return h2pp.annotationConfig.Annotations return h2pp.annotationConfig.Annotations
} }
func (a http2PushPreload) Validate(anns map[string]string) error { func (h2pp http2PushPreload) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(h2pp.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, http2PushPreloadAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, http2PushPreloadAnnotations.Annotations)
} }

View file

@ -96,16 +96,15 @@ func (a ipallowlist) Parse(ing *networking.Ingress) (interface{}, error) {
return &SourceRange{CIDR: defaultAllowlistSourceRange}, nil return &SourceRange{CIDR: defaultAllowlistSourceRange}, nil
} }
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDenied{ return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{
Reason: err, Reason: err,
} }
} }
values := strings.Split(val, ",") values := strings.Split(val, ",")
ipnets, ips, err := net.ParseIPNets(values...) ipnets, ips, err := net.ParseIPNets(values...)
if err != nil && len(ips) == 0 { if err != nil && len(ips) == 0 {
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDenied{ return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err), Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err),
} }
} }

View file

@ -93,16 +93,15 @@ func (a ipdenylist) Parse(ing *networking.Ingress) (interface{}, error) {
return &SourceRange{CIDR: defaultDenylistSourceRange}, nil return &SourceRange{CIDR: defaultDenylistSourceRange}, nil
} }
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{ return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{
Reason: err, Reason: err,
} }
} }
values := strings.Split(val, ",") values := strings.Split(val, ",")
ipnets, ips, err := net.ParseIPNets(values...) ipnets, ips, err := net.ParseIPNets(values...)
if err != nil && len(ips) == 0 { if err != nil && len(ips) == 0 {
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{ return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err), Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err),
} }
} }

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -101,7 +101,7 @@ func (l log) GetDocumentation() parser.AnnotationFields {
return l.annotationConfig.Annotations return l.annotationConfig.Annotations
} }
func (a log) Validate(anns map[string]string) error { func (l log) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(l.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, logAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, logAnnotations.Annotations)
} }

View file

@ -76,7 +76,10 @@ func TestIngressAccessLogConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableAccessLogAnnotation)] = "false" data[parser.GetAnnotationWithPrefix(enableAccessLogAnnotation)] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing) log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
nginxLogs, ok := log.(*Config) nginxLogs, ok := log.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -94,7 +97,10 @@ func TestIngressRewriteLogConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing) log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing annotations %v", err)
}
nginxLogs, ok := log.(*Config) nginxLogs, ok := log.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -112,7 +118,10 @@ func TestInvalidBoolConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "blo" data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "blo"
ing.SetAnnotations(data) ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing) log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
nginxLogs, ok := log.(*Config) nginxLogs, ok := log.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -34,15 +34,13 @@ const (
mirrorHostAnnotation = "mirror-host" mirrorHostAnnotation = "mirror-host"
) )
var ( var OnOffRegex = regexp.MustCompile(`^(on|off)$`)
OnOffRegex = regexp.MustCompile(`^(on|off)$`)
)
var mirrorAnnotation = parser.Annotation{ var mirrorAnnotation = parser.Annotation{
Group: "mirror", Group: "mirror",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
mirrorRequestBodyAnnotation: { mirrorRequestBodyAnnotation: {
Validator: parser.ValidateRegex(*OnOffRegex, true), Validator: parser.ValidateRegex(OnOffRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines if the request-body should be sent to the mirror backend. Can be 'on' or 'off'`, Documentation: `This annotation defines if the request-body should be sent to the mirror backend. Can be 'on' or 'off'`,

View file

@ -27,7 +27,7 @@ import (
const ( const (
modsecEnableAnnotation = "enable-modsecurity" modsecEnableAnnotation = "enable-modsecurity"
modsecEnableOwaspCoreAnnotation = "enable-owasp-core-rules" modsecEnableOwaspCoreAnnotation = "enable-owasp-core-rules"
modesecTransactionIdAnnotation = "modsecurity-transaction-id" modesecTransactionIDAnnotation = "modsecurity-transaction-id"
modsecSnippetAnnotation = "modsecurity-snippet" modsecSnippetAnnotation = "modsecurity-snippet"
) )
@ -46,8 +46,8 @@ var modsecurityAnnotation = parser.Annotation{
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables the OWASP Core Rule Set`, Documentation: `This annotation enables the OWASP Core Rule Set`,
}, },
modesecTransactionIdAnnotation: { modesecTransactionIDAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true), Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh, Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation enables passing an NGINX variable to ModSecurity.`, Documentation: `This annotation enables passing an NGINX variable to ModSecurity.`,
@ -98,9 +98,9 @@ func (modsec1 *Config) Equal(modsec2 *Config) bool {
} }
// NewParser creates a new ModSecurity annotation parser // NewParser creates a new ModSecurity annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return modSecurity{ return modSecurity{
r: resolver, r: r,
annotationConfig: modsecurityAnnotation, annotationConfig: modsecurityAnnotation,
} }
} }
@ -134,10 +134,10 @@ func (a modSecurity) Parse(ing *networking.Ingress) (interface{}, error) {
config.OWASPRules = false config.OWASPRules = false
} }
config.TransactionID, err = parser.GetStringAnnotation(modesecTransactionIdAnnotation, ing, a.annotationConfig.Annotations) config.TransactionID, err = parser.GetStringAnnotation(modesecTransactionIDAnnotation, ing, a.annotationConfig.Annotations)
if err != nil { if err != nil {
if errors.IsInvalidContent(err) { if errors.IsInvalidContent(err) {
klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIdAnnotation) klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIDAnnotation)
} }
config.TransactionID = "" config.TransactionID = ""
} }

View file

@ -69,8 +69,14 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
result, _ := ap.Parse(ing) result, err := ap.Parse(ing)
config := result.(*Config) if err != nil {
t.Errorf("unexpected error: %v", err)
}
config, ok := result.(*Config)
if !ok {
t.Errorf("unexpected type: %T", result)
}
if !config.Equal(&testCase.expected) { if !config.Equal(&testCase.expected) {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
} }

View file

@ -51,7 +51,7 @@ var otelAnnotations = parser.Annotation{
Documentation: `This annotation enables or disables using spans from incoming requests as parent for created ones`, Documentation: `This annotation enables or disables using spans from incoming requests as parent for created ones`,
}, },
otelOperationNameAnnotation: { otelOperationNameAnnotation: {
Validator: parser.ValidateRegex(*regexOperationName, true), Validator: parser.ValidateRegex(regexOperationName, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines what operation name should be added to the span`, Documentation: `This annotation defines what operation name should be added to the span`,
@ -75,7 +75,6 @@ type Config struct {
// Equal tests for equality between two Config types // Equal tests for equality between two Config types
func (bd1 *Config) Equal(bd2 *Config) bool { func (bd1 *Config) Equal(bd2 *Config) bool {
if bd1.Set != bd2.Set { if bd1.Set != bd2.Set {
return false return false
} }
@ -150,7 +149,7 @@ func (c opentelemetry) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations return c.annotationConfig.Annotations
} }
func (a opentelemetry) Validate(anns map[string]string) error { func (c opentelemetry) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, otelAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, otelAnnotations.Annotations)
} }

View file

@ -26,6 +26,8 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const enableAnnotation = "true"
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -73,10 +75,13 @@ func TestIngressAnnotationOpentelemetrySetTrue(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
openTelemetry, ok := val.(*Config) openTelemetry, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -103,7 +108,10 @@ func TestIngressAnnotationOpentelemetrySetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "false" data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
openTelemetry, ok := val.(*Config) openTelemetry, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -123,8 +131,8 @@ func TestIngressAnnotationOpentelemetryTrustSetTrue(t *testing.T) {
data := map[string]string{} data := map[string]string{}
opName := "foo-op" opName := "foo-op"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
ing.SetAnnotations(data) ing.SetAnnotations(data)
@ -163,7 +171,7 @@ func TestIngressAnnotationOpentelemetryWithBadOpName(t *testing.T) {
data := map[string]string{} data := map[string]string{}
opName := "fooxpto_123$la;" opName := "fooxpto_123$la;"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
ing.SetAnnotations(data) ing.SetAnnotations(data)
@ -180,7 +188,10 @@ func TestIngressAnnotationOpentelemetryUnset(t *testing.T) {
data := map[string]string{} data := map[string]string{}
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, ok := val.(*Config) _, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -89,13 +89,13 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
} }
} }
func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) { func (o opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, s.annotationConfig.Annotations) enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, o.annotationConfig.Annotations)
if err != nil { if err != nil {
return &Config{}, nil return &Config{}, nil
} }
trustSpan, err := parser.GetBoolAnnotation(opentracingTrustSpanAnnotation, ing, s.annotationConfig.Annotations) trustSpan, err := parser.GetBoolAnnotation(opentracingTrustSpanAnnotation, ing, o.annotationConfig.Annotations)
if err != nil { if err != nil {
return &Config{Set: true, Enabled: enabled}, nil return &Config{Set: true, Enabled: enabled}, nil
} }
@ -103,11 +103,11 @@ func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
return &Config{Set: true, Enabled: enabled, TrustSet: true, TrustEnabled: trustSpan}, nil return &Config{Set: true, Enabled: enabled, TrustSet: true, TrustEnabled: trustSpan}, nil
} }
func (s opentracing) GetDocumentation() parser.AnnotationFields { func (o opentracing) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations return o.annotationConfig.Annotations
} }
func (a opentracing) Validate(anns map[string]string) error { func (o opentracing) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(o.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, opentracingAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, opentracingAnnotations.Annotations)
} }

View file

@ -26,6 +26,8 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const enableAnnotation = "true"
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -73,10 +75,13 @@ func TestIngressAnnotationOpentracingSetTrue(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config) openTracing, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -95,7 +100,10 @@ func TestIngressAnnotationOpentracingSetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "false" data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config) openTracing, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -110,11 +118,14 @@ func TestIngressAnnotationOpentracingTrustSetTrue(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = enableAnnotation
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config) openTracing, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")
@ -136,7 +147,11 @@ func TestIngressAnnotationOpentracingUnset(t *testing.T) {
data := map[string]string{} data := map[string]string{}
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, ok := val.(*Config) _, ok := val.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -118,7 +118,7 @@ func (a ingAnnotations) parseString(name string) (string, error) {
val, ok := a[name] val, ok := a[name]
if ok { if ok {
s := normalizeString(val) s := normalizeString(val)
if len(s) == 0 { if s == "" {
return "", errors.NewInvalidAnnotationContent(name, val) return "", errors.NewInvalidAnnotationContent(name, val)
} }
@ -248,13 +248,14 @@ func StringToURL(input string) (*url.URL, error) {
return nil, fmt.Errorf("%v is not a valid URL: %v", input, err) return nil, fmt.Errorf("%v is not a valid URL: %v", input, err)
} }
if parsedURL.Scheme == "" { switch {
case parsedURL.Scheme == "":
return nil, fmt.Errorf("url scheme is empty") return nil, fmt.Errorf("url scheme is empty")
} else if parsedURL.Host == "" { case parsedURL.Host == "":
return nil, fmt.Errorf("url host is empty") return nil, fmt.Errorf("url host is empty")
} else if strings.Contains(parsedURL.Host, "..") { case strings.Contains(parsedURL.Host, ".."):
return nil, fmt.Errorf("invalid url host") return nil, fmt.Errorf("invalid url host")
} default:
return parsedURL, nil return parsedURL, nil
}
} }

View file

@ -93,14 +93,16 @@ func TestGetStringAnnotation(t *testing.T) {
{"valid - A", "string", "A ", "A", false}, {"valid - A", "string", "A ", "A", false},
{"valid - B", "string", " B", "B", false}, {"valid - B", "string", " B", "B", false},
{"empty", "string", " ", "", true}, {"empty", "string", " ", "", true},
{"valid multiline", "string", ` {
"valid multiline", "string", `
rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break;
rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break;
`, ` `, `
rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break;
rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break; rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break;
`, `,
false}, false,
},
} }
data := map[string]string{} data := map[string]string{}
@ -213,8 +215,10 @@ func TestGetIntAnnotation(t *testing.T) {
func TestStringToURL(t *testing.T) { func TestStringToURL(t *testing.T) {
validURL := "http://bar.foo.com/external-auth" validURL := "http://bar.foo.com/external-auth"
validParsedURL, _ := url.Parse(validURL) validParsedURL, err := url.Parse(validURL)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
tests := []struct { tests := []struct {
title string title string
url string url string

View file

@ -52,7 +52,7 @@ var (
var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$") var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$")
// SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M // SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M
var SizeRegex = regexp.MustCompile("^(?i)[0-9]+[bkmg]?$") var SizeRegex = regexp.MustCompile(`^(?i)\d+[bkmg]?$`)
// URLRegex is used to validate a URL but with only a specific set of characters: // URLRegex is used to validate a URL but with only a specific set of characters:
// It is alphanumericChar + ":", "?", "&" // It is alphanumericChar + ":", "?", "&"
@ -103,7 +103,7 @@ func ValidateServerName(value string) error {
// ValidateRegex receives a regex as an argument and uses it to validate // ValidateRegex receives a regex as an argument and uses it to validate
// the value of the field. // the value of the field.
// Annotation can define if the spaces should be trimmed before validating the value // Annotation can define if the spaces should be trimmed before validating the value
func ValidateRegex(regex regexp.Regexp, removeSpace bool) AnnotationValidator { func ValidateRegex(regex *regexp.Regexp, removeSpace bool) AnnotationValidator {
return func(s string) error { return func(s string) error {
if removeSpace { if removeSpace {
s = strings.ReplaceAll(s, " ", "") s = strings.ReplaceAll(s, " ", "")
@ -117,7 +117,7 @@ func ValidateRegex(regex regexp.Regexp, removeSpace bool) AnnotationValidator {
// ValidateOptions receives an array of valid options that can be the value of annotation. // ValidateOptions receives an array of valid options that can be the value of annotation.
// If no valid option is found, it will return an error // If no valid option is found, it will return an error
func ValidateOptions(options []string, caseSensitive bool, trimSpace bool) AnnotationValidator { func ValidateOptions(options []string, caseSensitive, trimSpace bool) AnnotationValidator {
return func(s string) error { return func(s string) error {
if trimSpace { if trimSpace {
s = strings.TrimSpace(s) s = strings.TrimSpace(s)
@ -161,7 +161,7 @@ func ValidateDuration(value string) error {
// ValidateNull always return null values and should not be widely used. // ValidateNull always return null values and should not be widely used.
// It is used on the "snippet" annotations, as it is up to the admin to allow its // It is used on the "snippet" annotations, as it is up to the admin to allow its
// usage, knowing it can be critical! // usage, knowing it can be critical!
func ValidateNull(value string) error { func ValidateNull(_ string) error {
return nil return nil
} }

View file

@ -223,7 +223,6 @@ func Test_checkAnnotation(t *testing.T) {
} }
func TestCheckAnnotationRisk(t *testing.T) { func TestCheckAnnotationRisk(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
annotations map[string]string annotations map[string]string

View file

@ -45,9 +45,7 @@ const (
proxyMaxTempFileSizeAnnotation = "proxy-max-temp-file-size" proxyMaxTempFileSizeAnnotation = "proxy-max-temp-file-size"
) )
var ( var validUpstreamAnnotation = regexp.MustCompile(`^((error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off)\s?)+$`)
validUpstreamAnnotation = regexp.MustCompile(`^((error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off)\s?)+$`)
)
var proxyAnnotations = parser.Annotation{ var proxyAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
@ -78,32 +76,32 @@ var proxyAnnotations = parser.Annotation{
By default proxy buffers number is set as 4`, By default proxy buffers number is set as 4`,
}, },
proxyBufferSizeAnnotation: { proxyBufferSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true), Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation sets the size of the buffer proxy_buffer_size used for reading the first part of the response received from the proxied server. Documentation: `This annotation sets the size of the buffer proxy_buffer_size used for reading the first part of the response received from the proxied server.
By default proxy buffer size is set as "4k".`, By default proxy buffer size is set as "4k".`,
}, },
proxyCookiePathAnnotation: { proxyCookiePathAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets a text that should be changed in the path attribute of the "Set-Cookie" header fields of a proxied server response.`, Documentation: `This annotation sets a text that should be changed in the path attribute of the "Set-Cookie" header fields of a proxied server response.`,
}, },
proxyCookieDomainAnnotation: { proxyCookieDomainAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation ets a text that should be changed in the domain attribute of the "Set-Cookie" header fields of a proxied server response.`, Documentation: `This annotation ets a text that should be changed in the domain attribute of the "Set-Cookie" header fields of a proxied server response.`,
}, },
proxyBodySizeAnnotation: { proxyBodySizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true), Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows setting the maximum allowed size of a client request body.`, Documentation: `This annotation allows setting the maximum allowed size of a client request body.`,
}, },
proxyNextUpstreamAnnotation: { proxyNextUpstreamAnnotation: {
Validator: parser.ValidateRegex(*validUpstreamAnnotation, false), Validator: parser.ValidateRegex(validUpstreamAnnotation, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines when the next upstream should be used. Documentation: `This annotation defines when the next upstream should be used.
@ -129,13 +127,13 @@ var proxyAnnotations = parser.Annotation{
Documentation: `This annotation enables or disables buffering of a client request body.`, Documentation: `This annotation enables or disables buffering of a client request body.`,
}, },
proxyRedirectFromAnnotation: { proxyRedirectFromAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`, Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`,
}, },
proxyRedirectToAnnotation: { proxyRedirectToAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`, Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`,
@ -153,7 +151,7 @@ var proxyAnnotations = parser.Annotation{
Documentation: `This annotations sets the HTTP protocol version for proxying. Can be "1.0" or "1.1".`, Documentation: `This annotations sets the HTTP protocol version for proxying. Can be "1.0" or "1.1".`,
}, },
proxyMaxTempFileSizeAnnotation: { proxyMaxTempFileSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true), Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines the maximum size of a temporary file when buffering responses.`, Documentation: `This annotation defines the maximum size of a temporary file when buffering responses.`,
@ -253,7 +251,8 @@ type proxy struct {
// NewParser creates a new reverse proxy configuration annotation parser // NewParser creates a new reverse proxy configuration annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxy{r: r, return proxy{
r: r,
annotationConfig: proxyAnnotations, annotationConfig: proxyAnnotations,
} }
} }

View file

@ -28,6 +28,12 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
off = "off"
proxyHTTPVersion = "1.0"
proxyMaxTempFileSize = "128k"
)
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -87,7 +93,7 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend {
ProxyNextUpstreamTimeout: 0, ProxyNextUpstreamTimeout: 0,
ProxyNextUpstreamTries: 3, ProxyNextUpstreamTries: 3,
ProxyRequestBuffering: "on", ProxyRequestBuffering: "on",
ProxyBuffering: "off", ProxyBuffering: off,
ProxyHTTPVersion: "1.1", ProxyHTTPVersion: "1.1",
ProxyMaxTempFileSize: "1024m", ProxyMaxTempFileSize: "1024m",
} }
@ -103,13 +109,13 @@ func TestProxy(t *testing.T) {
data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8" data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8"
data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k" data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k" data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off" data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = off
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5" data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3" data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off" data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = off
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on" data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0" data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k" data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)
@ -138,7 +144,7 @@ func TestProxy(t *testing.T) {
if p.BodySize != "2k" { if p.BodySize != "2k" {
t.Errorf("expected 2k as body-size but returned %v", p.BodySize) t.Errorf("expected 2k as body-size but returned %v", p.BodySize)
} }
if p.NextUpstream != "off" { if p.NextUpstream != off {
t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream) t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream)
} }
if p.NextUpstreamTimeout != 5 { if p.NextUpstreamTimeout != 5 {
@ -147,16 +153,16 @@ func TestProxy(t *testing.T) {
if p.NextUpstreamTries != 3 { if p.NextUpstreamTries != 3 {
t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries) t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries)
} }
if p.RequestBuffering != "off" { if p.RequestBuffering != off {
t.Errorf("expected off as request-buffering but returned %v", p.RequestBuffering) t.Errorf("expected off as request-buffering but returned %v", p.RequestBuffering)
} }
if p.ProxyBuffering != "on" { if p.ProxyBuffering != "on" {
t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering) t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering)
} }
if p.ProxyHTTPVersion != "1.0" { if p.ProxyHTTPVersion != proxyHTTPVersion {
t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion) t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion)
} }
if p.ProxyMaxTempFileSize != "128k" { if p.ProxyMaxTempFileSize != proxyMaxTempFileSize {
t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize) t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize)
} }
} }
@ -176,8 +182,8 @@ func TestProxyComplex(t *testing.T) {
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3" data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off" data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on" data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0" data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k" data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)
@ -221,10 +227,10 @@ func TestProxyComplex(t *testing.T) {
if p.ProxyBuffering != "on" { if p.ProxyBuffering != "on" {
t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering) t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering)
} }
if p.ProxyHTTPVersion != "1.0" { if p.ProxyHTTPVersion != proxyHTTPVersion {
t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion) t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion)
} }
if p.ProxyMaxTempFileSize != "128k" { if p.ProxyMaxTempFileSize != proxyMaxTempFileSize {
t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize) t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize)
} }
} }

View file

@ -24,7 +24,6 @@ import (
networking "k8s.io/api/networking/v1" networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/k8s" "k8s.io/ingress-nginx/internal/k8s"
@ -42,7 +41,7 @@ const (
var ( var (
proxySSLOnOffRegex = regexp.MustCompile(`^(on|off)$`) proxySSLOnOffRegex = regexp.MustCompile(`^(on|off)$`)
proxySSLProtocolRegex = regexp.MustCompile(`^(SSLv2|SSLv3|TLSv1|TLSv1\.1|TLSv1\.2|TLSv1\.3| )*$`) proxySSLProtocolRegex = regexp.MustCompile(`^(SSLv2|SSLv3|TLSv1|TLSv1\.1|TLSv1\.2|TLSv1\.3| )*$`)
proxySSLCiphersRegex = regexp.MustCompile(`^[A-Za-z0-9\+\:\_\-\!]*$`) proxySSLCiphersRegex = regexp.MustCompile(`^[A-Za-z0-9\+:\_\-!]*$`)
) )
const ( const (
@ -59,7 +58,7 @@ var proxySSLAnnotation = parser.Annotation{
Group: "proxy", Group: "proxy",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
proxySSLSecretAnnotation: { proxySSLSecretAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation specifies a Secret with the certificate tls.crt, key tls.key in PEM format used for authentication to a proxied HTTPS server. Documentation: `This annotation specifies a Secret with the certificate tls.crt, key tls.key in PEM format used for authentication to a proxied HTTPS server.
@ -68,14 +67,14 @@ var proxySSLAnnotation = parser.Annotation{
Just secrets on the same namespace of the ingress can be used.`, Just secrets on the same namespace of the ingress can be used.`,
}, },
proxySSLCiphersAnnotation: { proxySSLCiphersAnnotation: {
Validator: parser.ValidateRegex(*proxySSLCiphersRegex, true), Validator: parser.ValidateRegex(proxySSLCiphersRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation Specifies the enabled ciphers for requests to a proxied HTTPS server. Documentation: `This annotation Specifies the enabled ciphers for requests to a proxied HTTPS server.
The ciphers are specified in the format understood by the OpenSSL library.`, The ciphers are specified in the format understood by the OpenSSL library.`,
}, },
proxySSLProtocolsAnnotation: { proxySSLProtocolsAnnotation: {
Validator: parser.ValidateRegex(*proxySSLProtocolRegex, true), Validator: parser.ValidateRegex(proxySSLProtocolRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables the specified protocols for requests to a proxied HTTPS server.`, Documentation: `This annotation enables the specified protocols for requests to a proxied HTTPS server.`,
@ -88,7 +87,7 @@ var proxySSLAnnotation = parser.Annotation{
This value is also passed through SNI when a connection is established to the proxied HTTPS server.`, This value is also passed through SNI when a connection is established to the proxied HTTPS server.`,
}, },
proxySSLVerifyAnnotation: { proxySSLVerifyAnnotation: {
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true), Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables or disables verification of the proxied HTTPS server certificate. (default: off)`, Documentation: `This annotation enables or disables verification of the proxied HTTPS server certificate. (default: off)`,
@ -100,7 +99,7 @@ var proxySSLAnnotation = parser.Annotation{
Documentation: `This annotation Sets the verification depth in the proxied HTTPS server certificates chain. (default: 1).`, Documentation: `This annotation Sets the verification depth in the proxied HTTPS server certificates chain. (default: 1).`,
}, },
proxySSLServerNameAnnotation: { proxySSLServerNameAnnotation: {
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true), Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.`, Documentation: `This annotation enables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.`,
@ -150,10 +149,11 @@ func (pssl1 *Config) Equal(pssl2 *Config) bool {
} }
// NewParser creates a new TLS authentication annotation parser // NewParser creates a new TLS authentication annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxySSL{ return proxySSL{
r: resolver, r: r,
annotationConfig: proxySSLAnnotation} annotationConfig: proxySSLAnnotation,
}
} }
type proxySSL struct { type proxySSL struct {
@ -208,13 +208,13 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
proxyCert, err := p.r.GetAuthCertificate(proxysslsecret) proxyCert, err := p.r.GetAuthCertificate(proxysslsecret)
if err != nil { if err != nil {
e := fmt.Errorf("error obtaining certificate: %w", err) e := fmt.Errorf("error obtaining certificate: %w", err)
return &Config{}, ing_errors.LocationDenied{Reason: e} return &Config{}, ing_errors.LocationDeniedError{Reason: e}
} }
config.AuthSSLCert = *proxyCert config.AuthSSLCert = *proxyCert
config.Ciphers, err = parser.GetStringAnnotation(proxySSLCiphersAnnotation, ing, p.annotationConfig.Annotations) config.Ciphers, err = parser.GetStringAnnotation(proxySSLCiphersAnnotation, ing, p.annotationConfig.Annotations)
if err != nil { if err != nil {
if errors.IsValidationError(err) { if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-ciphers, defaulting to %s", defaultProxySSLCiphers) klog.Warningf("invalid value passed to proxy-ssl-ciphers, defaulting to %s", defaultProxySSLCiphers)
} }
config.Ciphers = defaultProxySSLCiphers config.Ciphers = defaultProxySSLCiphers
@ -222,7 +222,7 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
config.Protocols, err = parser.GetStringAnnotation(proxySSLProtocolsAnnotation, ing, p.annotationConfig.Annotations) config.Protocols, err = parser.GetStringAnnotation(proxySSLProtocolsAnnotation, ing, p.annotationConfig.Annotations)
if err != nil { if err != nil {
if errors.IsValidationError(err) { if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-protocols, defaulting to %s", defaultProxySSLProtocols) klog.Warningf("invalid value passed to proxy-ssl-protocols, defaulting to %s", defaultProxySSLProtocols)
} }
config.Protocols = defaultProxySSLProtocols config.Protocols = defaultProxySSLProtocols
@ -232,7 +232,7 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
config.ProxySSLName, err = parser.GetStringAnnotation(proxySSLNameAnnotation, ing, p.annotationConfig.Annotations) config.ProxySSLName, err = parser.GetStringAnnotation(proxySSLNameAnnotation, ing, p.annotationConfig.Annotations)
if err != nil { if err != nil {
if errors.IsValidationError(err) { if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-name, defaulting to empty") klog.Warningf("invalid value passed to proxy-ssl-name, defaulting to empty")
} }
config.ProxySSLName = "" config.ProxySSLName = ""
@ -260,7 +260,7 @@ func (p proxySSL) GetDocumentation() parser.AnnotationFields {
return p.annotationConfig.Annotations return p.annotationConfig.Annotations
} }
func (a proxySSL) Validate(anns map[string]string) error { func (p proxySSL) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(p.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, proxySSLAnnotation.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, proxySSLAnnotation.Annotations)
} }

View file

@ -27,6 +27,14 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
defaultDemoSecret = "default/demo-secret"
proxySslCiphers = "HIGH:-SHA"
off = "off"
sslServerName = "w00t"
defaultProtocol = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
)
func buildIngress() *networking.Ingress { func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{ defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{ Service: &networking.IngressServiceBackend{
@ -77,28 +85,27 @@ type mockSecret struct {
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for backend certificate authentication // GetAuthCertificate from mockSecret mocks the GetAuthCertificate for backend certificate authentication
func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if name != "default/demo-secret" { if name != defaultDemoSecret {
return nil, errors.Errorf("there is no secret with name %v", name) return nil, errors.Errorf("there is no secret with name %v", name)
} }
return &resolver.AuthSSLCert{ return &resolver.AuthSSLCert{
Secret: "default/demo-secret", Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt", CAFileName: "/ssl/ca.crt",
CASHA: "abc", CASHA: "abc",
}, nil }, nil
} }
func TestAnnotations(t *testing.T) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = "default/demo-secret" data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = defaultDemoSecret
data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = "HIGH:-SHA" data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = proxySslCiphers
data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host" data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host"
data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv1.3 SSLv2 TLSv1 TLSv1.2" data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv1.3 SSLv2 TLSv1 TLSv1.2"
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "on" data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "off" data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = off
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "3" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "3"
@ -115,7 +122,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected *Config but got %v", u) t.Errorf("expected *Config but got %v", u)
} }
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret") secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
if err != nil { if err != nil {
t.Errorf("unexpected error getting secret %v", err) t.Errorf("unexpected error getting secret %v", err)
} }
@ -123,11 +130,11 @@ func TestAnnotations(t *testing.T) {
if u.AuthSSLCert.Secret != secret.Secret { if u.AuthSSLCert.Secret != secret.Secret {
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret) t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
} }
if u.Ciphers != "HIGH:-SHA" { if u.Ciphers != proxySslCiphers {
t.Errorf("expected %v but got %v", "HIGH:-SHA", u.Ciphers) t.Errorf("expected %v but got %v", proxySslCiphers, u.Ciphers)
} }
if u.Protocols != "SSLv2 TLSv1 TLSv1.2 TLSv1.3" { if u.Protocols != defaultProtocol {
t.Errorf("expected %v but got %v", "SSLv2 TLSv1 TLSv1.2 TLSv1.3", u.Protocols) t.Errorf("expected %v but got %v", defaultProtocol, u.Protocols)
} }
if u.Verify != "on" { if u.Verify != "on" {
t.Errorf("expected %v but got %v", "on", u.Verify) t.Errorf("expected %v but got %v", "on", u.Verify)
@ -141,7 +148,6 @@ func TestAnnotations(t *testing.T) {
if u.ProxySSLServerName != "on" { if u.ProxySSLServerName != "on" {
t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName) t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName)
} }
} }
func TestInvalidAnnotations(t *testing.T) { func TestInvalidAnnotations(t *testing.T) {
@ -172,11 +178,11 @@ func TestInvalidAnnotations(t *testing.T) {
} }
// Invalid optional Annotations // Invalid optional Annotations
data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = "default/demo-secret" data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = defaultDemoSecret
data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv111 SSLv1" data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv111 SSLv1"
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "w00t" data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "w00t" data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "w00t" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "abcd" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "abcd"
ing.SetAnnotations(data) ing.SetAnnotations(data)
@ -207,21 +213,15 @@ func TestEquals(t *testing.T) {
cfg1 := &Config{} cfg1 := &Config{}
cfg2 := &Config{} cfg2 := &Config{}
// Same config
result := cfg1.Equal(cfg1)
if result != true {
t.Errorf("Expected true")
}
// compare nil // compare nil
result = cfg1.Equal(nil) result := cfg1.Equal(nil)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
} }
// Different Certs // Different Certs
sslCert1 := resolver.AuthSSLCert{ sslCert1 := resolver.AuthSSLCert{
Secret: "default/demo-secret", Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt", CAFileName: "/ssl/ca.crt",
CASHA: "abc", CASHA: "abc",
} }
@ -240,7 +240,7 @@ func TestEquals(t *testing.T) {
// Different Ciphers // Different Ciphers
cfg1.Ciphers = "DEFAULT" cfg1.Ciphers = "DEFAULT"
cfg2.Ciphers = "HIGH:-SHA" cfg2.Ciphers = proxySslCiphers
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
@ -248,22 +248,22 @@ func TestEquals(t *testing.T) {
cfg2.Ciphers = "DEFAULT" cfg2.Ciphers = "DEFAULT"
// Different Protocols // Different Protocols
cfg1.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3" cfg1.Protocols = defaultProtocol
cfg2.Protocols = "SSLv3 TLSv1 TLSv1.2 TLSv1.3" cfg2.Protocols = "SSLv3 TLSv1 TLSv1.2 TLSv1.3"
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
} }
cfg2.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3" cfg2.Protocols = defaultProtocol
// Different Verify // Different Verify
cfg1.Verify = "off" cfg1.Verify = off
cfg2.Verify = "on" cfg2.Verify = "on"
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
} }
cfg2.Verify = "off" cfg2.Verify = off
// Different VerifyDepth // Different VerifyDepth
cfg1.VerifyDepth = 1 cfg1.VerifyDepth = 1
@ -275,13 +275,13 @@ func TestEquals(t *testing.T) {
cfg2.VerifyDepth = 1 cfg2.VerifyDepth = 1
// Different ProxySSLServerName // Different ProxySSLServerName
cfg1.ProxySSLServerName = "off" cfg1.ProxySSLServerName = off
cfg2.ProxySSLServerName = "on" cfg2.ProxySSLServerName = "on"
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)
if result != false { if result != false {
t.Errorf("Expected false") t.Errorf("Expected false")
} }
cfg2.ProxySSLServerName = "off" cfg2.ProxySSLServerName = off
// Equal Configs // Equal Configs
result = cfg1.Equal(cfg2) result = cfg1.Equal(cfg2)

View file

@ -288,7 +288,7 @@ func (a ratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
func encode(s string) string { func encode(s string) string {
str := base64.URLEncoding.EncodeToString([]byte(s)) str := base64.URLEncoding.EncodeToString([]byte(s))
return strings.Replace(str, "=", "", -1) return strings.ReplaceAll(str, "=", "")
} }
func (a ratelimit) GetDocumentation() parser.AnnotationFields { func (a ratelimit) GetDocumentation() parser.AnnotationFields {

View file

@ -54,14 +54,14 @@ var redirectAnnotations = parser.Annotation{
Documentation: `In some scenarios is required to redirect from www.domain.com to domain.com or vice versa. To enable this feature use this annotation.`, Documentation: `In some scenarios is required to redirect from www.domain.com to domain.com or vice versa. To enable this feature use this annotation.`,
}, },
temporalRedirectAnnotation: { temporalRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false), Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated
Documentation: `This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream. Documentation: `This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream.
For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`, For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`,
}, },
permanentRedirectAnnotation: { permanentRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false), Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated
Documentation: `This annotation allows to return a permanent redirect (Return Code 301) instead of sending data to the upstream. Documentation: `This annotation allows to return a permanent redirect (Return Code 301) instead of sending data to the upstream.
@ -174,11 +174,11 @@ func isValidURL(s string) error {
return nil return nil
} }
func (a redirect) GetDocumentation() parser.AnnotationFields { func (r redirect) GetDocumentation() parser.AnnotationFields {
return a.annotationConfig.Annotations return r.annotationConfig.Annotations
} }
func (a redirect) Validate(anns map[string]string) error { func (r redirect) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(r.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, redirectAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, redirectAnnotations.Annotations)
} }

View file

@ -136,7 +136,6 @@ func TestTemporalRedirect(t *testing.T) {
} }
func TestIsValidURL(t *testing.T) { func TestIsValidURL(t *testing.T) {
invalid := "ok.com" invalid := "ok.com"
urlParse, err := url.Parse(invalid) urlParse, err := url.Parse(invalid)
if err != nil { if err != nil {

View file

@ -40,7 +40,7 @@ var rewriteAnnotations = parser.Annotation{
Group: "rewrite", Group: "rewrite",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
rewriteTargetAnnotation: { rewriteTargetAnnotation: {
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false), Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the target URI where the traffic must be redirected. It can contain regular characters and captured Documentation: `This annotation allows to specify the target URI where the traffic must be redirected. It can contain regular characters and captured
@ -72,7 +72,7 @@ var rewriteAnnotations = parser.Annotation{
the pathType should also be defined as 'ImplementationSpecific'.`, the pathType should also be defined as 'ImplementationSpecific'.`,
}, },
appRootAnnotation: { appRootAnnotation: {
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false), Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Application Root that the Controller must redirect if it's in / context`, Documentation: `This annotation defines the Application Root that the Controller must redirect if it's in / context`,

View file

@ -120,7 +120,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -132,7 +135,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/$1/abc/$2" data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/$1/abc/$2"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: true}).Parse(ing) i, err = NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -144,7 +150,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/xas{445}" data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/xas{445}"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: true}).Parse(ing) i, err = NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -156,7 +165,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false" data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) i, err = NewParser(mockBackend{redirect: false}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -173,7 +185,10 @@ func TestForceSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -185,7 +200,10 @@ func TestForceSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true" data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) i, err = NewParser(mockBackend{redirect: false}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -194,6 +212,7 @@ func TestForceSSLRedirect(t *testing.T) {
t.Errorf("Expected true but returned false") t.Errorf("Expected true but returned false")
} }
} }
func TestAppRoot(t *testing.T) { func TestAppRoot(t *testing.T) {
ap := NewParser(mockBackend{redirect: true}) ap := NewParser(mockBackend{redirect: true})
@ -241,7 +260,10 @@ func TestUseRegex(t *testing.T) {
data[parser.GetAnnotationWithPrefix("use-regex")] = "true" data[parser.GetAnnotationWithPrefix("use-regex")] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a App Context") t.Errorf("expected a App Context")

View file

@ -69,7 +69,7 @@ func (s satisfy) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations return s.annotationConfig.Annotations
} }
func (a satisfy) Validate(anns map[string]string) error { func (s satisfy) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, satisfyAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, satisfyAnnotations.Annotations)
} }

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -69,7 +69,7 @@ func (s serviceUpstream) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations return s.annotationConfig.Annotations
} }
func (a serviceUpstream) Validate(anns map[string]string) error { func (s serviceUpstream) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, serviceUpstreamAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, serviceUpstreamAnnotations.Annotations)
} }

View file

@ -77,7 +77,10 @@ func TestIngressAnnotationServiceUpstreamEnabled(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "true" data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")
@ -96,7 +99,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false" data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")
@ -110,7 +116,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data = map[string]string{} data = map[string]string{}
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ = NewParser(&resolver.Mock{}).Parse(ing) val, err = NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok = val.(bool) enabled, ok = val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")
@ -137,7 +146,10 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend {
func TestParseAnnotationsWithDefaultConfig(t *testing.T) { func TestParseAnnotationsWithDefaultConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
val, _ := NewParser(mockBackend{}).Parse(ing) val, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {
@ -158,7 +170,10 @@ func TestParseAnnotationsOverridesDefaultConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false" data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(mockBackend{}).Parse(ing) val, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {

View file

@ -63,13 +63,15 @@ const (
// This is used to control the cookie change after request failure // This is used to control the cookie change after request failure
annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure" annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
cookieAffinity = "cookie"
) )
var sessionAffinityAnnotations = parser.Annotation{ var sessionAffinityAnnotations = parser.Annotation{
Group: "affinity", Group: "affinity",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
annotationAffinityType: { annotationAffinityType: {
Validator: parser.ValidateOptions([]string{"cookie"}, true, true), Validator: parser.ValidateOptions([]string{cookieAffinity}, true, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables and sets the affinity type in all Upstreams of an Ingress. This way, a request will always be directed to the same upstream server. The only affinity type available for NGINX is cookie`, Documentation: `This annotation enables and sets the affinity type in all Upstreams of an Ingress. This way, a request will always be directed to the same upstream server. The only affinity type available for NGINX is cookie`,
@ -91,7 +93,7 @@ var sessionAffinityAnnotations = parser.Annotation{
Setting this to legacy will restore original canary behavior, when session affinity was ignored.`, Setting this to legacy will restore original canary behavior, when session affinity was ignored.`,
}, },
annotationAffinityCookieName: { annotationAffinityCookieName: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the name of the cookie that will be used to route the requests`, Documentation: `This annotation allows to specify the name of the cookie that will be used to route the requests`,
@ -103,25 +105,25 @@ var sessionAffinityAnnotations = parser.Annotation{
Documentation: `This annotation set the cookie as secure regardless the protocol of the incoming request`, Documentation: `This annotation set the cookie as secure regardless the protocol of the incoming request`,
}, },
annotationAffinityCookieExpires: { annotationAffinityCookieExpires: {
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, true), Validator: parser.ValidateRegex(affinityCookieExpiresRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation is a legacy version of "session-cookie-max-age" for compatibility with older browsers, generates an "Expires" cookie directive by adding the seconds to the current date`, Documentation: `This annotation is a legacy version of "session-cookie-max-age" for compatibility with older browsers, generates an "Expires" cookie directive by adding the seconds to the current date`,
}, },
annotationAffinityCookieMaxAge: { annotationAffinityCookieMaxAge: {
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, false), Validator: parser.ValidateRegex(affinityCookieExpiresRegex, false),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the time until the cookie expires`, Documentation: `This annotation sets the time until the cookie expires`,
}, },
annotationAffinityCookiePath: { annotationAffinityCookiePath: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true), Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Path that will be set on the cookie (required if your Ingress paths use regular expressions)`, Documentation: `This annotation defines the Path that will be set on the cookie (required if your Ingress paths use regular expressions)`,
}, },
annotationAffinityCookieDomain: { annotationAffinityCookieDomain: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium, Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Domain attribute of the sticky cookie.`, Documentation: `This annotation defines the Domain attribute of the sticky cookie.`,
@ -149,9 +151,7 @@ var sessionAffinityAnnotations = parser.Annotation{
}, },
} }
var ( var affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
)
// Config describes the per ingress session affinity config // Config describes the per ingress session affinity config
type Config struct { type Config struct {
@ -186,6 +186,11 @@ type Cookie struct {
ConditionalSameSiteNone bool `json:"conditional-samesite-none"` ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
} }
type affinity struct {
r resolver.Resolver
annotationConfig parser.Annotation
}
// cookieAffinityParse gets the annotation values related to Cookie Affinity // cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found // It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie { func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
@ -252,11 +257,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
} }
} }
type affinity struct {
r resolver.Resolver
annotationConfig parser.Annotation
}
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to configure the affinity directives // rule used to configure the affinity directives
func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) { func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
@ -279,11 +279,10 @@ func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
} }
switch at { switch at {
case "cookie": case cookieAffinity:
cookie = a.cookieAffinityParse(ing) cookie = a.cookieAffinityParse(ing)
default: default:
klog.V(3).InfoS("No default affinity found", "ingress", ing.Name) klog.V(3).InfoS("No default affinity found", "ingress", ing.Name)
} }
return &Config{ return &Config{

View file

@ -83,7 +83,11 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
affin, _ := NewParser(&resolver.Mock{}).Parse(ing) affin, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing annotations: %v", err)
}
nginxAffinity, ok := affin.(*Config) nginxAffinity, ok := affin.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -31,10 +31,8 @@ const (
sslCipherAnnotation = "ssl-ciphers" sslCipherAnnotation = "ssl-ciphers"
) )
var ( // Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" var regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
)
var sslCipherAnnotations = parser.Annotation{ var sslCipherAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
@ -47,7 +45,7 @@ var sslCipherAnnotations = parser.Annotation{
This configuration specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.`, This configuration specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.`,
}, },
sslCipherAnnotation: { sslCipherAnnotation: {
Validator: parser.ValidateRegex(*regexValidSSLCipher, true), Validator: parser.ValidateRegex(regexValidSSLCipher, true),
Scope: parser.AnnotationScopeIngress, Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow, Risk: parser.AnnotationRiskLow,
Documentation: `Using this annotation will set the ssl_ciphers directive at the server level. This configuration is active for all the paths in the host.`, Documentation: `Using this annotation will set the ssl_ciphers directive at the server level. This configuration is active for all the paths in the host.`,
@ -104,7 +102,7 @@ func (sc sslCipher) GetDocumentation() parser.AnnotationFields {
return sc.annotationConfig.Annotations return sc.annotationConfig.Annotations
} }
func (a sslCipher) Validate(anns map[string]string) error { func (sc sslCipher) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(sc.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, sslCipherAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, sslCipherAnnotations.Annotations)
} }

View file

@ -42,8 +42,11 @@ func TestParse(t *testing.T) {
expectErr bool expectErr bool
}{ }{
{map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", ""}, false}, {map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", ""}, false},
{map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"}, {
Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""}, false}, map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"},
Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""},
false,
},
{map[string]string{annotationSSLCiphers: ""}, Config{"", ""}, false}, {map[string]string{annotationSSLCiphers: ""}, Config{"", ""}, false},
{map[string]string{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}, false}, {map[string]string{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}, false},
{map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}, false}, {map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}, false},

View file

@ -47,7 +47,8 @@ type sslpt struct {
// NewParser creates a new SSL passthrough annotation parser // NewParser creates a new SSL passthrough annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return sslpt{r: r, return sslpt{
r: r,
annotationConfig: sslPassthroughAnnotations, annotationConfig: sslPassthroughAnnotations,
} }
} }

View file

@ -38,7 +38,8 @@ func TestParse(t *testing.T) {
annotations map[string]string annotations map[string]string
expected string expected string
}{ }{
{map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"}, {
map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"},
"server { listen: 8000; proxy_pass 127.0.0.1:80}", "server { listen: 8000; proxy_pass 127.0.0.1:80}",
}, },
{map[string]string{annotation: "false"}, "false"}, {map[string]string{annotation: "false"}, "false"},
@ -56,6 +57,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -41,7 +41,7 @@ var upstreamHashByAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
upstreamHashByAnnotation: { upstreamHashByAnnotation: {
Validator: parser.ValidateRegex(*hashByRegex, true), Validator: parser.ValidateRegex(hashByRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, // High, this annotation allows accessing NGINX variables Risk: parser.AnnotationRiskHigh, // High, this annotation allows accessing NGINX variables
Documentation: `This annotation defines the nginx variable, text value or any combination thereof to use for consistent hashing. Documentation: `This annotation defines the nginx variable, text value or any combination thereof to use for consistent hashing.

View file

@ -31,7 +31,7 @@ var xForwardedForAnnotations = parser.Annotation{
Group: "backend", Group: "backend",
Annotations: parser.AnnotationFields{ Annotations: parser.AnnotationFields{
xForwardedForPrefixAnnotation: { xForwardedForPrefixAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true), Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation, Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, // Low, as it allows regexes but on a very limited set Risk: parser.AnnotationRiskLow, // Low, as it allows regexes but on a very limited set
Documentation: `This annotation can be used to add the non-standard X-Forwarded-Prefix header to the upstream request with a string value`, Documentation: `This annotation can be used to add the non-standard X-Forwarded-Prefix header to the upstream request with a string value`,
@ -54,15 +54,15 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to add an x-forwarded-prefix header to the request // used to add an x-forwarded-prefix header to the request
func (cbbs xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) { func (x xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, cbbs.annotationConfig.Annotations) return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, x.annotationConfig.Annotations)
} }
func (cbbs xforwardedprefix) GetDocumentation() parser.AnnotationFields { func (x xforwardedprefix) GetDocumentation() parser.AnnotationFields {
return cbbs.annotationConfig.Annotations return x.annotationConfig.Annotations
} }
func (a xforwardedprefix) Validate(anns map[string]string) error { func (x xforwardedprefix) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel) maxrisk := parser.StringRiskToRisk(x.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, xForwardedForAnnotations.Annotations) return parser.CheckAnnotationRisk(anns, maxrisk, xForwardedForAnnotations.Annotations)
} }

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations) ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing) result, _ := ap.Parse(ing)
if result != testCase.expected { if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations) t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -100,7 +100,7 @@ func matchHostnames(pattern, host string) bool {
host = strings.TrimSuffix(host, ".") host = strings.TrimSuffix(host, ".")
pattern = strings.TrimSuffix(pattern, ".") pattern = strings.TrimSuffix(pattern, ".")
if len(pattern) == 0 || len(host) == 0 { if pattern == "" || host == "" {
return false return false
} }

View file

@ -29,7 +29,7 @@ import (
) )
// Name returns the healthcheck name // Name returns the healthcheck name
func (n NGINXController) Name() string { func (n *NGINXController) Name() string {
return "nginx-ingress-controller" return "nginx-ingress-controller"
} }

View file

@ -32,7 +32,7 @@ import (
) )
func TestNginxCheck(t *testing.T) { func TestNginxCheck(t *testing.T) {
var tests = []struct { tests := []struct {
healthzPath string healthzPath string
}{ }{
{"/healthz"}, {"/healthz"},
@ -42,7 +42,6 @@ func TestNginxCheck(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
testName := fmt.Sprintf("health path: %s", tt.healthzPath) testName := fmt.Sprintf("health path: %s", tt.healthzPath)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
mux := http.NewServeMux() mux := http.NewServeMux()
listener, err := tryListen("tcp", fmt.Sprintf(":%v", nginx.StatusPort)) listener, err := tryListen("tcp", fmt.Sprintf(":%v", nginx.StatusPort))
@ -50,7 +49,7 @@ func TestNginxCheck(t *testing.T) {
t.Fatalf("creating tcp listener: %s", err) t.Fatalf("creating tcp listener: %s", err)
} }
defer listener.Close() defer listener.Close()
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
server := &httptest.Server{ server := &httptest.Server{
Listener: listener, Listener: listener,
Config: &http.Server{ Config: &http.Server{
@ -103,10 +102,10 @@ func TestNginxCheck(t *testing.T) {
} }
}() }()
go func() { go func() {
cmd.Wait() //nolint:errcheck cmd.Wait() //nolint:errcheck // Ignore the error
}() }()
if _, err := pidFile.Write([]byte(fmt.Sprintf("%v", pid))); err != nil { if _, err := fmt.Fprintf(pidFile, "%v", pid); err != nil {
t.Errorf("unexpected error writing the pid file: %v", err) t.Errorf("unexpected error writing the pid file: %v", err)
} }
@ -121,7 +120,7 @@ func TestNginxCheck(t *testing.T) {
}) })
// pollute pid file // pollute pid file
pidFile.Write([]byte("999999")) //nolint:errcheck pidFile.WriteString("999999") //nolint:errcheck // Ignore the error
pidFile.Close() pidFile.Close()
t.Run("bad pid", func(t *testing.T) { t.Run("bad pid", func(t *testing.T) {
@ -134,7 +133,7 @@ func TestNginxCheck(t *testing.T) {
} }
func callHealthz(expErr bool, healthzPath string, mux *http.ServeMux) error { func callHealthz(expErr bool, healthzPath string, mux *http.ServeMux) error {
req, err := http.NewRequest(http.MethodGet, healthzPath, nil) req, err := http.NewRequest(http.MethodGet, healthzPath, http.NoBody)
if err != nil { if err != nil {
return fmt.Errorf("healthz error: %v", err) return fmt.Errorf("healthz error: %v", err)
} }

View file

@ -29,10 +29,8 @@ import (
"k8s.io/ingress-nginx/pkg/util/runtime" "k8s.io/ingress-nginx/pkg/util/runtime"
) )
var ( // EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates. var EnableSSLChainCompletion = false
EnableSSLChainCompletion = false
)
const ( const (
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size // http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
@ -91,7 +89,7 @@ const (
// Configuration represents the content of nginx.conf file // Configuration represents the content of nginx.conf file
type Configuration struct { type Configuration struct {
defaults.Backend `json:",squash"` //nolint:staticcheck defaults.Backend `json:",squash"` //nolint:staticcheck // Ignore unknown JSON option "squash" error
// AllowSnippetAnnotations enable users to add their own snippets via ingress annotation. // AllowSnippetAnnotations enable users to add their own snippets via ingress annotation.
// If disabled, only snippets added via ConfigMap are added to ingress. // If disabled, only snippets added via ConfigMap are added to ingress.
@ -141,9 +139,9 @@ type Configuration struct {
// By default access logs go to /var/log/nginx/access.log // By default access logs go to /var/log/nginx/access.log
AccessLogPath string `json:"access-log-path,omitempty"` AccessLogPath string `json:"access-log-path,omitempty"`
// HttpAccessLogPath sets the path of the access logs for http context globally if enabled // HTTPAccessLogPath sets the path of the access logs for http context globally if enabled
// http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log // http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
HttpAccessLogPath string `json:"http-access-log-path,omitempty"` HTTPAccessLogPath string `json:"http-access-log-path,omitempty"`
// StreamAccessLogPath sets the path of the access logs for stream context globally if enabled // StreamAccessLogPath sets the path of the access logs for stream context globally if enabled
// http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log // http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log
@ -230,19 +228,19 @@ type Configuration struct {
// https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_field_size // https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_field_size
// HTTP2MaxFieldSize Limits the maximum size of an HPACK-compressed request header field // HTTP2MaxFieldSize Limits the maximum size of an HPACK-compressed request header field
// NOTE: Deprecated // Deprecated: HTTP2MaxFieldSize is deprecated.
HTTP2MaxFieldSize string `json:"http2-max-field-size,omitempty"` HTTP2MaxFieldSize string `json:"http2-max-field-size,omitempty"`
// https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_header_size // https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_header_size
// HTTP2MaxHeaderSize Limits the maximum size of the entire request header list after HPACK decompression // HTTP2MaxHeaderSize Limits the maximum size of the entire request header list after HPACK decompression
// NOTE: Deprecated // Deprecated: HTTP2MaxHeaderSize is deprecated.
HTTP2MaxHeaderSize string `json:"http2-max-header-size,omitempty"` HTTP2MaxHeaderSize string `json:"http2-max-header-size,omitempty"`
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests // http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests
// HTTP2MaxRequests Sets the maximum number of requests (including push requests) that can be served // HTTP2MaxRequests Sets the maximum number of requests (including push requests) that can be served
// through one HTTP/2 connection, after which the next client request will lead to connection closing // through one HTTP/2 connection, after which the next client request will lead to connection closing
// and the need of establishing a new connection. // and the need of establishing a new connection.
// NOTE: Deprecated // Deprecated: HTTP2MaxRequests is deprecated.
HTTP2MaxRequests int `json:"http2-max-requests,omitempty"` HTTP2MaxRequests int `json:"http2-max-requests,omitempty"`
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams // http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams
@ -552,7 +550,7 @@ type Configuration struct {
UseForwardedHeaders bool `json:"use-forwarded-headers"` UseForwardedHeaders bool `json:"use-forwarded-headers"`
// Sets whether to enable the real ip module // Sets whether to enable the real ip module
EnableRealIp bool `json:"enable-real-ip"` EnableRealIP bool `json:"enable-real-ip"`
// Sets the header field for identifying the originating IP address of a client // Sets the header field for identifying the originating IP address of a client
// Default is X-Forwarded-For // Default is X-Forwarded-For
@ -621,7 +619,7 @@ type Configuration struct {
// Default: 0.01 // Default: 0.01
OtelSamplerRatio float32 `json:"otel-sampler-ratio"` OtelSamplerRatio float32 `json:"otel-sampler-ratio"`
//OtelSamplerParentBased specifies the parent based sampler to be use for any traces created // OtelSamplerParentBased specifies the parent based sampler to be use for any traces created
// Default: true // Default: true
OtelSamplerParentBased bool `json:"otel-sampler-parent-based"` OtelSamplerParentBased bool `json:"otel-sampler-parent-based"`
@ -891,7 +889,7 @@ func NewDefault() Configuration {
EnableUnderscoresInHeaders: false, EnableUnderscoresInHeaders: false,
ErrorLogLevel: errorLevel, ErrorLogLevel: errorLevel,
UseForwardedHeaders: false, UseForwardedHeaders: false,
EnableRealIp: false, EnableRealIP: false,
ForwardedForHeader: "X-Forwarded-For", ForwardedForHeader: "X-Forwarded-For",
ComputeFullForwardedFor: false, ComputeFullForwardedFor: false,
ProxyAddOriginalURIHeader: false, ProxyAddOriginalURIHeader: false,
@ -1039,41 +1037,41 @@ func NewDefault() Configuration {
// TemplateConfig contains the nginx configuration to render the file nginx.conf // TemplateConfig contains the nginx configuration to render the file nginx.conf
type TemplateConfig struct { type TemplateConfig struct {
ProxySetHeaders map[string]string ProxySetHeaders map[string]string `json:"ProxySetHeaders"`
AddHeaders map[string]string AddHeaders map[string]string `json:"AddHeaders"`
BacklogSize int BacklogSize int `json:"BacklogSize"`
Backends []*ingress.Backend Backends []*ingress.Backend `json:"Backends"`
PassthroughBackends []*ingress.SSLPassthroughBackend PassthroughBackends []*ingress.SSLPassthroughBackend `json:"PassthroughBackends"`
Servers []*ingress.Server Servers []*ingress.Server `json:"Servers"`
TCPBackends []ingress.L4Service TCPBackends []ingress.L4Service `json:"TCPBackends"`
UDPBackends []ingress.L4Service UDPBackends []ingress.L4Service `json:"UDPBackends"`
HealthzURI string HealthzURI string `json:"HealthzURI"`
Cfg Configuration Cfg Configuration `json:"Cfg"`
IsIPV6Enabled bool IsIPV6Enabled bool `json:"IsIPV6Enabled"`
IsSSLPassthroughEnabled bool IsSSLPassthroughEnabled bool `json:"IsSSLPassthroughEnabled"`
NginxStatusIpv4Whitelist []string NginxStatusIpv4Whitelist []string `json:"NginxStatusIpv4Whitelist"`
NginxStatusIpv6Whitelist []string NginxStatusIpv6Whitelist []string `json:"NginxStatusIpv6Whitelist"`
RedirectServers interface{} RedirectServers interface{} `json:"RedirectServers"`
ListenPorts *ListenPorts ListenPorts *ListenPorts `json:"ListenPorts"`
PublishService *apiv1.Service PublishService *apiv1.Service `json:"PublishService"`
EnableMetrics bool EnableMetrics bool `json:"EnableMetrics"`
MaxmindEditionFiles *[]string MaxmindEditionFiles *[]string `json:"MaxmindEditionFiles"`
MonitorMaxBatchSize int MonitorMaxBatchSize int `json:"MonitorMaxBatchSize"`
PID string PID string `json:"PID"`
StatusPath string StatusPath string `json:"StatusPath"`
StatusPort int StatusPort int `json:"StatusPort"`
StreamPort int StreamPort int `json:"StreamPort"`
StreamSnippets []string StreamSnippets []string `json:"StreamSnippets"`
} }
// ListenPorts describe the ports required to run the // ListenPorts describe the ports required to run the
// NGINX Ingress controller // NGINX Ingress controller
type ListenPorts struct { type ListenPorts struct {
HTTP int HTTP int `json:"HTTP"`
HTTPS int HTTPS int `json:"HTTPS"`
Health int Health int `json:"Health"`
Default int Default int `json:"Default"`
SSLProxy int SSLProxy int `json:"SSLProxy"`
} }
// GlobalExternalAuth describe external authentication configuration for the // GlobalExternalAuth describe external authentication configuration for the

View file

@ -114,7 +114,7 @@ type Configuration struct {
DisableCatchAll bool DisableCatchAll bool
IngressClassConfiguration *ingressclass.IngressClassConfiguration IngressClassConfiguration *ingressclass.Configuration
ValidationWebhook string ValidationWebhook string
ValidationWebhookCertPath string ValidationWebhookCertPath string
@ -143,7 +143,7 @@ type Configuration struct {
func getIngressPodZone(svc *apiv1.Service) string { func getIngressPodZone(svc *apiv1.Service) string {
svcKey := k8s.MetaNamespaceKey(svc) svcKey := k8s.MetaNamespaceKey(svc)
if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyMode]; ok { if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyMode]; ok {
if strings.ToLower(svcZoneAnnotation) == "auto" { if strings.EqualFold(svcZoneAnnotation, "auto") {
if foundZone, ok := k8s.IngressNodeDetails.GetLabels()[apiv1.LabelTopologyZone]; ok { if foundZone, ok := k8s.IngressNodeDetails.GetLabels()[apiv1.LabelTopologyZone]; ok {
klog.V(3).Infof("Svc has topology aware annotation enabled, try to use zone %q where controller pod is running for Service %q ", foundZone, svcKey) klog.V(3).Infof("Svc has topology aware annotation enabled, try to use zone %q where controller pod is running for Service %q ", foundZone, svcKey)
return foundZone return foundZone
@ -154,7 +154,7 @@ func getIngressPodZone(svc *apiv1.Service) string {
} }
// GetPublishService returns the Service used to set the load-balancer status of Ingresses. // GetPublishService returns the Service used to set the load-balancer status of Ingresses.
func (n NGINXController) GetPublishService() *apiv1.Service { func (n *NGINXController) GetPublishService() *apiv1.Service {
s, err := n.store.GetService(n.cfg.PublishService) s, err := n.store.GetService(n.cfg.PublishService)
if err != nil { if err != nil {
return nil return nil
@ -189,13 +189,16 @@ func (n *NGINXController) syncIngress(interface{}) error {
if !utilingress.IsDynamicConfigurationEnough(pcfg, n.runningConfig) { if !utilingress.IsDynamicConfigurationEnough(pcfg, n.runningConfig) {
klog.InfoS("Configuration changes detected, backend reload required") klog.InfoS("Configuration changes detected, backend reload required")
hash, _ := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{ hash, err := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{
TagName: "json", TagName: "json",
}) })
if err != nil {
klog.Errorf("unexpected error hashing configuration: %v", err)
}
pcfg.ConfigurationChecksum = fmt.Sprintf("%v", hash) pcfg.ConfigurationChecksum = fmt.Sprintf("%v", hash)
err := n.OnUpdate(*pcfg) err = n.OnUpdate(*pcfg)
if err != nil { if err != nil {
n.metricCollector.IncReloadErrorCount() n.metricCollector.IncReloadErrorCount()
n.metricCollector.ConfigSuccess(hash, false) n.metricCollector.ConfigSuccess(hash, false)
@ -263,7 +266,7 @@ func (n *NGINXController) syncIngress(interface{}) error {
func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) { func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) {
warnings := make([]string, 0) warnings := make([]string, 0)
var deprecatedAnnotations = sets.NewString() deprecatedAnnotations := sets.NewString()
deprecatedAnnotations.Insert( deprecatedAnnotations.Insert(
"enable-influxdb", "enable-influxdb",
"influxdb-measurement", "influxdb-measurement",
@ -335,7 +338,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
} }
if n.cfg.DisableCatchAll && ing.Spec.DefaultBackend != nil { if n.cfg.DisableCatchAll && ing.Spec.DefaultBackend != nil {
return fmt.Errorf("This deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false.") return fmt.Errorf("this deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false")
} }
startRender := time.Now().UnixNano() / 1000000 startRender := time.Now().UnixNano() / 1000000
cfg := n.store.GetBackendConfiguration() cfg := n.store.GetBackendConfiguration()
@ -355,10 +358,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
} }
for key, value := range ing.ObjectMeta.GetAnnotations() { for key, value := range ing.ObjectMeta.GetAnnotations() {
if parser.AnnotationsPrefix != parser.DefaultAnnotationsPrefix { if parser.AnnotationsPrefix != parser.DefaultAnnotationsPrefix {
if strings.HasPrefix(key, fmt.Sprintf("%s/", parser.DefaultAnnotationsPrefix)) { if strings.HasPrefix(key, fmt.Sprintf("%s/", parser.DefaultAnnotationsPrefix)) {
return fmt.Errorf("This deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix) return fmt.Errorf("this deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix)
} }
} }
@ -374,10 +376,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
return fmt.Errorf("%s annotation cannot be used. Snippet directives are disabled by the Ingress administrator", key) return fmt.Errorf("%s annotation cannot be used. Snippet directives are disabled by the Ingress administrator", key)
} }
if len(cfg.GlobalRateLimitMemcachedHost) == 0 && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) { if cfg.GlobalRateLimitMemcachedHost == "" && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap") return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap")
} }
} }
k8s.SetDefaultNGINXPathType(ing) k8s.SetDefaultNGINXPathType(ing)
@ -401,7 +402,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
startTest := time.Now().UnixNano() / 1000000 startTest := time.Now().UnixNano() / 1000000
_, servers, pcfg := n.getConfiguration(ings) _, servers, pcfg := n.getConfiguration(ings)
err = checkOverlap(ing, allIngresses, servers) err = checkOverlap(ing, servers)
if err != nil { if err != nil {
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name) n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
return err return err
@ -452,7 +453,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
return []ingress.L4Service{} return []ingress.L4Service{}
} }
var svcs []ingress.L4Service svcs := make([]ingress.L4Service, 0, len(configmap.Data))
var svcProxyProtocol ingress.ProxyProtocol var svcProxyProtocol ingress.ProxyProtocol
rp := []int{ rp := []int{
@ -489,10 +490,10 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
svcProxyProtocol.Encode = false svcProxyProtocol.Encode = false
// Proxy Protocol is only compatible with TCP Services // Proxy Protocol is only compatible with TCP Services
if len(nsSvcPort) >= 3 && proto == apiv1.ProtocolTCP { if len(nsSvcPort) >= 3 && proto == apiv1.ProtocolTCP {
if len(nsSvcPort) >= 3 && strings.ToUpper(nsSvcPort[2]) == "PROXY" { if len(nsSvcPort) >= 3 && strings.EqualFold(nsSvcPort[2], "PROXY") {
svcProxyProtocol.Decode = true svcProxyProtocol.Decode = true
} }
if len(nsSvcPort) == 4 && strings.ToUpper(nsSvcPort[3]) == "PROXY" { if len(nsSvcPort) == 4 && strings.EqualFold(nsSvcPort[3], "PROXY") {
svcProxyProtocol.Encode = true svcProxyProtocol.Encode = true
} }
} }
@ -532,6 +533,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
klog.V(3).Infof("Searching Endpoints with %v port number %d for Service %q", proto, targetPort, nsName) klog.V(3).Infof("Searching Endpoints with %v port number %d for Service %q", proto, targetPort, nsName)
for i := range svc.Spec.Ports { for i := range svc.Spec.Ports {
sp := svc.Spec.Ports[i] sp := svc.Spec.Ports[i]
//nolint:gosec // Ignore G109 error
if sp.Port == int32(targetPort) { if sp.Port == int32(targetPort) {
if sp.Protocol == proto { if sp.Protocol == proto {
endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices) endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices)
@ -574,7 +576,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
} }
svcKey := n.cfg.DefaultService svcKey := n.cfg.DefaultService
if len(svcKey) == 0 { if svcKey == "" {
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint()) upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
return upstream return upstream
} }
@ -690,13 +692,14 @@ func dropSnippetDirectives(anns *annotations.Ingress, ingKey string) {
klog.V(3).Infof("Ingress %q tried to use stream-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey) klog.V(3).Infof("Ingress %q tried to use stream-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
anns.StreamSnippet = "" anns.StreamSnippet = ""
} }
} }
} }
// getBackendServers returns a list of Upstream and Server to be used by the // getBackendServers returns a list of Upstream and Server to be used by the
// backend. An upstream can be used in multiple servers if the namespace, // backend. An upstream can be used in multiple servers if the namespace,
// service name and port are the same. // service name and port are the same.
//
//nolint:gocyclo // Ignore function complexity error
func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*ingress.Backend, []*ingress.Server) { func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*ingress.Backend, []*ingress.Server) {
du := n.getDefaultUpstream() du := n.getDefaultUpstream()
upstreams := n.createUpstreams(ingresses, du) upstreams := n.createUpstreams(ingresses, du)
@ -1030,7 +1033,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
// configure traffic shaping for canary // configure traffic shaping for canary
if anns.Canary.Enabled { if anns.Canary.Enabled {
upstreams[defBackend].NoServer = true upstreams[defBackend].NoServer = true
upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary) upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
} }
if len(upstreams[defBackend].Endpoints) == 0 { if len(upstreams[defBackend].Endpoints) == 0 {
@ -1095,7 +1098,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
// configure traffic shaping for canary // configure traffic shaping for canary
if anns.Canary.Enabled { if anns.Canary.Enabled {
upstreams[name].NoServer = true upstreams[name].NoServer = true
upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary) upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
} }
if len(upstreams[name].Endpoints) == 0 { if len(upstreams[name].Endpoints) == 0 {
@ -1206,7 +1209,6 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
if strconv.Itoa(int(servicePort.Port)) == backendPort || if strconv.Itoa(int(servicePort.Port)) == backendPort ||
servicePort.TargetPort.String() == backendPort || servicePort.TargetPort.String() == backendPort ||
servicePort.Name == backendPort { servicePort.Name == backendPort {
endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices) endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
if len(endps) == 0 { if len(endps) == 0 {
klog.Warningf("Service %q does not have any active Endpoint.", svcKey) klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
@ -1239,8 +1241,8 @@ func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert {
// one root location, which uses a default backend if left unspecified. // one root location, which uses a default backend if left unspecified.
func (n *NGINXController) createServers(data []*ingress.Ingress, func (n *NGINXController) createServers(data []*ingress.Ingress,
upstreams map[string]*ingress.Backend, upstreams map[string]*ingress.Backend,
du *ingress.Backend) map[string]*ingress.Server { du *ingress.Backend,
) map[string]*ingress.Server {
servers := make(map[string]*ingress.Server, len(data)) servers := make(map[string]*ingress.Server, len(data))
allAliases := make(map[string][]string, len(data)) allAliases := make(map[string][]string, len(data))
@ -1282,7 +1284,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
Rewrite: false, Rewrite: false,
}, },
}, },
}} },
}
// initialize all other servers // initialize all other servers
for _, ing := range data { for _, ing := range data {
@ -1532,7 +1535,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
} }
// OK to merge canary ingresses iff there exists one or more ingresses to potentially merge into // OK to merge canary ingresses iff there exists one or more ingresses to potentially merge into
func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ingress.Ingress) bool { func nonCanaryIngressExists(ingresses, canaryIngresses []*ingress.Ingress) bool {
return len(ingresses)-len(canaryIngresses) > 0 return len(ingresses)-len(canaryIngresses) > 0
} }
@ -1540,12 +1543,12 @@ func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ing
// 1) names of backends do not match and canary doesn't merge into itself // 1) names of backends do not match and canary doesn't merge into itself
// 2) primary name is not the default upstream // 2) primary name is not the default upstream
// 3) the primary has a server // 3) the primary has a server
func canMergeBackend(primary *ingress.Backend, alternative *ingress.Backend) bool { func canMergeBackend(primary, alternative *ingress.Backend) bool {
return alternative != nil && primary.Name != alternative.Name && primary.Name != defUpstreamName && !primary.NoServer return alternative != nil && primary.Name != alternative.Name && primary.Name != defUpstreamName && !primary.NoServer
} }
// Performs the merge action and checks to ensure that one two alternative backends do not merge into each other // Performs the merge action and checks to ensure that one two alternative backends do not merge into each other
func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altUps *ingress.Backend) bool { func mergeAlternativeBackend(ing *ingress.Ingress, priUps, altUps *ingress.Backend) bool {
if priUps.NoServer { if priUps.NoServer {
klog.Warningf("unable to merge alternative backend %v into primary backend %v because %v is a primary backend", klog.Warningf("unable to merge alternative backend %v into primary backend %v because %v is a primary backend",
altUps.Name, priUps.Name, priUps.Name) altUps.Name, priUps.Name, priUps.Name)
@ -1563,8 +1566,7 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
priUps.SessionAffinity.DeepCopyInto(&altUps.SessionAffinity) priUps.SessionAffinity.DeepCopyInto(&altUps.SessionAffinity)
} }
priUps.AlternativeBackends = priUps.AlternativeBackends = append(priUps.AlternativeBackends, altUps.Name)
append(priUps.AlternativeBackends, altUps.Name)
return true return true
} }
@ -1574,8 +1576,8 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
// to a backend's alternative list. // to a backend's alternative list.
// If no match is found, then the serverless backend is deleted. // If no match is found, then the serverless backend is deleted.
func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingress.Backend, func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingress.Backend,
servers map[string]*ingress.Server) { servers map[string]*ingress.Server,
) {
// merge catch-all alternative backends // merge catch-all alternative backends
if ing.Spec.DefaultBackend != nil { if ing.Spec.DefaultBackend != nil {
upsName := upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service) upsName := upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service)
@ -1585,7 +1587,6 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
if altUps == nil { if altUps == nil {
klog.Warningf("alternative backend %s has already been removed", upsName) klog.Warningf("alternative backend %s has already been removed", upsName)
} else { } else {
merged := false merged := false
altEqualsPri := false altEqualsPri := false
@ -1676,8 +1677,8 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
// extractTLSSecretName returns the name of the Secret containing a SSL // extractTLSSecretName returns the name of the Secret containing a SSL
// certificate for the given host name, or an empty string. // certificate for the given host name, or an empty string.
func extractTLSSecretName(host string, ing *ingress.Ingress, func extractTLSSecretName(host string, ing *ingress.Ingress,
getLocalSSLCert func(string) (*ingress.SSLCert, error)) string { getLocalSSLCert func(string) (*ingress.SSLCert, error),
) string {
if ing == nil { if ing == nil {
return "" return ""
} }
@ -1694,7 +1695,6 @@ func extractTLSSecretName(host string, ing *ingress.Ingress,
// no TLS host matching host name, try each TLS host for matching SAN or CN // no TLS host matching host name, try each TLS host for matching SAN or CN
for _, tls := range ing.Spec.TLS { for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" { if tls.SecretName == "" {
// There's no secretName specified, so it will never be available // There's no secretName specified, so it will never be available
continue continue
@ -1753,6 +1753,7 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
} }
for _, svcPort := range svc.Spec.Ports { for _, svcPort := range svc.Spec.Ports {
//nolint:gosec // Ignore G109 error
if svcPort.Port != int32(port) { if svcPort.Port != int32(port) {
continue continue
} }
@ -1772,12 +1773,13 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
// ExternalName without port // ExternalName without port
return &apiv1.ServicePort{ return &apiv1.ServicePort{
Protocol: "TCP", Protocol: "TCP",
//nolint:gosec // Ignore G109 error
Port: int32(port), Port: int32(port),
TargetPort: intstr.FromInt(port), TargetPort: intstr.FromInt(port),
} }
} }
func checkOverlap(ing *networking.Ingress, ingresses []*ingress.Ingress, servers []*ingress.Server) error { func checkOverlap(ing *networking.Ingress, servers []*ingress.Server) error {
for _, rule := range ing.Spec.Rules { for _, rule := range ing.Spec.Rules {
if rule.HTTP == nil { if rule.HTTP == nil {
continue continue
@ -1870,7 +1872,7 @@ func (n *NGINXController) getStreamSnippets(ingresses []*ingress.Ingress) []stri
} }
// newTrafficShapingPolicy creates new ingress.TrafficShapingPolicy instance using canary configuration // newTrafficShapingPolicy creates new ingress.TrafficShapingPolicy instance using canary configuration
func newTrafficShapingPolicy(cfg canary.Config) ingress.TrafficShapingPolicy { func newTrafficShapingPolicy(cfg *canary.Config) ingress.TrafficShapingPolicy {
return ingress.TrafficShapingPolicy{ return ingress.TrafficShapingPolicy{
Weight: cfg.Weight, Weight: cfg.Weight,
WeightTotal: cfg.WeightTotal, WeightTotal: cfg.WeightTotal,

View file

@ -60,51 +60,56 @@ import (
"k8s.io/ingress-nginx/pkg/util/file" "k8s.io/ingress-nginx/pkg/util/file"
) )
const (
exampleBackend = "example-http-svc-1-80"
TRUE = "true"
)
type fakeIngressStore struct { type fakeIngressStore struct {
ingresses []*ingress.Ingress ingresses []*ingress.Ingress
configuration ngx_config.Configuration configuration ngx_config.Configuration
} }
func (fakeIngressStore) GetIngressClass(ing *networking.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) { func (fakeIngressStore) GetIngressClass(_ *networking.Ingress, _ *ingressclass.Configuration) (string, error) {
return "nginx", nil return "nginx", nil
} }
func (fis fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration { func (fis *fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
return fis.configuration return fis.configuration
} }
func (fis fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration { func (fis *fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
return defaults.SecurityConfiguration{ return defaults.SecurityConfiguration{
AnnotationsRiskLevel: fis.configuration.AnnotationsRiskLevel, AnnotationsRiskLevel: fis.configuration.AnnotationsRiskLevel,
AllowCrossNamespaceResources: fis.configuration.AllowCrossNamespaceResources, AllowCrossNamespaceResources: fis.configuration.AllowCrossNamespaceResources,
} }
} }
func (fakeIngressStore) GetConfigMap(key string) (*corev1.ConfigMap, error) { func (fakeIngressStore) GetConfigMap(_ string) (*corev1.ConfigMap, error) {
return nil, fmt.Errorf("test error") return nil, fmt.Errorf("test error")
} }
func (fakeIngressStore) GetSecret(key string) (*corev1.Secret, error) { func (fakeIngressStore) GetSecret(_ string) (*corev1.Secret, error) {
return nil, fmt.Errorf("test error") return nil, fmt.Errorf("test error")
} }
func (fakeIngressStore) GetService(key string) (*corev1.Service, error) { func (fakeIngressStore) GetService(_ string) (*corev1.Service, error) {
return nil, fmt.Errorf("test error") return nil, fmt.Errorf("test error")
} }
func (fakeIngressStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) { func (fakeIngressStore) GetServiceEndpointsSlices(_ string) ([]*discoveryv1.EndpointSlice, error) {
return nil, fmt.Errorf("test error") return nil, fmt.Errorf("test error")
} }
func (fis fakeIngressStore) ListIngresses() []*ingress.Ingress { func (fis *fakeIngressStore) ListIngresses() []*ingress.Ingress {
return fis.ingresses return fis.ingresses
} }
func (fis fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, filterFunc store.IngressFilterFunc) []*ingress.Ingress { func (fis *fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, _ store.IngressFilterFunc) []*ingress.Ingress {
return ingresses return ingresses
} }
func (fakeIngressStore) GetLocalSSLCert(name string) (*ingress.SSLCert, error) { func (fakeIngressStore) GetLocalSSLCert(_ string) (*ingress.SSLCert, error) {
return nil, fmt.Errorf("test error") return nil, fmt.Errorf("test error")
} }
@ -120,7 +125,7 @@ func (fakeIngressStore) GetDefaultBackend() defaults.Backend {
return defaults.Backend{} return defaults.Backend{}
} }
func (fakeIngressStore) Run(stopCh chan struct{}) {} func (fakeIngressStore) Run(_ chan struct{}) {}
type testNginxTestCommand struct { type testNginxTestCommand struct {
t *testing.T t *testing.T
@ -129,7 +134,7 @@ type testNginxTestCommand struct {
err error err error
} }
func (ntc testNginxTestCommand) ExecCommand(args ...string) *exec.Cmd { func (ntc testNginxTestCommand) ExecCommand(_ ...string) *exec.Cmd {
return nil return nil
} }
@ -152,7 +157,7 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) {
type fakeTemplate struct{} type fakeTemplate struct{}
func (fakeTemplate) Write(conf ngx_config.TemplateConfig) ([]byte, error) { func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) {
r := []byte{} r := []byte{}
for _, s := range conf.Servers { for _, s := range conf.Servers {
if len(r) > 0 { if len(r) > 0 {
@ -196,7 +201,7 @@ func TestCheckIngress(t *testing.T) {
nginx.metricCollector = metric.DummyCollector{} nginx.metricCollector = metric.DummyCollector{}
nginx.t = fakeTemplate{} nginx.t = fakeTemplate{}
nginx.store = fakeIngressStore{ nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{}, ingresses: []*ingress.Ingress{},
} }
@ -226,7 +231,7 @@ func TestCheckIngress(t *testing.T) {
} }
t.Run("When the hostname is updated", func(t *testing.T) { t.Run("When the hostname is updated", func(t *testing.T) {
nginx.store = fakeIngressStore{ nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{ ingresses: []*ingress.Ingress{
{ {
Ingress: *ing, Ingress: *ing,
@ -273,7 +278,7 @@ func TestCheckIngress(t *testing.T) {
}) })
t.Run("When snippets are disabled and user tries to use snippet annotation", func(t *testing.T) { t.Run("When snippets are disabled and user tries to use snippet annotation", func(t *testing.T) {
nginx.store = fakeIngressStore{ nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{}, ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{ configuration: ngx_config.Configuration{
AllowSnippetAnnotations: false, AllowSnippetAnnotations: false,
@ -290,7 +295,7 @@ func TestCheckIngress(t *testing.T) {
}) })
t.Run("When invalid directives are used in annotation values", func(t *testing.T) { t.Run("When invalid directives are used in annotation values", func(t *testing.T) {
nginx.store = fakeIngressStore{ nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{}, ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{ configuration: ngx_config.Configuration{
AnnotationValueWordBlocklist: "invalid_directive, another_directive", AnnotationValueWordBlocklist: "invalid_directive, another_directive",
@ -366,12 +371,11 @@ func TestCheckIngress(t *testing.T) {
} }
func TestCheckWarning(t *testing.T) { func TestCheckWarning(t *testing.T) {
// Ensure no panic with wrong arguments // Ensure no panic with wrong arguments
var nginx = &NGINXController{} nginx := &NGINXController{}
nginx.t = fakeTemplate{} nginx.t = fakeTemplate{}
nginx.store = fakeIngressStore{ nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{}, ingresses: []*ingress.Ingress{},
} }
@ -390,7 +394,7 @@ func TestCheckWarning(t *testing.T) {
}, },
} }
t.Run("when a deprecated annotation is used a warning should be returned", func(t *testing.T) { t.Run("when a deprecated annotation is used a warning should be returned", func(t *testing.T) {
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true" ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
defer func() { defer func() {
ing.ObjectMeta.Annotations = map[string]string{} ing.ObjectMeta.Annotations = map[string]string{}
}() }()
@ -407,7 +411,6 @@ func TestCheckWarning(t *testing.T) {
}) })
t.Run("When an invalid pathType is used, a warning should be returned", func(t *testing.T) { t.Run("When an invalid pathType is used, a warning should be returned", func(t *testing.T) {
rules := ing.Spec.DeepCopy().Rules rules := ing.Spec.DeepCopy().Rules
ing.Spec.Rules = []networking.IngressRule{ ing.Spec.Rules = []networking.IngressRule{
{ {
@ -443,8 +446,8 @@ func TestCheckWarning(t *testing.T) {
} }
t.Run("adding invalid annotations increases the warning count", func(t *testing.T) { t.Run("adding invalid annotations increases the warning count", func(t *testing.T) {
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true" ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "true" ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = TRUE
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla" ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla"
defer func() { defer func() {
ing.ObjectMeta.Annotations = map[string]string{} ing.ObjectMeta.Annotations = map[string]string{}
@ -472,6 +475,7 @@ func TestCheckWarning(t *testing.T) {
}) })
} }
//nolint:dupl // Ignore dupl errors for similar test case
func TestMergeAlternativeBackends(t *testing.T) { func TestMergeAlternativeBackends(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
ingress *ingress.Ingress ingress *ingress.Ingress
@ -1537,8 +1541,8 @@ func TestExtractTLSSecretName(t *testing.T) {
} }
} }
//nolint:gocyclo // Ignore function complexity error
func TestGetBackendServers(t *testing.T) { func TestGetBackendServers(t *testing.T) {
testCases := []struct { testCases := []struct {
Ingresses []*ingress.Ingress Ingresses []*ingress.Ingress
Validate func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server) Validate func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server)
@ -2078,7 +2082,7 @@ func TestGetBackendServers(t *testing.T) {
t.Errorf("server hostname should be 'example.com', got '%s'", s.Hostname) t.Errorf("server hostname should be 'example.com', got '%s'", s.Hostname)
} }
if s.Locations[0].Backend != "example-http-svc-1-80" || s.Locations[1].Backend != "example-http-svc-1-80" || s.Locations[2].Backend != "example-http-svc-1-80" { if s.Locations[0].Backend != exampleBackend || s.Locations[1].Backend != exampleBackend || s.Locations[2].Backend != exampleBackend {
t.Errorf("all location backend should be 'example-http-svc-1-80'") t.Errorf("all location backend should be 'example-http-svc-1-80'")
} }
@ -2087,7 +2091,7 @@ func TestGetBackendServers(t *testing.T) {
return return
} }
if upstreams[0].Name != "example-http-svc-1-80" { if upstreams[0].Name != exampleBackend {
t.Errorf("example-http-svc-1-80 should be first upstream, got %s", upstreams[0].Name) t.Errorf("example-http-svc-1-80 should be first upstream, got %s", upstreams[0].Name)
return return
} }
@ -2101,6 +2105,7 @@ func TestGetBackendServers(t *testing.T) {
SetConfigMap: testConfigMap, SetConfigMap: testConfigMap,
}, },
{ {
//nolint:dupl // Ignore dupl errors for similar test case
Ingresses: []*ingress.Ingress{ Ingresses: []*ingress.Ingress{
{ {
Ingress: networking.Ingress{ Ingress: networking.Ingress{
@ -2208,6 +2213,7 @@ func TestGetBackendServers(t *testing.T) {
SetConfigMap: testConfigMap, SetConfigMap: testConfigMap,
}, },
{ {
//nolint:dupl // Ignore dupl errors for similar test case
Ingresses: []*ingress.Ingress{ Ingresses: []*ingress.Ingress{
{ {
Ingress: networking.Ingress{ Ingress: networking.Ingress{
@ -2319,7 +2325,7 @@ func TestGetBackendServers(t *testing.T) {
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
}, },
Data: map[string]string{ Data: map[string]string{
"proxy-ssl-location-only": "true", "proxy-ssl-location-only": TRUE,
}, },
} }
}, },
@ -2380,7 +2386,7 @@ func TestGetBackendServers(t *testing.T) {
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns), SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
}, },
Data: map[string]string{ Data: map[string]string{
"proxy-ssl-location-only": "true", "proxy-ssl-location-only": TRUE,
}, },
} }
}, },
@ -2449,7 +2455,6 @@ func TestGetBackendServers(t *testing.T) {
if len(s.Locations[0].Allowlist.CIDR) != 1 || s.Locations[0].Allowlist.CIDR[0] != "10.0.0.0/24" { if len(s.Locations[0].Allowlist.CIDR) != 1 || s.Locations[0].Allowlist.CIDR[0] != "10.0.0.0/24" {
t.Errorf("allow list was incorrectly dropped, len should be 1 and contain 10.0.0.0/24") t.Errorf("allow list was incorrectly dropped, len should be 1 and contain 10.0.0.0/24")
} }
}, },
SetConfigMap: func(ns string) *corev1.ConfigMap { SetConfigMap: func(ns string) *corev1.ConfigMap {
return &corev1.ConfigMap{ return &corev1.ConfigMap{
@ -2520,7 +2525,7 @@ func newNGINXController(t *testing.T) *NGINXController {
channels.NewRingChannel(10), channels.NewRingChannel(10),
false, false,
true, true,
&ingressclass.IngressClassConfiguration{ &ingressclass.Configuration{
Controller: "k8s.io/ingress-nginx", Controller: "k8s.io/ingress-nginx",
AnnotationValue: "nginx", AnnotationValue: "nginx",
}, },
@ -2586,7 +2591,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *corev1.C
channels.NewRingChannel(10), channels.NewRingChannel(10),
false, false,
true, true,
&ingressclass.IngressClassConfiguration{ &ingressclass.Configuration{
Controller: "k8s.io/ingress-nginx", Controller: "k8s.io/ingress-nginx",
AnnotationValue: "nginx", AnnotationValue: "nginx",
}, },

Some files were not shown because too many files have changed in this diff Show more