diff --git a/kubernetes/README.md b/kubernetes/README.md index 3c7433d..a721ed8 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -1,5 +1,16 @@ -Setup K3s Kubernetes Cluster -=================================== +# Setup K3s Kubernetes Cluster + +# Configure Traefik with extra values + +The Traefik ingress controller is deployed along with K3s. To modify the +default values, + +```bash +# k3s still uses traefik V2 +helm upgrade traefik traefik/traefik \ + -n kube-system -f traefik/traefik-values.yaml \ + --version 22.1.0 +``` # Configure Cert Manager for automating SSL certificate handling @@ -38,6 +49,7 @@ export KUBE_EDITOR=nvim # Change the forward section with . 1.1.1.1 1.0.0.1 kubectl -n kube-system edit configmap coredns ``` + Next, deploy the ClusterIssuer, WildcardCert, and secrets using helm ```bash @@ -66,10 +78,10 @@ kubectl get certificateRequest -n cert-manager kubectl describe challenges -n cert-manager kubectl describe orders -n cert-manager ``` + Alternatively, it is possible to generate service specific certs in desired namespaces by deploying the Certificate resource in the namespace. - # Deploy Private Docker Registry Create a new namespace called docker-registry and deploy the private @@ -100,7 +112,6 @@ helm install registry docker-registry-helm-chart/ \ --atomic ``` - # Deploy Portfolio Website from Private Docker Registry First, create a secret to access the private docker registry. Then copy the @@ -123,7 +134,6 @@ envsubst < my-portfolio/portfolioManifest.yaml | \ kubectl apply -n my-portfolio -f - ``` - # Expose External Services via Traefik Ingress Controller External services hosted outside the kubernetes cluster can be exposed using @@ -144,14 +154,13 @@ envsubst < external-service/proxmox.yaml | \ kubectl apply -n external-services -f - ``` - # Create Shared NFS Storage for Plex and Jellyfin A 1TB NVME SSD is mounted to one of the original homelab VMs. This serves as an NFS mount for all k3s nodes to use as shared storage for plex and jellyfin containers. -## On the host VM: +## On the host VM: ```bash sudo apt update @@ -176,7 +185,8 @@ sudo systemctl start nfs-kernel-server sudo systemctl enable nfs-kernel-server ``` -## On all the K3s VMs: +## On all the K3s VMs: + ``` sudo apt install nfs-common sudo mkdir /mnt/media @@ -187,7 +197,6 @@ sudo mount 192.168.1.113:/media/flexdrive /mnt/media sudo umount /mnt/media ``` - # Deploy Jellyfin Container in K3s Jellyfin is a media server that can be used to organize, play, and stream @@ -226,7 +235,6 @@ kubectl exec -it temp-pod -n media -- bash cp -r /mnt/source/* /mnt/destination/ ``` - # Create Storage Solution Longhorn is a distributed block storage solution for Kubernetes that is built @@ -265,7 +273,8 @@ kubectl -n longhorn-system edit svc longhorn-frontend kubectl -n longhorn-system get nodes.longhorn.io kubectl -n longhorn-system edit nodes.longhorn.io -``` + +```` Add the following block under disks for all nodes: ```bash @@ -277,7 +286,7 @@ Add the following block under disks for all nodes: path: /mnt/longhorn # Specify the new mount path storageReserved: 0 # Adjust storageReserved if needed tags: [] -``` +```` ## Setting the number of replicas @@ -290,7 +299,6 @@ kubectl edit configmap -n longhorn-system longhorn-storageclass set the numberOfReplicas: "1" ``` - # Configure AdGuard Adblocker AdGuard is deployed in the K3S cluster for network ad protection. @@ -312,7 +320,6 @@ helm install adguard \ --atomic adguard-helm-chart ``` - # Pocketbase Database and Authentication Backend Pocketbase serves as the database and authentication backend for @@ -371,7 +378,6 @@ qBittorrent pod: curl ipinfo.io ``` - # PostgreSQL Database The PostgreSQL database uses the bitnami postgres helm chart with one primary @@ -481,7 +487,6 @@ and set the replicas to the desired number. kubectl edit statefulset gitea-act-runner -n gitea ``` - ## Authentication Middleware Configuration for Traefik Ingress Controller The Traefik Ingress Controller provides robust authentication capabilities @@ -511,3 +516,29 @@ Following middleware deployment, the authentication must be enabled by adding th ``` traefik.ingress.kubernetes.io/router.middlewares: my-portfolio-basic-auth@kubernetescrd ``` + +# LLDAP Authentication Server + +LDAP is a protocol used to access and maintain distributed directory information. +To provide central authentication for all services, an LDAP server is deployed in the +k3s cluster. LLDAP is a lightweight LDAP server that is easy to deploy and manage. +The LLDAP server is deployed using the helm chart and is accessible via the ingress +controller. + +```bash +source .env + +kubectl create namespace ldap +kubectl get secret wildcard-cert-secret --namespace=cert-manager -o yaml \ + | sed 's/namespace: cert-manager/namespace: ldap/' | kubectl apply -f - + +helm install ldap \ + lldap-helm-chart/ \ + --set ingress.hosts.host=$LDAP_HOST \ + --set ingress.tls[0].hosts[0]=$DNSNAME \ + --set secret.lldapUserName=$LLDAP_ADMIN_USER \ + --set secret.lldapJwtSecret=$LLDAP_JWT_SECRET \ + --set secret.lldapUserPass=$LLDAP_ADMIN_PASSWORD \ + --atomic \ + -n ldap +``` diff --git a/kubernetes/lldap-helm-chart/Chart.yaml b/kubernetes/lldap-helm-chart/Chart.yaml new file mode 100644 index 0000000..36fcbed --- /dev/null +++ b/kubernetes/lldap-helm-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: lldap-chart +description: lldap - Light LDAP implementation for authentication +type: application +version: 0.1.0 +appVersion: "latest" diff --git a/kubernetes/lldap-helm-chart/templates/_helpers.tpl b/kubernetes/lldap-helm-chart/templates/_helpers.tpl new file mode 100644 index 0000000..f6dec88 --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "lldap-chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "lldap-chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "lldap-chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "lldap-chart.labels" -}} +helm.sh/chart: {{ include "lldap-chart.chart" . }} +{{ include "lldap-chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "lldap-chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "lldap-chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "lldap-chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "lldap-chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/kubernetes/lldap-helm-chart/templates/deployment.yaml b/kubernetes/lldap-helm-chart/templates/deployment.yaml new file mode 100644 index 0000000..ada75d2 --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/deployment.yaml @@ -0,0 +1,99 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lldap + namespace: {{ .Values.namespace }} + labels: + app: lldap + annotations: +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: lldap + strategy: + type: Recreate + template: + metadata: + labels: + app: lldap + annotations: + spec: + containers: + - name: lldap + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + + env: + - name: GID + value: "{{ .Values.env.GID }}" + - name: LLDAP_JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.secret.name }} + key: lldap-jwt-secret + - name: LLDAP_LDAP_BASE_DN + valueFrom: + secretKeyRef: + name: {{ .Values.secret.name }} + key: base-dn + - name: LLDAP_LDAP_USER_DN + valueFrom: + secretKeyRef: + name: {{ .Values.secret.name }} + key: lldap-ldap-user-name + - name: LLDAP_LDAP_USER_PASS + valueFrom: + secretKeyRef: + name: {{ .Values.secret.name }} + key: lldap-ldap-user-pass + - name: TZ + value: "{{ .Values.env.TZ }}" + - name: UID + value: "{{ .Values.env.UID }}" + {{- if .Values.extraEnv}} + {{- toYaml .Values.extraEnv | nindent 12}} + {{- end }} + ports: + - containerPort: 3890 + - containerPort: 6360 + - containerPort: 17170 + volumeMounts: + {{- if .Values.persistence.enabled }} + - mountPath: /data + name: lldap-data + {{- end }} + + {{- if .Values.extraVolumeMounts}} + {{- toYaml .Values.extraVolumeMounts | nindent 12}} + {{- end }} + volumes: + {{- if .Values.persistence.enabled}} + - name: lldap-data + persistentVolumeClaim: + claimName: lldap-data + {{- end }} + + {{- if .Values.extraVolumes}} + {{- toYaml .Values.extraVolumes | nindent 8}} + {{- end }} + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/kubernetes/lldap-helm-chart/templates/ingress.yaml b/kubernetes/lldap-helm-chart/templates/ingress.yaml new file mode 100644 index 0000000..4ae5504 --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/ingress.yaml @@ -0,0 +1,38 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.ingress.name | quote }} + namespace: {{ .Values.namespace | quote }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.ingress.labels }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + rules: + - host: {{ .Values.ingress.hosts.host | quote }} + http: + paths: + - path: {{ .Values.ingress.hosts.paths.path | quote }} + pathType: {{ .Values.ingress.hosts.paths.pathType | default "Prefix" | quote }} + backend: + service: + name: {{ $.Values.service.webui.name | quote }} + port: + number: {{ $.Values.service.webui.ports.port | default 17170 }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName | quote }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/lldap-helm-chart/templates/pvc.yaml b/kubernetes/lldap-helm-chart/templates/pvc.yaml new file mode 100644 index 0000000..46c8546 --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/pvc.yaml @@ -0,0 +1,40 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: lldap-data + namespace: {{ .Values.namespace }} + labels: + app: lldap +spec: + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName }} + {{- end }} + accessModes: + - {{ .Values.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.storageSize }} +{{- end }} +{{- if and .Values.persistence.enabled .Values.persistence.manualProvision }} +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: lldap-data-pv + namespace: {{ .Values.namespace }} + labels: + app: lldap +spec: + capacity: + storage: {{ .Values.persistence.storageSize }} + accessModes: + - {{ .Values.persistence.accessMode }} + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName }} + {{- end }} + {{- if .Values.persistence.localPath }} + hostPath: + path: {{ .Values.persistence.localPath }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/lldap-helm-chart/templates/secret.yaml b/kubernetes/lldap-helm-chart/templates/secret.yaml new file mode 100644 index 0000000..466a069 --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/secret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.name }} + namespace: {{ .Values.namespace }} +type: Opaque +data: + lldap-jwt-secret: {{ .Values.secret.lldapJwtSecret | b64enc }} + lldap-ldap-user-name: {{ .Values.secret.lldapUserName | b64enc }} + lldap-ldap-user-pass: {{ .Values.secret.lldapUserPass | b64enc }} + base-dn: {{ .Values.secret.lldapBaseDn | b64enc }} +{{- end }} diff --git a/kubernetes/lldap-helm-chart/templates/service.yaml b/kubernetes/lldap-helm-chart/templates/service.yaml new file mode 100644 index 0000000..4bdab7f --- /dev/null +++ b/kubernetes/lldap-helm-chart/templates/service.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.service.webui.name }} + namespace: {{ .Values.namespace }} + labels: + app: lldap +spec: + type: {{ .Values.service.webui.type }} + ports: + - name: {{ .Values.service.webui.ports.name | quote }} + port: {{ .Values.service.webui.ports.port }} + targetPort: {{ .Values.service.webui.ports.targetPort }} + selector: + app: lldap +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.service.ldap.name }} + annotations: + external-dns.alpha.kubernetes.io/hostname: lldap.tahmidcloud.com + namespace: {{ .Values.namespace }} + labels: + app: lldap +spec: + type: {{ .Values.service.ldap.type }} + ports: + - name: {{ .Values.service.ldap.ports.name | quote }} + port: {{ .Values.service.ldap.ports.port }} + targetPort: {{ .Values.service.ldap.ports.targetPort }} + selector: + app: lldap diff --git a/kubernetes/lldap-helm-chart/values.yaml b/kubernetes/lldap-helm-chart/values.yaml new file mode 100644 index 0000000..487210e --- /dev/null +++ b/kubernetes/lldap-helm-chart/values.yaml @@ -0,0 +1,97 @@ +##### secret creation +secret: + create: true + name: lldap-credentials + lldapJwtSecret: "placeholder" + lldapUserName: "placeholder" + lldapUserPass: "placeholder" + lldapBaseDn: "dc=homelab,dc=local" + + +##### pvc +persistence: + enabled: true + storageClassName: "" + storageSize: "100Mi" + accessMode: "ReadWriteOnce" + + # in case the StorageClass used does not automatically provision volumes, + # you can specify a local path for manual mounting here like for example /mnt/data/lldap + # if the StorageClass supports automatic provisioning, leave this field empty. + localPath: "" # Local filesystem path for storage, used if 'local-path' is the SC. + + # if manualProvision is set to true, a persistentVolume is created with helm + # if the StorageClass used supports automatic provisioning, this should be set to false. + # and if it does not supports automatic provisioning, set to true. Default is false + manualProvision: false + +extraVolumes: [] + +extraVolumeMounts: [] + +##### deployment +# hour zone +env: + TZ: "EET" + GID: "1001" + UID: "1001" + +extraEnv: [] + +resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50M + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +replicaCount: 1 + +image: + repository: "nitnelave/lldap" + tag: "v0.6.1" + pullPolicy: "IfNotPresent" + + +#### service this is unique service, so no enabled is added as if not it wont work +service: + webui: + name: lldap-service + type: ClusterIP + ports: + name: "17170" + port: 17170 + targetPort: 17170 + ldap: + name: lldap + type: LoadBalancer + ports: + name: "3890" + port: 3890 + targetPort: 3890 + +#####ingress +ingress: + ingressClassName: "traefik" + enabled: true + name: lldap-web-ingress + annotations: {} + labels: {} + hosts: + host: "placeholder.test.com" + paths: + path: "/" + pathType: "Prefix" + tls: + - secretName: "lldap-secret-tls" + hosts: + - "placeholder.test.com" + + diff --git a/kubernetes/my-portfolio/portfolioManifest.yaml b/kubernetes/my-portfolio/portfolioManifest.yaml index efbf606..6bf01e7 100644 --- a/kubernetes/my-portfolio/portfolioManifest.yaml +++ b/kubernetes/my-portfolio/portfolioManifest.yaml @@ -64,20 +64,20 @@ spec: pathType: Prefix backend: service: - name: react-app-service + name: portfolio-app-svc port: number: 80 - path: /interest pathType: Prefix backend: service: - name: react-app-service + name: portfolio-app-svc port: number: 80 - path: /project pathType: Prefix backend: service: - name: react-app-service + name: portfolio-app-svc port: number: 80 diff --git a/kubernetes/traefik-middleware/auth.yaml b/kubernetes/traefik/traefik-middleware/auth.yaml similarity index 100% rename from kubernetes/traefik-middleware/auth.yaml rename to kubernetes/traefik/traefik-middleware/auth.yaml diff --git a/kubernetes/traefik-middleware/auth_secret.yaml b/kubernetes/traefik/traefik-middleware/auth_secret.yaml similarity index 100% rename from kubernetes/traefik-middleware/auth_secret.yaml rename to kubernetes/traefik/traefik-middleware/auth_secret.yaml diff --git a/kubernetes/traefik/traefik-values.yaml b/kubernetes/traefik/traefik-values.yaml new file mode 100644 index 0000000..f74c6e0 --- /dev/null +++ b/kubernetes/traefik/traefik-values.yaml @@ -0,0 +1,26 @@ +USER-SUPPLIED VALUES: +deployment: + podAnnotations: + prometheus.io/port: "8082" + prometheus.io/scrape: "true" +global: + systemDefaultRegistry: "" +image: + repository: rancher/mirrored-library-traefik + tag: 2.11.8 +priorityClassName: system-cluster-critical +providers: + kubernetesIngress: + publishedService: + enabled: true +service: + ipFamilyPolicy: PreferDualStack +tolerations: + - key: CriticalAddonsOnly + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists