├── .github ├── lint │ ├── .prettierignore │ ├── .prettierrc.yaml │ ├── .yamllint.yaml │ └── .markdownlint.yaml ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature-request.md │ └── bug-report.md ├── workflows │ ├── invalid-template.yaml │ ├── lint-shell.yaml │ ├── lint-markdown.yaml │ ├── lint.yaml │ ├── helm-deps-update-schedule.yaml │ ├── renovate-schedule.yaml │ └── flux-schedule.yaml ├── PULL_REQUEST_TEMPLATE.md └── renovate.json5 ├── charts └── kah-common │ ├── templates │ └── common.yaml │ ├── charts │ └── common-4.3.0.tgz │ ├── Chart.lock │ └── Chart.yaml ├── server ├── ansible │ ├── templates │ │ └── coredns │ │ │ ├── coredns-tmpfiles.conf.j2 │ │ │ ├── coredns-sysusers.conf.j2 │ │ │ ├── Corefile.j2 │ │ │ └── coredns.service.j2 │ ├── requirements.yaml │ └── playbooks │ │ ├── storage │ │ └── fileserver.yaml │ │ ├── dns │ │ ├── coredns.yaml │ │ └── coredns-service.yaml │ │ └── kubernetes │ │ └── kubernetes.yaml ├── packer │ ├── images │ │ ├── variables.auto.pkrvars.hcl │ │ ├── variables.pkr.hcl │ │ ├── debian.pkr.hcl │ │ └── k3os.pkr.hcl │ └── templates │ │ ├── k3os-config.pkrtpl.hcl │ │ └── debian-preseed.pkrtpl.hcl └── terraform │ └── proxmox │ ├── variables.auto.tfvars │ ├── templates │ └── inventory.tpl │ ├── ansible_inventory.tf │ ├── provider.tf │ ├── fileserver.tf │ ├── variables.tf │ ├── k3os.tf │ ├── coredns.tf │ └── .terraform.lock.hcl ├── cluster ├── core │ ├── namespaces │ │ ├── home.yaml │ │ ├── media.yaml │ │ ├── devices.yaml │ │ ├── networking.yaml │ │ ├── cert-manager.yaml │ │ ├── organization.yaml │ │ ├── metallb-system.yaml │ │ ├── observability.yaml │ │ └── kustomization.yaml │ ├── metallb-system │ │ ├── kustomization.yaml │ │ └── helm-release.yaml │ ├── kustomization.yaml │ └── cert-manager │ │ ├── kustomization.yaml │ │ ├── letsencrypt-production.yaml │ │ ├── letsencrypt-staging.yaml │ │ ├── helm-release.yaml │ │ ├── secret.sops.yaml │ │ └── prometheus-rule.yaml ├── apps │ ├── media │ │ ├── kustomization.yaml │ │ └── hyperion-ng │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ └── helm-release.yaml │ ├── home │ │ ├── emqx │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ ├── zigbee2mqtt │ │ │ ├── kustomization.yaml │ │ │ ├── config-pvc.yaml │ │ │ └── helm-release.yaml │ │ ├── zwavejs2mqtt │ │ │ ├── kustomization.yaml │ │ │ ├── config-pvc.yaml │ │ │ └── helm-release.yaml │ │ ├── kustomization.yaml │ │ ├── mopidy │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ ├── mopidy-config-map.yaml │ │ │ └── helm-release.yaml │ │ └── home-assistant │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ ├── helm-release.yaml │ │ │ ├── home-assistant.sops.yaml │ │ │ └── code-server.sops.yaml │ ├── devices │ │ ├── kustomization.yaml │ │ └── smarter-device-manager │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ ├── kube-system │ │ ├── home-dns │ │ │ ├── kustomization.yaml │ │ │ └── rbac.yaml │ │ ├── reloader │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ ├── metrics-server │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ ├── nfs-subdir-external-provisioner │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ └── kustomization.yaml │ ├── flux-system │ │ ├── notifications │ │ │ ├── kustomization.yaml │ │ │ ├── github │ │ │ │ ├── kustomization.yaml │ │ │ │ ├── notification.yaml │ │ │ │ └── secret.sops.yaml │ │ │ └── slack │ │ │ │ ├── kustomization.yaml │ │ │ │ ├── notification.yaml │ │ │ │ └── secret.sops.yaml │ │ ├── kustomization.yaml │ │ └── monitoring │ │ │ ├── kustomization.yaml │ │ │ ├── pod-monitor.yaml │ │ │ └── prometheus-rule.yaml │ ├── organization │ │ ├── kustomization.yaml │ │ ├── hajimari │ │ │ ├── kustomization.yaml │ │ │ ├── config-pvc.yaml │ │ │ └── helm-release.yaml │ │ └── dokuwiki │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ ├── helm-release.yaml │ │ │ └── secret.sops.yaml │ ├── networking │ │ ├── traefik │ │ │ ├── dashboard │ │ │ │ ├── kustomization.yaml │ │ │ │ └── ingress.yaml │ │ │ ├── tls-store │ │ │ │ ├── kustomization.yaml │ │ │ │ └── default.yaml │ │ │ ├── middlewares │ │ │ │ ├── kustomization.yaml │ │ │ │ ├── basic-auth.yaml │ │ │ │ └── secret.sops.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── service-monitor.yaml │ │ │ ├── helm-release.yaml │ │ │ └── prometheus-rule.yaml │ │ ├── wildcard-certificate │ │ │ ├── kustomization.yaml │ │ │ └── certificate.yaml │ │ ├── unifi │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ └── helm-release.yaml │ │ ├── adguard-home │ │ │ ├── kustomization.yaml │ │ │ ├── pvc.yaml │ │ │ └── helm-release.yaml │ │ └── kustomization.yaml │ ├── observability │ │ ├── unifi-poller │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ ├── kube-prometheus-stack │ │ │ ├── kustomization.yaml │ │ │ └── helm-release.yaml │ │ ├── grafana │ │ │ ├── kustomization.yaml │ │ │ ├── secret.sops.yaml │ │ │ └── helm-release.yaml │ │ └── kustomization.yaml │ └── kustomization.yaml ├── crds │ ├── traefik │ │ ├── kustomization.yaml │ │ └── crds.yaml │ ├── kube-prometheus-stack │ │ ├── kustomization.yaml │ │ └── crds.yaml │ ├── kustomization.yaml │ └── cert-manager │ │ └── kustomization.yaml └── base │ ├── flux-system │ ├── charts │ │ ├── kustomization.yaml │ │ └── helm │ │ │ ├── emqx-charts.yaml │ │ │ ├── hajimari-charts.yaml │ │ │ ├── jetstack-charts.yaml │ │ │ ├── bitnami-charts.yaml │ │ │ ├── metallb-charts.yaml │ │ │ ├── traefik-charts.yaml │ │ │ ├── grafana-charts.yaml │ │ │ ├── k8s-at-home-charts.yaml │ │ │ ├── stakater-charts.yaml │ │ │ ├── external-dns-charts.yaml │ │ │ ├── metrics-server-charts.yaml │ │ │ ├── prometheus-community-charts.yaml │ │ │ ├── node-feature-discovery-charts.yaml │ │ │ ├── nfs-subdir-external-provisioner-charts.yaml │ │ │ └── kustomization.yaml │ ├── kustomization.yaml │ └── gotk-sync.yaml │ ├── crds.yaml │ ├── cluster-settings.yaml │ ├── apps.yaml │ ├── core.yaml │ └── cluster-secrets.sops.yaml ├── .sops.yaml ├── SECURITY.md ├── .taskfiles ├── ansible.yml ├── pre-commit.yml ├── packer.yml ├── lint.yml ├── flux.yml ├── format.yml └── terraform.yml ├── Taskfile.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .sops.pub.asc └── README.md /.github/lint/.prettierignore: -------------------------------------------------------------------------------- 1 | *.sops.* 2 | gotk-components.yaml 3 | -------------------------------------------------------------------------------- /charts/kah-common/templates/common.yaml: -------------------------------------------------------------------------------- 1 | {{ include "common.all" . }} 2 | -------------------------------------------------------------------------------- /server/ansible/templates/coredns/coredns-tmpfiles.conf.j2: -------------------------------------------------------------------------------- 1 | d /var/lib/coredns 0755 coredns coredns - 2 | -------------------------------------------------------------------------------- /cluster/core/namespaces/home.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: home 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/media.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: media 6 | -------------------------------------------------------------------------------- /.github/lint/.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | trailingComma: "es5" 3 | tabWidth: 2 4 | semi: false 5 | singleQuote: false 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/devices.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: devices 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/networking.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: networking 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/cert-manager.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: cert-manager 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/organization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: organization 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/metallb-system.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: metallb-system 6 | -------------------------------------------------------------------------------- /cluster/core/namespaces/observability.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: observability 6 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | * @alexwaibel 3 | -------------------------------------------------------------------------------- /.sops.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | creation_rules: 3 | - encrypted_regex: ^(data|stringData)$ 4 | pgp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 5 | -------------------------------------------------------------------------------- /server/ansible/templates/coredns/coredns-sysusers.conf.j2: -------------------------------------------------------------------------------- 1 | u coredns - "CoreDNS is a DNS server that chains plugins" /var/lib/coredns 2 | -------------------------------------------------------------------------------- /charts/kah-common/charts/common-4.3.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwaibel/home-cluster-proxmox/HEAD/charts/kah-common/charts/common-4.3.0.tgz -------------------------------------------------------------------------------- /cluster/apps/media/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - hyperion-ng 6 | -------------------------------------------------------------------------------- /cluster/crds/traefik/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - crds.yaml 6 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm 6 | -------------------------------------------------------------------------------- /cluster/apps/home/emqx/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/devices/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - smarter-device-manager 6 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/home-dns/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - rbac.yaml 6 | -------------------------------------------------------------------------------- /cluster/core/metallb-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/crds/kube-prometheus-stack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - crds.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - github 5 | - slack 6 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/reloader/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/organization/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - dokuwiki 6 | - hajimari 7 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/dashboard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - ingress.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/tls-store/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - default.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/devices/smarter-device-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - monitoring 6 | - notifications 7 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/metrics-server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/networking/wildcard-certificate/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - certificate.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/observability/unifi-poller/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/home/zigbee2mqtt/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - config-pvc.yaml 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/home/zwavejs2mqtt/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - config-pvc.yaml 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/media/hyperion-ng/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - pvc.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/networking/unifi/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - pvc.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/observability/kube-prometheus-stack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /server/ansible/requirements.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | roles: 3 | - name: fubarhouse.golang 4 | - name: geerlingguy.nfs 5 | 6 | collections: 7 | - name: kubernetes.core 8 | version: 2.2.2 9 | -------------------------------------------------------------------------------- /cluster/apps/networking/adguard-home/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - pvc.yaml 7 | -------------------------------------------------------------------------------- /cluster/core/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - namespaces 6 | - metallb-system 7 | - cert-manager 8 | -------------------------------------------------------------------------------- /cluster/crds/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - cert-manager 6 | - kube-prometheus-stack 7 | - traefik 8 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/nfs-subdir-external-provisioner/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/organization/hajimari/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - config-pvc.yaml 6 | - helm-release.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/monitoring/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - pod-monitor.yaml 6 | - prometheus-rule.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/github/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - notification.yaml 5 | - secret.sops.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/slack/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - notification.yaml 5 | - secret.sops.yaml 6 | -------------------------------------------------------------------------------- /cluster/apps/observability/grafana/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - secret.sops.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/middlewares/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - basic-auth.yaml 6 | - secret.sops.yaml 7 | -------------------------------------------------------------------------------- /cluster/apps/observability/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - grafana 6 | - kube-prometheus-stack 7 | - unifi-poller 8 | -------------------------------------------------------------------------------- /cluster/base/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - gotk-components.yaml 6 | - gotk-sync.yaml 7 | - charts 8 | -------------------------------------------------------------------------------- /cluster/apps/networking/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - adguard-home 6 | - traefik 7 | - unifi 8 | - wildcard-certificate 9 | -------------------------------------------------------------------------------- /cluster/apps/organization/dokuwiki/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - pvc.yaml 7 | - secret.sops.yaml 8 | -------------------------------------------------------------------------------- /cluster/apps/home/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - emqx 6 | - home-assistant 7 | - mopidy 8 | - zigbee2mqtt 9 | - zwavejs2mqtt 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please do not open a GitHub issue if you believe you have discovered a vulnerability. Instead please email a description of your bug to security@waibel.us. 6 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - home-dns 6 | - metrics-server 7 | - nfs-subdir-external-provisioner 8 | - reloader 9 | -------------------------------------------------------------------------------- /cluster/apps/home/mopidy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - icecast-config-map.yaml 7 | - mopidy-config-map.yaml 8 | - pvc.yaml 9 | -------------------------------------------------------------------------------- /server/packer/images/variables.auto.pkrvars.hcl: -------------------------------------------------------------------------------- 1 | proxmox_ip = "192.168.1.124" 2 | proxmox_username = "terraform@pve" 3 | proxmox_node = "server" 4 | nameserver = "1.1.1.1" 5 | master_node_ip = "192.168.1.10" 6 | github_username = "alexwaibel" 7 | -------------------------------------------------------------------------------- /cluster/apps/home/home-assistant/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - code-server.sops.yaml 6 | - helm-release.yaml 7 | - home-assistant.sops.yaml 8 | - pvc.yaml 9 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/middlewares/basic-auth.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: traefik.containo.us/v1alpha1 3 | kind: Middleware 4 | metadata: 5 | name: basic-auth 6 | namespace: networking 7 | spec: 8 | basicAuth: 9 | secret: basic-auth 10 | -------------------------------------------------------------------------------- /charts/kah-common/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: common 3 | repository: https://library-charts.k8s-at-home.com 4 | version: 4.3.0 5 | digest: sha256:2d7424f966ee4f1710dad86daf14bdf0ad2ab04db18c5215c1b0d0226952b433 6 | generated: "2022-01-03T08:52:44.290620223Z" 7 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/tls-store/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: traefik.containo.us/v1alpha1 3 | kind: TLSStore 4 | metadata: 5 | name: default 6 | namespace: networking 7 | spec: 8 | defaultCertificate: 9 | secretName: "${SECRET_DOMAIN//./-}-tls" 10 | -------------------------------------------------------------------------------- /cluster/apps/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - devices 6 | - flux-system 7 | - home 8 | - kube-system 9 | - media 10 | - networking 11 | - observability 12 | - organization 13 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - dashboard 6 | - helm-release.yaml 7 | - middlewares 8 | - prometheus-rule.yaml 9 | - service-monitor.yaml 10 | - tls-store 11 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - helm-release.yaml 6 | - letsencrypt-production.yaml 7 | - letsencrypt-staging.yaml 8 | - prometheus-rule.yaml 9 | - secret.sops.yaml 10 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/emqx-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: emqx-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://repos.emqx.io/charts 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/hajimari-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: hajimari-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://hajimari.io 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/jetstack-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: jetstack-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://charts.jetstack.io/ 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /server/terraform/proxmox/variables.auto.tfvars: -------------------------------------------------------------------------------- 1 | proxmox_ip = "192.168.1.124" 2 | proxmox_username = "terraform@pve" 3 | proxmox_node = "server" 4 | master_node_ip_address = "192.168.1.10" 5 | fileserver_ip_address = "192.168.1.11" 6 | coredns_ip_address = "192.168.1.3" 7 | -------------------------------------------------------------------------------- /cluster/apps/home/mopidy/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: mopidy-data 6 | namespace: home 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/bitnami-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: bitnami-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://charts.bitnami.com/bitnami 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/metallb-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: metallb-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://metallb.github.io/metallb 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/traefik-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: traefik-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://helm.traefik.io/traefik 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/crds/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | # renovate: registryUrl=https://charts.jetstack.io chart=cert-manager 6 | - https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.crds.yaml 7 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/grafana-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: grafana-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://grafana.github.io/helm-charts 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/k8s-at-home-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: k8s-at-home-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://k8s-at-home.com/charts/ 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /.taskfiles/ansible.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | vars: 5 | ANSIBLE_DIR: "{{.SERVER_DIR}}/ansible" 6 | 7 | tasks: 8 | install: 9 | desc: Install ansible dependencies 10 | dir: "{{.ANSIBLE_DIR}}" 11 | cmds: 12 | - "ansible-galaxy install -r requirements.yaml" 13 | silent: true 14 | -------------------------------------------------------------------------------- /cluster/apps/networking/unifi/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: unifi-data 6 | namespace: networking 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 10Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/stakater-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: stakater-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://stakater.github.io/stakater-charts 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /.taskfiles/pre-commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | tasks: 5 | init: 6 | desc: Initialize pre-commit hooks 7 | cmds: 8 | - pre-commit install-hooks 9 | silent: true 10 | 11 | run: 12 | desc: Run pre-commit 13 | cmds: 14 | - pre-commit run --all-files 15 | silent: true 16 | -------------------------------------------------------------------------------- /cluster/apps/media/hyperion-ng/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: hyperion-ng-config 6 | namespace: media 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/apps/home/home-assistant/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: home-assistant-config 6 | namespace: home 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/apps/home/zigbee2mqtt/config-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: zigbee2mqtt-config 6 | namespace: home 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/apps/home/zwavejs2mqtt/config-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: zwavejs2mqtt-config 6 | namespace: home 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/apps/organization/dokuwiki/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: dokuwiki-data 6 | namespace: organization 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 10Gi 13 | storageClassName: nfs-client 14 | -------------------------------------------------------------------------------- /cluster/base/crds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 3 | kind: Kustomization 4 | metadata: 5 | name: crds 6 | namespace: flux-system 7 | spec: 8 | interval: 10m0s 9 | path: ./cluster/crds 10 | prune: false 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/external-dns-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: external-dns-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://kubernetes-sigs.github.io/external-dns 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /server/ansible/templates/coredns/Corefile.j2: -------------------------------------------------------------------------------- 1 | .:53 { 2 | errors 3 | log 4 | prometheus :9153 5 | k8s_gateway {{ domain }} { 6 | ttl 30 7 | kubeconfig /etc/coredns/kubeconfig 8 | fallthrough 9 | } 10 | forward . tls://1.1.1.1 tls://1.0.0.1 { 11 | tls_servername cloudflare-dns.com 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cluster/apps/organization/hajimari/config-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: hajimari-config 6 | namespace: organization 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | storageClassName: nfs-client 11 | resources: 12 | requests: 13 | storage: 128Mi 14 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/metrics-server-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: metrics-server-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://kubernetes-sigs.github.io/metrics-server 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/core/namespaces/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - cert-manager.yaml 6 | - devices.yaml 7 | - home.yaml 8 | - media.yaml 9 | - metallb-system.yaml 10 | - networking.yaml 11 | - observability.yaml 12 | - organization.yaml 13 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/prometheus-community-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: prometheus-community-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://prometheus-community.github.io/helm-charts 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/node-feature-discovery-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: node-feature-discovery-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://kubernetes-sigs.github.io/node-feature-discovery/charts 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/nfs-subdir-external-provisioner-charts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: HelmRepository 4 | metadata: 5 | name: nfs-subdir-external-provisioner-charts 6 | namespace: flux-system 7 | spec: 8 | interval: 15m 9 | url: https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ 10 | timeout: 3m 11 | -------------------------------------------------------------------------------- /cluster/base/cluster-settings.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: cluster-settings 6 | namespace: flux-system 7 | data: 8 | COREDNS_ADDR: "192.168.1.3" 9 | METALLB_LB_RANGE: "192.168.1.150-192.168.1.200" 10 | SVC_TRAEFIK_ADDR: "192.168.1.150" 11 | SVC_UNIFI_ADDR: "192.168.1.151" 12 | SVC_HYPERION_ADDR: "192.168.1.152" 13 | SVC_ADGUARD_ADDR: "192.168.1.153" 14 | SVC_EMQX_ADDR: "192.168.1.154" 15 | -------------------------------------------------------------------------------- /server/terraform/proxmox/templates/inventory.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | dns: 4 | hosts: 5 | coredns: 6 | ansible_host: ${coredns_ip} 7 | ansible_user: ${coredns_user} 8 | 9 | kubernetes: 10 | hosts: 11 | master: 12 | ansible_host: ${master_node_ip} 13 | ansible_user: ${k3os_user} 14 | 15 | storage: 16 | hosts: 17 | fileserver: 18 | ansible_host: ${fileserver_ip} 19 | ansible_user: ${fileserver_user} 20 | -------------------------------------------------------------------------------- /cluster/apps/networking/wildcard-certificate/certificate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: Certificate 4 | metadata: 5 | name: "${SECRET_DOMAIN//./-}" 6 | namespace: networking 7 | spec: 8 | secretName: "${SECRET_DOMAIN//./-}-tls" 9 | issuerRef: 10 | name: letsencrypt-production 11 | kind: ClusterIssuer 12 | commonName: "${SECRET_DOMAIN}" 13 | dnsNames: 14 | - "${SECRET_DOMAIN}" 15 | - "*.${SECRET_DOMAIN}" 16 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | vars: 5 | PROJECT_DIR: 6 | sh: "git rev-parse --show-toplevel" 7 | CLUSTER_DIR: "{{.PROJECT_DIR}}/cluster" 8 | SERVER_DIR: "{{.PROJECT_DIR}}/server" 9 | 10 | includes: 11 | ansible: .taskfiles/ansible.yml 12 | flux: .taskfiles/flux.yml 13 | format: .taskfiles/format.yml 14 | lint: .taskfiles/lint.yml 15 | packer: .taskfiles/packer.yml 16 | pre-commit: .taskfiles/pre-commit.yml 17 | terraform: .taskfiles/terraform.yml 18 | -------------------------------------------------------------------------------- /.github/lint/.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignore: | 3 | *.sops.* 4 | gotk-components.yaml 5 | extends: default 6 | rules: 7 | truthy: 8 | allowed-values: ["true", "false", "yes", "no"] 9 | comments: 10 | min-spaces-from-content: 1 11 | line-length: disable 12 | braces: 13 | min-spaces-inside: 0 14 | max-spaces-inside: 1 15 | brackets: 16 | min-spaces-inside: 0 17 | max-spaces-inside: 0 18 | indentation: 19 | spaces: 2 20 | indent-sequences: consistent 21 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/service-monitor.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: traefik 6 | namespace: networking 7 | labels: 8 | app.kubernetes.io/name: traefik 9 | spec: 10 | endpoints: 11 | - path: /metrics 12 | targetPort: metrics 13 | jobLabel: traefik 14 | namespaceSelector: 15 | matchNames: 16 | - networking 17 | selector: 18 | matchLabels: 19 | app.kubernetes.io/name: traefik 20 | -------------------------------------------------------------------------------- /server/ansible/playbooks/storage/fileserver.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - fileserver 4 | become: true 5 | roles: 6 | - role: geerlingguy.nfs 7 | nfs_exports: ["/mnt/share 192.168.1.0/24(rw,no_subtree_check)"] 8 | tasks: 9 | - name: Disable password login 10 | user: 11 | name: debian 12 | password_lock: true 13 | 14 | - name: Set share directory permissions 15 | file: 16 | path: /mnt/share 17 | owner: nobody 18 | group: nogroup 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question to the maintainer 4 | title: "" 5 | labels: kind/question 6 | assignees: "" 7 | --- 8 | 9 | ## Details 10 | 11 | **Ask your question:** 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/terraform/proxmox/ansible_inventory.tf: -------------------------------------------------------------------------------- 1 | resource "local_file" "ansible_inventory" { 2 | content = templatefile("${path.module}/templates/inventory.tpl", 3 | { 4 | fileserver_ip = var.fileserver_ip_address, 5 | fileserver_user = var.fileserver_user, 6 | master_node_ip = var.master_node_ip_address, 7 | k3os_user = var.k3os_user 8 | coredns_ip = var.coredns_ip_address 9 | coredns_user = var.coredns_user 10 | } 11 | ) 12 | filename = "../../ansible/inventory/hosts.yml" 13 | } 14 | -------------------------------------------------------------------------------- /.taskfiles/packer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | vars: 5 | PACKER_DIR: "{{.SERVER_DIR}}/packer" 6 | 7 | tasks: 8 | all: 9 | desc: Build all images 10 | cmds: 11 | - "packer build server/packer/images" 12 | silent: true 13 | 14 | debian: 15 | desc: Build debian image 16 | cmds: 17 | - "packer build --only=proxmox.debian_cloudinit server/packer/images" 18 | silent: true 19 | 20 | k3os: 21 | desc: Build k3os image 22 | cmds: 23 | - "packer build --only=proxmox.k3os_cloudinit server/packer/images" 24 | silent: true 25 | -------------------------------------------------------------------------------- /server/ansible/templates/coredns/coredns.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=CoreDNS DNS server 3 | Documentation=https://coredns.io 4 | After=network.target 5 | 6 | [Service] 7 | PermissionsStartOnly=true 8 | LimitNOFILE=1048576 9 | LimitNPROC=512 10 | CapabilityBoundingSet=CAP_NET_BIND_SERVICE 11 | AmbientCapabilities=CAP_NET_BIND_SERVICE 12 | NoNewPrivileges=true 13 | User=coredns 14 | WorkingDirectory=~ 15 | ExecStart=/usr/bin/coredns -conf=/etc/coredns/Corefile 16 | ExecReload=/bin/kill -SIGUSR1 $MAINPID 17 | Restart=on-failure 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /cluster/apps/networking/adguard-home/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: adguard-config 6 | namespace: networking 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 1Gi 13 | storageClassName: nfs-client 14 | --- 15 | apiVersion: v1 16 | kind: PersistentVolumeClaim 17 | metadata: 18 | name: adguard-data 19 | namespace: networking 20 | spec: 21 | accessModes: 22 | - ReadWriteOnce 23 | resources: 24 | requests: 25 | storage: 5Gi 26 | storageClassName: nfs-client 27 | -------------------------------------------------------------------------------- /.github/lint/.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default: true 3 | 4 | # MD013/line-length - Line length 5 | MD013: 6 | # Number of characters 7 | line_length: 240 8 | # Number of characters for headings 9 | heading_line_length: 80 10 | # Number of characters for code blocks 11 | code_block_line_length: 80 12 | # Include code blocks 13 | code_blocks: true 14 | # Include tables 15 | tables: true 16 | # Include headings 17 | headings: true 18 | # Include headers 19 | headers: true 20 | # Strict length checking 21 | strict: false 22 | # Stern length checking 23 | stern: false 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: kind/enhancement 6 | assignees: "" 7 | --- 8 | 9 | ## Details 10 | 11 | **Describe the solution you'd like:** 12 | 13 | 14 | 15 | **Anything else you would like to add:** 16 | 17 | 18 | 19 | **Additional Information:** 20 | 21 | 22 | -------------------------------------------------------------------------------- /cluster/base/flux-system/charts/helm/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - bitnami-charts.yaml 6 | - emqx-charts.yaml 7 | - external-dns-charts.yaml 8 | - grafana-charts.yaml 9 | - hajimari-charts.yaml 10 | - jetstack-charts.yaml 11 | - k8s-at-home-charts.yaml 12 | - metallb-charts.yaml 13 | - metrics-server-charts.yaml 14 | - nfs-subdir-external-provisioner-charts.yaml 15 | - node-feature-discovery-charts.yaml 16 | - prometheus-community-charts.yaml 17 | - stakater-charts.yaml 18 | - traefik-charts.yaml 19 | -------------------------------------------------------------------------------- /cluster/base/apps.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 3 | kind: Kustomization 4 | metadata: 5 | name: apps 6 | namespace: flux-system 7 | spec: 8 | interval: 10m0s 9 | dependsOn: 10 | - name: core 11 | path: ./cluster/apps 12 | prune: true 13 | sourceRef: 14 | kind: GitRepository 15 | name: flux-system 16 | decryption: 17 | provider: sops 18 | secretRef: 19 | name: sops-gpg 20 | postBuild: 21 | substitute: {} 22 | substituteFrom: 23 | - kind: ConfigMap 24 | name: cluster-settings 25 | - kind: Secret 26 | name: cluster-secrets 27 | -------------------------------------------------------------------------------- /cluster/base/core.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 3 | kind: Kustomization 4 | metadata: 5 | name: core 6 | namespace: flux-system 7 | spec: 8 | interval: 10m0s 9 | dependsOn: 10 | - name: crds 11 | path: ./cluster/core 12 | prune: false 13 | sourceRef: 14 | kind: GitRepository 15 | name: flux-system 16 | decryption: 17 | provider: sops 18 | secretRef: 19 | name: sops-gpg 20 | postBuild: 21 | substitute: {} 22 | substituteFrom: 23 | - kind: ConfigMap 24 | name: cluster-settings 25 | - kind: Secret 26 | name: cluster-secrets 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: kind/bug 6 | assignees: "" 7 | --- 8 | 9 | ## Details 10 | 11 | **What steps did you take and what happened:** 12 | 13 | 14 | 15 | **What did you expect to happen:** 16 | 17 | **Anything else you would like to add:** 18 | 19 | 20 | 21 | **Additional Information:** 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/invalid-template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Invalid Template 3 | 4 | on: # yamllint disable-line rule:truthy 5 | issues: 6 | types: 7 | - labeled 8 | - unlabeled 9 | - reopened 10 | 11 | jobs: 12 | support: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - uses: dessant/support-requests@v2 16 | with: 17 | github-token: ${{ github.token }} 18 | support-label: "template-incomplete" 19 | issue-comment: > 20 | :wave: @{issue-author}, please follow the template provided. 21 | close-issue: true 22 | lock-issue: true 23 | issue-lock-reason: "resolved" 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Description of the change 3 | 4 | 5 | 6 | ## Benefits 7 | 8 | 9 | 10 | ## Possible drawbacks 11 | 12 | 13 | 14 | ## Applicable issues 15 | 16 | 17 | 18 | - fixes # 19 | 20 | ## Additional information 21 | 22 | 23 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/github/notification.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: notification.toolkit.fluxcd.io/v1beta1 3 | kind: Provider 4 | metadata: 5 | name: github 6 | namespace: flux-system 7 | spec: 8 | type: github 9 | address: https://github.com/alexwaibel/home-cluster 10 | secretRef: 11 | name: github-token 12 | --- 13 | apiVersion: notification.toolkit.fluxcd.io/v1beta1 14 | kind: Alert 15 | metadata: 16 | name: github 17 | namespace: flux-system 18 | spec: 19 | providerRef: 20 | name: github 21 | eventSeverity: info 22 | eventSources: 23 | - kind: Kustomization 24 | name: "*" 25 | - kind: HelmRelease 26 | name: "*" 27 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/letsencrypt-production.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: ClusterIssuer 4 | metadata: 5 | name: letsencrypt-production 6 | spec: 7 | acme: 8 | server: https://acme-v02.api.letsencrypt.org/directory 9 | email: "${SECRET_CLOUDFLARE_EMAIL}" 10 | privateKeySecretRef: 11 | name: letsencrypt-production 12 | solvers: 13 | - dns01: 14 | cloudflare: 15 | email: "${SECRET_CLOUDFLARE_EMAIL}" 16 | apiTokenSecretRef: 17 | name: cloudflare-token-secret 18 | key: cloudflare-token 19 | selector: 20 | dnsZones: 21 | - ${SECRET_DOMAIN} 22 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/letsencrypt-staging.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: ClusterIssuer 4 | metadata: 5 | name: letsencrypt-staging 6 | spec: 7 | acme: 8 | server: https://acme-staging-v02.api.letsencrypt.org/directory 9 | email: "${SECRET_CLOUDFLARE_EMAIL}" 10 | privateKeySecretRef: 11 | name: letsencrypt-staging 12 | solvers: 13 | - dns01: 14 | cloudflare: 15 | email: "${SECRET_CLOUDFLARE_EMAIL}" 16 | apiTokenSecretRef: 17 | name: cloudflare-token-secret 18 | key: cloudflare-token 19 | selector: 20 | dnsZones: 21 | - ${SECRET_DOMAIN} 22 | -------------------------------------------------------------------------------- /cluster/base/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: GitRepository 4 | metadata: 5 | name: flux-system 6 | namespace: flux-system 7 | spec: 8 | interval: 1m0s 9 | ref: 10 | branch: main 11 | url: https://github.com/alexwaibel/home-cluster 12 | --- 13 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 14 | kind: Kustomization 15 | metadata: 16 | name: flux-system 17 | namespace: flux-system 18 | spec: 19 | interval: 10m0s 20 | path: ./cluster/base 21 | prune: true 22 | sourceRef: 23 | kind: GitRepository 24 | name: flux-system 25 | decryption: 26 | provider: sops 27 | secretRef: 28 | name: sops-gpg 29 | -------------------------------------------------------------------------------- /.taskfiles/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | tasks: 5 | all: 6 | - task: markdown 7 | - task: yaml 8 | - task: format 9 | 10 | markdown: 11 | desc: Lint Markdown 12 | cmds: 13 | - markdownlint -c '.github/lint/.markdownlint.yaml' *.md **/*.md 14 | ignore_errors: true 15 | silent: true 16 | 17 | yaml: 18 | desc: Lint YAML 19 | cmds: 20 | - yamllint -c '.github/lint/.yamllint.yaml' . 21 | ignore_errors: true 22 | silent: true 23 | 24 | format: 25 | desc: Lint general formatting 26 | cmds: 27 | - prettier --ignore-path '.github/lint/.prettierignore' --config '.github/lint/.prettierrc.yaml' --check . 28 | ignore_errors: true 29 | silent: true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | .vscode/ 3 | # Trash 4 | .DS_Store 5 | Thumbs.db 6 | # Binaries 7 | bin 8 | flux 9 | *.iso 10 | # Temp folders 11 | .ignore/ 12 | .logs/ 13 | .task/ 14 | # Local .terraform directories 15 | **/.terraform/* 16 | # .tfstate files 17 | *.tfstate 18 | *.tfstate.* 19 | # Crash log files 20 | crash.log 21 | # Ignore override files as they are usually used to override resources locally and so 22 | # are not checked in 23 | override.tf 24 | override.tf.json 25 | *_override.tf 26 | *_override.tf.json 27 | # Ignore CLI configuration files 28 | .terraformrc 29 | terraform.rc 30 | # ignore packer cache 31 | packer_cache/ 32 | # Secrets files 33 | server/**/secrets.* 34 | # Files generated by terraform 35 | server/ansible/inventory/hosts.yml 36 | -------------------------------------------------------------------------------- /server/terraform/proxmox/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "Telmate/proxmox" 5 | version = "2.9.5" 6 | } 7 | } 8 | } 9 | 10 | variable "proxmox_port" { 11 | type = string 12 | description = "The port used by the proxmox management interface." 13 | default = "8006" 14 | } 15 | 16 | variable "proxmox_password" { 17 | type = string 18 | description = "The password for the proxmox user." 19 | sensitive = true 20 | } 21 | 22 | provider "proxmox" { 23 | pm_api_url = "https://${var.proxmox_ip}:${var.proxmox_port}/api2/json" 24 | pm_user = var.proxmox_username 25 | pm_password = var.proxmox_password 26 | pm_tls_insecure = true 27 | } 28 | -------------------------------------------------------------------------------- /charts/kah-common/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | description: A KaH-common powered chart template 4 | name: kah-common 5 | version: 1.0.0 6 | maintainers: 7 | - name: bjw-s 8 | email: me@bjw-s.dev 9 | dependencies: 10 | - name: common 11 | repository: https://library-charts.k8s-at-home.com 12 | version: 4.3.0 13 | # - name: mariadb 14 | # version: 9.3.19 15 | # repository: https://charts.bitnami.com/bitnami 16 | # condition: mariadb.enabled 17 | # - name: postgresql 18 | # version: 10.5.3 19 | # repository: https://charts.bitnami.com/bitnami 20 | # condition: postgresql.enabled 21 | # - name: redis 22 | # version: 14.6.6 23 | # repository: https://charts.bitnami.com/bitnami 24 | # condition: redis.enabled 25 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/metrics-server/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: metrics-server 6 | namespace: kube-system 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://kubernetes-sigs.github.io/metrics-server 12 | chart: metrics-server 13 | version: 3.7.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: metrics-server-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | args: 21 | - --kubelet-insecure-tls 22 | - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname 23 | - --kubelet-use-node-status-port 24 | - --metric-resolution=15s 25 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/monitoring/pod-monitor.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PodMonitor 4 | metadata: 5 | name: flux-system 6 | namespace: flux-system 7 | labels: 8 | app.kubernetes.io/part-of: flux 9 | spec: 10 | namespaceSelector: 11 | matchNames: 12 | - flux-system 13 | selector: 14 | matchExpressions: 15 | - key: app 16 | operator: In 17 | values: 18 | - helm-controller 19 | - kustomize-controller 20 | - notification-controller 21 | - source-controller 22 | podMetricsEndpoints: 23 | - port: http-prom 24 | relabelings: 25 | - sourceLabels: ['namespace', 'job'] 26 | regex: ".+/(.*)" 27 | targetLabel: "job" 28 | replacement: "$1" 29 | -------------------------------------------------------------------------------- /cluster/core/metallb-system/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: metallb 6 | namespace: metallb-system 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://metallb.github.io/metallb 12 | chart: metallb 13 | version: 0.11.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: metallb-charts 17 | namespace: flux-system 18 | values: 19 | configInline: 20 | address-pools: 21 | - name: default 22 | protocol: layer2 23 | addresses: 24 | - "${METALLB_LB_RANGE}" 25 | prometheus: 26 | podMonitor: 27 | enabled: true 28 | prometheusRule: 29 | enabled: true 30 | -------------------------------------------------------------------------------- /.github/workflows/lint-shell.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint Shell scripts on Pull Requests 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | paths: 7 | - "**.sh" 8 | 9 | jobs: 10 | shellcheck: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - name: Get changes 16 | uses: dorny/paths-filter@v2 17 | id: filter 18 | with: 19 | list-files: shell 20 | filters: | 21 | shell: 22 | - added|modified: "**.sh" 23 | - name: Lint files 24 | if: ${{ steps.filter.outputs.shell == 'true' }} 25 | uses: reviewdog/action-shellcheck@v1 26 | with: 27 | shellcheck_flags: "${{ steps.filter.outputs.shell_files }}" 28 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/reloader/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: reloader 6 | namespace: kube-system 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://stakater.github.io/stakater-charts 12 | chart: reloader 13 | version: v0.0.104 14 | sourceRef: 15 | kind: HelmRepository 16 | name: stakater-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | nameOverride: reloader 21 | fullnameOverride: reloader 22 | reloader: 23 | deployment: 24 | image: 25 | name: ghcr.io/k8s-at-home/reloader 26 | podMonitor: 27 | enabled: true 28 | namespace: kube-system 29 | -------------------------------------------------------------------------------- /.taskfiles/flux.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | tasks: 5 | sync: 6 | desc: Sync flux-system with the Git Repository 7 | cmds: 8 | - flux reconcile source git flux-system 9 | silent: true 10 | 11 | get: 12 | desc: List all flux resources 13 | cmds: 14 | - flux get all -A 15 | silent: true 16 | 17 | hr: 18 | desc: List all Helm Releases 19 | cmds: 20 | - flux get hr -A 21 | silent: true 22 | 23 | hs: 24 | desc: List all Helm sources 25 | cmds: 26 | - flux get sources helm -A 27 | silent: true 28 | 29 | hc: 30 | desc: List all Helm charts 31 | cmds: 32 | - flux get sources chart -A 33 | silent: true 34 | 35 | k: 36 | desc: List all Kustomizations 37 | cmds: 38 | - flux get kustomizations -A 39 | silent: true 40 | -------------------------------------------------------------------------------- /cluster/apps/devices/smarter-device-manager/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: smarter-device-manager 6 | namespace: devices 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: smarter-device-manager 13 | version: 6.3.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: registry.gitlab.com/arm-research/smarter/smarter-device-manager 22 | tag: v1.20.7 23 | config: | 24 | - devicematch: ^ttyUSB[0-9]*$ 25 | nummaxdevices: 1 26 | - devicematch: ^video[0-9]*$ 27 | nummaxdevices: 1 28 | -------------------------------------------------------------------------------- /.github/workflows/lint-markdown.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint Markdown files on Pull Requests 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | paths: 7 | - "**.md" 8 | 9 | jobs: 10 | markdownlint: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - name: Get changes 16 | uses: dorny/paths-filter@v2 17 | id: filter 18 | with: 19 | list-files: shell 20 | filters: | 21 | markdown: 22 | - added|modified: "**.md" 23 | - name: Lint files 24 | if: ${{ steps.filter.outputs.markdown == 'true' }} 25 | uses: avto-dev/markdown-lint@v1.5.0 26 | with: 27 | config: '.github/lint/.markdownlint.yaml' 28 | args: '${{ steps.filter.outputs.markdown_files }}' 29 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint YAML files on Pull Requests 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | paths: 7 | - "**.yaml" 8 | - "**.yml" 9 | 10 | jobs: 11 | yamllint: 12 | runs-on: ubuntu-20.04 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | - name: Get changes 17 | uses: dorny/paths-filter@v2 18 | id: filter 19 | with: 20 | list-files: shell 21 | filters: | 22 | yaml: 23 | - added|modified: "**.yaml" 24 | - added|modified: "**.yml" 25 | - name: Lint files 26 | if: ${{ steps.filter.outputs.yaml == 'true' }} 27 | uses: reviewdog/action-yamllint@v1 28 | with: 29 | yamllint_flags: "-c .github/lint/.yamllint.yaml ${{ steps.filter.outputs.yaml_files }}" 30 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/slack/notification.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: notification.toolkit.fluxcd.io/v1beta1 3 | kind: Provider 4 | metadata: 5 | name: slack 6 | namespace: flux-system 7 | spec: 8 | type: slack 9 | username: Flux 10 | channel: flux 11 | secretRef: 12 | name: slack-webhook-url 13 | --- 14 | apiVersion: notification.toolkit.fluxcd.io/v1beta1 15 | kind: Alert 16 | metadata: 17 | name: home-cluster 18 | namespace: flux-system 19 | spec: 20 | providerRef: 21 | name: slack 22 | eventSeverity: error 23 | eventSources: 24 | - kind: GitRepository 25 | name: "*" 26 | - kind: Kustomization 27 | name: "*" 28 | - kind: HelmRepository 29 | name: "*" 30 | - kind: HelmRelease 31 | name: "*" 32 | exclusionList: 33 | - "error.*lookup github\\.com" 34 | - "waiting.*socket" 35 | suspend: false 36 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/nfs-subdir-external-provisioner/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: nfs-subdir-external-provisioner 6 | namespace: kube-system 7 | labels: 8 | kustomize.toolkit.fluxcd.io/substitute: "disabled" 9 | spec: 10 | interval: 5m 11 | chart: 12 | spec: 13 | # renovate: registryUrl=https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ 14 | chart: nfs-subdir-external-provisioner 15 | version: 4.0.16 16 | sourceRef: 17 | kind: HelmRepository 18 | name: nfs-subdir-external-provisioner-charts 19 | namespace: flux-system 20 | interval: 5m 21 | values: 22 | nfs: 23 | server: "192.168.1.11" 24 | path: "/mnt/share" 25 | storageClass: 26 | defaultClass: true 27 | pathPattern: "${.PVC.namespace}-${.PVC.name}" 28 | -------------------------------------------------------------------------------- /.taskfiles/format.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | tasks: 5 | all: 6 | - task: markdown 7 | - task: yaml 8 | 9 | markdown: 10 | desc: Format Markdown 11 | cmds: 12 | - >- 13 | prettier 14 | --ignore-path '.github/lint/.prettierignore' 15 | --config '.github/lint/.prettierrc.yaml' 16 | --list-different 17 | --ignore-unknown 18 | --parser=markdown 19 | --write '*.md' '**/*.md' 20 | ignore_error: true 21 | silent: true 22 | 23 | yaml: 24 | desc: Format YAML 25 | cmds: 26 | - >- 27 | prettier 28 | --ignore-path '.github/lint/.prettierignore' 29 | --config 30 | '.github/lint/.prettierrc.yaml' 31 | --list-different 32 | --ignore-unknown 33 | --parser=yaml 34 | --write '*.y*ml' 35 | '**/*.y*ml' 36 | ignore_error: true 37 | silent: true 38 | -------------------------------------------------------------------------------- /cluster/apps/kube-system/home-dns/rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: home-dns 6 | namespace: kube-system 7 | --- 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: home-dns 12 | rules: 13 | - apiGroups: 14 | - "" 15 | resources: 16 | - services 17 | - namespaces 18 | verbs: 19 | - list 20 | - watch 21 | - apiGroups: 22 | - extensions 23 | - networking.k8s.io 24 | resources: 25 | - ingresses 26 | verbs: 27 | - list 28 | - watch 29 | --- 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRoleBinding 32 | metadata: 33 | name: home-dns 34 | roleRef: 35 | apiGroup: rbac.authorization.k8s.io 36 | kind: ClusterRole 37 | name: home-dns 38 | subjects: 39 | - kind: ServiceAccount 40 | name: home-dns 41 | namespace: kube-system 42 | -------------------------------------------------------------------------------- /server/packer/images/variables.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "proxmox_ip" { 2 | type = string 3 | description = "The IP of the proxmox server." 4 | } 5 | 6 | variable "proxmox_port" { 7 | type = string 8 | description = "The port used by the proxmox management interface." 9 | default = "8006" 10 | } 11 | 12 | variable "proxmox_username" { 13 | type = string 14 | description = "The user used to connect to proxmox." 15 | } 16 | 17 | variable "proxmox_password" { 18 | type = string 19 | description = "The password for the proxmox user." 20 | sensitive = true 21 | } 22 | 23 | variable "proxmox_node" { 24 | type = string 25 | description = "The name of the proxmox node on which to create the template." 26 | } 27 | 28 | variable "proxmox_iso_storage_pool" { 29 | type = string 30 | description = "The name of the proxmox storage pool to store ISOs in." 31 | default = "local" 32 | } 33 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: cert-manager 6 | namespace: cert-manager 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://charts.jetstack.io/ 12 | chart: cert-manager 13 | version: v1.6.1 14 | sourceRef: 15 | kind: HelmRepository 16 | name: jetstack-charts 17 | namespace: flux-system 18 | values: 19 | installCRDs: false 20 | webhook: 21 | enabled: true 22 | extraArgs: 23 | - --dns01-recursive-nameservers=1.1.1.1:53 24 | - --dns01-recursive-nameservers-only 25 | replicaCount: 1 26 | podDnsPolicy: "None" 27 | podDnsConfig: 28 | nameservers: 29 | - "1.1.1.1" 30 | - "8.8.8.8" 31 | prometheus: 32 | enabled: true 33 | servicemonitor: 34 | enabled: true 35 | prometheusInstance: observability 36 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/dashboard/ingress.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: Ingress 4 | metadata: 5 | name: traefik-dashboard 6 | namespace: networking 7 | annotations: 8 | cert-manager.io/cluster-issuer: "letsencrypt-production" 9 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 10 | traefik.ingress.kubernetes.io/router.middlewares: "networking-basic-auth@kubernetescrd" 11 | hajimari.io/enable: "true" 12 | hajimari.io/icon: "web" 13 | hajimari.io/appName: "traefik" 14 | spec: 15 | ingressClassName: "traefik" 16 | tls: 17 | - hosts: 18 | - "traefik.${SECRET_DOMAIN}" 19 | secretName: "traefik-${SECRET_DOMAIN//./-}-tls" 20 | rules: 21 | - host: "traefik.${SECRET_DOMAIN}" 22 | http: 23 | paths: 24 | - path: / 25 | pathType: Prefix 26 | backend: 27 | service: 28 | name: traefik 29 | port: 30 | number: 9000 31 | -------------------------------------------------------------------------------- /cluster/apps/observability/unifi-poller/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: unifi-poller 6 | namespace: observability 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: unifi-poller 13 | version: 10.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: ghcr.io/k8s-at-home/unpoller 22 | tag: 2.1.3 23 | env: 24 | UP_UNIFI_DEFAULT_ROLE: "homelab-controller" 25 | UP_UNIFI_DEFAULT_URL: "https://unifi.${SECRET_DOMAIN}" 26 | UP_UNIFI_DEFAULT_USER: "${UNIFI_USER}" 27 | UP_UNIFI_DEFAULT_PASS: "${UNIFI_PASS}" 28 | UP_INFLUXDB_DISABLE: true 29 | metrics: 30 | enabled: true 31 | serviceMonitor: 32 | interval: 1m 33 | scrapeTimeout: 30s 34 | prometheusRule: 35 | enabled: true 36 | -------------------------------------------------------------------------------- /cluster/apps/organization/dokuwiki/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: dokuwiki 6 | namespace: organization 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://charts.bitnami.com/bitnami 12 | chart: dokuwiki 13 | version: 12.2.2 14 | sourceRef: 15 | kind: HelmRepository 16 | name: bitnami-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | dokuwikiUsername: alex 21 | existingSecret: dokuwiki-password 22 | service: 23 | type: ClusterIP 24 | persistence: 25 | existingClaim: dokuwiki-data 26 | ingress: 27 | enabled: true 28 | ingressClassName: "traefik" 29 | annotations: 30 | cert-manager.io/cluster-issuer: "letsencrypt-production" 31 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 32 | hajimari.io/enable: "true" 33 | hajimari.io/icon: "file-document" 34 | hostname: "wiki.${SECRET_DOMAIN}" 35 | path: / 36 | certManager: true 37 | tls: true 38 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/monitoring/prometheus-rule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PrometheusRule 4 | metadata: 5 | name: flux 6 | namespace: flux-system 7 | spec: 8 | groups: 9 | - name: flux 10 | rules: 11 | - alert: FluxComponentAbsent 12 | annotations: 13 | description: Flux component has disappeared from Prometheus target discovery. 14 | summary: Flux component is down. 15 | expr: | 16 | absent(up{job=~".*flux-system.*"} == 1) 17 | for: 5m 18 | labels: 19 | severity: critical 20 | - alert: FluxReconciliationFailure 21 | annotations: 22 | description: '{{ $labels.kind }} {{ $labels.namespace }}/{{ $labels.name }} reconciliation has been failing 23 | for more than ten minutes.' 24 | summary: Flux reconciliation failure. 25 | expr: | 26 | max(gotk_reconcile_condition{status="False",type="Ready"}) by (namespace, name, kind) 27 | + 28 | on(namespace, name, kind) (max(gotk_reconcile_condition{status="Deleted"}) 29 | by (namespace, name, kind)) * 2 == 1 30 | for: 10m 31 | labels: 32 | severity: critical 33 | -------------------------------------------------------------------------------- /.github/workflows/helm-deps-update-schedule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Schedule - Update Kah Library Helm chart 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: "0 */4 * * *" 8 | 9 | jobs: 10 | update-helm-deps: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: azure/setup-helm@v1 15 | with: 16 | version: v3.6.3 17 | - run: helm repo add k8s-at-home-library https://library-charts.k8s-at-home.com 18 | - run: helm repo update 19 | - run: helm dep update 20 | working-directory: charts/kah-common 21 | - uses: peter-evans/create-pull-request@v3 22 | with: 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | branch: renovate-kah-library-chart 25 | delete-branch: true 26 | title: "chore(deps): update kah library chart" 27 | signoff: true 28 | committer: "Alex Waibel " 29 | author: "Alex Waibel " 30 | assignees: "alexwaibel" 31 | commit-message: "chore(deps): update kah library chart" 32 | body: | 33 | Update the kah library chart 34 | labels: renovate/kah-library-chart 35 | -------------------------------------------------------------------------------- /server/terraform/proxmox/fileserver.tf: -------------------------------------------------------------------------------- 1 | resource "proxmox_vm_qemu" "fileserver" { 2 | name = "fileserver" 3 | desc = "Debian nfs share" 4 | target_node = var.proxmox_node 5 | vmid = 801 6 | 7 | clone = "debian-cloudinit" 8 | os_type = "cloud-init" 9 | boot = "c" 10 | 11 | agent = 1 12 | ipconfig0 = "ip=${var.fileserver_ip_address}/24,gw=192.168.1.1" 13 | sshkeys = file(var.ssh_public_key) 14 | 15 | disk { 16 | cache = "none" 17 | format = "raw" 18 | size = "200G" 19 | storage = "local-zfs" 20 | type = "scsi" 21 | } 22 | 23 | network { 24 | bridge = "vmbr0" 25 | firewall = false 26 | link_down = false 27 | macaddr = "C6:3D:90:63:41:A6" 28 | model = "virtio" 29 | } 30 | 31 | provisioner "remote-exec" { 32 | inline = ["echo provisioned"] 33 | 34 | connection { 35 | host = var.fileserver_ip_address 36 | type = "ssh" 37 | user = var.fileserver_user 38 | agent = true 39 | } 40 | } 41 | 42 | provisioner "local-exec" { 43 | command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook --user ${var.fileserver_user} --inventory '../../ansible/inventory' ../../ansible/playbooks/storage/fileserver.yaml" 44 | } 45 | 46 | depends_on = [local_file.ansible_inventory] 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/renovate-schedule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Schedule - Renovate Helm Releases 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: "0 */12 * * *" 8 | 9 | jobs: 10 | renovate-helm-releases: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | token: ${{ secrets.GITHUB_TOKEN }} 16 | fetch-depth: 1 17 | 18 | - name: Renovate Helm Releases 19 | uses: k8s-at-home/renovate-helm-releases@v1 20 | with: 21 | cluster-path: "./cluster" 22 | 23 | - name: Create pull request for renovatebot helm-release annotations 24 | uses: peter-evans/create-pull-request@v3 25 | with: 26 | token: ${{ secrets.GITHUB_TOKEN }} 27 | branch: renovate-annotations 28 | delete-branch: true 29 | title: "(deps) chore: update renovate annotations" 30 | signoff: true 31 | committer: GitHub 32 | author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> 33 | assignees: "alexwaibel" 34 | commit-message: "(deps) chore: update renovate annotations" 35 | body: | 36 | Update HelmReleases using Renovate to pick up new Helm chart versions 37 | labels: renovate/annotations 38 | -------------------------------------------------------------------------------- /cluster/apps/home/mopidy/mopidy-config-map.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: mopidy-config 6 | namespace: home 7 | data: 8 | mopidy.conf: | 9 | # For information about configuration values that can be set in this file see: 10 | # 11 | # https://docs.mopidy.com/en/latest/config/ 12 | # 13 | # Run `sudo mopidyctl config` to see the current effective config, based on 14 | # both defaults and this configuration file. 15 | 16 | [audio] 17 | output = tee name=t t. ! queue ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo t. ! queue ! lamemp3enc bitrate=320 ! shout2send async=false mount=listen ip=mopidy-icecast port=8000 password=${SECRET_ICECAST_PASSWORD} 18 | 19 | [gmusic] 20 | enabled = false 21 | 22 | [http] 23 | hostname = 0.0.0.0 24 | default_app = iris 25 | 26 | [iris] 27 | country = US 28 | locale = en_US 29 | 30 | [pandora] 31 | enabled = false 32 | 33 | [party] 34 | enabled = true 35 | votes_to_skip = 2 36 | 37 | [spotify] 38 | username = ${SECRET_SPOTIFY_USERNAME} 39 | password = ${SECRET_SPOTIFY_PASSWORD} 40 | client_id = ${SECRET_MOPIDY_SPOTIFY_CLIENT_ID} 41 | client_secret = ${SECRET_MOPIDY_SPOTIFY_CLIENT_SECRET} 42 | bitrate = 320 43 | toplist_countries = US 44 | 45 | [soundcloud] 46 | enabled = false 47 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.0.1 5 | hooks: 6 | - id: check-added-large-files 7 | - id: check-case-conflict 8 | - id: check-merge-conflict 9 | - id: check-vcs-permalinks 10 | - id: detect-private-key 11 | - id: end-of-file-fixer 12 | - id: mixed-line-ending 13 | - id: trailing-whitespace 14 | - repo: https://github.com/adrienverge/yamllint.git 15 | rev: v1.26.3 16 | hooks: 17 | - id: yamllint 18 | args: 19 | - --config-file 20 | - .github/lint/.yamllint.yaml 21 | - repo: https://github.com/shellcheck-py/shellcheck-py 22 | rev: v0.7.1.1 23 | hooks: 24 | - id: shellcheck 25 | - repo: git://github.com/antonbabenko/pre-commit-terraform 26 | rev: v1.50.0 27 | hooks: 28 | - id: terraform_fmt 29 | - id: terraform_validate 30 | - id: terraform_tflint 31 | - id: terraform_tfsec 32 | - repo: https://github.com/cisagov/pre-commit-packer 33 | rev: v0.0.2 34 | hooks: 35 | - id: packer_fmt 36 | - repo: https://github.com/k8s-at-home/sops-pre-commit 37 | rev: v2.0.3 38 | hooks: 39 | - id: forbid-secrets 40 | - repo: https://github.com/igorshubovych/markdownlint-cli 41 | rev: v0.28.1 42 | hooks: 43 | - id: markdownlint 44 | args: 45 | - --config 46 | - .github/lint/.markdownlint.yaml 47 | -------------------------------------------------------------------------------- /cluster/apps/home/zwavejs2mqtt/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: zwavejs2mqtt 6 | namespace: home 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: zwavejs2mqtt 13 | version: 5.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: ghcr.io/zwave-js/zwavejs2mqtt 22 | tag: 6.5.1 23 | ingress: 24 | main: 25 | enabled: true 26 | ingressClassName: "traefik" 27 | annotations: 28 | cert-manager.io/cluster-issuer: "letsencrypt-production" 29 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 30 | hosts: 31 | - host: "zwavejs2mqtt.${SECRET_DOMAIN}" 32 | paths: 33 | - path: / 34 | pathType: Prefix 35 | tls: 36 | - hosts: 37 | - "zwavejs2mqtt.${SECRET_DOMAIN}" 38 | secretName: "zwavejs2mqtt-${SECRET_DOMAIN//./-}-tls" 39 | persistence: 40 | config: 41 | enabled: true 42 | existingClaim: zwavejs2mqtt-config 43 | mountPath: "/usr/src/app/store" 44 | resources: 45 | limits: 46 | smarter-devices/ttyUSB0: 1 47 | requests: 48 | smarter-devices/ttyUSB0: 1 49 | -------------------------------------------------------------------------------- /server/packer/templates/k3os-config.pkrtpl.hcl: -------------------------------------------------------------------------------- 1 | ssh_authorized_keys: 2 | - github:${github_username} 3 | write_files: 4 | - path: /var/lib/connman/default.config 5 | content: |- 6 | [service_eth0] 7 | Type = ethernet 8 | IPv4 = ${node_ip}/255.255.255.0/192.168.1.1 9 | Nameservers = ${nameserver} 10 | - path: /etc/conf.d/qemu-guest-agent 11 | content: |- 12 | GA_PATH=/dev/vport2p1 13 | owner: root 14 | permissions: '0644' 15 | hostname: ${hostname} 16 | 17 | k3os: 18 | ntp_servers: 19 | - 0.us.pool.ntp.org 20 | - 1.us.pool.ntp.org 21 | k3s_args: 22 | - server 23 | - "--disable" 24 | - "traefik" 25 | - "--disable" 26 | - "servicelb" 27 | - "--disable" 28 | - "metrics-server" 29 | - "--disable" 30 | - "local-storage" 31 | - "--kube-controller-manager-arg" 32 | - "address=0.0.0.0" 33 | - "--kube-controller-manager-arg" 34 | - "bind-address=0.0.0.0" 35 | - "--kube-proxy-arg" 36 | - "metrics-bind-address=0.0.0.0" 37 | - "--kube-scheduler-arg" 38 | - "address=0.0.0.0" 39 | - "--kube-scheduler-arg" 40 | - "bind-address=0.0.0.0" 41 | - "--kube-scheduler-arg" 42 | - "feature-gates=MixedProtocolLBService=true" 43 | - "--kube-proxy-arg" 44 | - "feature-gates=MixedProtocolLBService=true" 45 | - "--kube-controller-manager-arg" 46 | - "feature-gates=MixedProtocolLBService=true" 47 | - "--kube-apiserver-arg" 48 | - "feature-gates=MixedProtocolLBService=true" 49 | - "--kubelet-arg" 50 | - "feature-gates=MixedProtocolLBService=true" 51 | -------------------------------------------------------------------------------- /server/terraform/proxmox/variables.tf: -------------------------------------------------------------------------------- 1 | variable "proxmox_ip" { 2 | type = string 3 | description = "The IP of the proxmox server." 4 | } 5 | 6 | variable "proxmox_username" { 7 | type = string 8 | description = "The user used to connect to proxmox." 9 | } 10 | 11 | variable "proxmox_node" { 12 | type = string 13 | description = "The name of the proxmox node." 14 | } 15 | 16 | variable "proxmox_disk_storage_pool" { 17 | type = string 18 | description = "The name of the proxmox storage pool to store disks in." 19 | default = "local-zfs" 20 | } 21 | 22 | variable "ssh_public_key" { 23 | description = "The location of the SSH public key." 24 | type = string 25 | default = "~/.ssh/id_rsa.pub" 26 | } 27 | 28 | variable "fileserver_ip_address" { 29 | description = "The IP address to be used for NFS fileserver." 30 | type = string 31 | } 32 | 33 | variable "fileserver_user" { 34 | description = "The user account for the fileserver." 35 | type = string 36 | default = "debian" 37 | } 38 | 39 | variable "master_node_ip_address" { 40 | description = "The IP address to be used for master k3os node." 41 | type = string 42 | } 43 | 44 | variable "k3os_user" { 45 | description = "The user for the k3os machine." 46 | type = string 47 | default = "rancher" 48 | } 49 | 50 | variable "coredns_ip_address" { 51 | description = "The IP address to be used for coredns." 52 | type = string 53 | } 54 | 55 | variable "coredns_user" { 56 | description = "The user for the k3os machine." 57 | type = string 58 | default = "root" 59 | } 60 | -------------------------------------------------------------------------------- /.github/workflows/flux-schedule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Schedule - Update Flux 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: "0 12 * * *" 8 | 9 | jobs: 10 | flux-upgrade: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 1 16 | 17 | - name: Setup Flux CLI 18 | uses: fluxcd/flux2/action@main 19 | 20 | - name: Upgrade Flux 21 | id: upgrade 22 | run: | 23 | CLI_VERSION="$(flux -v)" 24 | VERSION="v${CLI_VERSION#*flux version }" 25 | flux install --version="${VERSION}" \ 26 | --network-policy=false \ 27 | --export > ./cluster/base/flux-system/gotk-components.yaml 28 | echo "::set-output name=flux_version::$VERSION" 29 | - name: Create pull request for Flux upgrade 30 | uses: peter-evans/create-pull-request@v3 31 | with: 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | branch: "flux/upgrade-${{ steps.upgrade.outputs.flux_version }}" 34 | delete-branch: true 35 | title: "chore(deps): upgrade flux components to ${{ steps.upgrade.outputs.flux_version }}" 36 | signoff: false 37 | committer: GitHub 38 | author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> 39 | commit-message: "chore(deps): upgrade flux components to ${{ steps.upgrade.outputs.flux_version }}" 40 | body: | 41 | Release notes: https://github.com/fluxcd/flux2/releases/tag/${{ steps.upgrade.outputs.flux_version }} 42 | labels: flux/upgrade 43 | -------------------------------------------------------------------------------- /cluster/apps/networking/unifi/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: unifi 6 | namespace: networking 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: unifi 13 | version: 4.6.1 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | env: 21 | RUNAS_UID0: "true" 22 | image: 23 | repository: jacobalberty/unifi 24 | tag: v6.5.55 25 | service: 26 | main: 27 | type: LoadBalancer 28 | loadBalancerIP: ${SVC_UNIFI_ADDR} 29 | ports: 30 | http: 31 | protocol: HTTPS 32 | persistence: 33 | data: 34 | enabled: true 35 | existingClaim: unifi-data 36 | ingress: 37 | main: 38 | enabled: true 39 | ingressClassName: "traefik" 40 | annotations: 41 | cert-manager.io/cluster-issuer: "letsencrypt-production" 42 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 43 | hajimari.io/enable: "true" 44 | hajimari.io/icon: "access-point" 45 | hosts: 46 | - host: "unifi.${SECRET_DOMAIN}" 47 | paths: 48 | - path: / 49 | pathType: Prefix 50 | tls: 51 | - hosts: 52 | - "unifi.${SECRET_DOMAIN}" 53 | secretName: "unifi-${SECRET_DOMAIN//./-}-tls" 54 | resources: 55 | requests: 56 | cpu: 15m 57 | memory: 500Mi 58 | limits: 59 | memory: 750Mi 60 | -------------------------------------------------------------------------------- /cluster/apps/media/hyperion-ng/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: hyperion-ng 6 | namespace: media 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: hyperion-ng 13 | version: 5.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: sirfragalot/hyperion.ng 22 | tag: 2.0.0-alpha.9-x86_64 23 | resources: 24 | limits: 25 | smarter-devices/ttyUSB2: 1 26 | smarter-devices/video0: 1 27 | smarter-devices/video1: 1 28 | requests: 29 | smarter-devices/ttyUSB2: 1 30 | service: 31 | main: 32 | type: LoadBalancer 33 | loadBalancerIP: ${SVC_HYPERION_ADDR} 34 | ingress: 35 | main: 36 | enabled: true 37 | ingressClassName: "traefik" 38 | annotations: 39 | cert-manager.io/cluster-issuer: "letsencrypt-production" 40 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 41 | hajimari.io/enable: "true" 42 | hajimari.io/icon: "television-ambient-light" 43 | hosts: 44 | - host: "hyperion.${SECRET_DOMAIN}" 45 | paths: 46 | - path: / 47 | pathType: Prefix 48 | tls: 49 | - hosts: 50 | - "hyperion.${SECRET_DOMAIN}" 51 | secretName: "hyperion-${SECRET_DOMAIN//./-}-tls" 52 | persistence: 53 | config: 54 | enabled: true 55 | existingClaim: hyperion-ng-config 56 | -------------------------------------------------------------------------------- /server/ansible/playbooks/dns/coredns.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - localhost 4 | tasks: 5 | - name: Fetch service account for home-dns 6 | kubernetes.core.k8s: 7 | namespace: kube-system 8 | kind: ServiceAccount 9 | name: home-dns 10 | register: DNSServiceAccountRaw 11 | until: DNSServiceAccountRaw | json_query('result.secrets') is iterable 12 | retries: 5 13 | delay: 10 14 | 15 | - name: Fetch CA from service account secret 16 | kubernetes.core.k8s: 17 | namespace: kube-system 18 | kind: Secret 19 | name: "{{ DNSServiceAccountRaw | json_query('result.secrets[0].name') }}" 20 | register: DNSCertificateAuthorityRaw 21 | 22 | - name: Extract CA Cert from CA 23 | set_fact: 24 | DNSCertificateAuthority: '{{ DNSCertificateAuthorityRaw | json_query(''result.data."ca.crt"'') }}' 25 | 26 | - name: Extract CA Token from CA 27 | set_fact: 28 | DNSCertificateAuthorityToken: "{{ DNSCertificateAuthorityRaw | json_query('result.data.token') | b64decode }}" 29 | 30 | - name: Create kubeconfig 31 | copy: 32 | dest: "/tmp/kubeconfig-homedns" 33 | content: | 34 | --- 35 | apiVersion: v1 36 | kind: Config 37 | clusters: 38 | - name: home 39 | cluster: 40 | certificate-authority-data: {{ DNSCertificateAuthority }} 41 | server: https://{{ master_node_ip_address }}:6443 42 | contexts: 43 | - name: home 44 | context: 45 | cluster: home 46 | user: home-dns 47 | users: 48 | - name: home-dns 49 | user: 50 | token: {{ DNSCertificateAuthorityToken }} 51 | current-context: home 52 | mode: 0755 53 | 54 | - name: Install coredns service 55 | import_playbook: coredns-service.yaml 56 | -------------------------------------------------------------------------------- /cluster/apps/home/emqx/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: emqx 6 | namespace: home 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://repos.emqx.io/charts 12 | chart: emqx 13 | version: 4.3.12 14 | sourceRef: 15 | kind: HelmRepository 16 | name: emqx-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: public.ecr.aws/emqx/emqx 22 | service: 23 | type: LoadBalancer 24 | loadBalancerIP: ${SVC_EMQX_ADDR} 25 | emqxConfig: 26 | EMQX_ALLOW_ANONYMOUS: "false" 27 | EMQX_ADMIN_PASSWORD: "${SECRET_EMQX_ADMIN_PASSWORD}" 28 | EMQX_AUTH__MNESIA__PASSWORD_HASH: plain 29 | EMQX_AUTH__USER__1__USERNAME: "${SECRET_MQTT_USERNAME}" 30 | EMQX_AUTH__USER__1__PASSWORD: "${SECRET_MQTT_PASSWORD}" 31 | emqxLoadedPlugins: > 32 | {emqx_auth_mnesia, true}. 33 | {emqx_bridge_mqtt, false}. 34 | {emqx_dashboard, true}. 35 | {emqx_management, true}. 36 | {emqx_recon, true}. 37 | {emqx_retainer, true}. 38 | {emqx_rule_engine, true}. 39 | {emqx_telemetry, false}. 40 | persistence: 41 | enabled: true 42 | size: 100Mi 43 | storageClass: "nfs-client" 44 | accessMode: ReadWriteOnce 45 | ingress: 46 | dashboard: 47 | enabled: true 48 | ingressClassName: "traefik" 49 | annotations: 50 | cert-manager.io/cluster-issuer: "letsencrypt-production" 51 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 52 | hajimari.io/enable: "true" 53 | hajimari.io/icon: "rss" 54 | path: / 55 | hosts: 56 | - "emqx.${SECRET_DOMAIN}" 57 | tls: 58 | - hosts: 59 | - "emqx.${SECRET_DOMAIN}" 60 | secretName: "emqx-${SECRET_DOMAIN//./-}-tls" 61 | -------------------------------------------------------------------------------- /cluster/crds/kube-prometheus-stack/crds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: GitRepository 4 | metadata: 5 | name: kube-prometheus-stack-source 6 | namespace: flux-system 7 | spec: 8 | interval: 30m 9 | url: https://github.com/prometheus-community/helm-charts.git 10 | ref: 11 | # renovate: registryUrl=https://prometheus-community.github.io/helm-charts 12 | tag: kube-prometheus-stack-21.0.3 13 | ignore: | 14 | # exclude all 15 | /* 16 | # include deploy crds dir 17 | !/charts/kube-prometheus-stack/crds 18 | --- 19 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 20 | kind: Kustomization 21 | metadata: 22 | name: kube-prometheus-stack-crds 23 | namespace: flux-system 24 | spec: 25 | interval: 15m 26 | prune: false 27 | sourceRef: 28 | kind: GitRepository 29 | name: kube-prometheus-stack-source 30 | healthChecks: 31 | - apiVersion: apiextensions.k8s.io/v1 32 | kind: CustomResourceDefinition 33 | name: alertmanagerconfigs.monitoring.coreos.com 34 | - apiVersion: apiextensions.k8s.io/v1 35 | kind: CustomResourceDefinition 36 | name: alertmanagers.monitoring.coreos.com 37 | - apiVersion: apiextensions.k8s.io/v1 38 | kind: CustomResourceDefinition 39 | name: podmonitors.monitoring.coreos.com 40 | - apiVersion: apiextensions.k8s.io/v1 41 | kind: CustomResourceDefinition 42 | name: probes.monitoring.coreos.com 43 | - apiVersion: apiextensions.k8s.io/v1 44 | kind: CustomResourceDefinition 45 | name: prometheuses.monitoring.coreos.com 46 | - apiVersion: apiextensions.k8s.io/v1 47 | kind: CustomResourceDefinition 48 | name: prometheusrules.monitoring.coreos.com 49 | - apiVersion: apiextensions.k8s.io/v1 50 | kind: CustomResourceDefinition 51 | name: servicemonitors.monitoring.coreos.com 52 | - apiVersion: apiextensions.k8s.io/v1 53 | kind: CustomResourceDefinition 54 | name: thanosrulers.monitoring.coreos.com 55 | -------------------------------------------------------------------------------- /cluster/crds/traefik/crds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1beta1 3 | kind: GitRepository 4 | metadata: 5 | name: traefik-crd-source 6 | namespace: flux-system 7 | spec: 8 | interval: 30m 9 | url: https://github.com/traefik/traefik-helm-chart.git 10 | ref: 11 | # renovate: registryUrl=https://helm.traefik.io/traefik chart=traefik 12 | tag: v10.14.1 13 | ignore: | 14 | # exclude all 15 | /* 16 | # path to crds 17 | !/traefik/crds/ 18 | --- 19 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 20 | kind: Kustomization 21 | metadata: 22 | name: traefik-crds 23 | namespace: flux-system 24 | spec: 25 | interval: 15m 26 | prune: false 27 | sourceRef: 28 | kind: GitRepository 29 | name: traefik-crd-source 30 | healthChecks: 31 | - apiVersion: apiextensions.k8s.io/v1 32 | kind: CustomResourceDefinition 33 | name: ingressroutes.traefik.containo.us 34 | - apiVersion: apiextensions.k8s.io/v1 35 | kind: CustomResourceDefinition 36 | name: ingressroutetcps.traefik.containo.us 37 | - apiVersion: apiextensions.k8s.io/v1 38 | kind: CustomResourceDefinition 39 | name: ingressrouteudps.traefik.containo.us 40 | - apiVersion: apiextensions.k8s.io/v1 41 | kind: CustomResourceDefinition 42 | name: middlewares.traefik.containo.us 43 | - apiVersion: apiextensions.k8s.io/v1 44 | kind: CustomResourceDefinition 45 | name: middlewaretcps.traefik.containo.us 46 | - apiVersion: apiextensions.k8s.io/v1 47 | kind: CustomResourceDefinition 48 | name: serverstransports.traefik.containo.us 49 | - apiVersion: apiextensions.k8s.io/v1 50 | kind: CustomResourceDefinition 51 | name: tlsoptions.traefik.containo.us 52 | - apiVersion: apiextensions.k8s.io/v1 53 | kind: CustomResourceDefinition 54 | name: tlsstores.traefik.containo.us 55 | - apiVersion: apiextensions.k8s.io/v1 56 | kind: CustomResourceDefinition 57 | name: traefikservices.traefik.containo.us 58 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: cloudflare-token-secret 5 | namespace: cert-manager 6 | stringData: 7 | cloudflare-token: ENC[AES256_GCM,data:2EK2QtUeicxxNZ0GDUr2BlYZxnkLhF+w0kg5+XxepY6jX9iWXrOBbw==,iv:D3vvdimYq+wcrfyvhoK+D3QVm7w569bS4jvSN3G148M=,tag:tQggvr2IZYM8H2M9F9nzUA==,type:str] 8 | sops: 9 | kms: [] 10 | gcp_kms: [] 11 | azure_kv: [] 12 | hc_vault: [] 13 | age: [] 14 | lastmodified: "2021-09-15T02:15:36Z" 15 | mac: ENC[AES256_GCM,data:PDuvDBGC5ix+AtiBTN5yePrIh5fETChMIFsoZj0MmZfaDHCtY8dnJ1D3NfWv2VKjGGkwiCcoPdZxVJCi2r/d6AhKhs9WyS9f6fs804+JubB2fG2dhKGrmjpNFFN/szt5WYNXlbuxZXWz5NWFZUiZsG3GGgV978UD2NEMJmS6YmU=,iv:GpnrIIuRhgsqTATsfCLkUBdiCd/4yIR66vuivXcr3X8=,tag:2Y/rhcw3Pbik7/igdAavrQ==,type:str] 16 | pgp: 17 | - created_at: "2021-09-15T02:15:35Z" 18 | enc: | 19 | -----BEGIN PGP MESSAGE----- 20 | 21 | hQIMA7sq1npTE+1dARAAh3qWENulfNXfDEWlG0trj4laYxIhUBZKj9T0dp73EOMv 22 | e752p7ADPryyy1ri32Aodp6uO+nXZUlxFooVJCRw0NdBVzBhQ7jH5sFPxDjNd5Z9 23 | +NxqKrtB1rReROgE9vfGDoZsk5UZXBt5qeUnYKjnvKP041U6Tyb5gobKCsAfBf0n 24 | HAVEWax8hTvVLJduHKQA2v3Pr1q3Ec9HJ/U54tyJN+Tw/2o9EJ+MbVWswIPCgZDd 25 | P7KFPeD4M0tgXAmEC1/Y1lSmEBAAGUGgbv6+3+67jKVZ0g1rgVqe2deSs/xOVh2I 26 | mHNLYLYoh8IFeAcOU414y/vf38kgZfxUJ12bZIq2bPKPpePxgodLUALu5qbD3abv 27 | 0DvrOIvhLYUjqqzagecDacujo40SUdyCdc7uMUzdBDj18rdgaqGoAqmuMA9ei6KL 28 | fmpxIQc78b9MtnCYlgUdPuW1TcbOSH4VtkjLog4viR5CsXBkf2bYDgMpW8BEEkdP 29 | 6VxBTAtVpXBu9IPdl2fgTgKKtejCOBO0E461Q2wfdg9ozHQyPGwPw7U3+DgX0adV 30 | iFEZDamkiJwGECJUQ53OVFOiY2/lw6ymtdLH76jJGwtnEsrBcccSM5C/x5sBvpqo 31 | mI786x+Rus0ODdetDu3WESMiFUIQjjG1HK/gbLJi0QfGWLTlwUf7zyeBn3+o47XS 32 | XgEuoExyqbXahIUu/XCe8xrPJrB0k3/N4UbiURbtXoqdj5QC4eRmrIcXvcV42Eif 33 | /2pe8YEgev0Y8jv8WlMeVpKWCsa9mwucfn+DTD0HQ00HWaiA/Z/Ms1KImwElNyg= 34 | =+P2b 35 | -----END PGP MESSAGE----- 36 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 37 | encrypted_regex: ^(data|stringData)$ 38 | version: 3.7.1 39 | -------------------------------------------------------------------------------- /server/packer/images/debian.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "domain" { 2 | type = string 3 | description = "Domain name for the network." 4 | } 5 | 6 | source "proxmox" "debian_cloudinit" { 7 | proxmox_url = "https://${var.proxmox_ip}:${var.proxmox_port}/api2/json" 8 | username = var.proxmox_username 9 | password = var.proxmox_password 10 | node = var.proxmox_node 11 | iso_url = "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.0.0-amd64-netinst.iso" 12 | iso_storage_pool = var.proxmox_iso_storage_pool 13 | iso_checksum = "sha512:5f6aed67b159d7ccc1a90df33cc8a314aa278728a6f50707ebf10c02e46664e383ca5fa19163b0a1c6a4cb77a39587881584b00b45f512b4a470f1138eaa1801" 14 | insecure_skip_tls_verify = true 15 | 16 | cloud_init = true 17 | cloud_init_storage_pool = "local-zfs" 18 | disks { 19 | disk_size = "200G" 20 | storage_pool = "local-zfs" 21 | storage_pool_type = "zfspool" 22 | } 23 | memory = 2048 24 | network_adapters { 25 | model = "virtio" 26 | bridge = "vmbr0" 27 | } 28 | onboot = true 29 | qemu_agent = true 30 | scsi_controller = "virtio-scsi-pci" 31 | ssh_username = "debian" 32 | ssh_password = "debian" 33 | ssh_wait_timeout = "10000s" 34 | template_name = "debian-cloudinit" 35 | unmount_iso = true 36 | vm_name = "provisioning-debian-template" 37 | vm_id = 9000 38 | 39 | boot_command = [ 40 | "", 41 | "priority=critical", 42 | "", 43 | "DEBIAN_FRONTEND=text", 44 | "", 45 | "auto=true", 46 | "", 47 | "url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg", 48 | "" 49 | ] 50 | boot_wait = "10s" 51 | http_content = { 52 | "/preseed.cfg" = templatefile("${path.cwd}/server/packer/templates/debian-preseed.pkrtpl.hcl", { domain = var.domain }) 53 | } 54 | } 55 | 56 | build { 57 | sources = ["source.proxmox.debian_cloudinit"] 58 | } 59 | -------------------------------------------------------------------------------- /cluster/apps/organization/dokuwiki/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: dokuwiki-password 6 | namespace: organization 7 | stringData: 8 | dokuwiki-password: ENC[AES256_GCM,data:OcOIjYHTft20RMTr+tX+SPk7ercWZCqiW03q9pkuslI=,iv:jyfSxbf8QGfrlRpgnhUGCpXlB8A5zBE+hhDz5QOyQLc=,tag:jcW9QAa+LOR+UGETLDtrPg==,type:str] 9 | sops: 10 | kms: [] 11 | gcp_kms: [] 12 | azure_kv: [] 13 | hc_vault: [] 14 | age: [] 15 | lastmodified: "2021-09-17T07:09:58Z" 16 | mac: ENC[AES256_GCM,data:dKkiHxFIIYiZ1XZLF/JJTXzFNZWI0bNIGj2AfFSUJ40GLONBtTlatuE1SzS+0aBGuXIe2/qMQdTYaHJbz2lY2sxD/yq6ATnpJY1ngXu9t5K0QnBcNu/6b2qQK1vIsquUwkYLRNLL/aaUCmnQOk5zyVyfiKTCQFdFKgSAOatM+JU=,iv:GH88qqdEgk2P7EjfLldk3SKxBYF8IzDvAUQ4Ciq7tq0=,tag:MA6dvgO6Wbkl454r5SSWcA==,type:str] 17 | pgp: 18 | - created_at: "2021-09-17T07:09:57Z" 19 | enc: | 20 | -----BEGIN PGP MESSAGE----- 21 | 22 | hQIMA7sq1npTE+1dAQ/+NAxMhh3cCxr6FQfkPsgLEfz3AXaA2xJYQbX9/WOvpbyJ 23 | VFl9Ct/AmgurT+n8lzOfDrRCYLzW11qzgU0l+8jLxBL/nsjSbl4SkfznDYEDS+3c 24 | HCNclqzlUPhUOMZOR+P54WjYIE5LDdy14GfbT8u3TetT/ieGLMUfgw11NFZ0Fyht 25 | htmwsKMmXN3flZcUsAAYTVcE8ok1wVxp+qmep68SUt0/mXS+A+2C4Nx9vmmfEUzs 26 | 7yIG3w8+DSZRXiZ3eVf62hyP6atru5qKsja8/4CN8Hx5vMQu2+hQysB+hHHXuDWL 27 | mYiK5D1KvKF00qD24/oX3e5iTbeL0hkQmS26Yz/aX43DEMPaKGgXEUXC6sWNJvj0 28 | Z4GEqN0R7NgSLbK+Fu1b7+xlnapK2c52UbUlnalUOdKb9stmd7pWl7ArQ4hgZ+MV 29 | oX9Xy6Cr/+uyt5jHuuIfWriBlCKzkXWHpH9GQZkSorcaUJqawOo1rbRNRsWVDXL2 30 | P9VCEs+xbEXIA9WwSFN7AuemErKV2Su2Xu9M+Xfl2Z1z54d5sLIhF+u1SeSg5sWG 31 | Ht+G3mhNAOhsNM8ZeeMsgiUWzUdcKmbQHnc6M9ZOXK4yqxnMtH5HGnL4Zqu2h2mY 32 | qk2dT0xe0g1ltLMs4+ZNcb7cbM0tYFZ8nc73fy0sAFqSQzNOBA9q8qGsr0ay4ljS 33 | XgHBunPJL7fuJzODY/nbNHQLrmRJpYBZ7jbys2fHE7wvEujYYpz74Om+B0IEji8+ 34 | vatOE5dyupavsX85EdtUb051e0v1V4b76DHh8cQzAzzXxx3giSJLj/0JiGme550= 35 | =Xc3C 36 | -----END PGP MESSAGE----- 37 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 38 | encrypted_regex: ^(data|stringData)$ 39 | version: 3.7.1 40 | -------------------------------------------------------------------------------- /server/terraform/proxmox/k3os.tf: -------------------------------------------------------------------------------- 1 | variable "key_fp" { 2 | description = "The fingerprint of the GPG key used by SOPS." 3 | type = string 4 | default = "5BFE57B283EBFAA9C14392882895075AEB2D5149" 5 | } 6 | 7 | resource "proxmox_vm_qemu" "k3os_master" { 8 | name = "k3os-master" 9 | desc = "Master k3s node running on k3os" 10 | target_node = var.proxmox_node 11 | vmid = 802 12 | 13 | clone = "k3os-cloudinit" 14 | agent = 1 15 | boot = "c" 16 | 17 | cores = 2 18 | memory = 8192 19 | 20 | disk { 21 | cache = "none" 22 | format = "raw" 23 | size = "50G" 24 | storage = "local-zfs" 25 | type = "scsi" 26 | } 27 | 28 | network { 29 | bridge = "vmbr0" 30 | firewall = false 31 | link_down = false 32 | macaddr = "36:BA:2C:EC:A7:58" 33 | model = "virtio" 34 | } 35 | 36 | 37 | provisioner "remote-exec" { 38 | inline = [ 39 | "qm set ${proxmox_vm_qemu.k3os_master.vmid} -usb0 host=10c4:8a2a", 40 | "qm set ${proxmox_vm_qemu.k3os_master.vmid} -usb1 host=534d:2109", 41 | "qm set ${proxmox_vm_qemu.k3os_master.vmid} -usb2 host=1a86:7523", 42 | "qm reboot ${proxmox_vm_qemu.k3os_master.vmid}", 43 | ] 44 | 45 | connection { 46 | host = var.proxmox_ip 47 | type = "ssh" 48 | user = "root" 49 | agent = true 50 | } 51 | } 52 | 53 | provisioner "remote-exec" { 54 | inline = [ 55 | "systemctl reboot", 56 | "echo provisioned", 57 | ] 58 | 59 | connection { 60 | host = var.master_node_ip_address 61 | type = "ssh" 62 | user = var.k3os_user 63 | agent = true 64 | script_path = "~/is-provisioned.sh" 65 | } 66 | } 67 | 68 | provisioner "local-exec" { 69 | command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook --user ${var.k3os_user} --inventory '../../ansible/inventory' --extra-vars \"key_fp=${var.key_fp} repo_dir=${path.cwd}\" ../../ansible/playbooks/kubernetes/kubernetes.yaml" 70 | } 71 | 72 | depends_on = [local_file.ansible_inventory] 73 | } 74 | -------------------------------------------------------------------------------- /cluster/apps/networking/adguard-home/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: adguard-home 6 | namespace: networking 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: adguard-home 13 | version: 5.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: adguard/adguardhome 22 | tag: v0.107.3 23 | service: 24 | dns-tcp: 25 | enabled: false 26 | dns-udp: 27 | enabled: false 28 | dns: 29 | enabled: true 30 | type: LoadBalancer 31 | loadBalancerIP: ${SVC_ADGUARD_ADDR} 32 | ports: 33 | tcp: 34 | enabled: true 35 | port: 53 36 | protocol: TCP 37 | targetPort: 53 38 | udp: 39 | enabled: true 40 | port: 53 41 | protocol: UDP 42 | targetPort: 53 43 | externalTrafficPolicy: Local 44 | persistence: 45 | config: 46 | enabled: true 47 | existingClaim: adguard-config 48 | data: 49 | enabled: true 50 | existingClaim: adguard-data 51 | ingress: 52 | main: 53 | enabled: true 54 | ingressClassName: "traefik" 55 | annotations: 56 | cert-manager.io/cluster-issuer: "letsencrypt-production" 57 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 58 | hajimari.io/enable: "true" 59 | hajimari.io/icon: "shield-check" 60 | hosts: 61 | - host: "adguard.${SECRET_DOMAIN}" 62 | paths: 63 | - path: / 64 | pathType: Prefix 65 | tls: 66 | - hosts: 67 | - "adguard.${SECRET_DOMAIN}" 68 | secretName: "adguard-${SECRET_DOMAIN//./-}-tls" 69 | resources: 70 | requests: 71 | cpu: 15m 72 | memory: 500Mi 73 | limits: 74 | memory: 750Mi 75 | -------------------------------------------------------------------------------- /server/ansible/playbooks/kubernetes/kubernetes.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - localhost 4 | tasks: 5 | - name: Set k3s node address 6 | ansible.builtin.set_fact: 7 | master_node_ip_address: "{{ hostvars['master']['ansible_host'] }}" 8 | 9 | - name: Copy over k3s config for kubectl 10 | ansible.builtin.command: scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null rancher@{{ master_node_ip_address }}:/etc/rancher/k3s/k3s.yaml ~/.kube 11 | 12 | - name: Rename k3s config 13 | ansible.builtin.command: mv ~/.kube/k3s.yaml ~/.kube/config 14 | 15 | - name: Replace localhost in k3s config with node address 16 | ansible.builtin.replace: 17 | path: ~/.kube/config 18 | regexp: '127\.0\.0\.1' 19 | replace: "{{ master_node_ip_address }}" 20 | 21 | - name: Set k3s config file permissions 22 | ansible.builtin.file: 23 | path: ~/.kube/config 24 | mode: "644" 25 | 26 | - name: Wait to allow k3s node to have a chance to come online 27 | ansible.builtin.wait_for: 28 | timeout: 10 29 | 30 | - name: Enable k3os auto updates 31 | ansible.builtin.command: kubectl label node k3os-master k3os.io/upgrade=enabled 32 | 33 | - name: Allow node to manage USB devices 34 | ansible.builtin.command: kubectl label node k3os-master smarter-device-manager=enabled 35 | 36 | - name: Create flux-system namespace 37 | ansible.builtin.shell: kubectl create namespace flux-system --dry-run=client -o yaml | kubectl apply -f - 38 | 39 | - name: Add GPG key secret 40 | ansible.builtin.shell: gpg --export-secret-keys --armor "{{ key_fp }}" | 41 | kubectl create secret generic sops-gpg --namespace=flux-system --from-file=sops.asc=/dev/stdin 42 | 43 | - name: Bootstrap cluster components (fails the first time due to race conditions with the Flux CRDs) 44 | ansible.builtin.command: kubectl apply --kustomize={{ repo_dir }}/cluster/base/flux-system 45 | ignore_errors: true 46 | 47 | - name: Finish bootstrapping cluster components 48 | ansible.builtin.command: kubectl apply --kustomize={{ repo_dir }}/cluster/base/flux-system 49 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/github/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: github-token 6 | namespace: flux-system 7 | stringData: 8 | token: ENC[AES256_GCM,data:GofKL7Of6K2wl/o2cX0eSmchgFZi15k7BmLnqNE4gY7N1vcMZ2l1AQ==,iv:fDAFOJcNNsmUUN/GXW13hLy/wyQ3bRvzMTWSYMKPnrc=,tag:OlfFlrWcPV0lwof4vmMq5A==,type:str] 9 | sops: 10 | kms: [] 11 | gcp_kms: [] 12 | azure_kv: [] 13 | hc_vault: [] 14 | age: [] 15 | lastmodified: "2021-10-06T05:27:40Z" 16 | mac: ENC[AES256_GCM,data:zj+3XI7jk67gZNRwC9psejS+NoOtlD1gn46+YljKf9uBJZSVXilLsGfo8EpFm8McHvjzcdJhdI9tfLojBK/K10DYlc592mn1ZrvfcsOuntYVURwu0/mDbOueWz+OyPatOvVIzIzznfsKWQy3BZ1gJtrhV1R9pFnhxQ0ks2hy0/Q=,iv:u8QKtKTtqyFJZ3j8L2CWwQ9wbh6o2ZUTMpQ3hDItjrI=,tag:jL/ru6iDdtysNQG0ZjeNog==,type:str] 17 | pgp: 18 | - created_at: "2021-09-21T03:54:31Z" 19 | enc: | 20 | -----BEGIN PGP MESSAGE----- 21 | 22 | hQIMA7sq1npTE+1dARAAperx1/JX5svvq3lWmGrVhsXuS4A/+mO/8PLLKCxiq4jQ 23 | 9MVLTsHevNvE442nmlczYiMYXW3pHypc14hnX6MtF0tiY32p1N2ANilPYEM4zYXF 24 | eZTg2t5fRIAYnXy3oYRwZbykpqR01JlAikgBJsztovS/RgKQalvxDVkMRKe2YUkF 25 | 7nP6NqKFxvfbUGnwhDftmdSlCTcI6a2rlg8ix1Q8candsq45yy41yctY0O2ypUl5 26 | wDWAZpoY6cw6Pk8ct4NMjlqMAUbxLA7koQbV+qw/e/hJl2mBrEiKcUmFHXty4LA1 27 | aLQF+nh+lr8Q59hQyQvxFXsIBxGuv4XB6OPK6bGy4KvG/JCs1vEwL+ZLwNiocy68 28 | yrMMClZb2QJZYjG9u/k3Ms53F6qjLNN0FaeP5UZQ6nUK9caZwAV8fVxdLfED6eet 29 | ZMSVT4EzOO6XPw4BoRCltHQRV8lbqSbIHUxkrKto1gvhz5g5FUKFWJp/un59TT57 30 | JeQ7GsTYGu7YTOc71P5G3LN4uMO0mVPhJtyanHGbgvaM33N5ZnAnVVJQosJ/BTas 31 | S2qPlnZjukKioiB2KMxq38fqUOQ3Mh2PL9jd/epg046cX8+KKWjtVGq2AtFO2S7h 32 | u5c96h+F7u/dZxgkV56QyATw3K6Tfo9f1omYOAjKdbST34Kohi0JMFEh1iuDa2rS 33 | XgFlEf0e+L85kgvO09llbD8OVMn0UpKeLPU3ikBafjn2oc4+bzIDRC/gYQwi9J2l 34 | Sjpa/cbUp15RJbjnaUlP+6QhKHnHpDFRIXUzId0konM4zVbgrNV1fUUfFf2iBqs= 35 | =OWPk 36 | -----END PGP MESSAGE----- 37 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 38 | encrypted_regex: ^(data|stringData)$ 39 | version: 3.7.1 40 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/middlewares/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | metadata: 6 | name: basic-auth 7 | namespace: networking 8 | stringData: 9 | users: ENC[AES256_GCM,data:TxLjoPO13YylxhgwzmAe+lFpGfYTQiv+Vhm2+jJVVyiVWiQEYOq3gXOppP4mjMjwO0RpYgbM4XoORIrWXjP4QfTNkUY=,iv:aqof5mKnpwzFHp1sEC9kib/Kegx2ovoBoBMIcmmSFD4=,tag:227VpGLSPNek0zCOWzCy2w==,type:str] 10 | sops: 11 | kms: [] 12 | gcp_kms: [] 13 | azure_kv: [] 14 | hc_vault: [] 15 | age: [] 16 | lastmodified: "2021-09-21T02:07:53Z" 17 | mac: ENC[AES256_GCM,data:+mR7C+GDf/IqcETNMa1+2X8wKLvyAeS3YgP5cczl8HkEUbIgFddN0AHL1fHyqBvWYOJNB9BAH84bBJYutHq3RWYOdipFz3a+ARmeERnwpQy4vCosOgtSl8qELKznf3qJdWn5A4hmHZHWM17SPVR/PAnXPJLmfKgVCUaOLcpj0r4=,iv:5Qcvlz2kgiRdcrYwdclHFfW5Iypu1Ka+6ZkdEjmnPEk=,tag:eCG0czyNDwLvZNhCY3vLXQ==,type:str] 18 | pgp: 19 | - created_at: "2021-09-21T01:54:14Z" 20 | enc: | 21 | -----BEGIN PGP MESSAGE----- 22 | 23 | hQIMA7sq1npTE+1dAQ//e/UeCcJv7HGH1tCPcfp+usJZGbXgC2OPdqHwgfL46BwQ 24 | aVHQZeC5jmc4tHHN/euovA0gdshgDusrAOxNJiSClat/7TxSU3tKw2xVkEfNS15V 25 | 0TDeU/ynumK05Kwj0YtxF6kpSOOb0eblMyROmuTyS6JDTHCbFjv/YzA4fq3hND0W 26 | XgaQ0nTbRghEmo0hOX/wHSDu+/HSZgpsiKAyg+HFpx7T2f8t9289vDVJIhPuww+4 27 | zZzZCnNrxWP2YI64ImRe73r3uKAc27UlgDaA8r4yGNS7h0+cT2eZrGQss5mlx15Q 28 | q8xeBHKhYP7wA9nhe4ZUZD31y5P9I/wfRaP22VJA96K7kDU3lwsQw+TDOMhN4SV9 29 | /gPuqLk87K5wgQ/cutclBofU+aUAywj84IUfMhWZ0eDWCzg5kjAe3K366oCgPIM9 30 | Sw/EeV/tlu2EQkMvJCVim7y3MYSwcBs6KW3/MSLIjd4OsGoLGJdAn/aeeZTYwoxJ 31 | a8c89mWoxrF1Od37uDFJIkeSgA7czLKOVlrEEP87TXOdQOBYswilO8EjRc0x+mLp 32 | lC1kxhp3n09RKJDGxBqPTmgnWXTD5PE/lF8XCaJ0O1o9y2wbHek4YTLm+4ROSO9h 33 | bEa78bNq2M0258MpbkjHeEJPFS+mDAadYtlqNQeSa4S82eqxRUDAHm/4nkda0LXS 34 | XgGAzo6bHYdWjdwHF6U3SYpBDc34Mz4bB+0N62Fb7VxoEfm7fLA//OYh6pQShgKD 35 | RlCafCiNrWmEZLHrsp9SeWuV5lUWB+53/6pGXkEr8Q3t0xTi5CSHflNSCneGLg4= 36 | =Syse 37 | -----END PGP MESSAGE----- 38 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 39 | encrypted_regex: ^(data|stringData)$ 40 | version: 3.7.1 41 | -------------------------------------------------------------------------------- /cluster/apps/flux-system/notifications/slack/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: slack-webhook-url 6 | namespace: flux-system 7 | stringData: 8 | address: ENC[AES256_GCM,data:FPtYcJsEj3lwGstF48RpBcrsw0XRpJbgAieovvB9BMNHo1ktXs/EPgy811cuyPOB3F0sJgcx1DYyjtRhWQXb3wB0vgWQL09KbPDeymJ26ei4,iv:XfTh4JuK7jOC8quUZa6VB7LxfFBV0rjq0jNnqLp2L8k=,tag:LnzWp4ArJdUE7KlYb9/EqA==,type:str] 9 | sops: 10 | kms: [] 11 | gcp_kms: [] 12 | azure_kv: [] 13 | hc_vault: [] 14 | age: [] 15 | lastmodified: "2021-09-21T03:54:41Z" 16 | mac: ENC[AES256_GCM,data:HzgCgm2VQBKaZAPunDJJ0YXLnWLYGdHd2/mkGJTncnLBkl58oRf1C9weQAfWQkFf6NpXVsVJY5TJI3zc+YP2OonjHQmS9CtmYW179TdxgGK8IKJC13qNswJxfwCZ27e1XYe5rtz0Nbymqv9qkfsEVgXo85opd6xpiHnratgLR+g=,iv:ON4w6cXSOFMDUfwZjev/AHEX8Eye40zcZTTKk+xcP+0=,tag:rzHBqatjcxbdbdzvCMqKvA==,type:str] 17 | pgp: 18 | - created_at: "2021-09-21T03:54:41Z" 19 | enc: | 20 | -----BEGIN PGP MESSAGE----- 21 | 22 | hQIMA7sq1npTE+1dAQ/+PbSuLbRRbhlTR+LG4joFO1k6yyTLykDosjJuOROUYED1 23 | dTddUAst0bUFi+5fVFpQJ5b8m9E2eYCrDX2IRhDtdGhyKv+aFn8/YHMKoFvchea4 24 | JZjdOUKr7SU9BHY811smLyTkUbgtr4HaoP+St9B3CXt2tzNekERA17vcyaCV9Q5I 25 | Z1HHCGmPaw+4mGRm1vccaZGW/ThagQhMoxgzVATiqsL+u0RGpCJRVgc1lvkLmCP0 26 | cQdoGGd4kDGHRtxvLtl/nG3wXaIMVC7o6Ho3QGCW7XCzAekN7HD/i85ONrK8DLe0 27 | EFMVh8LrZTtLfKipMx6DfzByXVTJeJXoNdRc8euLtql779puDIZafe4Es9++fu+I 28 | 2CnC0zXVMZNCQa+JYLb8/Uoc/ppx0/cpG4Uqbq0Bh0FTdYaKljT3kTJngD/CWmZw 29 | nDEVDqkpACJZ3LhaNv2YeBaOAz843HsdkSUN0hOw5QTtN4A7JSTH0PPYd4RmCebu 30 | CNmvEkl3ssLuOtHLbeBfnWS3JqI4sTOcfpiwLXWJv5XfmNydBWtnSGCSTiGgOeag 31 | OtT63X5UeYPHFiIfQBXMvfjKPF4NmPmVj63ahzVEmNwHP0QdxYn7zQcw6MimDi9p 32 | rTtDOZqNp/yio+4iJLyywwderf06lwn+n+Jf8YDEQWH66yHgkz6IbupIDRZJZ0HS 33 | XAE7jiWnpgenW9jUAF2fe3P9KYQ1dLnzlutW1QHf39Cojv/noLL/Bi/z3yoGzLQA 34 | B7ZFi27pj/uY4Ease4rCLufodHgCIB3dyM3cJ1u/+ziK7TYWkl1zdSOjhxFK 35 | =/k/h 36 | -----END PGP MESSAGE----- 37 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 38 | encrypted_regex: ^(data|stringData)$ 39 | version: 3.7.1 40 | -------------------------------------------------------------------------------- /server/terraform/proxmox/coredns.tf: -------------------------------------------------------------------------------- 1 | variable "domain" { 2 | description = "Network domain name." 3 | type = string 4 | } 5 | 6 | resource "random_password" "password" { 7 | length = 32 8 | special = true 9 | } 10 | 11 | resource "proxmox_lxc" "coredns" { 12 | hostname = "coredns" 13 | target_node = var.proxmox_node 14 | vmid = 800 15 | 16 | ostemplate = "local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz" 17 | unprivileged = true 18 | onboot = true 19 | start = true 20 | 21 | ssh_public_keys = file(var.ssh_public_key) 22 | password = random_password.password.result 23 | nameserver = "1.1.1.1" 24 | 25 | cores = 1 26 | memory = 512 27 | 28 | rootfs { 29 | storage = var.proxmox_disk_storage_pool 30 | size = "5G" 31 | } 32 | 33 | network { 34 | name = "eth0.1" 35 | bridge = "vmbr0" 36 | ip = "${var.coredns_ip_address}/24" 37 | ip6 = "auto" 38 | gw = "192.168.1.1" 39 | tag = 1 40 | } 41 | 42 | network { 43 | name = "eth0.2" 44 | bridge = "vmbr0" 45 | ip = "192.168.2.3/24" 46 | ip6 = "auto" 47 | gw = "192.168.2.1" 48 | tag = 2 49 | } 50 | 51 | network { 52 | name = "eth0.3" 53 | bridge = "vmbr0" 54 | ip = "192.168.3.3/24" 55 | ip6 = "auto" 56 | gw = "192.168.3.1" 57 | tag = 3 58 | } 59 | 60 | network { 61 | name = "eth0.4" 62 | bridge = "vmbr0" 63 | ip = "192.168.4.3/24" 64 | ip6 = "auto" 65 | gw = "192.168.4.1" 66 | tag = 4 67 | } 68 | 69 | provisioner "remote-exec" { 70 | inline = ["echo provisioned"] 71 | 72 | connection { 73 | host = var.coredns_ip_address 74 | type = "ssh" 75 | user = var.coredns_user 76 | agent = true 77 | } 78 | } 79 | 80 | provisioner "local-exec" { 81 | command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook --inventory '../../ansible/inventory' --extra-vars \"master_node_ip_address=${var.master_node_ip_address} domain=${var.domain}\" ../../ansible/playbooks/dns/coredns.yaml" 82 | } 83 | 84 | depends_on = [ 85 | local_file.ansible_inventory, 86 | proxmox_vm_qemu.k3os_master 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /cluster/apps/home/zigbee2mqtt/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: zigbee2mqtt 6 | namespace: home 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: zigbee2mqtt 13 | version: 9.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: ghcr.io/koenkk/zigbee2mqtt 22 | tag: 1.23.0 23 | env: 24 | ZIGBEE2MQTT_DATA: /data 25 | ingress: 26 | main: 27 | enabled: true 28 | ingressClassName: "traefik" 29 | annotations: 30 | cert-manager.io/cluster-issuer: "letsencrypt-production" 31 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 32 | traefik.ingress.kubernetes.io/router.middlewares: "networking-basic-auth@kubernetescrd" 33 | hosts: 34 | - host: "zigbee2mqtt.${SECRET_DOMAIN}" 35 | paths: 36 | - path: / 37 | pathType: Prefix 38 | tls: 39 | - hosts: 40 | - "zigbee2mqtt.${SECRET_DOMAIN}" 41 | secretName: "zigbee2mqtt-${SECRET_DOMAIN//./-}-tls" 42 | persistence: 43 | config: 44 | enabled: true 45 | existingClaim: zigbee2mqtt-config 46 | mountPath: "/data" 47 | resources: 48 | limits: 49 | smarter-devices/ttyUSB1: 1 50 | requests: 51 | smarter-devices/ttyUSB1: 1 52 | config: 53 | homeassistant: true 54 | device_options: 55 | retain: true 56 | permit_join: false 57 | mqtt: 58 | base_topic: zigbee2mqtt 59 | server: "mqtt://emqx" 60 | user: "${SECRET_MQTT_USERNAME}" 61 | password: "${SECRET_MQTT_PASSWORD}" 62 | serial: 63 | port: "/dev/ttyUSB1" 64 | adapter: ezsp 65 | advanced: 66 | log_level: debug 67 | log_output: 68 | - console 69 | network_key: GENERATE 70 | baudrate: 57600 71 | frontend: 72 | port: 8080 73 | experimental: 74 | new_api: true 75 | -------------------------------------------------------------------------------- /cluster/apps/observability/grafana/secret.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: grafana-password 6 | namespace: observability 7 | stringData: 8 | SECRET_GRAFANA_USER: ENC[AES256_GCM,data:SlmbgOg=,iv:rfGQ1AEStrC7old5QyP91zpq2OVcdGWg7VZuYP3arMo=,tag:AVJQ8Y79QJ2AIkjZn+HIfQ==,type:str] 9 | SECRET_GRAFANA_PASSWORD: ENC[AES256_GCM,data:nZTlmaG02v/jIJcXLra1RgkIJcWMRJ81jaNqEmtryjc=,iv:6weQkBfzpVbd32N6qhJuQJUywKEVP4mFDoEzaJLAtbU=,tag:gYzaAEqLvSJjDHIK4lo6vA==,type:str] 10 | sops: 11 | kms: [] 12 | gcp_kms: [] 13 | azure_kv: [] 14 | hc_vault: [] 15 | age: [] 16 | lastmodified: "2021-09-20T07:57:44Z" 17 | mac: ENC[AES256_GCM,data:Jq2T/FLvMOzOeuAA+czEGESZlCJDby/3uj9TZEvTxguOqf6V/BIe/qEzcSz5SpwAlEumTHWso7qu3bO9+Ez3tYuuNZeuVmuDo+dBCq9j5qmnIVMLhOa3ahopn2jpHaXCiRqm/jdmqdy0buvcLJTQnlINm9wpXJqllwq3/0leXT0=,iv:NS8M0p0j+6Yr7q3DDxWht1XXEd76pOdfbyV7pxkACcE=,tag:1ENXMdvrMETpBtaa8RQU0A==,type:str] 18 | pgp: 19 | - created_at: "2021-09-20T07:26:59Z" 20 | enc: | 21 | -----BEGIN PGP MESSAGE----- 22 | 23 | hQIMA7sq1npTE+1dARAAhk3Iy5xoQQ1C1IEVVeGcnL/bjXc8I2ZDFxtfnZ1Fkzgl 24 | OJyIOsvQuQ/QOQM0fjwl/Y/yAS5xxvcII2otOa7W+76dTQig35eEFw4Vx7KVKDqj 25 | vgF0OpfPzrULXQeSf9RupKAyoMS2wIOSQbrK0OdoUuZxRN4zIaL5xo3C79smyRPP 26 | XaBXsVk5LZ8iSYFxqyIAuaHSlxi5YTH7INdKpG1zS3ruvYa6wxidY97Q2CyI5ZZ8 27 | Eha4UN+9PKhdmOFfqWMqS2X6tj5UO6noR5CYtJMtxWbq4IXeTMa5hxmX0Nw/Xi+n 28 | vV9gnxthZcgVokvV3YxMRWcd8sbwayjhStzLc8qkrFHKI+bfYumJ9ZMkYdgSH24k 29 | yxrpYT/YydpjYHb7l50sKOiv2oju+6a4P6ATuwf70LoMid2+uk0xUx4zSOqB+OY+ 30 | xCVDgH8CSg67gVOqG6wEs4Uetp59HKKcUZH70rH4YYv2bdsqHSE4DNdxvGUr9rQl 31 | vP6zO9qyQX3enn8WQ9eZ0mum91rHb4wyLpSFpX18m9G43IUs7Sty4lwmwm63XRBF 32 | oGgtPZeFsiDcMqC08iYRtCzjkllCbM74VEVblNRxLL5+5w1BMDyfgyUH1KPqVzqT 33 | uWEL3dHBsTRGmzOZ776udyKTxlmN9nppi6zLP36LGgAm2K9rFAyp+7wObZ/GMZ7S 34 | XgFDuh340tynuib1cRFU1AUKBFA2FHWgcsMnBc/MrOkhclKFnN/G0JcUBpc0iGyQ 35 | bEve7pR9BcL+jtS30MTHePxKXpEJ1QHPZNNATyWKAEKiwQFy+q7l+mFpZ9dwehA= 36 | =Lu/Z 37 | -----END PGP MESSAGE----- 38 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 39 | encrypted_regex: ^(data|stringData)$ 40 | version: 3.7.1 41 | -------------------------------------------------------------------------------- /.taskfiles/terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | vars: 5 | TERRAFORM_DIR: "{{.SERVER_DIR}}/terraform/proxmox" 6 | 7 | tasks: 8 | apply: 9 | desc: Apply terraform changes 10 | cmds: 11 | - "terraform -chdir={{.TERRAFORM_DIR}} apply" 12 | silent: true 13 | 14 | apply:coredns: 15 | desc: Apply terraform changes for coredns resource 16 | cmds: 17 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -target=proxmox_lxc.coredns" 18 | 19 | apply:fileserver: 20 | desc: Apply terraform changes for fileserver resource 21 | cmds: 22 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -target=proxmox_vm_qemu.fileserver" 23 | 24 | apply:k3os: 25 | desc: Apply terraform changes for k3os resource 26 | cmds: 27 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -target=proxmox_vm_qemu.k3os_master" 28 | silent: true 29 | 30 | destroy: 31 | desc: destroy terraform resources 32 | cmds: 33 | - "terraform -chdir={{.TERRAFORM_DIR}} destroy" 34 | silent: true 35 | 36 | destroy:coredns: 37 | desc: destroy coredns terraform resource 38 | cmds: 39 | - "terraform -chdir={{.TERRAFORM_DIR}} destroy -target=proxmox_lxc.coredns" 40 | silent: true 41 | 42 | destroy:fileserver: 43 | desc: destroy fileserver terraform resource 44 | cmds: 45 | - "terraform -chdir={{.TERRAFORM_DIR}} destroy -target=proxmox_vm_qemu.fileserver" 46 | silent: true 47 | 48 | destroy:k3os: 49 | desc: destroy k3os terraform resource 50 | cmds: 51 | - "terraform -chdir={{.TERRAFORM_DIR}} destroy -target=proxmox_vm_qemu.k3os_master" 52 | silent: true 53 | 54 | replace:coredns: 55 | desc: Replace coredns resource 56 | cmds: 57 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -replace=proxmox_lxc.coredns" 58 | 59 | replace:fileserver: 60 | desc: Replace fileserver resource 61 | cmds: 62 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -replace=proxmox_vm_qemu.fileserver" 63 | 64 | replace:k3os: 65 | desc: Replace k3os resource 66 | cmds: 67 | - "terraform -chdir={{.TERRAFORM_DIR}} apply -replace=proxmox_vm_qemu.k3os_master" 68 | silent: true 69 | 70 | init: 71 | desc: Initialize terraform 72 | cmds: 73 | - "terraform -chdir={{.TERRAFORM_DIR}} init" 74 | silent: true 75 | 76 | plan: 77 | desc: Plan terraform changes 78 | cmds: 79 | - "terraform -chdir={{.TERRAFORM_DIR}} plan" 80 | silent: true 81 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: traefik 6 | namespace: networking 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://helm.traefik.io/traefik 12 | chart: traefik 13 | version: 10.14.1 14 | sourceRef: 15 | kind: HelmRepository 16 | name: traefik-charts 17 | namespace: flux-system 18 | interval: 5m 19 | dependsOn: 20 | - name: cert-manager 21 | namespace: cert-manager 22 | values: 23 | image: 24 | name: ghcr.io/k8s-at-home/traefik 25 | deployment: 26 | kind: Deployment 27 | replicas: 1 28 | service: 29 | enabled: true 30 | type: LoadBalancer 31 | spec: 32 | loadBalancerIP: "${SVC_TRAEFIK_ADDR}" 33 | externalTrafficPolicy: Local 34 | logs: 35 | general: 36 | format: json 37 | level: DEBUG 38 | access: 39 | enabled: true 40 | format: json 41 | ingressClass: 42 | enabled: true 43 | isDefaultClass: true 44 | fallbackApiVersion: v1 45 | ingressRoute: 46 | dashboard: 47 | enabled: false 48 | globalArguments: 49 | - "--api.insecure=true" 50 | - "--serverstransport.insecureskipverify=true" 51 | - "--providers.kubernetesingress.ingressclass=traefik" 52 | - "--metrics.prometheus=true" 53 | - "--metrics.prometheus.entryPoint=metrics" 54 | additionalArguments: 55 | - "--providers.kubernetesingress.ingressendpoint.ip=${SVC_TRAEFIK_ADDR}" 56 | - "--providers.kubernetescrd.allowexternalnameservices=true" 57 | - "--providers.kubernetesingress.allowexternalnameservices=true" 58 | ports: 59 | traefik: 60 | expose: true 61 | web: 62 | redirectTo: websecure 63 | websecure: 64 | tls: 65 | enabled: true 66 | options: "default" 67 | metrics: 68 | port: 8082 69 | expose: true 70 | exposedPort: 8082 71 | tlsOptions: 72 | default: 73 | minVersion: VersionTLS12 74 | maxVersion: VersionTLS13 75 | sniStrict: true 76 | pilot: 77 | enabled: false 78 | experimental: 79 | plugins: 80 | enabled: false 81 | resources: 82 | requests: 83 | memory: 100Mi 84 | cpu: 500m 85 | limits: 86 | memory: 500Mi 87 | -------------------------------------------------------------------------------- /server/ansible/playbooks/dns/coredns-service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - coredns 4 | gather_facts: yes 5 | roles: 6 | - role: fubarhouse.golang 7 | go_version: 1.17.1 8 | GOPATH: /var/lib/coredns/go 9 | GOROOT: /usr/local/go 10 | GOPROXY: https://proxy.golang.org 11 | 12 | tasks: 13 | - name: Add the coredns user 14 | ansible.builtin.user: 15 | name: coredns 16 | home: /var/lib/coredns 17 | 18 | - name: Install git 19 | ansible.builtin.apt: 20 | name: git 21 | state: present 22 | 23 | - name: Ensure coredns directory exists 24 | ansible.builtin.file: 25 | path: /etc/coredns 26 | state: directory 27 | mode: 0755 28 | 29 | - name: Copy kubeconfig to DNS server 30 | ansible.builtin.copy: 31 | src: /tmp/kubeconfig-homedns 32 | dest: /etc/coredns/kubeconfig 33 | mode: 0755 34 | 35 | - name: Clone k8s gateway repo 36 | ansible.builtin.git: 37 | repo: https://github.com/ori-edge/k8s_gateway.git 38 | dest: /var/lib/coredns/k8s_gateway 39 | depth: 1 40 | 41 | - name: Build coredns with k8s gateway plugin 42 | ansible.builtin.shell: /usr/local/go/bin/go build cmd/coredns.go 43 | args: 44 | chdir: /var/lib/coredns/k8s_gateway 45 | 46 | - name: Move coredns binary to /user/bin 47 | ansible.builtin.command: mv /var/lib/coredns/k8s_gateway/coredns /usr/bin 48 | 49 | - name: Create coredns systemd unit file 50 | ansible.builtin.template: 51 | src: ../../templates/coredns/coredns.service.j2 52 | dest: /etc/systemd/system/coredns.service 53 | mode: 0755 54 | 55 | - name: Create CoreDNS Corefile 56 | ansible.builtin.template: 57 | src: ../../templates/coredns/Corefile.j2 58 | dest: /etc/coredns/Corefile 59 | mode: 0755 60 | 61 | - name: Create CoreDNS sysusers config 62 | ansible.builtin.template: 63 | src: ../../templates/coredns/coredns-sysusers.conf.j2 64 | dest: /usr/lib/sysusers.d/doredns-sysusers.conf 65 | mode: 0755 66 | 67 | - name: Create CoreDNS tmpfiles config 68 | ansible.builtin.template: 69 | src: ../../templates/coredns/coredns-tmpfiles.conf.j2 70 | dest: /usr/lib/tmpfiles.d/coredns-tmpfiles.conf 71 | mode: 0755 72 | 73 | - name: Enable coredns service 74 | ansible.builtin.service: 75 | name: coredns 76 | enabled: yes 77 | daemon_reload: yes 78 | 79 | - name: Start coredns service 80 | ansible.builtin.service: 81 | name: coredns 82 | state: started 83 | -------------------------------------------------------------------------------- /server/packer/images/k3os.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "nameserver" { 2 | type = string 3 | description = "IP of the nameserver." 4 | } 5 | 6 | variable "master_node_ip" { 7 | type = string 8 | description = "IP address used by the master k3s node." 9 | } 10 | 11 | variable "github_username" { 12 | type = string 13 | description = "GitHub username from which to add an authorized SSH key." 14 | } 15 | 16 | source "proxmox" "k3os_cloudinit" { 17 | proxmox_url = "https://${var.proxmox_ip}:${var.proxmox_port}/api2/json" 18 | username = var.proxmox_username 19 | password = var.proxmox_password 20 | node = var.proxmox_node 21 | iso_url = "https://github.com/rancher/k3os/releases/latest/download/k3os-amd64.iso" 22 | iso_storage_pool = var.proxmox_iso_storage_pool 23 | iso_checksum = "sha256:85a560585bc5520a793365d70e6ce984f3fb2ce5a43b31f0f7833dc347487e69" 24 | insecure_skip_tls_verify = true 25 | 26 | cloud_init = true 27 | cloud_init_storage_pool = "local-zfs" 28 | cores = 2 29 | disks { 30 | disk_size = "50G" 31 | storage_pool = "local-zfs" 32 | storage_pool_type = "zfspool" 33 | } 34 | memory = 8192 35 | network_adapters { 36 | model = "virtio" 37 | bridge = "vmbr0" 38 | } 39 | onboot = true 40 | qemu_agent = true 41 | scsi_controller = "virtio-scsi-pci" 42 | ssh_username = "rancher" 43 | ssh_agent_auth = true 44 | ssh_wait_timeout = "10000s" 45 | template_name = "k3os-cloudinit" 46 | unmount_iso = true 47 | vm_name = "provisioning-k3os-template" 48 | vm_id = 9001 49 | 50 | boot_command = [ 51 | "", 52 | "", 53 | "", 54 | "e", 55 | "", 56 | "", 57 | "", 58 | "", 59 | "", 60 | "", 61 | "", 62 | "", 63 | "k3os.install.config_url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/config.yaml", 64 | "", 65 | "k3os.fallback_mode=install", 66 | "", 67 | "k3os.install.silent=true", 68 | "", 69 | "k3os.install.device=/dev/sda", 70 | "", 71 | "k3os.install.debug=true", 72 | "" 73 | ] 74 | boot_wait = "10s" 75 | http_content = { 76 | "/config.yaml" = templatefile("${path.cwd}/server/packer/templates/k3os-config.pkrtpl.hcl", { hostname = "k3os-master", node_ip = var.master_node_ip, nameserver = var.nameserver, github_username = var.github_username }) 77 | } 78 | } 79 | 80 | build { 81 | sources = ["source.proxmox.k3os_cloudinit"] 82 | } 83 | -------------------------------------------------------------------------------- /cluster/apps/networking/traefik/prometheus-rule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PrometheusRule 4 | metadata: 5 | labels: 6 | app: traefik 7 | name: traefik.rules 8 | namespace: networking 9 | spec: 10 | groups: 11 | - name: traefik.rules 12 | rules: 13 | - alert: TraefikAbsent 14 | annotations: 15 | summary: "Traefik has disappeared from Prometheus service discovery." 16 | description: "Ingresses will be down until the Traefik reverse proxy is back up." 17 | expr: | 18 | absent(up{job="traefik"}) 19 | for: 5m 20 | labels: 21 | severity: critical 22 | - alert: TraefikConfigError 23 | annotations: 24 | summary: "Traefik config error." 25 | description: "Traefik has failed to load the config file. Check Traefik 26 | logs for exact parsing error." 27 | expr: | 28 | traefik_config_last_reload_failure{job="traefik"} == 1 29 | for: 0m 30 | labels: 31 | severity: critical 32 | - alert: TraefikHighHttp4xxErrorRateService 33 | annotations: 34 | summary: "Traefik has a high HTTP 4xx error rate." 35 | description: "Traefik is reporting {{ $value | humanizePercentage }} of 4xx 36 | errors on {{ $labels.service }}" 37 | expr: | 38 | sum(rate(traefik_service_requests_total{code=~"4.*"}[1m])) by (service) 39 | / 40 | sum(rate(traefik_service_requests_total[1m])) by (service) 41 | > .05 42 | for: 5m 43 | labels: 44 | severity: critical 45 | - alert: TraefikHighHttp5xxErrorRateService 46 | annotations: 47 | summary: "Traefik has a high HTTP 5xx error rate." 48 | description: "Traefik is reporting {{ $value | humanizePercentage }} of 5xx 49 | errors on {{ $labels.service }}" 50 | expr: | 51 | sum(rate(traefik_service_requests_total{code=~"5.*"}[1m])) by (service) 52 | / 53 | sum(rate(traefik_service_requests_total[1m])) by (service) 54 | > .05 55 | for: 5m 56 | labels: 57 | severity: critical 58 | - alert: TraefikTooManyRequest 59 | annotations: 60 | summary: "Traefik has too many open connections" 61 | description: "Traefik is reporting {{ $value }} of open connections on entrypoint 62 | {{ $labels.entrypoint }}" 63 | expr: | 64 | avg(traefik_entrypoint_open_connections{job="traefik"}) 65 | > 5 66 | for: 1m 67 | labels: 68 | severity: critical 69 | -------------------------------------------------------------------------------- /cluster/apps/home/home-assistant/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: home-assistant 6 | namespace: home 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://k8s-at-home.com/charts/ 12 | chart: home-assistant 13 | version: 12.0.1 14 | sourceRef: 15 | kind: HelmRepository 16 | name: k8s-at-home-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: homeassistant/home-assistant 22 | tag: 2022.2.6 23 | env: 24 | HASS_URL: "https://hass.${SECRET_DOMAIN}" 25 | ROBOROCK_ADDR: 192.168.3.164 26 | envFrom: 27 | - secretRef: 28 | name: home-assistant 29 | hostNetwork: true 30 | dnsPolicy: ClusterFirstWithHostNet 31 | ingress: 32 | main: 33 | enabled: true 34 | ingressClassName: "traefik" 35 | annotations: 36 | cert-manager.io/cluster-issuer: "letsencrypt-production" 37 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 38 | hajimari.io/enable: "true" 39 | hajimari.io/icon: "home-assistant" 40 | hosts: 41 | - host: "hass.${SECRET_DOMAIN}" 42 | paths: 43 | - path: / 44 | pathType: Prefix 45 | tls: 46 | - hosts: 47 | - "hass.${SECRET_DOMAIN}" 48 | secretName: "hass-${SECRET_DOMAIN//./-}-tls" 49 | persistence: 50 | config: 51 | enabled: true 52 | existingClaim: home-assistant-config 53 | addons: 54 | codeserver: 55 | enabled: true 56 | image: 57 | repository: ghcr.io/k8s-at-home/code-server 58 | tag: v4.0.2 59 | git: 60 | deployKeySecret: code-server 61 | workingDir: "/config" 62 | args: 63 | - --auth 64 | - "none" 65 | - --user-data-dir 66 | - "/config/.vscode" 67 | - --extensions-dir 68 | - "/config/.vscode" 69 | ingress: 70 | enabled: true 71 | ingressClassName: "traefik" 72 | annotations: 73 | cert-manager.io/cluster-issuer: "letsencrypt-production" 74 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 75 | traefik.ingress.kubernetes.io/router.middlewares: "networking-basic-auth@kubernetescrd" 76 | hosts: 77 | - host: "hass-config.${SECRET_DOMAIN}" 78 | paths: 79 | - path: / 80 | pathType: Prefix 81 | tls: 82 | - hosts: 83 | - "hass-config.${SECRET_DOMAIN}" 84 | secretName: "hass-config-${SECRET_DOMAIN//./-}-tls" 85 | volumeMounts: 86 | - name: config 87 | mountPath: /config 88 | resources: 89 | requests: 90 | cpu: 100m 91 | memory: 100Mi 92 | limits: 93 | memory: 2000Mi 94 | -------------------------------------------------------------------------------- /cluster/apps/organization/hajimari/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: hajimari 6 | namespace: organization 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://hajimari.io 12 | chart: hajimari 13 | version: 1.2.0 14 | sourceRef: 15 | kind: HelmRepository 16 | name: hajimari-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: ghcr.io/toboshii/hajimari 22 | tag: v0.2.0 23 | hajimari: 24 | defaultEnable: false 25 | namespaceSelector: 26 | matchNames: 27 | - home 28 | - media 29 | - networking 30 | - observability 31 | - organization 32 | name: "Alex" 33 | groups: 34 | - name: Communicate 35 | links: 36 | - name: Discord 37 | url: "https://discord.com" 38 | - name: Protonmail 39 | url: "https://protonmail.com" 40 | - name: Slack 41 | url: "https://slack.com/signin" 42 | - name: Cloud 43 | links: 44 | - name: Cloudflare 45 | url: "https://dash.cloudflare.com" 46 | - name: Domains 47 | url: "https://domains.google.com" 48 | - name: Drive 49 | url: "https://drive.google.com" 50 | - name: Dev 51 | links: 52 | - name: GitHub 53 | url: "https://github.com" 54 | - name: Media 55 | links: 56 | - name: Spotify 57 | url: "http://browse.spotify.com" 58 | - name: YouTube 59 | url: "https://youtube.com/feed/subscriptions" 60 | - name: Reddit 61 | links: 62 | - name: Hiphopheads 63 | url: "https://reddit.com/r/hiphopheads" 64 | - name: Homelab 65 | url: "https://reddit.com/r/homelab" 66 | - name: Indieheads 67 | url: "https://reddit.com/r/indieheads" 68 | - name: Tech 69 | links: 70 | - name: Hacker News 71 | url: "https://news.ycombinator.com/" 72 | ingress: 73 | main: 74 | enabled: true 75 | ingressClassName: "traefik" 76 | annotations: 77 | cert-manager.io/cluster-issuer: "letsencrypt-production" 78 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 79 | hosts: 80 | - host: "homepage.${SECRET_DOMAIN}" 81 | paths: 82 | - path: / 83 | pathType: Prefix 84 | tls: 85 | - hosts: 86 | - "homepage.${SECRET_DOMAIN}" 87 | secretName: "hajimari-${SECRET_DOMAIN//./-}-tls" 88 | persistence: 89 | data: 90 | enabled: true 91 | existingClaim: hajimari-config 92 | resources: 93 | requests: 94 | cpu: 100m 95 | memory: 128Mi 96 | limits: 97 | memory: 256Mi 98 | -------------------------------------------------------------------------------- /cluster/core/cert-manager/prometheus-rule.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PrometheusRule 4 | metadata: 5 | name: cert-manager.rules 6 | namespace: cert-manager 7 | spec: 8 | groups: 9 | - name: cert-manager 10 | rules: 11 | - alert: CertManagerAbsent 12 | expr: | 13 | absent(up{job="cert-manager"}) 14 | for: 10m 15 | labels: 16 | severity: critical 17 | annotations: 18 | description: "New certificates will not be able to be minted, and existing 19 | ones can't be renewed until cert-manager is back." 20 | runbook_url: https://gitlab.com/uneeq-oss/cert-manager-mixin/-/blob/master/RUNBOOK.md#certmanagerabsent 21 | summary: "Cert Manager has dissapeared from Prometheus service discovery." 22 | - name: certificates 23 | rules: 24 | - alert: CertManagerCertExpirySoon 25 | expr: | 26 | avg by (exported_namespace, namespace, name) ( 27 | certmanager_certificate_expiration_timestamp_seconds - time()) 28 | < (21 * 24 * 3600) 29 | for: 1h 30 | labels: 31 | severity: warning 32 | annotations: 33 | description: "The domain that this cert covers will be unavailable after 34 | {{ $value | humanizeDuration }}. Clients using endpoints that this cert 35 | protects will start to fail in {{ $value | humanizeDuration }}." 36 | runbook_url: https://gitlab.com/uneeq-oss/cert-manager-mixin/-/blob/master/RUNBOOK.md#certmanagercertexpirysoon 37 | summary: "The cert {{ $labels.name }} is {{ $value | humanizeDuration }} 38 | from expiry, it should have renewed over a week ago." 39 | - alert: CertManagerCertNotReady 40 | expr: | 41 | max by (name, exported_namespace, namespace, condition) ( 42 | certmanager_certificate_ready_status{condition!="True"} == 1) 43 | for: 10m 44 | labels: 45 | severity: critical 46 | annotations: 47 | description: "This certificate has not been ready to serve traffic for at least 48 | 10m. If the cert is being renewed or there is another valid cert, the ingress 49 | controller _may_ be able to serve that instead." 50 | runbook_url: https://gitlab.com/uneeq-oss/cert-manager-mixin/-/blob/master/RUNBOOK.md#certmanagercertnotready 51 | summary: "The cert {{ $labels.name }} is not ready to serve traffic." 52 | - alert: CertManagerHittingRateLimits 53 | expr: | 54 | sum by (host) (rate(certmanager_http_acme_client_request_count{status="429"}[5m])) 55 | > 0 56 | for: 5m 57 | labels: 58 | severity: critical 59 | annotations: 60 | description: "Depending on the rate limit, cert-manager may be unable to generate 61 | certificates for up to a week." 62 | runbook_url: https://gitlab.com/uneeq-oss/cert-manager-mixin/-/blob/master/RUNBOOK.md#certmanagerhittingratelimits 63 | summary: "Cert manager hitting LetsEncrypt rate limits." 64 | -------------------------------------------------------------------------------- /cluster/apps/home/mopidy/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: mopidy 6 | namespace: home 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | chart: /charts/kah-common/ 12 | sourceRef: 13 | kind: GitRepository 14 | name: flux-system 15 | namespace: flux-system 16 | interval: 5m 17 | values: 18 | image: 19 | repository: wernight/mopidy 20 | tag: latest 21 | nameOverride: mopidy 22 | service: 23 | main: 24 | enabled: true 25 | ports: 26 | http: 27 | enabled: true 28 | port: 6680 29 | protocol: TCP 30 | icecast: 31 | enabled: true 32 | ports: 33 | icecast: 34 | enabled: true 35 | port: 8000 36 | protocol: TCP 37 | snapserver: 38 | enabled: true 39 | ports: 40 | snapserver: 41 | enabled: true 42 | port: 1780 43 | protocol: HTTP 44 | ingress: 45 | main: 46 | enabled: true 47 | annotations: 48 | cert-manager.io/cluster-issuer: "letsencrypt-production" 49 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 50 | hajimari.io/enable: "true" 51 | hajimari.io/icon: "playlist-music" 52 | hosts: 53 | - host: "mopidy.${SECRET_DOMAIN}" 54 | paths: 55 | - path: / 56 | - path: /listen 57 | pathType: Prefix 58 | service: 59 | name: mopidy-icecast 60 | port: 8000 61 | - path: /snapserver 62 | pathType: Prefix 63 | service: 64 | name: mopidy-snapserver 65 | port: 1780 66 | tls: 67 | - hosts: 68 | - "mopidy.${SECRET_DOMAIN}" 69 | secretName: "mopidy-${SECRET_DOMAIN//./-}-tls" 70 | persistence: 71 | mopidy-config: 72 | enabled: true 73 | type: custom 74 | mountPath: /config 75 | volumeSpec: 76 | configMap: 77 | name: mopidy-config 78 | icecast-config: 79 | enabled: true 80 | type: custom 81 | volumeSpec: 82 | configMap: 83 | name: icecast-config 84 | snapfifo: 85 | enabled: true 86 | type: emptyDir 87 | mountPath: /tmp/snapfifo 88 | data: 89 | enabled: true 90 | existingClaim: mopidy-data 91 | mountPath: /var/lib/mopidy/local 92 | resources: 93 | requests: 94 | cpu: 100m 95 | memory: 500Mi 96 | limits: 97 | memory: 750Mi 98 | additionalContainers: 99 | icecast: 100 | name: icecast 101 | image: vimagick/icecast:latest 102 | imagePullPolicy: IfNotPresent 103 | ports: 104 | - name: web 105 | containerPort: 8000 106 | volumeMounts: 107 | - name: icecast-config 108 | mountPath: /etc/icecast.xml 109 | subPath: icecast.xml 110 | - name: icecast-config 111 | mountPath: /usr/share/icecast/web/silence.mp3 112 | subPath: silence.mp3 113 | snapserver: 114 | name: snapserver 115 | image: jaedb/snapserver:latest 116 | imagePullPolicy: IfNotPresent 117 | ports: 118 | - name: web 119 | containerPort: 1780 120 | volumeMounts: 121 | - name: snapfifo 122 | mountPath: /tmp/snapfifo 123 | -------------------------------------------------------------------------------- /cluster/apps/home/home-assistant/home-assistant.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: home-assistant 6 | namespace: home 7 | stringData: 8 | HASS_ELEVATION: ENC[AES256_GCM,data:Hww=,iv:enI/F3XEXu1RDGIOj4MGrRerGNQ+CZxbWP8DgUf7KDo=,tag:bp8XRXT50fR1+tWc7BmTLQ==,type:str] 9 | HASS_LATITUDE: ENC[AES256_GCM,data:iUyqjlIf8Pl6,iv:kCe7FnsqsLQ3vGjW0kHvbVjgFHxZD/ic801ozYKkx9Y=,tag:Oi5izpBCFlGA7/LBoWwnug==,type:str] 10 | HASS_LONGITUDE: ENC[AES256_GCM,data:FUuQjdETUddjpsI=,iv:hGZdpA2V1SHALk7waaW3T/qeV0JldXhkPRJ93zI8DYk=,tag:BvPfVlG8SgXxy+7G0h8UaQ==,type:str] 11 | HASS_MQTT_PASSWORD: ENC[AES256_GCM,data:2g+f8Npn+bgxJ+2vkkuHqVaeMNFPuFIzOVzgpqCFq0w=,iv:wo5T0oPU+ZRsZoUzi9bcuvP3yUw2Q1f/GyzCInCpYpc=,tag:4k+PQZEmHjtNsdZ0TqsZ7Q==,type:str] 12 | HASS_MQTT_USERNAME: ENC[AES256_GCM,data:No2qOA6kXHxpsT2AEYM=,iv:xW8Bn2zHCryAlCEyDvngwN3/fH1ALdGJ6yyROrMKlNc=,tag:Oh1ne1QGhnKQGaGdIc54Lw==,type:str] 13 | HASS_SPOTIFY_CLIENT_ID: ENC[AES256_GCM,data:hTrt1Jg6NyMj6CrnXNQHKE9QQiWRl0WXiUv2azTaH6o=,iv:Kc4xirsp4KCdl+rEkpGYh0tNp94s+/ILnkHk6ayrzD0=,tag:qJaWQn+0XvOB/Hkhgr064g==,type:str] 14 | HASS_SPOTIFY_CLIENT_SECRET: ENC[AES256_GCM,data:yDUDOmUVkFp6+MlaxGsWM18mKxz4hw9vfJSHue3BFpY=,iv:zOgQvahdyjPxvPBmOe248Po57/EiIRngyigflrtWz90=,tag:NGHHugjMXroU6WvenBRC0Q==,type:str] 15 | HASS_TRUSTED_PROXIES_1: ENC[AES256_GCM,data:8Lm7FnG0bnqVjN7A,iv:y+wNqYJh9vKeDsBc37zqwPfR9cTE71bR9VyeYceYI7s=,tag:u7KkZFdvl5Bv5TmczgUUBQ==,type:str] 16 | HASS_TRUSTED_PROXIES_2: ENC[AES256_GCM,data:HAJmmfcMqvYn/yzWlyk=,iv:daIzXm9kMr+CZrk0zQpoZTfIYosIKH5jUvn504qzZkk=,tag:PRIkP7qa91s74hfx1VNzkg==,type:str] 17 | ROBOROCK_PASSWORD: ENC[AES256_GCM,data:rl95LuWJUUO5PCwS,iv:vwAyzfASB3hlh9qQngvBVu0dU1jSBajgvwWUrmWVw3w=,tag:R7XQeI5JVcomfQ5m2dBkoA==,type:str] 18 | ROBOROCK_TOKEN: ENC[AES256_GCM,data:sjJgFCEIwPqQEWIBJK12ZumjcqTI1RMjDnPgCmM+rNk=,iv:T6+TQvZUiiqOtEiDoTRiBdLAFoiXgvx5cyzsXp7zXoo=,tag:4rCDka1NIQQ15ep1ZG18LQ==,type:str] 19 | ROBOROCK_USERNAME: ENC[AES256_GCM,data:qtGT8SnC8i24p0yvNFc=,iv:M8HXDD2R2/3Jj4teAcO59Ba7MLA02TOABHVyDB4p+EA=,tag:v9kDdSORJsvIEFptH1N92Q==,type:str] 20 | sops: 21 | kms: [] 22 | gcp_kms: [] 23 | azure_kv: [] 24 | hc_vault: [] 25 | age: [] 26 | lastmodified: "2021-10-12T09:07:47Z" 27 | mac: ENC[AES256_GCM,data:brXOTrFxSFC9/t4XlR8qase30U2Tj0KSyHQYxzXO5lzfVbfuojyP4TuaQxwdlj+vmKNbYD5vKLyM1YXrYZtvQDJN4npUV/ewMZKG/d/aVujtiWgfpDk5EKinz/IUHzm8btsYSldc+/eIzaRcuApD5lQV7G0Db4ULvBu5Pz9mahk=,iv:J5g87UwvHZM7CmeHjVyoXd1sx4kEe7W/hbnCH4Flewc=,tag:DYuWYyW1htAxplpkRJgbtQ==,type:str] 28 | pgp: 29 | - created_at: "2021-10-06T04:39:03Z" 30 | enc: | 31 | -----BEGIN PGP MESSAGE----- 32 | 33 | hQIMA7sq1npTE+1dAQ//VcbOQgWIM1kKM+tpv3J30tGaTTN92doVI5OMbeL9BENj 34 | +b2vtb6NaGUnic1XEvg/nkPMbdHLy5Xi/oVtHGRaV7CLIQkOW9ZbNkbJ5pgn72Sm 35 | IPbmijD/DtFccAXL3MkVSGZEIFrFth/ZpzY9Txq6TxchmHGIAEz85zyMYq3kRbFW 36 | P6OqTLPVv8nFDOZDnNu5XcYP/tZde7uE6fQVM7mkyEUVD9AygKXOjNmSlXe8R/p+ 37 | bc/3Rwx/8p74awiuHLi8V1lPvwUMVsfiavMTJXQ7mFxUYiu94I1H3FiVuZzcTo3O 38 | h74oYWUoMwcBBmlBi0LimJ8U7xj2ewiYGxXL5m/sOxKsu+xijeO8VHLvKYzWU6z3 39 | PGzqM/7vg0Ws+sd+/BlJ0FjtZQVNoqJQzFr+3h0mZzmB0k7SIxvk90ud1JMmx0Aj 40 | xoIShGP+x+ew04uvwea1HtQYsVmtk5T528O15Azpui/OZtRmUQj4zDN+H34NR4tJ 41 | GZiml04vvBydZEJbR7WBcemlf43pznn/Uob03jimjYCmXM3p/X3AmDZkvH1747Zc 42 | m9KXkFARN0wrX50eVlwQ1ySz2jyd7xJBsUeW1FGDEtIdfu95IgcwtmFqMe1JMbmT 43 | 3viUbGcWBDig0fX0XQM72mJdKuGnvWRZ6bjLpFKMxtqvhDgGEce4LPKbPif1pMXS 44 | XgFq4I9I+b4clb9rDY/1pnZQclETvKfN9mr9QxgKCUOX8XHROplrizPJQr3GaZvQ 45 | 7y9Ki/0sAKl+y/6yyY1Exp8jsUIHBGW2kUiWQc27ZL6dfrqEjxgSjaUYhNGTl4Q= 46 | =+8J+ 47 | -----END PGP MESSAGE----- 48 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 49 | encrypted_regex: ^(data|stringData)$ 50 | version: 3.7.1 51 | -------------------------------------------------------------------------------- /.sops.pub.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQINBGCUw3YBEADOJH/9yfDcPjKWAsmEF6n7LR2BRcNwUb5i64dNu4RiJ7+gv6ir 4 | 9HH40v8u9zZQ3xA7S0c/IDmDjgs7ZrWVjfLKQJCUOz3yLScjAaj5/Yzv+BJMchaz 5 | YGN43P7o2V7NhVapqzCUljjJ3q3sFnA6U8RoF6MHK1XpGzxu24YTC+ahW/LBfe6Y 6 | LfyqjbmuGldGJg0psmWc6qqAJlfVHezwjnwNJo+u1+9n1FYusuO5QqsQz7iIc+VG 7 | cVyP4QIOlvmpnOQjLi4VzJ3FUO3OGjS7bENK8bOKFB1mXSqAPTuaGC7lbRX+e9/r 8 | SHtKSiNl4pHJPs6TtkXgGsMi4uTQuCCzYDUmh1zypNHrE+6WNgWZkAU8M9DSH7sk 9 | Gnls8Lvh5M62s785o/APhmE50sUXKBW2URn79U2x5riv7CnTDOGqk2SSYhkqs5st 10 | imHG12aqBeQJgOHbtDGQ0cL6s71L4yZwCz6Lj8bK0IKThSb5SbEWvfPmYBYyPsv7 11 | S5rgzJqe2cXspiV1HAyowrhhf1cx5FcTeVIJmb614DAf6Q2SIUwzgfE+uTnyLb2+ 12 | eTSicKGWDbX0tZTGRwLxKos1UGgOrgzcUzC92+xLy143JJxal4bCgIATJcCy4nof 13 | NScYmVEZya0P2laaGiUrgv+rqNLnzJvLokbQ/pYJu01mI+offOgYI6ZkDwARAQAB 14 | tCVjbHVzdGVyLmhvbWUud2FpYmVsLnVzIChmbHV4IHNlY3JldHMpiQJOBBMBCgA4 15 | FiEEW/5XsoPr+qnBQ5KIKJUHWustUUkFAmCUw3YCGy8FCwkIBwIGFQoJCAsCBBYC 16 | AwECHgECF4AACgkQKJUHWustUUnGPBAAydYNWbYHd2QYF3au69BX7DavefOj80WG 17 | muxTAvvzvu5Qom3FLkzwTRLA5YNBkTdRGbNRvTNjidTgwB4EEDl1E2sv0Ouo0PvE 18 | 6HAlCnLgT4U45Ur7y0UWVp2YuD2isbr7pxX6BnAiCjSxOw3a7KbfstemyF0FYroK 19 | vyqhfqztFI1tqEaC9YnbZsmEA+QXR033t0mTcFfXvrre4TjsgCGyY8oipLynG4in 20 | XERibjASMwSOYADrzwzzcYnfmhp6DGPDZ+fzKUqjpYAlicZr4rpf6xer2Bn7584x 21 | fDwTJ/G7q8KSpIrZVWy9gc4xTX4mIep/JbM/llbc0R3sbFwJaZ+dBUpzhCtoRJHt 22 | M9SS8BsrNTjOII555kEEsg307C85h7voYqujpCVZQ7XpKBRgp4/OnH5UJ2u3hnCo 23 | +Irrj8rpGhuNLCcE4MBLpTSNPlvvzXB5cE3uch1KPcueG2DYsrkG8qJqq7yYhsyH 24 | qEWWj6mra3O8KRWDm2sNpl6Vaf221sHs7ixbB2kYOyMpi+XSKtunqSm/EYVWEe4K 25 | 3D0D+3wpFs07+xjJuVOc8ZxZ1IONZA/XnjtE3GqhqqLkm8VEd03XagIzAFEgsdG+ 26 | fIg+H23iNY3VCll1mm0QoYs9mTrYXENcpFHRtXPXJaxFqbQFsQHQq1wttupCxt2W 27 | G7TyoM8M/5u5Ag0EYJTDdgEQALenEVElifpvkri4ZUL5L3/W5fYyws/61O/JTYCu 28 | n2qjjaa0F0RVaymeCLiJlVReF8r5ba6iCijSBV/b46hHtiPfqjLRmbn8JJMKC/U8 29 | Ul7jz2xrdk1nM5nCEKF+CWZrrOSCRCbP/2uPl1TtkVezv2Ry2wWLrx4KBvC37bWG 30 | bS2XIdw9gaJSokACVq3S4wpd0bubZ5JdvLRIu1DRsDT1Hje3z+SNhuYAuw2fflly 31 | ZfRb3urFIOPI2hC6eGpxIEEGpdKptcb7cRpcSyApBvVwH68umoIJg3SJPBJjInEJ 32 | fFdfTz+0tqRXatMHVYrPuo1I6QavyIMzRSJLv59xy7dLNnX5uB8pxDEftuKEAcOm 33 | n6i9o2XHo1vMl/lmZf/kj2GVLG2ahmcr11sAM0vb/ZH+5ppNSeB+gd1xFqi42j70 34 | 2sC352nCDsscVJ2vdbgH7dIGlDV012D0HoVuAuRn0XvZ+n3LuiderL8M8kjaz0V/ 35 | BRKCc7qYhLHTOLpZ9nceUPLC3Yd40126gAP3CS/0e/Zi3xwAWxHFm+E1pU6cwW08 36 | NgFgDR2nSUuWukH1hEdRar21MBSp2DF/8CV4TQ4flHPOeWDa+QHk+VOr5fhXWOX4 37 | YJt8lDLm6fHykWZw69Ye6MHrQBZ2Ad8Dk7QrH9Xbs6upFPSjpLN59THFJJcJhiFM 38 | HSJJABEBAAGJBGwEGAEKACAWIQRb/leyg+v6qcFDkogolQda6y1RSQUCYJTDdgIb 39 | LgJACRAolQda6y1RScF0IAQZAQoAHRYhBKbkYdyItr0murh+CLsq1npTE+1dBQJg 40 | lMN2AAoJELsq1npTE+1daVYP/1RYK33wdc6PRHkMUOaWq66p62k+Bu2rSm9JwQes 41 | IS+dzqih/2btUT9emBkkWr9teJLUyyGP3O6AJw0Q9YQnrkKOLL7Pj+3d/WU7Yv2F 42 | BAzu3Q6opzbPTpTYidi9XZxyb6ZClFBH3x+sXIm6MVuAwSWBeoA1V72e6b6xE10p 43 | 6EYzvXL/oPgPYsAAAAyJVh8hOndRy+soewV0FHQv2HBOILUi9gg9GK3/HWbZ1A6Q 44 | IgAf0g+95RLwrUCIqDKIrUBtThIl57+1b3sd9/MPdFaVjOCiqVrMrh9zGGWU49LX 45 | 5FfG6H/qBdlPLq5zjfad+jp82ogvr/hk5/f8luKueTDwxwUBh0nUuKNP6XglD4vq 46 | xpCvZHdVpvVnp/xjX/uNynAHfyUencmZcVEteMWH33z4NVEQHc9Cka/Ytz3Yu7GW 47 | Lj+GLu7PVefc2UsrRwnHxARck71hw/dDer7YguxnI5kDUtHbNDU7nv2ntFHQdPZB 48 | SQub7endXPvK0q14UZkRp4YJe1Ep1eXkLzxJmTqHAzxyOVLSghg/mKr2iaKSYtIu 49 | 7nqVM9ZkIeJJPsBxWYroZxEO6QC6k4saLbLmGCmvxpMMLGRtVf7TfEhWhaAaGDOw 50 | Jr6jNgrE87phH1lfqH6XaKv9B/lP6dy1c3efQXH/NqybETdcCyH8qsuF4M9LgQrZ 51 | ywR9bY8P/i2Ad0FMHyC3+i7Q+sJEugTOsOY+gTLQ1oRX4KGXDm30NdU+HIAMz4Js 52 | V/2RgxEcttppnmdriRv13cNafnxOMNI6tr2Rm/r7hNUbxvlCi50QyWo6cPb+egjx 53 | UU5/t+vNIBxAxToMbFkDRADpW37Ji9nrj1D9XV5SeZRMX0pQftzVSdf+oDS19+3Y 54 | 3ZE5L9dENZT5aE/rSzUR0m4rOe50arIhJutwhdZ8OOgCTeRMk+VuLw9fnO7xe4mM 55 | 55hw2u2T8VwnH3rYgPGsQ86m8IUDRzcGlgPYS1EJ0YVM4Dx2sXLEb9cIs2/86inl 56 | oA0eZjkCNiT/bmcfE5abHb8gxDVBmRK37jlRZQma3cub+Z/lWvlQv0GEy1ldCsGI 57 | mhGUT2i8x3AhMc6Y5uSXysP90YNS6w6QcLsf3btHQesLesCqdIvRr2UvlSRJWC73 58 | CkxwEqWiNNGaSZ92q6W5U40O33YQAtxwWhK0C70e5A8OZd1Sk+ergOZ0RYGNugXX 59 | NUQC9U2VZ+QK/fsvx+QfwISXStu5DCXUGpuHXuW7XDa4BZLg87QgGOx5YHbzFc0t 60 | 2zNWn5GunIv/ryfohIYq6QHjLON8U5htieLkDmI1XKbaTl9Z5VOZQ6sRRxiQNpYB 61 | xwUDVFlx+US+jpzuVXWrTs9j5gkXngi1WCi/LtLR4aHTKOfCcGmZ 62 | =B2fI 63 | -----END PGP PUBLIC KEY BLOCK----- 64 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "timezone": "America/Los_Angeles", 4 | "semanticCommits": "enabled", 5 | "dependencyDashboard": true, 6 | "dependencyDashboardTitle": "Renovate Dashboard 🤖", 7 | "commitBody": "Signed-off-by: Alex Waibel ", 8 | "suppressNotifications": ["prIgnoreNotification"], 9 | "rebaseWhen": "conflicted", 10 | "commitMessageTopic": "{{depName}}", 11 | "commitMessageExtra": "to {{newVersion}}", 12 | "commitMessageSuffix": "", 13 | "helmv3": { 14 | "fileMatch": ["charts/.+/Chart\\.yaml$"] 15 | }, 16 | "helm-values": { 17 | "fileMatch": ["cluster/.+/helm-release\\.yaml$"] 18 | }, 19 | "kubernetes": { 20 | "fileMatch": [ 21 | "cluster/.+\\.yaml$", 22 | "server/.+\\.yaml$" 23 | ], 24 | "ignorePaths": [ 25 | "cluster/base/" 26 | ] 27 | }, 28 | "regexManagers": [ 29 | // Read and process HelmRelease files 30 | { 31 | "fileMatch": [ 32 | "cluster/.+\\.yaml$" 33 | ], 34 | "matchStrings": [ 35 | // helm releases 36 | "registryUrl=(?.*?)\n *chart: (?.*?)\n *version: (?.*)\n" 37 | ], 38 | "datasourceTemplate": "helm" 39 | }, 40 | // Read and process cert-manager CRD's 41 | { 42 | "fileMatch": [ 43 | "cluster/crds/cert-manager/.+\\.yaml$" 44 | ], 45 | "matchStrings": [ 46 | "registryUrl=(?.*?) chart=(?.*?)\n.*\\/(?.*?)\\/" 47 | ], 48 | "datasourceTemplate": "helm" 49 | }, 50 | // Read and process kube-prometheus-stack CRD's 51 | { 52 | "fileMatch": [ 53 | "cluster/crds/kube-prometheus-stack/.+\\.yaml$", 54 | ], 55 | "matchStrings": [ 56 | "registryUrl=(?.*?)\n *tag: (?[a-zA-Z-]+)-(?.*)\n" 57 | ], 58 | "datasourceTemplate": "helm" 59 | }, 60 | // Read and process Traefik CRD's 61 | { 62 | "fileMatch": [ 63 | "cluster/crds/traefik/.+\\.yaml$" 64 | ], 65 | "matchStrings": [ 66 | "registryUrl=(?.*?) chart=(?.*?)\n *tag: v(?.*)\n" 67 | ], 68 | "datasourceTemplate": "helm" 69 | } 70 | ], 71 | "packageRules": [ 72 | // setup datasources 73 | { 74 | "matchDatasources": ["helm"], 75 | "semanticCommitScope": "charts", 76 | "separateMinorPatch": true, 77 | "ignoreDeprecated": true 78 | }, 79 | { 80 | "matchDatasources": ["docker"], 81 | "enabled": true, 82 | "matchUpdateTypes": ["major", "minor", "patch"] 83 | }, 84 | { 85 | "matchDatasources": ["docker"], 86 | "semanticCommitScope": "images", 87 | "separateMinorPatch": true 88 | }, 89 | // add labels according to package and update types 90 | { 91 | "matchDatasources": ["docker"], 92 | "matchUpdateTypes": ["major"], 93 | "commitMessagePrefix": "feat(images)!: ", 94 | "labels": ["renovate/image", "dep/major"] 95 | }, 96 | { 97 | "matchDatasources": ["docker"], 98 | "matchUpdateTypes": ["minor"], 99 | "semanticCommitType": "feat", 100 | "labels": ["renovate/image", "dep/minor"] 101 | }, 102 | { 103 | "matchDatasources": ["docker"], 104 | "matchUpdateTypes": ["patch"], 105 | "semanticCommitType": "fix", 106 | "labels": ["renovate/image", "dep/patch"] 107 | }, 108 | { 109 | "matchDatasources": ["helm"], 110 | "matchUpdateTypes": ["major"], 111 | "commitMessagePrefix": "feat(charts)!: ", 112 | "labels": ["renovate/helm", "dep/major"] 113 | }, 114 | { 115 | "matchDatasources": ["helm"], 116 | "matchUpdateTypes": ["minor"], 117 | "semanticCommitType": "feat", 118 | "labels": ["renovate/helm", "dep/minor"] 119 | }, 120 | { 121 | "matchDatasources": ["helm"], 122 | "matchUpdateTypes": ["patch"], 123 | "semanticCommitType": "fix", 124 | "labels": ["renovate/helm", "dep/patch"] 125 | } 126 | ] 127 | } 128 | -------------------------------------------------------------------------------- /cluster/base/cluster-secrets.sops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: cluster-secrets 5 | namespace: flux-system 6 | stringData: 7 | SECRET_ALERT_MANAGER_SLACK_WEBHOOK: ENC[AES256_GCM,data:Yiuo6Vs5Xb0Da1slb78oQKGoP7Km04hN+OOENNYobz3SVpZ4DJuvOVDofPzljCXk1M6Qb5tMqmOHxa6r9vS2k7lq+vStyP11d6s2kOlTKL/h,iv:AdmRN6bD5xD7xNpGqjcYqKTVO0eYx3jdoLA7tVoN98o=,tag:DfKR3L8LxZDwDIgzZUNGPg==,type:str] 8 | SECRET_CLOUDFLARE_EMAIL: ENC[AES256_GCM,data:nqHiQU+pxY5UWfLfZA8=,iv:tlCj2/0cjSO0RoXhcA7U0Q/b7uXVzy6u4oDMBlhKrow=,tag:6/xyxfylZAlWhGXwmbWj2Q==,type:str] 9 | SECRET_DOMAIN: ENC[AES256_GCM,data:8heuDZofPl3ouJOKOqc=,iv:kwbGhynaSaUEJg7yyRnrM1wbz2D7dH0A6Bsy8/WAUaQ=,tag:FmNAehZlqlqrMTQkbLr3Lg==,type:str] 10 | SECRET_EMQX_ADMIN_PASSWORD: ENC[AES256_GCM,data:pcvyImRyVyPYjQ5dUXH4WGpFv2maQUtGTcJoCrSMbYk=,iv:1Ys8YeP+ByGpKlG4kJLk3cuMwbPxDC+fS3to4aCGR7g=,tag:4QQhvOZ+9LU/hvZo04sd1g==,type:str] 11 | SECRET_ICECAST_PASSWORD: ENC[AES256_GCM,data:cAd+3fdNpcXwOqHVn15wLDgOG5FUgVWwIJ7DbCE/QYE=,iv:08w0VNOqlmKHFRp+ztVP4kqGZa9T+47NsY2jELYfE6Q=,tag:1nlk3+IyOYV9tOzQHnJjiQ==,type:str] 12 | SECRET_MOPIDY_SPOTIFY_CLIENT_ID: ENC[AES256_GCM,data:eE/YUZ9tBIdbfzPmNCh76bxPKYG0OPKbsmb1HehSZWJh+QpX,iv:Kb0SI8Vv7GnXzXZGPN0bptiU3ZusGrMV0NKzizzL15M=,tag:FP9RNLbv7Muz+B5lA8O5sg==,type:str] 13 | SECRET_MOPIDY_SPOTIFY_CLIENT_SECRET: ENC[AES256_GCM,data:EZ8rEufnHFHP4HYZdeQbCr0g2aV9YPxjtfWWMsTul8Diz7sLlyT7iaRGkUo=,iv:Pf58SnIQwtwuNA78QDeZ8GspT+7tGYVp9Q1QaVl4hJg=,tag:mPGIGW+/RFsTE21nfJkdOw==,type:str] 14 | SECRET_MQTT_USERNAME: ENC[AES256_GCM,data:drvS1w==,iv:OHnHxiRCb+HDt3Njo7TbWs28Ekqqbzo9MSVUXgcvg0g=,tag:WVSncHAWZvcWFTdxuBSOaA==,type:str] 15 | SECRET_MQTT_PASSWORD: ENC[AES256_GCM,data:tl8Ln1bmmouHVQXOgRIw4WDhbExSQEEWaN1J21Pyiug=,iv:MH9tNXSB6ipFP1DmNasPONKpcJPRRKOr15BjTpUvJuc=,tag:178lLA2Gzc8u6nSMP2BvsA==,type:str] 16 | SECRET_SPOTIFY_USERNAME: ENC[AES256_GCM,data:LT1xfYHh/gv1NfI=,iv:DrpGPKk9siUF3v05qUjUyQecYGVMdcYvtxqJizc/C1I=,tag:hU+8O25akgN6V3jlW9TvxQ==,type:str] 17 | SECRET_SPOTIFY_PASSWORD: ENC[AES256_GCM,data:cZwJpoTNK+YJPCm4Y1ycudK3MwA=,iv:U7ChZ2VKjz/BO2rT6f97EDsD5AQvjMfdWYBewrPYqn8=,tag:VvtSWwZ7XlQ11kIWm9MYlA==,type:str] 18 | UNIFI_USER: ENC[AES256_GCM,data:M/Tj1n6O6YkfJA==,iv:cMe67xeos82sKv6FMuoUnUciSLtIfZyWmM1iAApqkZ0=,tag:+mNJAkrVNWre62n0ekIrlQ==,type:str] 19 | UNIFI_PASS: ENC[AES256_GCM,data:WKHUO1NjS+vZRSEU3d8CDDqFAXOjpWfa3fuE+Hv3uYE=,iv:g6iGtAA5uReAbD7nKzLcaPJR3YoZTRHSbDyNHK/XMi8=,tag:PPew9yko3hxYiQx9Rx29mw==,type:str] 20 | sops: 21 | kms: [] 22 | gcp_kms: [] 23 | azure_kv: [] 24 | hc_vault: [] 25 | age: [] 26 | lastmodified: "2021-10-18T09:11:16Z" 27 | mac: ENC[AES256_GCM,data:gJc1Tgg3LX05KSGsVTXJVarVw1ntdkDu02vKKg0zxbg6UzvbXhI4aqILg2uje1J3tDNf85IC170+niCIpWB+rXVgCd0dpuTrOzI0FO+55w7439o/+cLRH9uT4/g2d95Klzo43D+9v7u4IWN6YzRqewdGnBMbuqKWKqSBXSu/TDk=,iv:pYy1gsPTnRAJSEt5DgXOlsf/YPMnvGuffXwUZfB2MzM=,tag:FhGtJyWNu79wQfyBhYbXsg==,type:str] 28 | pgp: 29 | - created_at: "2021-09-15T02:57:07Z" 30 | enc: | 31 | -----BEGIN PGP MESSAGE----- 32 | 33 | hQIMA7sq1npTE+1dAQ//Uak/9N/eAX++t8qi/J23j7T3hXOH5Z2y1ledu7pRUULo 34 | T0hawhENRCJlokS89Pof83kTi2LdUYZazM16cNHHpZORqawKKNTGw3WbCxXLrOLg 35 | O6JL/oSj2f+e1j3gC+cpByEqgs9BBg7Rpn8HpMU9oH0+/BM1c2Lk0JB4yruXBiPs 36 | ItOTfA76I8SKQ+SRa0PsbwX8cllefWSpkKlClbCiAXBtaM2c1hhPS0nJgj33GHI+ 37 | p8Xas4s5YvYoQSs4xzFY2X9YI60ApybnP3llmjDBHwwGQjxCJaHqCZYsxIF3JG8h 38 | 35Te8AgpxFSyUgHPmK+Yusnm5eiKi+XnsBBQwwW4lMNuWIU4oKgU9fq4FRwVVvSH 39 | CzfjivTjfo25yZnbQs9jmXnJNG13GQMGXAN++24yq2ob835FM5lbCaQ+ZxJKUBmh 40 | DW+oJnx9PkbAcKA8W1DeLczu4YgQqZnj95JYk5UtBJjD6qlVfiquf2PgrwXwjFz9 41 | Eox0A9hWtSTTvzonOUE0MdnFXKWqtSkEr31gf8xOTp6wty7nRslykTAp7scAqMYR 42 | WgY+f1aJPfRTzgtfOqQL50b0cCFC94pHXRQAcEgFBvNVdhDYsr44ONMng6fTjW1G 43 | uHAojn7YxsX65POsOJ8zhC39CjdJpTBdXOQHDrdRuzbS0B/hrxX0v+rn4hNkF6bS 44 | XgHjJvV2O6JozhguiaQsQrR5VK0HGHztqmr70crF4QKq6MjJNwDTJPurvzPEndiB 45 | Q5AWk9Pk2J8GwvROp1CJ4T31xS0ZUfmyUDbPFYb4EbQkBSJI4ELFdcV5MD/+1us= 46 | =hbid 47 | -----END PGP MESSAGE----- 48 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 49 | encrypted_regex: ^(data|stringData)$ 50 | version: 3.7.1 51 | -------------------------------------------------------------------------------- /cluster/apps/observability/grafana/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: grafana 6 | namespace: observability 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://grafana.github.io/helm-charts 12 | chart: grafana 13 | version: 6.21.5 14 | sourceRef: 15 | kind: HelmRepository 16 | name: grafana-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | image: 21 | repository: ghcr.io/k8s-at-home/grafana 22 | replicas: 1 23 | admin: 24 | existingSecret: "grafana-password" 25 | userKey: "SECRET_GRAFANA_USER" 26 | passwordKey: "SECRET_GRAFANA_PASSWORD" 27 | grafana.ini: 28 | server: 29 | root_url: "https://grafana.${SECRET_DOMAIN}" 30 | paths: 31 | data: /var/lib/grafana/data 32 | logs: /var/log/grafana 33 | plugins: /var/lib/grafana/plugins 34 | provisioning: /etc/grafana/provisioning 35 | analytics: 36 | check_for_updates: false 37 | log: 38 | mode: console 39 | grafana_net: 40 | url: https://grafana.net 41 | auth.basic: 42 | disable_login_form: false 43 | dashboardProviders: 44 | dashboardproviders.yaml: 45 | apiVersion: 1 46 | providers: 47 | - name: "default" 48 | orgId: 1 49 | folder: "" 50 | type: file 51 | disableDeletion: false 52 | editable: true 53 | options: 54 | path: /var/lib/grafana/dashboards/default 55 | datasources: 56 | datasources.yaml: 57 | apiVersion: 1 58 | # list of datasources that should be deleted from the database 59 | deleteDatasources: 60 | - name: Loki 61 | orgId: 1 62 | datasources: 63 | - name: Prometheus 64 | type: prometheus 65 | access: proxy 66 | url: http://prometheus-operated:9090 67 | isDefault: true 68 | dashboards: 69 | default: 70 | # Ref: https://grafana.com/grafana/dashboards/11001 71 | cert-manager: 72 | gnetId: 11001 73 | revision: 1 74 | datasource: Prometheus 75 | flux-cluster: 76 | url: https://raw.githubusercontent.com/fluxcd/flux2/main/manifests/monitoring/grafana/dashboards/cluster.json 77 | datasource: Prometheus 78 | flux-control-plane: 79 | url: https://raw.githubusercontent.com/fluxcd/flux2/main/manifests/monitoring/grafana/dashboards/control-plane.json 80 | datasource: Prometheus 81 | # Ref: https://grafana.com/grafana/dashboards/12250 82 | traefik: 83 | gnetId: 12250 84 | revision: 1 85 | datasource: Prometheus 86 | # Ref: https://grafana.com/grafana/dashboards/11315 87 | unifi-client-insights: 88 | gnetId: 11315 89 | revision: 8 90 | datasource: Prometheus 91 | # Ref: https://grafana.com/grafana/dashboards/11311 92 | unifi-network-sites: 93 | gnetId: 11311 94 | revision: 4 95 | datasource: Prometheus 96 | # Ref: https://grafana.com/grafana/dashboards/11314 97 | unifi-uap-insights: 98 | gnetId: 11314 99 | revision: 9 100 | datasource: Prometheus 101 | # Ref: https://grafana.com/grafana/dashboards/11312 102 | unifi-usw-insights: 103 | gnetId: 11312 104 | revision: 8 105 | datasource: Prometheus 106 | sidecar: 107 | dashboards: 108 | enabled: true 109 | searchNamespace: ALL 110 | datasources: 111 | enabled: true 112 | searchNamespace: ALL 113 | plugins: 114 | - grafana-piechart-panel 115 | - grafana-worldmap-panel 116 | - grafana-clock-panel 117 | serviceMonitor: 118 | enabled: true 119 | ingress: 120 | enabled: true 121 | ingressClassName: "traefik" 122 | annotations: 123 | cert-manager.io/cluster-issuer: "letsencrypt-production" 124 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 125 | hajimari.io/enable: "true" 126 | hajimari.io/icon: "view-dashboard-variant" 127 | hosts: 128 | - "grafana.${SECRET_DOMAIN}" 129 | tls: 130 | - hosts: 131 | - "grafana.${SECRET_DOMAIN}" 132 | secretName: "grafana-${SECRET_DOMAIN//./-}-tls" 133 | serviceAccount: 134 | create: true 135 | autoMount: true 136 | persistence: 137 | enabled: false 138 | -------------------------------------------------------------------------------- /server/terraform/proxmox/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/local" { 5 | version = "2.1.0" 6 | hashes = [ 7 | "h1:EYZdckuGU3n6APs97nS2LxZm3dDtGqyM4qaIvsmac8o=", 8 | "zh:0f1ec65101fa35050978d483d6e8916664b7556800348456ff3d09454ac1eae2", 9 | "zh:36e42ac19f5d68467aacf07e6adcf83c7486f2e5b5f4339e9671f68525fc87ab", 10 | "zh:6db9db2a1819e77b1642ec3b5e95042b202aee8151a0256d289f2e141bf3ceb3", 11 | "zh:719dfd97bb9ddce99f7d741260b8ece2682b363735c764cac83303f02386075a", 12 | "zh:7598bb86e0378fd97eaa04638c1a4c75f960f62f69d3662e6d80ffa5a89847fe", 13 | "zh:ad0a188b52517fec9eca393f1e2c9daea362b33ae2eb38a857b6b09949a727c1", 14 | "zh:c46846c8df66a13fee6eff7dc5d528a7f868ae0dcf92d79deaac73cc297ed20c", 15 | "zh:dc1a20a2eec12095d04bf6da5321f535351a594a636912361db20eb2a707ccc4", 16 | "zh:e57ab4771a9d999401f6badd8b018558357d3cbdf3d33cc0c4f83e818ca8e94b", 17 | "zh:ebdcde208072b4b0f8d305ebf2bfdc62c926e0717599dcf8ec2fd8c5845031c3", 18 | "zh:ef34c52b68933bedd0868a13ccfd59ff1c820f299760b3c02e008dc95e2ece91", 19 | ] 20 | } 21 | 22 | provider "registry.terraform.io/hashicorp/null" { 23 | version = "3.1.0" 24 | hashes = [ 25 | "h1:vpC6bgUQoJ0znqIKVFevOdq+YQw42bRq0u+H3nto8nA=", 26 | "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2", 27 | "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515", 28 | "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521", 29 | "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2", 30 | "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e", 31 | "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53", 32 | "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d", 33 | "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8", 34 | "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70", 35 | "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b", 36 | "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e", 37 | ] 38 | } 39 | 40 | provider "registry.terraform.io/hashicorp/random" { 41 | version = "3.1.0" 42 | hashes = [ 43 | "h1:BZMEPucF+pbu9gsPk0G0BHx7YP04+tKdq2MrRDF1EDM=", 44 | "zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc", 45 | "zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626", 46 | "zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff", 47 | "zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2", 48 | "zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992", 49 | "zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427", 50 | "zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc", 51 | "zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f", 52 | "zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b", 53 | "zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7", 54 | "zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a", 55 | ] 56 | } 57 | 58 | provider "registry.terraform.io/hashicorp/template" { 59 | version = "2.2.0" 60 | hashes = [ 61 | "h1:94qn780bi1qjrbC3uQtjJh3Wkfwd5+tTtJHOb7KTg9w=", 62 | "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", 63 | "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", 64 | "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", 65 | "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", 66 | "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", 67 | "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", 68 | "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", 69 | "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", 70 | "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", 71 | "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", 72 | ] 73 | } 74 | 75 | provider "registry.terraform.io/telmate/proxmox" { 76 | version = "2.9.5" 77 | constraints = "2.9.5" 78 | hashes = [ 79 | "h1:7CHXiX52C3YDsL7N3F183BqHYBg9a7E6YxPVpZsB15k=", 80 | "h1:7yXobkaX1tmLvD7q54EVjZdpGVdmDYzGHU3ldWyKt1E=", 81 | "h1:F8AXdgj80/ebxlPne+1Zgc77mpBmwodvVwZN8AKOlgA=", 82 | "h1:LXNlwouNcL7xwYB9mRBij7Y9eWVG+yUw7bsZUrRb/8s=", 83 | "h1:Os/hDOu0hLYJojWpktnWd0PrBrbmjrIBkHxcxr0Q4aE=", 84 | "h1:X2x8KY3l2W7Ae66LukqrQLTujmJMTO7MjQH1RlCf/K4=", 85 | "h1:XloQkL57l563FA+VEXc07l1M8Vkx0Pg0P5WNBSzejuY=", 86 | "h1:hPZb6EP6S6EAudJrcGB1r8++wmeW6S0ytWPO3qEJ51s=", 87 | "h1:jNUW3PT7h8JSg8Ps/i5U396YIwcUeYIAAr378W09yHg=", 88 | "h1:lpbxEwdxlpYDyu7m2YBeDvn5W1yNvHNHq9w4BotJ2H4=", 89 | "h1:uCkSva0R193/zZAb42j+7CHj848D+VoRldDxSUwS7Bs=", 90 | "h1:vj2frwkHmlCDapvMCXcCvQYpklhrcjNKQIK7pJxQ968=", 91 | "h1:xRfZJvJb7+3P+wnRUd0iIBKvr5aOAjvgmKv9gTST0WI=", 92 | "h1:z8znw1WxrdL/EiRIto4AGMCyAzWwuMY9gmSXnr0xQ4Q=", 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /cluster/apps/home/home-assistant/code-server.sops.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | metadata: 6 | name: code-server 7 | namespace: home 8 | stringData: 9 | id_rsa: ENC[AES256_GCM,data:NnrtBe2IIa+I8UfWMwooyuRsZrPtxzqLrH0m+g81GAYGhZyOJ2hlTHQpjq7OH823hsL/vwLBcFRWrQqjpAbny6qSVWEStBeFeBGGu4GlKt5biDNuwkT90dYBdeJGDiyuo252ouO+ACRySVukZ/CAmmt3L1er3N6xRYOlHusPxplxNFI1lQbYsBeU6H48KMeg4g9XTki8z6ZhSpwsN2IlwfcL5m340voaN2ylvJGN0JUmVlUYdrNhdr/w+Ki53frnbxr7AODKub49vNfpnPfzheLYOF9rvBzg3BA92AKukeY3ATADRCzuWdXR7hsyUcs6gNaB8DNYD8ZXEP/huPau2VE07PfY71a6vRHnEX87cVLcr1nF8TRKR3X3CqJZIVVT6XQ2GzldSH0rvyCkyCzWU3rgpuzetzIzfTLd3l8MmnLvy1Uom3x2XnMUh8LXz/pvhpzlfQYuyfkg9eGhPPJsXk6NtG0C7ruO4GwwJ2SvfvNyKFpYxloakpnu/wCrD2gfq/1S2NSxryX4q500F3qsMsHdMQUyFAHVufLQ9TqU+F0NCyHDWIXdlqrzpA/a8U8aUjKPLIjLEhWfeygdPR8i/SEAAbeo+yDXHWHcBA4EbxyvHB28owvJLd0unxHofoEKtJ4IVlEEM4SNMac8vM/PAvkdgxFm8eYBeDIHa1DIrOaIm1GU6CafbMC6V1hemKhkRyfnbGkmD1lJW3+43MrJ+psWQVijmUNS59nYaJqKsL6dRdFzV+qW8XapcdmKM4XsKM91d1vZ34EqVf1FEJwXfYF4mlwVMHpeAQlfgVmQu8nTY4c443Np8o7F2YkE+7X40xvyg1wdyS6POERChUBEkUJPvJkgfe6sWQmyQfHvkSE20xb08zC5U5DaX7zB5+v5vOdUpWv+AMbxdnYQHr/FGgSKwlTDQOU9IYklNfA09SkqIkRgWw//LAIKLnDHd5ktV5lgCiHRvP99nEjAGD5mZjtzeBmq/5NBcoZzZVY3uW7w++jnoWNZZMoy10VASeJvsbLmfF2FEWDm7wrWx5Wt3738gmIh0V+Bhfs3mPgnU7r2JS/Lnm+9sIeG+Q8N/FcDEyc5IY+Ba6RvAywMXVWSb6tLL0CHDPWfLLftj8vqy/ZRfe/1+keyoQcvEheF48DnIDJuALmLJZdPTKQBR/LyZq18c6XA0+d276N31JOu8SMPkr6OlzAT1wLup58jaCTHMBFw8zENDyHfcwEKsLXwv5HkvK3UeKMcMSA7qH+c9Bi6sz9MTTLYVQ7DHLh2S04RMD4Q96dq5tj8E11OvaCnOX5WPM3NOhDPDQDrDJVxzL8mh/GjUdWmaukoCD39/8VOEQaFnALvmoY6+jhyxjFHVXToVicKn5UKGdbcSEOosZXEggU1KfwSc1OEU8lLBaScR1y/sEjMcNf1mhiudGZIvEpF2dLZI8yT0j5/FwWYIR84RG27lES/jJd06aWH1ZkOhnlggJfE2bLX++36iykCCxPxcupAnBphiY4F4f8+XiduqO86LqO7A/ttOk3C2OJy+zwYtnolVDwCO7x9iP2Peps5f3U061cvm8qF9jyk1YCBYYtT45mw4lBwvWuQFV366L9yAXO+dO2FVWQVclfjflVn75YepspanG2z6BLEOpscKq6STCzLQQpLO6if3yfrqxopkuJFKJRjIQ2H5HtHiVQ0qPPuWEAzIbO+ANLpwHb/KhC0/0CUA6+YTXX4zyE+GQXkt8ixM8lk6n1wPW+WDrzMhdNYYfCv4wSeuriG1qID7DuNtEFjuo+9gj4Jg565Va5i1vQ7U8b2J+Aqjj06bOJ51gSlyvBIb8sPdhjgB5abpoU/AIEdXGn9NbnKhUzoVjzcf3gOmB61xv5lpEauepxAefpnr7T8rM4y6sISd2htVT8I/zd6duLADsbAJTf5ltVn3n95/pj2u0+aEJawIkFHW6wywuhZVxVjl3AlnRVxIEjQe/D2+pEjngWOipBGE97vE7U20XQ1q8bBCKsNzMLYlnE5PWAl/9d2MRLqbb/PDJaDyV/KrZLzTrEofCrF9AVpsnk74DRSNm1th0uDe151tFEE906bOtpbN2yRRjEwuu2+glSCyvg/hsgJvi/HvP+bTrelISbPlRV1SVO+o4kua9mDqhGQlVCazznji6VhdI1W/6MSvq7pMl0IxKJlpA+vNpQEKjdsbHY5M92WYLz++IohBFc0a73Lv18YT6/6epq/nVlKP4b1yzptwA6WOQS4JawCZfe/Zy2gfZFc/ss1Y0zyaipkuou36WDWG+GtcTRYWVAi0N9OIZrEMeSeUp1sapGQVeH6bKP5UNHIJNTuXymZYtDXDhA06R7i9ND+iqSKN7/GFrPytROZ/9kr50zHKNMcw4jX+dHLBHjmpnilRfSrM6uw6/T6pInP5/j27fn7c6s/NDo2MUAjcbsCRPEmjEP+J2NpMf23wQcZMAfriK59EgI35XkVU98nKtayXrAm9RIiGY8AZEf67CRMz4ho01z0KtnSFDyjBlkCn/Lo0kZDb7oIeKwvJG9+pjju84LL08M6FyBQhl1VaDbqHt8gZ5eupaw88iB09QoVAyb/IUdFQfKuUbD6VElsrI8MwOWlXN7Y8AzdCObvCTdMwkZHlEp42Cn7sGIjP+HhtPYpeMtq7kptLP6sxfZFBWJoCJFoWs6scRg3DHeEdE+WvvAsWwOw+mk60VTDXW/p9vadx+PF1a0hCneNOU5kZQbYi+xsJE8NX42kUkuA+CK0lmTDVDffuho2rGD1uw5QLhntrsG4s5DBDMGPXz8ieeUBT4DuOYIcmbSl3/4Xp8jikmukpbaUcUKK3FhxTV7tQOzROtK0JUaPPHIHi0KvTxSvwmj5/8gScBgjb3Z54wdmq2VzsZHbypvVRFGa+1fzWo6+p8nwLQCQMsjCLwDia+TyO60yTYcXyk/f6ogeugghS+xyqDQxLN1cm+lTY+VxWW0BZsqGblXx4wdi2e+zCEG0CCnFkb9Sk+xUOJTnFnXNCTN8IOmampdZrbKeHTvDQCNf0Ibw3GAk9SU+18OfB5/zG4ip1nWw/tsjZd7PRxEZCcZsbCmyFjh+Ic0+fvVWHYo+4YtcHuUdP0RUjAMlwPsM0m3WD6FqshGb8xq46BwcOaQ/5iMezfAwr+GbC/qdAQEnOzNnh7XfIdY8m8P0tIGIssV/yTmAw6GjRZztNbUCz+aCyW2c8JoPJ308YNHTpKxMrewMEYUQPnk4xYa/CEXGT0Wb17C22EIMwHTn9n5zaIONOroY6EpNTx9qIOU6YcpbFrxEKjh2Xk/ndGCvtsX3pW3xvFogBemLnp4lQ/rF4GgHdEsB83+xMk93CzuAgRJFkMAFjlu9Ce+NdNS6OMc2c5MYK7JCZCK042cNssrJVyH5vWcBRkZvMwmjA6zXoKIl9ee7s8NiyHssmvVIbaq4na962PmGj36MnJ5INfac0TLrrO6ZMuswmXvyB1+H9QLRrxiMBhGQmQqJo0efr7j94stLFo3k3ybzXAaSqxwD3fQ0hrGGQ1GezWobWm5wZK7B2rj9NLx7clCikYYJN+6eBNQ=,iv:MXiLANg5kr7MY6rwuEP4Oas2ahGecYfIU/pq2jtVDZg=,tag:NWiYLwunbmYKuLVrcrC1Ow==,type:str] 10 | sops: 11 | kms: [] 12 | gcp_kms: [] 13 | azure_kv: [] 14 | hc_vault: [] 15 | age: [] 16 | lastmodified: "2021-10-06T04:20:57Z" 17 | mac: ENC[AES256_GCM,data:hyF/+fnPQNWhKP2eF85hOmV4TVjjHDeAHbDjuQxJR0W51+uZCaHIqg/7UZeRYjTzQ6KRZlupwgMlJ7eVhVOob2JlK/7dqln9S7NH9YDPQe+KRvdj55YwhKSsJ9fw31tugDGXJxN7CX6yjcvWXtWa5f50e9ZpnZvCBZA7XLzcP8w=,iv:z/40edZjV1EbyNhaATzurl0EE3juPnhZOeiXNlVRIbI=,tag:Ta3zJXTQdpmq5zu4GggV/Q==,type:str] 18 | pgp: 19 | - created_at: "2021-10-06T04:20:56Z" 20 | enc: | 21 | -----BEGIN PGP MESSAGE----- 22 | 23 | hQIMA7sq1npTE+1dAQ/9EI2q6LAEZdR6HBF0JKj2GHE5lWfu8kp/P5QnaATXC/f0 24 | OBpJ02Zw4S8PquB2ixPHJA/2WmBDNI6pWiqRTo8gzqAmIRmXTKLEb7+phL6Q75qR 25 | zLCwjbr8Xp/rpCnZHvFGk4lar692TawUZgStVf6mfY16AYSgv8Typ6ssJ74yHo10 26 | YIIsPU2qcK7EUrxGZj4XYBoG4C9WXd9JX/f3cMoMHFQ2GtNrtN0o3+Lmh+oLceqv 27 | /vWhpQrG6Fopk+hhcMKf7xeSvv7SbARYTscY34mDEmoUdyi+Zb5UfNp/nZZscazv 28 | jCx7TTPiOeaw0bpQqQ1oYglXK1d4/w8wLgs9Gdry8PDUDO8vZVuEzh31DpeE80bh 29 | a9KPOBQgWMzZ831sM2Y/qIONLK9UrLUetngn1bH4tbCih7yRPn6ypBfAeoZYiwA/ 30 | oJStokHsP5Lx/Y3c09KdHufisKW0oZ0Webo3SdIK0Z0TnFBws5QOHn48+4nEXnCn 31 | Gf9smBQyfLY1Hs6A37gjJGr/gZ6+gi8GsxrDOUvnzD9raSNM8kFe6fmtsZyK0m3O 32 | iHNW+ArEu3gPmMu8cYfrvAhMiENPL9QB+qqvXPjjE4yv7qRmOEPSs+88n56PxE9G 33 | dzN6g9tBc9uwXpx8B3YDeDjTZ1KiNgqgojMKnT/JCK18tNU4pTYIEE1T1YSCApHS 34 | XAGlIYgizlqEa8EbR+xCO/SDsfQleHIvrazDjb4JgQZ7Lf5u6jyhJq1sJa9Q0A3n 35 | VohNxe8JW10jFn0aB/q9v/0SmyS3iAyH7BHyHEduZhYvf4/+MBloTAE3HxtL 36 | =owxZ 37 | -----END PGP MESSAGE----- 38 | fp: 5BFE57B283EBFAA9C14392882895075AEB2D5149 39 | encrypted_regex: ^(data|stringData)$ 40 | version: 3.7.1 41 | -------------------------------------------------------------------------------- /server/packer/templates/debian-preseed.pkrtpl.hcl: -------------------------------------------------------------------------------- 1 | #### Original template: https://www.debian.org/releases/stable/example-preseed.txt 2 | #### Contents of the preconfiguration file for Debian 11 (bullseye) 3 | ### Localization 4 | # Preseeding only locale sets language, country and locale. 5 | d-i debian-installer/locale string en_US 6 | 7 | # Keyboard selection. 8 | d-i keyboard-configuration/xkb-keymap select us 9 | 10 | ### Network configuration 11 | # netcfg will choose an interface that has link if possible. This makes it 12 | # skip displaying a list if there is more than one interface. 13 | d-i netcfg/choose_interface select auto 14 | 15 | # Any hostname and domain names assigned from dhcp take precedence over 16 | # values set here. However, setting the values still prevents the questions 17 | # from being shown, even if values come from dhcp. 18 | d-i netcfg/get_hostname string fileserver 19 | d-i netcfg/get_domain string ${domain} 20 | 21 | ### Mirror settings 22 | d-i mirror/country string manual 23 | d-i mirror/http/hostname string http.us.debian.org 24 | d-i mirror/http/directory string /debian 25 | d-i mirror/http/proxy string 26 | 27 | ### Account setup 28 | # Skip creation of a root account (normal user account will be able to 29 | # use sudo). 30 | d-i passwd/root-login boolean false 31 | 32 | # To create a normal user account. 33 | d-i passwd/user-fullname string debian 34 | d-i passwd/username string debian 35 | # Normal user's password, either in clear text 36 | d-i passwd/user-password password debian 37 | d-i passwd/user-password-again password debian 38 | 39 | ### Clock and time zone setup 40 | # Controls whether or not the hardware clock is set to UTC. 41 | d-i clock-setup/utc boolean true 42 | 43 | # You may set this to any valid setting for $TZ; see the contents of 44 | # /usr/share/zoneinfo/ for valid values. 45 | d-i time/zone string US/Pacific 46 | 47 | # Controls whether to use NTP to set the clock during the install 48 | d-i clock-setup/ntp boolean true 49 | # NTP server to use. The default is almost always fine here. 50 | #d-i clock-setup/ntp-server string ntp.example.com 51 | 52 | ### Partitioning 53 | # Specify a disk to partition. 54 | # Use the first SCSI/SATA hard disk: 55 | d-i partman-auto/disk string /dev/sda 56 | # Specify the method to use. 57 | # The presently available methods are: 58 | # - regular: use the usual partition types for your architecture 59 | # - lvm: use LVM to partition the disk 60 | # - crypto: use LVM within an encrypted partition 61 | d-i partman-auto/method string lvm 62 | 63 | # Define the amount of space that will be used for the LVM volume 64 | # group. It can either be a size with its unit (eg. 20 GB), a percentage of 65 | # free space or the 'max' keyword. 66 | d-i partman-auto-lvm/guided_size string max 67 | 68 | # If one of the disks that are going to be automatically partitioned 69 | # contains an old LVM configuration, the user will normally receive a 70 | # warning. This can be preseeded away... 71 | d-i partman-lvm/device_remove_lvm boolean true 72 | # The same applies to pre-existing software RAID array: 73 | d-i partman-md/device_remove_md boolean true 74 | # And the same goes for the confirmation to write the lvm partitions. 75 | d-i partman-lvm/confirm boolean true 76 | d-i partman-lvm/confirm_nooverwrite boolean true 77 | 78 | # You can choose one of the three predefined partitioning recipes: 79 | # - atomic: all files in one partition 80 | # - home: separate /home partition 81 | # - multi: separate /home, /var, and /tmp partitions 82 | d-i partman-auto/choose_recipe select atomic 83 | 84 | # This makes partman automatically partition without confirmation. 85 | d-i partman-md/confirm boolean true 86 | d-i partman-partitioning/confirm_write_new_label boolean true 87 | d-i partman/choose_partition select finish 88 | d-i partman/confirm boolean true 89 | d-i partman/confirm_nooverwrite boolean true 90 | 91 | ### Apt setup 92 | # You can choose to install non-free and contrib software. 93 | #d-i apt-setup/non-free boolean true 94 | #d-i apt-setup/contrib boolean true 95 | d-i apt-setup/universe boolean true 96 | # Select which update services to use; define the mirrors to be used. 97 | # Values shown below are the normal defaults. 98 | d-i apt-setup/services-select multiselect security, updates 99 | d-i apt-setup/security_host string security.debian.org 100 | 101 | ### Package selection 102 | # Individual additional packages to install 103 | d-i pkgsel/include string openssh-server qemu-guest-agent cloud-init 104 | # Whether to upgrade packages after debootstrap. 105 | # Allowed values: none, safe-upgrade, full-upgrade 106 | d-i pkgsel/upgrade select full-upgrade 107 | 108 | # Policy for applying updates. May be "none" (no automatic updates), 109 | # "unattended-upgrades" (install security updates automatically), or 110 | # "landscape" (manage system with Landscape). 111 | d-i pkgsel/update-policy select unattended-upgrades 112 | 113 | # Some versions of the installer can report back on what software you have 114 | # installed, and what software you use. The default is not to report back, 115 | # but sending reports helps the project determine what software is most 116 | # popular and should be included on the first CD/DVD. 117 | popularity-contest popularity-contest/participate boolean false 118 | 119 | ### Boot loader installation 120 | # Grub is the boot loader (for x86). 121 | 122 | # This is fairly safe to set, it makes grub install automatically to the UEFI 123 | # partition/boot record if no other operating system is detected on the machine. 124 | d-i grub-installer/only_debian boolean true 125 | 126 | # This one makes grub-installer install to the UEFI partition/boot record, if 127 | # it also finds some other OS, which is less safe as it might not be able to 128 | # boot that other OS. 129 | d-i grub-installer/with_other_os boolean true 130 | 131 | # Due notably to potential USB sticks, the location of the primary drive can 132 | # not be determined safely in general, so this needs to be specified: 133 | #d-i grub-installer/bootdev string /dev/sda 134 | # To install to the primary device (assuming it is not a USB stick): 135 | d-i grub-installer/bootdev string default 136 | 137 | ### Finishing up the installation 138 | # Avoid that last message about the install being complete. 139 | d-i finish-install/reboot_in_progress note 140 | -------------------------------------------------------------------------------- /cluster/apps/observability/kube-prometheus-stack/helm-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: kube-prometheus-stack 6 | namespace: observability 7 | spec: 8 | interval: 5m 9 | chart: 10 | spec: 11 | # renovate: registryUrl=https://prometheus-community.github.io/helm-charts 12 | chart: kube-prometheus-stack 13 | version: 21.0.3 14 | sourceRef: 15 | kind: HelmRepository 16 | name: prometheus-community-charts 17 | namespace: flux-system 18 | interval: 5m 19 | values: 20 | fullnameOverride: prometheus 21 | alertmanager: 22 | config: 23 | global: 24 | slack_api_url: "${SECRET_ALERT_MANAGER_SLACK_WEBHOOK}" 25 | resolve_timeout: 5m 26 | receivers: 27 | - name: "null" 28 | - name: "slack" 29 | slack_configs: 30 | - channel: "#prometheus" 31 | icon_url: https://avatars3.githubusercontent.com/u/3380462 32 | username: "Prometheus" 33 | send_resolved: true 34 | title: |- 35 | [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} 36 | {{- if gt (len .CommonLabels) (len .GroupLabels) -}} 37 | {{" "}}( 38 | {{- with .CommonLabels.Remove .GroupLabels.Names }} 39 | {{- range $index, $label := .SortedPairs -}} 40 | {{ if $index }}, {{ end }} 41 | {{- $label.Name }}="{{ $label.Value -}}" 42 | {{- end }} 43 | {{- end -}} 44 | ) 45 | {{- end }} 46 | text: >- 47 | {{ with index .Alerts 0 -}} 48 | :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>* 49 | {{- if .Annotations.runbook }} :notebook: *<{{ .Annotations.runbook }}|Runbook>*{{ end }} 50 | {{ end }} 51 | 52 | *Alert details*: 53 | 54 | {{ range .Alerts -}} 55 | *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} 56 | *Description:* {{ .Annotations.description }} 57 | *Details:* 58 | {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` 59 | {{ end }} 60 | {{ end }} 61 | route: 62 | group_by: ["alertname", "job"] 63 | group_wait: 30s 64 | group_interval: 5m 65 | repeat_interval: 6h 66 | routes: 67 | - receiver: "null" 68 | match: 69 | alertname: Watchdog 70 | - receiver: "slack" 71 | continue: true 72 | ingress: 73 | enabled: true 74 | pathType: Prefix 75 | ingressClassName: "traefik" 76 | annotations: 77 | cert-manager.io/cluster-issuer: "letsencrypt-production" 78 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 79 | traefik.ingress.kubernetes.io/router.middlewares: "networking-basic-auth@kubernetescrd" 80 | hajimari.io/enable: "true" 81 | hajimari.io/icon: "alert-circle" 82 | hajimari.io/appName: "alert-manager" 83 | hosts: 84 | - "alert-manager.${SECRET_DOMAIN}" 85 | tls: 86 | - hosts: 87 | - "alert-manager.${SECRET_DOMAIN}" 88 | secretName: "alert-manager-${SECRET_DOMAIN//./-}-tls" 89 | alertmanagerSpec: 90 | storage: 91 | volumeClaimTemplate: 92 | spec: 93 | storageClassName: "nfs-client" 94 | resources: 95 | requests: 96 | storage: 10Gi 97 | nodeExporter: 98 | serviceMonitor: 99 | relabelings: 100 | - action: replace 101 | regex: (.*) 102 | replacement: $1 103 | sourceLabels: 104 | - __meta_kubernetes_pod_node_name 105 | targetLabel: kubernetes_node 106 | kube-state-metrics: 107 | fullnameOverride: kube-state-metrics 108 | grafana: 109 | enabled: false 110 | forceDeployDashboards: true 111 | kubelet: 112 | enabled: true 113 | serviceMonitor: 114 | metricRelabelings: 115 | - action: replace 116 | sourceLabels: 117 | - node 118 | targetLabel: instance 119 | kubeApiServer: 120 | enabled: true 121 | kubeControllerManager: 122 | enabled: true 123 | endpoints: 124 | - 192.168.1.10 125 | kubeScheduler: 126 | enabled: true 127 | endpoints: 128 | - 192.168.1.10 129 | kubeProxy: 130 | enabled: true 131 | endpoints: 132 | - 192.168.1.10 133 | kubeEtcd: 134 | enabled: false 135 | prometheus: 136 | ingress: 137 | enabled: true 138 | pathType: Prefix 139 | ingressClassName: "traefik" 140 | annotations: 141 | cert-manager.io/cluster-issuer: "letsencrypt-production" 142 | traefik.ingress.kubernetes.io/router.entrypoints: "websecure" 143 | traefik.ingress.kubernetes.io/router.middlewares: "networking-basic-auth@kubernetescrd" 144 | hajimari.io/enable: "true" 145 | hajimari.io/icon: "database-arrow-up" 146 | hajimari.io/appName: "prometheus" 147 | hosts: 148 | - "prometheus.${SECRET_DOMAIN}" 149 | tls: 150 | - hosts: 151 | - "prometheus.${SECRET_DOMAIN}" 152 | secretName: "prometheus-${SECRET_DOMAIN//./-}-tls" 153 | prometheusSpec: 154 | ruleSelectorNilUsesHelmValues: false 155 | serviceMonitorSelectorNilUsesHelmValues: false 156 | podMonitorSelectorNilUsesHelmValues: false 157 | probeSelectorNilUsesHelmValues: false 158 | retention: 14d 159 | walCompression: true 160 | storageSpec: 161 | volumeClaimTemplate: 162 | spec: 163 | storageClassName: "nfs-client" 164 | resources: 165 | requests: 166 | storage: 50Gi 167 | additionalScrapeConfigs: 168 | - job_name: "coredns" 169 | honor_timestamps: true 170 | static_configs: 171 | - targets: 172 | - "${COREDNS_ADDR}:9153" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Home Cluster 2 | 3 | My homelab Kubernetes cluster in declarative state. [Flux](https://github.com/fluxcd/flux2) automatically deploys any changes to the `/cluster` directory. 4 | 5 | ## Overview 6 | 7 | - [Architecture](#architecture) 8 | - [Installation](#installation) 9 | - [Thanks](#thanks) 10 | 11 | ## Architecture 12 | 13 | The cluster runs on a single [k3s](https://github.com/k3s-io/k3s) node running in a [k3os](https://github.com/rancher/k3os) VM 14 | on a single [Proxmox](https://pve.proxmox.com/) host. I'm currently running the entire cluster on my previous desktop machine. 15 | 16 | ### Virtual Hosts 17 | 18 | - DNS server (LXC) 19 | - k3s (VM) 20 | - NFS fileserver (VM) 21 | 22 | ### Hardware 23 | 24 | - 4x12TB HDD 25 | - 32GB DDR4 2400MHz RAM 26 | - Intel i7 6700k 27 | - Nvidia 1070 28 | - HUSBZB-1 Zigbee + Z-Wave Adapter 29 | - Arduino Uno + WS2812B LED strip 30 | 31 | ### Networking 32 | 33 | 1. This cluster uses a custom Traefik ingress controller to configure hostnames using a subdomain of my registered tld. 34 | 1. Traefik uses cert-manager with LetsEncrypt and Cloudflare ACME DNS solver to provide certificates for all my services without exposing any ports on my router to the public internet. 35 | 1. The coreDNS server provides DNS for my home network. Using the k8s_gateway plugin it can query the cluster's ingresses. 36 | 37 | #### Repository Structure 38 | 39 | The Git repository contains the following directories under `cluster` and are ordered below by how Flux will apply them. 40 | 41 | - **base** directory is the entrypoint to Flux 42 | - **crds** directory contains custom resource definitions (CRDs) that need to exist globally in your cluster before anything else exists 43 | - **core** directory (depends on **crds**) are important infrastructure applications (grouped by namespace) that should never be pruned by Flux 44 | - **apps** directory (depends on **core**) is where your common applications (grouped by namespace) could be placed, Flux will prune resources here if they are not tracked by Git anymore 45 | 46 | ```text 47 | cluster 48 | ├── apps 49 | │ ├── default 50 | │ └── networking 51 | ├── base 52 | │ └── flux-system 53 | ├── core 54 | │ ├── cert-manager 55 | │ ├── metallb-system 56 | │ └── namespaces 57 | └── crds 58 | └── cert-manager 59 | ``` 60 | 61 | As well as the following directories under `server` which is used to provision the infrastructure on the Proxmox host. 62 | 63 | - **packer** directory contains Packer configs for used to build the custom Proxmox template images 64 | - **terraform** directory contains Terraform templates used to deploy the infrastructure to Proxmox 65 | - **ansible** directory contains Ansible playbooks for configuring the machines once deployed 66 | 67 | ```text 68 | server 69 | ├── ansible 70 | │ ├── inventory 71 | │ ├── playbooks 72 | │ └── requirements.yaml 73 | ├── packer 74 | │ ├── images 75 | │ └── templates 76 | └── terraform 77 | └── proxmox 78 | ``` 79 | 80 | ### Storage 81 | 82 | I provisioned Proxmox using the ZFS filesystem which provides many benefits including redundancy and snapshots. My current RAID configuration (mirror vdevs, roughly RAID 10) offers 1/2 of my total storage capacity. 83 | 84 | Storage for the k3s cluster is provided by the virtualized NFS server. The [NFS Subdirectory External Provisioner Helm chart](https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/) is used to provision persistent volume claims automatically. 85 | 86 | ## Installation 87 | 88 | The below steps will provision the k3s cluster as well as a fileserver and a DNS server. 89 | 90 | ### Tools 91 | 92 | These tools should be installed on the machine you'll be managing the cluster from. 93 | 94 | 95 | 96 | | Tool | Description | 97 | | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | 98 | | [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) | Uses the [Proxmox provisioner](https://registry.terraform.io/providers/Telmate/proxmox/latest/docs) to create VMs and LXC containers | 99 | | [Packer](https://learn.hashicorp.com/tutorials/packer/get-started-install-cli) | Creates custom Proxmox template images | 100 | | [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-and-upgrading-ansible-with-pip) | Performs configuration on machines once the OS is installed | 101 | | [kubectl](https://kubernetes.io/docs/tasks/tools/) | Runs commands against Kubernetes clusters | 102 | | [flux](https://toolkit.fluxcd.io/) | Operator that manages cluster resources based on Git repositories | 103 | | [SOPS](https://github.com/mozilla/sops) | Encrypts Kubernetes secrets with GnuPG | 104 | | [GnuPG](https://gnupg.org/) | Encrypts and signs data | 105 | | [pre-commit](https://github.com/pre-commit/pre-commit) | Runs checks before `git commit` | 106 | | [helm](https://helm.sh/) | Manages Kubernetes applications | 107 | | [prettier](https://github.com/prettier/prettier) | Formats code | 108 | | [go-task](https://taskfile.dev/) | A task runner comparable to GNU make | 109 | 110 | 111 | 112 | ### Prerequisites 113 | 114 | - You must have a server with Proxmox VE installed 115 | - Must use ZFS for storage 116 | - Must have sufficient storage space (I use RAID10 with 4x12tb drives) 117 | - You must have a public/private ssh key-pair generated and added to ssh agent 118 | 119 | ```bash 120 | # Generate key 121 | ssh-keygen -t ed25519 -C "your_email@example.com" 122 | # Add to ssh agent (you may need to enable the agent separately) 123 | ssh-add ~/.ssh/id_ed25519 124 | ``` 125 | 126 | - You must have the SOPS GPG private key imported 127 | - Install the `pre-commit` hooks to ensure linting runs on every commit as well as to ensure unencrypted secrets are not committed 128 | 129 | ```bash 130 | task pre-commit:init 131 | ``` 132 | 133 | - Install Ansible dependencies 134 | 135 | ```bash 136 | task ansible:install 137 | ``` 138 | 139 | ### Provisioning cluster 140 | 141 | 1. Double check the [packer config](./server/packer/variables.auto.pkrvars.hcl), 142 | [terraform config](./server/terraform/proxmox/variables.auto.tfvars), 143 | and [cluster settings](./cluster/base/cluster-settings.yaml), then add your secrets to the secrets files 144 | 145 | ```bash 146 | echo "proxmox_password = \"TERRAFORM ADMIN USER PASSWORD\"" >> server/terraform/proxmox/secrets.auto.tfvars 147 | echo "proxmox_password = \"TERRAFORM ADMIN USER PASSWORD\"" >> server/packer/images/secrets.auto.pkrvars.hcl 148 | echo "domain = \"your-domain.com\"" >> server/packer/images/secrets.auto.pkrvars.hcl 149 | ``` 150 | 151 | 1. Build the template images 152 | 153 | ```bash 154 | task packer:all 155 | ``` 156 | 157 | 1. Check the terraform plan and apply it 158 | 159 | ```bash 160 | task terraform:init 161 | task terraform:plan 162 | task terraform:apply 163 | ``` 164 | 165 | 1. Set your DHCP server to use the new DNS server 166 | 167 | ## Thanks 168 | 169 | This cluster has been heavily inspired by the [k8s@home](https://github.com/k8s-at-home) community. 170 | Thank you to everyone that contributes there as well as to the authors of the open source technologies which operate this cluster. 171 | --------------------------------------------------------------------------------