Skip to main content

Control Access to GUI Elements

  • We should only expose services externally if we really need to.
  • If we need to access some service that is not available to everyone, we can simply use kubectl port-forward or kubectl proxy instead of exposing through a load balancer.

The Kubernetes Dashboard has many privileges in the cluster and Tesla was hacked in 2018 because of this.

  • It doesn't use RBAC or roles
  • Tesla's Kubernetes was exposed to the Internet, which is not a standard practice.

Kubectl Proxy and Kubectl Port-Forward

Proxy

  • Creates a proxy server between localhost and the Kubernetes API server.
  • Uses the connection configured in your kubeconfig and the credentials to communicate with the api server.
  • Allows accessing the Kubernetes API locally with HTTP and without authentication so it's comfortable to develop and debug.

alt text

Port Forward

  • Forwards connections from localhost on a specific port to a pod's port within the cluster.
  • More generic than kubectl proxy.
  • Can be used for TCP protocols and not just HTTP as in the case of kubectl proxy.

The pod IP is the cluster's internal IP. We have, for example, the Dashboard pod running in the cluster without being exposed externally.

  • If we have the api-server running inside a pod, we can also use port forward.

alt text

Kubernetes Dashboard

If we want to expose the dashboard to the Internet without using kubectl, we should take certain precautions such as using an Ingress (Nginx) and applying proper security such as authentication using LDAP or some other system.

Let's install the kubernetes dashboard.

root@cks-master:~# curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

root@cks-master:~# chmod 700 get_helm.sh

root@cks-master:~# ./get_helm.sh
Downloading https://get.helm.sh/helm-v3.15.4-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm

root@cks-master:~# helm version
version.BuildInfo{Version:"v3.15.4", GitCommit:"fa9efb07d9d8debbb4306d72af76a383895aa8c4", GitTreeState:"clean", GoVersion:"go1.22.6"}

root@cks-master:~# helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
"kubernetes-dashboard" has been added to your repositories
root@cks-master:~#
root@cks-master:~# helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard
Release "kubernetes-dashboard" does not exist. Installing it now.

Everything was created in the kubernetes-dashboard namespace, let's check.

root@cks-master:~# kubectl get -n kubernetes-dashboard svc,pod
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes-dashboard-api ClusterIP 10.98.57.37 <none> 8000/TCP 2m36s
service/kubernetes-dashboard-auth ClusterIP 10.109.32.254 <none> 8000/TCP 2m36s
service/kubernetes-dashboard-kong-manager NodePort 10.108.177.217 <none> 8002:32335/TCP,8445:32590/TCP 2m36s
service/kubernetes-dashboard-kong-proxy ClusterIP 10.105.243.208 <none> 443/TCP 2m36s
service/kubernetes-dashboard-metrics-scraper ClusterIP 10.96.106.246 <none> 8000/TCP 2m36s
service/kubernetes-dashboard-web ClusterIP 10.100.17.113 <none> 8000/TCP 2m36s

NAME READY STATUS RESTARTS AGE
pod/kubernetes-dashboard-api-9567bc759-mdvl5 1/1 Running 0 2m36s
pod/kubernetes-dashboard-auth-784d848dcb-p686b 1/1 Running 0 2m36s
pod/kubernetes-dashboard-kong-7696bb8c88-v4zk6 1/1 Running 0 2m36s
pod/kubernetes-dashboard-metrics-scraper-5485b64c47-9fkpb 1/1 Running 0 2m36s
pod/kubernetes-dashboard-web-84f8d6fff4-t4qgq 1/1 Running 0 2m36s

We need to pay attention:

  • Only expose to the internet if needed and if protected.
  • Restrict access using RBAC.
  • Enforce security.
  • Change the deployment arguments. Check dashboard arguments.

If we were to apply this as it is... We don't have an ingress, we don't have valid certificates, we don't have authentication yet. The way it is, it's not secure.

Let's do an analysis of the values.yaml we have in this helm.

root@cks-master:~# helm show values kubernetes-dashboard/kubernetes-dashboard

# General configuration shared across resources
app:
# Mode determines if chart should deploy a full Dashboard with all containers or just the API.
# - dashboard - deploys all the containers
# - api - deploys just the API
mode: 'dashboard'
image:
pullPolicy: IfNotPresent
pullSecrets: []
scheduling:
# Node labels for pod assignment
# Ref: https://kubernetes.io/docs/user-guide/node-selection/
nodeSelector: {}
security:
# Allow overriding csrfKey used by API/Auth containers.
# It has to be base64 encoded random 256 bytes string.
# If empty, it will be autogenerated.
csrfKey: ~
# SecurityContext to be added to pods
# To disable set the following configuration to null:
# securityContext: null
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
# ContainerSecurityContext to be added to containers
# To disable set the following configuration to null:
# containerSecurityContext: null
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
capabilities:
drop: ["ALL"]
# Pod Disruption Budget configuration
# Ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
podDisruptionBudget:
enabled: false
minAvailable: 0
maxUnavailable: 0
networkPolicy:
enabled: false
ingressDenyAll: false
# Raw network policy spec that overrides predefined spec
# Example:
# spec:
# egress:
# - ports:
# - port: 123
spec: {}

# Common labels & annotations shared across all deployed resources
labels: {}
annotations: {}
# Common priority class used for all deployed resources
priorityClassName: null
settings:
## Global dashboard settings
global:
# # Cluster name that appears in the browser window title if it is set
# clusterName: ""
# # Max number of items that can be displayed on each list page
# itemsPerPage: 10
# # Number of seconds between every auto-refresh of logs
# logsAutoRefreshTimeInterval: 5
# # Number of seconds between every auto-refresh of every resource. Set 0 to disable
# resourceAutoRefreshTimeInterval: 5
# # Hide all access denied warnings in the notification panel
# disableAccessDeniedNotifications: false
## Pinned resources that will be displayed in dashboard's menu
pinnedResources: []
# - kind: customresourcedefinition
# # Fully qualified name of a CRD
# name: prometheus.monitoring.coreos.com
# # Display name
# displayName: Prometheus
# # Is this CRD namespaced?
# namespaced: true
ingress:
enabled: false
hosts:
# Keep 'localhost' host only if you want to access Dashboard using 'kubectl port-forward ...' on:
# https://localhost:8443
- localhost
# - kubernetes.dashboard.domain.com
ingressClassName: internal-nginx
# Use only if your ingress controllers support default ingress classes.
# If set to true ingressClassName will be ignored and not added to the Ingress resources.
# It should fall back to using IngressClass marked as the default.
useDefaultIngressClass: false
# This will append our Ingress with annotations required by our default configuration.
# nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
# nginx.ingress.kubernetes.io/ssl-passthrough: "true"
# nginx.ingress.kubernetes.io/ssl-redirect: "true"
useDefaultAnnotations: true
pathType: ImplementationSpecific
# If path is not the default (/), rewrite-target annotation will be added to the Ingress.
# It allows serving Kubernetes Dashboard on a sub-path. Make sure that the configured path
# does not conflict with gateway route configuration.
path: /
issuer:
name: selfsigned
# Scope determines what kind of issuer annotation will be used on ingress resource
# - default - adds 'cert-manager.io/issuer'
# - cluster - adds 'cert-manager.io/cluster-issuer'
# - disabled - disables cert-manager annotations
scope: default
tls:
enabled: true
# If provided it will override autogenerated secret name
secretName: ""
labels: {}
annotations: {}
# Use the following toleration if Dashboard can be deployed on a tainted control-plane nodes
# - key: node-role.kubernetes.io/control-plane
# effect: NoSchedule
tolerations: []
affinity: {}

auth:
role: auth
image:
repository: docker.io/kubernetesui/dashboard-auth
tag: 1.1.3
scaling:
replicas: 1
revisionHistoryLimit: 10
containers:
ports:
- name: auth
containerPort: 8000
protocol: TCP
args: []
env: []
volumeMounts:
- mountPath: /tmp
name: tmp-volume
# TODO: Validate configuration
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
automountServiceAccountToken: true
volumes:
# Create on-disk volume to store exec logs (required)
- name: tmp-volume
emptyDir: {}
nodeSelector: {}
# Labels & annotations shared between API related resources
labels: {}
annotations: {}

# API deployment configuration
api:
role: api
image:
repository: docker.io/kubernetesui/dashboard-api
tag: 1.7.0
scaling:
replicas: 1
revisionHistoryLimit: 10
containers:
ports:
- name: api
containerPort: 8000
protocol: TCP
# Additional container arguments
# Full list of arguments: https://github.com/kubernetes/dashboard/blob/master/docs/common/arguments.md
# args:
# - --system-banner="Welcome to the Kubernetes Dashboard"
args: []
# Additional container environment variables
# env:
# - name: SOME_VAR
# value: 'some value'
env: []
# Additional volume mounts
# - mountPath: /kubeconfig
# name: dashboard-kubeconfig
# readOnly: true
volumeMounts:
# Create volume mount to store exec logs (required)
- mountPath: /tmp
name: tmp-volume
# TODO: Validate configuration
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
automountServiceAccountToken: true
# Additional volumes
# - name: dashboard-kubeconfig
# secret:
# defaultMode: 420
# secretName: dashboard-kubeconfig
volumes:
# Create on-disk volume to store exec logs (required)
- name: tmp-volume
emptyDir: {}
nodeSelector: {}
# Labels & annotations shared between API related resources
labels: {}
annotations: {}

# WEB UI deployment configuration
web:
role: web
image:
repository: docker.io/kubernetesui/dashboard-web
tag: 1.4.0
scaling:
replicas: 1
revisionHistoryLimit: 10
containers:
ports:
- name: web
containerPort: 8000
protocol: TCP
# Additional container arguments
# Full list of arguments: https://github.com/kubernetes/dashboard/blob/master/docs/common/arguments.md
# args:
# - --system-banner="Welcome to the Kubernetes Dashboard"
args: []
# Additional container environment variables
# env:
# - name: SOME_VAR
# value: 'some value'
env: []
# Additional volume mounts
# - mountPath: /kubeconfig
# name: dashboard-kubeconfig
# readOnly: true
volumeMounts:
# Create volume mount to store logs (required)
- mountPath: /tmp
name: tmp-volume
# TODO: Validate configuration
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
automountServiceAccountToken: true
# Additional volumes
# - name: dashboard-kubeconfig
# secret:
# defaultMode: 420
# secretName: dashboard-kubeconfig
volumes:
# Create on-disk volume to store exec logs (required)
- name: tmp-volume
emptyDir: {}
nodeSelector: {}
# Labels & annotations shared between WEB UI related resources
labels: {}
annotations: {}

### Metrics Scraper
### Container to scrape, store, and retrieve a window of time from the Metrics Server.
### refs: https://github.com/kubernetes/dashboard/tree/master/modules/metrics-scraper
metricsScraper:
enabled: true
role: metrics-scraper
image:
repository: docker.io/kubernetesui/dashboard-metrics-scraper
tag: 1.1.1
scaling:
replicas: 1
revisionHistoryLimit: 10
containers:
ports:
- containerPort: 8000
protocol: TCP
args: []
# Additional container environment variables
# env:
# - name: SOME_VAR
# value: 'some value'
env: []
# Additional volume mounts
# - mountPath: /kubeconfig
# name: dashboard-kubeconfig
# readOnly: true
volumeMounts:
# Create volume mount to store logs (required)
- mountPath: /tmp
name: tmp-volume
# TODO: Validate configuration
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
livenessProbe:
httpGet:
scheme: HTTP
path: /
port: 8000
initialDelaySeconds: 30
timeoutSeconds: 30
automountServiceAccountToken: true
# Additional volumes
# - name: dashboard-kubeconfig
# secret:
# defaultMode: 420
# secretName: dashboard-kubeconfig
volumes:
- name: tmp-volume
emptyDir: {}
nodeSelector: {}
# Labels & annotations shared between WEB UI related resources
labels: {}
annotations: {}

## Optional Metrics Server sub-chart configuration
## Enable this if you don't already have metrics-server enabled on your cluster and
## want to use it with dashboard metrics-scraper
## refs:
## - https://github.com/kubernetes-sigs/metrics-server
## - https://github.com/kubernetes-sigs/metrics-server/tree/master/charts/metrics-server
metrics-server:
enabled: false
args:
- --kubelet-preferred-address-types=InternalIP
- --kubelet-insecure-tls

## Required Kong sub-chart with DBless configuration to act as a gateway
## for our all containers.
kong:
enabled: true
## Configuration reference: https://docs.konghq.com/gateway/3.6.x/reference/configuration
env:
dns_order: LAST,A,CNAME,AAAA,SRV
plugins: 'off'
nginx_worker_processes: 1
ingressController:
enabled: false
dblessConfig:
configMap: kong-dbless-config
proxy:
type: ClusterIP
http:
enabled: false

## Optional Cert Manager sub-chart configuration
## Enable this if you don't already have cert-manager enabled on your cluster.
cert-manager:
enabled: false
installCRDs: true

## Optional Nginx Ingress sub-chart configuration
## Enable this if you don't already have nginx-ingress enabled on your cluster.
nginx:
enabled: false
controller:
electionID: ingress-controller-leader
ingressClassResource:
name: internal-nginx
default: false
controllerValue: k8s.io/internal-ingress-nginx
service:
type: ClusterIP

## Extra configurations:
## - manifests
## - predefined roles
## - prometheus
## - etc...
extras:
# Extra Kubernetes manifests to be deployed
# manifests:
# - apiVersion: v1
# kind: ConfigMap
# metadata:
# name: additional-configmap
# data:
# mykey: myvalue
manifests: []
serviceMonitor:
# Whether to create a Prometheus Operator service monitor.
enabled: false
# Here labels can be added to the serviceMonitor
labels: {}
# Here annotations can be added to the serviceMonitor
annotations: {}
# metrics.serviceMonitor.metricRelabelings Specify Metric Relabelings to add to the scrape endpoint
# ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#relabelconfig
metricRelabelings: []
# metrics.serviceMonitor.relabelings [array] Prometheus relabeling rules
relabelings: []
# ServiceMonitor connection scheme. Defaults to HTTPS.
scheme: https
# ServiceMonitor connection tlsConfig. Defaults to {insecureSkipVerify:true}.
tlsConfig:
insecureSkipVerify: true