├── .markdownlintignore
├── .yamllint
├── infrastructure
├── roles
│ ├── ca
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── files
│ │ │ └── req.conf
│ │ ├── vars
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── main.yml
│ │ │ └── directory.yml
│ ├── containers
│ │ ├── storage
│ │ │ ├── vars
│ │ │ │ └── main.yml
│ │ │ └── defaults
│ │ │ │ └── main.yml
│ │ ├── network
│ │ │ ├── flannel
│ │ │ │ ├── files
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── meta
│ │ │ │ │ └── main.yml
│ │ │ │ └── tasks
│ │ │ │ │ └── reset.yml
│ │ │ ├── cilium
│ │ │ │ ├── templates
│ │ │ │ │ ├── cilium
│ │ │ │ │ │ ├── sa.yml.j2
│ │ │ │ │ │ ├── secret.yml.j2
│ │ │ │ │ │ └── crb.yml.j2
│ │ │ │ │ ├── cilium-operator
│ │ │ │ │ │ ├── sa.yml.j2
│ │ │ │ │ │ └── crb.yml.j2
│ │ │ │ │ ├── 000-cilium-portmap.conflist.j2
│ │ │ │ │ └── hubble
│ │ │ │ │ │ └── sa.yml.j2
│ │ │ │ └── tasks
│ │ │ │ │ ├── main.yml
│ │ │ │ │ ├── reset.yml
│ │ │ │ │ └── reset_iface.yml
│ │ │ └── meta
│ │ │ │ └── main.yml
│ │ ├── runtime
│ │ │ ├── files
│ │ │ │ └── mounts.conf
│ │ │ ├── meta
│ │ │ │ └── main.yml
│ │ │ ├── vars
│ │ │ │ └── main.yml
│ │ │ ├── templates
│ │ │ │ ├── crictl.yaml.j2
│ │ │ │ ├── http-proxy.conf.j2
│ │ │ │ ├── config.json.j2
│ │ │ │ ├── unqualified.conf.j2
│ │ │ │ └── registry.conf.j2
│ │ │ ├── handlers
│ │ │ │ └── main.yml
│ │ │ └── tasks
│ │ │ │ └── reset.yml
│ │ └── registry
│ │ │ └── templates
│ │ │ ├── registry-sa.yml.j2
│ │ │ ├── registry-ns.yml.j2
│ │ │ ├── registry-secrets.yml.j2
│ │ │ ├── registry-cm.yml.j2
│ │ │ ├── registry-cr.yml.j2
│ │ │ ├── registry-crb.yml.j2
│ │ │ ├── registry-pvc.yml.j2
│ │ │ ├── registry-ing.yml.j2
│ │ │ └── registry-svc.yml.j2
│ ├── apps
│ │ ├── templates
│ │ │ ├── pvc.yaml.j2
│ │ │ ├── storage.yaml.j2
│ │ │ ├── application.yaml.j2
│ │ │ ├── kustomization.yaml.j2
│ │ │ └── secret-generator.yaml.j2
│ │ ├── meta
│ │ │ ├── main.yaml
│ │ │ └── argument_specs.yaml
│ │ ├── tasks
│ │ │ ├── pre-start
│ │ │ │ ├── keycloak.yml
│ │ │ │ ├── linkace.yml
│ │ │ │ ├── nextcloud.yml
│ │ │ │ ├── linkding.yml
│ │ │ │ ├── mealie.yml
│ │ │ │ ├── wger.yml
│ │ │ │ ├── paperless-ngx.yml
│ │ │ │ ├── photoprism.yml
│ │ │ │ ├── wallabag.yml
│ │ │ │ ├── immich.yml
│ │ │ │ ├── bazarr.yml
│ │ │ │ ├── cert-manager.yml
│ │ │ │ ├── lidarr.yml
│ │ │ │ ├── radarr.yml
│ │ │ │ ├── sonarr.yml
│ │ │ │ ├── prowlarr.yml
│ │ │ │ ├── readarr.yml
│ │ │ │ ├── linkwarden.yml
│ │ │ │ └── homepage.yml
│ │ │ ├── reset.yml
│ │ │ └── post-launch
│ │ │ │ ├── ingress-nginx.yml
│ │ │ │ └── cloudnative-pg.yml
│ │ └── README.md
│ ├── common
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ ├── main.yml
│ │ │ └── os_vars.yml
│ │ └── vars
│ │ │ ├── ubuntu.yml
│ │ │ ├── os.yml
│ │ │ └── debian.yml
│ ├── server
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── kvm
│ │ │ ├── meta
│ │ │ │ └── main.yml
│ │ │ └── templates
│ │ │ │ ├── libvirt_dir_pool.xml.j2
│ │ │ │ └── libvirt_rbd_pool.xml.j2
│ │ ├── nvr
│ │ │ ├── meta
│ │ │ │ └── main.yml
│ │ │ └── tasks
│ │ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── partitions.yml
│ ├── dns
│ │ └── meta
│ │ │ └── main.yml
│ ├── workstation
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── vars
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── terraform.yml
│ │ │ ├── main.yml
│ │ │ └── debian.yml
│ ├── kubernetes
│ │ ├── node
│ │ │ ├── meta
│ │ │ │ └── main.yml
│ │ │ └── tasks
│ │ │ │ ├── debian.yml
│ │ │ │ ├── redhat.yml
│ │ │ │ └── accounts.yml
│ │ ├── control_plane
│ │ │ ├── meta
│ │ │ │ └── main.yml
│ │ │ ├── tasks
│ │ │ │ ├── reset.yml
│ │ │ │ └── approve-cert.yml
│ │ │ ├── templates
│ │ │ │ └── csr.yml.j2
│ │ │ └── defaults
│ │ │ │ └── main
│ │ │ │ └── etcd.yml
│ │ ├── client
│ │ │ └── defaults
│ │ │ │ └── main.yml
│ │ └── kubelet
│ │ │ ├── handlers
│ │ │ └── main.yml
│ │ │ ├── files
│ │ │ ├── kubelet.service
│ │ │ └── 10-kubeadm.conf
│ │ │ └── templates
│ │ │ └── node-kubeconfig.yaml.j2
│ ├── opnsense
│ │ └── meta
│ │ │ └── main.yml
│ ├── download
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── kubeadm-images.yaml.j2
│ │ └── tasks
│ │ │ └── prep_download.yml
│ ├── crypto_device
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── close.yml
│ │ │ └── open.yml
│ ├── dvb
│ │ ├── files
│ │ │ └── environment.conf
│ │ └── tasks
│ │ │ ├── main.yml
│ │ │ └── build.yml
│ └── user
│ │ ├── templates
│ │ ├── crb.yml.j2
│ │ └── kubeconfig.yml.j2
│ │ ├── README.md
│ │ └── meta
│ │ └── argument_specs.yaml
├── inventory
│ ├── host_vars
│ │ ├── parche
│ │ │ ├── config.yml
│ │ │ ├── packages.yml
│ │ │ └── node-labels.yml
│ │ ├── localhost
│ │ │ └── packages.yml
│ │ └── tirante
│ │ │ └── packages.yml
│ ├── inventory.k8s.yaml
│ ├── group_vars
│ │ ├── kubectl
│ │ │ └── 05-container-storage.yml
│ │ ├── workstation
│ │ │ └── packages.yaml
│ │ ├── all
│ │ │ ├── git-repo.yml
│ │ │ ├── pki
│ │ │ │ └── 00-all.yml
│ │ │ └── 00-all.yml
│ │ └── ipaserver
│ │ │ └── config.yml
│ └── libvirt.yaml
├── terraform
│ ├── ipa
│ │ ├── provider.tf
│ │ ├── main.tf
│ │ ├── ipa_network_config
│ │ ├── variables.tf
│ │ ├── ipa.tf
│ │ ├── resources.tf
│ │ └── ipa_cloud_init.tftpl
│ └── kvm
│ │ ├── main.tf
│ │ ├── resources.tf
│ │ └── README.md
└── _shared
│ └── networks.yaml
├── .markdownlint.yaml
├── cluster
├── apps
│ ├── services
│ │ ├── homepage
│ │ │ ├── config
│ │ │ │ ├── docker.yaml
│ │ │ │ ├── bookmarks.yaml
│ │ │ │ ├── kubernetes.yaml
│ │ │ │ ├── settings.yaml
│ │ │ │ └── widgets.yaml
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── wger
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── values.yaml
│ │ ├── linkace
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── linkding
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── linkding.svg
│ │ ├── wallabag
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── linkwarden
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ └── stirling-pdf
│ │ │ └── values.yaml
│ ├── db
│ │ ├── mysql
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── values.yaml
│ │ │ └── storage.yaml
│ │ ├── cloudnative-pg
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── Dockerfile
│ │ │ ├── service.yaml
│ │ │ ├── values.yaml
│ │ │ └── cluster.yaml
│ │ └── redis
│ │ │ └── values.yaml
│ ├── home
│ │ ├── mealie
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── paperless-ngx
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── hp-scan-to-cm.yaml
│ │ │ └── storage.yaml
│ │ ├── mosquitto
│ │ │ ├── mosquitto.conf
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── grocy
│ │ │ ├── svc.yaml
│ │ │ ├── cm.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── README.md
│ │ │ └── storage.yaml
│ │ ├── homebox
│ │ │ └── storage.yaml
│ │ ├── zwave-js-ui
│ │ │ └── storage.yaml
│ │ └── home-assistant
│ │ │ └── storage.yaml
│ ├── media
│ │ ├── immich
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── photoprism
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── calibre
│ │ │ └── storage.yaml
│ │ └── jellyfin
│ │ │ └── storage.yaml
│ ├── downloads
│ │ ├── bazarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── scripts
│ │ │ │ └── post-process.sh
│ │ │ └── storage.yaml
│ │ ├── lidarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── radarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── sonarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ ├── prowlarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ │ └── readarr
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ └── storage.yaml
│ ├── infrastructure
│ │ ├── netbox
│ │ │ ├── secret-generator.yaml
│ │ │ ├── graphite
│ │ │ │ ├── kustomization.yaml
│ │ │ │ ├── service.yaml
│ │ │ │ └── ingress.yaml
│ │ │ └── kustomization.yaml
│ │ ├── keycloak
│ │ │ ├── secret-generator.yaml
│ │ │ ├── keycloak.yaml
│ │ │ └── kustomization.yaml
│ │ ├── cert-manager
│ │ │ ├── cluster-issuer.yaml
│ │ │ ├── secret-generator.yaml
│ │ │ ├── kustomization.yaml
│ │ │ ├── values.yaml
│ │ │ ├── issuer-letsencrypt-prod.yaml
│ │ │ └── issuer-letsencrypt-staging.yaml
│ │ ├── external-dns
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── oauth2-proxy
│ │ │ ├── secret-generator.yaml
│ │ │ └── kustomization.yaml
│ │ ├── kubeshark
│ │ │ └── values.yaml
│ │ ├── traefik
│ │ │ ├── traefik-forward-auth
│ │ │ │ ├── configs
│ │ │ │ │ └── traefik-forward-auth.ini
│ │ │ │ ├── service.yaml
│ │ │ │ ├── middleware.yaml
│ │ │ │ └── kustomization.yaml
│ │ │ ├── tlsstore.yaml
│ │ │ ├── middleware.yaml
│ │ │ ├── ingress-route.yaml
│ │ │ └── values.yaml
│ │ ├── reloader
│ │ │ └── values.yaml
│ │ ├── metallb
│ │ │ ├── layer2.yaml
│ │ │ └── pools.yaml
│ │ ├── cloudflared
│ │ │ └── config.yaml
│ │ ├── rook-ceph
│ │ │ └── README.md
│ │ ├── ingress-nginx
│ │ │ ├── wildcard-certificate.yaml
│ │ │ └── external-wildcard-certificate.yaml
│ │ └── local-path-provisioner
│ │ │ └── storage-classes.yaml
│ └── monitoring
│ │ └── kube-prometheus-stack
│ │ └── storage.yaml
├── kube-system
│ ├── intel-device-plugin
│ │ ├── values.yaml
│ │ └── kustomization.yaml
│ └── node-feature-discovery
│ │ ├── values.yaml
│ │ ├── google-coral-device.yaml
│ │ └── zooz-zwave-device.yaml
├── argocd
│ └── kustomization.yaml
├── bootstrap
│ ├── infrastructure
│ │ ├── keycloak.yaml
│ │ ├── local-path-provisioner.yaml
│ │ ├── reflector.yaml
│ │ ├── oauth2-proxy.yaml
│ │ ├── traefik.yaml
│ │ ├── cert-manager.yaml
│ │ ├── external-dns.yaml
│ │ ├── ingress-nginx.yaml
│ │ ├── netbox.yaml
│ │ └── reloader.yaml
│ ├── argocd.yaml
│ ├── argocd
│ │ └── argocd.yaml
│ ├── services
│ │ ├── wger.yaml
│ │ ├── homepage.yaml
│ │ ├── linkace.yaml
│ │ ├── linkding.yaml
│ │ ├── wallabag.yaml
│ │ ├── stirling-pdf.yaml
│ │ ├── change-detection.yaml
│ │ └── linkwarden.yaml
│ ├── home
│ │ ├── owntracks.yaml
│ │ ├── grocy.yaml
│ │ ├── mosquitto.yaml
│ │ ├── homebox.yaml
│ │ ├── nvr.yaml
│ │ ├── zwave-js-ui.yaml
│ │ ├── home-assistant.yaml
│ │ └── mealie.yaml
│ ├── media
│ │ ├── navidrome.yaml
│ │ ├── immich.yaml
│ │ ├── calibre.yaml
│ │ ├── photoprism.yaml
│ │ └── audiobookshelf.yaml
│ ├── db
│ │ ├── redis.yaml
│ │ ├── cloudnative-pg.yaml
│ │ └── mysql.yaml
│ ├── kube-system
│ │ ├── node-feature-discovery.yaml
│ │ └── intel-device-plugin.yaml
│ └── downloads
│ │ ├── bazarr.yaml
│ │ ├── lidarr.yaml
│ │ ├── radarr.yaml
│ │ ├── sonarr.yaml
│ │ ├── readarr.yaml
│ │ ├── prowlarr.yaml
│ │ └── qbittorrent.yaml
└── cluster.yaml
├── docs
└── Homelab.png
├── .gitconfig
├── .gitleaks.toml
├── scripts
├── kustomize
├── references.sh
└── install_helm.sh
├── .envrc
├── .gitattributes
├── playbooks
├── storage.yml
├── network.yml
├── apps.yml
├── host.yml
└── reset.yml
├── .hooks
└── post-checkout
├── .sops.yaml
├── requirements.txt
├── homelab.yml
├── requirements.yml
├── LICENSE.md
├── .github
├── linters
│ ├── .markdownlint.yaml
│ ├── .yamllint
│ └── .ansible-lint
└── workflows
│ └── linters.yaml
└── TODO.md
/.markdownlintignore:
--------------------------------------------------------------------------------
1 | LICENSE.md
2 |
--------------------------------------------------------------------------------
/.yamllint:
--------------------------------------------------------------------------------
1 | .github/linters/.yamllint
--------------------------------------------------------------------------------
/infrastructure/roles/ca/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | .github/linters/.markdownlint.yaml
--------------------------------------------------------------------------------
/infrastructure/inventory/host_vars/parche/config.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/storage/vars/main.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/config/docker.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/config/bookmarks.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/flannel/files/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/templates/pvc.yaml.j2:
--------------------------------------------------------------------------------
1 | {{ app | pvc}}
2 |
--------------------------------------------------------------------------------
/infrastructure/inventory/inventory.k8s.yaml:
--------------------------------------------------------------------------------
1 | plugin: kubernetes.core.k8s
2 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | allow_duplicates: true
3 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/config/kubernetes.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | mode: cluster
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/docs/Homelab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clearlybaffled/homelab/HEAD/docs/Homelab.png
--------------------------------------------------------------------------------
/infrastructure/roles/apps/meta/main.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | collections:
3 | - "lvrfrc87.git_acp"
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/dns/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - robertdebock.dns
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/kvm/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/nvr/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/workstation/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/.gitconfig:
--------------------------------------------------------------------------------
1 | [merge "ours"]
2 | driver = true
3 | [diff "sopsdiffer"]
4 | textconv = sops -d
5 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/files/mounts.conf:
--------------------------------------------------------------------------------
1 | /usr/share/rhel/secrets:/run/secrets
2 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/node/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/opnsense/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # dependencies:
3 | # - ansibleguy.opnsense
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/flannel/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/control_plane/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: common
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/templates/storage.yaml.j2:
--------------------------------------------------------------------------------
1 | {{ ansible_managed | comment }}
2 | {{ app | storage }}
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/ca/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pki_home: /srv/pki
3 |
4 | root_ca_cn: Hermleigh Home Root CA
5 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: include os var file
3 | include_tasks: 'os_vars.yml'
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/download/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | allow_duplicates: true
3 | dependencies:
4 | - role: common
5 |
--------------------------------------------------------------------------------
/.gitleaks.toml:
--------------------------------------------------------------------------------
1 | [allowlist]
2 | description = "global allow list"
3 | paths = [
4 | '''(.*?)\.sops\.yaml$'''
5 | ]
6 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/templates/application.yaml.j2:
--------------------------------------------------------------------------------
1 | {{ ansible_managed | comment }}
2 | {{ app | application }}
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/templates/kustomization.yaml.j2:
--------------------------------------------------------------------------------
1 | {{ ansible_managed | comment }}
2 | {{ app | kustomization }}
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/workstation/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | hashicorp_repo_key_url: https://apt.releases.hashicorp.com/gpg
3 |
--------------------------------------------------------------------------------
/cluster/kube-system/intel-device-plugin/values.yaml:
--------------------------------------------------------------------------------
1 | name: intel-gpu-plugin
2 | sharedDevNum: 3
3 | nodeFeatureRule: true
4 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/kubectl/05-container-storage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | local_storage_root_dir: /srv/cluster/volumes
3 |
--------------------------------------------------------------------------------
/scripts/kustomize:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | /usr/local/bin/kustomize "$1" --enable-helm --enable-alpha-plugins --enable-exec "$2"
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/vars/main.yml:
--------------------------------------------------------------------------------
1 | crio_graphroot: /containers/storage
2 | crio_runroot: /containers/storage
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/nvr/tasks/main.yml:
--------------------------------------------------------------------------------
1 | - name: nvr | Install coral driver
2 | include_tasks:
3 | file: coral.yml
4 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | export SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt
2 | PATH_add ./scripts
3 | export VIRTUAL_ENV=.venv
4 | layout python3
5 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/workstation/packages.yaml:
--------------------------------------------------------------------------------
1 | debian:
2 | - postgresql
3 | - libpq-dev
4 | - libvirt-clients
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sops.* diff=sopsdiffer
2 | cluster/bootstrap/** merge=ours
3 | infrastructure/inventory/group_vars/all/git-repo.yml merge=ours
4 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/node/tasks/debian.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: debian | Enable fstrim
3 | systemd:
4 | service: fstrim.timer
5 | enabled: true
6 |
--------------------------------------------------------------------------------
/infrastructure/inventory/host_vars/localhost/packages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | local_packages:
3 | - mysql-common
4 | - postgresql-client
5 | - shellcheck
6 | - shfmt
7 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/templates/crictl.yaml.j2:
--------------------------------------------------------------------------------
1 | runtime-endpoint: {{ cri_socket }}
2 | image-endpoint: {{ cri_socket }}
3 | timeout: 30
4 | debug: false
5 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/provider.tf:
--------------------------------------------------------------------------------
1 | provider "libvirt" {
2 | uri = "qemu+ssh://ansible@parche/system?keyfile=$HOME/.ssh/ansible"
3 | }
4 |
5 | provider "sops" {}
6 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/cilium/sa.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: cilium
6 | namespace: kube-system
7 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-sa.yml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: registry
5 | namespace: {{ registry_namespace }}
6 |
--------------------------------------------------------------------------------
/cluster/apps/db/mysql/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: mysql-secret-generator
5 | namespace: db
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/home/mealie/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: mealie-secret-generator
5 | namespace: home
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/media/immich/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: immich-secret-generator
5 | namespace: media
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/wger/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: wger-secret-generator
5 | namespace: services
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/all/git-repo.yml:
--------------------------------------------------------------------------------
1 | ---
2 | git_repo_url: https://github.com/clearlybaffled/homelab
3 | git_branch: main
4 | git_repo_ssh_url: git@github.com:clearlybaffled/homelab.git
5 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/cilium-operator/sa.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: cilium-operator
6 | namespace: kube-system
7 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/bazarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: bazarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/lidarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: lidarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/radarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: radarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/sonarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: sonarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/media/photoprism/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: photoprism-secret-generator
5 | namespace: media
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkace/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: linkace-secret-generator
5 | namespace: services
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart crio
3 | command: /bin/true
4 | changed_when: false
5 | notify:
6 | - Reload systemd
7 | - CRI-O | reload crio
8 |
--------------------------------------------------------------------------------
/playbooks/storage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Generate application manifest files
3 | become: false
4 | hosts: localhost
5 | connection: local
6 | gather_facts: true
7 | roles:
8 | - containers/storage
9 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/prowlarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: prowlarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/readarr/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: readarr-secret-generator
5 | namespace: downloads
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/home/paperless-ngx/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: paperless-ngx-secret-generator
5 | namespace: home
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkding/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: linkding-secret-generator
5 | namespace: services
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/wallabag/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: wallabag-secret-generator
5 | namespace: services
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/crypto_device/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | crypt_name: pki
3 | crypt_mount_path: "{{ ('/srv', crypt_name)| path_join }}"
4 | crypt_mapping_file: "{{ ('/dev','mapper', crypt_name) | path_join }}"
5 |
--------------------------------------------------------------------------------
/playbooks/network.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Test connection
3 | hosts: ios
4 | gather_facts: false
5 | roles:
6 | - role: network
7 | vars:
8 | ansible_become_password: "{{ enable_password }}"
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/netbox/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: netbox-secret-generator
5 | namespace: infrastructure
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/wger/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: services
7 | generators:
8 | - ./secret-generator.yaml
9 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-ns.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 | name: {{ registry_namespace }}
6 | labels:
7 | name: {{ registry_namespace }}
8 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/keycloak/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: keycloak-secret-generator
5 | namespace: infrastructure
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/netbox/graphite/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | namespace: infrastructure
4 | resources:
5 | - ./ingress.yaml
6 | - ./service.yaml
7 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkding/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: services
7 | generators:
8 | - ./secret-generator.yaml
9 |
--------------------------------------------------------------------------------
/cluster/apps/services/wallabag/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: services
7 | generators:
8 | - ./secret-generator.yaml
9 |
--------------------------------------------------------------------------------
/infrastructure/inventory/libvirt.yaml:
--------------------------------------------------------------------------------
1 | # Connect to qemu
2 | plugin: community.libvirt.libvirt
3 | uri: 'qemu+ssh://ansible@parche/system?keyfile=~/.ssh/ansible'
4 | groups:
5 | ipaserver: inventory_hostname.startswith('tang')
6 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/cluster-issuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: ClusterIssuer
3 | metadata:
4 | name: ca-issuer
5 | namespace: cert-manager
6 | spec:
7 | ca:
8 | secretName: ca-key-pair
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/external-dns/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: external-dns-secret-generator
5 | namespace: infrastructure
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/oauth2-proxy/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: oauth2-proxy-secret-generator
5 | namespace: infrastructure
6 | files:
7 | - ./secret.sops.yaml
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/vars/ubuntu.yml:
--------------------------------------------------------------------------------
1 | ---
2 | system_packages:
3 | - ipvsadm
4 | - lm-sensors
5 | - nfs-common
6 | - nvme-cli
7 |
8 | k8s_required_packages:
9 | - python3-kubernetes
10 | - python3-yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/external-dns/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: infrastructure
7 | generators:
8 | - ./secret-generator.yaml
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/kubeshark/values.yaml:
--------------------------------------------------------------------------------
1 | tap:
2 | ingress:
3 | enabled: true
4 | host: &host kubeshark.hermleigh.home
5 | tls:
6 | - hosts:
7 | - *host
8 | secretName: kubeshark-ingress-tls
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/oauth2-proxy/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: infrastructure
7 | generators:
8 | - ./secret-generator.yaml
9 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: homepage-secret-generator
5 | namespace: homepage
6 | files:
7 | - ./secret.sops.yaml
8 | - ./app-keys.sops.yaml
9 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: cloudnative-pg-secret-generator
5 | namespace: db
6 | files:
7 | - ./superuser.sops.yaml
8 | - ./app-user.sops.yaml
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: cert-manager-secret-generator
5 | namespace: cert-manager
6 | files:
7 | - ./cloudflare-api-token.sops.yaml
8 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkwarden/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: linkwarden-secret-generator
5 | namespace: services
6 | files:
7 | - ./secret.sops.yaml
8 | - ./db_url.sops.yaml
9 |
--------------------------------------------------------------------------------
/.hooks/post-checkout:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Need to run `git config --global core.hookspath .hooks` once per host before this works
3 | git config --local include.path ../.gitconfig
4 | # Reference: https://github.com/stefanhoelzl/share-git-hooks-and-config
5 |
--------------------------------------------------------------------------------
/cluster/apps/db/mysql/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: db
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/home/mealie/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: home
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/ca/files/req.conf:
--------------------------------------------------------------------------------
1 | [req]
2 | default_bits = 4096
3 | encrypt_key = yes
4 | default_md = sha512
5 | utf8 = yes
6 | string_mask = utf8only
7 | prompt = no
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Cilium check
3 | import_tasks: check.yml
4 |
5 | - name: Cilium install
6 | include_tasks: install.yml
7 |
8 | - name: Cilium apply
9 | include_tasks: apply.yml
10 |
--------------------------------------------------------------------------------
/cluster/apps/media/immich/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: media
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/media/photoprism/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: media
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/infrastructure/inventory/host_vars/tirante/packages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | host_packages:
3 | - fuse
4 | - ntfs-3g
5 | - nmap
6 | - exuberant-ctags
7 | - nvme-cli
8 | - direnv
9 | - jc
10 | - zerotier-one
11 |
12 | pip_packages:
13 | - pre-commit
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/cilium/secret.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | data:
4 | keys: {{ cilium_ipsec_key }}
5 | kind: Secret
6 | metadata:
7 | name: cilium-ipsec-keys
8 | namespace: kube-system
9 | type: Opaque
10 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/lidarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/radarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/readarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/sonarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/all/pki/00-all.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pki_dir: "{{ (infra_dir, 'inventory','group_vars','all','pki') | path_join }}"
3 |
4 | dn:
5 | country: US
6 | organization_name: HERMLEIGH.HOME
7 | organization_unit_name: Hermleigh House Network
8 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/prowlarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkwarden/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: services
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./storage.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/traefik-forward-auth/configs/traefik-forward-auth.ini:
--------------------------------------------------------------------------------
1 | cookie-name = "_traefik_auth"
2 | log-level = "error"
3 | cookie-domain = "hermleigh.home"
4 | auth-host = "auth-traefik.gato.hermleigh.home"
5 | whitelist = "clearlybaffled120@gmail.com"
6 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/keycloak.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: username
7 | db_pass_var: password
8 | db_names:
9 | - keycloak
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/workstation/tasks/terraform.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install Terraform
3 | package:
4 | name: terraform
5 |
6 | - name: Verify install
7 | command: terraform -help
8 |
9 | - name: Install autocomplete
10 | command: terraform -install-autocomplete
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/linkace.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-mysql.yml
4 | apply:
5 | vars:
6 | db_user_var: DB_USERNAME
7 | db_pass_var: DB_PASSWORD
8 | db_names:
9 | - linkacedb
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/nextcloud.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-mysql.yml
4 | apply:
5 | vars:
6 | db_user_var: db-username
7 | db_pass_var: db-password
8 | db_names:
9 | - nextcloud
10 |
--------------------------------------------------------------------------------
/cluster/apps/home/paperless-ngx/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: home
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./hp-scan-to-cm.yaml
11 | - ./storage.yaml
12 |
--------------------------------------------------------------------------------
/infrastructure/inventory/host_vars/parche/packages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | host_packages:
3 | - fuse
4 | - ntfs-3g
5 | - nmap
6 | - exfat-fuse
7 | - exuberant-ctags
8 | - hdparm
9 | - lm-sensors
10 | - nfs-common
11 | - nvme-cli
12 | - zerotier-one
13 | - firmware-realtek
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/linkding.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: LD_DB_USER
7 | db_pass_var: LD_DB_PASSWORD
8 | db_names:
9 | - linkding
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/mealie.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: POSTGRES_USER
7 | db_pass_var: POSTGRES_PASSWORD
8 | db_names:
9 | - mealie
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/wger.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: DJANGO_DB_USER
7 | db_pass_var: DJANGO_DB_PASSWORD
8 | db_names:
9 | - wger
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/tasks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Reset | check and remove devices if still present
3 | include_tasks: reset_iface.yml
4 | vars:
5 | iface: "{{ item }}"
6 | loop:
7 | - cilium_host
8 | - cilium_net
9 | - cilium_vxlan
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/client/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | kubeconfig_localhost: false
3 | kubeconfig_localhost_ansible_host: false
4 | kubectl_localhost: false
5 | artifacts_dir: "{{ inventory_dir }}/artifacts"
6 |
7 | kube_config_dir: "/etc/kubernetes"
8 | kube_apiserver_port: "6443"
9 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/netbox/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: infrastructure
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./graphite/
11 | - ./storage.yaml
12 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/traefik-forward-auth/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: traefik-forward-auth
5 | spec:
6 | type: ClusterIP
7 | selector:
8 | app: traefik-forward-auth
9 | ports:
10 | - name: auth-http
11 | port: 4181
12 |
--------------------------------------------------------------------------------
/infrastructure/_shared/networks.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | lan: "172.16.1.0/24"
3 | servers: "172.16.1.8/29"
4 | #trusted: "10.1.2.0/24"
5 | guest: "192.168.2.0/24"
6 | iot: "192.168.7.0/24"
7 | video: "10.1.4.0/24"
8 | wg_trusted: "10.0.11.0/24"
9 | services: "10.5.0.0/24"
10 | rescue: "192.168.15.0/24"
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/paperless-ngx.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: PAPERLESS_DBUSER
7 | db_pass_var: PAPERLESS_DBPASS
8 | db_names:
9 | - paperless
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/templates/http-proxy.conf.j2:
--------------------------------------------------------------------------------
1 | [Service]
2 | Environment={% if http_proxy is defined %}"HTTP_PROXY={{ http_proxy }}"{% endif %} {% if https_proxy is defined %}"HTTPS_PROXY={{ https_proxy }}"{% endif %} {% if no_proxy is defined %}"NO_PROXY={{ no_proxy }}"{% endif %}
3 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/templates/secret-generator.yaml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: {{ app.name }}-secret-generator
5 | namespace: {{ app.namespace }}
6 | files:
7 | {% for name in app.secrets.keys() %}
8 | - ./{{ name }}.sops.yaml
9 | {% endfor %}
10 |
--------------------------------------------------------------------------------
/cluster/apps/home/mosquitto/mosquitto.conf:
--------------------------------------------------------------------------------
1 | per_listener_settings false
2 | listener 1883
3 | allow_anonymous false
4 | persistence true
5 | persistence_location /data
6 | autosave_interval 1800
7 | connection_messages false
8 | autosave_interval 60
9 | password_file /mosquitto/external_config/mosquitto_pwd
10 |
--------------------------------------------------------------------------------
/infrastructure/inventory/host_vars/parche/node-labels.yml:
--------------------------------------------------------------------------------
1 | ---
2 | node_labels:
3 | - hermleigh.home/dvb: installed
4 | - hermleigh.home/jbod: "true"
5 | - intel.feature.node.kubernetes.io/gpu: "true"
6 | - zoos.feature.node.kubernetes.io/zwave: "true"
7 | - google.feature.node.kubernetes.io/coral: "true"
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/photoprism.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-mysql.yml
4 | apply:
5 | vars:
6 | db_user_var: PHOTOPRISM_DATABASE_USER
7 | db_pass_var: PHOTOPRISM_DATABASE_PASSWORD
8 | db_names:
9 | - photoprism
10 |
--------------------------------------------------------------------------------
/.sops.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | keys_groups: &default_keys
3 | - age:
4 | - &age age1e764qpphm5nlzp04qf6zcq8f400390d9wmzramq84hqp60k6qyvqsvgg45
5 |
6 | creation_rules:
7 | - path_regex: .*\.sops\.yaml
8 | encrypted_regex: "^(data|stringData)$"
9 | key_groups: *default_keys
10 | - key_groups: *default_keys
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/wallabag.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: SYMFONY__ENV__DATABASE_USER
7 | db_pass_var: SYMFONY__ENV__DATABASE_PASSWORD
8 | db_names:
9 | - wallabag
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/node/tasks/redhat.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: redhat | Node Configuration
3 | notify: Reboot
4 | block:
5 | - name: RedHat| Disable firewalld
6 | systemd:
7 | service: firewalld.service
8 | enabled: false
9 | masked: true
10 | state: stopped
11 |
--------------------------------------------------------------------------------
/cluster/apps/home/mosquitto/secret-generator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: viaduct.ai/v1
2 | kind: ksops
3 | metadata:
4 | name: mosquitto-secret-generator
5 | namespace: home
6 | files:
7 | - ./secret.sops.yaml
8 | - ./frigate-secret.sops.yaml
9 | - ./home-assistant-secret.sops.yaml
10 | - ./owntracks-secret.sops.yaml
11 |
--------------------------------------------------------------------------------
/cluster/apps/home/paperless-ngx/hp-scan-to-cm.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: hp-scan-to-env
5 | namespace: services
6 | data:
7 | IP: 172.16.1.180
8 | PATTERN: '"scan"_dd.mm.yyyy_hh:MM:ss'
9 | PGID: "997"
10 | PUID: "997"
11 | RESOLUTION: "300"
12 | TZ: America/New_York
13 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/kubelet/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Reboot
3 | reboot:
4 | msg: Rebooting nodes
5 | reboot_timeout: 3600
6 |
7 | - name: Node | restart kubelet # noqa no-changed-when
8 | command: /bin/true
9 | notify:
10 | - Reload systemd
11 | - Kubelet | restart kubelet
12 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.5.7"
3 | required_providers {
4 | libvirt = {
5 | source = "dmacvicar/libvirt"
6 | version = "0.7.1"
7 | }
8 | sops = {
9 | source = "carlpett/sops"
10 | version = "1.0.0"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/README.md:
--------------------------------------------------------------------------------
1 | # Apps
2 |
3 | > The part that does the real heavy lifting.
4 |
5 | Once the cluster is up and ready, this role start launching the actual applications.
6 | I think it has become one of my crowning achievements in my jounrey towards peak ansible-ness.
7 |
8 | \[more coming when i feel like it\]
9 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-secrets.yml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: registry-secret
5 | namespace: {{ registry_namespace }}
6 | type: Opaque
7 | data:
8 | {% if registry_htpasswd != "" %}
9 | htpasswd: {{ registry_htpasswd | b64encode }}
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/kvm/templates/libvirt_dir_pool.xml.j2:
--------------------------------------------------------------------------------
1 |
2 | {{ pool_name }}
3 |
4 | {{ storage_dir }}
5 |
6 | 0755
7 | 0
8 | 0
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/tlsstore.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://github.com/datreeio/CRDs-catalog/raw/main/traefik.io/tlsstore_v1alpha1.json
2 | apiVersion: traefik.io/v1alpha1
3 | kind: TLSStore
4 | metadata:
5 | name: default
6 | spec:
7 | defaultCertificate:
8 | secretName: external-wildcard-certificate
9 |
--------------------------------------------------------------------------------
/infrastructure/roles/ca/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ca_home: "{{ (pki_home, ca_name) | path_join }}"
3 |
4 | ca_private_keyfile: "{{ ( ca_home, 'private', ca_name+'.key') | path_join }}"
5 | signing_ca_home: "{{ (pki_home, signing_ca)|path_join }}"
6 | signing_ca_private_keyfile: "{{ ( signing_ca_home, 'private', signing_ca+'.key') | path_join }}"
7 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/ipa_network_config:
--------------------------------------------------------------------------------
1 | version: 1
2 | config:
3 | - type: physical
4 | name: eth0
5 | subnets:
6 | - type: static
7 | address: 172.16.2.2/24
8 | gateway: 172.16.2.1
9 | dns_nameservers:
10 | - 172.16.1.1
11 | dns_search:
12 | - hermleigh.home
13 |
--------------------------------------------------------------------------------
/infrastructure/terraform/kvm/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.5.7"
3 | required_providers {
4 | libvirt = {
5 | source = "dmacvicar/libvirt"
6 | version = "0.7.1"
7 | }
8 | }
9 | }
10 |
11 | provider "libvirt" {
12 | uri = "qemu+ssh://ansible@parche/system?keyfile=$HOME/.ssh/ansible"
13 | }
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-cm.yml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: registry-config
5 | namespace: {{ registry_namespace }}
6 | {% if registry_config %}
7 | data:
8 | config.yml: |-
9 | {{ registry_config | to_yaml(indent=2, width=1337) | indent(width=4) }}
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: cert-manager
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./cluster-issuer.yaml
11 | - ./issuer-letsencrypt-prod.yaml
12 | - ./issuer-letsencrypt-staging.yaml
13 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/reloader/values.yaml:
--------------------------------------------------------------------------------
1 | reloader:
2 | reloadStrategy: annotations
3 | readOnlyRootFilesystem: true
4 | deployment:
5 | resources:
6 | limits:
7 | memory: "512Mi"
8 | requests:
9 | cpu: "10m"
10 | memory: "512Mi"
11 | podMonitor:
12 | enabled: true
13 | namespace: monitoring
14 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkace/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: services
7 | generators:
8 | - ./secret-generator.yaml
9 | configMapGenerator:
10 | - name: nginx-config
11 | files:
12 | - nginx.conf
13 | options:
14 | disableNameSuffixHash: true
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: network/cilium
4 | when: kube_network_plugin == 'cilium' or cilium_deploy_additionally | default(false) | bool
5 | tags:
6 | - cilium
7 |
8 | - role: network/flannel
9 | when: kube_network_plugin == 'flannel'
10 | tags:
11 | - flannel
12 |
--------------------------------------------------------------------------------
/cluster/apps/home/mosquitto/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: home
7 | generators:
8 | - ./secret-generator.yaml
9 | configMapGenerator:
10 | - name: mosquitto-configmap
11 | files:
12 | - ./mosquitto.conf
13 | options:
14 | disableNameSuffixHash: true
15 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/metallb/layer2.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: metallb.io/v1beta1
2 | kind: L2Advertisement
3 | metadata:
4 | name: layer2
5 | namespace: metallb-system
6 | spec:
7 | ipAddressPools:
8 | - ingress-pool
9 | - general-pool
10 | interfaces:
11 | - br0
12 | nodeSelectors:
13 | - matchLabels:
14 | kubernetes.io/hostname: parche
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/dvb/files/environment.conf:
--------------------------------------------------------------------------------
1 | [Service]
2 | # Command line arguments to be passed by systemd to mythbackend on startup.
3 | # This variable must be defined or the service will not start.
4 | Environment="MYTHBACKEND_ARGS=--systemd-journal"
5 |
6 | # Environment variables for the mythbackend program itself
7 | Environment=MYTHCONFDIR=/srv/mythtv/.mythtv
8 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | ansible>=9.0.0
2 | ansible-core>=2.16.0
3 | ansible-lint>=6.14.3
4 | ansible-pylibssh>=1.1.0
5 | kubernetes>=26.1.0
6 | jinja2>=3.1.2
7 | ansible-navigator
8 | netaddr
9 | pywinrm>=0.3.0
10 | cryptography>=1.3
11 | jsonpatch
12 | psycopg2-binary
13 | PyMySQL
14 | mysqlclient
15 | libvirt-python
16 | pre-commit
17 | jc
18 | checkov
19 | httpx
20 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: grocy
5 | namespace: services
6 | labels:
7 | app.kubernetes.io/name: grocy
8 | spec:
9 | selector:
10 | app.kubernetes.io/name: grocy
11 | ports:
12 | - name: grocy-web
13 | port: 80
14 | targetPort: 8080
15 | protocol: TCP
16 | type: ClusterIP
17 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/middleware.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: traefik.containo.us/v1alpha1
2 | kind: Middleware
3 | metadata:
4 | name: forward-auth
5 | spec:
6 | forwardAuth:
7 | address: https://oauth.sso.hermleigh.home
8 | authResponseHeaders:
9 | - Authorization
10 | - Set-Cookie
11 | authResponseHeadersRegex: ^X-
12 | trustForwardHeader: true
13 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/vars/os.yml:
--------------------------------------------------------------------------------
1 | ---
2 | common_packages:
3 | - curl
4 | - git
5 | - ca-certificates
6 | - vim
7 | - git
8 | - wget
9 | - policycoreutils-python-utils
10 | - tree
11 | - jq
12 | - lshw
13 | - htop
14 | - hdparm
15 | - gnupg-agent
16 | - socat
17 | - dos2unix
18 | - smartmontools
19 | - rsync
20 | - unzip
21 | - zstd
22 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/cilium/crb.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRoleBinding
4 | metadata:
5 | name: cilium
6 | roleRef:
7 | apiGroup: rbac.authorization.k8s.io
8 | kind: ClusterRole
9 | name: cilium
10 | subjects:
11 | - kind: ServiceAccount
12 | name: cilium
13 | namespace: kube-system
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/kvm/templates/libvirt_rbd_pool.xml.j2:
--------------------------------------------------------------------------------
1 |
2 | {{ pool_name }}
3 |
4 | rbdpool
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/000-cilium-portmap.conflist.j2:
--------------------------------------------------------------------------------
1 | {
2 | "cniVersion": "0.3.1",
3 | "name": "cilium-portmap",
4 | "plugins": [
5 | {
6 | "type": "cilium-cni"
7 | },
8 | {
9 | "type": "portmap",
10 | "capabilities": { "portMappings": true }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/netbox/graphite/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: graphite
5 | namespace: infrastructure
6 | spec:
7 | selector:
8 | app.kubernetes.io/component: netbox
9 | app.kubernetes.io/instance: netbox
10 | app.kubernetes.io/name: netbox
11 | ports:
12 | - port: 80
13 | targetPort: 80
14 | protocol: TCP
15 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/traefik-forward-auth/middleware.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: traefik.containo.us/v1alpha1
2 | kind: Middleware
3 | metadata:
4 | name: traefik-forward-auth
5 | spec:
6 | forwardAuth:
7 | address: http://traefik-forward-auth.traefik-system.svc.cluster.local:4181
8 | authResponseHeaders:
9 | - X-Forwarded-User
10 | trustForwardHeader: true
11 |
--------------------------------------------------------------------------------
/infrastructure/roles/crypto_device/tasks/close.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Unmount {{ crypt_name }} volume
3 | ansible.posix.mount:
4 | path: "{{ crypt_mount_path }}"
5 | state: unmounted
6 | register: umount
7 |
8 | - name: Close dm-crypt
9 | command:
10 | cmd: "cryptsetup close {{ crypt_name }}"
11 | removes: "{{ crypt_mapping_file }}"
12 | when: umount is success
13 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Shutdown all apps
3 | include_tasks: shutdown.yml
4 | loop: "{{ (apps|items|rejectattr('1.wave','defined') + (apps | items | selectattr('1.wave','in',['3','4']) | sort(attribute='1.wave', reverse=True)) ) }}"
5 | loop_control:
6 | label: "{{ item.0 }}"
7 | vars:
8 | app_name: "{{ item.0 }}"
9 | app: "{{ item.1 }}"
10 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-cr.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: psp:registry
6 | namespace: {{ registry_namespace }}
7 | rules:
8 | - apiGroups:
9 | - policy
10 | resourceNames:
11 | - registry
12 | resources:
13 | - podsecuritypolicies
14 | verbs:
15 | - use
16 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/control_plane/tasks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Check if cluster is still up
3 | kubernetes.core.k8s_cluster_info:
4 | ssl_ca_cert: "{{ kube_cluster_cacerts }}"
5 | register: cluster_status
6 | ignore_errors: true
7 |
8 | - name: kubeadm | Reset cluster
9 | command: "{{ bin_dir }}/kubeadm reset --force"
10 | # when: cluster_status is not failed
11 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: db
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./cluster.yaml
11 | - ./service.yaml
12 | - https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/docs/src/samples/monitoring/prometheusrule.yaml
13 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/traefik-forward-auth/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | commonLabels:
4 | app: traefik-forward-auth
5 |
6 | resources:
7 | - service.yaml
8 | - deployment.yaml
9 | - middleware.yaml
10 |
11 | configMapGenerator:
12 | - name: configs
13 | files:
14 | - configs/traefik-forward-auth.ini
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/post-launch/ingress-nginx.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ingress-nginx | Wait for SSL Certificate Generation Jobs to complete
3 | kubernetes.core.k8s_info:
4 | namespace: infrastructure
5 | kind: Pod
6 | wait: true
7 | wait_timeout: 120
8 | label_selectors:
9 | - app.kubernetes.io/component=controller
10 | wait_condition:
11 | type: Ready
12 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/variables.tf:
--------------------------------------------------------------------------------
1 | data "sops_file" "ansible_ssh_key" {
2 | source_file = "${path.module}/../../inventory/group_vars/all/ansible_user.sops.yml"
3 | }
4 |
5 | data "sops_file" "root_ca_crt" {
6 | source_file = "${path.module}/../../inventory/group_vars/all/pki/root-ca.sops.yml"
7 | }
8 |
9 | output "hosts_created" {
10 | value = [libvirt_domain.ipa-server.name]
11 | }
12 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/kubelet/files/kubelet.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=kubelet: The Kubernetes Node Agent
3 | Documentation=https://kubernetes.io/docs/home/
4 | Wants=network-online.target
5 | After=network-online.target
6 |
7 | [Service]
8 | ExecStart=/usr/local/bin/kubelet
9 | Restart=always
10 | StartLimitInterval=0
11 | RestartSec=10
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/cilium-operator/crb.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRoleBinding
4 | metadata:
5 | name: cilium-operator
6 | roleRef:
7 | apiGroup: rbac.authorization.k8s.io
8 | kind: ClusterRole
9 | name: cilium-operator
10 | subjects:
11 | - kind: ServiceAccount
12 | name: cilium-operator
13 | namespace: kube-system
14 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/bazarr/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: downloads
7 | generators:
8 | - ./secret-generator.yaml
9 | configMapGenerator:
10 | - name: bazarr-scripts
11 | files:
12 | - ./scripts/post-process.sh
13 | generatorOptions:
14 | disableNameSuffixHash: true
15 | resources:
16 | - ./storage.yaml
17 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/immich.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: DB_USERNAME
7 | db_pass_var: DB_PASSWORD
8 | db_names:
9 | - "{{ secret.data.DB_DATABASE_NAME | b64decode }}"
10 | vars:
11 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
12 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-crb.yml.j2:
--------------------------------------------------------------------------------
1 | kind: RoleBinding
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | metadata:
4 | name: psp:registry
5 | namespace: {{ registry_namespace }}
6 | subjects:
7 | - kind: ServiceAccount
8 | name: registry
9 | namespace: {{ registry_namespace }}
10 | roleRef:
11 | kind: ClusterRole
12 | name: psp:registry
13 | apiGroup: rbac.authorization.k8s.io
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/post-launch/cloudnative-pg.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Wait for PostgreSQL cluster to be healthy
3 | kubernetes.core.k8s_info:
4 | kind: Cluster
5 | api_version: postgresql.cnpg.io/v1
6 | namespace: db
7 | name: cnpg-cluster
8 | wait: true
9 | wait_sleep: 60
10 | wait_timeout: 360
11 | wait_condition:
12 | type: Ready
13 | reason: ClusterIsReady
14 | status: 'True'
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/bazarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: POSTGRES_USERNAME
7 | db_pass_var: POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.POSTGRES_DATABASE | b64decode }}"
10 | vars:
11 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
12 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/tasks/reset_iface.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Reset | check if network device {{ iface }} is present"
3 | stat:
4 | path: "/sys/class/net/{{ iface }}"
5 | get_attributes: no
6 | get_checksum: no
7 | get_mime: no
8 | register: device_remains
9 |
10 | - name: "Reset | remove network device {{ iface }}"
11 | command: "ip link del {{ iface }}"
12 | when: device_remains.stat.exists
13 |
--------------------------------------------------------------------------------
/playbooks/apps.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Manage ArgoCD Application(s)
3 | hosts: localhost
4 | gather_facts: true
5 | roles:
6 | - apps
7 | vars:
8 | # see ./infrastructure/roles/apps/meta/argument_specs.yaml for definitions
9 | git_push_force: false
10 | git_push: false
11 | app_no_loop_pause: true
12 | app_tasks: true
13 | batch_apps: true
14 | # app_skip_list:
15 | app_deploy_list:
16 | - intel-device-plugin
17 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/cert-manager.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install Cert-manager certs
3 | kubernetes.core.k8s:
4 | api_version: v1
5 | kind: Secret
6 | name: ca-key-pair
7 | namespace: cert-manager
8 | server_side_apply:
9 | field_manager: ansible
10 | definition:
11 | type: kubernetes.io/tls
12 | data:
13 | tls.crt: "{{ kubernetes_ca.crt }}"
14 | tls.key: "{{ kubernetes_ca.key }}"
15 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/control_plane/templates/csr.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: certificates.k8s.io/v1
3 | kind: CertificateSigningRequest
4 | metadata:
5 | name: {{ name }}
6 | spec:
7 | groups:
8 | - system:authenticated
9 | - system:masters
10 | request: {{ csr.csr | b64encode | replace('\n','') }}
11 | signerName: kubernetes.io/kube-apiserver-client
12 | usages:
13 | - digital signature
14 | - key encipherment
15 | - client auth
16 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/cm.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grocy-cm
5 | namespace: services
6 | labels:
7 | app.kubernetes.io/name: grocy
8 | data:
9 | GENERIC_TIMEZONE: "America/New_York"
10 | TZ: "America/New_York"
11 | PUID: "997"
12 | PGID: "997"
13 | GROCY_CULTURE: en
14 | MAX_UPLOAD: 50M
15 | PHP_MAX_FILE_UPLOAD: "200"
16 | PHP_MAX_POST: 100M
17 | PHP_MEMORY_LIMIT: 512M
18 | GROCY_MODE: production
19 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/templates/config.json.j2:
--------------------------------------------------------------------------------
1 | {% if crio_registry_auth is defined and crio_registry_auth|length %}
2 | {
3 | {% for reg in crio_registry_auth %}
4 | "auths": {
5 | "{{ reg.registry }}": {
6 | "auth": "{{ (reg.username + ':' + reg.password) | string | b64encode }}"
7 | }
8 | {% if not loop.last %}
9 | },
10 | {% else %}
11 | }
12 | {% endif %}
13 | {% endfor %}
14 | }
15 | {% else %}
16 | {}
17 | {% endif %}
18 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/templates/unqualified.conf.j2:
--------------------------------------------------------------------------------
1 | {%- set _unqualified_registries = [] -%}
2 | {% for _registry in crio_registries if _registry.unqualified -%}
3 | {% if _registry.prefix is defined -%}
4 | {{ _unqualified_registries.append(_registry.prefix) }}
5 | {% else %}
6 | {{ _unqualified_registries.append(_registry.location) }}
7 | {%- endif %}
8 | {%- endfor %}
9 |
10 | unqualified-search-registries = {{ _unqualified_registries | string }}
11 |
--------------------------------------------------------------------------------
/homelab.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Host initialization
3 | import_playbook: playbooks/host.yml
4 | tags:
5 | - host
6 |
7 | - name: Kubernetes Cluster initialization
8 | import_playbook: playbooks/cluster.yml
9 | tags:
10 | - cluster
11 |
12 | - name: Cluster Application bootstrap
13 | import_playbook: playbooks/apps.yml
14 | tags:
15 | - apps
16 |
17 | - name: IPA Server startup
18 | import_playbook: playbooks/freeipa.yml
19 | tags:
20 | - ipa
21 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/values.yaml:
--------------------------------------------------------------------------------
1 | installCRDs: false
2 |
3 | extraArgs:
4 | - '--default-issuer-kind=ClusterIssuer'
5 | - '--default-issuer-name=ca-issuer'
6 | - '--dns01-recursive-nameservers-only'
7 | - '--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53'
8 |
9 | prometheus:
10 | enabled: true
11 | servicemonitor:
12 | enabled: true
13 | prometheusInstance: kube-prometheus-stack
14 | labels:
15 | release: kube-prometheus-stack
16 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-pvc.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: PersistentVolumeClaim
4 | metadata:
5 | name: registry-pvc
6 | namespace: {{ registry_namespace }}
7 | labels:
8 | addonmanager.kubernetes.io/mode: Reconcile
9 | spec:
10 | accessModes:
11 | - {{ registry_storage_access_mode }}
12 | storageClassName: {{ registry_storage_class }}
13 | resources:
14 | requests:
15 | storage: {{ registry_disk_size }}
16 |
--------------------------------------------------------------------------------
/scripts/references.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
4 | cd "${scriptDir}/.." || exit 1
5 | base=$(pwd)
6 |
7 | if [ $# -ne 0 ]; then
8 |
9 | for repo in "$@"; do
10 | path=$(dirname $repo)
11 | git clone --single-branch https://github.com/$repo $base/references/$path
12 | done
13 |
14 | else
15 |
16 | for repo in $base/references/*; do
17 | git -C $repo pull --rebase
18 | done
19 |
20 | fi
21 |
--------------------------------------------------------------------------------
/infrastructure/roles/workstation/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: execute os specific tasks
3 | include_tasks: "{{ ansible_os_family|lower }}.yml"
4 |
5 | - name: Install python packages
6 | pip:
7 | name: "{{ pip_packages }}"
8 | when: pip_packages is defined
9 |
10 | - name: Install Mozilla SOPS
11 | include_role:
12 | name: community.sops.install
13 |
14 | - name: Create local ansible user
15 | include_role:
16 | name: user
17 | tasks_from: system-user.yml
18 |
--------------------------------------------------------------------------------
/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | collections:
3 | - name: lvrfrc87.git_acp
4 | type: galaxy
5 | - name: freeipa.ansible_freeipa
6 | type: galaxy
7 | - ansible.posix
8 | - ansible.windows
9 | - community.crypto
10 | - community.general
11 | - community.libvirt
12 | - community.sops
13 | - community.postgresql
14 | - community.mysql
15 | - community.grafana
16 | - kubernetes.core
17 | - cisco.ios
18 | - name: ansibleguy.opnsense
19 | type: galaxy
20 | version: 1.2.9
21 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cloudflared/config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: cloudflared-configmap
5 | namespace: infrastructure
6 | data:
7 | config.yaml: |
8 | ---
9 | tunnel: 8b18a8e8-a189-45f0-a88d-5b38ee7086c4
10 |
11 | ingress:
12 | - hostname: hermleigh.cc
13 | service: &svc https://ingress-nginx-controller.infrastructure.svc
14 | - hostname: "*.hermleigh.cc"
15 | service: *svc
16 | - service: http_status:404
17 |
--------------------------------------------------------------------------------
/playbooks/host.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Workstation Provisioning
3 | hosts: workstations
4 | gather_facts: true
5 | become: true
6 | tags: workstation
7 | roles:
8 | - workstation
9 |
10 | - name: Server Provisioning
11 | hosts: servers
12 | gather_facts: true
13 | any_errors_fatal: true
14 | become: true
15 | tags: server
16 | roles:
17 | - server
18 | - dvb
19 |
20 | - name: KVM setup
21 | hosts: hypervisor
22 | become: true
23 | tags: kvm
24 | roles:
25 | - server/kvm
26 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: home
7 | configMapGenerator:
8 | - name: grocy-config
9 | files:
10 | - ./config.php
11 | options:
12 | disableNameSuffixHash: true
13 | labels:
14 | app.kubernetes.io/name: grocy
15 | app.kubernetes.io/instance: grocy
16 | resources:
17 | - ./cm.yaml
18 | - ./deployment.yaml
19 | - ./ingress.yaml
20 | - ./storage.yaml
21 | - ./svc.yaml
22 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/metallb/pools.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: metallb.io/v1beta1
3 | kind: IPAddressPool
4 | metadata:
5 | namespace: metallb-system
6 | name: general-pool
7 | spec:
8 | addresses:
9 | - 172.16.1.32/27
10 | autoAssign: true
11 | avoidBuggyIPs: true
12 | ---
13 | apiVersion: metallb.io/v1beta1
14 | kind: IPAddressPool
15 | metadata:
16 | namespace: metallb-system
17 | name: ingress-pool
18 | spec:
19 | addresses:
20 | - 172.16.1.24/31
21 | autoAssign: true
22 | avoidBuggyIPs: true
23 |
--------------------------------------------------------------------------------
/infrastructure/roles/user/templates/crb.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRoleBinding
4 | metadata:
5 | annotations:
6 | rbac.authorization.kubernetes.io/autoupdate: "true"
7 | labels:
8 | kubernetes.io/bootstrapping: rbac-defaults
9 | name: {{ kube_user }}
10 | roleRef:
11 | apiGroup: rbac.authorization.k8s.io
12 | kind: ClusterRole
13 | name: cluster-admin
14 | subjects:
15 | - apiGroup: rbac.authorization.k8s.io
16 | kind: Group
17 | name: service:masters
18 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/lidarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: LIDARR__POSTGRES_USER
7 | db_pass_var: LIDARR__POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.LIDARR__POSTGRES_MAIN_DB | b64decode }}"
10 | - "{{ secret.data.LIDARR__POSTGRES_LOG_DB | b64decode }}"
11 | vars:
12 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
13 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/radarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: RADARR__POSTGRES_USER
7 | db_pass_var: RADARR__POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.RADARR__POSTGRES_MAIN_DB | b64decode }}"
10 | - "{{ secret.data.RADARR__POSTGRES_LOG_DB | b64decode }}"
11 | vars:
12 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
13 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/sonarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: SONARR__POSTGRES_USER
7 | db_pass_var: SONARR__POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.SONARR__POSTGRES_MAIN_DB | b64decode }}"
10 | - "{{ secret.data.SONARR__POSTGRES_LOG_DB | b64decode }}"
11 | vars:
12 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
13 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile-upstream:master
2 | ARG CNPG_TAG
3 |
4 | FROM ghcr.io/cloudnative-pg/postgresql:$CNPG_TAG
5 |
6 | ARG CNPG_TAG
7 | ARG PGVECTORS_TAG
8 | ARG TARGETARCH
9 |
10 | # drop to root to install packages
11 | USER root
12 |
13 | # install pgvecto.rs
14 | ADD https://github.com/tensorchord/pgvecto.rs/releases/download/$PGVECTORS_TAG/vectors-pg${CNPG_TAG%.*}_${PGVECTORS_TAG#"v"}_$TARGETARCH.deb ./pgvectors.deb
15 | RUN apt install ./pgvectors.deb
16 |
17 | USER postgres
18 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/prowlarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: PROWLARR__POSTGRES_USER
7 | db_pass_var: PROWLARR__POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.PROWLARR__POSTGRES_MAIN_DB | b64decode }}"
10 | - "{{ secret.data.PROWLARR__POSTGRES_LOG_DB | b64decode }}"
11 | vars:
12 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
13 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2 | Version 2, December 2004
3 |
4 | Copyright (C) 2004 Sam Hocevar
5 |
6 | Everyone is permitted to copy and distribute verbatim or modified
7 | copies of this license document, and changing it is allowed as long
8 | as the name is changed.
9 |
10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12 |
13 | 0. You just DO WHAT THE FUCK YOU WANT TO.
14 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: postgresql-ext
5 | namespace: db
6 | annotations:
7 | metallb.universe.tf/loadBalancerIPs: 172.16.1.33
8 | external-dns.alpha.kubernetes.io/hostname: postgres.hermleigh.home
9 | spec:
10 | selector:
11 | cnpg.io/cluster: cnpg-cluster
12 | role: primary
13 | ports:
14 | - name: postgres
15 | protocol: TCP
16 | port: 5432
17 | targetPort: 5432
18 | type: LoadBalancer
19 | externalTrafficPolicy: Cluster
20 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/readarr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: READARR__POSTGRES_USER
7 | db_pass_var: READARR__POSTGRES_PASSWORD
8 | db_names:
9 | - "{{ secret.data.READARR__POSTGRES_MAIN_DB | b64decode }}"
10 | - "{{ secret.data.READARR__POSTGRES_LOG_DB | b64decode }}"
11 | - readarr
12 | vars:
13 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
14 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/templates/registry.conf.j2:
--------------------------------------------------------------------------------
1 | [[registry]]
2 | prefix = "{{ item.prefix | default(item.location) }}"
3 | insecure = {{ item.insecure | default('false') | string | lower }}
4 | blocked = {{ item.blocked | default('false') | string | lower }}
5 | location = "{{ item.location }}"
6 | {% if item.mirrors is defined %}
7 | {% for mirror in item.mirrors %}
8 |
9 | [[registry.mirror]]
10 | location = "{{ mirror.location }}"
11 | insecure = {{ mirror.insecure | default('false') | string | lower }}
12 | {% endfor %}
13 | {% endif %}
14 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/ipa.tf:
--------------------------------------------------------------------------------
1 | resource "libvirt_domain" "ipa-server" {
2 | name = "tang"
3 | memory = "4096"
4 | vcpu = 2
5 | autostart = true
6 | qemu_agent = true
7 |
8 |
9 | network_interface {
10 | network_name = "virtnet"
11 | }
12 |
13 | disk {
14 | volume_id = libvirt_volume.ipa-root.id
15 | scsi = true
16 | }
17 |
18 | cloudinit = libvirt_cloudinit_disk.ipa-cloud-init.id
19 |
20 | console {
21 | type = "pty"
22 | target_type = "serial"
23 | target_port = "0"
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/infrastructure/terraform/kvm/resources.tf:
--------------------------------------------------------------------------------
1 | resource "libvirt_volume" "fedora38-qcow2" {
2 | name = "fedora38.qcow2"
3 | pool = "cluster"
4 | source = "https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/Fedora-Cloud-Base-38-1.6.x86_64.qcow2"
5 | format = "qcow2"
6 | }
7 |
8 | resource "libvirt_network" "server_network" {
9 | name = "servers"
10 | mode = "bridge"
11 | bridge = "br0"
12 | }
13 |
14 | resource "libvirt_network" "admin_network" {
15 | name = "admin"
16 | mode = "bridge"
17 | bridge = "br1"
18 | }
19 |
--------------------------------------------------------------------------------
/infrastructure/roles/dvb/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install DVB utilities
3 | apt:
4 | pkg:
5 | - v4l-utils
6 | - v4l-conf
7 | - dtv-scan-tables
8 | - dvb-apps
9 | - w-scan
10 | - jmtpfs
11 | - libmtp-runtime
12 | - mtp-tools
13 | - i2c-tools
14 | - xmltv
15 | update_cache: true
16 |
17 |
18 | - name: Create mythtv user
19 | user:
20 | name: mythtv
21 | uid: 990
22 | group: mythtv
23 | home: /srv/mythtv
24 | system: true
25 | groups:
26 | - video
27 | append: true
28 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/rook-ceph/README.md:
--------------------------------------------------------------------------------
1 | # ☠ Rook Ceph Cluster ☠
2 |
3 | Here lies my old rook/ceph cluster. Hyperconverged, single node, single mon, single mgr, single OSD per disk, consumer grade disks. She just wasn't built to last.
4 |
5 | [The death knell](https://github.com/clearlybaffled/homelab/issues/26)
6 |
7 | Maybe, one day, when my lab grows up to have some real servers with real disk drives
8 | lots of cores, tons of RAM, multiple nodes, it'll make sense to bring this back.
9 |
10 | But for the meantime, localPath PV/PVCs sound so much more manageable now.
11 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/netbox/graphite/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: graphite
5 | namespace: infrastructure
6 | labels:
7 | name: graphite
8 | spec:
9 | rules:
10 | - host: &host graphite.hermleigh.cc
11 | http:
12 | paths:
13 | - pathType: Prefix
14 | path: "/"
15 | backend:
16 | service:
17 | name: graphite
18 | port:
19 | number: 80
20 | tls:
21 | - hosts:
22 | - *host
23 | secretName: external-wildcard-certificate
24 |
--------------------------------------------------------------------------------
/cluster/kube-system/node-feature-discovery/values.yaml:
--------------------------------------------------------------------------------
1 | worker:
2 | annotations:
3 | configmap.reloader.stakater.com/reload: node-feature-discovery-worker-conf
4 | config:
5 | core:
6 | sources:
7 | - custom
8 | - pci
9 | - usb
10 | sources:
11 | usb:
12 | deviceClassWhitelist:
13 | - "02"
14 | - "03"
15 | - "0e"
16 | - "ef"
17 | - "fe"
18 | - "ff"
19 | deviceLabelFields:
20 | - class
21 | - vendor
22 | - device
23 | prometheus:
24 | enable: true
25 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/keycloak/keycloak.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: k8s.keycloak.org/v2alpha1
3 | kind: Keycloak
4 | metadata:
5 | name: keycloak
6 | namespace: infrastructure
7 | spec:
8 | instances: 1
9 | db:
10 | vendor: postgres
11 | host: cnpg-cluster-rw.db.svc
12 | usernameSecret:
13 | name: keycloak-db-secret
14 | key: username
15 | passwordSecret:
16 | name: keycloak-db-secret
17 | key: password
18 | http:
19 | tlsSecret: external-wildcard-certificate
20 | hostname:
21 | hostname: keycloak.hermleigh.cc
22 | ingress:
23 | enabled: false
24 |
--------------------------------------------------------------------------------
/infrastructure/roles/server/tasks/partitions.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create Partitions
3 | community.general.lvg:
4 | vg: "{{ inventory_hostname }}-vg"
5 |
6 | - name: Create Logical volumes
7 | community.general.lvol:
8 | vg: "{{ inventory_hostname }}-vg"
9 | lv: "{{ item.name }}"
10 | size: "{{ item.size }}"
11 | active: true
12 | state: present
13 | loop: partitions
14 |
15 | - name: Create Filsystems
16 | community.general.filesystem:
17 | dev: "/dev/mapper/{{ inventory_hostname }}--vg-{{ item.name|regex_sub('-','--') }}"
18 | fstype: "{{ item.fstype }}"
19 | loop: partitions
20 |
--------------------------------------------------------------------------------
/infrastructure/roles/user/templates/kubeconfig.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Config
4 | clusters:
5 | - name: {{ cluster_name }}
6 | cluster:
7 | certificate-authority-data: {{ kubernetes_ca.crt }}
8 | server: {{ kube_apiserver_endpoint }}
9 | users:
10 | - name: {{ kube_user }}
11 | user:
12 | client-certificate-data: {{ certificate }}
13 | client-key-data: {{ private_key.privatekey | string | b64encode | replace('\n','') }}
14 | contexts:
15 | - context:
16 | cluster: {{ cluster_name }}
17 | user: {{ kube_user }}
18 | name: {{ cluster_name }}
19 | current-context: {{ cluster_name }}
20 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/kubelet/templates/node-kubeconfig.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Config
4 | clusters:
5 | - name: local
6 | cluster:
7 | certificate-authority: {{ kube_cert_dir }}/ca.pem
8 | server: {{ kube_apiserver_endpoint }}
9 | users:
10 | - name: kubelet
11 | user:
12 | client-certificate: {{ kube_cert_dir }}/node-{{ inventory_hostname }}.pem
13 | client-key: {{ kube_cert_dir }}/node-{{ inventory_hostname }}-key.pem
14 | contexts:
15 | - context:
16 | cluster: local
17 | user: kubelet
18 | name: kubelet-{{ cluster_name }}
19 | current-context: kubelet-{{ cluster_name }}
20 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: homepage
7 | generators:
8 | - ./secret-generator.yaml
9 | configMapGenerator:
10 | - name: homepage-config
11 | files:
12 | - ./config/bookmarks.yaml
13 | - ./config/docker.yaml
14 | - ./config/kubernetes.yaml
15 | - ./config/services.yaml
16 | - ./config/settings.yaml
17 | - ./config/widgets.yaml
18 | options:
19 | disableNameSuffixHash: true
20 | labels:
21 | app.kubernetes.io/name: homepage
22 | app.kubernetes.io/instance: homepage
23 |
--------------------------------------------------------------------------------
/cluster/kube-system/intel-device-plugin/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: kube-system
7 | helmCharts:
8 | - name: intel-device-plugins-gpu
9 | repo: https://intel.github.io/helm-charts/
10 | version: 0.29.0
11 | namespace: kube-system
12 | releaseName: intel-device-plugins-gpu
13 | valuesFile: values.yaml
14 | patches:
15 | - patch: "- op: remove\n path: /spec/resourceManager\n- op: remove\n path: /metadata/annotations"
16 | target:
17 | version: v1
18 | group: deviceplugin.intel.com
19 | kind: GpuDevicePlugin
20 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/keycloak/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: infrastructure
7 | generators:
8 | - ./secret-generator.yaml
9 | resources:
10 | - ./ingress.yaml
11 | - ./keycloak.yaml
12 | - https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/25.0.2/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml
13 | - https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/25.0.2/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
14 | - https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/25.0.2/kubernetes/kubernetes.yml
15 |
--------------------------------------------------------------------------------
/cluster/apps/db/mysql/values.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/bitnami/charts/main/bitnami/mysql/values.schema.json
2 | auth:
3 | existingSecret: mysql-secret
4 | metrics:
5 | enabled: true
6 | primary:
7 | persistence:
8 | existingClaim: mysql
9 | podSecurityContext:
10 | enabled: true
11 | fsGroup: 997
12 | containerSecurityContext:
13 | enabled: true
14 | runAsUser: 997
15 | runAsNonRoot: true
16 | service:
17 | annotations:
18 | metallb.universe.tf/loadBalancerIPs: 172.16.1.32
19 | external-dns.alpha.kubernetes.io/hostname: mysql.hermleigh.home
20 | type: LoadBalancer
21 |
--------------------------------------------------------------------------------
/cluster/apps/db/redis/values.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/bitnami/charts/main/bitnami/redis/values.schema.json
2 | clusterDomain: seawolf
3 |
4 | architecture: standalone
5 |
6 | auth:
7 | enabled: false
8 |
9 | master:
10 | persistence:
11 | enabled: false
12 | service:
13 | type: LoadBalancer
14 | dnsConfig:
15 | options:
16 | - name: ndots
17 | value: "8"
18 |
19 | metrics:
20 | enabled: true
21 | prometheusRule:
22 | enabled: true
23 | namespace: monitoring
24 | serviceMonitor:
25 | enabled: true
26 | interval: 1m
27 |
28 | useExternalDNS:
29 | enabled: true
30 |
--------------------------------------------------------------------------------
/.github/linters/.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 | # Strict length checking
19 | strict: false
20 | # Stern length checking
21 | stern: false
22 |
23 | # Allow all inline HTML
24 | MD033: false
25 |
26 | # All images without alt-text
27 | MD045: false
28 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | Apps to deploy:
2 | - [ ] Vault
3 | - [x] [node-hp-scan-to](https://github.com/manuc66/node-hp-scan-to) to paperless-ngx pod
4 | - [ ] [wger](https://github.com/wger-project/wger)
5 | - [ ] [Stirling-PDF](https://github.com/Frooodle/Stirling-PDF)
6 | - [ ] Thanos? Elastic?
7 | - [ ] Snipe-IT and/or homebox?
8 | - [ ] changedetecion.io
9 |
10 | Other tasks:
11 | - [ ] preload helm repos on argocd-app-controller using ansible kubectl connection
12 | - [ ] Add [grafana-dashboards-kubernetes](https://github.com/dotdc/grafana-dashboards-kubernetes) to grafana setup
13 | - [ ] CoreDns standalone config (still doing this??)
14 |
15 |
16 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/bazarr/scripts/post-process.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | printf "Cleaning subtitles for '%s' ...\n" "$1"
4 | python3 /add-ons/subcleaner/subcleaner.py "$1" -s
5 |
6 | case $1 in
7 | *movies*) section="1" ;;
8 | *shows*) section="2" ;;
9 | esac
10 |
11 | if [[ -n "$section" ]]; then
12 | printf "Refreshing Plex section '%s' for '%s' ...\n" "$section" "$(dirname "$1")"
13 | /usr/bin/curl -I -X GET -G \
14 | --data-urlencode "path=$(dirname "$1")" \
15 | --data-urlencode "X-Plex-Token=$2" \
16 | --no-progress-meter \
17 | "http://plex.media.svc.cluster.local:32400/library/sections/$section/refresh"
18 | fi
19 |
--------------------------------------------------------------------------------
/cluster/kube-system/node-feature-discovery/google-coral-device.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/nfd.k8s-sigs.io/nodefeaturerule_v1alpha1.json
3 | apiVersion: nfd.k8s-sigs.io/v1alpha1
4 | kind: NodeFeatureRule
5 | metadata:
6 | name: google-coral-device
7 | spec:
8 | rules:
9 | - # Google Coral USB Accelerator
10 | name: google.coral
11 | labels:
12 | google.feature.node.kubernetes.io/coral: "true"
13 | matchFeatures:
14 | - feature: pci.device
15 | matchExpressions:
16 | vendor: { op: In, value: ["1ac1"] }
17 | device: { op: In, value: ["089a"] }
18 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/config/settings.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | title: Dashboard
3 | theme: dark
4 | color: neutral
5 | useEqualHeights: true
6 | layout: # key by group name in services.yaml
7 | Apps & Services:
8 | style: row
9 | columns: 4
10 | tab: Apps
11 | icon: mdi-application
12 | Media:
13 | style: row
14 | columns: 4
15 | tab: Apps
16 | icon: mdi-movie-open
17 | Downloads:
18 | style: row
19 | columns: 4
20 | tab: Apps
21 | icon: mdi-download
22 | Infrastructure:
23 | style: row
24 | columns: 4
25 | tab: Infrastructure
26 | Monitoring:
27 | style: row
28 | columns: 4
29 | tab: Infrastructure
30 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/cilium/templates/hubble/sa.yml.j2:
--------------------------------------------------------------------------------
1 | {% if cilium_hubble_tls_generate %}
2 | ---
3 | # Source: cilium/templates/hubble-generate-certs-serviceaccount.yaml
4 | apiVersion: v1
5 | kind: ServiceAccount
6 | metadata:
7 | name: hubble-generate-certs
8 | namespace: kube-system
9 | {% endif %}
10 | ---
11 | # Source: cilium/templates/hubble-relay-serviceaccount.yaml
12 | apiVersion: v1
13 | kind: ServiceAccount
14 | metadata:
15 | name: hubble-relay
16 | namespace: kube-system
17 | ---
18 | # Source: cilium/templates/hubble-ui-serviceaccount.yaml
19 | apiVersion: v1
20 | kind: ServiceAccount
21 | metadata:
22 | name: hubble-ui
23 | namespace: kube-system
24 |
--------------------------------------------------------------------------------
/cluster/argocd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: kustomize.config.k8s.io/v1beta1
5 | kind: Kustomization
6 | namespace: argocd
7 | helmCharts:
8 | - name: argo-cd
9 | repo: https://argoproj.github.io/argo-helm
10 | version: 8.0.0
11 | namespace: argocd
12 | releaseName: argocd
13 | valuesFile: values.yaml
14 | patches:
15 | - patch: "- op: add\n path: /spec/template/spec/containers/0/env/-\n value: { name:
16 | ARGOCD_SYNC_WAVE_DELAY, value: '30' }"
17 | target:
18 | version: v1
19 | group: apps
20 | kind: StatefulSet
21 | name: argocd-application-controller
22 | labelSelector: app.kubernetes.io/name=argocd-application-controller
23 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/keycloak.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: keycloak
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | source:
14 | repoURL: https://github.com/clearlybaffled/homelab
15 | path: cluster/apps/infrastructure/keycloak
16 | targetRevision: main
17 | destination:
18 | name: in-cluster
19 | namespace: infrastructure
20 | syncPolicy:
21 | automated:
22 | prune: true
23 | selfHeal: true
24 | syncOptions:
25 | - CreateNamespace=true
26 | - ServerSideApply=true
27 |
--------------------------------------------------------------------------------
/cluster/kube-system/node-feature-discovery/zooz-zwave-device.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/nfd.k8s-sigs.io/nodefeaturerule_v1alpha1.json
3 | apiVersion: nfd.k8s-sigs.io/v1alpha1
4 | kind: NodeFeatureRule
5 | metadata:
6 | name: zooz-zwave-device
7 | spec:
8 | rules:
9 | - # Zooz Z-Stick 800
10 | name: zooz.zwave
11 | labels:
12 | zooz.feature.node.kubernetes.io/zwave: "true"
13 | matchFeatures:
14 | - feature: usb.device
15 | matchExpressions:
16 | class: { op: In, value: ["02"] }
17 | vendor: { op: In, value: ["1a86"] }
18 | device: { op: In, value: ["55d4"] }
19 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/tasks/os_vars.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Include common os variables
3 | include_vars: os.yml
4 |
5 | - name: gather os specific variable
6 | include_vars: "{{ item }}"
7 | with_first_found:
8 | - files:
9 | - "{{ ansible_distribution|lower }}-{{ ansible_distribution_version|lower|replace('/', '_') }}.yml"
10 | - "{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}.yml"
11 | - "{{ ansible_distribution|lower }}-{{ ansible_distribution_major_version|lower|replace('/', '_') }}.yml"
12 | - "{{ ansible_distribution|lower }}.yml"
13 | - "{{ ansible_os_family|lower }}.yml"
14 | - defaults.yml
15 | skip: true
16 |
--------------------------------------------------------------------------------
/infrastructure/roles/ca/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Open crypto container
3 | include_role:
4 | name: crypto_device
5 | vars:
6 | cmd: open
7 | when: purpose is defined
8 |
9 | - name: Signed
10 | block:
11 | - include_tasks: # noqa name
12 | file: "{{ task }}.yml"
13 | vars:
14 | task: |-
15 | {%- if purpose is not defined -%}
16 | root-ca
17 | {%- elif purpose == 'sub-ca' -%}
18 | sub-ca
19 | {%- else -%}
20 | sign
21 | {%- endif -%}
22 |
23 | always:
24 | - name: Close crypto container
25 | include_role:
26 | name: crypto_device
27 | vars:
28 | cmd: close
29 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/values.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/cloudnative-pg/charts/main/charts/cloudnative-pg/values.schema.json
2 |
3 | # -- Container Security Context
4 | containerSecurityContext:
5 | allowPrivilegeEscalation: false
6 | readOnlyRootFilesystem: true
7 | runAsUser: 997
8 | runAsGroup: 997
9 | capabilities:
10 | drop:
11 | - "ALL"
12 |
13 | # -- Security Context for the whole pod
14 | podSecurityContext:
15 | runAsNonRoot: true
16 | seccompProfile:
17 | type: RuntimeDefault
18 | fsGroup: 997
19 |
20 | monitoring:
21 | grafanaDashboard:
22 | create: true
23 | namespace: monitoring
24 | podMonitorEnabled: true
25 |
--------------------------------------------------------------------------------
/cluster/bootstrap/argocd.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: argocd
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '0'
13 | spec:
14 | project: default
15 | source:
16 | repoURL: https://github.com/clearlybaffled/homelab
17 | path: cluster/argocd
18 | targetRevision: main
19 | destination:
20 | name: in-cluster
21 | namespace: argocd
22 | syncPolicy:
23 | automated:
24 | prune: true
25 | selfHeal: true
26 | syncOptions:
27 | - CreateNamespace=true
28 | - ServerSideApply=true
29 |
--------------------------------------------------------------------------------
/cluster/bootstrap/argocd/argocd.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: argocd
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '0'
13 | spec:
14 | project: default
15 | source:
16 | repoURL: https://github.com/clearlybaffled/homelab
17 | path: cluster/argocd
18 | targetRevision: main
19 | destination:
20 | name: in-cluster
21 | namespace: argocd
22 | syncPolicy:
23 | automated:
24 | prune: true
25 | selfHeal: true
26 | syncOptions:
27 | - CreateNamespace=true
28 | - ServerSideApply=true
29 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/ingress-nginx/wildcard-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Certificate
3 | metadata:
4 | name: wildcard
5 | namespace: infrastructure
6 | spec:
7 | secretName: wildcard-certificate
8 | issuerRef:
9 | kind: ClusterIssuer
10 | name: ca-issuer
11 | commonName: "Hermleigh House Network Service"
12 | subject:
13 | organizations:
14 | - "HERMLEIGH.HOME"
15 | organizationalUnits:
16 | - "Hermleigh House Network"
17 | countries:
18 | - "US"
19 | dnsNames:
20 | - "seawolf.hermleigh.home"
21 | - "seawolf"
22 | - "*.seawolf.hermleigh.home"
23 | - "*.seawolf"
24 | - "*.hermleigh.home"
25 | ipAddresses:
26 | - "172.16.1.24"
27 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/local-path-provisioner.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: local-path-provisioner
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | source:
14 | repoURL: https://github.com/clearlybaffled/homelab
15 | path: cluster/apps/infrastructure/local-path-provisioner
16 | targetRevision: main
17 | destination:
18 | name: in-cluster
19 | namespace: local-path-provisioner
20 | syncPolicy:
21 | automated:
22 | prune: true
23 | selfHeal: true
24 | syncOptions:
25 | - CreateNamespace=true
26 | - ServerSideApply=true
27 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/local-path-provisioner/storage-classes.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: storage.k8s.io/v1
3 | kind: StorageClass
4 | metadata:
5 | annotations:
6 | storageclass.kubernetes.io/is-default-class: 'False'
7 | name: local-path-videos
8 | provisioner: rancher.io/local-path
9 | parameters:
10 | nodePath: /videos/volumes
11 | volumeBindingMode: WaitForFirstConsumer
12 | reclaimPolicy: Retain
13 | ---
14 | apiVersion: storage.k8s.io/v1
15 | kind: StorageClass
16 | metadata:
17 | annotations:
18 | storageclass.kubernetes.io/is-default-class: 'False'
19 | name: local-path-shared
20 | provisioner: rancher.io/local-path
21 | parameters:
22 | nodePath: /share/volumes
23 | volumeBindingMode: WaitForFirstConsumer
24 | reclaimPolicy: Retain
25 |
--------------------------------------------------------------------------------
/.github/linters/.yamllint:
--------------------------------------------------------------------------------
1 | ---
2 | ignore: |
3 | .ansible/
4 | .direnv/
5 | .private/
6 | .vscode/
7 | *.sops.*
8 |
9 | extends: default
10 |
11 | rules:
12 | truthy:
13 | allowed-values: ["true", "false"]
14 |
15 | comments:
16 | min-spaces-from-content: 1
17 |
18 | comments-indentation: false
19 |
20 | line-length: disable
21 |
22 | braces:
23 | min-spaces-inside: 0
24 | max-spaces-inside: 1
25 |
26 | brackets:
27 | min-spaces-inside: 0
28 | max-spaces-inside: 0
29 |
30 | indentation:
31 | indent-sequences: whatever
32 | spaces: 2
33 |
34 | hyphens:
35 | max-spaces-after: 1
36 |
37 | document-start: disable
38 |
39 | octal-values:
40 | forbid-explicit-octal: true
41 | forbid-implicit-octal: true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/reflector.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: reflector
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '3'
13 | spec:
14 | project: default
15 | source:
16 | chart: reflector
17 | repoURL: https://emberstack.github.io/helm-charts
18 | targetRevision: 9.1.4
19 | helm:
20 | releaseName: reflector
21 | destination:
22 | name: in-cluster
23 | namespace: infrastructure
24 | syncPolicy:
25 | automated:
26 | prune: true
27 | selfHeal: true
28 | syncOptions:
29 | - CreateNamespace=true
30 | - ServerSideApply=true
31 |
--------------------------------------------------------------------------------
/infrastructure/terraform/kvm/README.md:
--------------------------------------------------------------------------------
1 | # KVM
2 |
3 | ## Requirements
4 |
5 | | Name | Version |
6 | |------|---------|
7 | | [terraform](#requirement\_terraform) | >= 1.5.7 |
8 | | [libvirt](#requirement\_libvirt) | 0.7.1 |
9 |
10 | ## Providers
11 |
12 | | Name | Version |
13 | |------|---------|
14 | | [libvirt](#provider\_libvirt) | 0.7.1 |
15 |
16 | ## Modules
17 |
18 | No modules.
19 |
20 | ## Resources
21 |
22 | | Name | Type |
23 | |------|------|
24 | | [libvirt_volume.fedora38-qcow2](https://registry.terraform.io/providers/dmacvicar/libvirt/0.7.1/docs/resources/volume) | resource |
25 |
26 | ## Inputs
27 |
28 | No inputs.
29 |
30 | ## Outputs
31 |
32 | No outputs.
33 |
--------------------------------------------------------------------------------
/infrastructure/roles/common/vars/debian.yml:
--------------------------------------------------------------------------------
1 | ---
2 | system_packages:
3 | - dbus
4 | - gnupg-agent
5 | - software-properties-common
6 | - lm-sensors
7 | - nvme-cli
8 |
9 | k8s_required_packages:
10 | - python3-yaml
11 | - python3-kubernetes
12 | - python3-apt
13 | - age
14 | - apt-transport-https
15 | - software-properties-common
16 | - conntrack
17 | - iptables
18 | - apparmor
19 | - libseccomp2
20 |
21 | kvm_packages:
22 | - "qemu-system{{ (host_architecture == 'amd64') | ternary('-x86', ( (host_architecture == 'arm64') | ternary('-arm', ''))) }}"
23 | - qemu-utils
24 | - libvirt-clients
25 | - libvirt-daemon-system
26 | - libvirt-daemon-system-systemd
27 | - libvirt-daemon-driver-storage-rbd
28 | - libvirt-dev
29 | - virtinst
30 | - bridge-utils
31 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/ingress-nginx/external-wildcard-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Certificate
3 | metadata:
4 | name: external-wildcard
5 | namespace: infrastructure
6 | spec:
7 | secretName: external-wildcard-certificate
8 | issuerRef:
9 | kind: ClusterIssuer
10 | name: letsencrypt-production
11 | commonName: hermleigh.cc
12 | subject:
13 | organizations:
14 | - "HERMLEIGH.CC"
15 | organizationalUnits:
16 | - "Hermleigh House Network"
17 | countries:
18 | - "US"
19 | dnsNames:
20 | - "*.hermleigh.cc"
21 | - "hermleigh.cc"
22 | secretTemplate:
23 | annotations:
24 | reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
25 | reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
26 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/runtime/tasks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # WHY??
3 | # - name: CRI-O | Remove crictl
4 | # file:
5 | # name: "{{ item }}"
6 | # state: absent
7 | # loop:
8 | # - /etc/crictl.yaml
9 | # - "{{ bin_dir }}/crictl"
10 | # tags:
11 | # - reset_crio
12 |
13 | - name: CRI-O | Stop crio service
14 | service:
15 | name: crio
16 | daemon_reload: true
17 | enabled: false
18 | # masked: true
19 | state: stopped
20 | tags:
21 | - reset_crio
22 |
23 | # Also, why??
24 | # - name: CRI-O | Remove CRI-O configuration files
25 | # file:
26 | # name: "{{ item }}"
27 | # state: absent
28 | # loop:
29 | # - /etc/crio
30 | # - /etc/containers
31 | # - /etc/systemd/system/crio.service.d
32 | # tags:
33 | # - reset_crio
34 |
--------------------------------------------------------------------------------
/cluster/apps/services/homepage/config/widgets.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # - logo:
3 | - kubernetes:
4 | cluster:
5 | show: true
6 | cpu: true
7 | memory: true
8 | showLabel: true
9 | label: "seawolf"
10 | nodes:
11 | show: true
12 | cpu: true
13 | memory: true
14 | showLabel: true
15 | - search:
16 | provider: custom
17 | url: https://search.unlocked.link/search?q=
18 | target: _blank
19 | # focus: true # Optional, will set focus to the search bar on page load
20 | - openmeteo:
21 | label: Home # optional
22 | latitude: {{HOMEPAGE_VAR_LATITUDE}}
23 | longitude: {{HOMEPAGE_VAR_LONGITUDE}}
24 | timezone: America/New_York
25 | units: imperial # or metric
26 | cache: 5 # Time in minutes to cache API responses, to stay within limits
27 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/ipaserver/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Reference: https://github.com/freeipa/ansible-freeipa/blob/master/roles/ipaserver/README.md#variables
3 |
4 | # Base
5 | ipaserver_domain: hermleigh.home
6 | ipaserver_realm: HERMLEIGH.HOME
7 | ipaserver_hostname: tang.hermleigh.home
8 |
9 | # Server
10 | ipaserver_no_host_dns: true
11 |
12 | # SSL Certificate
13 |
14 | # Client
15 |
16 | ipaclient_no_ntp: true
17 |
18 | # Certificate System
19 | ipaserver_subject_base: "OU=Hermleigh House Network,O=HERMLEIGH.HOME,C=US"
20 | ipaserver_ca_subject: "CN=IPA Intermediate CA,OU=Hermleigh House Network,O=HERMLEIGH.HOME,C=US"
21 | ipaserver_ca_signing_algorithm: SHA512withRSA
22 |
23 | # DNS
24 |
25 | # AD Trust
26 |
27 | # Special
28 |
29 | ipa_csr_path: "/tmp/{{ groups.ipaserver[0] + '-ipa' }}"
30 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/issuer-letsencrypt-prod.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/cert-manager.io/clusterissuer_v1.json
3 | apiVersion: cert-manager.io/v1
4 | kind: ClusterIssuer
5 | metadata:
6 | name: letsencrypt-production
7 | namespace: cert-manager
8 | spec:
9 | acme:
10 | email: clearlybaffled120@gmail.com
11 | preferredChain: ""
12 | privateKeySecretRef:
13 | name: letsencrypt-production
14 | server: https://acme-v02.api.letsencrypt.org/directory
15 | solvers:
16 | - dns01:
17 | cloudflare:
18 | apiTokenSecretRef:
19 | name: cloudflare-api-token
20 | key: api-token
21 | selector:
22 | dnsZones:
23 | - hermleigh.cc
24 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: grocy
5 | namespace: services
6 | annotations:
7 | gethomepage.dev/enabled: "true"
8 | gethomepage.dev/group: "Apps & Services"
9 | gethomepage.dev/icon: grocy.png
10 | gethomepage.dev/description: "ERP beyond your Fridge"
11 | labels:
12 | app.kubernetes.io/name: grocy
13 | spec:
14 | ingressClassName: nginx
15 | rules:
16 | - host: &host grocy.hermleigh.cc
17 | http:
18 | paths:
19 | - path: /
20 | pathType: Prefix
21 | backend:
22 | service:
23 | name: grocy
24 | port:
25 | number: 8080
26 | tls:
27 | - hosts:
28 | - *host
29 | secretName: external-wildcard-certificate
30 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/cert-manager/issuer-letsencrypt-staging.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/cert-manager.io/clusterissuer_v1.json
3 | apiVersion: cert-manager.io/v1
4 | kind: ClusterIssuer
5 | metadata:
6 | name: letsencrypt-staging
7 | namespace: cert-manager
8 | spec:
9 | acme:
10 | email: clearlybaffled120@gmail.com
11 | preferredChain: ""
12 | privateKeySecretRef:
13 | name: letsencrypt-staging
14 | server: https://acme-staging-v02.api.letsencrypt.org/directory
15 | solvers:
16 | - dns01:
17 | cloudflare:
18 | apiTokenSecretRef:
19 | name: cloudflare-api-token
20 | key: api-token
21 | selector:
22 | dnsZones:
23 | - hermleigh.cc
24 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/ingress-route.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: traefik.io/v1alpha1
2 | kind: IngressRoute
3 | metadata:
4 | name: traefik-dashboard
5 | namespace: traefik-system
6 | annotations:
7 | cert-manager.io/common-name: traefik.seawolf.hermleigh.home
8 | cert-manager.io/private-key-algorithm: ECDSA
9 | cert-manager.io/private-key-size: '384'
10 | cert-manager.io/subject-countries: US
11 | cert-manager.io/subject-organizationalunits: Kubernetes - Services
12 | cert-manager.io/subject-organizations: HERMLEIGH.HOME
13 | spec:
14 | entryPoints:
15 | - websecure
16 | routes:
17 | - match: Host(`traefik.seawolf.hermleigh.home`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
18 | kind: Rule
19 | services:
20 | - name: api@internal
21 | kind: TraefikService
22 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-ing.yml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: registry
5 | namespace: {{ registry_namespace }}
6 | {% if registry_ingress_annotations %}
7 | annotations:
8 | {{ registry_ingress_annotations | to_nice_yaml(indent=2, width=1337) | indent(width=4) }}
9 | {% endif %}
10 | spec:
11 | {% if registry_ingress_tls_secret %}
12 | tls:
13 | - hosts:
14 | - {{ registry_ingress_host }}
15 | secretName: {{ registry_ingress_tls_secret }}
16 | {% endif %}
17 | rules:
18 | - host: {{ registry_ingress_host }}
19 | http:
20 | paths:
21 | - path: /
22 | pathType: Prefix
23 | backend:
24 | service:
25 | name: registry
26 | port:
27 | number: {{ registry_port }}
28 |
--------------------------------------------------------------------------------
/scripts/install_helm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | HELM=/usr/local/bin/helm
4 | JQ=/usr/bin/jq
5 | VALUES=values.yaml
6 | KONFIG=kustomization.yaml
7 | YQ=/usr/local/bin/yq
8 |
9 | # $HELM install --create-namespace -f $VALUES $($YQ -o=json -I=0 '.helmCharts[0]' $KONFIG | $JQ -r --arg name $(basename $PWD) '"\($name) \(.name) --version \(.version) --repo \(.repo) -n \(if .namespace then .namespace else $name end) "')
10 |
11 | if [[ $# -ne 0 ]]; then
12 | pushd $1 &>/dev/null || return
13 | fi
14 |
15 | CMD="$HELM install --create-namespace -f $VALUES "
16 | CMD+=$($YQ -o=json -I=0 '.helmCharts[0]' $KONFIG | $JQ -r --arg name $(basename $PWD) '"\($name) \(.name) --version \(.version) --repo \(.repo) -n \(if .namespace then .namespace else $name end) "')
17 |
18 | echo "Command is: $CMD"
19 |
20 | exec ${CMD}
21 |
22 | popd &>/dev/null || true
23 |
--------------------------------------------------------------------------------
/infrastructure/roles/ca/tasks/directory.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Set up CA directory
3 | file:
4 | path: "{{(ca_home, item)|path_join }}"
5 | state: directory
6 | mode: "{{ (item == 'private') | ternary('0700','0755') }}"
7 | with_items:
8 | - certs
9 | - db
10 | - private
11 |
12 | - name: Create empty index file
13 | copy:
14 | dest: "{{ (ca_home, 'db','index')|path_join }}"
15 | mode: '0644'
16 | content: ''
17 | force: false
18 |
19 | - name: Set initial serial number
20 | copy:
21 | dest: "{{ (ca_home, 'db','serial') | path_join }}"
22 | mode: '0644'
23 | content: "{{ lookup('pipe', 'openssl rand -hex 16') }}"
24 | force: false
25 |
26 | - name: Set initial CRL number
27 | copy:
28 | dest: "{{ (ca_home, 'db', 'crlnumber') | path_join }}"
29 | mode: '0644'
30 | content: '1001'
31 |
--------------------------------------------------------------------------------
/cluster/apps/db/cloudnative-pg/cluster.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cnpg-cluster
5 | namespace: db
6 | spec:
7 | instances: 1
8 |
9 | imageName: localhost:10010/postgresql-vectors:16.2-10
10 |
11 | superuserSecret:
12 | name: cloudnative-pg-superuser
13 | enableSuperuserAccess: true
14 |
15 | bootstrap:
16 | initdb:
17 | database: app
18 | owner: app
19 | secret:
20 | name: cloudnative-pg-app-user
21 | postInitSQL:
22 | - "CREATE EXTENSION cube;"
23 | - "CREATE EXTENSION earthdistance;"
24 | - "CREATE EXTENSION vectors;"
25 |
26 | postgresql:
27 | shared_preload_libraries:
28 | - "vectors.so"
29 |
30 | storage:
31 | size: 50Gi
32 | storageClass: local-path
33 | monitoring:
34 | enablePodMonitor: true
35 |
--------------------------------------------------------------------------------
/cluster/cluster.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: cluster
5 | namespace: argocd
6 | finalizers:
7 | - resources-finalizer.argocd.argoproj.io
8 | spec:
9 | project: default
10 |
11 | source:
12 | repoURL: https://github.com/clearlybaffled/homelab
13 | path: cluster/bootstrap
14 | targetRevision: main
15 | directory:
16 | recurse: true
17 |
18 | destination:
19 | name: in-cluster
20 | namespace: argocd
21 |
22 | syncPolicy:
23 | automated:
24 | prune: true
25 | selfHeal: true
26 | syncOptions:
27 | - CreateNamespace=true
28 | - ServerSideApply=true
29 | - RespectIgnoreDifferences=true
30 | ignoreDifferences:
31 | - group: argoproj.io
32 | kind: Application
33 | jsonPointers:
34 | - /spec/syncPolicy
35 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/resources.tf:
--------------------------------------------------------------------------------
1 | resource "libvirt_cloudinit_disk" "ipa-cloud-init" {
2 | name = "ipa-cloud-init.iso"
3 | pool = "cluster"
4 | user_data = templatefile(
5 | "${path.module}/ipa_cloud_init.tftpl",
6 | {
7 | ssh_pubkey = data.sops_file.ansible_ssh_key.data["ansible_user_ssh_pubkey"]
8 | ssh_key = data.sops_file.ansible_ssh_key.data["ansible_user_ssh_key"]
9 | root_ca_crt = data.sops_file.root_ca_crt.data["root_ca.crt"]
10 | }
11 | )
12 | network_config = file("${path.module}/ipa_network_config")
13 | }
14 |
15 | resource "libvirt_volume" "ipa-root" {
16 | name = "ipa-root.qcow2"
17 | pool = "cluster"
18 | format = "qcow2"
19 | size = "10737418240"
20 | base_volume_name = "fedora38.qcow2"
21 | base_volume_pool = "cluster"
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/infrastructure/roles/download/templates/kubeadm-images.yaml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: kubeadm.k8s.io/v1beta3
2 | kind: InitConfiguration
3 | nodeRegistration:
4 | criSocket: {{ cri_socket }}
5 | ---
6 | apiVersion: kubeadm.k8s.io/v1beta3
7 | kind: ClusterConfiguration
8 | imageRepository: {{ kube_image_repo }}
9 | kubernetesVersion: {{ kube_version }}
10 | etcd:
11 | {% if etcd_deployment_type == "kubeadm" %}
12 | local:
13 | imageRepository: "{{ etcd_image_repo | regex_replace("/etcd$","") }}"
14 | imageTag: "{{ etcd_image_tag }}"
15 | {% else %}
16 | external:
17 | endpoints:
18 | {% for endpoint in etcd_access_addresses.split(',') %}
19 | - {{ endpoint }}
20 | {% endfor %}
21 | {% endif %}
22 | dns:
23 | type: CoreDNS
24 | imageRepository: {{ coredns_image_repo | regex_replace('/coredns(?!/coredns).*$','') }}
25 | imageTag: {{ coredns_image_tag }}
26 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/storage/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | storage:
3 | namespaces:
4 | - services
5 | - media
6 | - downloads
7 | volumes:
8 | - name: files
9 | path: /share/Files
10 | size: &share 5.5Ti
11 | - name: books
12 | path: /share/Books
13 | size: *share
14 | - name: pictures
15 | path: /share/Pictures
16 | size: *share
17 | - name: movies
18 | path: /videos/Movies
19 | size: &videos 7.5Ti
20 | - name: tv-shows
21 | path: "/videos/TV Shows"
22 | size: *videos
23 | - name: audio
24 | path: /share/Audio
25 | size: *share
26 | - name: downloads
27 | path: /share/Downloads
28 | size: *share
29 | - name: music
30 | path: /share/Music
31 | size: *share
32 | - name: data
33 | path: /share
34 | size: *share
35 |
--------------------------------------------------------------------------------
/cluster/apps/infrastructure/traefik/values.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | providers:
3 | kubernetesCRD:
4 | allowCrossNamespace: true
5 | kubernetesIngress:
6 | publishedService:
7 | # To fix argocd stuck in progressing https://github.com/argoproj/argo-cd/issues/968#issuecomment-955976397
8 | enabled: true
9 | # Need to override path since otherwise the namespace is set as default
10 | pathOverride: traefik-system/traefik
11 |
12 | additionalArguments:
13 | - "--api"
14 | - "--api.insecure=true"
15 |
16 | logs:
17 | general:
18 | level: ERROR
19 |
20 | ingressRoute:
21 | dashboard:
22 | enabled: false
23 |
24 | persistence:
25 | enabled: false
26 |
27 | service:
28 | type: LoadBalancer
29 | annotations:
30 | metallb.universe.tf/address-pool: ingress-pool
31 | external-dns.alpha.kubernetes.io/hostname: traefik.seawolf.hermleigh.home
32 | externalTrafficPolicy: Local
33 |
--------------------------------------------------------------------------------
/cluster/apps/services/wger/values.yaml:
--------------------------------------------------------------------------------
1 | app:
2 | django:
3 | existingDatabase:
4 | enabled: true
5 | host: cnpg-cluster-rw.db.svc
6 | existingSecret:
7 | name: wger-secret
8 | dbuserKey: DJANGO_DB_USER
9 | dbpsKey: DJANGO_DB_PASSWORD
10 | environment:
11 | - name: DJANGO_CACHE_LOCATION
12 | value: "redis://redis-master.db.svc:6379/1"
13 |
14 | ingress:
15 | enabled: true
16 | ingressClassName: nginx
17 | url: fit.hermleigh.cc
18 | tls: true
19 | annotations:
20 | gethomepage.dev/enabled: "true"
21 | gethomepage.dev/name: Wger
22 | gethomepage.dev/description: "Personal workout/food tracker"
23 | gethomepage.dev/icon: "wger.svg"
24 | gethomepage.dev/group: "Apps & Services"
25 | cert-manager.io/cluster-issuer: ca-issuer
26 |
27 | redis:
28 | storage:
29 | className: local-path
30 |
31 | postgres:
32 | enabled: false
33 |
--------------------------------------------------------------------------------
/infrastructure/roles/download/tasks/prep_download.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: prep_download | Set a few facts
3 | set_fact:
4 | download_force_cache: "{{ true if download_run_once else download_force_cache }}"
5 | tags:
6 | - facts
7 |
8 | - name: prep_download | Register docker images info
9 | shell: "{{ image_info_command }}" # noqa command-instead-of-shell image_info_command contains pipe therefore requires shell
10 | no_log: "{{ not (unsafe_show_logs|bool) }}"
11 | register: docker_images
12 | failed_when: false
13 | changed_when: false
14 | check_mode: false
15 | when: download_container
16 |
17 | - name: prep_download | Create staging directory on remote node
18 | file:
19 | path: "{{ download_cache_dir }}/images"
20 | state: directory
21 | recurse: true
22 | mode: "0777"
23 | owner: "{{ ansible_ssh_user | default(ansible_user_id) }}"
24 | group: "{{ kube_group }}"
25 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/wger.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: wger
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: wger
15 | repoURL: https://wger-project.github.io/helm-charts
16 | targetRevision: 0.2.4
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/wger/values.yaml
20 | releaseName: wger
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/wger
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/.github/linters/.ansible-lint:
--------------------------------------------------------------------------------
1 | ---
2 | parseable: true
3 | exclude_paths:
4 | - cluster
5 | - '*.yaml'
6 |
7 | skip_list:
8 | # see https://docs.ansible.com/ansible-lint/rules/default_rules.html for a list of all default rules
9 |
10 | # These rules are intentionally skipped:
11 | #
12 | # [E204]: "Lines should be no longer than 160 chars"
13 | - '204'
14 |
15 | # [E701]: "meta/main.yml should contain relevant info"
16 | - '701'
17 |
18 | # [role-name] "meta/main.yml" Role name role-name does not match ``^+$`` pattern
19 | - 'role-name'
20 |
21 | - 'experimental'
22 | # [var-naming] "defaults/main.yml" File defines variable 'apiVersion' that violates variable naming standards
23 | - 'var-naming'
24 | - 'var-spacing'
25 |
26 | - fqcn-builtins
27 |
28 | - name[casing]
29 |
30 | - yaml
31 |
32 | - no-changed-when
33 |
34 | - schema[meta]
35 | - 'risky-file-permissions'
36 | - name[template]
37 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/owntracks.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: owntracks
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/owntracks/values.yaml
20 | releaseName: owntracks
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/home/owntracks
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: home
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/media/navidrome.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: navidrome
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/media/navidrome/values.yaml
20 | releaseName: navidrome
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/media/navidrome
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: media
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/homepage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: homepage
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: homepage
15 | repoURL: http://jameswynn.github.io/helm-charts
16 | targetRevision: 2.0.2
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/homepage/values.yaml
20 | releaseName: homepage
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/homepage
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: homepage
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/linkace.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: linkace
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/linkace/values.yaml
20 | releaseName: linkace
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/linkace
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/linkding.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: linkding
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/linkding/values.yaml
20 | releaseName: linkding
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/linkding
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/wallabag.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: wallabag
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/wallabag/values.yaml
20 | releaseName: wallabag
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/wallabag
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/linkwarden.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: # noqa name
3 | file: ../init-postgres.yml
4 | apply:
5 | vars:
6 | db_user_var: DB_USER
7 | db_pass_var: DB_PASS
8 | db_names:
9 | - linkwarden
10 |
11 | - name: Create secret for db url
12 | community.sops.sops_encrypt:
13 | path: "{{ app.app_dir }}/db_url.sops.yaml"
14 | encrypted_regex: "^(data|stringData)$"
15 | content_yaml:
16 | apiVersion: v1
17 | kind: Secret
18 | type: Opaque
19 | metadata:
20 | name: linkwarden-db-url
21 | namespace: "{{ app.namespace }}"
22 | data:
23 | DATABASE_URL: "{{ ('postgres://' ~ secret.data.DB_USER | b64decode ~ ':' ~ secret.data.DB_PASS | b64decode ~ '@cnpg-cluster-rw.db:5432/linkwarden' ) | b64encode }}"
24 | vars:
25 | secret: "{{ lookup('community.sops.sops', app.app_dir+'/secret.sops.yaml') | from_yaml }}"
26 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/grocy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: grocy
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '4'
13 | spec:
14 | project: default
15 | source:
16 | repoURL: https://github.com/clearlybaffled/homelab
17 | path: cluster/apps/home/grocy
18 | targetRevision: main
19 | destination:
20 | name: in-cluster
21 | namespace: home
22 | ignoreDifferences:
23 | - kind: PersistentVolume
24 | jsonPointers:
25 | - /spec/claimRef/resourceVersion
26 | - /spec/claimRef/uid
27 | - /status/lastPhaseTransitionTime
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 | - RespectIgnoreDifferences=true
36 |
--------------------------------------------------------------------------------
/cluster/bootstrap/db/redis.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: redis
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '0'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: redis
17 | repoURL: https://charts.bitnami.com/bitnami
18 | targetRevision: 18.19.2
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/db/redis/values.yaml
22 | releaseName: redis
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/db/redis
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: db
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/stirling-pdf.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: stirling-pdf
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/stirling-pdf/values.yaml
20 | releaseName: stirling-pdf
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/stirling-pdf
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/control_plane/defaults/main/etcd.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Set etcd user/group
3 | etcd_owner: etcd
4 |
5 | # Note: This does not set up DNS entries. It simply adds the following DNS
6 | # entries to the certificate
7 | etcd_cert_alt_names:
8 | - "etcd.kube-system.svc.{{ dns_domain }}"
9 | - "etcd.kube-system.svc"
10 | - "etcd.kube-system"
11 | - "etcd"
12 | etcd_cert_alt_ips: []
13 |
14 | etcd_heartbeat_interval: "250"
15 | etcd_election_timeout: "5000"
16 |
17 | # etcd_snapshot_count: "10000"
18 |
19 | etcd_metrics: "basic"
20 |
21 | ## A dictionary of extra environment variables to add to etcd.env, formatted like:
22 | ## etcd_extra_vars:
23 | ## var1: "value1"
24 | ## var2: "value2"
25 | ## Note this is different from the etcd role with ETCD_ prfexi, caps, and underscores
26 | etcd_extra_vars: {}
27 |
28 | # etcd_quota_backend_bytes: "2147483648"
29 | # etcd_max_request_bytes: "1572864"
30 |
31 | etcd_compaction_retention: "8"
32 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/node/tasks/accounts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create kube_cert_group
3 | group:
4 | name: "{{ kube_cert_group }}"
5 | system: true
6 |
7 | - name: Create kube group
8 | group:
9 | name: "{{ kube_group }}"
10 | system: true
11 |
12 | - name: Create user
13 | user:
14 | name: "{{ kube_owner }}"
15 | groups:
16 | - "{{ kube_cert_group }}"
17 | - sys
18 | shell: "/sbin/nologin"
19 | create_home: false
20 | system: true
21 | comment: "Kubernetes User"
22 |
23 | - name: Add ansible_user to kube groups
24 | user:
25 | name: "{{ ansible_user }}"
26 | groups:
27 | - "{{ kube_group }}"
28 | - "{{ kube_cert_group }}"
29 | append: true
30 |
31 | - name: Ensure ansible_user has a ~/.kube directory
32 | file:
33 | path: "{{ ansible_homedir }}/.kube"
34 | owner: "{{ ansible_user }}"
35 | group: "{{ ansible_group }}"
36 | state: directory
37 | mode: "0700"
38 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/change-detection.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: change-detection
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/change-detection/values.yaml
20 | releaseName: change-detection
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/change-detection
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/oauth2-proxy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: oauth2-proxy
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: oauth2-proxy
15 | repoURL: https://oauth2-proxy.github.io/manifests
16 | targetRevision: 7.12.13
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/infrastructure/oauth2-proxy/values.yaml
20 | releaseName: oauth2-proxy
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/infrastructure/oauth2-proxy
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: infrastructure
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/kubelet/files/10-kubeadm.conf:
--------------------------------------------------------------------------------
1 | # Note: This dropin only works with kubeadm and kubelet v1.11+
2 | [Service]
3 | Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
4 | Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
5 | # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
6 | EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
7 | # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
8 | # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
9 | EnvironmentFile=-/etc/default/kubelet
10 | ExecStart=
11 | ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
12 |
--------------------------------------------------------------------------------
/cluster/bootstrap/db/cloudnative-pg.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: cloudnative-pg
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '3'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: cloudnative-pg
17 | repoURL: https://cloudnative-pg.github.io/charts
18 | targetRevision: 0.20.2
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/db/cloudnative-pg/values.yaml
22 | releaseName: cloudnative-pg
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/db/cloudnative-pg
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: db
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/traefik.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: traefik
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '1'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: traefik
17 | repoURL: https://traefik.github.io/charts
18 | targetRevision: 35.2.0
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/infrastructure/traefik/values.yaml
22 | releaseName: traefik
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/infrastructure/traefik
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: traefik-system
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/infrastructure/roles/crypto_device/tasks/open.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Open crypto partition
3 | # Reference: https://wiki.archlinux.org/title/Dm-crypt/Device_encryption#Encryption_options_for_plain_mode
4 | command:
5 | cmd: >-
6 | cryptsetup open
7 | --type plain
8 | --size={{ ((crypt.size | human_to_bytes(default_unit='M')) / crypt_device_blocksize|int)|int }}
9 | --offset={{ crypt.offset }}
10 | --key-file={{ crypt_key_file | default(crypt_device_file) }}
11 | --keyfile-offset={{ crypt.key_offset * crypt_device_blocksize|int }}
12 | --keyfile-size={{ ((crypt.key_size | human_to_bytes(default_unit='K')) / crypt_device_blocksize|int)|int }}
13 | {{ crypt_device_file }}
14 | {{ crypt_name }}
15 | creates: "{{ crypt_mapping_file }}"
16 | register: crypt_open
17 |
18 | - name: Mount crypto partition
19 | ansible.posix.mount:
20 | path: "{{ crypt_mount_path }}"
21 | state: ephemeral
22 | fstype: ext4
23 | src: "{{ crypt_mapping_file }}"
24 |
--------------------------------------------------------------------------------
/infrastructure/roles/workstation/tasks/debian.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add HashiCorp apt repository key
3 | block:
4 | - name: Download Hashicorp repo GPG key
5 | get_url:
6 | url: "{{ hashicorp_repo_key_url }}"
7 | dest: /etc/apt/trusted.gpg.d/hashicorp.asc
8 | mode: '0644'
9 | force: true
10 |
11 | - name: HashiCorp | apt source
12 | apt_repository:
13 | repo: "deb [signed-by=/etc/apt/trusted.gpg.d/hashicorp.asc] https://apt.releases.hashicorp.com {{ ansible_lsb.codename }} main"
14 | state: present
15 |
16 | - name: Update apt cache
17 | apt:
18 | update_cache: true
19 |
20 | - name: Load Root CA into system ca trust store
21 | copy:
22 | dest: "/usr/local/share/ca-certificates/{{ domain_name }}-root.crt"
23 | content: "{{ root_ca.crt | b64decode }}"
24 | notify:
25 | - "update ca truststore"
26 |
27 | - name: Ensure a locale exists
28 | community.general.locale_gen:
29 | name: en_US.UTF-8
30 | state: present
31 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/cert-manager.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: cert-manager
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '1'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: cert-manager
17 | repoURL: https://charts.jetstack.io
18 | targetRevision: v1.17.2
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/infrastructure/cert-manager/values.yaml
22 | releaseName: cert-manager
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/infrastructure/cert-manager
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: cert-manager
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/cluster/bootstrap/kube-system/node-feature-discovery.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: node-feature-discovery
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: node-feature-discovery
15 | repoURL: https://kubernetes-sigs.github.io/node-feature-discovery/charts
16 | targetRevision: 0.15.4
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/kube-system/node-feature-discovery/values.yaml
20 | releaseName: node-feature-discovery
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/kube-system/node-feature-discovery
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: kube-system
28 | syncPolicy:
29 | automated:
30 | prune: true
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 | - ServerSideApply=true
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/external-dns.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: external-dns
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '1'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: external-dns
17 | repoURL: https://kubernetes-sigs.github.io/external-dns/
18 | targetRevision: 1.16.1
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/infrastructure/external-dns/values.yaml
22 | releaseName: external-dns
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/infrastructure/external-dns
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: infrastructure
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/ingress-nginx.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: ingress-nginx
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '1'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: ingress-nginx
17 | repoURL: https://kubernetes.github.io/ingress-nginx
18 | targetRevision: 4.12.2
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/infrastructure/ingress-nginx/values.yaml
22 | releaseName: ingress-nginx
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/infrastructure/ingress-nginx
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: infrastructure
30 | syncPolicy:
31 | automated:
32 | prune: true
33 | selfHeal: true
34 | syncOptions:
35 | - CreateNamespace=true
36 | - ServerSideApply=true
37 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | The first app I deployed to the cluster. I decided to try to build this one completely from scratch (i.e. without using [Bern's chart template](https://bjw-s.github.io/helm-charts/docs/app-template/introduction.html))
4 | and used John Hamelinks's k8s-config's for [grocy](https://git.sr.ht/~johnhamelink/k8s-grocy/) and [barcode-buddy](https://git.sr.ht/~johnhamelink/k8s-barcodebuddy).
5 | Currently, I've just configured each manifest file to have 2 YAML documents, one for each grocy and barcode-buddy.
6 | My thoughts are, if I'm feeling enterprising enough, would be to reconfig it to have barcode-buddy run as a sidecar to grocy isntead of a separate pod, and maybe even share underlying volumes.
7 | Grocy was also built and deployed before the [rook-ceph](../../infrastructure/rook-ceph/) storage solution was in place, so it uses local volumes (see [#22](https://github.com/clearlybaffled/homelab/issues/22) and [#23](https://github.com/clearlybaffled/homelab/issues/23)).
8 |
--------------------------------------------------------------------------------
/infrastructure/roles/dvb/tasks/build.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create a work directory
3 | ansible.builtin.tempfile:
4 | state: directory
5 | register: workdir
6 |
7 | - name: Git clone mythtv repo
8 | git:
9 | repo: 'https://github.com/mythtv/mythtv.git'
10 | dest: "{{ workdir.path }}"
11 | version: 'fixes/33'
12 | single_branch: true
13 | register: git_clone
14 |
15 | - name: Configure
16 | command:
17 | argv:
18 | - ./configure
19 | - --disable-firewire
20 | - --disable-hdhomerun
21 | - --disable-vbox
22 | - --disable-ceton
23 | - --disable-satip
24 | - --enable-libxvid
25 | - --enable-libx264
26 | - --enable-libx265
27 | - --enable-libmp3lame
28 | chdir: "{{ git_clone.git_dir_now }}"
29 |
30 | - name: Make
31 | community.general.make:
32 | chdir: "{{ git_clone.git_dir_now }}"
33 | jobs: 5
34 | register: make
35 |
36 | - name: Make Install
37 | community.general.make:
38 | chdir: "{{ git_clone.git_dir_now }}"
39 | target: Install
40 | when: make is not failed
41 |
--------------------------------------------------------------------------------
/infrastructure/roles/user/README.md:
--------------------------------------------------------------------------------
1 |
2 | # USER (infrastructure/roles/user)
3 |
4 | ## ENTRY POINT: kube-user
5 |
6 | ### OPTIONS (= is mandatory)
7 |
8 | ```
9 | = groupname
10 | Name of the local system group
11 | type: str
12 |
13 | = kube-user
14 | Name of the kubernetes user to create
15 | type: str
16 |
17 | = username
18 | Name of the local system user to apply the config to
19 | type: str
20 | ```
21 |
22 | ## ENTRY POINT: system-user
23 |
24 | ### OPTIONS (= is mandatory)
25 |
26 | ```
27 | = ansible_group
28 | ansible usergroup
29 | type: str
30 |
31 | = ansible_homedir
32 | ansible home directory
33 | type: str
34 |
35 | = ansible_user
36 | ansible username
37 | type: str
38 |
39 | - ansible_user_ssh_key
40 | ssh private key for ansible user
41 | default: null
42 | type: str
43 |
44 | - ansible_user_ssh_pubkey
45 | ssh public key for ansible user
46 | default: null
47 | type: str
48 | ```
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/mosquitto.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: mosquitto
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/mosquitto/values.yaml
20 | releaseName: mosquitto
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/home/mosquitto
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: home
28 | ignoreDifferences:
29 | - group: apps
30 | kind: StatefulSet
31 | jsonPointers:
32 | - /spec/volumeClaimTemplates
33 | syncPolicy:
34 | automated:
35 | prune: true
36 | selfHeal: true
37 | syncOptions:
38 | - CreateNamespace=true
39 | - ServerSideApply=true
40 | - RespectIgnoreDifferences=true
41 |
--------------------------------------------------------------------------------
/cluster/apps/db/mysql/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: mysql
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: mysql
17 | namespace: db
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/db/mysql/mysql
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: mysql
38 | namespace: db
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: mysql
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/media/immich.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: immich
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: immich
15 | repoURL: https://immich-app.github.io/immich-charts
16 | targetRevision: 0.7.0
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/media/immich/values.yaml
20 | releaseName: immich
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/media/immich
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: media
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/tasks/pre-start/homepage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Build Secret with credentials needed for widgets
3 | vars:
4 | secrets: {}
5 | block:
6 | - name: Get all secrets
7 | set_fact:
8 | secrets: >-
9 | {{
10 | secrets | combine({
11 | "HOMEPAGE_VAR_" + item|upper + "_API_KEY":
12 | lookup('community.sops.sops', (apps[item].app_dir, 'secret.sops.yaml')|path_join) | from_yaml | json_query('data.'+item|upper+'__API_KEY')
13 | })
14 | }}
15 | with_items:
16 | - radarr
17 | - sonarr
18 | - lidarr
19 | - readarr
20 | - prowlarr
21 |
22 | - name: Write out SOPS file
23 | community.sops.sops_encrypt:
24 | path: "{{ (app.app_dir, 'app-keys.sops.yaml') | path_join }}"
25 | encrypted_regex: "^(data|stringData)$"
26 | content_yaml:
27 | apiVersion: v1
28 | kind: Secret
29 | type: Opaque
30 | metadata:
31 | name: app-keys
32 | namespace: "{{ app.namespace }}"
33 | data:
34 | "{{ secrets }}"
35 |
--------------------------------------------------------------------------------
/cluster/apps/home/grocy/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: grocy
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: grocy
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/home/grocy/grocy
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: grocy
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: grocy
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/homebox.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: homebox
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/homebox/values.yaml
20 | releaseName: homebox
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/home/homebox
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: home
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/nvr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: nvr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: frigate
15 | repoURL: https://blakeblackshear.github.io/blakeshome-charts/
16 | targetRevision: 7.5.1
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/nvr/frigate.yaml
20 | releaseName: frigate
21 | - chart: app-template
22 | repoURL: https://bjw-s-labs.github.io/helm-charts/
23 | targetRevision: 3.7.3
24 | helm:
25 | valueFiles:
26 | - $repo/cluster/apps/home/nvr/wyze-bridge.yaml
27 | releaseName: wyze-bridge
28 | - repoURL: https://github.com/clearlybaffled/homelab
29 | path: cluster/apps/home/nvr
30 | targetRevision: main
31 | ref: repo
32 | destination:
33 | name: in-cluster
34 | namespace: home
35 | syncPolicy:
36 | automated:
37 | prune: true
38 | selfHeal: true
39 | syncOptions:
40 | - CreateNamespace=true
41 | - ServerSideApply=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/media/calibre.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: calibre
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/media/calibre/values.yaml
20 | releaseName: calibre
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/media/calibre
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: media
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/infrastructure/roles/apps/meta/argument_specs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | argument_specs:
3 | main:
4 | options:
5 | app_deploy_list:
6 | type: list
7 | description: A list of applications to deploy instead of the entire list
8 | required: false
9 | default: []
10 | app_skip_list:
11 | type: list
12 | description: A list of applications to not deploy out of the entire list
13 | required: false
14 | default: []
15 | app_start_wave:
16 | type: int
17 | required: false
18 | description: Wave number to start deployment from
19 | app_start_at:
20 | type: str
21 | description: Name of the app to start deploying from
22 | required: false
23 | app_no_loop_pause:
24 | type: bool
25 | required: false
26 | default: false
27 | description: Don't wait for user input before proceeding to next app in deployment
28 | batch_apps:
29 | type: bool
30 | required: false
31 | default: false
32 | description: Wait until deployment is complete to create a git commit instead of once per app.
33 |
--------------------------------------------------------------------------------
/cluster/apps/home/homebox/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: homebox
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: homebox
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153Gi
23 | local:
24 | path: /srv/cluster/volumes/home/homebox/homebox
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: homebox
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153Gi
47 | storageClassName: local-storage
48 | volumeName: homebox
49 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkding/linkding.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/netbox.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: netbox
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: netbox
15 | repoURL: https://charts.boo.tc
16 | targetRevision: 4.1.1
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/infrastructure/netbox/values.yaml
20 | releaseName: netbox
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/infrastructure/netbox
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: infrastructure
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/apps/media/photoprism/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: photoprism
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: photoprism
17 | namespace: media
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 9100.0Gi
23 | local:
24 | path: /disks/sdg/photoprism
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: photoprism
38 | namespace: media
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 9100.0Gi
47 | storageClassName: local-storage
48 | volumeName: photoprism
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/bazarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: bazarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/bazarr/values.yaml
20 | releaseName: bazarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/bazarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/lidarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: lidarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/lidarr/values.yaml
20 | releaseName: lidarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/lidarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/radarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: radarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/radarr/values.yaml
20 | releaseName: radarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/radarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/sonarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: sonarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/sonarr/values.yaml
20 | releaseName: sonarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/sonarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/infrastructure/inventory/group_vars/all/00-all.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ## Directory where the binaries will be installed
3 | bin_dir: /usr/local/bin
4 |
5 | ## Directory convenience variables
6 | root_dir: "{{ ansible_config_file | dirname }}"
7 | cluster_dir: "{{ root_dir }}/cluster"
8 | infra_dir: "{{ root_dir }}/infrastructure"
9 |
10 | ## Ansible User setup
11 | ansible_user: ansible
12 | ansible_group: ansible
13 | ansible_groups:
14 | - sys
15 | - adm
16 | ansible_homedir: /var/local/ansible
17 |
18 | # (string) Timezone for the servers
19 | timezone: "America/New_York"
20 |
21 | _host_architecture_groups:
22 | x86_64: amd64
23 | aarch64: arm64
24 | armv7l: arm
25 | host_architecture: >-
26 | {%- if ansible_architecture in _host_architecture_groups -%}
27 | {{ _host_architecture_groups[ansible_architecture] }}
28 | {%- else -%}
29 | {{ ansible_architecture }}
30 | {%- endif -%}
31 |
32 | _host_os_groups:
33 | Linux: linux
34 | Darwin: darwin
35 | Win32NT: windows
36 | host_os: >-
37 | {%- if ansible_system in _host_os_groups -%}
38 | {{ _host_os_groups[ansible_system] }}
39 | {%- else -%}
40 | {{ ansible_system|lower }}
41 | {%- endif -%}
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/readarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: readarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/readarr/values.yaml
20 | releaseName: readarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/readarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/zwave-js-ui.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: zwave-js-ui
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/zwave-js-ui/values.yaml
20 | releaseName: zwave-js-ui
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/home/zwave-js-ui
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: home
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/media/photoprism.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: photoprism
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/media/photoprism/values.yaml
20 | releaseName: photoprism
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/media/photoprism
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: media
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/prowlarr.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: prowlarr
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/prowlarr/values.yaml
20 | releaseName: prowlarr
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/prowlarr
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/.github/workflows/linters.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Linters
3 |
4 | on:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | ansible-lint:
11 | name: Ansible Lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 | - name: Run ansible-lint
19 | uses: ansible/ansible-lint@main
20 | kubeconform:
21 | name: Kubernetes Lint
22 | runs-on: ubuntu-latest
23 | steps:
24 | - name: login to Github Packages
25 | run: echo "${{ github.token }}" | docker login https://ghcr.io -u ${GITHUB_ACTOR} --password-stdin
26 | - name: Checkout
27 | uses: actions/checkout@v3
28 | with:
29 | fetch-depth: 0
30 | - name: Kubeconform
31 | uses: docker://ghcr.io/yannh/kubeconform:v0.6.3
32 | with:
33 | entrypoint: '/kubeconform'
34 | args: "-summary -output text -schema-location default -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' -ignore-missing-schemas $(grep -lr '^kind: ' cluster)"
35 |
--------------------------------------------------------------------------------
/cluster/bootstrap/services/linkwarden.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: linkwarden
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/services/linkwarden/values.yaml
20 | releaseName: linkwarden
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/services/linkwarden
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: services
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/apps/services/stirling-pdf/values.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/library/common/values.schema.json
2 | controllers:
3 | main:
4 | containers:
5 | stirling-pdf:
6 | image:
7 | repository: docker.io/frooodle/s-pdf
8 | tag: 0.27.0
9 | resources:
10 | requests:
11 | cpu: 10m
12 | memory: 256Mi
13 | limits:
14 | memory: 256Mi
15 | service:
16 | main:
17 | controller: main
18 | ports:
19 | http:
20 | port: 8080
21 | ingress:
22 | main:
23 | enabled: true
24 | className: nginx
25 | annotations:
26 | gethomepage.dev/enabled: "true"
27 | gethomepage.dev/group: "Apps & Services"
28 | gethomepage.dev/name: Stirling PDF
29 | gethomepage.dev/icon: stirling-pdf.png
30 | hosts:
31 | - host: &host pdf.hermleigh.cc
32 | paths:
33 | - path: /
34 | service:
35 | identifier: main
36 | port: http
37 | tls:
38 | - hosts:
39 | - *host
40 | secretName: external-wildcard-certificate
41 |
--------------------------------------------------------------------------------
/cluster/bootstrap/db/mysql.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: mysql
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '3'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: mysql
17 | repoURL: https://charts.bitnami.com/bitnami
18 | targetRevision: 9.14.3
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/db/mysql/values.yaml
22 | releaseName: mysql
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/db/mysql
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: db
30 | ignoreDifferences:
31 | - kind: PersistentVolume
32 | jsonPointers:
33 | - /spec/claimRef/resourceVersion
34 | - /spec/claimRef/uid
35 | - /status/lastPhaseTransitionTime
36 | syncPolicy:
37 | automated:
38 | prune: true
39 | selfHeal: true
40 | syncOptions:
41 | - CreateNamespace=true
42 | - ServerSideApply=true
43 | - RespectIgnoreDifferences=true
44 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/home-assistant.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: home-assistant
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/home/home-assistant/values.yaml
20 | releaseName: home-assistant
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/home/home-assistant
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: home
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/infrastructure/terraform/ipa/ipa_cloud_init.tftpl:
--------------------------------------------------------------------------------
1 | #cloud-config
2 | timezone: America/New_York
3 |
4 | hostname: tang
5 | fqdn: tang.hermleigh.home
6 | prefer_fqdn_over_hostname: true
7 |
8 | ssh_pwauth: false
9 |
10 | runcmd:
11 | - setenforce 0
12 |
13 |
14 | users:
15 | - name: ansible
16 | gecos: Ansible User
17 | groups: users,admin,wheel
18 | sudo: ALL=(ALL) NOPASSWD:ALL
19 | shell: /bin/bash
20 | lock_passwd: false
21 | plain_text_passwd: ansible
22 | homedir: /var/lib/ansible
23 | ssh_authorized_keys:
24 | - "${base64decode(ssh_pubkey)}"
25 |
26 | write_files:
27 | - path: /var/local/ansible/.ssh/id_ed25519
28 | owner: ansible:ansible
29 | permissions: '0o600'
30 | encoding: base64
31 | append: false
32 | defer: true
33 | content: ${ssh_key}
34 |
35 | package_update: true
36 | package_upgrade: true
37 | package_reboot_if_required: true
38 |
39 | packages:
40 | - qemu-guest-agent
41 |
42 | ntp:
43 | enabled: true
44 | config:
45 | service_name: chronyd
46 | servers:
47 | - 172.16.1.1
48 |
49 |
50 | ${yamlencode({
51 | "ca_certs": {
52 | "trusted": [
53 | "${root_ca_crt}"
54 | ]
55 | }
56 | })}
57 |
--------------------------------------------------------------------------------
/cluster/apps/home/paperless-ngx/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: paperless-library
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: paperless-library
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 5.5Ti
23 | local:
24 | path: /share/Files/PaperlessNGX
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: paperless-library
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 5.5Ti
47 | storageClassName: local-storage
48 | volumeName: paperless-library
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/downloads/qbittorrent.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: qbittorrent
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/downloads/qbittorrent/values.yaml
20 | releaseName: qbittorrent
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/downloads/qbittorrent
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: downloads
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/bootstrap/media/audiobookshelf.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: audiobookshelf
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: app-template
15 | repoURL: https://bjw-s-labs.github.io/helm-charts/
16 | targetRevision: 3.7.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/media/audiobookshelf/values.yaml
20 | releaseName: audiobookshelf
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/media/audiobookshelf
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: media
28 | ignoreDifferences:
29 | - kind: PersistentVolume
30 | jsonPointers:
31 | - /spec/claimRef/resourceVersion
32 | - /spec/claimRef/uid
33 | - /status/lastPhaseTransitionTime
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/infrastructure/roles/user/meta/argument_specs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | argument_specs:
3 | kube-user:
4 | options:
5 | kube-user:
6 | type: str
7 | required: true
8 | description: Name of the kubernetes user to create
9 | username:
10 | type: str
11 | required: true
12 | description: Name of the local system user to apply the config to
13 | groupname:
14 | type: str
15 | required: true
16 | description: Name of the local system group
17 | system-user:
18 | options:
19 | ansible_user:
20 | type: str
21 | required: true
22 | description: ansible username
23 | ansible_group:
24 | type: str
25 | required: true
26 | description: ansible usergroup
27 | ansible_homedir:
28 | type: str
29 | required: true
30 | description: ansible home directory
31 | ansible_user_ssh_key:
32 | type: str
33 | required: false
34 | description: ssh private key for ansible user
35 | ansible_user_ssh_pubkey:
36 | type: str
37 | required: false
38 | description: ssh public key for ansible user
39 |
--------------------------------------------------------------------------------
/cluster/apps/media/immich/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: immich-library
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: immich-library
17 | namespace: media
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 916G
23 | local:
24 | path: /containers/volumes/media/immich/immich-library
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: immich-library
38 | namespace: media
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 916G
47 | storageClassName: local-storage
48 | volumeName: immich-library
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/kube-system/intel-device-plugin.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: intel-device-plugin
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: intel-device-plugins-operator
15 | repoURL: https://intel.github.io/helm-charts/
16 | targetRevision: 0.29.0
17 | helm:
18 | releaseName: intel-device-plugins-operator
19 | - chart: app-template
20 | repoURL: https://bjw-s-labs.github.io/helm-charts/
21 | targetRevision: 3.7.3
22 | helm:
23 | valueFiles:
24 | - $repo/cluster/kube-system/intel-device-plugin/exporter.yaml
25 | releaseName: intel-gpu-exporter
26 | - repoURL: https://github.com/clearlybaffled/homelab
27 | path: cluster/kube-system/intel-device-plugin
28 | targetRevision: main
29 | ref: repo
30 | destination:
31 | name: in-cluster
32 | namespace: kube-system
33 | syncPolicy:
34 | automated:
35 | prune: true
36 | selfHeal: true
37 | syncOptions:
38 | - CreateNamespace=true
39 | - ServerSideApply=true
40 |
--------------------------------------------------------------------------------
/cluster/apps/services/linkwarden/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: linkwarden
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: linkwarden
17 | namespace: services
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/services/linkwarden/linkwarden
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: linkwarden
38 | namespace: services
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: linkwarden
49 |
--------------------------------------------------------------------------------
/cluster/apps/home/mealie/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: mealie-api-data
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: mealie-api-data
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/home/mealie/mealie-api-data
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: mealie-api-data
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: mealie-api-data
49 |
--------------------------------------------------------------------------------
/cluster/apps/media/calibre/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: calibre-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: calibre-config
17 | namespace: media
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/media/calibre/calibre-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: calibre-config
38 | namespace: media
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: calibre-config
49 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/registry/templates/registry-svc.yml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: registry
6 | namespace: {{ registry_namespace }}
7 | labels:
8 | k8s-app: registry
9 | addonmanager.kubernetes.io/mode: Reconcile
10 | kubernetes.io/name: "KubeRegistry"
11 | {% if registry_service_annotations %}
12 | annotations:
13 | {{ registry_service_annotations | to_nice_yaml(indent=2, width=1337) | indent(width=4) }}
14 | {% endif %}
15 | spec:
16 | selector:
17 | k8s-app: registry
18 | type: {{ registry_service_type }}
19 | {% if registry_service_type == "ClusterIP" and registry_service_cluster_ip != "" %}
20 | clusterIP: {{ registry_service_cluster_ip }}
21 | {% endif %}
22 | {% if registry_service_type == "LoadBalancer" and registry_service_loadbalancer_ip != "" %}
23 | loadBalancerIP: {{ registry_service_loadbalancer_ip }}
24 | {% endif %}
25 | ports:
26 | - name: registry
27 | port: {{ registry_port }}
28 | protocol: TCP
29 | targetPort: {{ registry_port }}
30 | {% if registry_service_type == "NodePort" and registry_service_nodeport != "" %}
31 | nodePort: {{ registry_service_nodeport }}
32 | {% endif %}
33 |
--------------------------------------------------------------------------------
/playbooks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Reset K8S cluster
3 | hosts: kube_control_plane
4 | any_errors_fatal: true
5 | gather_facts: true
6 | become: true
7 | tags:
8 | - cluster
9 | tasks:
10 | - name: Run resets from relevant roles
11 | include_role:
12 | name: "{{ item }}"
13 | tasks_from: reset
14 | with_items:
15 | # - apps
16 | - kubernetes/control_plane
17 | - containers/runtime
18 |
19 | - name: Reset | include file with reset tasks specific to the network_plugin if exists
20 | include_role:
21 | name: "containers/network/{{ kube_network_plugin }}"
22 | tasks_from: reset
23 | when:
24 | - kube_network_plugin in ['flannel', 'cilium', 'kube-router', 'calico']
25 | tags:
26 | - network
27 |
28 | - name: Tear down terraform stacks
29 | hosts: localhost
30 | tags:
31 | - terraform
32 | tasks:
33 | - name: Tear down terraform stack
34 | community.general.terraform:
35 | project_path: "{{ (infra_dir, 'terraform', item) | path_join }}"
36 | state: absent
37 | when: item is directory
38 | with_fileglob:
39 | - "{{ infra_dir }}/terraform/*"
40 |
--------------------------------------------------------------------------------
/cluster/apps/home/zwave-js-ui/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: zwave-app-store
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: zwave-app-store
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153Gi
23 | local:
24 | path: /srv/cluster/volumes/home/zwave-js-ui/zwave-app-store
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: zwave-app-store
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153Gi
47 | storageClassName: local-storage
48 | volumeName: zwave-app-store
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/home/mealie.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: mealie
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | annotations:
12 | argocd.argoproj.io/sync-wave: '4'
13 | spec:
14 | project: default
15 | sources:
16 | - chart: app-template
17 | repoURL: https://bjw-s-labs.github.io/helm-charts/
18 | targetRevision: 3.7.3
19 | helm:
20 | valueFiles:
21 | - $repo/cluster/apps/home/mealie/values.yaml
22 | releaseName: mealie
23 | - repoURL: https://github.com/clearlybaffled/homelab
24 | path: cluster/apps/home/mealie
25 | targetRevision: main
26 | ref: repo
27 | destination:
28 | name: in-cluster
29 | namespace: home
30 | ignoreDifferences:
31 | - kind: PersistentVolume
32 | jsonPointers:
33 | - /spec/claimRef/resourceVersion
34 | - /spec/claimRef/uid
35 | - /status/lastPhaseTransitionTime
36 | syncPolicy:
37 | automated:
38 | prune: true
39 | selfHeal: true
40 | syncOptions:
41 | - CreateNamespace=true
42 | - ServerSideApply=true
43 | - RespectIgnoreDifferences=true
44 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/bazarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: bazarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: bazarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/bazarr/bazarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: bazarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: bazarr-config
49 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/lidarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: lidarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: lidarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/lidarr/lidarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: lidarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: lidarr-config
49 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/radarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: radarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: radarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/radarr/radarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: radarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: radarr-config
49 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/sonarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: sonarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: sonarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/sonarr/sonarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: sonarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: sonarr-config
49 |
--------------------------------------------------------------------------------
/cluster/apps/home/home-assistant/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: home-assistant
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: home-assistant
17 | namespace: home
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/home/home-assistant/home-assistant
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: home-assistant
38 | namespace: home
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: home-assistant
49 |
--------------------------------------------------------------------------------
/cluster/apps/media/jellyfin/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: jellyseer-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: jellyseer-config
17 | namespace: media
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/media/jellyfin/jellyseer-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: jellyseer-config
38 | namespace: media
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: jellyseer-config
49 |
--------------------------------------------------------------------------------
/cluster/apps/monitoring/kube-prometheus-stack/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: grafana
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: grafana
17 | namespace: monitoring
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/monitoring/kube-prometheus-stack/grafana
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: grafana
38 | namespace: monitoring
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: grafana
49 |
--------------------------------------------------------------------------------
/cluster/bootstrap/infrastructure/reloader.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: argoproj.io/v1alpha1
5 | kind: Application
6 | metadata:
7 | name: reloader
8 | namespace: argocd
9 | finalizers:
10 | - resources-finalizer.argocd.argoproj.io
11 | spec:
12 | project: default
13 | sources:
14 | - chart: reloader
15 | repoURL: https://stakater.github.io/stakater-charts
16 | targetRevision: 2.1.3
17 | helm:
18 | valueFiles:
19 | - $repo/cluster/apps/infrastructure/reloader/values.yaml
20 | releaseName: reloader
21 | - repoURL: https://github.com/clearlybaffled/homelab
22 | path: cluster/apps/infrastructure/reloader
23 | targetRevision: main
24 | ref: repo
25 | destination:
26 | name: in-cluster
27 | namespace: infrastructure
28 | ignoreDifferences:
29 | - group: apps
30 | kind: Deployment
31 | name: reloader-reloader
32 | jqPathExpressions:
33 | - .spec.template.spec.containers[].env[].valueFrom.resourceFieldRef.divisor
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | syncOptions:
39 | - CreateNamespace=true
40 | - ServerSideApply=true
41 | - RespectIgnoreDifferences=true
42 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/readarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: readarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: readarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/readarr/readarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: readarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: readarr-config
49 |
--------------------------------------------------------------------------------
/infrastructure/roles/containers/network/flannel/tasks/reset.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Clean CNI config directory
3 | file:
4 | path: /etc/cni/net.d
5 | state: absent
6 | - name: Clear iptables
7 | block:
8 | - name: iptables | flush
9 | ansible.builtin.iptables:
10 | flush: true
11 | - name: iptables | flush tables
12 | iptables:
13 | flush: true
14 | table: "{{ item }}"
15 | with_items:
16 | - nat
17 | - mangle
18 | - name: iptables | delete chains
19 | command: 'iptables -X'
20 |
21 | - name: Remove cni0 interface
22 | shell: |
23 | ip link set cni0 down
24 | ip link delete cni0 type bridge
25 | register: rm_if
26 | failed_when: rm_if.rc != 0 and not "Cannot find device" in rm_if.stderr
27 | notify:
28 | - 'Kubelet | stop kubelet'
29 | - 'CRI-O | stop crio'
30 |
31 | - name: iptables flush all
32 | iptables:
33 | flush: true
34 | - name: iptables flush nat
35 | iptables:
36 | flush: true
37 | table: nat
38 | notify:
39 | - 'Kubelet | start kubelet'
40 | - 'CRI-O | start crio'
41 |
42 | - name: Remove flannel/subnet.env
43 | file:
44 | path: /run/flannel/subnet.env
45 | state: absent
46 |
--------------------------------------------------------------------------------
/infrastructure/roles/kubernetes/control_plane/tasks/approve-cert.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: kubernetes | Ensure CSR is provided
3 | assert:
4 | that:
5 | - csr is defined and csr
6 | - name is defined and name != ""
7 |
8 | - name: kubernetes | Submit CSR
9 | delegate_to: "{{ groups['kube_control_plane'][0] }}"
10 | connection: ssh
11 | become: true
12 | kubernetes.core.k8s:
13 | kubeconfig: "{{ kubeconfig_file }}"
14 | template: csr.yml.j2
15 | ca_cert: "{{ kube_cluster_cacerts }}"
16 |
17 | - name: kubernetes | Approve CSR
18 | delegate_to: "{{ groups['kube_control_plane'][0] }}"
19 | connection: ssh
20 | become: true
21 | command: "{{ kubectl }} certificate approve {{ name }}"
22 | register: approve_csr
23 | failed_when: approve_csr.rc != 0
24 |
25 | - name: kubernetes | Get Signed certificate
26 | delegate_to: "{{ groups['kube_control_plane'][0] }}"
27 | connection: ssh
28 | become: true
29 | command: "{{ kubectl }} get csr {{ name }} -o jsonpath='{.status.certificate}'"
30 | register: crt
31 | until: crt.stdout is ne ""
32 | retries: 10
33 | delay: 5
34 |
35 | - name: kubernetes | Return signed certificate
36 | set_fact:
37 | certificate: "{{ crt.stdout }}"
38 |
--------------------------------------------------------------------------------
/cluster/apps/downloads/prowlarr/storage.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ansible managed
3 | #
4 | apiVersion: v1
5 | kind: PersistentVolume
6 | metadata:
7 | name: prowlarr-config
8 | finalizers:
9 | - kubernetes.io/pv-protection
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | claimRef:
14 | apiVersion: v1
15 | kind: PersistentVolumeClaim
16 | name: prowlarr-config
17 | namespace: downloads
18 | persistentVolumeReclaimPolicy: Retain
19 | volumeMode: Filesystem
20 | storageClassName: local-storage
21 | capacity:
22 | storage: 153.0Gi
23 | local:
24 | path: /srv/cluster/volumes/downloads/prowlarr/prowlarr-config
25 | nodeAffinity:
26 | required:
27 | nodeSelectorTerms:
28 | - matchExpressions:
29 | - key: kubernetes.io/hostname
30 | operator: In
31 | values:
32 | - parche
33 | ---
34 | apiVersion: v1
35 | kind: PersistentVolumeClaim
36 | metadata:
37 | name: prowlarr-config
38 | namespace: downloads
39 | finalizers:
40 | - kubernetes.io/pvc-protection
41 | spec:
42 | accessModes:
43 | - ReadWriteOnce
44 | resources:
45 | requests:
46 | storage: 153.0Gi
47 | storageClassName: local-storage
48 | volumeName: prowlarr-config
49 |
--------------------------------------------------------------------------------