Repository: canonical/microk8s

Issue:
[dashboard] should be exposed via ingress
When running microk8s on the server, rather than doing port forwarding it should be possible to access the dashboard via ingress (similar to kubeflow dashboard)


Discussion:
Thank you for this request, it is brought up frequently. We need to address the need for easy access to the dashboard in a secure way. Until we do so, to access the dashboard have a look at the MicroK8s docs [1] and the dashboard upstream wiki [2]

[1] https://microk8s.io/docs/addon-dashboard
[2] https://github.com/kubernetes/dashboard/blob/master/docs/user/accessing-dashboard/1.7.x-and-above.md
So it's 2020 and this still hasn't been solved yet?
Enable the ingress addon:
`microk8s enable ingress`

and create an ingress ressource:
```
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  name: dashboard
  namespace: kube-system
spec:
  rules:
  - http:
      paths:
      - path: "/dashboard/?(.*)"
        pathType: Prefix
        backend:
          service:
            name: kubernetes-dashboard
            port:
              number: 443
```

Now you can access the dashboard with `http://<Microk8s-IP>/dashboard/`
> Enable the ingress addon:
> `microk8s enable ingress`
> 
> and create an ingress ressource:
> 
> ```
> ---
> apiVersion: networking.k8s.io/v1
> kind: Ingress
> metadata:
>   annotations:
>     kubernetes.io/ingress.class: nginx
>     nginx.ingress.kubernetes.io/rewrite-target: /$1
>     nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
>   name: dashboard
>   namespace: kube-system
> spec:
>   rules:
>   - http:
>       paths:
>       - path: "/dashboard/?(.*)"
>         pathType: Prefix
>         backend:
>           service:
>             name: kubernetes-dashboard
>             port:
>               number: 443
> ```
> 
> Now you can access the dashboard with `http://<Microk8s-IP>/dashboard/`

Following these instructions I can access http://<Microk8s-IP>/dashboard/ but the page doesn't load correctly because the assets (styling, js, images) cannot be resolved correctly (404).

![Screenshot from 2020-12-07 20-38-37](https://user-images.githubusercontent.com/321040/101385128-6988f800-38cc-11eb-895b-f76df6182567.png)

Manually appending `/dashboard/` to the asset path fixes this issue. Any workaround?

This works for me (microk8s 1.19):

```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dashboard
  namespace: kube-system
  annotations:
    # use the shared ingress-nginx
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/dashboard)$ $1/ redirect;
spec:
  # https://kubernetes.io/docs/concepts/services-networking/ingress/
  # https://kubernetes.github.io/ingress-nginx/user-guide/tls/
  rules:
  - http:
      paths:
      - path: /dashboard(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: kubernetes-dashboard
            port:
              number: 443
```
Thanks Rom888, works for me too. Other than in my microk8s I need to use Ingress class: public
```
I1225 18:15:43.940765       6 flags.go:205] Watching for Ingress class: public
W1225 18:15:43.940833       6 flags.go:208] Only Ingresses with class "public" will be processed by this Ingress controller
```
Stop working after upgrading to 1.20
Empty ADDRESS:
```
kubectl get ing -n kube-system
NAME        CLASS    HOSTS   ADDRESS   PORTS   AGE
dashboard   <none>   *                 80      27m
```
@wuzhuoqing where should it be specified: "Ingress class: public"?
`kubernetes.io/ingress.class: nginx` change to public. @Rom888 
This isn't working in my 1.20.2 version:

```
root@mkube1:~# cat ingress-dashboard.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dashboard
  namespace: ingress
  annotations:
    # use the shared ingress-nginx
    kubernetes.io/ingress.class: public
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/dashboard)$ $1/ redirect;
spec:
  # https://kubernetes.io/docs/concepts/services-networking/ingress/
  # https://kubernetes.github.io/ingress-nginx/user-guide/tls/
  rules:
  - http:
      paths:
      - path: /dashboard(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: kubernetes-dashboard
            port:
              number: 443

root@mkube1:~# microk8s kubectl apply -f ingress-dashboard.yaml
ingress.networking.k8s.io/dashboard created
root@mkube1:~# microk8s kubectl get ing -A
NAMESPACE     NAME        CLASS    HOSTS   ADDRESS   PORTS   AGE
kube-system   dashboard   <none>   *                 80      11s

```

Last lines of the ingress container logs:
```
I0225 21:04:04.660923       6 event.go:278] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"kube-system", Name:"dashboard", UID:"6a654f27-e98d-4c64-87be-03bbdf621ee5", APIVersion:"networking.k8s.io/v1beta1", ResourceVersion:"8583555", FieldPath:""}): type: 'Normal' reason: 'CREATE' Ingress kube-system/dashboard
I0225 21:04:04.661260       6 controller.go:141] Configuration changes detected, backend reload required.
I0225 21:04:04.745626       6 controller.go:157] Backend successfully reloaded.
I0225 21:04:28.628083       6 status.go:275] updating Ingress kube-system/dashboard status from [] to [{127.0.0.1 }]
I0225 21:04:28.641134       6 event.go:278] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"kube-system", Name:"dashboard", UID:"6a654f27-e98d-4c64-87be-03bbdf621ee5", APIVersion:"networking.k8s.io/v1beta1", ResourceVersion:"8583595", FieldPath:""}): type: 'Normal' reason: 'UPDATE' Ingress kube-system/dashboard
```
Browser gets nothing by Connection Refused errors.

What am I doing wrong here?
> This isn't working in my 1.20.2 version:
> 
> ```
> root@mkube1:~# cat ingress-dashboard.yaml
> apiVersion: networking.k8s.io/v1
> kind: Ingress
> metadata:
>   name: dashboard
>   namespace: ingress
>   annotations:
>     # use the shared ingress-nginx
>     kubernetes.io/ingress.class: public
>     nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
>     nginx.ingress.kubernetes.io/rewrite-target: /$2
>     nginx.ingress.kubernetes.io/configuration-snippet: |
>       rewrite ^(/dashboard)$ $1/ redirect;
> spec:
>   # https://kubernetes.io/docs/concepts/services-networking/ingress/
>   # https://kubernetes.github.io/ingress-nginx/user-guide/tls/
>   rules:
>   - http:
>       paths:
>       - path: /dashboard(/|$)(.*)
>         pathType: ImplementationSpecific
>         backend:
>           service:
>             name: kubernetes-dashboard
>             port:
>               number: 443
> 
> root@mkube1:~# microk8s kubectl apply -f ingress-dashboard.yaml
> ingress.networking.k8s.io/dashboard created
> root@mkube1:~# microk8s kubectl get ing -A
> NAMESPACE     NAME        CLASS    HOSTS   ADDRESS   PORTS   AGE
> kube-system   dashboard   <none>   *                 80      11s
> ```
> 
> Last lines of the ingress container logs:
> 
> ```
> I0225 21:04:04.660923       6 event.go:278] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"kube-system", Name:"dashboard", UID:"6a654f27-e98d-4c64-87be-03bbdf621ee5", APIVersion:"networking.k8s.io/v1beta1", ResourceVersion:"8583555", FieldPath:""}): type: 'Normal' reason: 'CREATE' Ingress kube-system/dashboard
> I0225 21:04:04.661260       6 controller.go:141] Configuration changes detected, backend reload required.
> I0225 21:04:04.745626       6 controller.go:157] Backend successfully reloaded.
> I0225 21:04:28.628083       6 status.go:275] updating Ingress kube-system/dashboard status from [] to [{127.0.0.1 }]
> I0225 21:04:28.641134       6 event.go:278] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"kube-system", Name:"dashboard", UID:"6a654f27-e98d-4c64-87be-03bbdf621ee5", APIVersion:"networking.k8s.io/v1beta1", ResourceVersion:"8583595", FieldPath:""}): type: 'Normal' reason: 'UPDATE' Ingress kube-system/dashboard
> ```
> 
> Browser gets nothing by Connection Refused errors.
> 
> What am I doing wrong here?

check your TCP port.  You appear to have an ingress listening on port 80 but you have HTTPS setup for your listener. 
> `kubernetes.io/ingress.class: nginx` change to public. @Rom888

Hi there, 

thanks for posting the ingress configuration file. 
I followed, however on WSL2 (windows 10). It does looks okay:

`$ microk8s kubectl get ingress -n kube-system`
`NAME        CLASS    HOSTS   ADDRESS     PORTS   AGE`
`dashboard   <none>   *       127.0.0.1   80      53s`

However, neither "https://127.0.0.1/dashboard" nor "http://127.0.0.1/dashboard" work. ===> 127.0.0.1 refused to connect.

What did I miss? Is this WSL2 thing?

I would really appreciate your help. 

many thanks!!!


Resolving patch:
diff --git a/scripts/wrappers/status.py b/scripts/wrappers/status.py
--- a/scripts/wrappers/status.py
+++ b/scripts/wrappers/status.py
@@ -154,7 +154,8 @@ def get_status(available_addons, isReady):
     enabled = []
     disabled = []
     if isReady:
-        kube_output = kubectl_get("all")
+        # 'all' does not include ingress
+        kube_output = kubectl_get("all,ingress")
         cluster_output = kubectl_get_clusterroles()
         kube_output = kube_output + cluster_output
         for addon in available_addons:


Test patch:
diff --git a/tests/test-addons.py b/tests/test-addons.py
--- a/tests/test-addons.py
+++ b/tests/test-addons.py
@@ -6,6 +6,7 @@
 
 from validators import (
     validate_dns_dashboard,
+    validate_dashboard_ingress,
     validate_storage,
     validate_ingress,
     validate_ambassador,
@@ -126,6 +127,12 @@ def test_basic(self):
         microk8s_enable("dashboard")
         print("Validating dashboard")
         validate_dns_dashboard()
+        print("Enabling dashboard-ingress")
+        microk8s_enable("dashboard-ingress")
+        print("Validating dashboard-ingress")
+        validate_dashboard_ingress()
+        print("Disabling dashboard-ingress")
+        microk8s_disable("dashboard-ingress")
         print("Enabling storage")
         microk8s_enable("storage")
         print("Validating storage")
diff --git a/tests/validators.py b/tests/validators.py
--- a/tests/validators.py
+++ b/tests/validators.py
@@ -42,6 +42,29 @@ def validate_dns_dashboard():
     assert attempt > 0
 
 
+def validate_dashboard_ingress():
+    """
+    Validate the ingress for dashboard addon by trying to access the kubernetes dashboard
+    using ingress ports. The dashboard will return HTTP 200 and HTML indicating that it is
+    up and running.
+    """
+    service_ok = False
+    attempt = 50
+    while attempt >= 0:
+        try:
+            resp = requests.get(
+                "https://kubernetes-dashboard.127.0.0.1.nip.io/#/login", verify=False
+            )
+            if resp.status_code == 200 and "Kubernetes Dashboard" in resp.content.decode("utf-8"):
+                service_ok = True
+                break
+        except requests.RequestException:
+            time.sleep(5)
+            attempt -= 1
+
+    assert service_ok
+
+
 def validate_storage():
     """
     Validate storage by creating a PVC.