76

I set up a k8s cluster using kubeadm (v1.18) on an Ubuntu virtual machine. Now I need to add an Ingress Controller. I decided for nginx (but I'm open for other solutions). I installed it according to the docs, section "bare-metal":

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.1/deploy/static/provider/baremetal/deploy.yaml

The installation seems fine to me:

kubectl get all -n ingress-nginx

NAME                                            READY   STATUS      RESTARTS   AGE
pod/ingress-nginx-admission-create-b8smg        0/1     Completed   0          8m21s
pod/ingress-nginx-admission-patch-6nbjb         0/1     Completed   1          8m21s
pod/ingress-nginx-controller-78f6c57f64-m89n8   1/1     Running     0          8m31s

NAME                                         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             NodePort    10.107.152.204   <none>        80:32367/TCP,443:31480/TCP   8m31s
service/ingress-nginx-controller-admission   ClusterIP   10.110.191.169   <none>        443/TCP                      8m31s

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           8m31s

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-78f6c57f64   1         1         1       8m31s

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           2s         8m31s
job.batch/ingress-nginx-admission-patch    1/1           3s         8m31s

However, when trying to apply a custom Ingress, I get the following error:

Error from server (InternalError): error when creating "yaml/xxx/xxx-ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post https://ingress-nginx-controller-admission.ingress-nginx.svc:443/extensions/v1beta1/ingresses?timeout=30s: Temporary Redirect

Any idea what could be wrong?

I suspected DNS, but other NodePort services are working as expected and DNS works within the cluster.

The only thing I can see is that I don't have a default-http-backend which is mentioned in the docs here. However, this seems normal in my case, according to this thread.

Last but not least, I tried as well the installation with manifests (after removing ingress-nginx namespace from previous installation) and the installation via Helm chart. It has the same result.

I'm pretty much a beginner on k8s and this is my playground-cluster. So I'm open to alternative solutions as well, as long as I don't need to set up the whole cluster from scratch.

Update: With "applying custom Ingress", I mean: kubectl apply -f <myIngress.yaml>

Content of myIngress.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /someroute/fittingmyneeds
        pathType: Prefix
        backend:
          serviceName: some-service
          servicePort: 5000
PhotonTamer
  • 1,047
  • 1
  • 8
  • 9
  • What do you mean by `when trying to apply a custom Ingress`? What exactly is your custom Ingress? – Wytrzymały Wiktor May 06 '20 at 11:23
  • @OhHiMark: I mean `kubectl apply -f . I added the information in the original post. – PhotonTamer May 06 '20 at 12:50
  • Looks like your Ingress.yaml is misconfigured. I see you are trying to use the rewrite annotation but there is no capture group defined. [Here](https://kubernetes.github.io/ingress-nginx/examples/rewrite/) you will find an explained example of how to use that annotation. Please take a look and let me know if that helps. – Wytrzymały Wiktor May 08 '20 at 08:14
  • any solution without deleting ValidatingWebhookConfiguration? – jarvo69 Apr 18 '21 at 14:35

16 Answers16

86

Another option you have is to remove the Validating Webhook entirely:

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

I found I had to do that on another issue, but the workaround/solution works here as well.

This isn't the best answer; the best answer is to figure out why this doesn't work. But at some point, you live with workarounds.

I'm installing on Docker for Mac, so I used the cloud rather than baremetal version:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml

Patrick Gardella
  • 3,971
  • 1
  • 17
  • 17
  • 2
    `kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission` resolved it for me on minikube 1.12 with k8s 1.18. – Fernando Correia Aug 20 '20 at 00:09
  • 1
    @Patrick Gardella this seems to be the practical solution for many people, as demonstrated by the many upvotes on your related post https://stackoverflow.com/a/62044090/1549918. I'm not even sure it's only a workaround,. – Chris Halcrow Sep 24 '20 at 06:04
  • 3
    Instead of deleting the Admission Webhook a more practical solution is to allow in the firewall all nodes to communicate with port 8443. https://kubernetes.github.io/ingress-nginx/deploy/ - "In case Network policies or additional firewalls, please allow access to port 8443." – Joao M Sep 01 '21 at 17:53
  • 1
    I really hate upvoting this. But it worked. – meh Feb 21 '22 at 22:21
60

In my case I'd mixed the installations up. I resolved the issue by executing the following steps:

$ kubectl get validatingwebhookconfigurations 

I iterated through the list of configurations received from the above steps and deleted the configuration using

$ `kubectl delete validatingwebhookconfigurations [configuration-name]`
J K
  • 1,597
  • 1
  • 12
  • 12
  • 11
    any solution without deleting ValidatingWebhookConfiguration? – jarvo69 Apr 18 '21 at 14:36
  • 1
    Thanks! I my case, `ingress-nginx-admission` had another name => nginx-ingress-nginx-admission. Got it with `kubectl get validatingwebhookconfigurations` – Pleymor May 05 '22 at 12:50
  • I did the mess to install a second ingress and it just mess up everything. When I removed the second ingress the validatingwebhookconfigurations was keeped in the cluster, so this solution solved my problem – Ziul Apr 17 '23 at 15:00
27

In my case I didn't need to delete the ValidatingWebhookConfiguration. The issue was that I was using a private cluster on GCP version 1.17.14-gke.1600. For gke private clusters, the validatingwebhook tries to access master node API at port 8443. In my case it was failing because I did not have that port allowed at the firewall. So the current workaround for that, as recommended by Google itself (but very poorly documented) is adding a Firewall rule on GCP, that will allow inbound (Ingress) TCP requests to your master node at port 8443, so that the other nodes within the cluster can reach the master for validatingwebhook API running on it with that very port.

As to how to create the rule, this is how I did it:

  1. Went to Firewall Rules and added a new one.
  2. At the field Network I selected the VPC from which my cluster is.
  3. Direction of traffic I set as Ingress
  4. Action on match to Allow
  5. Targets to Specified target tags
  6. The Target tags can be found on the master node details in a property called Network tags. To find it, I opened a new window, went to my cluster node pools, found the master node pool. Then entered one of the nodes to look for the Virtual Machine details. There I found Network Tags. Copied its value and went back to the Firewall Rule form.
  7. Pasted the copied network tag to the tag field
  8. At Protocols and ports, checked the Specified protocols and ports
  9. Then checked TCP and placed 8443
  10. Saved the rule and applied the manifest again.

NOTE: Most threads out there will say it's the port 9443. It may work. But I first attempted 8443 since it was reported to work on this thread. It worked for me so I didn't even try 9443.

Mauricio
  • 2,552
  • 2
  • 29
  • 43
  • 2
    Thank you for your great explanation! I encountered this problem with our private prod GKE cluster. I should notice that i only added the port 8443 to make it work. – Abdenour Keddar Mar 23 '21 at 15:44
  • Same for me. In my case, I had to open a security group on my custom install at AWS. – Joao M Sep 01 '21 at 17:53
  • 3
    Super helpful, thanks! I also only added port 8443 and it worked. You might want to add some info about what source to select when creating the firewall rule (I used the entire block I also specified in --master-ipv4-cidr= when creating the cluster). – otherguy Jan 04 '22 at 14:57
  • 2
    This is the correct answer. Better filter source for master-CIDR. – motobói Jul 11 '22 at 15:42
  • 1
    Thank you, Mauricio, your instruction helped me a lot. Much appreciated. – Stanislav Hordiyenko Aug 10 '23 at 06:42
19

Might be because of a previous nginx-ingress-controller configuration.
You can try to run the following command -

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
Gilad Sharaby
  • 910
  • 9
  • 12
  • Thanks. Your comment probably explains my situation. I managed to install some older ingress controller version thanks to some non-update documentation (or not paying attention). – ilvez May 09 '22 at 13:34
  • This worked for me. I had a previous installation of ingress-nginx, then updated docker and had to reinstall the ingress-nginx, after that I was getting the error, but with this command I was able to get rid of the previous nginx-ingress controller configuration – Eric Guzman May 25 '22 at 12:09
12

I've solved this issue. The problem was that you use Kubernetes version 1.18, but the ValidatingWebhookConfiguration in current ingress-Nginx uses the oldest API; see the doc: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites

Ensure that the Kubernetes cluster is at least as new as v1.16 (to use admissionregistration.k8s.io/v1), or v1.9 (to use admissionregistration.k8s.io/v1beta1).

And in current yaml :

 # Source: ingress-nginx/templates/admission-webhooks/validating-webhook.yaml
    # before changing this value, check the required kubernetes version
    # https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites
apiVersion: admissionregistration.k8s.io/v1beta1

and in rules :

apiVersions:
          - v1beta1

So you need to change it on v1 :

apiVersion: admissionregistration.k8s.io/v1

and add rule -v1 :

apiVersions:
          - v1beta1
          - v1

After you change it and redeploy -your custom ingress service will deploy sucessfull

oz123
  • 27,559
  • 27
  • 125
  • 187
  • 1
    In order to know what version of `admissionregistration.k8s.io` is compatible with your setup, use `kubectl api-versions | grep admissionregistration` – Lucas Cimon Dec 17 '20 at 10:35
  • this is great, has saved me after way too long trying to troubleshoot. Thanks Oleg. – Brett Mar 24 '21 at 21:31
  • That seems to have solved my problem mate. Thanks! – davidfm Nov 12 '21 at 14:01
  • Oleg, in "or v1.9 (to use admissionregistration.k8s.io/v1beta1)", did you mean to write v1.19? v1.24 seems to be the newest K8s version as I'm writing this. – David Wesby Jun 09 '22 at 14:34
9

Finally, I managed to run Ingress Nginx properly by changing the way of installation. I still don't understand why the previous installation didn't work, but I'll share nevertheless the solution along with some more insights into the original problem.

Solution

Uninstall ingress nginx: Delete the ingress-nginx namespace. This does not remove the validating webhook configuration - delete this one manually. Then install MetalLB and install ingress nginx again. I now used the version from the Helm stable repo. Now everything works as expected. Thanks to Long on the kubernetes slack channel!

Some more insights into the original problem

The yamls provided by the installation guide contain a ValidatingWebHookConfiguration:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.0.3
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.32.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
  name: ingress-nginx-admission
  namespace: ingress-nginx
webhooks:
  - name: validate.nginx.ingress.kubernetes.io
    rules:
      - apiGroups:
          - extensions
          - networking.k8s.io
        apiVersions:
          - v1beta1
        operations:
          - CREATE
          - UPDATE
        resources:
          - ingresses
    failurePolicy: Fail
    clientConfig:
      service:
        namespace: ingress-nginx
        name: ingress-nginx-controller-admission
        path: /extensions/v1beta1/ingresses

Validation is performed whenever I create or update an ingress (the content of my ingress.yaml doesn't matter). The validation failed, because when calling the service, the response is a Temporary Redirect. I don't know why. The corresponding service is:

apiVersion: v1
kind: Service
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.0.3
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.32.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller-admission
  namespace: ingress-nginx
spec:
  type: ClusterIP
  ports:
    - name: https-webhook
      port: 443
      targetPort: webhook
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/component: controller

The pod matching the selector comes from this deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.0.3
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.32.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  revisionHistoryLimit: 10
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.32.0
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
          args:
            - /nginx-ingress-controller
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=ingress-nginx/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 101
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
            - name: webhook
              containerPort: 8443
              protocol: TCP
          volumeMounts:
            - name: webhook-cert
              mountPath: /usr/local/certificates/
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 90Mi
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
        - name: webhook-cert
          secret:
            secretName: ingress-nginx-admission

Something in this validation chain goes wrong. Would be interesting to know, what and why, but I can continue working with my MetalLB solution. Note that this solution does not contain a validating webhook at all.

PhotonTamer
  • 1,047
  • 1
  • 8
  • 9
6

I am not sure if this helps this late, but might it be, that your cluster was behind proxy? Because in that case you have to have no_proxy configured correctly. Specifically, it has to include .svc,.cluster.local otherwise validation webhook requests such as https://ingress-nginx-controller-admission.ingress-nginx.svc:443/extensions/v1beta1/ingresses?timeout=30s will be routed via proxy server (note that .svc in the URL).

I had exactly this issue and adding .svc into no_proxy variable helped. You can try this out quickly by modifying /etc/kubernetes/manifests/kube-apiserver.yaml file which will in turn automatically recreate your kubernetes api server pod.

This is not the case just for ingress validation, but also for other things that might refer URL in your cluster ending with .svc or .namespace.svc.cluster.local (i.e. see this bug)

petermicuch
  • 116
  • 1
  • 3
  • 1
    And BTW - I would not disable validation webhook for ingress resources. They are there for reason and can prevent your controller going completely down by someone applying corrupted ingress (not in terms of syntax, but runtime issues). Then all applications behind this ingress controller will become unavailable. – petermicuch Sep 22 '21 at 18:17
  • Indeed, I am behind a proxy. Your solutions seems clean and works. Thank you! – PhotonTamer Sep 24 '21 at 09:42
  • @petermicuch, kindly please elaborate more on how todo that. I tried add --> - --no_proxy=".svc" , restart the master node result on my kubectl can not access it. – Bino Oetomo Jul 22 '22 at 10:19
5

On a baremetal cluster, I disabled the admissionWebhooks during the Helm3 install:

kubectl create ns ingress-nginx

helm install [RELEASE_NAME] ingress-nginx/ingress-nginx -n ingress-nginx --set controller.admissionWebhooks.enabled=false

h q
  • 1,168
  • 2
  • 10
  • 23
4

In my case, it was the AWS EKS module, which now comes with harden security group. but nginx-ingress requires the cluster to communicate with the ingress controller so I have to whitelist below port in the node security group

  node_security_group_additional_rules = {
    cluster_to_node = {
      description      = "Cluster to ingress-nginx webhook"
      protocol         = "-1"
      from_port        = 8443
      to_port          = 8443
      type             = "ingress"
      source_cluster_security_group = true
    }
  }

input_node_security_group_additional_rules

Adiii
  • 54,482
  • 7
  • 145
  • 148
  • Can you please share how to do this from AWS UI? I do not see type as ingress there and cutom protocol does not allow port. – Ishika Jain Dec 24 '22 at 20:42
  • 1
    from AWS UI, you can click on any worker node, security -> security group and open the desired port. – Adiii Dec 29 '22 at 02:07
  • If anybody is following the Official guide of Nginx Ingress (https://kubernetes.github.io/ingress-nginx/deploy/#aws) and using Terraform, this is the solution. – johnny68 May 24 '23 at 10:00
2

If using terraform and helm disable the Validating Webhook

resource "helm_release" "nginx_ingress" {

...

  set {
    name  = "controller.admissionWebhooks.enabled"
    value = "false"
  }

...

}
philo
  • 121
  • 1
  • 9
1

I had this error. Basically I have a script installing the nginx controller with helm; the script then immediately installs an application that uses ingress, also with helm. That app install failed, just the ingress part.

Solution was to wait 60s after the install of the nginx, to give the WebAdmissionHook time to come up and be ready.

0

what worked for me was to increase the timeout while waiting for ingress to come up.

MichaelMoser
  • 3,172
  • 1
  • 26
  • 26
0

I was bringing up a cluster with a known-good configuration and another had been created just last week in essentially the same way. And my error message was a little more specific about what failed in the webhook :

│ Error: Failed to create Ingress
'auth-system/alertmanager-oauth2-proxy' 
because: Internal error occurred: failed calling webhook
"validate.nginx.ingress.kubernetes.io": Post
"https://nginx-nginx-ingress-controller-controller-admission.ingress-nginx.svc:443/networking/v1beta1/ingresses?timeout=10s":
x509: certificate signed by unknown authority

It turns out that in my many configs, one of them had a typo in the DNS names input to nginx creation. So nginx thought it had one domain name, but it got a certificate for a slightly different dns name, which caused the validating web hook to fail.

The solution was not to delete the hook, but to address the underlying config problem in nginx dns so that it matched its X.509 certificate domain.

0

just use v1 instead v1beta1 in deploy.yaml

Soheil
  • 1
  • 1
0

This is a solution for those using GKE cluster.

I tested two ways to fix this issue.

  • Terraform
  • GCP Console

Terraform

resource "google_compute_firewall" "validate-nginx" {
  project = "${YOUR_PROJECT_ID}"
  name    = "access-master-to-validatenginx"
  network = "${YOUR_NETWORK}"
  
  allow {
    protocol = "tcp"
    ports    = ["8443"]
  }

  target_tags   = ["${NODE_NETWORK_TAG}"]
  source_ranges = ["${CONTROL_PLANE_ADDRESS_RANGE}"]
}

GCP Console

enter image description here

Xirehat
  • 1,155
  • 1
  • 8
  • 20
0

To add a terraform example for GCP, extending @mauricio

resource "google_container_cluster" "primary" {
...
}


resource "google_compute_firewall" "validate_nginx" {
  project = local.project
  name    = "validate-nginx"
  network = google_compute_network.vpc.name
  allow {
    protocol = "tcp"
    ports    = ["8443"]
  }
  direction = "INGRESS"
  source_ranges = [google_container_cluster.primary.private_cluster_config[0].master_ipv4_cidr_block]
}
110100100
  • 169
  • 4