Fix golangci-lint errors
Signed-off-by: z1cheng <imchench@gmail.com>
This commit is contained in:
parent
c5f348ea2e
commit
d4ace6a8ab
238 changed files with 1902 additions and 1968 deletions
|
@ -18,11 +18,12 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||
|
|
|
@ -114,7 +114,6 @@ func main() {
|
|||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func backendsAll() {
|
||||
|
@ -185,7 +184,11 @@ func backendsGet(name string) {
|
|||
for _, backendi := range backends {
|
||||
backend := backendi.(map[string]interface{})
|
||||
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))
|
||||
return
|
||||
}
|
||||
|
@ -213,18 +216,7 @@ func certGet(host string) {
|
|||
}
|
||||
|
||||
func general() {
|
||||
//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
|
||||
}
|
||||
*/
|
||||
// TODO: refactor to obtain ingress-nginx pod count from the api server
|
||||
|
||||
var prettyBuffer bytes.Buffer
|
||||
indentErr := json.Indent(&prettyBuffer, []byte("{}"), "", " ")
|
||||
|
|
|
@ -47,5 +47,4 @@ func logger(address string) {
|
|||
|
||||
server.Wait()
|
||||
klog.Infof("Stopping logger")
|
||||
|
||||
}
|
||||
|
|
|
@ -153,7 +153,6 @@ func main() {
|
|||
if errExists == nil {
|
||||
conf.IsChroot = true
|
||||
go logger(conf.InternalLoggerAddress)
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
|
|
|
@ -47,7 +47,7 @@ func TestCreateApiserverClient(t *testing.T) {
|
|||
func init() {
|
||||
// the default value of nginx.TemplatePath assumes the template exists in
|
||||
// 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 {
|
||||
nginx.TemplatePath = path
|
||||
}
|
||||
|
@ -87,14 +87,14 @@ func TestHandleSigterm(t *testing.T) {
|
|||
|
||||
ingressflags.ResetForTesting(func() { t.Fatal("bad parse") })
|
||||
|
||||
os.Setenv("POD_NAME", podName)
|
||||
os.Setenv("POD_NAMESPACE", namespace)
|
||||
t.Setenv("POD_NAME", podName)
|
||||
t.Setenv("POD_NAMESPACE", namespace)
|
||||
|
||||
oldArgs := os.Args
|
||||
|
||||
defer func() {
|
||||
os.Setenv("POD_NAME", "")
|
||||
os.Setenv("POD_NAMESPACE", "")
|
||||
t.Setenv("POD_NAME", "")
|
||||
t.Setenv("POD_NAMESPACE", "")
|
||||
os.Args = oldArgs
|
||||
}()
|
||||
|
||||
|
|
|
@ -63,13 +63,14 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
|
|||
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
|
||||
if onlyList {
|
||||
switch {
|
||||
case onlyList:
|
||||
command = []string{"/dbg", "backends", "list"}
|
||||
} else if backend != "" {
|
||||
case backend != "":
|
||||
command = []string{"/dbg", "backends", "get", backend}
|
||||
} else {
|
||||
default:
|
||||
command = []string{"/dbg", "backends", "all"}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
|
|||
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}
|
||||
|
||||
pod, err := request.ChoosePod(flags, podName, deployment, selector)
|
||||
|
|
|
@ -55,7 +55,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -55,7 +55,7 @@ type execFlags struct {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -47,7 +47,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -74,9 +74,9 @@ func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces
|
|||
|
||||
if host != "" {
|
||||
rowsWithHost := make([]ingressRow, 0)
|
||||
for _, row := range rows {
|
||||
if row.Host == host {
|
||||
rowsWithHost = append(rowsWithHost, row)
|
||||
for i := range rows {
|
||||
if rows[i].Host == host {
|
||||
rowsWithHost = append(rowsWithHost, rows[i])
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
for i := range rows {
|
||||
row := &rows[i]
|
||||
var tlsMsg string
|
||||
if row.TLS {
|
||||
tlsMsg = "YES"
|
||||
|
@ -134,8 +135,8 @@ type ingressRow struct {
|
|||
func getIngressRows(ingresses *[]networking.Ingress) []ingressRow {
|
||||
rows := make([]ingressRow, 0)
|
||||
|
||||
for _, ing := range *ingresses {
|
||||
|
||||
for i := range *ingresses {
|
||||
ing := &(*ingresses)[i]
|
||||
address := ""
|
||||
for _, lbIng := range ing.Status.LoadBalancer.Ingress {
|
||||
if len(lbIng.IP) > 0 {
|
||||
|
@ -182,7 +183,7 @@ func getIngressRows(ingresses *[]networking.Ingress) []ingressRow {
|
|||
for _, rule := range ing.Spec.Rules {
|
||||
_, hasTLS := tlsHosts[rule.Host]
|
||||
|
||||
//Handle ingress with no paths
|
||||
// Handle ingress with no paths
|
||||
if rule.HTTP == nil {
|
||||
row := ingressRow{
|
||||
Namespace: ing.Namespace,
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
)
|
||||
|
||||
func TestGetIngressInformation(t *testing.T) {
|
||||
|
||||
testcases := map[string]struct {
|
||||
ServiceBackend *networking.IngressServiceBackend
|
||||
wantName string
|
||||
|
|
|
@ -111,11 +111,13 @@ type lintOptions struct {
|
|||
}
|
||||
|
||||
func (opts *lintOptions) Validate() error {
|
||||
//nolint:dogsled // Ignore 3 blank identifiers
|
||||
_, _, _, err := util.ParseVersionString(opts.versionFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:dogsled // Ignore 3 blank identifiers
|
||||
_, _, _, err = util.ParseVersionString(opts.versionTo)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -131,9 +133,9 @@ type lint interface {
|
|||
Version() string
|
||||
}
|
||||
|
||||
func checkObjectArray(lints []lint, objects []kmeta.Object, opts lintOptions) {
|
||||
func checkObjectArray(allLints []lint, objects []kmeta.Object, opts lintOptions) {
|
||||
usedLints := make([]lint, 0)
|
||||
for _, lint := range lints {
|
||||
for _, lint := range allLints {
|
||||
lintVersion := lint.Version()
|
||||
if lint.Version() == "" {
|
||||
lintVersion = "0.0.0"
|
||||
|
@ -189,7 +191,7 @@ func ingresses(opts lintOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var iLints []lints.IngressLint = lints.GetIngressLints()
|
||||
iLints := lints.GetIngressLints()
|
||||
genericLints := make([]lint, len(iLints))
|
||||
for i := range iLints {
|
||||
genericLints[i] = iLints[i]
|
||||
|
@ -216,7 +218,7 @@ func deployments(opts lintOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var iLints []lints.DeploymentLint = lints.GetDeploymentLints()
|
||||
iLints := lints.GetDeploymentLints()
|
||||
genericLints := make([]lint, len(iLints))
|
||||
for i := range iLints {
|
||||
genericLints[i] = iLints[i]
|
||||
|
|
|
@ -95,7 +95,7 @@ func (o *logsFlags) toStrings() []string {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -45,7 +45,7 @@ func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -38,11 +38,11 @@ func PodExecString(flags *genericclioptions.ConfigFlags, pod *apiv1.Pod, contain
|
|||
|
||||
// ExecToString runs a kubectl subcommand and returns stdout as a string
|
||||
func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, error) {
|
||||
kArgs := getKubectlConfigFlags(flags)
|
||||
kArgs = append(kArgs, args...)
|
||||
kubectlArgs := getKubectlConfigFlags(flags)
|
||||
kubectlArgs = append(kubectlArgs, args...)
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
err := execToWriter(append([]string{"kubectl"}, kArgs...), buf)
|
||||
err := execToWriter(append([]string{"kubectl"}, kubectlArgs...), buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string,
|
|||
|
||||
// Exec replaces the current process with a kubectl invocation
|
||||
func Exec(flags *genericclioptions.ConfigFlags, args []string) error {
|
||||
kArgs := getKubectlConfigFlags(flags)
|
||||
kArgs = append(kArgs, args...)
|
||||
return execCommand(append([]string{"kubectl"}, kArgs...))
|
||||
kubectlArgs := getKubectlConfigFlags(flags)
|
||||
kubectlArgs = append(kubectlArgs, args...)
|
||||
return execCommand(append([]string{"kubectl"}, kubectlArgs...))
|
||||
}
|
||||
|
||||
// Replaces the currently running process with the given command
|
||||
|
@ -70,6 +70,7 @@ func execCommand(args []string) error {
|
|||
|
||||
// Runs a command and returns stdout
|
||||
func execToWriter(args []string, writer io.Writer) error {
|
||||
//nolint:gosec // Ignore G204 error
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
|
||||
op, err := cmd.StdoutPipe()
|
||||
|
@ -78,7 +79,7 @@ func execToWriter(args []string, writer io.Writer) error {
|
|||
}
|
||||
|
||||
go func() {
|
||||
io.Copy(writer, op) //nolint:errcheck
|
||||
io.Copy(writer, op) //nolint:errcheck // Ignore the error
|
||||
}()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
|
@ -106,7 +107,6 @@ func getKubectlConfigFlags(flags *genericclioptions.ConfigFlags) []string {
|
|||
appendStringFlag(o, flags.Password, "password")
|
||||
appendStringFlag(o, flags.ClusterName, "cluster")
|
||||
appendStringFlag(o, flags.AuthInfoName, "user")
|
||||
//appendStringFlag(o, flags.Namespace, "namespace")
|
||||
appendStringFlag(o, flags.Context, "context")
|
||||
appendStringFlag(o, flags.APIServer, "server")
|
||||
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 {
|
||||
*out = append(*out, fmt.Sprintf("--%v=%v'", flag, strings.Join(*in, ",")))
|
||||
}
|
||||
|
|
|
@ -72,11 +72,11 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint {
|
|||
issue: issueNumber,
|
||||
version: version,
|
||||
f: func(dep v1.Deployment) bool {
|
||||
if !isIngressNginxDeployment(dep) {
|
||||
if !isIngressNginxDeployment(&dep) {
|
||||
return false
|
||||
}
|
||||
|
||||
args := getNginxArgs(dep)
|
||||
args := getNginxArgs(&dep)
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, fmt.Sprintf("--%v", flag)) {
|
||||
return true
|
||||
|
@ -88,8 +88,9 @@ func removedFlag(flag string, issueNumber int, version string) DeploymentLint {
|
|||
}
|
||||
}
|
||||
|
||||
func getNginxArgs(dep v1.Deployment) []string {
|
||||
for _, container := range dep.Spec.Template.Spec.Containers {
|
||||
func getNginxArgs(dep *v1.Deployment) []string {
|
||||
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" {
|
||||
return container.Args
|
||||
}
|
||||
|
@ -97,10 +98,10 @@ func getNginxArgs(dep v1.Deployment) []string {
|
|||
return make([]string, 0)
|
||||
}
|
||||
|
||||
func isIngressNginxDeployment(dep v1.Deployment) bool {
|
||||
func isIngressNginxDeployment(dep *v1.Deployment) bool {
|
||||
containers := dep.Spec.Template.Spec.Containers
|
||||
for _, container := range containers {
|
||||
if len(container.Args) > 0 && container.Args[0] == "/nginx-ingress-controller" {
|
||||
for i := range containers {
|
||||
if len(containers[i].Args) > 0 && containers[i].Args[0] == "/nginx-ingress-controller" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,13 @@ type IngressLint struct {
|
|||
message string
|
||||
issue int
|
||||
version string
|
||||
f func(ing networking.Ingress) bool
|
||||
f func(ing *networking.Ingress) bool
|
||||
}
|
||||
|
||||
// Check returns true if the lint detects an issue
|
||||
func (lint IngressLint) Check(obj kmeta.Object) bool {
|
||||
ing := obj.(*networking.Ingress)
|
||||
return lint.f(*ing)
|
||||
return lint.f(ing)
|
||||
}
|
||||
|
||||
// Message is a description of the lint
|
||||
|
@ -94,7 +94,7 @@ func GetIngressLints() []IngressLint {
|
|||
}
|
||||
}
|
||||
|
||||
func xForwardedPrefixIsBool(ing networking.Ingress) bool {
|
||||
func xForwardedPrefixIsBool(ing *networking.Ingress) bool {
|
||||
for name, val := range ing.Annotations {
|
||||
if strings.HasSuffix(name, "/x-forwarded-prefix") && (val == "true" || val == "false") {
|
||||
return true
|
||||
|
@ -103,7 +103,7 @@ func xForwardedPrefixIsBool(ing networking.Ingress) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func annotationPrefixIsNginxCom(ing networking.Ingress) bool {
|
||||
func annotationPrefixIsNginxCom(ing *networking.Ingress) bool {
|
||||
for name := range ing.Annotations {
|
||||
if strings.HasPrefix(name, "nginx.com/") {
|
||||
return true
|
||||
|
@ -112,7 +112,7 @@ func annotationPrefixIsNginxCom(ing networking.Ingress) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func annotationPrefixIsNginxOrg(ing networking.Ingress) bool {
|
||||
func annotationPrefixIsNginxOrg(ing *networking.Ingress) bool {
|
||||
for name := range ing.Annotations {
|
||||
if strings.HasPrefix(name, "nginx.org/") {
|
||||
return true
|
||||
|
@ -121,7 +121,7 @@ func annotationPrefixIsNginxOrg(ing networking.Ingress) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func rewriteTargetWithoutCaptureGroup(ing networking.Ingress) bool {
|
||||
func rewriteTargetWithoutCaptureGroup(ing *networking.Ingress) bool {
|
||||
for name, val := range ing.Annotations {
|
||||
if strings.HasSuffix(name, "/rewrite-target") && !strings.Contains(val, "$1") {
|
||||
return true
|
||||
|
@ -135,7 +135,7 @@ func removedAnnotation(annotationName string, issueNumber int, version string) I
|
|||
message: fmt.Sprintf("Contains the removed %v annotation.", annotationName),
|
||||
issue: issueNumber,
|
||||
version: version,
|
||||
f: func(ing networking.Ingress) bool {
|
||||
f: func(ing *networking.Ingress) bool {
|
||||
for annotation := range ing.Annotations {
|
||||
if strings.HasSuffix(annotation, "/"+annotationName) {
|
||||
return true
|
||||
|
@ -146,7 +146,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 {
|
||||
if strings.HasSuffix(name, "/configuration-snippet") {
|
||||
return strings.Contains(val, "satisfy")
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
"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 ...
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
)
|
||||
|
||||
// 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 != "" {
|
||||
return GetNamedPod(flags, podName)
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ func GetNamedPod(flags *genericclioptions.ConfigFlags, name string) (apiv1.Pod,
|
|||
return apiv1.Pod{}, err
|
||||
}
|
||||
|
||||
for _, pod := range allPods {
|
||||
if pod.Name == name {
|
||||
return pod, nil
|
||||
for i := range allPods {
|
||||
if allPods[i].Name == name {
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -143,25 +143,26 @@ func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, ser
|
|||
}
|
||||
|
||||
ret := 0
|
||||
for _, eps := range epss {
|
||||
for _, ep := range eps.Endpoints {
|
||||
ret += len(ep.Addresses)
|
||||
for i := range epss {
|
||||
eps := &epss[i]
|
||||
for j := range eps.Endpoints {
|
||||
ret += len(eps.Endpoints[j].Addresses)
|
||||
}
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var eps []discoveryv1.EndpointSlice
|
||||
for _, slice := range allEndpointsSlices {
|
||||
if svcName, ok := slice.ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok {
|
||||
for i := range allEndpointsSlices {
|
||||
if svcName, ok := allEndpointsSlices[i].ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok {
|
||||
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)
|
||||
}
|
||||
|
||||
cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(flags, namespace)
|
||||
cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(namespace)
|
||||
|
||||
if 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[""]
|
||||
if allEndpointSlices != nil {
|
||||
endpointSlices := make([]discoveryv1.EndpointSlice, 0)
|
||||
for _, slice := range *allEndpointSlices {
|
||||
if slice.Namespace == namespace {
|
||||
endpointSlices = append(endpointSlices, slice)
|
||||
for i := range *allEndpointSlices {
|
||||
if (*allEndpointSlices)[i].Namespace == namespace {
|
||||
endpointSlices = append(endpointSlices, (*allEndpointSlices)[i])
|
||||
}
|
||||
}
|
||||
endpointSlicesCache[namespace] = &endpointSlices
|
||||
|
@ -242,9 +243,9 @@ func GetServiceByName(flags *genericclioptions.ConfigFlags, name string, service
|
|||
services = &servicesArray
|
||||
}
|
||||
|
||||
for _, svc := range *services {
|
||||
if svc.Name == name {
|
||||
return svc, nil
|
||||
for i := range *services {
|
||||
if (*services)[i].Name == name {
|
||||
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{
|
||||
LabelSelector: label,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return make([]apiv1.Pod, 0), err
|
||||
}
|
||||
|
@ -303,9 +303,9 @@ func getDeploymentPods(flags *genericclioptions.ConfigFlags, deployment string)
|
|||
}
|
||||
|
||||
ingressPods := make([]apiv1.Pod, 0)
|
||||
for _, pod := range pods {
|
||||
if util.PodInDeployment(pod, deployment) {
|
||||
ingressPods = append(ingressPods, pod)
|
||||
for i := range pods {
|
||||
if util.PodInDeployment(&pods[i], deployment) {
|
||||
ingressPods = append(ingressPods, pods[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,5 +331,4 @@ func getServices(flags *genericclioptions.ConfigFlags) ([]apiv1.Service, error)
|
|||
}
|
||||
|
||||
return services.Items, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -47,16 +47,16 @@ func PrintError(e error) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
if len(parts) != 4 {
|
||||
return 0, 0, 0, fmt.Errorf("could not parse %v as a version string (like 0.20.3)", v)
|
||||
}
|
||||
|
||||
major, _ := strconv.Atoi(parts[1])
|
||||
minor, _ := strconv.Atoi(parts[2])
|
||||
patch, _ := strconv.Atoi(parts[3])
|
||||
major, _ = strconv.Atoi(parts[1])
|
||||
minor, _ = strconv.Atoi(parts[2])
|
||||
patch, _ = strconv.Atoi(parts[3])
|
||||
|
||||
return major, minor, patch, nil
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func isVersionLessThan(a, b string) bool {
|
|||
|
||||
// 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
|
||||
func PodInDeployment(pod apiv1.Pod, deployment string) bool {
|
||||
func PodInDeployment(pod *apiv1.Pod, deployment string) bool {
|
||||
for _, owner := range pod.OwnerReferences {
|
||||
if owner.Controller == nil || !*owner.Controller || owner.Kind != "ReplicaSet" {
|
||||
continue
|
||||
|
@ -138,7 +138,7 @@ func AddContainerFlag(cmd *cobra.Command) *string {
|
|||
// GetNamespace takes a set of kubectl flag values and returns the namespace we should be operating in
|
||||
func GetNamespace(flags *genericclioptions.ConfigFlags) string {
|
||||
namespace, _, err := flags.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil || len(namespace) == 0 {
|
||||
if err != nil || namespace == "" {
|
||||
namespace = apiv1.NamespaceDefault
|
||||
}
|
||||
return namespace
|
||||
|
|
|
@ -16,13 +16,13 @@ func hello(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
key := keys[0]
|
||||
fmt.Fprintf(w, "Hello "+string(key)+"!")
|
||||
fmt.Fprintf(w, "Hello "+key+"!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ type hwServer struct {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ type ecServer struct {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -42,19 +42,16 @@ type IngressAdmission struct {
|
|||
Checker Checker
|
||||
}
|
||||
|
||||
var (
|
||||
ingressResource = metav1.GroupVersionKind{
|
||||
Group: networking.GroupName,
|
||||
Version: "v1",
|
||||
Kind: "Ingress",
|
||||
}
|
||||
)
|
||||
var ingressResource = metav1.GroupVersionKind{
|
||||
Group: networking.GroupName,
|
||||
Version: "v1",
|
||||
Kind: "Ingress",
|
||||
}
|
||||
|
||||
// HandleAdmission populates the admission Response
|
||||
// with Allowed=false if the Object is an ingress that would prevent nginx to reload the configuration
|
||||
// with Allowed=true otherwise
|
||||
func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object, error) {
|
||||
|
||||
review, isV1 := obj.(*admissionv1.AdmissionReview)
|
||||
if !isV1 {
|
||||
return nil, fmt.Errorf("request is not of type AdmissionReview v1 or v1beta1")
|
||||
|
|
|
@ -33,12 +33,12 @@ type failTestChecker struct {
|
|||
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")
|
||||
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")
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -26,9 +26,7 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
)
|
||||
var scheme = runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
if err := admissionv1.AddToScheme(scheme); err != nil {
|
||||
|
|
|
@ -72,7 +72,7 @@ func (a alias) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
aliases := sets.NewString()
|
||||
for _, alias := range strings.Split(val, ",") {
|
||||
alias = strings.TrimSpace(alias)
|
||||
if len(alias) == 0 {
|
||||
if alias == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -65,9 +65,6 @@ func TestParse(t *testing.T) {
|
|||
if testCase.skipValidation {
|
||||
parser.EnableAnnotationValidation = false
|
||||
}
|
||||
defer func() {
|
||||
parser.EnableAnnotationValidation = true
|
||||
}()
|
||||
result, err := ap.Parse(ing)
|
||||
if (err != nil) != testCase.wantErr {
|
||||
t.Errorf("ParseAliasAnnotation() annotation: %s, error = %v, wantErr %v", testCase.annotations, err, testCase.wantErr)
|
||||
|
@ -76,4 +73,6 @@ func TestParse(t *testing.T) {
|
|||
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
|
||||
}
|
||||
}
|
||||
|
||||
parser.EnableAnnotationValidation = true
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ type Ingress struct {
|
|||
CorsConfig cors.Config
|
||||
CustomHTTPErrors []int
|
||||
DefaultBackend *apiv1.Service
|
||||
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
|
||||
// TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
|
||||
FastCGI fastcgi.Config
|
||||
Denied *string
|
||||
ExternalAuth authreq.Config
|
||||
|
|
|
@ -270,9 +270,9 @@ func TestCors(t *testing.T) {
|
|||
if r.CorsAllowCredentials != foo.credentials {
|
||||
t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomHTTPErrors(t *testing.T) {
|
||||
ec := NewAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
|
|
@ -50,7 +50,7 @@ var (
|
|||
)
|
||||
|
||||
var AuthSecretConfig = parser.AnnotationConfig{
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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. `,
|
||||
|
@ -61,20 +61,20 @@ var authSecretAnnotations = parser.Annotation{
|
|||
Annotations: parser.AnnotationFields{
|
||||
AuthSecretAnnotation: AuthSecretConfig,
|
||||
authSecretTypeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*authSecretTypeRegex, true),
|
||||
Validator: parser.ValidateRegex(authSecretTypeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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
|
||||
is a user and each value is the password.`,
|
||||
},
|
||||
authRealmAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.CharsWithSpace, false),
|
||||
Validator: parser.ValidateRegex(parser.CharsWithSpace, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.`,
|
||||
},
|
||||
authTypeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*authTypeRegex, true),
|
||||
Validator: parser.ValidateRegex(authTypeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, ing_errors.LocationDenied{
|
||||
return nil, ing_errors.LocationDeniedError{
|
||||
Reason: fmt.Errorf("error reading secret name from annotation: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
sns, sname, err := cache.SplitMetaNamespaceKey(s)
|
||||
if err != nil {
|
||||
return nil, ing_errors.LocationDenied{
|
||||
return nil, ing_errors.LocationDeniedError{
|
||||
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()
|
||||
// We don't accept different namespaces for secrets.
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
name := fmt.Sprintf("%v/%v", sns, sname)
|
||||
secret, err := a.r.GetSecret(name)
|
||||
if err != nil {
|
||||
return nil, ing_errors.LocationDenied{
|
||||
return nil, ing_errors.LocationDeniedError{
|
||||
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
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -238,14 +238,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
func dumpSecretAuthFile(filename string, secret *api.Secret) error {
|
||||
val, ok := secret.Data["auth"]
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(filename, val, file.ReadWriteByUser)
|
||||
if err != nil {
|
||||
return ing_errors.LocationDenied{
|
||||
return ing_errors.LocationDeniedError{
|
||||
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)
|
||||
if err != nil {
|
||||
return ing_errors.LocationDenied{
|
||||
return ing_errors.LocationDeniedError{
|
||||
Reason: fmt.Errorf("unexpected error creating password file: %w", err),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,15 @@ import (
|
|||
"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 {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -80,7 +89,7 @@ type mockSecret struct {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -92,7 +101,7 @@ func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
|
|||
return &api.Secret{
|
||||
ObjectMeta: meta_v1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: "demo-secret",
|
||||
Name: demoSecret,
|
||||
},
|
||||
Data: map[string][]byte{"auth": []byte("foo:$apr1$OFG3Xybp$ckL0FHDAkoXYIlH9.cysT0")},
|
||||
}, nil
|
||||
|
@ -129,9 +138,9 @@ func TestIngressInvalidRealm(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
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(AuthSecretAnnotation)] = "demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
|
@ -148,14 +157,14 @@ func TestIngressInvalidDifferentNamespace(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
expected := ing_errors.LocationDenied{
|
||||
expected := ing_errors.LocationDeniedError{
|
||||
Reason: errors.New("cross namespace usage of secrets is not allowed"),
|
||||
}
|
||||
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
|
||||
|
@ -168,8 +177,8 @@ func TestIngressInvalidDifferentNamespaceAllowed(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
|
@ -187,14 +196,14 @@ func TestIngressInvalidSecretName(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret;xpto"
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
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"),
|
||||
}
|
||||
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
|
||||
|
@ -207,13 +216,13 @@ func TestInvalidIngressAuthNoSecret(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
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"),
|
||||
}
|
||||
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
|
||||
|
@ -226,9 +235,9 @@ func TestIngressAuth(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
|
@ -242,10 +251,10 @@ func TestIngressAuth(t *testing.T) {
|
|||
if !ok {
|
||||
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)
|
||||
}
|
||||
if auth.Realm != "-realm-" {
|
||||
if auth.Realm != authRealm {
|
||||
t.Errorf("Expected -realm- as realm but returned %s", auth.Realm)
|
||||
}
|
||||
if !auth.Secured {
|
||||
|
@ -257,9 +266,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "invalid-secret"
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, dir, _ := dummySecretContent(t)
|
||||
|
@ -275,10 +284,10 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
|
||||
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
|
||||
data[parser.GetAnnotationWithPrefix(authSecretTypeAnnotation)] = "invalid-type"
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
|
||||
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, 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()))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -301,7 +310,7 @@ func dummySecretContent(t *testing.T) (string, string, *api.Secret) {
|
|||
t.Error(err)
|
||||
}
|
||||
defer tmpfile.Close()
|
||||
s, _ := mockSecret{}.GetSecret("default/demo-secret")
|
||||
s, _ = mockSecret{}.GetSecret(defaultDemoSecret)
|
||||
return tmpfile.Name(), dir, s
|
||||
}
|
||||
|
||||
|
|
|
@ -56,25 +56,25 @@ var authReqAnnotations = parser.Annotation{
|
|||
Group: "authentication",
|
||||
Annotations: parser.AnnotationFields{
|
||||
authReqURLAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskHigh,
|
||||
Documentation: `This annotation allows to indicate the URL where the HTTP request should be sent`,
|
||||
},
|
||||
authReqMethodAnnotation: {
|
||||
Validator: parser.ValidateRegex(*methodsRegex, true),
|
||||
Validator: parser.ValidateRegex(methodsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
Documentation: `This annotation allows to specify the HTTP method to use`,
|
||||
},
|
||||
authReqSigninAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskHigh,
|
||||
Documentation: `This annotation allows to specify the location of the error page`,
|
||||
},
|
||||
authReqSigninRedirParamAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
|
@ -86,7 +86,7 @@ var authReqAnnotations = parser.Annotation{
|
|||
Documentation: `This annotation allows to specify a custom snippet to use with external authentication`,
|
||||
},
|
||||
authReqCacheKeyAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
|
||||
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation enables caching for auth requests.`,
|
||||
|
@ -110,26 +110,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`,
|
||||
},
|
||||
authReqCacheDuration: {
|
||||
Validator: parser.ValidateRegex(*parser.ExtendedCharsRegex, false),
|
||||
Validator: parser.ValidateRegex(parser.ExtendedCharsRegex, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
},
|
||||
authReqResponseHeadersAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.HeadersVariable, true),
|
||||
Validator: parser.ValidateRegex(parser.HeadersVariable, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation sets the headers to pass to backend once authentication request completes. They should be separated by comma.`,
|
||||
},
|
||||
authReqProxySetHeadersAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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`,
|
||||
},
|
||||
authReqRequestRedirectAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation allows to specify the X-Auth-Request-Redirect header value`,
|
||||
|
@ -236,8 +236,8 @@ func (e1 *Config) Equal(e2 *Config) bool {
|
|||
var (
|
||||
methodsRegex = regexp.MustCompile("(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)")
|
||||
headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`)
|
||||
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
|
||||
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
|
||||
)
|
||||
|
||||
// ValidMethod checks is the provided string a valid HTTP method
|
||||
|
@ -260,7 +260,7 @@ func ValidCacheDuration(duration string) bool {
|
|||
seenDuration := false
|
||||
|
||||
for _, element := range elements {
|
||||
if len(element) == 0 {
|
||||
if element == "" {
|
||||
continue
|
||||
}
|
||||
if statusCodeRegex.MatchString(element) {
|
||||
|
@ -291,6 +291,8 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
|||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
// 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) {
|
||||
// Required Parameters
|
||||
urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations)
|
||||
|
@ -300,7 +302,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
|
||||
authURL, err := parser.StringToURL(urlString)
|
||||
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)
|
||||
|
@ -391,7 +393,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
if err != nil && ing_errors.IsValidationError(err) {
|
||||
return nil, ing_errors.NewLocationDenied("validation error")
|
||||
}
|
||||
if len(hstr) != 0 {
|
||||
if hstr != "" {
|
||||
harr := strings.Split(hstr, ",")
|
||||
for _, header := range harr {
|
||||
header = strings.TrimSpace(header)
|
||||
|
@ -411,7 +413,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
|
||||
cns, _, err := cache.SplitMetaNamespaceKey(proxySetHeaderMap)
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +425,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
secCfg := a.r.GetSecurityConfiguration()
|
||||
// We don't accept different namespaces for secrets.
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +481,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
// It will always return at least one duration (the default duration)
|
||||
func ParseStringToCacheDurations(input string) ([]string, error) {
|
||||
authCacheDuration := []string{}
|
||||
if len(input) != 0 {
|
||||
if input != "" {
|
||||
arr := strings.Split(input, ",")
|
||||
for _, duration := range arr {
|
||||
duration = strings.TrimSpace(duration)
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package authreq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -113,7 +112,7 @@ func TestAnnotations(t *testing.T) {
|
|||
data[parser.GetAnnotationWithPrefix("auth-url")] = test.url
|
||||
data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL
|
||||
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-snippet")] = test.authSnippet
|
||||
data[parser.GetAnnotationWithPrefix("auth-cache-key")] = test.authCacheKey
|
||||
|
@ -324,7 +323,6 @@ func TestKeepaliveAnnotations(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseStringToCacheDurations(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
title string
|
||||
duration string
|
||||
|
@ -339,7 +337,6 @@ func TestParseStringToCacheDurations(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
dur, err := ParseStringToCacheDurations(test.duration)
|
||||
if test.expErr {
|
||||
if err == nil {
|
||||
|
|
|
@ -55,7 +55,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
|||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
// rule used to enable or disable global external authentication
|
||||
func (a authReqGlobal) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
|
||||
enableGlobalAuth, err := parser.GetBoolAnnotation(enableGlobalAuthAnnotation, ing, a.annotationConfig.Annotations)
|
||||
if err != nil {
|
||||
enableGlobalAuth = true
|
||||
|
|
|
@ -18,11 +18,10 @@ package authtls
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
networking "k8s.io/api/networking/v1"
|
||||
|
||||
"regexp"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
|
||||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
|
@ -45,20 +44,20 @@ var (
|
|||
regexChars = regexp.QuoteMeta(`()|=`)
|
||||
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)
|
||||
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{
|
||||
Group: "authentication",
|
||||
Annotations: parser.AnnotationFields{
|
||||
annotationAuthTLSSecret: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
},
|
||||
annotationAuthTLSVerifyClient: {
|
||||
Validator: parser.ValidateRegex(*authVerifyClientRegex, true),
|
||||
Validator: parser.ValidateRegex(authVerifyClientRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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"`,
|
||||
|
@ -70,7 +69,7 @@ var authTLSAnnotations = parser.Annotation{
|
|||
Documentation: `This annotation defines validation depth between the provided client certificate and the Certification Authority chain.`,
|
||||
},
|
||||
annotationAuthTLSErrorPage: {
|
||||
Validator: parser.ValidateRegex(*redirectRegex, true),
|
||||
Validator: parser.ValidateRegex(redirectRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskHigh,
|
||||
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"`,
|
||||
},
|
||||
annotationAuthTLSMatchCN: {
|
||||
Validator: parser.ValidateRegex(*commonNameRegex, true),
|
||||
Validator: parser.ValidateRegex(commonNameRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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="`,
|
||||
|
@ -130,9 +129,9 @@ func (assl1 *Config) Equal(assl2 *Config) bool {
|
|||
}
|
||||
|
||||
// NewParser creates a new TLS authentication annotation parser
|
||||
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
|
||||
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||
return authTLS{
|
||||
r: resolver,
|
||||
r: r,
|
||||
annotationConfig: authTLSAnnotations,
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +168,7 @@ func (a authTLS) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
authCert, err := a.r.GetAuthCertificate(tlsauthsecret)
|
||||
if err != nil {
|
||||
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
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDemoSecret = "default/demo-secret"
|
||||
off = "off"
|
||||
)
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -77,23 +82,22 @@ type mockSecret struct {
|
|||
|
||||
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS
|
||||
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 &resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
Secret: defaultDemoSecret,
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
CASHA: "abc",
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
ing := buildIngress()
|
||||
data := map[string]string{}
|
||||
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "default/demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = defaultDemoSecret
|
||||
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
|
@ -108,7 +112,7 @@ func TestAnnotations(t *testing.T) {
|
|||
t.Errorf("expected *Config but got %v", u)
|
||||
}
|
||||
|
||||
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret")
|
||||
secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = "off"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = off
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true"
|
||||
|
@ -153,8 +157,8 @@ func TestAnnotations(t *testing.T) {
|
|||
if u.AuthSSLCert.Secret != secret.Secret {
|
||||
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
|
||||
}
|
||||
if u.VerifyClient != "off" {
|
||||
t.Errorf("expected %v but got %v", "off", u.VerifyClient)
|
||||
if u.VerifyClient != off {
|
||||
t.Errorf("expected %v but got %v", off, u.VerifyClient)
|
||||
}
|
||||
if u.ValidationDepth != 2 {
|
||||
t.Errorf("expected %v but got %v", 2, u.ValidationDepth)
|
||||
|
@ -262,28 +266,21 @@ func TestInvalidAnnotations(t *testing.T) {
|
|||
if u.MatchCN != "" {
|
||||
t.Errorf("expected empty string but got %v", u.MatchCN)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEquals(t *testing.T) {
|
||||
cfg1 := &Config{}
|
||||
cfg2 := &Config{}
|
||||
|
||||
// Same config
|
||||
result := cfg1.Equal(cfg1)
|
||||
if result != true {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
|
||||
// compare nil
|
||||
result = cfg1.Equal(nil)
|
||||
result := cfg1.Equal(nil)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
|
||||
// Different Certs
|
||||
sslCert1 := resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
Secret: defaultDemoSecret,
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
CASHA: "abc",
|
||||
}
|
||||
|
@ -302,7 +299,7 @@ func TestEquals(t *testing.T) {
|
|||
|
||||
// Different Verify Client
|
||||
cfg1.VerifyClient = "on"
|
||||
cfg2.VerifyClient = "off"
|
||||
cfg2.VerifyClient = off
|
||||
result = cfg1.Equal(cfg2)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
|
|
|
@ -25,9 +25,7 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
var (
|
||||
validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
|
||||
)
|
||||
var validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
|
||||
|
||||
const (
|
||||
http = "HTTP"
|
||||
|
|
|
@ -44,6 +44,7 @@ func buildIngress() *networking.Ingress {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInvalidAnnotations(t *testing.T) {
|
||||
ing := buildIngress()
|
||||
|
||||
|
@ -56,7 +57,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
|
|||
if !ok {
|
||||
t.Errorf("expected a string type")
|
||||
}
|
||||
if val != "HTTP" {
|
||||
if val != http {
|
||||
t.Errorf("expected HTTPS but %v returned", val)
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
|
|||
if !ok {
|
||||
t.Errorf("expected a string type")
|
||||
}
|
||||
if val != "HTTP" {
|
||||
if val != http {
|
||||
t.Errorf("expected HTTPS but %v returned", val)
|
||||
}
|
||||
|
||||
|
@ -88,7 +89,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
|
|||
if !ok {
|
||||
t.Errorf("expected a string type")
|
||||
}
|
||||
if val != "HTTP" {
|
||||
if val != http {
|
||||
t.Errorf("expected HTTPS but %v returned", val)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ var CanaryAnnotations = parser.Annotation{
|
|||
Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`,
|
||||
},
|
||||
canaryByHeaderAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.
|
||||
|
@ -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`,
|
||||
},
|
||||
canaryByHeaderValueAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.
|
||||
|
@ -74,7 +74,7 @@ var CanaryAnnotations = parser.Annotation{
|
|||
It doesn't have any effect if the 'canary-by-header' annotation is not defined`,
|
||||
},
|
||||
canaryByHeaderPatternAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.IsValidRegex, false),
|
||||
Validator: parser.ValidateRegex(parser.IsValidRegex, false),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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.`,
|
||||
},
|
||||
canaryByCookieAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.
|
||||
|
@ -189,7 +189,7 @@ func (c canary) GetDocumentation() parser.AnnotationFields {
|
|||
return c.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a canary) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (c canary) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package canary
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
|
@ -24,8 +25,6 @@ import (
|
|||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
|
||||
"strconv"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
|
@ -93,7 +92,6 @@ func TestCanaryInvalid(t *testing.T) {
|
|||
if val.Weight != 0 {
|
||||
t.Errorf("Expected %v but got %v", 0, val.Weight)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
|
@ -133,10 +131,9 @@ func TestAnnotations(t *testing.T) {
|
|||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("%v: expected nil but returned error %v", test.title, err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v: expected nil but returned error %v", test.title, err)
|
||||
}
|
||||
|
||||
canaryConfig, ok := i.(*Config)
|
||||
|
|
|
@ -31,7 +31,7 @@ var clientBodyBufferSizeConfig = parser.Annotation{
|
|||
Group: "backend",
|
||||
Annotations: parser.AnnotationFields{
|
||||
clientBodyBufferSizeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.SizeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options
|
||||
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)
|
||||
}
|
||||
|
||||
func (a clientBodyBufferSize) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (cbbs clientBodyBufferSize) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(cbbs.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, clientBodyBufferSizeConfig.Annotations)
|
||||
}
|
||||
|
|
|
@ -29,15 +29,13 @@ const (
|
|||
connectionProxyHeaderAnnotation = "connection-proxy-header"
|
||||
)
|
||||
|
||||
var (
|
||||
validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
|
||||
)
|
||||
var validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
|
||||
|
||||
var connectionHeadersAnnotations = parser.Annotation{
|
||||
Group: "backend",
|
||||
Annotations: parser.AnnotationFields{
|
||||
connectionProxyHeaderAnnotation: {
|
||||
Validator: parser.ValidateRegex(*validConnectionHeaderValue, true),
|
||||
Validator: parser.ValidateRegex(validConnectionHeaderValue, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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"`,
|
||||
|
|
|
@ -66,6 +66,5 @@ func TestParse(t *testing.T) {
|
|||
if !p.Equal(testCase.expected) {
|
||||
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, p, testCase.annotations)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,9 +43,9 @@ var (
|
|||
// * Sets a group that can be (https?://)?*?.something.com:port?
|
||||
// * Allows this to be repeated as much as possible, and separated by comma
|
||||
// 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 = 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)
|
||||
// May contain or not spaces between each verb
|
||||
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`,
|
||||
},
|
||||
corsAllowOriginAnnotation: {
|
||||
Validator: parser.ValidateRegex(*corsOriginRegexValidator, true),
|
||||
Validator: parser.ValidateRegex(corsOriginRegexValidator, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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`,
|
||||
},
|
||||
corsAllowHeadersAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.HeadersVariable, true),
|
||||
Validator: parser.ValidateRegex(parser.HeadersVariable, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation controls which headers are accepted.
|
||||
This is a multi-valued field, separated by ',' and accepts letters, numbers, _ and -`,
|
||||
},
|
||||
corsAllowMethodsAnnotation: {
|
||||
Validator: parser.ValidateRegex(*corsMethodsRegex, true),
|
||||
Validator: parser.ValidateRegex(corsMethodsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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.`,
|
||||
},
|
||||
corsExposeHeadersAnnotation: {
|
||||
Validator: parser.ValidateRegex(*corsExposeHeadersRegex, true),
|
||||
Validator: parser.ValidateRegex(corsExposeHeadersRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation controls which headers are exposed to response.
|
||||
|
@ -260,7 +260,7 @@ func (c cors) GetDocumentation() parser.AnnotationFields {
|
|||
return c.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a cors) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (c cors) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, corsAnnotation.Annotations)
|
||||
}
|
||||
|
|
|
@ -31,16 +31,14 @@ const (
|
|||
customHTTPErrorsAnnotation = "custom-http-errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// We accept anything between 400 and 599, on a comma separated.
|
||||
arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5][0-9][0-9],?)*$`)
|
||||
)
|
||||
// We accept anything between 400 and 599, on a comma separated.
|
||||
var arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5]\d{2},?)*$`)
|
||||
|
||||
var customHTTPErrorsAnnotations = parser.Annotation{
|
||||
Group: "backend",
|
||||
Annotations: parser.AnnotationFields{
|
||||
customHTTPErrorsAnnotation: {
|
||||
Validator: parser.ValidateRegex(*arrayOfHTTPErrors, true),
|
||||
Validator: parser.ValidateRegex(arrayOfHTTPErrors, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
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, ",")
|
||||
var codes []int
|
||||
codes := make([]int, 0, len(cSplit))
|
||||
for _, i := range cSplit {
|
||||
num, err := strconv.Atoi(i)
|
||||
if err != nil {
|
||||
|
@ -88,7 +86,7 @@ func (e customhttperrors) GetDocumentation() parser.AnnotationFields {
|
|||
return e.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a customhttperrors) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (e customhttperrors) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(e.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, customHTTPErrorsAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -57,14 +57,14 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
|||
|
||||
// Parse parses the annotations contained in the ingress to use
|
||||
// a custom default backend
|
||||
func (db backend) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, db.annotationConfig.Annotations)
|
||||
func (b backend) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, b.annotationConfig.Annotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("%v/%v", ing.Namespace, s)
|
||||
svc, err := db.r.GetService(name)
|
||||
svc, err := b.r.GetService(name)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (db backend) GetDocumentation() parser.AnnotationFields {
|
||||
return db.annotationConfig.Annotations
|
||||
func (b backend) GetDocumentation() parser.AnnotationFields {
|
||||
return b.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a backend) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (b backend) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(b.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, defaultBackendAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -35,22 +35,20 @@ const (
|
|||
fastCGIParamsAnnotation = "fastcgi-params-configmap"
|
||||
)
|
||||
|
||||
var (
|
||||
// fast-cgi valid parameters is just a single file name (like index.php)
|
||||
regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9\.\-\_]+$`)
|
||||
)
|
||||
// fast-cgi valid parameters is just a single file name (like index.php)
|
||||
var regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9.\-\_]+$`)
|
||||
|
||||
var fastCGIAnnotations = parser.Annotation{
|
||||
Group: "fastcgi",
|
||||
Annotations: parser.AnnotationFields{
|
||||
fastCGIIndexAnnotation: {
|
||||
Validator: parser.ValidateRegex(*regexValidIndexAnnotationAndKey, true),
|
||||
Validator: parser.ValidateRegex(regexValidIndexAnnotationAndKey, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation can be used to specify an index file`,
|
||||
},
|
||||
fastCGIParamsAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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
|
||||
// rule used to indicate the fastcgiConfig.
|
||||
func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
|
||||
fcgiConfig := Config{}
|
||||
|
||||
if ing.GetAnnotations() == nil {
|
||||
|
@ -125,7 +122,7 @@ func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
|
||||
cmns, cmn, err := cache.SplitMetaNamespaceKey(cm)
|
||||
if err != nil {
|
||||
return fcgiConfig, ing_errors.LocationDenied{
|
||||
return fcgiConfig, ing_errors.LocationDeniedError{
|
||||
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)
|
||||
cmap, err := a.r.GetConfigMap(cm)
|
||||
if err != nil {
|
||||
return fcgiConfig, ing_errors.LocationDenied{
|
||||
return fcgiConfig, ing_errors.LocationDeniedError{
|
||||
Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,6 @@ func TestParseFastCGIInvalidParamsConfigMapAnnotation(t *testing.T) {
|
|||
|
||||
invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"}
|
||||
for _, configmap := range invalidConfigMapList {
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap
|
||||
ing.SetAnnotations(data)
|
||||
|
@ -239,11 +238,9 @@ func TestParseFastCGIParamsConfigMapAnnotationWithDifferentNS(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Errorf("Different namespace configmap should return an error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigEquality(t *testing.T) {
|
||||
|
||||
var nilConfig *Config
|
||||
|
||||
config := Config{
|
||||
|
@ -297,7 +294,6 @@ func TestConfigEquality(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_fastcgi_Parse(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
index string
|
||||
|
@ -378,7 +374,6 @@ func Test_fastcgi_Parse(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
"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/net"
|
||||
|
@ -57,7 +56,7 @@ var globalRateLimitAnnotationConfig = parser.Annotation{
|
|||
Documentation: `Configures a time window (i.e 1m) that the limit is applied`,
|
||||
},
|
||||
globalRateLimitKeyAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
|
||||
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskHigh,
|
||||
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{}
|
||||
|
||||
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
|
||||
}
|
||||
rawWindowSize, err := parser.GetStringAnnotation(globalRateLimitWindowAnnotation, ing, a.annotationConfig.Annotations)
|
||||
if err != nil && errors.IsValidationError(err) {
|
||||
return config, ing_errors.LocationDenied{
|
||||
if err != nil && ing_errors.IsValidationError(err) {
|
||||
return config, ing_errors.LocationDeniedError{
|
||||
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
|
||||
}
|
||||
|
||||
windowSize, err := time.ParseDuration(rawWindowSize)
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -148,12 +147,12 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
if err != nil {
|
||||
klog.Warningf("invalid %s, defaulting to %s", globalRateLimitKeyAnnotation, defaultKey)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
if key == "" {
|
||||
key = defaultKey
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
ignoredCIDRs, err := net.ParseCIDRs(rawIgnoredCIDRs)
|
||||
|
@ -161,7 +160,7 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
config.Namespace = strings.Replace(string(ing.UID), "-", "", -1)
|
||||
config.Namespace = strings.ReplaceAll(string(ing.UID), "-", "")
|
||||
config.Limit = limit
|
||||
config.WindowSize = int(windowSize.Seconds())
|
||||
config.Key = key
|
||||
|
|
|
@ -30,8 +30,10 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
const UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e"
|
||||
const expectedUID = "31285d47b1504dcfbd6f12c46d769f6e"
|
||||
const (
|
||||
UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e"
|
||||
expectedUID = "31285d47b1504dcfbd6f12c46d769f6e"
|
||||
)
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
|
@ -192,8 +194,14 @@ func TestGlobalRateLimiting(t *testing.T) {
|
|||
|
||||
actualConfig := i.(*Config)
|
||||
if !testCase.expectedConfig.Equal(actualConfig) {
|
||||
expectedJSON, _ := json.Marshal(testCase.expectedConfig)
|
||||
actualJSON, _ := json.Marshal(actualConfig)
|
||||
expectedJSON, err := json.Marshal(testCase.expectedConfig)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func (h2pp http2PushPreload) GetDocumentation() parser.AnnotationFields {
|
|||
return h2pp.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a http2PushPreload) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (h2pp http2PushPreload) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(h2pp.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, http2PushPreloadAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -96,16 +96,15 @@ func (a ipallowlist) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
return &SourceRange{CIDR: defaultAllowlistSourceRange}, nil
|
||||
}
|
||||
|
||||
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDenied{
|
||||
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{
|
||||
Reason: err,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
values := strings.Split(val, ",")
|
||||
ipnets, ips, err := net.ParseIPNets(values...)
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,16 +93,15 @@ func (a ipdenylist) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
return &SourceRange{CIDR: defaultDenylistSourceRange}, nil
|
||||
}
|
||||
|
||||
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{
|
||||
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{
|
||||
Reason: err,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
values := strings.Split(val, ",")
|
||||
ipnets, ips, err := net.ParseIPNets(values...)
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ func (l log) GetDocumentation() parser.AnnotationFields {
|
|||
return l.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a log) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (l log) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(l.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, logAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -34,15 +34,13 @@ const (
|
|||
mirrorHostAnnotation = "mirror-host"
|
||||
)
|
||||
|
||||
var (
|
||||
OnOffRegex = regexp.MustCompile(`^(on|off)$`)
|
||||
)
|
||||
var OnOffRegex = regexp.MustCompile(`^(on|off)$`)
|
||||
|
||||
var mirrorAnnotation = parser.Annotation{
|
||||
Group: "mirror",
|
||||
Annotations: parser.AnnotationFields{
|
||||
mirrorRequestBodyAnnotation: {
|
||||
Validator: parser.ValidateRegex(*OnOffRegex, true),
|
||||
Validator: parser.ValidateRegex(OnOffRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
Documentation: `This annotation defines if the request-body should be sent to the mirror backend. Can be 'on' or 'off'`,
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
const (
|
||||
modsecEnableAnnotation = "enable-modsecurity"
|
||||
modsecEnableOwaspCoreAnnotation = "enable-owasp-core-rules"
|
||||
modesecTransactionIdAnnotation = "modsecurity-transaction-id"
|
||||
modesecTransactionIDAnnotation = "modsecurity-transaction-id"
|
||||
modsecSnippetAnnotation = "modsecurity-snippet"
|
||||
)
|
||||
|
||||
|
@ -46,8 +46,8 @@ var modsecurityAnnotation = parser.Annotation{
|
|||
Risk: parser.AnnotationRiskLow,
|
||||
Documentation: `This annotation enables the OWASP Core Rule Set`,
|
||||
},
|
||||
modesecTransactionIdAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
|
||||
modesecTransactionIDAnnotation: {
|
||||
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskHigh,
|
||||
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
|
||||
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
|
||||
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||
return modSecurity{
|
||||
r: resolver,
|
||||
r: r,
|
||||
annotationConfig: modsecurityAnnotation,
|
||||
}
|
||||
}
|
||||
|
@ -134,10 +134,10 @@ func (a modSecurity) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
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 errors.IsInvalidContent(err) {
|
||||
klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIdAnnotation)
|
||||
klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIDAnnotation)
|
||||
}
|
||||
config.TransactionID = ""
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ var otelAnnotations = parser.Annotation{
|
|||
Documentation: `This annotation enables or disables using spans from incoming requests as parent for created ones`,
|
||||
},
|
||||
otelOperationNameAnnotation: {
|
||||
Validator: parser.ValidateRegex(*regexOperationName, true),
|
||||
Validator: parser.ValidateRegex(regexOperationName, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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
|
||||
func (bd1 *Config) Equal(bd2 *Config) bool {
|
||||
|
||||
if bd1.Set != bd2.Set {
|
||||
return false
|
||||
}
|
||||
|
@ -150,7 +149,7 @@ func (c opentelemetry) GetDocumentation() parser.AnnotationFields {
|
|||
return c.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a opentelemetry) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (c opentelemetry) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, otelAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
const enableAnnotation = "true"
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -73,7 +75,7 @@ func TestIngressAnnotationOpentelemetrySetTrue(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
|
||||
|
@ -123,8 +125,8 @@ func TestIngressAnnotationOpentelemetryTrustSetTrue(t *testing.T) {
|
|||
|
||||
data := map[string]string{}
|
||||
opName := "foo-op"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
|
||||
data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = enableAnnotation
|
||||
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
|
@ -163,7 +165,7 @@ func TestIngressAnnotationOpentelemetryWithBadOpName(t *testing.T) {
|
|||
|
||||
data := map[string]string{}
|
||||
opName := "fooxpto_123$la;"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
|
||||
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
|
|
|
@ -89,13 +89,13 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
|||
}
|
||||
}
|
||||
|
||||
func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, s.annotationConfig.Annotations)
|
||||
func (o opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, o.annotationConfig.Annotations)
|
||||
if err != 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 {
|
||||
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
|
||||
}
|
||||
|
||||
func (s opentracing) GetDocumentation() parser.AnnotationFields {
|
||||
return s.annotationConfig.Annotations
|
||||
func (o opentracing) GetDocumentation() parser.AnnotationFields {
|
||||
return o.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a opentracing) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (o opentracing) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(o.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, opentracingAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
const enableAnnotation = "true"
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -73,7 +75,7 @@ func TestIngressAnnotationOpentracingSetTrue(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
|
||||
|
@ -110,8 +112,8 @@ func TestIngressAnnotationOpentracingTrustSetTrue(t *testing.T) {
|
|||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
|
||||
data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = enableAnnotation
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
|
||||
|
|
|
@ -118,7 +118,7 @@ func (a ingAnnotations) parseString(name string) (string, error) {
|
|||
val, ok := a[name]
|
||||
if ok {
|
||||
s := normalizeString(val)
|
||||
if len(s) == 0 {
|
||||
if s == "" {
|
||||
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)
|
||||
}
|
||||
|
||||
if parsedURL.Scheme == "" {
|
||||
switch {
|
||||
case parsedURL.Scheme == "":
|
||||
return nil, fmt.Errorf("url scheme is empty")
|
||||
} else if parsedURL.Host == "" {
|
||||
case parsedURL.Host == "":
|
||||
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")
|
||||
default:
|
||||
return parsedURL, nil
|
||||
}
|
||||
|
||||
return parsedURL, nil
|
||||
}
|
||||
|
|
|
@ -93,14 +93,16 @@ func TestGetStringAnnotation(t *testing.T) {
|
|||
{"valid - A", "string", "A ", "A", false},
|
||||
{"valid - B", "string", " B", "B", false},
|
||||
{"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/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/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break;
|
||||
`,
|
||||
false},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
data := map[string]string{}
|
||||
|
|
|
@ -52,7 +52,7 @@ var (
|
|||
var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$")
|
||||
|
||||
// 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:
|
||||
// It is alphanumericChar + ":", "?", "&"
|
||||
|
@ -103,7 +103,7 @@ func ValidateServerName(value string) error {
|
|||
// ValidateRegex receives a regex as an argument and uses it to validate
|
||||
// the value of the field.
|
||||
// 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 {
|
||||
if removeSpace {
|
||||
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.
|
||||
// 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 {
|
||||
if trimSpace {
|
||||
s = strings.TrimSpace(s)
|
||||
|
@ -161,7 +161,7 @@ func ValidateDuration(value string) error {
|
|||
// 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
|
||||
// usage, knowing it can be critical!
|
||||
func ValidateNull(value string) error {
|
||||
func ValidateNull(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -223,7 +223,6 @@ func Test_checkAnnotation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCheckAnnotationRisk(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
annotations map[string]string
|
||||
|
|
|
@ -45,9 +45,7 @@ const (
|
|||
proxyMaxTempFileSizeAnnotation = "proxy-max-temp-file-size"
|
||||
)
|
||||
|
||||
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?)+$`)
|
||||
)
|
||||
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?)+$`)
|
||||
|
||||
var proxyAnnotations = parser.Annotation{
|
||||
Group: "backend",
|
||||
|
@ -78,32 +76,32 @@ var proxyAnnotations = parser.Annotation{
|
|||
By default proxy buffers number is set as 4`,
|
||||
},
|
||||
proxyBufferSizeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.SizeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.
|
||||
By default proxy buffer size is set as "4k".`,
|
||||
},
|
||||
proxyCookiePathAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.`,
|
||||
},
|
||||
proxyCookieDomainAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.`,
|
||||
},
|
||||
proxyBodySizeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.SizeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation allows setting the maximum allowed size of a client request body.`,
|
||||
},
|
||||
proxyNextUpstreamAnnotation: {
|
||||
Validator: parser.ValidateRegex(*validUpstreamAnnotation, false),
|
||||
Validator: parser.ValidateRegex(validUpstreamAnnotation, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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.`,
|
||||
},
|
||||
proxyRedirectFromAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
},
|
||||
proxyRedirectToAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
|
@ -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".`,
|
||||
},
|
||||
proxyMaxTempFileSizeAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.SizeRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
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
|
||||
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||
return proxy{r: r,
|
||||
return proxy{
|
||||
r: r,
|
||||
annotationConfig: proxyAnnotations,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,12 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
off = "off"
|
||||
proxyHTTPVersion = "1.0"
|
||||
proxyMaxTempFileSize = "128k"
|
||||
)
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -87,7 +93,7 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend {
|
|||
ProxyNextUpstreamTimeout: 0,
|
||||
ProxyNextUpstreamTries: 3,
|
||||
ProxyRequestBuffering: "on",
|
||||
ProxyBuffering: "off",
|
||||
ProxyBuffering: off,
|
||||
ProxyHTTPVersion: "1.1",
|
||||
ProxyMaxTempFileSize: "1024m",
|
||||
}
|
||||
|
@ -103,13 +109,13 @@ func TestProxy(t *testing.T) {
|
|||
data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
|
||||
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-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-http-version")] = "1.0"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
|
||||
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser(mockBackend{}).Parse(ing)
|
||||
|
@ -138,7 +144,7 @@ func TestProxy(t *testing.T) {
|
|||
if p.BodySize != "2k" {
|
||||
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)
|
||||
}
|
||||
if p.NextUpstreamTimeout != 5 {
|
||||
|
@ -147,16 +153,16 @@ func TestProxy(t *testing.T) {
|
|||
if p.NextUpstreamTries != 3 {
|
||||
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)
|
||||
}
|
||||
if p.ProxyBuffering != "on" {
|
||||
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)
|
||||
}
|
||||
if p.ProxyMaxTempFileSize != "128k" {
|
||||
if p.ProxyMaxTempFileSize != 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-request-buffering")] = "off"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
|
||||
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser(mockBackend{}).Parse(ing)
|
||||
|
@ -221,10 +227,10 @@ func TestProxyComplex(t *testing.T) {
|
|||
if p.ProxyBuffering != "on" {
|
||||
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)
|
||||
}
|
||||
if p.ProxyMaxTempFileSize != "128k" {
|
||||
if p.ProxyMaxTempFileSize != proxyMaxTempFileSize {
|
||||
t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
|
||||
networking "k8s.io/api/networking/v1"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
"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/k8s"
|
||||
|
@ -42,7 +41,7 @@ const (
|
|||
var (
|
||||
proxySSLOnOffRegex = regexp.MustCompile(`^(on|off)$`)
|
||||
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 (
|
||||
|
@ -59,7 +58,7 @@ var proxySSLAnnotation = parser.Annotation{
|
|||
Group: "proxy",
|
||||
Annotations: parser.AnnotationFields{
|
||||
proxySSLSecretAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.
|
||||
|
@ -68,14 +67,14 @@ var proxySSLAnnotation = parser.Annotation{
|
|||
Just secrets on the same namespace of the ingress can be used.`,
|
||||
},
|
||||
proxySSLCiphersAnnotation: {
|
||||
Validator: parser.ValidateRegex(*proxySSLCiphersRegex, true),
|
||||
Validator: parser.ValidateRegex(proxySSLCiphersRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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.`,
|
||||
},
|
||||
proxySSLProtocolsAnnotation: {
|
||||
Validator: parser.ValidateRegex(*proxySSLProtocolRegex, true),
|
||||
Validator: parser.ValidateRegex(proxySSLProtocolRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
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.`,
|
||||
},
|
||||
proxySSLVerifyAnnotation: {
|
||||
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true),
|
||||
Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskLow,
|
||||
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).`,
|
||||
},
|
||||
proxySSLServerNameAnnotation: {
|
||||
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true),
|
||||
Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.`,
|
||||
|
@ -150,10 +149,11 @@ func (pssl1 *Config) Equal(pssl2 *Config) bool {
|
|||
}
|
||||
|
||||
// NewParser creates a new TLS authentication annotation parser
|
||||
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
|
||||
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||
return proxySSL{
|
||||
r: resolver,
|
||||
annotationConfig: proxySSLAnnotation}
|
||||
r: r,
|
||||
annotationConfig: proxySSLAnnotation,
|
||||
}
|
||||
}
|
||||
|
||||
type proxySSL struct {
|
||||
|
@ -208,13 +208,13 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
proxyCert, err := p.r.GetAuthCertificate(proxysslsecret)
|
||||
if err != nil {
|
||||
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.Ciphers, err = parser.GetStringAnnotation(proxySSLCiphersAnnotation, ing, p.annotationConfig.Annotations)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
if errors.IsValidationError(err) {
|
||||
if ing_errors.IsValidationError(err) {
|
||||
klog.Warningf("invalid value passed to proxy-ssl-name, defaulting to empty")
|
||||
}
|
||||
config.ProxySSLName = ""
|
||||
|
@ -260,7 +260,7 @@ func (p proxySSL) GetDocumentation() parser.AnnotationFields {
|
|||
return p.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a proxySSL) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (p proxySSL) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(p.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, proxySSLAnnotation.Annotations)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,14 @@ import (
|
|||
"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 {
|
||||
defaultBackend := networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
|
@ -77,28 +85,27 @@ type mockSecret struct {
|
|||
|
||||
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for backend certificate authentication
|
||||
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 &resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
Secret: defaultDemoSecret,
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
CASHA: "abc",
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
ing := buildIngress()
|
||||
data := map[string]string{}
|
||||
|
||||
data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = "default/demo-secret"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = "HIGH:-SHA"
|
||||
data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = defaultDemoSecret
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = proxySslCiphers
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host"
|
||||
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-session-reuse")] = "off"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = off
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on"
|
||||
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)
|
||||
}
|
||||
|
||||
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret")
|
||||
secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting secret %v", err)
|
||||
}
|
||||
|
@ -123,11 +130,11 @@ func TestAnnotations(t *testing.T) {
|
|||
if u.AuthSSLCert.Secret != secret.Secret {
|
||||
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
|
||||
}
|
||||
if u.Ciphers != "HIGH:-SHA" {
|
||||
t.Errorf("expected %v but got %v", "HIGH:-SHA", u.Ciphers)
|
||||
if u.Ciphers != proxySslCiphers {
|
||||
t.Errorf("expected %v but got %v", proxySslCiphers, u.Ciphers)
|
||||
}
|
||||
if u.Protocols != "SSLv2 TLSv1 TLSv1.2 TLSv1.3" {
|
||||
t.Errorf("expected %v but got %v", "SSLv2 TLSv1 TLSv1.2 TLSv1.3", u.Protocols)
|
||||
if u.Protocols != defaultProtocol {
|
||||
t.Errorf("expected %v but got %v", defaultProtocol, u.Protocols)
|
||||
}
|
||||
if u.Verify != "on" {
|
||||
t.Errorf("expected %v but got %v", "on", u.Verify)
|
||||
|
@ -141,7 +148,6 @@ func TestAnnotations(t *testing.T) {
|
|||
if u.ProxySSLServerName != "on" {
|
||||
t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInvalidAnnotations(t *testing.T) {
|
||||
|
@ -172,11 +178,11 @@ func TestInvalidAnnotations(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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-server-name")] = "w00t"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "w00t"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "w00t"
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = sslServerName
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = sslServerName
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = sslServerName
|
||||
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "abcd"
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
|
@ -207,21 +213,15 @@ func TestEquals(t *testing.T) {
|
|||
cfg1 := &Config{}
|
||||
cfg2 := &Config{}
|
||||
|
||||
// Same config
|
||||
result := cfg1.Equal(cfg1)
|
||||
if result != true {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
|
||||
// compare nil
|
||||
result = cfg1.Equal(nil)
|
||||
result := cfg1.Equal(nil)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
|
||||
// Different Certs
|
||||
sslCert1 := resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
Secret: defaultDemoSecret,
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
CASHA: "abc",
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func TestEquals(t *testing.T) {
|
|||
|
||||
// Different Ciphers
|
||||
cfg1.Ciphers = "DEFAULT"
|
||||
cfg2.Ciphers = "HIGH:-SHA"
|
||||
cfg2.Ciphers = proxySslCiphers
|
||||
result = cfg1.Equal(cfg2)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
|
@ -248,22 +248,22 @@ func TestEquals(t *testing.T) {
|
|||
cfg2.Ciphers = "DEFAULT"
|
||||
|
||||
// Different Protocols
|
||||
cfg1.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
|
||||
cfg1.Protocols = defaultProtocol
|
||||
cfg2.Protocols = "SSLv3 TLSv1 TLSv1.2 TLSv1.3"
|
||||
result = cfg1.Equal(cfg2)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
cfg2.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
|
||||
cfg2.Protocols = defaultProtocol
|
||||
|
||||
// Different Verify
|
||||
cfg1.Verify = "off"
|
||||
cfg1.Verify = off
|
||||
cfg2.Verify = "on"
|
||||
result = cfg1.Equal(cfg2)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
cfg2.Verify = "off"
|
||||
cfg2.Verify = off
|
||||
|
||||
// Different VerifyDepth
|
||||
cfg1.VerifyDepth = 1
|
||||
|
@ -275,13 +275,13 @@ func TestEquals(t *testing.T) {
|
|||
cfg2.VerifyDepth = 1
|
||||
|
||||
// Different ProxySSLServerName
|
||||
cfg1.ProxySSLServerName = "off"
|
||||
cfg1.ProxySSLServerName = off
|
||||
cfg2.ProxySSLServerName = "on"
|
||||
result = cfg1.Equal(cfg2)
|
||||
if result != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
cfg2.ProxySSLServerName = "off"
|
||||
cfg2.ProxySSLServerName = off
|
||||
|
||||
// Equal Configs
|
||||
result = cfg1.Equal(cfg2)
|
||||
|
|
|
@ -288,7 +288,7 @@ func (a ratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
|
||||
func encode(s string) string {
|
||||
str := base64.URLEncoding.EncodeToString([]byte(s))
|
||||
return strings.Replace(str, "=", "", -1)
|
||||
return strings.ReplaceAll(str, "=", "")
|
||||
}
|
||||
|
||||
func (a ratelimit) GetDocumentation() parser.AnnotationFields {
|
||||
|
|
|
@ -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.`,
|
||||
},
|
||||
temporalRedirectAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.
|
||||
For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`,
|
||||
},
|
||||
permanentRedirectAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.
|
||||
|
@ -174,11 +174,11 @@ func isValidURL(s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a redirect) GetDocumentation() parser.AnnotationFields {
|
||||
return a.annotationConfig.Annotations
|
||||
func (r redirect) GetDocumentation() parser.AnnotationFields {
|
||||
return r.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a redirect) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (r redirect) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(r.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, redirectAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -136,7 +136,6 @@ func TestTemporalRedirect(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIsValidURL(t *testing.T) {
|
||||
|
||||
invalid := "ok.com"
|
||||
urlParse, err := url.Parse(invalid)
|
||||
if err != nil {
|
||||
|
|
|
@ -40,7 +40,7 @@ var rewriteAnnotations = parser.Annotation{
|
|||
Group: "rewrite",
|
||||
Annotations: parser.AnnotationFields{
|
||||
rewriteTargetAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false),
|
||||
Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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
|
||||
|
@ -72,7 +72,7 @@ var rewriteAnnotations = parser.Annotation{
|
|||
the pathType should also be defined as 'ImplementationSpecific'.`,
|
||||
},
|
||||
appRootAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false),
|
||||
Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation defines the Application Root that the Controller must redirect if it's in / context`,
|
||||
|
|
|
@ -194,6 +194,7 @@ func TestForceSSLRedirect(t *testing.T) {
|
|||
t.Errorf("Expected true but returned false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppRoot(t *testing.T) {
|
||||
ap := NewParser(mockBackend{redirect: true})
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ func (s satisfy) GetDocumentation() parser.AnnotationFields {
|
|||
return s.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a satisfy) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (s satisfy) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, satisfyAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func (s serviceUpstream) GetDocumentation() parser.AnnotationFields {
|
|||
return s.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a serviceUpstream) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (s serviceUpstream) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, serviceUpstreamAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -63,13 +63,15 @@ const (
|
|||
|
||||
// This is used to control the cookie change after request failure
|
||||
annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
|
||||
|
||||
cookieAffinity = "cookie"
|
||||
)
|
||||
|
||||
var sessionAffinityAnnotations = parser.Annotation{
|
||||
Group: "affinity",
|
||||
Annotations: parser.AnnotationFields{
|
||||
annotationAffinityType: {
|
||||
Validator: parser.ValidateOptions([]string{"cookie"}, true, true),
|
||||
Validator: parser.ValidateOptions([]string{cookieAffinity}, true, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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`,
|
||||
|
@ -91,7 +93,7 @@ var sessionAffinityAnnotations = parser.Annotation{
|
|||
Setting this to legacy will restore original canary behavior, when session affinity was ignored.`,
|
||||
},
|
||||
annotationAffinityCookieName: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
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`,
|
||||
},
|
||||
annotationAffinityCookieExpires: {
|
||||
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, true),
|
||||
Validator: parser.ValidateRegex(affinityCookieExpiresRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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`,
|
||||
},
|
||||
annotationAffinityCookieMaxAge: {
|
||||
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, false),
|
||||
Validator: parser.ValidateRegex(affinityCookieExpiresRegex, false),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation sets the time until the cookie expires`,
|
||||
},
|
||||
annotationAffinityCookiePath: {
|
||||
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation defines the Path that will be set on the cookie (required if your Ingress paths use regular expressions)`,
|
||||
},
|
||||
annotationAffinityCookieDomain: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
Risk: parser.AnnotationRiskMedium,
|
||||
Documentation: `This annotation defines the Domain attribute of the sticky cookie.`,
|
||||
|
@ -149,9 +151,7 @@ var sessionAffinityAnnotations = parser.Annotation{
|
|||
},
|
||||
}
|
||||
|
||||
var (
|
||||
affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
|
||||
)
|
||||
var affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
|
||||
|
||||
// Config describes the per ingress session affinity config
|
||||
type Config struct {
|
||||
|
@ -186,6 +186,11 @@ type Cookie struct {
|
|||
ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
|
||||
}
|
||||
|
||||
type affinity struct {
|
||||
r resolver.Resolver
|
||||
annotationConfig parser.Annotation
|
||||
}
|
||||
|
||||
// cookieAffinityParse gets the annotation values related to Cookie Affinity
|
||||
// It also sets default values when no value or incorrect value is found
|
||||
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
|
||||
// rule used to configure the affinity directives
|
||||
func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
|
@ -279,11 +279,10 @@ func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
|
|||
}
|
||||
|
||||
switch at {
|
||||
case "cookie":
|
||||
case cookieAffinity:
|
||||
cookie = a.cookieAffinityParse(ing)
|
||||
default:
|
||||
klog.V(3).InfoS("No default affinity found", "ingress", ing.Name)
|
||||
|
||||
}
|
||||
|
||||
return &Config{
|
||||
|
|
|
@ -31,10 +31,8 @@ const (
|
|||
sslCipherAnnotation = "ssl-ciphers"
|
||||
)
|
||||
|
||||
var (
|
||||
// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
|
||||
regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
|
||||
)
|
||||
// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
|
||||
var regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
|
||||
|
||||
var sslCipherAnnotations = parser.Annotation{
|
||||
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.`,
|
||||
},
|
||||
sslCipherAnnotation: {
|
||||
Validator: parser.ValidateRegex(*regexValidSSLCipher, true),
|
||||
Validator: parser.ValidateRegex(regexValidSSLCipher, true),
|
||||
Scope: parser.AnnotationScopeIngress,
|
||||
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.`,
|
||||
|
@ -104,7 +102,7 @@ func (sc sslCipher) GetDocumentation() parser.AnnotationFields {
|
|||
return sc.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a sslCipher) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (sc sslCipher) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(sc.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, sslCipherAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -42,8 +42,11 @@ func TestParse(t *testing.T) {
|
|||
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: "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{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}, false},
|
||||
{map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}, false},
|
||||
|
|
|
@ -47,7 +47,8 @@ type sslpt struct {
|
|||
|
||||
// NewParser creates a new SSL passthrough annotation parser
|
||||
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
||||
return sslpt{r: r,
|
||||
return sslpt{
|
||||
r: r,
|
||||
annotationConfig: sslPassthroughAnnotations,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ func TestParse(t *testing.T) {
|
|||
annotations map[string]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}",
|
||||
},
|
||||
{map[string]string{annotation: "false"}, "false"},
|
||||
|
|
|
@ -41,7 +41,7 @@ var upstreamHashByAnnotations = parser.Annotation{
|
|||
Group: "backend",
|
||||
Annotations: parser.AnnotationFields{
|
||||
upstreamHashByAnnotation: {
|
||||
Validator: parser.ValidateRegex(*hashByRegex, true),
|
||||
Validator: parser.ValidateRegex(hashByRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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.
|
||||
|
|
|
@ -31,7 +31,7 @@ var xForwardedForAnnotations = parser.Annotation{
|
|||
Group: "backend",
|
||||
Annotations: parser.AnnotationFields{
|
||||
xForwardedForPrefixAnnotation: {
|
||||
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
|
||||
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
|
||||
Scope: parser.AnnotationScopeLocation,
|
||||
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`,
|
||||
|
@ -54,15 +54,15 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
|
|||
|
||||
// Parse parses the annotations contained in the ingress rule
|
||||
// used to add an x-forwarded-prefix header to the request
|
||||
func (cbbs xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, cbbs.annotationConfig.Annotations)
|
||||
func (x xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) {
|
||||
return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, x.annotationConfig.Annotations)
|
||||
}
|
||||
|
||||
func (cbbs xforwardedprefix) GetDocumentation() parser.AnnotationFields {
|
||||
return cbbs.annotationConfig.Annotations
|
||||
func (x xforwardedprefix) GetDocumentation() parser.AnnotationFields {
|
||||
return x.annotationConfig.Annotations
|
||||
}
|
||||
|
||||
func (a xforwardedprefix) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
func (x xforwardedprefix) Validate(anns map[string]string) error {
|
||||
maxrisk := parser.StringRiskToRisk(x.r.GetSecurityConfiguration().AnnotationsRiskLevel)
|
||||
return parser.CheckAnnotationRisk(anns, maxrisk, xForwardedForAnnotations.Annotations)
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func matchHostnames(pattern, host string) bool {
|
|||
host = strings.TrimSuffix(host, ".")
|
||||
pattern = strings.TrimSuffix(pattern, ".")
|
||||
|
||||
if len(pattern) == 0 || len(host) == 0 {
|
||||
if pattern == "" || host == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
)
|
||||
|
||||
// Name returns the healthcheck name
|
||||
func (n NGINXController) Name() string {
|
||||
func (n *NGINXController) Name() string {
|
||||
return "nginx-ingress-controller"
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
)
|
||||
|
||||
func TestNginxCheck(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tests := []struct {
|
||||
healthzPath string
|
||||
}{
|
||||
{"/healthz"},
|
||||
|
@ -42,7 +42,6 @@ func TestNginxCheck(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
testName := fmt.Sprintf("health path: %s", tt.healthzPath)
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
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)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
|
||||
server := &httptest.Server{
|
||||
Listener: listener,
|
||||
Config: &http.Server{
|
||||
|
@ -103,10 +102,10 @@ func TestNginxCheck(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -121,7 +120,7 @@ func TestNginxCheck(t *testing.T) {
|
|||
})
|
||||
|
||||
// pollute pid file
|
||||
pidFile.Write([]byte("999999")) //nolint:errcheck
|
||||
pidFile.WriteString("999999") //nolint:errcheck // Ignore the error
|
||||
pidFile.Close()
|
||||
|
||||
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 {
|
||||
req, err := http.NewRequest(http.MethodGet, healthzPath, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, healthzPath, http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("healthz error: %v", err)
|
||||
}
|
||||
|
|
|
@ -29,10 +29,8 @@ import (
|
|||
"k8s.io/ingress-nginx/pkg/util/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
|
||||
EnableSSLChainCompletion = false
|
||||
)
|
||||
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
|
||||
var EnableSSLChainCompletion = false
|
||||
|
||||
const (
|
||||
// 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
|
||||
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.
|
||||
// If disabled, only snippets added via ConfigMap are added to ingress.
|
||||
|
@ -137,9 +135,9 @@ type Configuration struct {
|
|||
// By default access logs go to /var/log/nginx/access.log
|
||||
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
|
||||
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
|
||||
// http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log
|
||||
|
@ -226,19 +224,19 @@ type Configuration struct {
|
|||
|
||||
// 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
|
||||
// NOTE: Deprecated
|
||||
// Deprecated: HTTP2MaxFieldSize is deprecated.
|
||||
HTTP2MaxFieldSize string `json:"http2-max-field-size,omitempty"`
|
||||
|
||||
// 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
|
||||
// NOTE: Deprecated
|
||||
// Deprecated: HTTP2MaxHeaderSize is deprecated.
|
||||
HTTP2MaxHeaderSize string `json:"http2-max-header-size,omitempty"`
|
||||
|
||||
// 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
|
||||
// through one HTTP/2 connection, after which the next client request will lead to connection closing
|
||||
// and the need of establishing a new connection.
|
||||
// NOTE: Deprecated
|
||||
// Deprecated: HTTP2MaxRequests is deprecated.
|
||||
HTTP2MaxRequests int `json:"http2-max-requests,omitempty"`
|
||||
|
||||
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams
|
||||
|
@ -548,7 +546,7 @@ type Configuration struct {
|
|||
UseForwardedHeaders bool `json:"use-forwarded-headers"`
|
||||
|
||||
// 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
|
||||
// Default is X-Forwarded-For
|
||||
|
@ -617,7 +615,7 @@ type Configuration struct {
|
|||
// Default: 0.01
|
||||
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
|
||||
OtelSamplerParentBased bool `json:"otel-sampler-parent-based"`
|
||||
|
||||
|
@ -886,7 +884,7 @@ func NewDefault() Configuration {
|
|||
EnableUnderscoresInHeaders: false,
|
||||
ErrorLogLevel: errorLevel,
|
||||
UseForwardedHeaders: false,
|
||||
EnableRealIp: false,
|
||||
EnableRealIP: false,
|
||||
ForwardedForHeader: "X-Forwarded-For",
|
||||
ComputeFullForwardedFor: false,
|
||||
ProxyAddOriginalURIHeader: false,
|
||||
|
@ -1034,41 +1032,41 @@ func NewDefault() Configuration {
|
|||
|
||||
// TemplateConfig contains the nginx configuration to render the file nginx.conf
|
||||
type TemplateConfig struct {
|
||||
ProxySetHeaders map[string]string
|
||||
AddHeaders map[string]string
|
||||
BacklogSize int
|
||||
Backends []*ingress.Backend
|
||||
PassthroughBackends []*ingress.SSLPassthroughBackend
|
||||
Servers []*ingress.Server
|
||||
TCPBackends []ingress.L4Service
|
||||
UDPBackends []ingress.L4Service
|
||||
HealthzURI string
|
||||
Cfg Configuration
|
||||
IsIPV6Enabled bool
|
||||
IsSSLPassthroughEnabled bool
|
||||
NginxStatusIpv4Whitelist []string
|
||||
NginxStatusIpv6Whitelist []string
|
||||
RedirectServers interface{}
|
||||
ListenPorts *ListenPorts
|
||||
PublishService *apiv1.Service
|
||||
EnableMetrics bool
|
||||
MaxmindEditionFiles *[]string
|
||||
MonitorMaxBatchSize int
|
||||
PID string
|
||||
StatusPath string
|
||||
StatusPort int
|
||||
StreamPort int
|
||||
StreamSnippets []string
|
||||
ProxySetHeaders map[string]string `json:"ProxySetHeaders"`
|
||||
AddHeaders map[string]string `json:"AddHeaders"`
|
||||
BacklogSize int `json:"BacklogSize"`
|
||||
Backends []*ingress.Backend `json:"Backends"`
|
||||
PassthroughBackends []*ingress.SSLPassthroughBackend `json:"PassthroughBackends"`
|
||||
Servers []*ingress.Server `json:"Servers"`
|
||||
TCPBackends []ingress.L4Service `json:"TCPBackends"`
|
||||
UDPBackends []ingress.L4Service `json:"UDPBackends"`
|
||||
HealthzURI string `json:"HealthzURI"`
|
||||
Cfg Configuration `json:"Cfg"`
|
||||
IsIPV6Enabled bool `json:"IsIPV6Enabled"`
|
||||
IsSSLPassthroughEnabled bool `json:"IsSSLPassthroughEnabled"`
|
||||
NginxStatusIpv4Whitelist []string `json:"NginxStatusIpv4Whitelist"`
|
||||
NginxStatusIpv6Whitelist []string `json:"NginxStatusIpv6Whitelist"`
|
||||
RedirectServers interface{} `json:"RedirectServers"`
|
||||
ListenPorts *ListenPorts `json:"ListenPorts"`
|
||||
PublishService *apiv1.Service `json:"PublishService"`
|
||||
EnableMetrics bool `json:"EnableMetrics"`
|
||||
MaxmindEditionFiles *[]string `json:"MaxmindEditionFiles"`
|
||||
MonitorMaxBatchSize int `json:"MonitorMaxBatchSize"`
|
||||
PID string `json:"PID"`
|
||||
StatusPath string `json:"StatusPath"`
|
||||
StatusPort int `json:"StatusPort"`
|
||||
StreamPort int `json:"StreamPort"`
|
||||
StreamSnippets []string `json:"StreamSnippets"`
|
||||
}
|
||||
|
||||
// ListenPorts describe the ports required to run the
|
||||
// NGINX Ingress controller
|
||||
type ListenPorts struct {
|
||||
HTTP int
|
||||
HTTPS int
|
||||
Health int
|
||||
Default int
|
||||
SSLProxy int
|
||||
HTTP int `json:"HTTP"`
|
||||
HTTPS int `json:"HTTPS"`
|
||||
Health int `json:"Health"`
|
||||
Default int `json:"Default"`
|
||||
SSLProxy int `json:"SSLProxy"`
|
||||
}
|
||||
|
||||
// GlobalExternalAuth describe external authentication configuration for the
|
||||
|
|
|
@ -114,7 +114,7 @@ type Configuration struct {
|
|||
|
||||
DisableCatchAll bool
|
||||
|
||||
IngressClassConfiguration *ingressclass.IngressClassConfiguration
|
||||
IngressClassConfiguration *ingressclass.Configuration
|
||||
|
||||
ValidationWebhook string
|
||||
ValidationWebhookCertPath string
|
||||
|
@ -143,7 +143,7 @@ type Configuration struct {
|
|||
func getIngressPodZone(svc *apiv1.Service) string {
|
||||
svcKey := k8s.MetaNamespaceKey(svc)
|
||||
if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyAwareHints]; ok {
|
||||
if strings.ToLower(svcZoneAnnotation) == "auto" {
|
||||
if strings.EqualFold(svcZoneAnnotation, "auto") {
|
||||
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)
|
||||
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.
|
||||
func (n NGINXController) GetPublishService() *apiv1.Service {
|
||||
func (n *NGINXController) GetPublishService() *apiv1.Service {
|
||||
s, err := n.store.GetService(n.cfg.PublishService)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
@ -195,7 +195,7 @@ func (n *NGINXController) syncIngress(interface{}) error {
|
|||
|
||||
pcfg.ConfigurationChecksum = fmt.Sprintf("%v", hash)
|
||||
|
||||
err := n.OnUpdate(*pcfg)
|
||||
err := n.OnUpdate(pcfg)
|
||||
if err != nil {
|
||||
n.metricCollector.IncReloadErrorCount()
|
||||
n.metricCollector.ConfigSuccess(hash, false)
|
||||
|
@ -263,7 +263,7 @@ func (n *NGINXController) syncIngress(interface{}) error {
|
|||
func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) {
|
||||
warnings := make([]string, 0)
|
||||
|
||||
var deprecatedAnnotations = sets.NewString()
|
||||
deprecatedAnnotations := sets.NewString()
|
||||
deprecatedAnnotations.Insert(
|
||||
"enable-influxdb",
|
||||
"influxdb-measurement",
|
||||
|
@ -335,7 +335,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
|
|||
}
|
||||
|
||||
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
|
||||
cfg := n.store.GetBackendConfiguration()
|
||||
|
@ -355,10 +355,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
|
|||
}
|
||||
|
||||
for key, value := range ing.ObjectMeta.GetAnnotations() {
|
||||
|
||||
if parser.AnnotationsPrefix != 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 +373,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)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
k8s.SetDefaultNGINXPathType(ing)
|
||||
|
@ -401,7 +399,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
|
|||
startTest := time.Now().UnixNano() / 1000000
|
||||
_, servers, pcfg := n.getConfiguration(ings)
|
||||
|
||||
err = checkOverlap(ing, allIngresses, servers)
|
||||
err = checkOverlap(ing, servers)
|
||||
if err != nil {
|
||||
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
|
||||
return err
|
||||
|
@ -412,7 +410,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
|
|||
testedSize = 1
|
||||
}
|
||||
|
||||
content, err := n.generateTemplate(cfg, *pcfg)
|
||||
content, err := n.generateTemplate(&cfg, pcfg)
|
||||
if err != nil {
|
||||
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
|
||||
return err
|
||||
|
@ -452,7 +450,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
|||
return []ingress.L4Service{}
|
||||
}
|
||||
|
||||
var svcs []ingress.L4Service
|
||||
svcs := make([]ingress.L4Service, 0, len(configmap.Data))
|
||||
var svcProxyProtocol ingress.ProxyProtocol
|
||||
|
||||
rp := []int{
|
||||
|
@ -489,10 +487,10 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
|||
svcProxyProtocol.Encode = false
|
||||
// Proxy Protocol is only compatible with TCP Services
|
||||
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
|
||||
}
|
||||
if len(nsSvcPort) == 4 && strings.ToUpper(nsSvcPort[3]) == "PROXY" {
|
||||
if len(nsSvcPort) == 4 && strings.EqualFold(nsSvcPort[3], "PROXY") {
|
||||
svcProxyProtocol.Encode = true
|
||||
}
|
||||
}
|
||||
|
@ -532,6 +530,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)
|
||||
for i := range svc.Spec.Ports {
|
||||
sp := svc.Spec.Ports[i]
|
||||
//nolint:gosec // Ingore G109 error
|
||||
if sp.Port == int32(targetPort) {
|
||||
if sp.Protocol == proto {
|
||||
endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices)
|
||||
|
@ -574,7 +573,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
|
|||
}
|
||||
svcKey := n.cfg.DefaultService
|
||||
|
||||
if len(svcKey) == 0 {
|
||||
if svcKey == "" {
|
||||
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
|
||||
return upstream
|
||||
}
|
||||
|
@ -690,13 +689,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)
|
||||
anns.StreamSnippet = ""
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
// service name and port are the same.
|
||||
//
|
||||
//nolint:gocyclo // Ignore function complexity error
|
||||
func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*ingress.Backend, []*ingress.Server) {
|
||||
du := n.getDefaultUpstream()
|
||||
upstreams := n.createUpstreams(ingresses, du)
|
||||
|
@ -1030,7 +1030,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
|
|||
// configure traffic shaping for canary
|
||||
if anns.Canary.Enabled {
|
||||
upstreams[defBackend].NoServer = true
|
||||
upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary)
|
||||
upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
|
||||
}
|
||||
|
||||
if len(upstreams[defBackend].Endpoints) == 0 {
|
||||
|
@ -1095,7 +1095,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
|
|||
// configure traffic shaping for canary
|
||||
if anns.Canary.Enabled {
|
||||
upstreams[name].NoServer = true
|
||||
upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary)
|
||||
upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
|
||||
}
|
||||
|
||||
if len(upstreams[name].Endpoints) == 0 {
|
||||
|
@ -1206,7 +1206,6 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
|
|||
if strconv.Itoa(int(servicePort.Port)) == backendPort ||
|
||||
servicePort.TargetPort.String() == backendPort ||
|
||||
servicePort.Name == backendPort {
|
||||
|
||||
endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
|
||||
if len(endps) == 0 {
|
||||
klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
|
||||
|
@ -1239,8 +1238,8 @@ func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert {
|
|||
// one root location, which uses a default backend if left unspecified.
|
||||
func (n *NGINXController) createServers(data []*ingress.Ingress,
|
||||
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))
|
||||
allAliases := make(map[string][]string, len(data))
|
||||
|
||||
|
@ -1282,7 +1281,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
Rewrite: false,
|
||||
},
|
||||
},
|
||||
}}
|
||||
},
|
||||
}
|
||||
|
||||
// initialize all other servers
|
||||
for _, ing := range data {
|
||||
|
@ -1532,7 +1532,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
|
||||
func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ingress.Ingress) bool {
|
||||
func nonCanaryIngressExists(ingresses, canaryIngresses []*ingress.Ingress) bool {
|
||||
return len(ingresses)-len(canaryIngresses) > 0
|
||||
}
|
||||
|
||||
|
@ -1540,12 +1540,12 @@ func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ing
|
|||
// 1) names of backends do not match and canary doesn't merge into itself
|
||||
// 2) primary name is not the default upstream
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
klog.Warningf("unable to merge alternative backend %v into primary backend %v because %v is a primary backend",
|
||||
altUps.Name, priUps.Name, priUps.Name)
|
||||
|
@ -1563,8 +1563,7 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
|
|||
priUps.SessionAffinity.DeepCopyInto(&altUps.SessionAffinity)
|
||||
}
|
||||
|
||||
priUps.AlternativeBackends =
|
||||
append(priUps.AlternativeBackends, altUps.Name)
|
||||
priUps.AlternativeBackends = append(priUps.AlternativeBackends, altUps.Name)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -1574,8 +1573,8 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
|
|||
// to a backend's alternative list.
|
||||
// If no match is found, then the serverless backend is deleted.
|
||||
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
|
||||
if ing.Spec.DefaultBackend != nil {
|
||||
upsName := upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service)
|
||||
|
@ -1585,7 +1584,6 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
|
|||
if altUps == nil {
|
||||
klog.Warningf("alternative backend %s has already been removed", upsName)
|
||||
} else {
|
||||
|
||||
merged := false
|
||||
altEqualsPri := false
|
||||
|
||||
|
@ -1676,8 +1674,8 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
|
|||
// extractTLSSecretName returns the name of the Secret containing a SSL
|
||||
// certificate for the given host name, or an empty string.
|
||||
func extractTLSSecretName(host string, ing *ingress.Ingress,
|
||||
getLocalSSLCert func(string) (*ingress.SSLCert, error)) string {
|
||||
|
||||
getLocalSSLCert func(string) (*ingress.SSLCert, error),
|
||||
) string {
|
||||
if ing == nil {
|
||||
return ""
|
||||
}
|
||||
|
@ -1694,7 +1692,6 @@ func extractTLSSecretName(host string, ing *ingress.Ingress,
|
|||
|
||||
// no TLS host matching host name, try each TLS host for matching SAN or CN
|
||||
for _, tls := range ing.Spec.TLS {
|
||||
|
||||
if tls.SecretName == "" {
|
||||
// There's no secretName specified, so it will never be available
|
||||
continue
|
||||
|
@ -1753,6 +1750,7 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
|
|||
}
|
||||
|
||||
for _, svcPort := range svc.Spec.Ports {
|
||||
//nolint:gosec // Ingore G109 error
|
||||
if svcPort.Port != int32(port) {
|
||||
continue
|
||||
}
|
||||
|
@ -1771,13 +1769,14 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
|
|||
|
||||
// ExternalName without port
|
||||
return &apiv1.ServicePort{
|
||||
Protocol: "TCP",
|
||||
Protocol: "TCP",
|
||||
//nolint:gosec // Ingore G109 error
|
||||
Port: int32(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 {
|
||||
if rule.HTTP == nil {
|
||||
continue
|
||||
|
@ -1870,7 +1869,7 @@ func (n *NGINXController) getStreamSnippets(ingresses []*ingress.Ingress) []stri
|
|||
}
|
||||
|
||||
// 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{
|
||||
Weight: cfg.Weight,
|
||||
WeightTotal: cfg.WeightTotal,
|
||||
|
|
|
@ -60,51 +60,56 @@ import (
|
|||
"k8s.io/ingress-nginx/pkg/util/file"
|
||||
)
|
||||
|
||||
const (
|
||||
exampleBackend = "example-http-svc-1-80"
|
||||
TRUE = "true"
|
||||
)
|
||||
|
||||
type fakeIngressStore struct {
|
||||
ingresses []*ingress.Ingress
|
||||
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
|
||||
}
|
||||
|
||||
func (fis fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
|
||||
func (fis *fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
|
||||
return fis.configuration
|
||||
}
|
||||
|
||||
func (fis fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
|
||||
func (fis *fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
|
||||
return defaults.SecurityConfiguration{
|
||||
AnnotationsRiskLevel: fis.configuration.AnnotationsRiskLevel,
|
||||
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")
|
||||
}
|
||||
|
||||
func (fakeIngressStore) GetSecret(key string) (*corev1.Secret, error) {
|
||||
func (fakeIngressStore) GetSecret(_ string) (*corev1.Secret, 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")
|
||||
}
|
||||
|
||||
func (fakeIngressStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) {
|
||||
func (fakeIngressStore) GetServiceEndpointsSlices(_ string) ([]*discoveryv1.EndpointSlice, error) {
|
||||
return nil, fmt.Errorf("test error")
|
||||
}
|
||||
|
||||
func (fis fakeIngressStore) ListIngresses() []*ingress.Ingress {
|
||||
func (fis *fakeIngressStore) ListIngresses() []*ingress.Ingress {
|
||||
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
|
||||
}
|
||||
|
||||
func (fakeIngressStore) GetLocalSSLCert(name string) (*ingress.SSLCert, error) {
|
||||
func (fakeIngressStore) GetLocalSSLCert(_ string) (*ingress.SSLCert, error) {
|
||||
return nil, fmt.Errorf("test error")
|
||||
}
|
||||
|
||||
|
@ -120,7 +125,7 @@ func (fakeIngressStore) GetDefaultBackend() defaults.Backend {
|
|||
return defaults.Backend{}
|
||||
}
|
||||
|
||||
func (fakeIngressStore) Run(stopCh chan struct{}) {}
|
||||
func (fakeIngressStore) Run(_ chan struct{}) {}
|
||||
|
||||
type testNginxTestCommand struct {
|
||||
t *testing.T
|
||||
|
@ -129,7 +134,7 @@ type testNginxTestCommand struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (ntc testNginxTestCommand) ExecCommand(args ...string) *exec.Cmd {
|
||||
func (ntc testNginxTestCommand) ExecCommand(_ ...string) *exec.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -152,7 +157,7 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) {
|
|||
|
||||
type fakeTemplate struct{}
|
||||
|
||||
func (fakeTemplate) Write(conf ngx_config.TemplateConfig) ([]byte, error) {
|
||||
func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) {
|
||||
r := []byte{}
|
||||
for _, s := range conf.Servers {
|
||||
if len(r) > 0 {
|
||||
|
@ -196,7 +201,7 @@ func TestCheckIngress(t *testing.T) {
|
|||
nginx.metricCollector = metric.DummyCollector{}
|
||||
|
||||
nginx.t = fakeTemplate{}
|
||||
nginx.store = fakeIngressStore{
|
||||
nginx.store = &fakeIngressStore{
|
||||
ingresses: []*ingress.Ingress{},
|
||||
}
|
||||
|
||||
|
@ -226,7 +231,7 @@ func TestCheckIngress(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("When the hostname is updated", func(t *testing.T) {
|
||||
nginx.store = fakeIngressStore{
|
||||
nginx.store = &fakeIngressStore{
|
||||
ingresses: []*ingress.Ingress{
|
||||
{
|
||||
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) {
|
||||
nginx.store = fakeIngressStore{
|
||||
nginx.store = &fakeIngressStore{
|
||||
ingresses: []*ingress.Ingress{},
|
||||
configuration: ngx_config.Configuration{
|
||||
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) {
|
||||
nginx.store = fakeIngressStore{
|
||||
nginx.store = &fakeIngressStore{
|
||||
ingresses: []*ingress.Ingress{},
|
||||
configuration: ngx_config.Configuration{
|
||||
AnnotationValueWordBlocklist: "invalid_directive, another_directive",
|
||||
|
@ -366,12 +371,11 @@ func TestCheckIngress(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCheckWarning(t *testing.T) {
|
||||
|
||||
// Ensure no panic with wrong arguments
|
||||
var nginx = &NGINXController{}
|
||||
nginx := &NGINXController{}
|
||||
|
||||
nginx.t = fakeTemplate{}
|
||||
nginx.store = fakeIngressStore{
|
||||
nginx.store = &fakeIngressStore{
|
||||
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) {
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
|
||||
defer func() {
|
||||
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) {
|
||||
|
||||
rules := ing.Spec.DeepCopy().Rules
|
||||
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) {
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "true"
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = TRUE
|
||||
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla"
|
||||
defer func() {
|
||||
ing.ObjectMeta.Annotations = map[string]string{}
|
||||
|
@ -1537,8 +1540,8 @@ func TestExtractTLSSecretName(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo // Ignore function complexity error
|
||||
func TestGetBackendServers(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
Ingresses []*ingress.Ingress
|
||||
Validate func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server)
|
||||
|
@ -2078,7 +2081,7 @@ func TestGetBackendServers(t *testing.T) {
|
|||
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'")
|
||||
}
|
||||
|
||||
|
@ -2087,7 +2090,7 @@ func TestGetBackendServers(t *testing.T) {
|
|||
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)
|
||||
return
|
||||
}
|
||||
|
@ -2319,7 +2322,7 @@ func TestGetBackendServers(t *testing.T) {
|
|||
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"proxy-ssl-location-only": "true",
|
||||
"proxy-ssl-location-only": TRUE,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -2380,7 +2383,7 @@ func TestGetBackendServers(t *testing.T) {
|
|||
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"proxy-ssl-location-only": "true",
|
||||
"proxy-ssl-location-only": TRUE,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -2449,7 +2452,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" {
|
||||
t.Errorf("allow list was incorrectly dropped, len should be 1 and contain 10.0.0.0/24")
|
||||
}
|
||||
|
||||
},
|
||||
SetConfigMap: func(ns string) *corev1.ConfigMap {
|
||||
return &corev1.ConfigMap{
|
||||
|
@ -2520,7 +2522,7 @@ func newNGINXController(t *testing.T) *NGINXController {
|
|||
channels.NewRingChannel(10),
|
||||
false,
|
||||
true,
|
||||
&ingressclass.IngressClassConfiguration{
|
||||
&ingressclass.Configuration{
|
||||
Controller: "k8s.io/ingress-nginx",
|
||||
AnnotationValue: "nginx",
|
||||
},
|
||||
|
@ -2586,7 +2588,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *corev1.C
|
|||
channels.NewRingChannel(10),
|
||||
false,
|
||||
true,
|
||||
&ingressclass.IngressClassConfiguration{
|
||||
&ingressclass.Configuration{
|
||||
Controller: "k8s.io/ingress-nginx",
|
||||
AnnotationValue: "nginx",
|
||||
},
|
||||
|
|
|
@ -36,8 +36,8 @@ import (
|
|||
|
||||
// getEndpointsFromSlices returns a list of Endpoint structs for a given service/target port combination.
|
||||
func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol, zoneForHints string,
|
||||
getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error)) []ingress.Endpoint {
|
||||
|
||||
getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error),
|
||||
) []ingress.Endpoint {
|
||||
upsServers := []ingress.Endpoint{}
|
||||
|
||||
if s == nil || port == nil {
|
||||
|
@ -94,7 +94,7 @@ func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto c
|
|||
if !reflect.DeepEqual(*epPort.Protocol, proto) {
|
||||
continue
|
||||
}
|
||||
var targetPort int32 = 0
|
||||
var targetPort int32
|
||||
if port.Name == "" {
|
||||
// port.Name is optional if there is only one port
|
||||
targetPort = *epPort.Port
|
||||
|
|
|
@ -29,9 +29,9 @@ const (
|
|||
DefaultAnnotationValue = "nginx"
|
||||
)
|
||||
|
||||
// IngressClassConfiguration defines the various aspects of IngressClass parsing
|
||||
// Configuration defines the various aspects of IngressClass parsing
|
||||
// and how the controller should behave in each case
|
||||
type IngressClassConfiguration struct {
|
||||
type Configuration struct {
|
||||
// Controller defines the controller value this daemon watch to.
|
||||
// Defaults to "k8s.io/ingress-nginx" defined in flags
|
||||
Controller string
|
||||
|
@ -45,7 +45,7 @@ type IngressClassConfiguration struct {
|
|||
// IgnoreIngressClass defines if Controller should ignore the IngressClass Object if no permissions are
|
||||
// granted on IngressClass
|
||||
IgnoreIngressClass bool
|
||||
//IngressClassByName defines if the Controller should watch for Ingress Classes by
|
||||
// IngressClassByName defines if the Controller should watch for Ingress Classes by
|
||||
// .metadata.name together with .spec.Controller
|
||||
IngressClassByName bool
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro
|
|||
if n.cfg.ValidationWebhook != "" {
|
||||
n.validationWebhookServer = &http.Server{
|
||||
Addr: config.ValidationWebhook,
|
||||
//G112 (CWE-400): Potential Slowloris Attack
|
||||
// G112 (CWE-400): Potential Slowloris Attack
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
Handler: adm_controller.NewAdmissionControllerServer(&adm_controller.IngressAdmission{Checker: n}),
|
||||
TLSConfig: ssl.NewTLSListener(n.cfg.ValidationWebhookCertPath, n.cfg.ValidationWebhookKeyPath).TLSConfig(),
|
||||
|
@ -429,7 +429,7 @@ func (n *NGINXController) start(cmd *exec.Cmd) {
|
|||
}
|
||||
|
||||
// DefaultEndpoint returns the default endpoint to be use as default server that returns 404.
|
||||
func (n NGINXController) DefaultEndpoint() ingress.Endpoint {
|
||||
func (n *NGINXController) DefaultEndpoint() ingress.Endpoint {
|
||||
return ingress.Endpoint{
|
||||
Address: "127.0.0.1",
|
||||
Port: fmt.Sprintf("%v", n.cfg.ListenPorts.Default),
|
||||
|
@ -438,8 +438,7 @@ func (n NGINXController) DefaultEndpoint() ingress.Endpoint {
|
|||
}
|
||||
|
||||
// generateTemplate returns the nginx configuration file content
|
||||
func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) {
|
||||
|
||||
func (n *NGINXController) generateTemplate(cfg *ngx_config.Configuration, ingressCfg *ingress.Configuration) ([]byte, error) {
|
||||
if n.cfg.EnableSSLPassthrough {
|
||||
servers := []*tcpproxy.TCPServer{}
|
||||
for _, pb := range ingressCfg.PassthroughBackends {
|
||||
|
@ -458,6 +457,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
}
|
||||
} else {
|
||||
for _, sp := range svc.Spec.Ports {
|
||||
//nolint:gosec // Ignore G109 error
|
||||
if sp.Port == int32(port) {
|
||||
port = int(sp.Port)
|
||||
break
|
||||
|
@ -563,7 +563,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
if err != nil {
|
||||
klog.Warningf("Error reading Secret %q from local store: %v", secretName, err)
|
||||
} else {
|
||||
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
||||
nsSecName := strings.ReplaceAll(secretName, "/", "-")
|
||||
dh, ok := secret.Data["dhparam.pem"]
|
||||
if ok {
|
||||
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
|
||||
|
@ -589,7 +589,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
}
|
||||
}
|
||||
|
||||
tc := ngx_config.TemplateConfig{
|
||||
tc := &ngx_config.TemplateConfig{
|
||||
ProxySetHeaders: setHeaders,
|
||||
AddHeaders: addHeaders,
|
||||
BacklogSize: sysctlSomaxconn(),
|
||||
|
@ -598,7 +598,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
Servers: ingressCfg.Servers,
|
||||
TCPBackends: ingressCfg.TCPEndpoints,
|
||||
UDPBackends: ingressCfg.UDPEndpoints,
|
||||
Cfg: cfg,
|
||||
Cfg: *cfg,
|
||||
IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6,
|
||||
NginxStatusIpv4Whitelist: cfg.NginxStatusIpv4Whitelist,
|
||||
NginxStatusIpv6Whitelist: cfg.NginxStatusIpv6Whitelist,
|
||||
|
@ -623,7 +623,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
|
||||
// testTemplate checks if the NGINX configuration inside the byte array is valid
|
||||
// running the command "nginx -t" using a temporal file.
|
||||
func (n NGINXController) testTemplate(cfg []byte) error {
|
||||
func (n *NGINXController) testTemplate(cfg []byte) error {
|
||||
if len(cfg) == 0 {
|
||||
return fmt.Errorf("invalid NGINX configuration (empty)")
|
||||
}
|
||||
|
@ -658,21 +658,21 @@ Error: %v
|
|||
// changes were detected. The received backend Configuration is merged with the
|
||||
// configuration ConfigMap before generating the final configuration file.
|
||||
// Returns nil in case the backend was successfully reloaded.
|
||||
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
||||
func (n *NGINXController) OnUpdate(ingressCfg *ingress.Configuration) error {
|
||||
cfg := n.store.GetBackendConfiguration()
|
||||
cfg.Resolver = n.resolver
|
||||
|
||||
content, err := n.generateTemplate(cfg, ingressCfg)
|
||||
content, err := n.generateTemplate(&cfg, ingressCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = createOpentracingCfg(cfg)
|
||||
err = createOpentracingCfg(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = createOpentelemetryCfg(cfg)
|
||||
err = createOpentelemetryCfg(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -694,7 +694,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:gosec //Ignore G204 error
|
||||
diffOutput, err := exec.Command("diff", "-I", "'# Configuration.*'", "-u", cfgPath, tmpfile.Name()).CombinedOutput()
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
|
@ -828,9 +828,10 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []ingress.L4Service) error {
|
||||
func updateStreamConfiguration(tcpEndpoints, udpEndpoints []ingress.L4Service) error {
|
||||
streams := make([]ingress.Backend, 0)
|
||||
for _, ep := range TCPEndpoints {
|
||||
for i := range tcpEndpoints {
|
||||
ep := &tcpEndpoints[i]
|
||||
var service *apiv1.Service
|
||||
if ep.Service != nil {
|
||||
service = &apiv1.Service{Spec: ep.Service.Spec}
|
||||
|
@ -844,7 +845,8 @@ func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []
|
|||
Service: service,
|
||||
})
|
||||
}
|
||||
for _, ep := range UDPEndpoints {
|
||||
for i := range udpEndpoints {
|
||||
ep := &udpEndpoints[i]
|
||||
var service *apiv1.Service
|
||||
if ep.Service != nil {
|
||||
service = &apiv1.Service{Spec: ep.Service.Spec}
|
||||
|
@ -1034,7 +1036,7 @@ ratio = {{ .OtelSamplerRatio }}
|
|||
parent_based = {{ .OtelSamplerParentBased }}
|
||||
`
|
||||
|
||||
func datadogOpentracingCfg(cfg ngx_config.Configuration) (string, error) {
|
||||
func datadogOpentracingCfg(cfg *ngx_config.Configuration) (string, error) {
|
||||
m := map[string]interface{}{
|
||||
"service": cfg.DatadogServiceName,
|
||||
"agent_host": cfg.DatadogCollectorHost,
|
||||
|
@ -1058,7 +1060,7 @@ func datadogOpentracingCfg(cfg ngx_config.Configuration) (string, error) {
|
|||
return string(buf), nil
|
||||
}
|
||||
|
||||
func opentracingCfgFromTemplate(cfg ngx_config.Configuration, tmplName string, tmplText string) (string, error) {
|
||||
func opentracingCfgFromTemplate(cfg *ngx_config.Configuration, tmplName, tmplText string) (string, error) {
|
||||
tmpl, err := template.New(tmplName).Parse(tmplText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -1073,17 +1075,18 @@ func opentracingCfgFromTemplate(cfg ngx_config.Configuration, tmplName string, t
|
|||
return tmplBuf.String(), nil
|
||||
}
|
||||
|
||||
func createOpentracingCfg(cfg ngx_config.Configuration) error {
|
||||
func createOpentracingCfg(cfg *ngx_config.Configuration) error {
|
||||
var configData string
|
||||
var err error
|
||||
|
||||
if cfg.ZipkinCollectorHost != "" {
|
||||
switch {
|
||||
case cfg.ZipkinCollectorHost != "":
|
||||
configData, err = opentracingCfgFromTemplate(cfg, "zipkin", zipkinTmpl)
|
||||
} else if cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "" {
|
||||
case cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "":
|
||||
configData, err = opentracingCfgFromTemplate(cfg, "jaeger", jaegerTmpl)
|
||||
} else if cfg.DatadogCollectorHost != "" {
|
||||
case cfg.DatadogCollectorHost != "":
|
||||
configData, err = datadogOpentracingCfg(cfg)
|
||||
} else {
|
||||
default:
|
||||
configData = "{}"
|
||||
}
|
||||
|
||||
|
@ -1097,8 +1100,7 @@ func createOpentracingCfg(cfg ngx_config.Configuration) error {
|
|||
return os.WriteFile("/etc/nginx/opentracing.json", []byte(expanded), file.ReadWriteByUser)
|
||||
}
|
||||
|
||||
func createOpentelemetryCfg(cfg ngx_config.Configuration) error {
|
||||
|
||||
func createOpentelemetryCfg(cfg *ngx_config.Configuration) error {
|
||||
tmpl, err := template.New("otel").Parse(otelTmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -58,6 +58,7 @@ func TestConfigureDynamically(t *testing.T) {
|
|||
|
||||
server := &httptest.Server{
|
||||
Listener: listener,
|
||||
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
|
||||
Config: &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
@ -76,23 +77,17 @@ func TestConfigureDynamically(t *testing.T) {
|
|||
|
||||
switch r.URL.Path {
|
||||
case "/configuration/backends":
|
||||
{
|
||||
if strings.Contains(body, "target") {
|
||||
t.Errorf("unexpected target reference in JSON content: %v", body)
|
||||
}
|
||||
if strings.Contains(body, "target") {
|
||||
t.Errorf("unexpected target reference in JSON content: %v", body)
|
||||
}
|
||||
|
||||
if !strings.Contains(body, "service") {
|
||||
t.Errorf("service reference should be present in JSON content: %v", body)
|
||||
}
|
||||
if !strings.Contains(body, "service") {
|
||||
t.Errorf("service reference should be present in JSON content: %v", body)
|
||||
}
|
||||
case "/configuration/general":
|
||||
{
|
||||
}
|
||||
case "/configuration/servers":
|
||||
{
|
||||
if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) {
|
||||
t.Errorf("should be present in JSON content: %v", body)
|
||||
}
|
||||
if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) {
|
||||
t.Errorf("should be present in JSON content: %v", body)
|
||||
}
|
||||
default:
|
||||
t.Errorf("unknown request to %s", r.URL.Path)
|
||||
|
@ -218,6 +213,7 @@ func TestConfigureCertificates(t *testing.T) {
|
|||
|
||||
server := &httptest.Server{
|
||||
Listener: listener,
|
||||
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
|
||||
Config: &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
@ -414,6 +410,7 @@ func TestCleanTempNginxCfg(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
//nolint:unparam // Ingnore `network` always receives `"tcp"` error
|
||||
func tryListen(network, address string) (l net.Listener, err error) {
|
||||
condFunc := func() (bool, error) {
|
||||
l, err = net.Listen(network, address)
|
||||
|
|
|
@ -50,7 +50,7 @@ func setupLeaderElection(config *leaderElectionConfig) {
|
|||
|
||||
var cancelContext context.CancelFunc
|
||||
|
||||
var newLeaderCtx = func(ctx context.Context) context.CancelFunc {
|
||||
newLeaderCtx := func(ctx context.Context) context.CancelFunc {
|
||||
// allow to cancel the context in case we stop being the leader
|
||||
leaderCtx, cancel := context.WithCancel(ctx)
|
||||
go elector.Run(leaderCtx)
|
||||
|
|
|
@ -88,10 +88,11 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
auth := secret.Data["auth"]
|
||||
|
||||
// namespace/secretName -> namespace-secretName
|
||||
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
||||
nsSecName := strings.ReplaceAll(secretName, "/", "-")
|
||||
|
||||
var sslCert *ingress.SSLCert
|
||||
if okcert && okkey {
|
||||
switch {
|
||||
case okcert && okkey:
|
||||
if cert == nil {
|
||||
return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName)
|
||||
}
|
||||
|
@ -144,7 +145,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
}
|
||||
|
||||
klog.V(3).InfoS(msg)
|
||||
} else if len(ca) > 0 {
|
||||
case len(ca) > 0:
|
||||
sslCert, err = ssl.CreateCACert(ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
|
||||
|
@ -166,7 +167,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
// makes this secret in 'syncSecret' to be used for Certificate Authentication
|
||||
// this does not enable Certificate Authentication
|
||||
klog.V(3).InfoS("Configuring Secret for TLS authentication", "secret", secretName)
|
||||
} else {
|
||||
default:
|
||||
if auth != nil {
|
||||
return nil, ErrSecretForAuth
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func (s *EndpointSliceLister) MatchByKey(key string) ([]*discoveryv1.EndpointSli
|
|||
keyNsLen = 0
|
||||
} else {
|
||||
// count '/' char
|
||||
keyNsLen += 1
|
||||
keyNsLen++
|
||||
}
|
||||
// filter endpointSlices owned by svc
|
||||
for _, listKey := range s.ListKeys() {
|
||||
|
|
|
@ -87,7 +87,6 @@ func TestEndpointSliceLister(t *testing.T) {
|
|||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
eps, err := el.MatchByKey(key)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unexpeted error %v", err)
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ type Storer interface {
|
|||
Run(stopCh chan struct{})
|
||||
|
||||
// GetIngressClass validates given ingress against ingress class configuration and returns the ingress class.
|
||||
GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error)
|
||||
GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error)
|
||||
}
|
||||
|
||||
// EventType type of event associated with an informer
|
||||
|
@ -242,7 +242,9 @@ type k8sStore struct {
|
|||
defaultSSLCertificate string
|
||||
}
|
||||
|
||||
// New creates a new object store to be used in the ingress controller
|
||||
// New creates a new object store to be used in the ingress controller.
|
||||
//
|
||||
//nolint:gocyclo // Ignore function complexity error.
|
||||
func New(
|
||||
namespace string,
|
||||
namespaceSelector labels.Selector,
|
||||
|
@ -252,9 +254,9 @@ func New(
|
|||
updateCh *channels.RingChannel,
|
||||
disableCatchAll bool,
|
||||
deepInspector bool,
|
||||
icConfig *ingressclass.IngressClassConfiguration,
|
||||
disableSyncEvents bool) Storer {
|
||||
|
||||
icConfig *ingressclass.Configuration,
|
||||
disableSyncEvents bool,
|
||||
) Storer {
|
||||
store := &k8sStore{
|
||||
informers: &Informer{},
|
||||
listers: &Lister{},
|
||||
|
@ -474,7 +476,8 @@ func New(
|
|||
_, errOld = store.GetIngressClass(oldIng, icConfig)
|
||||
classCur, errCur = store.GetIngressClass(curIng, icConfig)
|
||||
}
|
||||
if errOld != nil && errCur == nil {
|
||||
switch {
|
||||
case errOld != nil && errCur == nil:
|
||||
if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll {
|
||||
klog.InfoS("ignoring update for catch-all ingress because of --disable-catch-all", "ingress", klog.KObj(curIng))
|
||||
return
|
||||
|
@ -482,11 +485,11 @@ func New(
|
|||
|
||||
klog.InfoS("creating ingress", "ingress", klog.KObj(curIng), "ingressclass", classCur)
|
||||
recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync")
|
||||
} else if errOld == nil && errCur != nil {
|
||||
case errOld == nil && errCur != nil:
|
||||
klog.InfoS("removing ingress because of unknown ingressclass", "ingress", klog.KObj(curIng))
|
||||
ingDeleteHandler(old)
|
||||
return
|
||||
} else if errCur == nil && !reflect.DeepEqual(old, cur) {
|
||||
case errCur == nil && !reflect.DeepEqual(old, cur):
|
||||
if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll {
|
||||
klog.InfoS("ignoring update for catch-all ingress and delete old one because of --disable-catch-all", "ingress", klog.KObj(curIng))
|
||||
ingDeleteHandler(old)
|
||||
|
@ -494,7 +497,7 @@ func New(
|
|||
}
|
||||
|
||||
recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync")
|
||||
} else {
|
||||
default:
|
||||
klog.V(3).InfoS("No changes on ingress. Skipping update", "ingress", klog.KObj(curIng))
|
||||
return
|
||||
}
|
||||
|
@ -837,10 +840,10 @@ func hasCatchAllIngressRule(spec networkingv1.IngressSpec) bool {
|
|||
return spec.DefaultBackend != nil
|
||||
}
|
||||
|
||||
func checkBadAnnotationValue(annotations map[string]string, badwords string) error {
|
||||
func checkBadAnnotationValue(annotationMap map[string]string, badwords string) error {
|
||||
arraybadWords := strings.Split(strings.TrimSpace(badwords), ",")
|
||||
|
||||
for annotation, value := range annotations {
|
||||
for annotation, value := range annotationMap {
|
||||
if strings.HasPrefix(annotation, fmt.Sprintf("%s/", parser.AnnotationsPrefix)) {
|
||||
for _, forbiddenvalue := range arraybadWords {
|
||||
if strings.Contains(value, forbiddenvalue) {
|
||||
|
@ -999,7 +1002,7 @@ func (s *k8sStore) GetService(key string) (*corev1.Service, error) {
|
|||
return s.listers.Service.ByKey(key)
|
||||
}
|
||||
|
||||
func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) {
|
||||
func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error) {
|
||||
// First we try ingressClassName
|
||||
if !icConfig.IgnoreIngressClass && ing.Spec.IngressClassName != nil {
|
||||
iclass, err := s.listers.IngressClass.ByKey(*ing.Spec.IngressClassName)
|
||||
|
@ -1010,11 +1013,11 @@ func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressc
|
|||
}
|
||||
|
||||
// Then we try annotation
|
||||
if ingressclass, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok {
|
||||
if ingressclass != icConfig.AnnotationValue {
|
||||
if class, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok {
|
||||
if class != icConfig.AnnotationValue {
|
||||
return "", fmt.Errorf("ingress class annotation is not equal to the expected by Ingress Controller")
|
||||
}
|
||||
return ingressclass, nil
|
||||
return class, nil
|
||||
}
|
||||
|
||||
// Then we accept if the WithoutClass is enabled
|
||||
|
|
|
@ -44,29 +44,27 @@ import (
|
|||
|
||||
var pathPrefix networking.PathType = networking.PathTypePrefix
|
||||
|
||||
var DefaultClassConfig = &ingressclass.IngressClassConfiguration{
|
||||
var DefaultClassConfig = &ingressclass.Configuration{
|
||||
Controller: ingressclass.DefaultControllerName,
|
||||
AnnotationValue: ingressclass.DefaultAnnotationValue,
|
||||
WatchWithoutClass: false,
|
||||
}
|
||||
|
||||
var (
|
||||
commonIngressSpec = networking.IngressSpec{
|
||||
Rules: []networking.IngressRule{
|
||||
{
|
||||
Host: "dummy",
|
||||
IngressRuleValue: networking.IngressRuleValue{
|
||||
HTTP: &networking.HTTPIngressRuleValue{
|
||||
Paths: []networking.HTTPIngressPath{
|
||||
{
|
||||
Path: "/",
|
||||
PathType: &pathPrefix,
|
||||
Backend: networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
Name: "http-svc",
|
||||
Port: networking.ServiceBackendPort{
|
||||
Number: 80,
|
||||
},
|
||||
var commonIngressSpec = networking.IngressSpec{
|
||||
Rules: []networking.IngressRule{
|
||||
{
|
||||
Host: "dummy",
|
||||
IngressRuleValue: networking.IngressRuleValue{
|
||||
HTTP: &networking.HTTPIngressRuleValue{
|
||||
Paths: []networking.HTTPIngressPath{
|
||||
{
|
||||
Path: "/",
|
||||
PathType: &pathPrefix,
|
||||
Backend: networking.IngressBackend{
|
||||
Service: &networking.IngressServiceBackend{
|
||||
Name: "http-svc",
|
||||
Port: networking.ServiceBackendPort{
|
||||
Number: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -75,12 +73,15 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
const updateDummyHost = "update-dummy"
|
||||
|
||||
//nolint:gocyclo // Ignore function complexity error
|
||||
func TestStore(t *testing.T) {
|
||||
//TODO: move env definition to docker image?
|
||||
os.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin")
|
||||
// TODO: move env definition to docker image?
|
||||
t.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin")
|
||||
|
||||
pathPrefix = networking.PathTypePrefix
|
||||
|
||||
|
@ -92,7 +93,7 @@ func TestStore(t *testing.T) {
|
|||
|
||||
emptySelector, _ := labels.Parse("")
|
||||
|
||||
defer te.Stop() //nolint:errcheck
|
||||
defer te.Stop() //nolint:errcheck // Ignore the error
|
||||
|
||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
|
@ -230,7 +231,7 @@ func TestStore(t *testing.T) {
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
ni := ing.DeepCopy()
|
||||
ni.Spec.Rules[0].Host = "update-dummy"
|
||||
ni.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(ni, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error creating ingress: %v", err)
|
||||
|
@ -343,7 +344,7 @@ func TestStore(t *testing.T) {
|
|||
defer deleteIngress(invalidIngress, clientSet, t)
|
||||
|
||||
ni := ing.DeepCopy()
|
||||
ni.Spec.Rules[0].Host = "update-dummy"
|
||||
ni.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(ni, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error creating ingress: %v", err)
|
||||
|
@ -411,7 +412,7 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
ingressClassconfig := &ingressclass.IngressClassConfiguration{
|
||||
ingressClassconfig := &ingressclass.Configuration{
|
||||
Controller: ingressclass.DefaultControllerName,
|
||||
AnnotationValue: ingressclass.DefaultAnnotationValue,
|
||||
WatchWithoutClass: true,
|
||||
|
@ -463,7 +464,7 @@ func TestStore(t *testing.T) {
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
validIngressUpdated := validIngress1.DeepCopy()
|
||||
validIngressUpdated.Spec.Rules[0].Host = "update-dummy"
|
||||
validIngressUpdated.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(validIngressUpdated, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error updating ingress: %v", err)
|
||||
|
@ -542,7 +543,7 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
ingressClassconfig := &ingressclass.IngressClassConfiguration{
|
||||
ingressClassconfig := &ingressclass.Configuration{
|
||||
Controller: ingressclass.DefaultControllerName,
|
||||
AnnotationValue: ic,
|
||||
IngressClassByName: true,
|
||||
|
@ -581,7 +582,7 @@ func TestStore(t *testing.T) {
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
ingressUpdated := ing.DeepCopy()
|
||||
ingressUpdated.Spec.Rules[0].Host = "update-dummy"
|
||||
ingressUpdated.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(ingressUpdated, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error updating ingress: %v", err)
|
||||
|
@ -684,7 +685,7 @@ func TestStore(t *testing.T) {
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
invalidIngressUpdated := invalidIngress.DeepCopy()
|
||||
invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy"
|
||||
invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(invalidIngressUpdated, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error creating ingress: %v", err)
|
||||
|
@ -778,7 +779,7 @@ func TestStore(t *testing.T) {
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
invalidIngressUpdated := invalidIngress.DeepCopy()
|
||||
invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy"
|
||||
invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost
|
||||
_ = ensureIngress(invalidIngressUpdated, clientSet, t)
|
||||
if err != nil {
|
||||
t.Errorf("error creating ingress: %v", err)
|
||||
|
@ -1008,7 +1009,6 @@ func TestStore(t *testing.T) {
|
|||
if atomic.LoadUint64(&del) != 1 {
|
||||
t.Errorf("expected 1 events of type Delete but %v occurred", del)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("should create an ingress with a secret which does not exist", func(t *testing.T) {
|
||||
|
@ -1236,7 +1236,6 @@ func TestStore(t *testing.T) {
|
|||
if atomic.LoadUint64(&del) != 0 {
|
||||
t.Errorf("expected 0 events of type Delete but %v occurred", del)
|
||||
}
|
||||
|
||||
})
|
||||
// test add ingress with secret it doesn't exists and then add secret
|
||||
// check secret is generated on fs
|
||||
|
@ -1274,16 +1273,16 @@ func deleteNamespace(ns string, clientSet kubernetes.Interface, t *testing.T) {
|
|||
|
||||
func createIngressClass(clientSet kubernetes.Interface, t *testing.T, controller string) string {
|
||||
t.Helper()
|
||||
ingressclass := &networking.IngressClass{
|
||||
class := &networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("ingress-nginx-%v", time.Now().Unix()),
|
||||
//Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet
|
||||
// Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet
|
||||
},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: controller,
|
||||
},
|
||||
}
|
||||
ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), ingressclass, metav1.CreateOptions{})
|
||||
ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), class, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("error creating ingress class: %v", err)
|
||||
}
|
||||
|
@ -1299,7 +1298,7 @@ func deleteIngressClass(ic string, clientSet kubernetes.Interface, t *testing.T)
|
|||
}
|
||||
}
|
||||
|
||||
func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) string {
|
||||
func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
configMap := &v1.ConfigMap{
|
||||
|
@ -1308,51 +1307,47 @@ func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) st
|
|||
},
|
||||
}
|
||||
|
||||
cm, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
|
||||
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("error creating the configuration map: %v", err)
|
||||
}
|
||||
|
||||
return cm.Name
|
||||
}
|
||||
|
||||
func ensureIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress {
|
||||
func ensureIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress {
|
||||
t.Helper()
|
||||
ing, err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
|
||||
|
||||
newIngress, err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Update(context.TODO(), ing, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
if k8sErrors.IsNotFound(err) {
|
||||
t.Logf("Ingress %v not found, creating", ingress)
|
||||
t.Logf("Ingress %v not found, creating", ing)
|
||||
|
||||
ing, err = clientSet.NetworkingV1().Ingresses(ingress.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{})
|
||||
newIngress, err = clientSet.NetworkingV1().Ingresses(ing.Namespace).Create(context.TODO(), ing, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("error creating ingress %+v: %v", ingress, err)
|
||||
t.Fatalf("error creating ingress %+v: %v", ing, err)
|
||||
}
|
||||
|
||||
t.Logf("Ingress %+v created", ingress)
|
||||
return ing
|
||||
t.Logf("Ingress %+v created", ing)
|
||||
return newIngress
|
||||
}
|
||||
|
||||
t.Fatalf("error updating ingress %+v: %v", ingress, err)
|
||||
t.Fatalf("error updating ingress %+v: %v", ing, err)
|
||||
}
|
||||
|
||||
return ing
|
||||
return newIngress
|
||||
}
|
||||
|
||||
func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) {
|
||||
func deleteIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) {
|
||||
t.Helper()
|
||||
err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Delete(context.TODO(), ingress.Name, metav1.DeleteOptions{})
|
||||
|
||||
err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Delete(context.TODO(), ing.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("failed to delete ingress %+v: %v", ingress, err)
|
||||
t.Errorf("failed to delete ingress %+v: %v", ing, err)
|
||||
}
|
||||
|
||||
t.Logf("Ingress %+v deleted", ingress)
|
||||
t.Logf("Ingress %+v deleted", ing)
|
||||
}
|
||||
|
||||
// newStore creates a new mock object store for tests which do not require the
|
||||
// use of Informers.
|
||||
func newStore(t *testing.T) *k8sStore {
|
||||
func newStore() *k8sStore {
|
||||
return &k8sStore{
|
||||
listers: &Lister{
|
||||
// add more listers if needed
|
||||
|
@ -1369,7 +1364,7 @@ func newStore(t *testing.T) *k8sStore {
|
|||
}
|
||||
|
||||
func TestUpdateSecretIngressMap(t *testing.T) {
|
||||
s := newStore(t)
|
||||
s := newStore()
|
||||
|
||||
ingTpl := &networking.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -1431,7 +1426,9 @@ func TestUpdateSecretIngressMap(t *testing.T) {
|
|||
ing.ObjectMeta.SetAnnotations(map[string]string{
|
||||
parser.GetAnnotationWithPrefix("auth-secret"): "anotherns/auth",
|
||||
})
|
||||
s.listers.Ingress.Update(ing)
|
||||
if err := s.listers.Ingress.Update(ing); err != nil {
|
||||
t.Errorf("error updating the Ingress: %v", err)
|
||||
}
|
||||
s.updateSecretIngressMap(ing)
|
||||
|
||||
if l := s.secretIngressMap.Len(); l != 0 {
|
||||
|
@ -1456,7 +1453,7 @@ func TestUpdateSecretIngressMap(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListIngresses(t *testing.T) {
|
||||
s := newStore(t)
|
||||
s := newStore()
|
||||
invalidIngressClass := "something"
|
||||
validIngressClass := ingressclass.DefaultControllerName
|
||||
|
||||
|
@ -1586,7 +1583,7 @@ func TestWriteSSLSessionTicketKey(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
s := newStore(t)
|
||||
s := newStore()
|
||||
|
||||
cmap := &v1.ConfigMap{
|
||||
Data: map[string]string{
|
||||
|
|
|
@ -91,6 +91,8 @@ const (
|
|||
)
|
||||
|
||||
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
||||
//
|
||||
//nolint:gocyclo // Ignore function complexity error
|
||||
func ReadConfig(src map[string]string) config.Configuration {
|
||||
conf := map[string]string{}
|
||||
// we need to copy the configmap data because the content is altered
|
||||
|
@ -116,12 +118,12 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
luaSharedDicts := make(map[string]int)
|
||||
debugConnectionsList := make([]string, 0)
|
||||
|
||||
//parse lua shared dict values
|
||||
// parse lua shared dict values
|
||||
if val, ok := conf[luaSharedDictsKey]; ok {
|
||||
delete(conf, luaSharedDictsKey)
|
||||
lsd := splitAndTrimSpace(val, ",")
|
||||
for _, v := range lsd {
|
||||
v = strings.Replace(v, " ", "", -1)
|
||||
v = strings.ReplaceAll(v, " ", "")
|
||||
results := strings.SplitN(v, ":", 2)
|
||||
dictName := results[0]
|
||||
size := dictStrToKb(results[1])
|
||||
|
@ -196,7 +198,7 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
if ing_net.IsIPV6(ns) {
|
||||
bindAddressIpv6List = append(bindAddressIpv6List, fmt.Sprintf("[%v]", ns))
|
||||
} else {
|
||||
bindAddressIpv4List = append(bindAddressIpv4List, fmt.Sprintf("%v", ns))
|
||||
bindAddressIpv4List = append(bindAddressIpv4List, ns.String())
|
||||
}
|
||||
} else {
|
||||
klog.Warningf("%v is not a valid textual representation of an IP address", i)
|
||||
|
@ -250,7 +252,7 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
if val, ok := conf[globalAuthMethod]; ok {
|
||||
delete(conf, globalAuthMethod)
|
||||
|
||||
if len(val) != 0 && !authreq.ValidMethod(val) {
|
||||
if val != "" && !authreq.ValidMethod(val) {
|
||||
klog.Warningf("Global auth location denied - %v.", "invalid HTTP method")
|
||||
} else {
|
||||
to.GlobalExternalAuth.Method = val
|
||||
|
@ -286,7 +288,7 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
if val, ok := conf[globalAuthResponseHeaders]; ok {
|
||||
delete(conf, globalAuthResponseHeaders)
|
||||
|
||||
if len(val) != 0 {
|
||||
if val != "" {
|
||||
harr := splitAndTrimSpace(val, ",")
|
||||
for _, header := range harr {
|
||||
if !authreq.ValidHeader(header) {
|
||||
|
@ -385,8 +387,8 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
if val, ok := conf[debugConnections]; ok {
|
||||
delete(conf, debugConnections)
|
||||
for _, i := range splitAndTrimSpace(val, ",") {
|
||||
validIp := net.ParseIP(i)
|
||||
if validIp != nil {
|
||||
validIP := net.ParseIP(i)
|
||||
if validIP != nil {
|
||||
debugConnectionsList = append(debugConnectionsList, i)
|
||||
} else {
|
||||
_, _, err := net.ParseCIDR(i)
|
||||
|
@ -415,14 +417,14 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
|
||||
to.LuaSharedDicts = luaSharedDicts
|
||||
|
||||
config := &mapstructure.DecoderConfig{
|
||||
decoderConfig := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
WeaklyTypedInput: true,
|
||||
Result: &to,
|
||||
TagName: "json",
|
||||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
decoder, err := mapstructure.NewDecoder(decoderConfig)
|
||||
if err != nil {
|
||||
klog.Warningf("unexpected error merging defaults: %v", err)
|
||||
}
|
||||
|
@ -456,6 +458,7 @@ func filterErrors(codes []int) []int {
|
|||
return fa
|
||||
}
|
||||
|
||||
//nolint:unparam // Ignore `sep` always receives `,` error
|
||||
func splitAndTrimSpace(s, sep string) []string {
|
||||
f := func(c rune) bool {
|
||||
return strings.EqualFold(string(c), sep)
|
||||
|
@ -475,7 +478,7 @@ func dictStrToKb(sizeStr string) int {
|
|||
return -1
|
||||
}
|
||||
size, _ := strconv.Atoi(sizeMatch[1]) // validated already with regex
|
||||
if sizeMatch[2] == "" || strings.ToLower(sizeMatch[2]) == "m" {
|
||||
if sizeMatch[2] == "" || strings.EqualFold(sizeMatch[2], "m") {
|
||||
size *= 1024
|
||||
}
|
||||
return size
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue