added information regarding how to configure http basic authentication using traefik ingress controller for services that do not provide any authentication mechanism
		
			
				
	
	
		
			451 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			451 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Setup K3s Kubernetes Cluster
 | |
| ===================================
 | |
| 
 | |
| # Configure Cert Manager for automating SSL certificate handling
 | |
| 
 | |
| Cert manager handles SSL certificate creation and renewal from Let's Encrypt.
 | |
| 
 | |
| ```bash
 | |
| helm repo add jetstack https://charts.jetstack.io --force-update
 | |
| helm repo update
 | |
| 
 | |
| helm install \
 | |
|   cert-manager jetstack/cert-manager \
 | |
|   --namespace cert-manager \
 | |
|   --create-namespace \
 | |
|   --version v1.15.3 \
 | |
|   --set crds.enabled=true \
 | |
|   --set prometheus.enabled=false \
 | |
|   --set webhook.timeoutSeconds=4 \
 | |
| ```
 | |
| 
 | |
| Next, deploy the certificate Issuer. Issuers, and ClusterIssuers,
 | |
| are Kubernetes resources that represent certificate authorities (CAs) that are
 | |
| able to generate signed certificates by honoring certificate signing requests.
 | |
| All cert-manager certificates require a referenced issuer that is in a ready
 | |
| condition to attempt to honor the request.
 | |
| [Ref](https://cert-manager.io/docs/concepts/issuer/).
 | |
| 
 | |
| The template for ClusterIssuer is in the cert-manager directory. A single
 | |
| wildcard-cert will be created and used for all ingress subdomains. Create a new
 | |
| certificate and cert in cert directory and copy the secret manually to all the
 | |
| namespaces.
 | |
| 
 | |
| First add the DNS servers to the coreDNS config:
 | |
| 
 | |
| ```bash
 | |
| 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
 | |
| source .env
 | |
| helm install cert-handler cert-manager-helm-chart \
 | |
|   --atomic --set secret.apiToken=$CLOUDFLARE_TOKEN \
 | |
|   --set clusterIssuer.email=$EMAIL \
 | |
|   --set wildcardCert.dnsNames[0]=$DNSNAME
 | |
| 
 | |
| # Copy the wildcard certificate to other namespaces
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert-manager -o yaml \
 | |
|   | sed 's/namespace: cert-manager/namespace: <namespace>/' | kubectl apply -f -
 | |
| ```
 | |
| 
 | |
| If for some reason certificate secret `wildcard-cert-secret` is not generated,
 | |
| the issue can be related to cloudflare API token is wrong, the token secret is
 | |
| missing, the Issuer or ClusterIssuer is not ready etc.
 | |
| 
 | |
| Here are some troubleshoot commands to test:
 | |
| 
 | |
| ```bash
 | |
| kubectl get clusterissuer
 | |
| kubectl describe clusterissuer
 | |
| kubectl get certificate -n nginx-test
 | |
| kubectl get certificateRequest -n nginx-test
 | |
| kubectl describe challenges -n cert-manager
 | |
| kubectl describe orders -n cert-manager
 | |
| ```
 | |
| 
 | |
| # Deploy Private Docker Registry
 | |
| 
 | |
| Create a new namespace called docker-registry and deploy the private
 | |
| docker-registry.
 | |
| 
 | |
| First create docker credentials with htpasswd:
 | |
| 
 | |
| ```bash
 | |
| htpasswd -cB registry-passwords USERNAME
 | |
| 
 | |
| kubectl create namespace docker-registry
 | |
| kubectl create secret generic registry-credentials \
 | |
|   --from-file=.secrets/registry-passwords \
 | |
|   -n docker-registry
 | |
| ```
 | |
| 
 | |
| Next, deploy the docker registry with helm chart:
 | |
| 
 | |
| ```bash
 | |
| source .env
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert-manager -o yaml \
 | |
|   | sed 's/namespace: cert-manager/namespace: docker-registry/' \
 | |
|   | kubectl apply -f -
 | |
| 
 | |
| helm install registry docker-registry-helm-chart/ \
 | |
|   --set host=$DOCKER_REGISTRY_HOST \
 | |
|   --set ingress.tls.host=$DNSNAME \
 | |
|   --atomic
 | |
| ```
 | |
| 
 | |
| 
 | |
| # Deploy Portfolio Website from Private Docker Registry
 | |
| 
 | |
| First, create a secret to access the private docker registry. Then copy the
 | |
| wildcard CA cert and deploy the portfolio webapp.
 | |
| 
 | |
| ```bash
 | |
| kubectl create namespace my-portfolio
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert -o yaml \
 | |
|   | sed 's/namespace: cert/namespace: my-portfolio/' | kubectl apply -f -
 | |
| 
 | |
| source .env
 | |
| kubectl create secret docker-registry my-registry-secret \
 | |
|   --docker-server="${DOCKER_REGISTRY_HOST}" \
 | |
|   --docker-username="${DOCKER_USER}" \
 | |
|   --docker-password="${DOCKER_PASSWORD}" \
 | |
|   -n my-portfolio
 | |
| 
 | |
| # use envsubst to substitute the environment variables in the manifest
 | |
| 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
 | |
| the kubernetes traefik reverse proxy.
 | |
| 
 | |
| A nginx http server is deployed as a proxy that listens on port 80
 | |
| and redirects requests to the proxmox local IP address. The server has an
 | |
| associated clusterIP service which is exposed via ingress. The nginx proxy can
 | |
| be configured to listen to other ports and forward traffic to other external
 | |
| services running locally or remotely.
 | |
| 
 | |
| ```bash
 | |
| source .env
 | |
| kubectl create namespace external-services
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert -o yaml \
 | |
|   | sed 's/namespace: cert/namespace: external-services/' | kubectl apply -f -
 | |
| 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:
 | |
| 
 | |
| ```bash
 | |
| sudo apt update
 | |
| sudo apt install nfs-kernel-server
 | |
| sudo chown nobody:nogroup /media/flexdrive
 | |
| 
 | |
| # Configure mount on /etc/fstab to persist across reboot
 | |
| sudo vim /etc/fstab
 | |
| # Add the following line. Change the filsystem if other than ntfs
 | |
| # /dev/sdb2    /media/flexdrive    ntfs    defaults    0    2
 | |
| 
 | |
| # Configure NFS exports by editing the NFS exports file
 | |
| sudo vim /etc/exports
 | |
| # Add the following line to the file
 | |
| # /media/flexdrive 192.168.1.113/24(rw,sync,no_subtree_check,no_root_squash)
 | |
| 
 | |
| # Apply the exports config
 | |
| sudo exportfs -ra
 | |
| 
 | |
| # Start and enable NFS Server
 | |
| sudo systemctl start nfs-kernel-server
 | |
| sudo systemctl enable nfs-kernel-server
 | |
| ```
 | |
| 
 | |
| ##  On all the K3s VMs:
 | |
| ```
 | |
| sudo apt install nfs-common
 | |
| sudo mkdir /mnt/media
 | |
| sudo mount 192.168.1.113:/media/flexdrive /mnt/media
 | |
| # And test if the contents are visible
 | |
| # After that unmount with the following command as mounting will be taken care
 | |
| # by k8s
 | |
| sudo umount /mnt/media
 | |
| ```
 | |
| 
 | |
| 
 | |
| # Deploy Jellyfin Container in K3s
 | |
| 
 | |
| Jellyfin is a media server that can be used to organize, play, and stream
 | |
| audio and video files. The Jellyfin container is deployed in the k3s cluster
 | |
| using the NFS shared storage for media files. Due to segregated nature of the
 | |
| media manifest files, it has not been helm charted.
 | |
| 
 | |
| ```bash
 | |
| source .env
 | |
| kubectl create namespace media
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert-manager -o yaml \
 | |
|   | sed 's/namespace: cert-manager/namespace: media/' | kubectl apply -f -
 | |
| 
 | |
| # Create a new storageclass called manual to not use longhorn storageclass
 | |
| kubectl apply -f media/storageclass-nfs.yaml
 | |
| 
 | |
| # Create NFS PV and PVC
 | |
| envsubst < media/pv.yaml | kubectl apply -n media -f -
 | |
| kubectl apply -f media/pvc.yaml -n media
 | |
| 
 | |
| # Deploy Jellyfin
 | |
| envsubst < media/jellyfin-deploy.yaml | kubectl apply -n media -f -
 | |
| ```
 | |
| 
 | |
| ## Transfer media files from one PVC to another (Optional)
 | |
| 
 | |
| To transfer media files from one PVC to another, create a temporary pod to copy
 | |
| files from one PVC to another. The following command will create a temporary
 | |
| pod in the media namespace to copy files from one PVC to another.
 | |
| 
 | |
| ```bash
 | |
| # Create a temporary pod to copy files from one PVC to another
 | |
| k apply -f temp-deploy.yaml -n media
 | |
| # Copy files from one PVC to another
 | |
| 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
 | |
| using containers. It provides a simple and efficient way to manage persistent
 | |
| volumes. Longhorn is deployed in the k3s cluster to provide storage for the
 | |
| containers. For security reasons, the longhorn UI is not exposed outside the
 | |
| network. It is accessible locally via port-forwarding or loadbalancer.
 | |
| 
 | |
| In order to use Longhorn, the storage disk must be formatted and mounted on
 | |
| each VM. The following commands format the disk and mount it on /mnt/longhorn
 | |
| directory. For deployment, the longhorn helm chart is used to install longhorn
 | |
| in the longhorn-system namespace.
 | |
| 
 | |
| ```bash
 | |
| # On each VM
 | |
| sudo mkfs.ext4 /dev/sda4
 | |
| sudo mkdir /mnt/longhorn
 | |
| sudo mount /dev/sda4 /mnt/longhorn
 | |
| 
 | |
| helm repo add longhorn https://charts.longhorn.io
 | |
| helm repo update
 | |
| 
 | |
| kubectl create namespace longhorn-system
 | |
| helm install longhorn longhorn/longhorn --namespace longhorn-system
 | |
| 
 | |
| kubectl -n longhorn-system get pods
 | |
| 
 | |
| # Access longhorn UI
 | |
| kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80
 | |
| # Or make it permanent by setting the longhorn-frontend service type to
 | |
| # LoadBalancer.
 | |
| kubectl -n longhorn-system edit svc longhorn-frontend
 | |
| ```
 | |
| 
 | |
| ## If the /mnt/longhorn is not shown
 | |
| 
 | |
| kubectl -n longhorn-system get nodes.longhorn.io
 | |
| kubectl -n longhorn-system edit nodes.longhorn.io <node-name>
 | |
| ```
 | |
| Add the following block under disks for all nodes:
 | |
| 
 | |
| ```bash
 | |
|     custom-disk-mnt-longhorn:           # New disk for /mnt/longhorn
 | |
|       allowScheduling: true
 | |
|       diskDriver: ""
 | |
|       diskType: filesystem
 | |
|       evictionRequested: false
 | |
|       path: /mnt/longhorn                # Specify the new mount path
 | |
|       storageReserved: 0                 # Adjust storageReserved if needed
 | |
|       tags: []
 | |
| ```
 | |
| 
 | |
| ## Setting the number of replicas
 | |
| 
 | |
| To set the number of replicas, edit the longhorn-storageclass configmap and
 | |
| set the numberOfReplicas to the desired number.
 | |
| 
 | |
| ```bash
 | |
| # Set number of replica count to 1
 | |
| 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.
 | |
| A loadbalancer service is used for DNS resolution and clusterIP
 | |
| and ingress for the WEBUI.
 | |
| 
 | |
| The adguard initial admin port is 3000 which is bound to the loadbalancer IP
 | |
| from the local network. The AdGuard UI is accessible from the ingress
 | |
| domain on the internet.
 | |
| 
 | |
| ```bash
 | |
| kubectl create namespace adguard
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert -o yaml \
 | |
|   | sed 's/namespace: cert/namespace: adguard/' | kubectl apply -f -
 | |
| 
 | |
| source .env
 | |
| helm install adguard \
 | |
|   --set host=$ADGUARD_HOST \
 | |
|   --atomic adguard-helm-chart
 | |
| ```
 | |
| 
 | |
| 
 | |
| # Pocketbase Database and Authentication Backend
 | |
| 
 | |
| Pocketbase serves as the database and authentication backend for
 | |
| various side projects.
 | |
| 
 | |
| ```bash
 | |
| # Create namespace and copy the wildcard cert secret
 | |
| kubectl create namespace pocketbase
 | |
| kubectl get secret wildcard-cert-secret --namespace=cert-manager -o yaml \
 | |
|   | sed 's/namespace: cert-manager/namespace: pocketbase/' | kubectl apply -f -
 | |
| 
 | |
| # Deploy pocketbase using helm chart
 | |
| helm install pocketbase \
 | |
|   --set ingress.host=$POCKETBASE_HOST \
 | |
|   --set ingress.tls.hosts[0]=$DNSNAME \
 | |
|   --atomic pocketbase-helm-chart
 | |
| ```
 | |
| 
 | |
| It may be required to create initial user and password for the superuser.
 | |
| To do that, exec into the pod and run the following command:
 | |
| 
 | |
| ```bash
 | |
| pocketbase superuser create email password
 | |
| ```
 | |
| 
 | |
| # qBittorrent with Wireguard
 | |
| 
 | |
| qBittorrent is deployed with wireguard to route traffic through a VPN tunnel.
 | |
| The following packages must be installed on each node:
 | |
| 
 | |
| ```bash
 | |
| # On each k3s node
 | |
| sudo apt update
 | |
| sudo apt install -y wireguard wireguard-tools linux-headers-$(uname -r)
 | |
| ```
 | |
| 
 | |
| The qBittorrent is deplyoyed via helm chart. The qBittorrent deployment uses
 | |
| the `media-nfs-pv` common NFS PVC for downloads. The helm chart contains both
 | |
| qBittorrent and wireguard. For security, qBittorrent is not exposed outside the
 | |
| network via ingress. It is accessible locally via loadbalancer IP address.
 | |
| 
 | |
| ```bash
 | |
| helm install qbittorrent qbittorrent-helm-chart/ --atomic
 | |
| ```
 | |
| 
 | |
| After deployment, verify qBittorrent is accessible on the loadbalancer IP and
 | |
| port. Login to the qBittorrent UI with default credentials from the deployment
 | |
| log. Change the user settings under settings/WebUI. Configure the network
 | |
| interface (wg0) in settings/Advanced and set download/upload speeds in
 | |
| settings/speed.
 | |
| 
 | |
| Also verify the VPM is working by executing the following command on the
 | |
| qBittorrent pod:
 | |
| 
 | |
| ```bash
 | |
| curl ipinfo.io
 | |
| ```
 | |
| 
 | |
| 
 | |
| # PostgreSQL Database
 | |
| 
 | |
| The PostgreSQL database uses the bitnami postgres helm chart with one primary
 | |
| and one replica statefulset, totaling 2 postgres pods.
 | |
| 
 | |
| ```bash
 | |
| # Add the Bitnami repo if not already added
 | |
| helm repo add bitnami https://charts.bitnami.com/bitnami
 | |
| helm repo update
 | |
| 
 | |
| # Install PostgreSQL with these values
 | |
| source .env
 | |
| helm install my-postgres \
 | |
|   bitnami/postgresql -f values.yaml \
 | |
|   --set global.postgresql.auth.username=$POSTGRES_USER \
 | |
|   --set global.postgresql.auth.password=$POSTGRES_PASSWORD \
 | |
|   --set global.postgresql.auth.postgresPassword=$POSTGRES_PASSWORD \
 | |
|   --atomic \
 | |
|   -n postgres
 | |
| ```
 | |
| 
 | |
| ## Connect to the Database
 | |
| 
 | |
| ```bash
 | |
| psql -U $POSTGRES_USER -d postgres --host 192.168.1.145 -p 5432
 | |
| ```
 | |
| 
 | |
| ## Backup and Restore PostgreSQL Database
 | |
| 
 | |
| ```bash
 | |
| # To backup
 | |
| # Dump format is compressed and allows parallel restore
 | |
| pg_dump -U $POSTGRES_USER -h 192.168.1.145 -p 5432 -F c \
 | |
|   -f db_backup.dump postgres
 | |
| 
 | |
| # To restore
 | |
| pg_restore -U $POSTGRES_USER -h 192.168.1.145 -p 5432 -d postgres db_backup.dump
 | |
| ```
 | |
| 
 | |
| ## pgAdmin
 | |
| 
 | |
| pgAdmin provides GUI support for PostgreSQL database management. Deploy using
 | |
| pgadmin.yaml manifest under postgres directory. The environment variables are
 | |
| substituted from the .env file.
 | |
| 
 | |
| ```bash
 | |
| source .env
 | |
| envsubst < postgres/pgadmin.yaml | kubectl apply -n postgres -f -
 | |
| ```
 | |
| 
 | |
| ## Authentication Middleware Configuration for Traefik Ingress Controller
 | |
| 
 | |
| The Traefik Ingress Controller provides robust authentication capabilities
 | |
| through middleware implementation. This functionality enables HTTP Basic
 | |
| Authentication for services that do not include native user authentication
 | |
| mechanisms.
 | |
| 
 | |
| To implement authentication, a Traefik middleware must be configured within
 | |
| the target namespace. The process requires creating a secret file containing
 | |
| authentication credentials (username and password). These credentials must
 | |
| be base64 encoded before being integrated into the secret manifest file.
 | |
| 
 | |
| Execute the following commands to configure the authentication:
 | |
| 
 | |
| ```bash
 | |
| htpasswd -c traefik_auth username
 | |
| 
 | |
| echo traefik_auth | base64
 | |
| 
 | |
| source .env
 | |
| envsubst < traefik-middleware/auth_secret.yaml | kubectl apply -n my-portfolio -f -
 | |
| kubernetes apply -f traefik-middleware/auth.yaml -n my-portfolio
 | |
| ```
 | |
| 
 | |
| Following middleware deployment, the authentication must be enabled by adding the appropriate annotation to the service's Ingress object specification:
 | |
| 
 | |
| ```
 | |
| traefik.ingress.kubernetes.io/router.middlewares: my-portfolio-basic-auth@kubernetescrd
 | |
| ```
 |