diff options
author | Aaditya Dhruv <[email protected]> | 2025-04-22 17:29:46 -0500 |
---|---|---|
committer | Aaditya Dhruv <[email protected]> | 2025-05-01 20:19:51 -0500 |
commit | 55298a51cb0cc5e68c5a43869f2f32b899d3a622 (patch) | |
tree | 50d271288502bcff6f0a5133ce0839d883ee8ef7 |
init
117 files changed, 2617 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d3beea --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +**values.yaml** +**env** +**secret** +**vault** +_archive +.vim +scripts/backups/*password diff --git a/README.md b/README.md new file mode 100644 index 0000000..92bdecb --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +### Purpose + +This monorepo has 3 uses: + +1. Setup infrastructure +2. Maintain infrastructure +3. Document infrastructure + +The inventory defines the lab's infrastructure. The config defines the state we +want the infrastructure to be in. For a given inventory, the playbook should be +idempotent. + +### Structure + +``` +src/ +├── network +│ ├── tasks +│ └── templates +├── pi +│ ├── files +│ ├── tasks +│ ├── templates +│ └── vars +└── servers + ├── files + ├── tasks + └── vars +``` + +### Network + +1. Wireguard (Idempotent if atomic) + +Sets up the VPN on the master and slaves. +Idempotent only if run on all the nodes and slaves at once (atomic) + + +2. VLANs + +To be run only on router. Idempotency not guaranteed. + +3. DNS + +Setup DNS on all the hosts. Idempotent. + + +### Pi + +1. PiHole + +Setup the PiHole service. Idempotent. + + +2. PXE + +Setup PXE boot serviec. Idempotent. + + +### Servers + + +1. Base + +Install required packages on the hosts. Idempotent. + + +2. Services + +Install and setup services on hosts. Idempotent. + + + +## Playbook Loops + +Each loop is a idempotent loop playbook which does certain actions. + +### Setup Playbook + +Used for the inital homelab setup. + +Steps: + +1. src/pi + - Start PXE + - Start PiHole +2. PXE boot all hosts with kickstart files +3. src/network + - Apply VLAN settings + - Apply DNS settings + - Apply VPN settings +4. src/servers + - Install base packages + - Start up required services on cluster +5. Misc - Print manual config - router, NAS etc. + +### Maintenance Playbook + +Used for upgrades, backups, and bringing services/networks up and down. + +Steps: + +1. src/pi + - Start PXE + - Start PiHole +2. PXE boot all hosts with kickstart files +3. src/network + - Apply VLAN settings + - Apply DNS settings + - Apply VPN settings +4. src/servers + - Install base packages + - Start up required services on cluster +5. Misc - Print manual config - router, NAS etc. @@ -0,0 +1,9 @@ +### General + +1. Setup playbooks for individual services +- Upgrade, reset, migrate etc. +2. Setup backup role +- Run systemd unit to backup to backblaze +3. Setup artifact store for custom images and packages +### Docs +Explain each role and usage diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..2f30f02 --- /dev/null +++ b/config.yaml @@ -0,0 +1,51 @@ +nfs: + server: 192.168.20.5 +actual: + enabled: true + version: 25.2.1 +jellyfin: + version: 10.10.0 + enabled: true +gonic: + version: 1c23771 #v0.16.4 + enabled: true +cgit: + version: v2.5 + enabled: true +pihole: + enabled: true + baremetal: false + version: 2024.01.0 +metallb: + enabled: true + pool: 192.168.20.100-192.168.20.250 +cloud: + enabled: true +immich: + enabled: true + postgres: + version: pg14-v0.2.0 +fishnet: + enabled: true + replicas: 5 +archivebox: + enabled: true +servers: + base: true +ca: + enabled: true +k3s: + enabled: true + token: 12345 +network: + router: + enabled: false + wireguard: + enabled: false + port: 43000 + path: "/etc/wireguard/" + dns: 192.168.1.1 + vlans: + - 10 + - 20 + - 30 diff --git a/inventories/homelab.ini b/inventories/homelab.ini new file mode 100644 index 0000000..ae11ff9 --- /dev/null +++ b/inventories/homelab.ini @@ -0,0 +1,16 @@ +[servers] +ampharos +regirock +#snorlax +regice +[cloud] +altaria +[router] +porygon +[pi] +goomy +[k3s-master] +ampharos +[k3s-agent] +regirock +regice diff --git a/inventories/inventory.ini b/inventories/inventory.ini new file mode 100644 index 0000000..b4c2474 --- /dev/null +++ b/inventories/inventory.ini @@ -0,0 +1,10 @@ +[servers] +192.168.122.101 +192.168.122.33 +[cloud] +192.168.122.33 +[router] +[pi] +goomy +[k3s-master] +[k3s-agent] diff --git a/playbooks/master.yaml b/playbooks/master.yaml new file mode 100644 index 0000000..8a84097 --- /dev/null +++ b/playbooks/master.yaml @@ -0,0 +1,50 @@ +--- +- name: Lab Configuration + hosts: all + tasks: + - name: Setup Network + include_role: + name: network + - name: Setup Systems + include_role: + name: system + - name: Setup Wireguard + include_role: + name: wireguard + - name: Setup K3S + include_role: + name: k3s + when: inventory_hostname in groups["servers"] + - name: Setup MetalLB + include_role: + name: metallb + - name: Setup CA + include_role: + name: ca + - name: Setup PiHole + include_role: + name: pihole + - name: Setup Cloud + include_role: + name: cloud + - name: Setup Immich + include_role: + name: immich + - name: Setup Jellyfin + include_role: + name: jellyfin + - name: Setup Actual + include_role: + name: actual + - name: Setup Git + include_role: + name: cgit + - name: Setup Fishnet + include_role: + name: fishnet + - name: Setup Prometheus + include_role: + name: prometheus + - name: Setup Fishnet + include_role: + name: fishnet diff --git a/playbooks/setup_k3s.yaml b/playbooks/setup_k3s.yaml new file mode 100644 index 0000000..912cca7 --- /dev/null +++ b/playbooks/setup_k3s.yaml @@ -0,0 +1,13 @@ +--- +- name: Lab Configuration + hosts: all + tasks: + - name: Setup K3S + include_role: + name: k3s + - name: Setup MetalLB + include_role: + name: metallb + - name: Setup CA + include_role: + name: ca diff --git a/playbooks/setup_services.yaml b/playbooks/setup_services.yaml new file mode 100644 index 0000000..91a5b22 --- /dev/null +++ b/playbooks/setup_services.yaml @@ -0,0 +1,32 @@ +--- +- name: Setup Services + hosts: all + gather_facts: false + tasks: + - name: Setup PiHole + include_role: + name: pihole + - name: Setup Cloud + include_role: + name: cloud + - name: Setup Immich + include_role: + name: immich + - name: Setup Jellyfin + include_role: + name: jellyfin + - name: Setup Actual + include_role: + name: actual + - name: Setup Git + include_role: + name: cgit + - name: Setup Fishnet + include_role: + name: fishnet + - name: Setup Prometheus + include_role: + name: prometheus + - name: Setup Fishnet + include_role: + name: fishnet diff --git a/playbooks/setup_single.yaml b/playbooks/setup_single.yaml new file mode 100644 index 0000000..f080bf9 --- /dev/null +++ b/playbooks/setup_single.yaml @@ -0,0 +1,7 @@ +- name: Setup Services + hosts: all + gather_facts: false + tasks: + - name: Setup cgit + include_role: + name: "roles/cgit" diff --git a/roles/actual/defaults/main.yaml b/roles/actual/defaults/main.yaml new file mode 100644 index 0000000..9ac174f --- /dev/null +++ b/roles/actual/defaults/main.yaml @@ -0,0 +1,9 @@ +actual: + enabled: false + replicas: 1 + port: 80 + image: actualbudget/actual-server + version: 24.9.0 + +nfs: + path: "/mnt/nfs/k3s/actual/" diff --git a/roles/actual/files/actual/.helmignore b/roles/actual/files/actual/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/roles/actual/files/actual/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/roles/actual/files/actual/Chart.yaml b/roles/actual/files/actual/Chart.yaml new file mode 100644 index 0000000..c3755a4 --- /dev/null +++ b/roles/actual/files/actual/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: actual +description: Actual Budget for budgeting +type: application + +version: 0.1.0 diff --git a/roles/actual/files/actual/templates/deployment.yaml b/roles/actual/files/actual/templates/deployment.yaml new file mode 100644 index 0000000..3336ee5 --- /dev/null +++ b/roles/actual/files/actual/templates/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: actual + image: "{{ .Values.image }}:{{ .Values.version }}" + volumeMounts: + - mountPath: "/data" + name: "{{ .Chart.Name }}-volume" + volumes: + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: "{{ .Chart.Name }}-pvc" diff --git a/roles/actual/files/actual/templates/ingress.yaml b/roles/actual/files/actual/templates/ingress.yaml new file mode 100644 index 0000000..749ff0b --- /dev/null +++ b/roles/actual/files/actual/templates/ingress.yaml @@ -0,0 +1,34 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: actual + annotations: + cert-manager.io/cluster-issuer: "ca-issuer" +spec: + ingressClassName: traefik + tls: + - hosts: + - finance.aadityadhruv.com + - finance.home + secretName: actual-tls + rules: + - host: finance.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: actual-service + port: + number: 80 + - host: finance.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: actual-service + port: + number: 80 diff --git a/roles/actual/files/actual/templates/pv.yaml b/roles/actual/files/actual/templates/pv.yaml new file mode 100644 index 0000000..498fbd5 --- /dev/null +++ b/roles/actual/files/actual/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .Chart.Name }}-pv" + labels: + app: "{{ .Chart.Name }}-pv" +spec: + storageClassName: nfs + capacity: + storage: 2Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/actual/files/actual/templates/pvc.yaml b/roles/actual/files/actual/templates/pvc.yaml new file mode 100644 index 0000000..71b9b85 --- /dev/null +++ b/roles/actual/files/actual/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 2Gi + selector: + matchLabels: + app: "{{ .Chart.Name }}-pv" diff --git a/roles/actual/files/actual/templates/service.yaml b/roles/actual/files/actual/templates/service.yaml new file mode 100644 index 0000000..5d9b8bc --- /dev/null +++ b/roles/actual/files/actual/templates/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: ClusterIP + selector: + app: {{ .Chart.Name }} + ports: + - protocol: TCP + port: {{ .Values.port }} + targetPort: 5006 + name: webui + diff --git a/roles/actual/tasks/main.yaml b/roles/actual/tasks/main.yaml new file mode 100644 index 0000000..1733c68 --- /dev/null +++ b/roles/actual/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +- name: Deploy Actual + kubernetes.core.helm: + name: actual + chart_ref: "{{ lookup('env', 'PWD') }}/roles/actual/files/actual" + namespace: default + state: "{%- if actual.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ actual.replicas}}" + port: "{{ actual.port }}" + image: "{{ actual.image }}" + version: "{{ actual.version }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + delegate_to: localhost + run_once: true diff --git a/roles/archivebox/defaults/main.yaml b/roles/archivebox/defaults/main.yaml new file mode 100644 index 0000000..0afc620 --- /dev/null +++ b/roles/archivebox/defaults/main.yaml @@ -0,0 +1,9 @@ +archivebox: + enabled: false + replicas: 1 + port: 8000 + image: archivebox/archivebox + version: 0.7 + +nfs: + path: "/mnt/nfs/k3s/archivebox" diff --git a/roles/archivebox/files/archivebox/.helmignore b/roles/archivebox/files/archivebox/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/roles/archivebox/files/archivebox/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/roles/archivebox/files/archivebox/Chart.yaml b/roles/archivebox/files/archivebox/Chart.yaml new file mode 100644 index 0000000..40b00c9 --- /dev/null +++ b/roles/archivebox/files/archivebox/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: archivebox +description: Archival system +type: application +version: 0.1.0 diff --git a/roles/archivebox/files/archivebox/templates/deployment.yaml b/roles/archivebox/files/archivebox/templates/deployment.yaml new file mode 100644 index 0000000..02a3bfb --- /dev/null +++ b/roles/archivebox/files/archivebox/templates/deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: archivebox + image: "{{ .Values.image }}:{{ .Values.version }}" + env: + - name: PUBLIC_INDEX + value: "False" + - name: PUBLIC_SNAPSHOTS + value: "False" + - name: PUID + value: "1000" + - name: PGID + value: "1000" + - name: SAVE_ARCHIVE_DOT_ORG + value: "False" + - name: SAVE_SCREENSHOT + value: "False" + - name: SAVE_DOM + value: "False" + - name: SAVE_SINGLEFILE + value: "False" + + volumeMounts: + - mountPath: "/data" + name: "{{ .Chart.Name }}-volume" + subPath: data + volumes: + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: "{{ .Chart.Name }}-pvc" diff --git a/roles/archivebox/files/archivebox/templates/ingress.yaml b/roles/archivebox/files/archivebox/templates/ingress.yaml new file mode 100644 index 0000000..2601655 --- /dev/null +++ b/roles/archivebox/files/archivebox/templates/ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: archivebox + annotations: + cert-manager.io/cluster-issuer: "ca-issuer" +spec: + ingressClassName: traefik + tls: + - hosts: + - archive.home + secretName: archivebox-tls + rules: + - host: archive.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: archivebox-service + port: + number: 80 diff --git a/roles/archivebox/files/archivebox/templates/pv.yaml b/roles/archivebox/files/archivebox/templates/pv.yaml new file mode 100644 index 0000000..869b121 --- /dev/null +++ b/roles/archivebox/files/archivebox/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .Chart.Name }}-pv" + labels: + app: "{{ .Chart.Name }}-pv" +spec: + storageClassName: nfs + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/archivebox/files/archivebox/templates/pvc.yaml b/roles/archivebox/files/archivebox/templates/pvc.yaml new file mode 100644 index 0000000..10a5ced --- /dev/null +++ b/roles/archivebox/files/archivebox/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + selector: + matchLabels: + app: "{{ .Chart.Name }}-pv" diff --git a/roles/archivebox/files/archivebox/templates/service.yaml b/roles/archivebox/files/archivebox/templates/service.yaml new file mode 100644 index 0000000..bb8bcc2 --- /dev/null +++ b/roles/archivebox/files/archivebox/templates/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: ClusterIP + selector: + app: {{ .Chart.Name }} + ports: + - protocol: TCP + port: 80 + targetPort: 8000 + name: webui + diff --git a/roles/archivebox/tasks/main.yaml b/roles/archivebox/tasks/main.yaml new file mode 100644 index 0000000..a4abe9f --- /dev/null +++ b/roles/archivebox/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +- name: Deploy Archivebox + kubernetes.core.helm: + name: archivebox + chart_ref: "{{ lookup('env', 'PWD') }}/roles/archivebox/files/archivebox" + namespace: default + state: "{%- if archivebox.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ archivebox.replicas }}" + port: "{{ archivebox.port }}" + image: "{{ archivebox.image }}" + version: "{{ archivebox.version }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + delegate_to: localhost + run_once: true diff --git a/roles/ca/files/ca.yaml b/roles/ca/files/ca.yaml new file mode 100644 index 0000000..a77b415 --- /dev/null +++ b/roles/ca/files/ca.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: selfsigned-issuer +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: selfsigned-ca + namespace: cert-manager +spec: + isCA: true + commonName: selfsigned-ca + secretName: root-secret + privateKey: + algorithm: ECDSA + size: 256 + issuerRef: + name: selfsigned-issuer + kind: ClusterIssuer + group: cert-manager.io +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: ca-issuer +spec: + ca: + secretName: root-secret diff --git a/roles/ca/files/lets-encrypt-dev.yaml b/roles/ca/files/lets-encrypt-dev.yaml new file mode 100644 index 0000000..e84120d --- /dev/null +++ b/roles/ca/files/lets-encrypt-dev.yaml @@ -0,0 +1,18 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-staging +spec: + acme: + # The ACME server URL + server: https://acme-staging-v02.api.letsencrypt.org/directory + # Email address used for ACME registration + email: [email protected] + # Name of a secret used to store the ACME account private key + privateKeySecretRef: + name: letsencrypt-staging + # Enable the HTTP-01 challenge provider + solvers: + - http01: + ingress: + ingressClassName: traefik diff --git a/roles/ca/files/lets-encrypt-prod.yaml b/roles/ca/files/lets-encrypt-prod.yaml new file mode 100644 index 0000000..fb9b541 --- /dev/null +++ b/roles/ca/files/lets-encrypt-prod.yaml @@ -0,0 +1,18 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + # The ACME server URL + server: https://acme-v02.api.letsencrypt.org/directory + # Email address used for ACME registration + email: [email protected] + # Name of a secret used to store the ACME account private key + privateKeySecretRef: + name: letsencrypt-prod + # Enable the HTTP-01 challenge provider + solvers: + - http01: + ingress: + ingressClassName: traefik diff --git a/roles/ca/tasks/main.yaml b/roles/ca/tasks/main.yaml new file mode 100644 index 0000000..36b17e1 --- /dev/null +++ b/roles/ca/tasks/main.yaml @@ -0,0 +1,32 @@ +- name: Setup Cert-manager chart + kubernetes.core.helm_repository: + name: jetstack + repo_url: "https://charts.jetstack.io" + +- name: Deploy Cert manager + kubernetes.core.helm: + name: cert-manager + chart_ref: jetstack/cert-manager + release_namespace: cert-manager + create_namespace: true + set_values: + - value: installCRDs=true + value_type: string + +- name: Create CA + kubernetes.core.k8s: + state: "{%- if servers.ca.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/ca/files/ca.yaml" + namespace: cert-manager + +- name: Add Lets Encrypt Dev + kubernetes.core.k8s: + state: "{%- if roles/ca.ca.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/ca/files/lets-encrypt-dev.yaml" + namespace: cert-manager + +- name: Add Lets Encrypt Dev + kubernetes.core.k8s: + state: "{%- if roles/ca.ca.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/ca/files/lets-encrypt-prod.yaml" + namespace: cert-manager diff --git a/roles/cgit/defaults/main.yaml b/roles/cgit/defaults/main.yaml new file mode 100644 index 0000000..727c4a9 --- /dev/null +++ b/roles/cgit/defaults/main.yaml @@ -0,0 +1,12 @@ +cgit: + enabled: false + replicas: 1 + port: 80 + image: aadityadhruv/cgit + version: latest +softserve: + image: charmcli/soft-serve + version: v0.8.5 + +nfs: + path: "/mnt/nfs/k3s/cgit" diff --git a/roles/cgit/files/cgit/Chart.yaml b/roles/cgit/files/cgit/Chart.yaml new file mode 100644 index 0000000..a5722d5 --- /dev/null +++ b/roles/cgit/files/cgit/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: cgit +description: Subsonic compatible music server +type: application +version: 0.1.0 diff --git a/roles/cgit/files/cgit/templates/configmap.yaml b/roles/cgit/files/cgit/templates/configmap.yaml new file mode 100644 index 0000000..e64de68 --- /dev/null +++ b/roles/cgit/files/cgit/templates/configmap.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Chart.Name }}-config +data: + cgitrc: | + cache-size=1000 + #css=/cgit.css + enable-http-clone=1 + enable-blame=1 + enable-commit-graph=1 + enable-log-filecount=1 + enable-log-linecount=1 + enable-git-config=1 + head-include=/usr/share/cgit/theme.html + enable-index-owner=0 + favicon=/favicon.ico + logo=/cgit.png + root-title=Space Junk + mimetype.gif=image/gif + mimetype.html=text/html + mimetype.jpg=image/jpeg + mimetype.jpeg=image/jpeg + mimetype.pdf=application/pdf + mimetype.png=image/png + mimetype.svg=image/svg+xml + source-filter=/usr/lib/cgit/syntax-highlighting.py + readme=:README.md + robots=noindex, nofollow + scan-path=/srv/git/ + diff --git a/roles/cgit/files/cgit/templates/deployment.yaml b/roles/cgit/files/cgit/templates/deployment.yaml new file mode 100644 index 0000000..6ad9c6e --- /dev/null +++ b/roles/cgit/files/cgit/templates/deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: cgit + image: "{{ .Values.cgit.image }}:{{ .Values.cgit.version }}" + volumeMounts: + - mountPath: "/etc/cgitrc" + name: "config" + subPath: cgitrc + - mountPath: "/srv/git" + name: "{{ .Chart.Name }}-volume" + subPath: repos + - name: soft-serve + image: "{{ .Values.softserve.image }}:{{ .Values.softserve.version }}" + volumeMounts: + - mountPath: "/soft-serve" + name: "{{ .Chart.Name }}-volume" + volumes: + - name: "config" + configMap: + name: "{{ .Chart.Name }}-config" + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: "{{ .Chart.Name }}-pvc" diff --git a/roles/cgit/files/cgit/templates/ingress.yaml b/roles/cgit/files/cgit/templates/ingress.yaml new file mode 100644 index 0000000..2b33366 --- /dev/null +++ b/roles/cgit/files/cgit/templates/ingress.yaml @@ -0,0 +1,47 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cgit-le + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: traefik + tls: + - hosts: + - git.aadityadhruv.com + secretName: cgit-tls-le + rules: + - host: git.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: cgit-service + port: + number: 80 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cgit-ca + annotations: + cert-manager.io/cluster-issuer: "ca-issuer" +spec: + ingressClassName: traefik + tls: + - hosts: + - git.home + secretName: cgit-tls-ca + rules: + - host: git.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: cgit-service + port: + number: 80 diff --git a/roles/cgit/files/cgit/templates/pv.yaml b/roles/cgit/files/cgit/templates/pv.yaml new file mode 100644 index 0000000..f62ea30 --- /dev/null +++ b/roles/cgit/files/cgit/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .Chart.Name }}-pv" + labels: + app: "{{ .Chart.Name }}-pv" +spec: + storageClassName: nfs + capacity: + storage: 20Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/cgit/files/cgit/templates/pvc.yaml b/roles/cgit/files/cgit/templates/pvc.yaml new file mode 100644 index 0000000..e6153d1 --- /dev/null +++ b/roles/cgit/files/cgit/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 20Gi + selector: + matchLabels: + app: "{{ .Chart.Name }}-pv" diff --git a/roles/cgit/files/cgit/templates/service.yaml b/roles/cgit/files/cgit/templates/service.yaml new file mode 100644 index 0000000..945bac0 --- /dev/null +++ b/roles/cgit/files/cgit/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: LoadBalancer + selector: + app: {{ .Chart.Name }} + ports: + - protocol: TCP + targetPort: 23231 + port: 22 + name: ssh + - protocol: TCP + port: 81 + targetPort: 23232 + name: http + - protocol: TCP + port: {{ .Values.port }} + targetPort: 80 + name: webui diff --git a/roles/cgit/files/image/Caddyfile b/roles/cgit/files/image/Caddyfile new file mode 100644 index 0000000..35a29b8 --- /dev/null +++ b/roles/cgit/files/image/Caddyfile @@ -0,0 +1,13 @@ +:80 { + @assets path /cgit.css /cgit.png /favicon.ico /theme.css /theme.html + handle @assets { + root * /usr/share/cgit + file_server + } + + reverse_proxy unix//run/fcgiwrap.socket { + transport fastcgi { + env SCRIPT_FILENAME /var/www/cgi-bin/cgit + } + } +} diff --git a/roles/cgit/files/image/Dockerfile b/roles/cgit/files/image/Dockerfile new file mode 100644 index 0000000..894e68a --- /dev/null +++ b/roles/cgit/files/image/Dockerfile @@ -0,0 +1,12 @@ +FROM docker.io/fedora:41 + +RUN dnf install cgit caddy fcgiwrap openssh-server python3-pygments -y +COPY Caddyfile /etc/caddy/Caddyfile +COPY start.sh start.sh +RUN mkdir /usr/lib/cgit -p +COPY theme.css /usr/share/cgit/theme.css +COPY theme.html /usr/share/cgit/theme.html +COPY syntax-highlighting.py /usr/lib/cgit/syntax-highlighting.py +RUN chmod 777 /usr/lib/cgit/syntax-highlighting.py +RUN adduser -m git +CMD ["./start.sh"] diff --git a/roles/cgit/files/image/start.sh b/roles/cgit/files/image/start.sh new file mode 100755 index 0000000..96a279b --- /dev/null +++ b/roles/cgit/files/image/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +/usr/sbin/fcgiwrap -s unix:/run/fcgiwrap.socket & +caddy run --config /etc/caddy/Caddyfile + diff --git a/roles/cgit/files/image/syntax-highlighting.py b/roles/cgit/files/image/syntax-highlighting.py new file mode 100644 index 0000000..e912594 --- /dev/null +++ b/roles/cgit/files/image/syntax-highlighting.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# This script uses Pygments and Python3. You must have both installed +# for this to work. +# +# http://pygments.org/ +# http://python.org/ +# +# It may be used with the source-filter or repo.source-filter settings +# in cgitrc. +# +# The following environment variables can be used to retrieve the +# configuration of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) + + +import sys +import io +from pygments import highlight +from pygments.util import ClassNotFound +from pygments.lexers import TextLexer +from pygments.lexers import guess_lexer +from pygments.lexers import guess_lexer_for_filename +from pygments.formatters import HtmlFormatter + + +sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace') +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +data = sys.stdin.read() +filename = sys.argv[1] +formatter = HtmlFormatter(style='pastie', nobackground=True) + +try: + lexer = guess_lexer_for_filename(filename, data) +except ClassNotFound: + # check if there is any shebang + if data[0:2] == '#!': + lexer = guess_lexer(data) + else: + lexer = TextLexer() +except TypeError: + lexer = TextLexer() + +# highlight! :-) +# printout pygments' css definitions as well +sys.stdout.write('<style>') +sys.stdout.write(formatter.get_style_defs('.highlight')) +sys.stdout.write('</style>') +sys.stdout.write(highlight(data, lexer, formatter, outfile=None)) diff --git a/roles/cgit/files/image/theme.css b/roles/cgit/files/image/theme.css new file mode 100644 index 0000000..54357b1 --- /dev/null +++ b/roles/cgit/files/image/theme.css @@ -0,0 +1,172 @@ +:root { + --bg_h: #1d2021; + --bg: #282828; + --bg_s: #32302f; + --bg1: #3c3836; + --bg2: #504945; + --bg3: #665c54; + --bg4: #7c6f64; + + --fg: #fbf1c7; + --fg1: #ebdbb2; + --fg2: #d5c4a1; + --fg3: #bdae93; + --fg4: #a89984; + + --red: #fb4934; + --green: #b8bb26; + --yellow: #fabd2f; + --blue: #83a598; + --purple: #d3869b; + --aqua: #8ec07c; + --gray: #928374; + --orange: #fe8019; + + --red-dim: #cc2412; + --green-dim: #98971a; + --yellow-dim: #d79921; + --blue-dim: #458588; + --purple-dim: #b16286; + --aqua-dim: #689d6a; + --gray-dim: #a89984; + --orange-dim: #d65d0e; +} + +body, #cgit, .path, div#cgit table.blob td.hashes, +div#cgit table.blob td.lines, div#cgit div.cgit-panel table, +div#cgit table.diffstat { + background: var(--bg) !important; + color: var(--fg) !important; + border: none +} + +a { + color: var(--fg) !important; + text-decoration: underline !important; +} + +select, input { + border: none; + background: var(--bg2); + color: var(--fg); +} + +/**************/ +/*** TABLES ***/ +/**************/ +div#cgit table.tabs td a.active { + background: var(--bg) !important; + color: var(--yellow) !important; +} + +div#cgit table.tabs, div#cgit div.content, +div#cgit table#header td.sub { + border: none; +} + +div#cgit table.list tr.nohover, +div#cgit table.list tr:nth-child(2n) { + background: var(--bg) !important; +} + +div#cgit table.list tr:nth-child(2n+1) { + background: var(--bg_s) !important; +} + +div#cgit table.list tr:hover:not(.nohover) { + background: var(--bg1) !important; +} + +/************/ +/*** CODE ***/ +/************/ +div#cgit table.blob td.linenumbers, +div#cgit table.blob { + border-color: var(--gray); +} + +div#cgit table.blob td.linenumbers a { + color: var(--gray) !important; + text-decoration: none !important; +} + +.markdown-body code, .markdown-body tt, +.markdown-body .highlight pre, .markdown-body pre { + background: var(--bg1) !important; +} + +/************/ +/*** AGES ***/ +/************/ +.age-hours { + color: var(--aqua) !important; +} + +.age-days { + color: var(--aqua-dim) !important; +} + +.age-weeks { + color: var(--fg) !important; +} + +.age-months { + color: var(--fg2) !important; +} + +.age-years { + color: var(--fg4) !important; +} + +/******************/ +/*** DECORATORS ***/ +/******************/ +div#cgit a.branch-deco { + background: var(--aqua); + border: none; + color: var(--bg) !important; +} + +div#cgit a.deco { + background: var(--yellow); + border: none; + color: var(--bg) !important; +} + +div#cgit a.tag-deco { + background: var(--gray); + border: none; + color: var(--bg) !important; +} + +/************/ +/*** DIFF ***/ +/************/ +div#cgit table.diff td div.hunk { + color: var(--blue); +} + +div#cgit table.diff td div.del { + color: var(--red); +} + +div#cgit table.diff td div.add { + color: var(--green); +} + +div#cgit table.diff td div.ctx { + color: var(--gray); +} + +div#cgit table.diff td div.head { + color: var(--fg); +} + +div#cgit table.diffstat td.graph td.add { + background: var(--green); +} + +div#cgit table.diffstat td.graph td.rem { + background: var(--red); +} + diff --git a/roles/cgit/files/image/theme.html b/roles/cgit/files/image/theme.html new file mode 100644 index 0000000..f95b5d1 --- /dev/null +++ b/roles/cgit/files/image/theme.html @@ -0,0 +1 @@ +<link rel="stylesheet" type="text/css" href="/theme.css"> diff --git a/roles/cgit/tasks/main.yaml b/roles/cgit/tasks/main.yaml new file mode 100644 index 0000000..0a99ce3 --- /dev/null +++ b/roles/cgit/tasks/main.yaml @@ -0,0 +1,21 @@ +--- +- name: Deploy cgit + kubernetes.core.helm: + name: cgit + chart_ref: "{{ lookup('env', 'PWD') }}/roles/cgit/files/cgit" + namespace: default + state: "{%- if cgit.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ cgit.replicas }}" + port: "{{ cgit.port }}" + cgit: + image: "{{ cgit.image }}" + version: "{{ cgit.version }}" + softserve: + image: "{{ softserve.image }}" + version: "{{ softserve.version }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + delegate_to: localhost + run_once: true diff --git a/roles/cloud/defaults/main.yaml b/roles/cloud/defaults/main.yaml new file mode 100644 index 0000000..23f8615 --- /dev/null +++ b/roles/cloud/defaults/main.yaml @@ -0,0 +1,6 @@ +cloud: + version: v2.26.0 + replicas: 1 + enabled: false + port: 80 + path: "/mnt/nfs/k3s/cloud/" diff --git a/roles/cloud/files/cloud/Chart.yaml b/roles/cloud/files/cloud/Chart.yaml new file mode 100644 index 0000000..0665bb9 --- /dev/null +++ b/roles/cloud/files/cloud/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: cloud +description: Filebrowser for Files +type: application + +version: 0.1.0 diff --git a/roles/cloud/files/cloud/templates/cloud.yaml b/roles/cloud/files/cloud/templates/cloud.yaml new file mode 100644 index 0000000..d688901 --- /dev/null +++ b/roles/cloud/files/cloud/templates/cloud.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: filebrowser + image: "{{ .Values.image }}:{{ .Values.version }}" + ports: + - name: http + containerPort: 80 + volumeMounts: + - mountPath: "/Drive" + name: "{{ .Chart.Name }}-volume" + subPath: "Drive" + - mountPath: "/database.db" + name: "{{ .Chart.Name }}-volume" + subPath: "db/database.db" + - mountPath: "/.filebrowser.json" + name: "{{ .Chart.Name }}-volume" + subPath: "config/filebrowser.json" + volumes: + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: {{ .Chart.Name }}-pvc +# securityContext: +# runAsUser: 1001 +# runAsGroup: 1001 +# fsGroup: 1001 diff --git a/roles/cloud/files/cloud/templates/ingress.yaml b/roles/cloud/files/cloud/templates/ingress.yaml new file mode 100644 index 0000000..22f55b1 --- /dev/null +++ b/roles/cloud/files/cloud/templates/ingress.yaml @@ -0,0 +1,33 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cloud + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: traefik + tls: + - hosts: + - drive.aadityadhruv.com + secretName: cloud-tls + rules: + - host: drive.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: cloud-service + port: + number: 80 + - host: drive.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: cloud-service + port: + number: 80 diff --git a/roles/cloud/files/cloud/templates/pv.yaml b/roles/cloud/files/cloud/templates/pv.yaml new file mode 100644 index 0000000..21b3611 --- /dev/null +++ b/roles/cloud/files/cloud/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ .Chart.Name }}-pv + labels: + app: {{ .Chart.Name }} +spec: + storageClassName: nfs + capacity: + storage: 16Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/cloud/files/cloud/templates/pvc.yaml b/roles/cloud/files/cloud/templates/pvc.yaml new file mode 100644 index 0000000..1324417 --- /dev/null +++ b/roles/cloud/files/cloud/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 16Gi + selector: + matchLabels: + app: {{ .Chart.Name }} diff --git a/roles/cloud/files/cloud/templates/service.yaml b/roles/cloud/files/cloud/templates/service.yaml new file mode 100644 index 0000000..c412876 --- /dev/null +++ b/roles/cloud/files/cloud/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: ClusterIP + selector: + app: {{ .Chart.Name }} + ports: + - protocol: TCP + port: {{ .Values.port }} + targetPort: http + name: webui diff --git a/roles/cloud/tasks/main.yaml b/roles/cloud/tasks/main.yaml new file mode 100644 index 0000000..4a3a168 --- /dev/null +++ b/roles/cloud/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +- name: Deploy Cloud + kubernetes.core.helm: + name: cloud + chart_ref: "{{ lookup('env', 'PWD') }}/roles/cloud/files/cloud" + namespace: default + state: "{%- if cloud.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + image: filebrowser/filebrowser + replicas: "{{ cloud.replicas }}" + version: "{{ cloud.version }}" + port: "{{ cloud.port }}" + nfs: + path: "{{ cloud.path }}" + server: "{{ nfs.server }}" + delegate_to: localhost + run_once: true diff --git a/roles/fishnet/defaults/main.yaml b/roles/fishnet/defaults/main.yaml new file mode 100644 index 0000000..fa99b84 --- /dev/null +++ b/roles/fishnet/defaults/main.yaml @@ -0,0 +1,3 @@ +fishnet: + enabled: false + replicas: 1 diff --git a/roles/fishnet/files/fishnet/Chart.yaml b/roles/fishnet/files/fishnet/Chart.yaml new file mode 100644 index 0000000..5c0e9f8 --- /dev/null +++ b/roles/fishnet/files/fishnet/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fishnet +description: Distributed analysis for lichess +type: application + +version: 0.1.0 diff --git a/roles/fishnet/files/fishnet/templates/deployment.yaml b/roles/fishnet/files/fishnet/templates/deployment.yaml new file mode 100644 index 0000000..4cdc4b4 --- /dev/null +++ b/roles/fishnet/files/fishnet/templates/deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: fishnet + image: niklasf/fishnet:2 + imagePullPolicy: Always + env: + # - name: CORES + # valueFrom: + # configMapKeyRef: + # name: fishnet-config + # key: cores + - name: KEY + valueFrom: + secretKeyRef: + name: lichess + key: fishnet-private-key + restartPolicy: Always diff --git a/roles/fishnet/tasks/main.yaml b/roles/fishnet/tasks/main.yaml new file mode 100644 index 0000000..57e8b7e --- /dev/null +++ b/roles/fishnet/tasks/main.yaml @@ -0,0 +1,12 @@ +--- +- name: Deploy Fishnet + kubernetes.core.helm: + name: fishnet + chart_ref: "{{ lookup('env', 'PWD') }}/roles/fishnet/files/fishnet" + namespace: default + state: "{%- if fishnet.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ fishnet.replicas }}" + key: "{{ fishnet.key }}" + delegate_to: localhost + run_once: true diff --git a/roles/gonic/defaults/main.yaml b/roles/gonic/defaults/main.yaml new file mode 100644 index 0000000..d0e845c --- /dev/null +++ b/roles/gonic/defaults/main.yaml @@ -0,0 +1,9 @@ +gonic: + enabled: false + replicas: 1 + port: 80 + image: sentriz/gonic + version: v0.16.4 + +nfs: + path: "/mnt/nfs/k3s/gonic" diff --git a/roles/gonic/files/gonic/.helmignore b/roles/gonic/files/gonic/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/roles/gonic/files/gonic/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/roles/gonic/files/gonic/Chart.yaml b/roles/gonic/files/gonic/Chart.yaml new file mode 100644 index 0000000..2bdf9fd --- /dev/null +++ b/roles/gonic/files/gonic/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: gonic +description: Subsonic compatible music server +type: application +version: 0.1.0 diff --git a/roles/gonic/files/gonic/templates/deployment.yaml b/roles/gonic/files/gonic/templates/deployment.yaml new file mode 100644 index 0000000..0b1ed08 --- /dev/null +++ b/roles/gonic/files/gonic/templates/deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: gonic + image: "{{ .Values.image }}:{{ .Values.version }}" + volumeMounts: + - mountPath: "/data" + name: "{{ .Chart.Name }}-volume" + subPath: data + - mountPath: "/music" + name: "{{ .Chart.Name }}-volume" + subPath: music + - mountPath: "/playlists" + name: "{{ .Chart.Name }}-volume" + subPath: playlists + volumes: + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: "{{ .Chart.Name }}-pvc" diff --git a/roles/gonic/files/gonic/templates/ingress.yaml b/roles/gonic/files/gonic/templates/ingress.yaml new file mode 100644 index 0000000..aa6a0bd --- /dev/null +++ b/roles/gonic/files/gonic/templates/ingress.yaml @@ -0,0 +1,34 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gonic + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: traefik + tls: + - hosts: + - music.aadityadhruv.com + - music.home + secretName: gonic-tls + rules: + - host: music.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gonic-service + port: + number: 80 + - host: music.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gonic-service + port: + number: 80 diff --git a/roles/gonic/files/gonic/templates/pv.yaml b/roles/gonic/files/gonic/templates/pv.yaml new file mode 100644 index 0000000..869b121 --- /dev/null +++ b/roles/gonic/files/gonic/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .Chart.Name }}-pv" + labels: + app: "{{ .Chart.Name }}-pv" +spec: + storageClassName: nfs + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/gonic/files/gonic/templates/pvc.yaml b/roles/gonic/files/gonic/templates/pvc.yaml new file mode 100644 index 0000000..10a5ced --- /dev/null +++ b/roles/gonic/files/gonic/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + selector: + matchLabels: + app: "{{ .Chart.Name }}-pv" diff --git a/roles/gonic/files/gonic/templates/service.yaml b/roles/gonic/files/gonic/templates/service.yaml new file mode 100644 index 0000000..a9c272f --- /dev/null +++ b/roles/gonic/files/gonic/templates/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: ClusterIP + selector: + app: {{ .Chart.Name }} + ports: + - protocol: TCP + port: {{ .Values.port }} + targetPort: 80 + name: webui + diff --git a/roles/gonic/tasks/main.yaml b/roles/gonic/tasks/main.yaml new file mode 100644 index 0000000..1d2daa6 --- /dev/null +++ b/roles/gonic/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +- name: Deploy Gonic + kubernetes.core.helm: + name: gonic + chart_ref: "{{ lookup('env', 'PWD') }}/roles/gonic/files/gonic" + namespace: default + state: "{%- if gonic.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ gonic.replicas }}" + port: "{{ gonic.port }}" + image: "{{ gonic.image }}" + version: "{{ gonic.version }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + delegate_to: localhost + run_once: true diff --git a/roles/jellyfin/defaults/main.yaml b/roles/jellyfin/defaults/main.yaml new file mode 100644 index 0000000..47cfbc7 --- /dev/null +++ b/roles/jellyfin/defaults/main.yaml @@ -0,0 +1,5 @@ +jellyfin: + enabled: false + port: 8096 + version: 10.10.0 + replicas: 1 diff --git a/roles/jellyfin/files/jellyfin/ingress.yaml b/roles/jellyfin/files/jellyfin/ingress.yaml new file mode 100644 index 0000000..ab148ba --- /dev/null +++ b/roles/jellyfin/files/jellyfin/ingress.yaml @@ -0,0 +1,33 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: jellyfin + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: traefik + tls: + - hosts: + - media.aadityadhruv.com + secretName: jellyfin-tls + rules: + - host: media.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: jellyfin + port: + number: 8096 + - host: media.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: jellyfin + port: + number: 8096 diff --git a/roles/jellyfin/files/jellyfin/pv.yaml b/roles/jellyfin/files/jellyfin/pv.yaml new file mode 100644 index 0000000..6c7fcb9 --- /dev/null +++ b/roles/jellyfin/files/jellyfin/pv.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: jellyfin-config + labels: + app: jellyfin-config +spec: + storageClassName: nfs + capacity: + storage: 2Gi + accessModes: + - ReadWriteMany + nfs: + server: 192.168.20.5 + path: /mnt/nfs/k3s/jellyfin/config + readOnly: false +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: jellyfin-data + labels: + app: jellyfin-data +spec: + storageClassName: nfs + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + nfs: + server: 192.168.20.5 + path: /mnt/nfs/k3s/jellyfin/data + readOnly: false diff --git a/roles/jellyfin/files/jellyfin/pvc.yaml b/roles/jellyfin/files/jellyfin/pvc.yaml new file mode 100644 index 0000000..47360a9 --- /dev/null +++ b/roles/jellyfin/files/jellyfin/pvc.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: jellyfin-config-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 2Gi + selector: + matchLabels: + app: jellyfin-config +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: jellyfin-data-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + selector: + matchLabels: + app: jellyfin-data diff --git a/roles/jellyfin/tasks/main.yaml b/roles/jellyfin/tasks/main.yaml new file mode 100644 index 0000000..c5f471f --- /dev/null +++ b/roles/jellyfin/tasks/main.yaml @@ -0,0 +1,68 @@ +- name: Add Jellyfin remote chart + kubernetes.core.helm_repository: + name: jellyfin + repo_url: https://utkuozdemir.org/helm-charts + delegate_to: localhost + run_once: true + +- name: Create Jellyfin PVs + kubernetes.core.k8s: + state: "{%- if jellyfin.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/jellyfin/files/jellyfin/pv.yaml" + namespace: default + delegate_to: localhost + run_once: true +- name: Create Jellyfin PVCs + kubernetes.core.k8s: + state: "{%- if jellyfin.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/jellyfin/files/jellyfin/pvc.yaml" + namespace: default + delegate_to: localhost + run_once: true +- name: Create Jellyfin Ingress + kubernetes.core.k8s: + state: "{%- if jellyfin.enabled -%} present {%- else -%} absent {%- endif -%}" + src: "{{ lookup('env', 'PWD') }}/roles/jellyfin/files/jellyfin/ingress.yaml" + namespace: default + delegate_to: localhost + run_once: true + +- name: Get Values Path + set_fact: + jellyfin_values_path: "{{ lookup('env', 'PWD') }}/roles/jellyfin/files/jellyfin/values.yaml" + delegate_to: localhost + run_once: true + +- name: Get Values + set_fact: + jellyfin_defaults: "{{ lookup('file', jellyfin_values_path) | from_yaml }}" + delegate_to: localhost + run_once: true + + +- name: Set User overrides fact + set_fact: + overrides: + port: "{{ jellyfin.port }}" + replicaCount: "{{ jellyfin.replicas }}" + image: + tag: "{{ jellyfin.version }}" + +- name: Merge Values with overrides + set_fact: + jellyfin_values: "{{ jellyfin_defaults | combine(overrides, recursive=True) }}" + delegate_to: localhost + run_once: true + +- debug: + var: jellyfin_values + +- name: Deploy Jellyfin + kubernetes.core.helm: + name: jellyfin + chart_ref: jellyfin/jellyfin + values: "{{ jellyfin_values }}" + namespace: default + state: "{%- if jellyfin.enabled -%} present {%- else -%} absent {%- endif -%}" + delegate_to: localhost + run_once: true diff --git a/roles/k3s/tasks/main.yaml b/roles/k3s/tasks/main.yaml new file mode 100644 index 0000000..dd8f25f --- /dev/null +++ b/roles/k3s/tasks/main.yaml @@ -0,0 +1,34 @@ +--- +- name: Setup K3S Master Node + ansible.builtin.shell: "curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC='server' sh -s - --token {{ servers.k3s.token }} --disable servicelb" + when: inventory_hostname in groups['k3s-master'] + become: true + +- name: Setup K3S Agent Nodes + shell: "curl -sfL https://get.k3s.io | K3S_URL=https://{{ hostvars[groups['k3s-master'][0]]['ansible_default_ipv4']['address'] }}:6443 sh -s - agent --token {{ servers.k3s.token }} --disable servicelb" + when: inventory_hostname in groups['k3s-agent'] + become: true + +- name: Change perms for k3s.yaml + file: + path: "/etc/rancher/k3s/k3s.yaml" + mode: "0755" + become: true + when: inventory_hostname in groups['k3s-master'] + + +- name: copy file over to localhost + ansible.builtin.fetch: + src: /etc/rancher/k3s/k3s.yaml + dest: /home/aaditya/.kube/config + flat: yes + when: inventory_hostname in groups['k3s-master'] + +- name: Update IP address of k3s-master in local kube config + ansible.builtin.replace: + path: /home/aaditya/.kube/config + regexp: "127.0.0.1" + replace: "{{ hostvars[groups['k3s-master'][0]]['ansible_default_ipv4']['address'] }}" + delegate_to: localhost + run_once: true + diff --git a/roles/metallb/defaults/main.yaml b/roles/metallb/defaults/main.yaml new file mode 100644 index 0000000..43916f8 --- /dev/null +++ b/roles/metallb/defaults/main.yaml @@ -0,0 +1,3 @@ +metallb: + enabled: false + pool: 192.168.20.100-192.168.20.250 diff --git a/roles/metallb/tasks/main.yaml b/roles/metallb/tasks/main.yaml new file mode 100644 index 0000000..b484411 --- /dev/null +++ b/roles/metallb/tasks/main.yaml @@ -0,0 +1,36 @@ +--- +- name: Create IPAddressPool + kubernetes.core.k8s: + state: "{{ 'present' if metallb.enabled else 'absent' }}" + definition: + apiVersion: metallb.io/v1beta1 + kind: IPAddressPool + metadata: + name: pool + namespace: default + spec: + addresses: + - "{{ metallb.pool }}" + delegate_to: localhost + run_once: true + +- name: Create L2Advertisement + kubernetes.core.k8s: + state: "{{ 'present' if metallb.enabled else 'absent' }}" + definition: + apiVersion: metallb.io/v1beta1 + kind: L2Advertisement + metadata: + name: metallb + namespace: default + spec: + ipAddressPools: + - pool + nodeSelectors: + - matchLabels: + kubernetes.io/hostname: regirock + - matchLabels: + kubernetes.io/hostname: regice + + delegate_to: localhost + run_once: true diff --git a/roles/monitoring/tasks/main.yaml b/roles/monitoring/tasks/main.yaml new file mode 100644 index 0000000..27bec57 --- /dev/null +++ b/roles/monitoring/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +- name: Add Prometheus remote chart + kubernetes.core.helm_repository: + name: prometheus + repo_url: https://prometheus-community.github.io/helm-charts + delegate_to: localhost + run_once: true + +- name: Deploy prometheus + kubernetes.core.helm: + name: prometheus + chart_ref: prometheus/prometheus + namespace: default + state: "{%- if monitoring.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + delegate_to: localhost + run_once: true diff --git a/roles/network/defaults/main.yaml b/roles/network/defaults/main.yaml new file mode 100644 index 0000000..44350c4 --- /dev/null +++ b/roles/network/defaults/main.yaml @@ -0,0 +1,2 @@ +network: + gateway: 192.168.1.1 diff --git a/roles/network/tasks/dns.yaml b/roles/network/tasks/dns.yaml new file mode 100644 index 0000000..f80ec45 --- /dev/null +++ b/roles/network/tasks/dns.yaml @@ -0,0 +1,14 @@ +--- +- name: Ensure NetworkManager is installed + dnf: + name: NetworkManager + state: latest + + become: true +- name: Set DNS to Router + community.general.nmcli: + conn_name: "{{ ansible_default_ipv4.interface }}" + state: present + dns4: + - "{{ network.gateway }}" + become: true diff --git a/roles/network/tasks/main.yaml b/roles/network/tasks/main.yaml new file mode 100644 index 0000000..626197b --- /dev/null +++ b/roles/network/tasks/main.yaml @@ -0,0 +1,11 @@ +- name: Setup VLANs + import_tasks: vlans.yaml + when: inventory_hostname in groups["router"] and network.router.enabled + +- name: Setup DNS + import_tasks: dns.yaml + when: inventory_hostname in groups["servers"] + +- name: Misc Print + import_tasks: misc.yaml + diff --git a/roles/network/tasks/misc.yaml b/roles/network/tasks/misc.yaml new file mode 100644 index 0000000..bf9cb47 --- /dev/null +++ b/roles/network/tasks/misc.yaml @@ -0,0 +1,12 @@ +- name: Print Tomato Setup + debug: + msg: "To complete the rest of the setup, setup the upstream DNS on the router. Also disable DNS rebind protection. Don't forget to add the bridges to the router's DNSMASQ configuration in the form interface=brXX + Setup the following iptables on altaria:\n + -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2 +-A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.2 +-A PREROUTING -d 137.184.95.59/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2 +-A PREROUTING -d 137.184.95.59/32 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.2 +-A PREROUTING -d 137.184.95.59/32 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 10.0.0.2:30022 +" + delegate_to: localhost + run_once: true diff --git a/roles/network/tasks/vlans.yaml b/roles/network/tasks/vlans.yaml new file mode 100644 index 0000000..f71d1e4 --- /dev/null +++ b/roles/network/tasks/vlans.yaml @@ -0,0 +1,39 @@ +- name: Add VLAN configuration + ansible.builtin.raw: "robocfg vlan {{ item }} ports '1t 5t'" + loop: "{{ network.vlans }}" + +- name: Bind VLANs to eth0 + ansible.builtin.raw: "vconfig add eth0 {{ item }}" + loop: "{{ network.vlans }}" + ignore_errors: true + +- name: Bring VLANs up + ansible.builtin.raw: "ip link set dev vlan{{ item }} up" + loop: "{{ network.vlans }}" + +- name: Create bridges + ansible.builtin.raw: "brctl addbr br{{ item }}" + loop: "{{ network.vlans }}" + ignore_errors: true + +- name: Add vlans to bridges + ansible.builtin.raw: "brctl addif br{{ item }} vlan{{ item }}" + loop: "{{ network.vlans }}" + ignore_errors: true + +- name: Setup IP Ranges for VLANs + ansible.builtin.raw: "ip addr add 192.168.{{ item }}.1/24 dev br{{ item }}" + loop: "{{ network.vlans }}" + ignore_errors: true + +- name: Bring bridges up + ansible.builtin.raw: "ip link set dev br{{ item }} up" + loop: "{{ network.vlans }}" + +- name: Allow INPUT from bridges #This allows packets to reach the router + ansible.builtin.raw: "iptables -A INPUT -i br{{ item }} -j ACCEPT" + loop: "{{ network.vlans }}" + +- name: Allow INPUT from bridges #This allows packets to be forwarded to other interfaces + ansible.builtin.raw: "iptables -A FORWARD -i br{{ item }} -j ACCEPT" + loop: "{{ network.vlans }}" diff --git a/roles/network/tasks/vps.yaml b/roles/network/tasks/vps.yaml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/roles/network/tasks/vps.yaml diff --git a/roles/photos/defaults/main.yaml b/roles/photos/defaults/main.yaml new file mode 100644 index 0000000..5c85766 --- /dev/null +++ b/roles/photos/defaults/main.yaml @@ -0,0 +1,9 @@ +immich: + version: v1.119.0 + postgres: + image: tensorchord/pgvecto-rs + user: postgres + nfs: + path: /mnt/nfs/k3s/immich/db +nfs: + path: /mnt/nfs/k3s/immich/data diff --git a/roles/photos/files/core/Chart.yaml b/roles/photos/files/core/Chart.yaml new file mode 100644 index 0000000..7cb306b --- /dev/null +++ b/roles/photos/files/core/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: immich-core +description: Core for immich +type: application + +version: 0.1.0 diff --git a/roles/photos/files/core/templates/ingress.yaml b/roles/photos/files/core/templates/ingress.yaml new file mode 100644 index 0000000..c883185 --- /dev/null +++ b/roles/photos/files/core/templates/ingress.yaml @@ -0,0 +1,33 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: immich + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: traefik + tls: + - hosts: + - photos.aadityadhruv.com + secretName: immich-tls + rules: + - host: photos.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: immich-server + port: + number: 2283 + - host: photos.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: immich-server + port: + number: 2283 diff --git a/roles/photos/files/core/templates/pv.yaml b/roles/photos/files/core/templates/pv.yaml new file mode 100644 index 0000000..cacdcf9 --- /dev/null +++ b/roles/photos/files/core/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "immich-pv" + labels: + app: "immich-pv" +spec: + storageClassName: nfs + capacity: + storage: 200Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/photos/files/core/templates/pvc.yaml b/roles/photos/files/core/templates/pvc.yaml new file mode 100644 index 0000000..6ce8f0c --- /dev/null +++ b/roles/photos/files/core/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: immich-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 200Gi + selector: + matchLabels: + app: "immich-pv" diff --git a/roles/photos/files/postgres/Chart.yaml b/roles/photos/files/postgres/Chart.yaml new file mode 100644 index 0000000..f64d74d --- /dev/null +++ b/roles/photos/files/postgres/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: pgvectors +description: Postgres chart with pgvector extension +type: application + +version: 0.1.0 diff --git a/roles/photos/files/postgres/templates/database.yaml b/roles/photos/files/postgres/templates/database.yaml new file mode 100644 index 0000000..098a410 --- /dev/null +++ b/roles/photos/files/postgres/templates/database.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "immich-postgres-deployment" + labels: + app: "immich-db" +spec: + replicas: 1 + selector: + matchLabels: + app: "immich-db" + template: + metadata: + labels: + app: "immich-db" + spec: + containers: + - name: immich-db + image: "{{ .Values.image }}:{{ .Values.version }}" + env: + - name: POSTGRES_USER + value: {{ .Values.user }} + - name: POSTGRES_DB + value: immich + - name: POSTGRES_PASSWORD + value: {{ .Values.password }} + - name: PGDATA + value: /var/lib/postgresql/data/_data + ports: + - containerPort: 5432 + volumeMounts: + - mountPath: "/var/lib/postgresql/data" + name: "immich-database-volume" + volumes: + - name: "immich-database-volume" + persistentVolumeClaim: + claimName: "immich-db-pvc" diff --git a/roles/photos/files/postgres/templates/pv.yaml b/roles/photos/files/postgres/templates/pv.yaml new file mode 100644 index 0000000..14b5aa2 --- /dev/null +++ b/roles/photos/files/postgres/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "immich-db-pv" + labels: + app: "immich-db-pv" +spec: + storageClassName: nfs + capacity: + storage: 10Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/photos/files/postgres/templates/pvc.yaml b/roles/photos/files/postgres/templates/pvc.yaml new file mode 100644 index 0000000..4b3aca7 --- /dev/null +++ b/roles/photos/files/postgres/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: immich-db-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 10Gi + selector: + matchLabels: + app: "immich-db-pv" diff --git a/roles/photos/files/postgres/templates/service.yaml b/roles/photos/files/postgres/templates/service.yaml new file mode 100644 index 0000000..8fb9a49 --- /dev/null +++ b/roles/photos/files/postgres/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: immich-db-service +spec: + type: ClusterIP + selector: + app: immich-db + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 diff --git a/roles/photos/tasks/main.yaml b/roles/photos/tasks/main.yaml new file mode 100644 index 0000000..1faca34 --- /dev/null +++ b/roles/photos/tasks/main.yaml @@ -0,0 +1,60 @@ +- name: Add Immich remote chart + kubernetes.core.helm_repository: + name: immich + repo_url: https://immich-app.github.io/immich-charts + delegate_to: localhost + run_once: true + +- name: Deploy Immich Postgres Chart + kubernetes.core.helm: + state: "{%- if immich.enabled -%} present {%- else -%} absent {%- endif -%}" + name: immich-postgres + chart_ref: "{{ lookup('env', 'PWD') }}/roles/photos/files/postgres" + values: + image: "{{ immich.postgres.image }}" + version: "{{ immich.postgres.version }}" + user: "{{ immich.postgres.user }}" + password: "{{ immich.postgres.password }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ immich.postgres.nfs.path }}" + namespace: default + delegate_to: localhost + run_once: true + +- name: Deploy Immich Core + kubernetes.core.helm: + state: "{%- if immich.enabled -%} present {%- else -%} absent {%- endif -%}" + name: immich-core + chart_ref: "{{ lookup('env', 'PWD') }}/roles/photos/files/core" + values: + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + namespace: default + delegate_to: localhost + run_once: true + + +- name: Deploy Immich + kubernetes.core.helm: + state: "{%- if immich.enabled -%} present {%- else -%} absent {%- endif -%}" + name: immich + chart_ref: immich/immich + values: + env: + DB_USERNAME: "{{ immich.postgres.user }}" + DB_PASSWORD: "{{ immich.postgres.password }}" + DB_DATABASE_NAME: immich + DB_HOSTNAME: immich-db-service + image: + tag: "{{ immich.version }}" + immich: + persistence: + library: + existingClaim: "immich-pvc" + redis: + enabled: true + namespace: default + delegate_to: localhost + run_once: true diff --git a/roles/pihole/defaults/main.yaml b/roles/pihole/defaults/main.yaml new file mode 100644 index 0000000..d31e2f0 --- /dev/null +++ b/roles/pihole/defaults/main.yaml @@ -0,0 +1,8 @@ +pihole: + enabled: false + baremetal: false + version: 2025.02.6 + replicas: 1 + image: pihole/pihole +nfs: + path: "/mnt/nfs/k3s/pihole" diff --git a/roles/pihole/files/pihole.service b/roles/pihole/files/pihole.service new file mode 100644 index 0000000..6d992d0 --- /dev/null +++ b/roles/pihole/files/pihole.service @@ -0,0 +1,14 @@ +[Unit] +Description=Manage PiHole +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +RemainAfterExit=true +User=root +ExecStart=/usr/local/bin/podman-compose -f /opt/containers/pihole.yaml up -d +ExecStop=/usr/local/bin/podman-compose -f /opt/containers/pihole.yaml down + +[Install] +WantedBy=multi-user.target diff --git a/roles/pihole/files/pihole/.helmignore b/roles/pihole/files/pihole/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/roles/pihole/files/pihole/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/roles/pihole/files/pihole/Chart.yaml b/roles/pihole/files/pihole/Chart.yaml new file mode 100644 index 0000000..e472ab4 --- /dev/null +++ b/roles/pihole/files/pihole/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: pihole +description: PiHole on K8s +type: application + +version: 0.1.0 diff --git a/roles/pihole/files/pihole/templates/deployment.yaml b/roles/pihole/files/pihole/templates/deployment.yaml new file mode 100644 index 0000000..4fc7faa --- /dev/null +++ b/roles/pihole/files/pihole/templates/deployment.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Chart.Name }}-deployment" + labels: + app: {{ .Chart.Name }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: pihole + image: "{{ .Values.image }}:{{ .Values.version }}" + ports: + - containerPort: 53 + protocol: TCP + - containerPort: 53 + protocol: UDP + - containerPort: 80 + protocol: TCP + volumeMounts: + - mountPath: "/etc/pihole" + name: "{{ .Chart.Name }}-volume" + subPath: "pihole" + - mountPath: "/etc/dnsmasq.d" + name: "{{ .Chart.Name }}-volume" + subPath: "dnsmasq" + volumes: + - name: "{{ .Chart.Name }}-volume" + persistentVolumeClaim: + claimName: "{{ .Chart.Name }}-pvc" diff --git a/roles/pihole/files/pihole/templates/ingress.yaml b/roles/pihole/files/pihole/templates/ingress.yaml new file mode 100644 index 0000000..8e84845 --- /dev/null +++ b/roles/pihole/files/pihole/templates/ingress.yaml @@ -0,0 +1,34 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: pihole + annotations: + cert-manager.io/cluster-issuer: "ca-issuer" +spec: + ingressClassName: traefik + tls: + - hosts: + - dns.aadityadhruv.com + - dns.home + secretName: pihole-tls + rules: + - host: dns.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: pihole-service + port: + number: 80 + - host: dns.aadityadhruv.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: pihole-service + port: + number: 80 diff --git a/roles/pihole/files/pihole/templates/pv.yaml b/roles/pihole/files/pihole/templates/pv.yaml new file mode 100644 index 0000000..498fbd5 --- /dev/null +++ b/roles/pihole/files/pihole/templates/pv.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: "{{ .Chart.Name }}-pv" + labels: + app: "{{ .Chart.Name }}-pv" +spec: + storageClassName: nfs + capacity: + storage: 2Gi + accessModes: + - ReadWriteMany + nfs: + server: {{ .Values.nfs.server }} + path: {{ .Values.nfs.path }} + readOnly: false diff --git a/roles/pihole/files/pihole/templates/pvc.yaml b/roles/pihole/files/pihole/templates/pvc.yaml new file mode 100644 index 0000000..71b9b85 --- /dev/null +++ b/roles/pihole/files/pihole/templates/pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Chart.Name }}-pvc +spec: + storageClassName: nfs + accessModes: + - ReadWriteMany + resources: + requests: + storage: 2Gi + selector: + matchLabels: + app: "{{ .Chart.Name }}-pv" diff --git a/roles/pihole/files/pihole/templates/service.yaml b/roles/pihole/files/pihole/templates/service.yaml new file mode 100644 index 0000000..72612c0 --- /dev/null +++ b/roles/pihole/files/pihole/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service +spec: + type: LoadBalancer + selector: + app: {{ .Chart.Name }} + ports: + - name: dns-tcp + port: 53 + targetPort: 53 + protocol: TCP + - name: dns-udp + port: 53 + targetPort: 53 + protocol: UDP + - name: web + port: 80 + targetPort: 80 + protocol: TCP diff --git a/roles/pihole/tasks/k8s.yaml b/roles/pihole/tasks/k8s.yaml new file mode 100644 index 0000000..a4fcb81 --- /dev/null +++ b/roles/pihole/tasks/k8s.yaml @@ -0,0 +1,15 @@ +- name: Deploy PiHole + kubernetes.core.helm: + name: pihole + chart_ref: "{{ lookup('env', 'PWD') }}/roles/pihole/files/pihole" + namespace: default + state: "{%- if pihole.enabled -%} present {%- else -%} absent {%- endif -%}" + values: + replicas: "{{ pihole.replicas }}" + image: "{{ pihole.image }}" + version: "{{ pihole.version }}" + nfs: + server: "{{ nfs.server }}" + path: "{{ nfs.path }}" + delegate_to: localhost + run_once: true diff --git a/roles/pihole/tasks/main.yaml b/roles/pihole/tasks/main.yaml new file mode 100644 index 0000000..7fa1cef --- /dev/null +++ b/roles/pihole/tasks/main.yaml @@ -0,0 +1,10 @@ +--- +- name: Setup PiHole (cluster) + import_tasks: k8s.yaml + when: not pihole.baremetal + +- name: Setup PiHole (baremetal) + import_tasks: pihole.yaml + when: pihole.enabled and pihole.baremetal and inventory_hostname in group["pi"] + + diff --git a/roles/pihole/tasks/pihole.yaml b/roles/pihole/tasks/pihole.yaml new file mode 100644 index 0000000..c4b1959 --- /dev/null +++ b/roles/pihole/tasks/pihole.yaml @@ -0,0 +1,73 @@ +--- +- name: Ensure podman exists + ansible.builtin.dnf: + name: podman + state: latest + become: true + +- name: Ensure pip exists + ansible.builtin.dnf: + name: python3-pip + state: latest + become: true + +- name: Install podman compose via pip + pip: + name: podman-compose + become: true + +- name: Create containers directory + ansible.builtin.file: + path: /opt/containers/ + state: directory + mode: '0755' + become: true + +- name: Copy compose file to containers directory + ansible.builtin.template: + src: pihole.yaml.j2 + dest: /opt/containers/pihole.yaml + become: true + +- name: Copy pihole service file to systemd directory + ansible.builtin.copy: + src: pihole.service + dest: /etc/systemd/system/ + become: true + +- name: Ensure systemd-resovled is disabled + ansible.builtin.systemd_service: + enabled: false + name: systemd-resolved + state: stopped + ignore_errors: true + become: true + +- name: Enable PiHole serivce + ansible.builtin.systemd_service: + daemon_reload: true + enabled: true + state: restarted + name: pihole + become: true + +- name: Open DNS Port TCP + ansible.posix.firewalld: + port: 53/tcp + permanent: true + state: enabled + become: true + +- name: Open DNS Port UDP + ansible.posix.firewalld: + port: 53/udp + permanent: true + state: enabled + become: true + +- name: Open Webserver port + ansible.posix.firewalld: + port: 8000/tcp + permanent: true + state: enabled + become: true diff --git a/roles/pihole/templates/pihole.yaml.j2 b/roles/pihole/templates/pihole.yaml.j2 new file mode 100644 index 0000000..7e744df --- /dev/null +++ b/roles/pihole/templates/pihole.yaml.j2 @@ -0,0 +1,20 @@ +version: "3" +services: + pihole: + image: docker.io/pihole/pihole:{{ pihole.tag }} + container_name: pihole + ports: + - "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:53:53/tcp" + - "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:53:53/udp" + - "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:8000:80/tcp" + environment: + TZ: 'America/Chicago' + WEBPASSWORD: {{ pihole.password }} + volumes: + - 'pihole:/etc/pihole:Z' + - 'pihole_dnsmaq:/etc/dnsmasq.d:Z' + restart: unless-stopped +volumes: + pihole: + pihole_dnsmaq: + diff --git a/roles/system/defaults/main.yaml b/roles/system/defaults/main.yaml new file mode 100644 index 0000000..2f889ab --- /dev/null +++ b/roles/system/defaults/main.yaml @@ -0,0 +1,18 @@ +servers: + base: true +packages: + dnf: + - epel-release + - htop + - fail2ban + - git + - neovim + - nfs-utils + - nmap + - podman + - podman-compose + - restic + - sqlite + - tmux + - wireguard-tools + - yamllint diff --git a/roles/system/files/packages/k9s/k9s.spec b/roles/system/files/packages/k9s/k9s.spec new file mode 100644 index 0000000..ae4297b --- /dev/null +++ b/roles/system/files/packages/k9s/k9s.spec @@ -0,0 +1,37 @@ +Name: k9s +Version: 0.27.4 +Release: %autorelease +Summary: A Kubernetes CLI To Manage Your Clusters + +License: Apache-2.0 +URL: https://github.com/derailed/k9s +Source0: https://github.com/derailed/k9s/archive/refs/tags/v%{version}.tar.gz + + +BuildRequires: golang, make, git + + +%description +K9s provides a terminal UI to interact with your Kubernetes clusters. + + +%global debug_package %{nil} +%prep +%autosetup -n k9s-%{version} + + +%build +make build + + +%install +mkdir %{buildroot}%{_bindir} -p +cp ./execs/k9s %{buildroot}%{_bindir} + +%files +%{_bindir}/k9s + + +%changelog +%autochangelog + diff --git a/roles/system/tasks/main.yaml b/roles/system/tasks/main.yaml new file mode 100644 index 0000000..3e86842 --- /dev/null +++ b/roles/system/tasks/main.yaml @@ -0,0 +1,7 @@ +--- +- name: Install required dnf packages + dnf: + name: "{{ item }}" + loop: "{{ packages.dnf }}" + when: inventory_hostname in groups['servers'] and servers.base + become: true diff --git a/roles/wireguard/defaults/main.yaml b/roles/wireguard/defaults/main.yaml new file mode 100644 index 0000000..649825c --- /dev/null +++ b/roles/wireguard/defaults/main.yaml @@ -0,0 +1,2 @@ +wireguard: + enabled: false diff --git a/roles/wireguard/tasks/main.yaml b/roles/wireguard/tasks/main.yaml new file mode 100644 index 0000000..4bdd7a9 --- /dev/null +++ b/roles/wireguard/tasks/main.yaml @@ -0,0 +1,3 @@ +- name: Setup wireguard + import_tasks: wireguard.yaml + when: wireguard.enabled and inventory_hostname in groups["servers"] diff --git a/roles/wireguard/tasks/wireguard.yaml b/roles/wireguard/tasks/wireguard.yaml new file mode 100644 index 0000000..eda00aa --- /dev/null +++ b/roles/wireguard/tasks/wireguard.yaml @@ -0,0 +1,93 @@ +- name: Setup Wireguard on Master Host + block: + + - name: Ensure wireguard is present + ansible.builtin.dnf: + name: wireguard-tools + state: latest + + - name: Generate private key + ansible.builtin.shell: "wg genkey" + register: privatekey + + - name: Set private key variable + ansible.builtin.set_fact: + privatekey: "{{ privatekey.stdout_lines[0] }}" + + - name: Generate public key + ansible.builtin.shell: "echo {{ privatekey }} | wg pubkey" + register: publickey + + - name: Set public key variable + ansible.builtin.set_fact: + publickey: "{{ publickey.stdout_lines[0] }}" + + - name: Create wireguard config file + ansible.builtin.template: + src: wireguard.master.j2 + dest: "{{ network.wireguard.path }}/wg0.conf" + + - name: Ensure ansible facts directory exists + ansible.builtin.file: + path: /etc/ansible/facts.d + state: directory + mode: 0755 + + - name: Add fact about wireguard config to host + ansible.builtin.copy: + content: '{ "PublicKey": "{{ publickey }}" }' + dest: "/etc/ansible/facts.d/wireguard.fact" + + - name: Re-gather facts + gather_facts: + when: inventory_hostname in groups["cloud"] + become: true + +- name: Setup Wireguard on Slave Hosts + block: + - name: Ensure wireguard is present + ansible.builtin.dnf: + name: wireguard-tools + state: latest + + - name: Wait for master node to generate file + ansible.builtin.wait_for: + host: "{{ groups['cloud'][0] }}" + path: /etc/ansible/facts.d/wireguard.fact + search_regex: PublicKey + delegate_to: "{{ groups['cloud'][0] }}" + register: output + + - name: Generate private key + ansible.builtin.shell: "wg genkey" + register: privatekey + + - name: Set private key variable + ansible.builtin.set_fact: + privatekey: "{{ privatekey.stdout_lines[0] }}" + + - name: Generate public key + ansible.builtin.shell: "echo {{ privatekey }} | wg pubkey" + register: publickey + + - name: Set public key variable + ansible.builtin.set_fact: + publickey: "{{ publickey.stdout_lines[0] }}" + + - name: Setup wireguard ip fact + set_fact: + wireguard_ip: "10.0.0.{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'].split('.')[3] }}" + + - name: Create wireguard config file + ansible.builtin.template: + src: wireguard.slave.j2 + dest: "{{ network.wireguard.path }}/wg0.conf" + + - name: Add host's details to master + lineinfile: + path: "{{ network.wireguard.path }}/wg0.conf" + line: "\n[Peer]\nPublicKey={{ publickey }}\nAllowedIPs={{ wireguard_ip }}\n" + delegate_to: "{{ groups['cloud'][0] }}" + + when: inventory_hostname not in groups["cloud"] + become: true diff --git a/roles/wireguard/templates/wireguard.master.j2 b/roles/wireguard/templates/wireguard.master.j2 new file mode 100644 index 0000000..c2ac41c --- /dev/null +++ b/roles/wireguard/templates/wireguard.master.j2 @@ -0,0 +1,6 @@ +[Interface] +Address = 10.0.0.1/24 +PostUp = firewall-cmd --add-masquerade +PostDown = firewall-cmd --remove-masquerade +ListenPort = {{ network.wireguard.port }} +PrivateKey = {{ privatekey }} diff --git a/roles/wireguard/templates/wireguard.slave.j2 b/roles/wireguard/templates/wireguard.slave.j2 new file mode 100644 index 0000000..b6a01b8 --- /dev/null +++ b/roles/wireguard/templates/wireguard.slave.j2 @@ -0,0 +1,11 @@ +[Interface] +Address = {{ wireguard_ip }}/24 +ListenPort = {{ network.wireguard.port }} +PrivateKey = {{ privatekey }} +DNS = {{ network.dns }} + +[Peer] +PublicKey = {{ hostvars[groups['cloud'][0]]['ansible_local']['wireguard']['PublicKey'] }} +AllowedIPs = 0.0.0.0/0 +Endpoint = {{ hostvars[groups['cloud'][0]]['ansible_default_ipv4']['address'] }}:{{ network.wireguard.port }} +PersistentKeepalive = 15 diff --git a/scripts/backups/backup.sh b/scripts/backups/backup.sh new file mode 100755 index 0000000..2636c1e --- /dev/null +++ b/scripts/backups/backup.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -x +source /etc/backups/backup.env + +# Make logs directory +mkdir -p $BACKUPS_DIR/logs +touch $BACKUPS_DIR/logs/$DATE.txt + +#Starting backup +echo "-------" $DATE BACKUP START "--------" &>> $BACKUPS_DIR/logs/$DATE.txt + +#Restic backup command +restic $DRY_RUN --verbose -r $RESTIC_REPOSITORY \ + backup $BACKUP_PATHS \ + &>> $BACKUPS_DIR/logs/$DATE.txt + +echo "-------" $DATE BACKUP PRUNE START "--------" &>> $BACKUPS_DIR/logs/$DATE.txt +#Prune +restic -r $RESTIC_REPOSITORY forget \ + --keep-last $SNAPSHOTS_RETAINED \ + --keep-weekly $WEEKS_RETAINED \ + --keep-monthly $MONTHS_RETAINED \ + --keep-yearly $YEARS_RETAINED $DRY_RUN \ + --prune \ + &>> $BACKUPS_DIR/logs/$DATE.txt +#End backup +echo "-------" $DATE BACKUP COMPLETE "--------" &>> $BACKUPS_DIR/logs/$DATE.txt + + |