feat: initial homelab-gitops declarative structure (Terraform & ArgoCD)
This commit is contained in:
41
README.md
41
README.md
@@ -1,3 +1,40 @@
|
|||||||
# homelab-gitops
|
# Snarfnet Homelab GitOps & Infrastructure as Code
|
||||||
|
|
||||||
Snarfnet Homelab GitOps, Terraform, and ArgoCD Declarations
|
This repository contains declarative configurations for the Snarfnet homelab infrastructure across Kubernetes clusters (`spaceport`, `aerodrome`), Docker Swarm (`shipyard`), Synology NAS storage arrays, and Harvester HCI.
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
homelab-gitops/
|
||||||
|
├── terraform/
|
||||||
|
│ ├── authentik/ # Authentik Providers, Applications, Proxy ForwardAuth, and Security Policies
|
||||||
|
│ ├── harvester/ # Declarative VLAN networks, VM templates, and SSH keys
|
||||||
|
│ ├── shipyard-swarm/ # Docker Swarm stacks (NPM, Gotify, Homebox, Peanut)
|
||||||
|
│ └── synology/ # Synology shared folders (kubernetes, docker, backups, media) and NFS rules
|
||||||
|
├── argocd/
|
||||||
|
│ ├── bootstrap/ # Root App-of-Apps and Project definitions
|
||||||
|
│ ├── spaceport/ # Applications hosted on spaceport (Gitea, Zabbix, Jenkins, Semaphore, Coder, Homepage)
|
||||||
|
│ └── aerodrome/ # Applications hosted on aerodrome (Authentik, AWX, Grafana, Uptime Kuma, Longhorn)
|
||||||
|
└── manifests/ # Raw Kubernetes YAML manifests managed by ArgoCD
|
||||||
|
├── spaceport/
|
||||||
|
└── aerodrome/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Terraform State Storage
|
||||||
|
|
||||||
|
Terraform state is stored in Gitea's built-in HTTP State Backend:
|
||||||
|
- Backend URL: `https://git.snarfnet.net/api/packages/snarf/terraform/state/<module>`
|
||||||
|
- HTTP Authentication: Basic auth via Gitea account (`snarf`)
|
||||||
|
|
||||||
|
## ArgoCD GitOps Engine
|
||||||
|
|
||||||
|
- **ArgoCD Server:** `https://argo.snarfnet.net` (hosted on `spaceport` in namespace `infrastructure`)
|
||||||
|
- **Target Clusters:**
|
||||||
|
- In-cluster (`https://kubernetes.default.svc`): Spaceport (`sninfr0lnxctl01–04`, `sninfr0lnxwrk01–06`)
|
||||||
|
- Remote cluster (`https://140.44.4.11:6443`): Aerodrome (`sninfr2lnxctl01–03`, `sninfr2lnxwrk01–07`)
|
||||||
|
- **Sync Policy:**
|
||||||
|
- `automated: { prune: false, selfHeal: true }`
|
||||||
|
- `syncOptions: [ServerSideApply=true, CreateNamespace=true]`
|
||||||
|
- **Notifications Pipeline:**
|
||||||
|
- Webhook integration to Gotify (`http://140.44.4.70:8090/message?token=A8aWxGiToPsAuToMaTiO`)
|
||||||
|
- Automated failover bridge to Discord channel `#infra-alerts`
|
||||||
|
|||||||
27
argocd/aerodrome/authentik-app.yaml
Normal file
27
argocd/aerodrome/authentik-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: aerodrome-authentik
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aerodrome-authentik
|
||||||
|
cluster: aerodrome
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/aerodrome/authentik
|
||||||
|
destination:
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
namespace: infra
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/aerodrome/awx-app.yaml
Normal file
27
argocd/aerodrome/awx-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: aerodrome-awx
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aerodrome-awx
|
||||||
|
cluster: aerodrome
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/aerodrome/awx
|
||||||
|
destination:
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
namespace: awx
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/aerodrome/grafana-app.yaml
Normal file
27
argocd/aerodrome/grafana-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: aerodrome-grafana
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aerodrome-grafana
|
||||||
|
cluster: aerodrome
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/aerodrome/grafana
|
||||||
|
destination:
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
namespace: monitoring
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/aerodrome/longhorn-recurring-jobs-app.yaml
Normal file
27
argocd/aerodrome/longhorn-recurring-jobs-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: aerodrome-longhorn-recurring-jobs
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aerodrome-longhorn-recurring-jobs
|
||||||
|
cluster: aerodrome
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/aerodrome/longhorn-recurring-jobs
|
||||||
|
destination:
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
namespace: longhorn-system
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/aerodrome/uptime-kuma-app.yaml
Normal file
27
argocd/aerodrome/uptime-kuma-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: aerodrome-uptime-kuma
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aerodrome-uptime-kuma
|
||||||
|
cluster: aerodrome
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/aerodrome/uptime-kuma
|
||||||
|
destination:
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
namespace: monitoring
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
23
argocd/bootstrap/argocd-project.yaml
Normal file
23
argocd/bootstrap/argocd-project.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: AppProject
|
||||||
|
metadata:
|
||||||
|
name: homelab
|
||||||
|
namespace: infrastructure
|
||||||
|
finalizers:
|
||||||
|
- resources-finalizer.argocd.argoproj.io
|
||||||
|
spec:
|
||||||
|
description: "Snarfnet Homelab Core Services & Workloads"
|
||||||
|
sourceRepos:
|
||||||
|
- "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
- "http://gitea-http.infrastructure.svc.cluster.local:3000/Snarf/homelab-gitops.git"
|
||||||
|
destinations:
|
||||||
|
- namespace: "*"
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
- namespace: "*"
|
||||||
|
server: "https://140.44.4.11:6443"
|
||||||
|
clusterResourceWhitelist:
|
||||||
|
- group: "*"
|
||||||
|
kind: "*"
|
||||||
|
namespaceResourceWhitelist:
|
||||||
|
- group: "*"
|
||||||
|
kind: "*"
|
||||||
30
argocd/bootstrap/root-app-of-apps.yaml
Normal file
30
argocd/bootstrap/root-app-of-apps.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: root-app-of-apps
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: root-app-of-apps
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
finalizers:
|
||||||
|
- resources-finalizer.argocd.argoproj.io
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: argocd/bootstrap
|
||||||
|
directory:
|
||||||
|
recurse: true
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/coder-app.yaml
Normal file
27
argocd/spaceport/coder-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-coder
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-coder
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/coder
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/gitea-app.yaml
Normal file
27
argocd/spaceport/gitea-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-gitea
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-gitea
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/gitea
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/homepage-app.yaml
Normal file
27
argocd/spaceport/homepage-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-homepage
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-homepage
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/homepage
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/jenkins-app.yaml
Normal file
27
argocd/spaceport/jenkins-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-jenkins
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-jenkins
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/jenkins
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/semaphore-app.yaml
Normal file
27
argocd/spaceport/semaphore-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-semaphore
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-semaphore
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/semaphore
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: infrastructure
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
27
argocd/spaceport/zabbix-app.yaml
Normal file
27
argocd/spaceport/zabbix-app.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: spaceport-zabbix
|
||||||
|
namespace: infrastructure
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: spaceport-zabbix
|
||||||
|
cluster: spaceport
|
||||||
|
annotations:
|
||||||
|
notifications.argoproj.io/subscribe.on-sync-failed.gotify: ""
|
||||||
|
notifications.argoproj.io/subscribe.on-health-degraded.gotify: ""
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: "https://git.snarfnet.net/Snarf/homelab-gitops.git"
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: manifests/spaceport/zabbix
|
||||||
|
destination:
|
||||||
|
server: "https://kubernetes.default.svc"
|
||||||
|
namespace: zabbix
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: false
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ServerSideApply=true
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
apiVersion: longhorn.io/v1beta2
|
||||||
|
kind: RecurringJob
|
||||||
|
metadata:
|
||||||
|
name: snapshot-4h
|
||||||
|
namespace: longhorn-system
|
||||||
|
spec:
|
||||||
|
name: snapshot-4h
|
||||||
|
task: snapshot
|
||||||
|
cron: "0 */4 * * *"
|
||||||
|
retain: 6
|
||||||
|
concurrency: 2
|
||||||
|
groups:
|
||||||
|
- default
|
||||||
|
---
|
||||||
|
apiVersion: longhorn.io/v1beta2
|
||||||
|
kind: RecurringJob
|
||||||
|
metadata:
|
||||||
|
name: snapshot-daily
|
||||||
|
namespace: longhorn-system
|
||||||
|
spec:
|
||||||
|
name: snapshot-daily
|
||||||
|
task: snapshot
|
||||||
|
cron: "0 2 * * *"
|
||||||
|
retain: 7
|
||||||
|
concurrency: 2
|
||||||
|
groups:
|
||||||
|
- default
|
||||||
|
---
|
||||||
|
apiVersion: longhorn.io/v1beta2
|
||||||
|
kind: RecurringJob
|
||||||
|
metadata:
|
||||||
|
name: backup-daily-nas
|
||||||
|
namespace: longhorn-system
|
||||||
|
spec:
|
||||||
|
name: backup-daily-nas
|
||||||
|
task: backup
|
||||||
|
cron: "0 3 * * *"
|
||||||
|
retain: 14
|
||||||
|
concurrency: 2
|
||||||
|
groups:
|
||||||
|
- default
|
||||||
131
terraform/authentik/applications.tf
Normal file
131
terraform/authentik/applications.tf
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# 1. Argo CD
|
||||||
|
resource "authentik_application" "argocd" {
|
||||||
|
name = "Argo CD"
|
||||||
|
slug = "argo-cd"
|
||||||
|
protocol_provider = authentik_provider_oauth2.argocd.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/argo.png"
|
||||||
|
meta_launch_url = "https://argo.snarfnet.net"
|
||||||
|
group = "Infrastructure"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Gitea
|
||||||
|
resource "authentik_application" "gitea" {
|
||||||
|
name = "Gitea"
|
||||||
|
slug = "gitea"
|
||||||
|
protocol_provider = authentik_provider_oauth2.gitea.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gitea.png"
|
||||||
|
meta_launch_url = "https://git.snarfnet.net"
|
||||||
|
group = "Development"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. AWX
|
||||||
|
resource "authentik_application" "awx" {
|
||||||
|
name = "AWX Automation"
|
||||||
|
slug = "awx"
|
||||||
|
protocol_provider = authentik_provider_oauth2.awx.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/ansible.png"
|
||||||
|
meta_launch_url = "https://awx.snarfnet.net"
|
||||||
|
group = "Infrastructure"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Grafana
|
||||||
|
resource "authentik_application" "grafana" {
|
||||||
|
name = "Grafana"
|
||||||
|
slug = "grafana"
|
||||||
|
protocol_provider = authentik_provider_oauth2.grafana.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/grafana.png"
|
||||||
|
meta_launch_url = "https://grafana.snarfnet.net"
|
||||||
|
group = "Observability"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Zabbix
|
||||||
|
resource "authentik_application" "zabbix" {
|
||||||
|
name = "Zabbix"
|
||||||
|
slug = "zabbix"
|
||||||
|
protocol_provider = authentik_provider_oauth2.zabbix.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/zabbix.png"
|
||||||
|
meta_launch_url = "https://zabbix.snarfnet.net"
|
||||||
|
group = "Observability"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 6. Coder
|
||||||
|
resource "authentik_application" "coder" {
|
||||||
|
name = "Coder"
|
||||||
|
slug = "coder"
|
||||||
|
protocol_provider = authentik_provider_oauth2.coder.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/coder.png"
|
||||||
|
meta_launch_url = "https://coder.snarfnet.net"
|
||||||
|
group = "Development"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 7. Semaphore
|
||||||
|
resource "authentik_application" "semaphore" {
|
||||||
|
name = "Semaphore UI"
|
||||||
|
slug = "semaphore"
|
||||||
|
protocol_provider = authentik_provider_oauth2.semaphore.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/ansible.png"
|
||||||
|
meta_launch_url = "https://semaphore.snarfnet.net"
|
||||||
|
group = "Infrastructure"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 8. Synology DSM
|
||||||
|
resource "authentik_application" "synology" {
|
||||||
|
name = "Synology DSM"
|
||||||
|
slug = "synology"
|
||||||
|
protocol_provider = authentik_provider_oauth2.synology.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/synology.png"
|
||||||
|
meta_launch_url = "https://nas.snarfnet.net"
|
||||||
|
group = "Storage"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9. Gotify
|
||||||
|
resource "authentik_application" "gotify" {
|
||||||
|
name = "Gotify"
|
||||||
|
slug = "gotify"
|
||||||
|
protocol_provider = authentik_provider_proxy.gotify.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gotify.png"
|
||||||
|
meta_launch_url = "https://gotify.snarfnet.net"
|
||||||
|
group = "Observability"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 10. Peanut
|
||||||
|
resource "authentik_application" "peanut" {
|
||||||
|
name = "Peanut UPS"
|
||||||
|
slug = "peanut"
|
||||||
|
protocol_provider = authentik_provider_proxy.peanut.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/ups.png"
|
||||||
|
meta_launch_url = "https://peanut.snarfnet.net"
|
||||||
|
group = "Power & Environment"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 11. Homebox
|
||||||
|
resource "authentik_application" "homebox" {
|
||||||
|
name = "Homebox"
|
||||||
|
slug = "homebox"
|
||||||
|
protocol_provider = authentik_provider_proxy.homebox.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/homebox.png"
|
||||||
|
meta_launch_url = "https://homebox.snarfnet.net"
|
||||||
|
group = "Management"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 12. NPM
|
||||||
|
resource "authentik_application" "npm" {
|
||||||
|
name = "Nginx Proxy Manager"
|
||||||
|
slug = "nginx-proxy-manager"
|
||||||
|
protocol_provider = authentik_provider_proxy.npm.id
|
||||||
|
meta_icon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nginx-proxy-manager.png"
|
||||||
|
meta_launch_url = "https://nginx.snarfnet.net"
|
||||||
|
group = "Infrastructure"
|
||||||
|
open_in_new_tab = true
|
||||||
|
}
|
||||||
140
terraform/authentik/oauth2_providers.tf
Normal file
140
terraform/authentik/oauth2_providers.tf
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Scopes
|
||||||
|
data "authentik_property_mapping_provider_scope" "openid" {
|
||||||
|
scope_name = "openid"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "email" {
|
||||||
|
scope_name = "email"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "profile" {
|
||||||
|
scope_name = "profile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Argo CD
|
||||||
|
resource "authentik_provider_oauth2" "argocd" {
|
||||||
|
name = "Argo CD"
|
||||||
|
client_id = "argocd-client-id"
|
||||||
|
client_secret = "SnarfNetArgoCDPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://argo.snarfnet.net/auth/callback"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Gitea
|
||||||
|
resource "authentik_provider_oauth2" "gitea" {
|
||||||
|
name = "Gitea"
|
||||||
|
client_id = "gitea-client-id"
|
||||||
|
client_secret = "SnarfNetGiteaPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://git.snarfnet.net/user/oauth2/authentik/callback"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. AWX
|
||||||
|
resource "authentik_provider_oauth2" "awx" {
|
||||||
|
name = "AWX Automation"
|
||||||
|
client_id = "awx-client-id"
|
||||||
|
client_secret = "SnarfNetAWXPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://awx.snarfnet.net/sso/complete/authentik/"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Grafana
|
||||||
|
resource "authentik_provider_oauth2" "grafana" {
|
||||||
|
name = "Grafana Monitoring"
|
||||||
|
client_id = "grafana-client-id"
|
||||||
|
client_secret = "SnarfNetGrafanaPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://grafana.snarfnet.net/login/generic_oauth"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_provider_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Zabbix
|
||||||
|
resource "authentik_provider_oauth2" "zabbix" {
|
||||||
|
name = "Zabbix Monitoring"
|
||||||
|
client_id = "zabbix-client-id"
|
||||||
|
client_secret = "SnarfNetZabbixPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://zabbix.snarfnet.net/index_sso.php"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 6. Coder Workspaces
|
||||||
|
resource "authentik_provider_oauth2" "coder" {
|
||||||
|
name = "Coder Workspaces"
|
||||||
|
client_id = "coder-client-id"
|
||||||
|
client_secret = "SnarfNetCoderPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://coder.snarfnet.net/api/v2/users/oidc/callback"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 7. Semaphore UI
|
||||||
|
resource "authentik_provider_oauth2" "semaphore" {
|
||||||
|
name = "Semaphore UI"
|
||||||
|
client_id = "semaphore-client-id"
|
||||||
|
client_secret = "SnarfNetSemaphorePermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://semaphore.snarfnet.net/api/auth/oidc/authentik/redirect"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 8. Synology DSM
|
||||||
|
resource "authentik_provider_oauth2" "synology" {
|
||||||
|
name = "Synology DSM"
|
||||||
|
client_id = "synology-client-id"
|
||||||
|
client_secret = "SnarfNetSynologyPermanentSecret2026"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
signing_key = var.default_signing_key_uuid
|
||||||
|
redirect_uris = ["https://nas.snarfnet.net/entry.cgi", "https://snarfnas.snarfnet.net/entry.cgi"]
|
||||||
|
property_mappings = [
|
||||||
|
data.authentik_property_mapping_provider_scope.openid.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.email.id,
|
||||||
|
data.authentik_property_mapping_provider_scope.profile.id,
|
||||||
|
]
|
||||||
|
}
|
||||||
17
terraform/authentik/outputs.tf
Normal file
17
terraform/authentik/outputs.tf
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
output "authentik_applications" {
|
||||||
|
description = "Configured Authentik application slugs"
|
||||||
|
value = [
|
||||||
|
authentik_application.argocd.slug,
|
||||||
|
authentik_application.gitea.slug,
|
||||||
|
authentik_application.awx.slug,
|
||||||
|
authentik_application.grafana.slug,
|
||||||
|
authentik_application.zabbix.slug,
|
||||||
|
authentik_application.coder.slug,
|
||||||
|
authentik_application.semaphore.slug,
|
||||||
|
authentik_application.synology.slug,
|
||||||
|
authentik_application.gotify.slug,
|
||||||
|
authentik_application.peanut.slug,
|
||||||
|
authentik_application.homebox.slug,
|
||||||
|
authentik_application.npm.slug,
|
||||||
|
]
|
||||||
|
}
|
||||||
7
terraform/authentik/policies.tf
Normal file
7
terraform/authentik/policies.tf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Conditional MFA & Security Policies
|
||||||
|
resource "authentik_policy_password" "default" {
|
||||||
|
name = "homelab-password-policy"
|
||||||
|
length_min = 12
|
||||||
|
error_message = "Password must be at least 12 characters long."
|
||||||
|
check_have_i_been_pwned = false
|
||||||
|
}
|
||||||
21
terraform/authentik/providers.tf
Normal file
21
terraform/authentik/providers.tf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
required_providers {
|
||||||
|
authentik = {
|
||||||
|
source = "goauthentik/authentik"
|
||||||
|
version = "~> 2024.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "http" {
|
||||||
|
address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/authentik"
|
||||||
|
lock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/authentik/lock"
|
||||||
|
unlock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/authentik/lock"
|
||||||
|
username = "snarf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "authentik" {
|
||||||
|
url = var.authentik_url
|
||||||
|
token = var.authentik_token
|
||||||
|
}
|
||||||
35
terraform/authentik/proxy_providers.tf
Normal file
35
terraform/authentik/proxy_providers.tf
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# 1. Gotify Web UI ForwardAuth
|
||||||
|
resource "authentik_provider_proxy" "gotify" {
|
||||||
|
name = "Gotify ForwardAuth"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
external_host = "https://gotify.snarfnet.net"
|
||||||
|
mode = "forward_single"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Peanut UPS Dashboard ForwardAuth
|
||||||
|
resource "authentik_provider_proxy" "peanut" {
|
||||||
|
name = "Peanut ForwardAuth"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
external_host = "https://peanut.snarfnet.net"
|
||||||
|
mode = "forward_single"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Homebox Inventory ForwardAuth
|
||||||
|
resource "authentik_provider_proxy" "homebox" {
|
||||||
|
name = "Homebox ForwardAuth"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
external_host = "https://homebox.snarfnet.net"
|
||||||
|
mode = "forward_single"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Nginx Proxy Manager Admin ForwardAuth
|
||||||
|
resource "authentik_provider_proxy" "npm" {
|
||||||
|
name = "NPM Admin ForwardAuth"
|
||||||
|
authorization_flow = var.default_authorization_flow_uuid
|
||||||
|
invalidation_flow = var.default_invalidation_flow_uuid
|
||||||
|
external_host = "https://nginx.snarfnet.net"
|
||||||
|
mode = "forward_single"
|
||||||
|
}
|
||||||
30
terraform/authentik/variables.tf
Normal file
30
terraform/authentik/variables.tf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
variable "authentik_url" {
|
||||||
|
type = string
|
||||||
|
description = "Base URL for the Authentik instance"
|
||||||
|
default = "https://authentik.snarfnet.net"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "authentik_token" {
|
||||||
|
type = string
|
||||||
|
description = "API Token for Authentik automation"
|
||||||
|
default = "SnarfNetPermanentAutomationToken2026"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "default_authorization_flow_uuid" {
|
||||||
|
type = string
|
||||||
|
description = "Default OAuth authorization flow UUID"
|
||||||
|
default = "38a86ef3-f43a-4756-8f54-ca008a8eef6e"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "default_invalidation_flow_uuid" {
|
||||||
|
type = string
|
||||||
|
description = "Default OAuth invalidation flow UUID"
|
||||||
|
default = "7a8145b8-2476-4636-9901-825f826df805"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "default_signing_key_uuid" {
|
||||||
|
type = string
|
||||||
|
description = "Default RSA signing key certificate UUID"
|
||||||
|
default = "6d48ba88-4b51-428f-903c-93334988a941"
|
||||||
|
}
|
||||||
37
terraform/harvester/networks.tf
Normal file
37
terraform/harvester/networks.tf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Declarative Harvester VLAN Networks
|
||||||
|
resource "harvester_network" "vlan20_infra" {
|
||||||
|
name = "vlan20-infra"
|
||||||
|
namespace = "default"
|
||||||
|
vlan_id = 20
|
||||||
|
route {
|
||||||
|
mode = "auto"
|
||||||
|
gateway = "140.44.4.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "harvester_network" "vlan40_storage" {
|
||||||
|
name = "vlan40-storage"
|
||||||
|
namespace = "default"
|
||||||
|
vlan_id = 40
|
||||||
|
route {
|
||||||
|
mode = "auto"
|
||||||
|
gateway = "14.10.10.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "harvester_network" "vlan50_smarthome" {
|
||||||
|
name = "vlan50-smarthome"
|
||||||
|
namespace = "default"
|
||||||
|
vlan_id = 50
|
||||||
|
route {
|
||||||
|
mode = "auto"
|
||||||
|
gateway = "14.4.4.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# SSH Key Pair for Virtual Machines
|
||||||
|
resource "harvester_ssh_key" "snarfnet_master" {
|
||||||
|
name = "snarfnet-master-key"
|
||||||
|
namespace = "default"
|
||||||
|
public_key = var.master_ssh_public_key
|
||||||
|
}
|
||||||
8
terraform/harvester/outputs.tf
Normal file
8
terraform/harvester/outputs.tf
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
output "harvester_vlan_networks" {
|
||||||
|
description = "Configured Harvester VLAN networks"
|
||||||
|
value = [
|
||||||
|
harvester_network.vlan20_infra.name,
|
||||||
|
harvester_network.vlan40_storage.name,
|
||||||
|
harvester_network.vlan50_smarthome.name,
|
||||||
|
]
|
||||||
|
}
|
||||||
20
terraform/harvester/providers.tf
Normal file
20
terraform/harvester/providers.tf
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
required_providers {
|
||||||
|
harvester = {
|
||||||
|
source = "harvester/harvester"
|
||||||
|
version = "~> 0.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "http" {
|
||||||
|
address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/harvester"
|
||||||
|
lock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/harvester/lock"
|
||||||
|
unlock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/harvester/lock"
|
||||||
|
username = "snarf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "harvester" {
|
||||||
|
kubeconfig = var.harvester_kubeconfig
|
||||||
|
}
|
||||||
11
terraform/harvester/variables.tf
Normal file
11
terraform/harvester/variables.tf
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
variable "harvester_kubeconfig" {
|
||||||
|
type = string
|
||||||
|
description = "Path to Harvester cluster kubeconfig file"
|
||||||
|
default = "~/.kube/harvester.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_ssh_public_key" {
|
||||||
|
type = string
|
||||||
|
description = "Snarfnet Master SSH Public Key"
|
||||||
|
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPqgXJd84m12yNq1gqjAOm6n8sK1l9K3eK0Y7Y+V8V7/ snarfnet-master"
|
||||||
|
}
|
||||||
11
terraform/harvester/vms.tf
Normal file
11
terraform/harvester/vms.tf
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Harvester Virtual Machine Templates / Definitions
|
||||||
|
# Note: Baremetal Harvester HCI nodes remain outside destructive automation scope.
|
||||||
|
# VM resources defined declaratively for state tracking.
|
||||||
|
|
||||||
|
resource "harvester_image" "ubuntu_focal" {
|
||||||
|
name = "ubuntu-20.04-server-cloudimg-arm64"
|
||||||
|
namespace = "default"
|
||||||
|
display_name = "Ubuntu 20.04 ARM64 Cloud Image"
|
||||||
|
source_type = "download"
|
||||||
|
url = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-arm64.img"
|
||||||
|
}
|
||||||
9
terraform/shipyard-swarm/outputs.tf
Normal file
9
terraform/shipyard-swarm/outputs.tf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
output "swarm_services" {
|
||||||
|
description = "Configured Docker Swarm services"
|
||||||
|
value = [
|
||||||
|
docker_service.gotify.name,
|
||||||
|
docker_service.peanut.name,
|
||||||
|
docker_service.homebox.name,
|
||||||
|
docker_service.nginx_proxy_manager.name,
|
||||||
|
]
|
||||||
|
}
|
||||||
20
terraform/shipyard-swarm/providers.tf
Normal file
20
terraform/shipyard-swarm/providers.tf
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
required_providers {
|
||||||
|
docker = {
|
||||||
|
source = "kreuzwerker/docker"
|
||||||
|
version = "~> 3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "http" {
|
||||||
|
address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/shipyard-swarm"
|
||||||
|
lock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/shipyard-swarm/lock"
|
||||||
|
unlock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/shipyard-swarm/lock"
|
||||||
|
username = "snarf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "docker" {
|
||||||
|
host = var.docker_host
|
||||||
|
}
|
||||||
173
terraform/shipyard-swarm/stacks.tf
Normal file
173
terraform/shipyard-swarm/stacks.tf
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# Swarm Overlay Network
|
||||||
|
resource "docker_network" "homelab_public" {
|
||||||
|
name = "homelab_public"
|
||||||
|
driver = "overlay"
|
||||||
|
attachable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Gotify Notification Service
|
||||||
|
resource "docker_service" "gotify" {
|
||||||
|
name = "gotify"
|
||||||
|
|
||||||
|
task_spec {
|
||||||
|
container_spec {
|
||||||
|
image = "gotify/server:latest"
|
||||||
|
env = {
|
||||||
|
GOTIFY_SERVER_PORT = "80"
|
||||||
|
TZ = "America/New_York"
|
||||||
|
}
|
||||||
|
mounts {
|
||||||
|
target = "/app/data"
|
||||||
|
source = "${var.swarm_storage_base}/gotify"
|
||||||
|
type = "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restart_policy {
|
||||||
|
condition = "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode {
|
||||||
|
replicated {
|
||||||
|
replicas = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_spec {
|
||||||
|
ports {
|
||||||
|
target_port = 80
|
||||||
|
published_port = 8090
|
||||||
|
publish_mode = "ingress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Peanut UPS Service
|
||||||
|
resource "docker_service" "peanut" {
|
||||||
|
name = "peanut"
|
||||||
|
|
||||||
|
task_spec {
|
||||||
|
container_spec {
|
||||||
|
image = "brandawg93/peanut:latest"
|
||||||
|
env = {
|
||||||
|
TZ = "America/New_York"
|
||||||
|
}
|
||||||
|
mounts {
|
||||||
|
target = "/config"
|
||||||
|
source = "${var.swarm_storage_base}/peanut"
|
||||||
|
type = "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restart_policy {
|
||||||
|
condition = "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode {
|
||||||
|
replicated {
|
||||||
|
replicas = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_spec {
|
||||||
|
ports {
|
||||||
|
target_port = 8080
|
||||||
|
published_port = 8050
|
||||||
|
publish_mode = "ingress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Homebox Inventory Service
|
||||||
|
resource "docker_service" "homebox" {
|
||||||
|
name = "homebox"
|
||||||
|
|
||||||
|
task_spec {
|
||||||
|
container_spec {
|
||||||
|
image = "ghcr.io/sysadminsmedia/homebox:latest"
|
||||||
|
env = {
|
||||||
|
HBOX_MODE = "production"
|
||||||
|
HBOX_STORAGE_DATA = "/data"
|
||||||
|
TZ = "America/New_York"
|
||||||
|
}
|
||||||
|
mounts {
|
||||||
|
target = "/data"
|
||||||
|
source = "${var.swarm_storage_base}/homebox"
|
||||||
|
type = "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
restart_policy {
|
||||||
|
condition = "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode {
|
||||||
|
replicated {
|
||||||
|
replicas = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_spec {
|
||||||
|
ports {
|
||||||
|
target_port = 7745
|
||||||
|
published_port = 7745
|
||||||
|
publish_mode = "ingress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Nginx Proxy Manager Service (Node-pinned to snshpd4lnxswm04)
|
||||||
|
resource "docker_service" "nginx_proxy_manager" {
|
||||||
|
name = "nginx-proxy-manager"
|
||||||
|
|
||||||
|
task_spec {
|
||||||
|
container_spec {
|
||||||
|
image = "jc21/nginx-proxy-manager:latest"
|
||||||
|
env = {
|
||||||
|
TZ = "America/New_York"
|
||||||
|
PUID = "1000"
|
||||||
|
PGID = "1000"
|
||||||
|
}
|
||||||
|
mounts {
|
||||||
|
target = "/data"
|
||||||
|
source = "${var.swarm_storage_base}/nginx-proxy-manager_nginx-data/_data"
|
||||||
|
type = "bind"
|
||||||
|
}
|
||||||
|
mounts {
|
||||||
|
target = "/etc/letsencrypt"
|
||||||
|
source = "${var.swarm_storage_base}/nginx-proxy-manager_nginx-letsencrypt/_data"
|
||||||
|
type = "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placement {
|
||||||
|
constraints = ["node.hostname == snshpd4lnxswm04"]
|
||||||
|
}
|
||||||
|
restart_policy {
|
||||||
|
condition = "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode {
|
||||||
|
replicated {
|
||||||
|
replicas = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_spec {
|
||||||
|
ports {
|
||||||
|
target_port = 80
|
||||||
|
published_port = 80
|
||||||
|
publish_mode = "host"
|
||||||
|
}
|
||||||
|
ports {
|
||||||
|
target_port = 443
|
||||||
|
published_port = 443
|
||||||
|
publish_mode = "host"
|
||||||
|
}
|
||||||
|
ports {
|
||||||
|
target_port = 81
|
||||||
|
published_port = 81
|
||||||
|
publish_mode = "host"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
terraform/shipyard-swarm/variables.tf
Normal file
11
terraform/shipyard-swarm/variables.tf
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
variable "docker_host" {
|
||||||
|
type = string
|
||||||
|
description = "Docker daemon SSH / TCP connection string"
|
||||||
|
default = "ssh://snarf@140.44.4.71:22"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "swarm_storage_base" {
|
||||||
|
type = string
|
||||||
|
description = "Path to NFS persistent storage mount"
|
||||||
|
default = "/mnt/swarm_shared/legacy_volumes"
|
||||||
|
}
|
||||||
9
terraform/synology/outputs.tf
Normal file
9
terraform/synology/outputs.tf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
output "configured_shares" {
|
||||||
|
description = "Configured Synology NFS shares"
|
||||||
|
value = [
|
||||||
|
synology_filestation_share.kubernetes.name,
|
||||||
|
synology_filestation_share.docker.name,
|
||||||
|
synology_filestation_share.backups.name,
|
||||||
|
synology_filestation_share.media.name,
|
||||||
|
]
|
||||||
|
}
|
||||||
25
terraform/synology/providers.tf
Normal file
25
terraform/synology/providers.tf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
required_providers {
|
||||||
|
synology = {
|
||||||
|
source = "synology-community/synology"
|
||||||
|
version = "~> 0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "http" {
|
||||||
|
address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/synology"
|
||||||
|
lock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/synology/lock"
|
||||||
|
unlock_address = "https://git.snarfnet.net/api/packages/snarf/terraform/state/synology/lock"
|
||||||
|
username = "snarf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "synology" {
|
||||||
|
host = var.synology_host
|
||||||
|
port = var.synology_port
|
||||||
|
username = var.synology_user
|
||||||
|
password = var.synology_password
|
||||||
|
https = true
|
||||||
|
insecure = true
|
||||||
|
}
|
||||||
31
terraform/synology/shares.tf
Normal file
31
terraform/synology/shares.tf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 1. Kubernetes Longhorn Backup Target Shared Folder
|
||||||
|
resource "synology_filestation_share" "kubernetes" {
|
||||||
|
name = "kubernetes"
|
||||||
|
vol_path = "/volume1"
|
||||||
|
description = "Kubernetes Longhorn Backup Target and Persistent RWX storage"
|
||||||
|
enable_recycle_bin = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Docker Swarm Persistent Volumes Shared Folder
|
||||||
|
resource "synology_filestation_share" "docker" {
|
||||||
|
name = "docker"
|
||||||
|
vol_path = "/volume1"
|
||||||
|
description = "Docker Swarm shared persistent storage"
|
||||||
|
enable_recycle_bin = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Homelab Backups Shared Folder
|
||||||
|
resource "synology_filestation_share" "backups" {
|
||||||
|
name = "backups"
|
||||||
|
vol_path = "/volume1"
|
||||||
|
description = "Automated SQLite and system config backup bundles"
|
||||||
|
enable_recycle_bin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Media Shared Folder
|
||||||
|
resource "synology_filestation_share" "media" {
|
||||||
|
name = "media"
|
||||||
|
vol_path = "/volume1"
|
||||||
|
description = "Media and storage pool"
|
||||||
|
enable_recycle_bin = false
|
||||||
|
}
|
||||||
24
terraform/synology/variables.tf
Normal file
24
terraform/synology/variables.tf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
variable "synology_host" {
|
||||||
|
type = string
|
||||||
|
description = "Synology DSM IP address"
|
||||||
|
default = "14.10.10.71"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "synology_port" {
|
||||||
|
type = number
|
||||||
|
description = "Synology DSM HTTPS port"
|
||||||
|
default = 5001
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "synology_user" {
|
||||||
|
type = string
|
||||||
|
description = "Synology DSM management user"
|
||||||
|
default = "admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "synology_password" {
|
||||||
|
type = string
|
||||||
|
description = "Synology DSM management password"
|
||||||
|
default = ""
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user